text: /*
   * Copyright 2018 Google Inc.
   *
   * Use of this source code is governed by a BSD-style license that can be
   * found in the LICENSE file.
   */
  
  #include "include/gpu/GrRecordingContext.h"
  #include "src/core/SkLRUCache.h"
  #include "src/gpu/GrCaps.h"
  #include "src/gpu/GrContextThreadSafeProxyPriv.h"
  #include "src/gpu/GrProgramDesc.h"
  #include "src/gpu/GrProgramInfo.h"
  #include "src/gpu/GrRecordingContextPriv.h"
  #include "src/gpu/effects/GrSkSLFP.h"
  
  /**
   * The DDL Context is the one in effect during DDL Recording. It isn't backed by a GrGPU and
   * cannot allocate any GPU resources.
   */
  class GrDDLContext final : public GrRecordingContext {
  public:
      GrDDLContext(sk_sp<GrContextThreadSafeProxy> proxy)
          : INHERITED(std::move(proxy)) {
      }
  
      ~GrDDLContext() override {}
  
      void abandonContext() override {
          SkASSERT(0); // abandoning in a DDL Recorder doesn't make a whole lot of sense
          INHERITED::abandonContext();
      }
  
  private:
      // Add to the set of unique program infos required by this DDL
      void recordProgramInfo(const GrProgramInfo* programInfo) final {
          if (!programInfo) {
              return;
          }
  
          const GrCaps* caps = this->caps();
  
          if (this->backend() == GrBackendApi::kMetal ||
              this->backend() == GrBackendApi::kDirect3D ||
              this->backend() == GrBackendApi::kDawn) {
              // Currently Metal, Direct3D, and Dawn require a live renderTarget to
              // compute the key
              return;
          }
  
          if (programInfo->requestedFeatures() & GrProcessor::CustomFeatures::kSampleLocations) {
              // Sample locations require a live renderTarget to compute the key
              return;
          }
  
          GrProgramDesc desc = caps->makeDesc(nullptr, *programInfo);
          if (!desc.isValid()) {
              return;
          }
  
          fProgramInfoMap.add(desc, programInfo);
      }
  
      void detachProgramData(SkTArray<ProgramData>* dst) final {
          SkASSERT(dst->empty());
  
          fProgramInfoMap.toArray(dst);
      }
  
  
  private:
      class ProgramInfoMap : public ::SkNoncopyable {
          typedef const GrProgramDesc  CacheKey;
          typedef const GrProgramInfo* CacheValue;
  
      public:
          // All the programInfo data should be stored in the record-time arena so there is no
          // need to ref them here or to delete them in the destructor.
          ProgramInfoMap() : fMap(10) {}
          ~ProgramInfoMap() {}
  
          // TODO: this is doing a lot of reallocating of the ProgramDesc! Once the program descs
          // are allocated in the record-time area there won't be a problem.
          void add(CacheKey& desc, const GrProgramInfo* programInfo) {
              SkASSERT(desc.isValid());
  
              const CacheValue* preExisting = fMap.find(desc);
              if (preExisting) {
                  return;
              }
  
              fMap.insert(desc, programInfo);
          }
  
          void toArray(SkTArray<ProgramData>* dst) {
              fMap.foreach([dst](CacheKey* programDesc, CacheValue* programInfo) {
                               // TODO: remove this allocation once the program descs are stored
                               // in the record-time arena.
                               dst->emplace_back(std::make_unique<const GrProgramDesc>(*programDesc),
                                                 *programInfo);
                           });
          }
  
      private:
          struct DescHash {
              uint32_t operator()(CacheKey& desc) const {
                  return SkOpts::hash_fn(desc.asKey(), desc.keyLength(), 0);
              }
          };
  
          SkLRUCache<CacheKey, CacheValue, DescHash> fMap;
      };
  
      ProgramInfoMap fProgramInfoMap;
  
      using INHERITED = GrRecordingContext;
  };
  
  sk_sp<GrRecordingContext> GrRecordingContextPriv::MakeDDL(sk_sp<GrContextThreadSafeProxy> proxy) {
      sk_sp<GrRecordingContext> context(new GrDDLContext(std::move(proxy)));
  
      if (!context->init()) {
          return nullptr;
      }
      return context;
  }
  