text: /*
   * Copyright 2017 Google Inc.
   *
   * Use of this source code is governed by a BSD-style license that can be
   * found in the LICENSE file.
   */
  
  #ifndef GrDeferredProxyUploader_DEFINED
  #define GrDeferredProxyUploader_DEFINED
  
  #include "include/core/SkRefCnt.h"
  #include "include/private/SkSemaphore.h"
  #include "src/core/SkAutoPixmapStorage.h"
  
  #include "src/gpu/GrOpFlushState.h"
  #include "src/gpu/GrTextureProxyPriv.h"
  
  /**
   * GrDeferredProxyUploader assists with threaded generation of textures. Currently used by both
   * software clip masks, and the software path renderer. The calling code typically needs to store
   * some additional data (T) for use on the worker thread. GrTDeferredProxyUploader allows storing
   * such data. The common flow is:
   *
   * 1) A GrTDeferredProxyUploader is created, with some payload (eg an SkPath to draw).
   *    The uploader is owned by the proxy that it's going to populate.
   * 2) A task is created with a pointer to the uploader. A worker thread executes that task, using
   *    the payload data to allocate and fill in the fPixels pixmap.
   * 3) The worker thread calls signalAndFreeData(), which notifies the main thread that the pixmap
   *    is ready, and then deletes the payload data (which is no longer needed).
   * 4) In parallel to 2-3, on the main thread... Some op is created that refers to the proxy. When
   *    that op is added to an op list, the op list retains a pointer to the "deferred" proxies.
   * 5) At flush time, the op list ensures that the deferred proxies are instantiated, then calls
   *    scheduleUpload on those proxies, which calls scheduleUpload on the uploader (below).
   * 6) scheduleUpload defers the upload even further, by adding an ASAPUpload to the flush.
   * 7) When the ASAP upload happens, we wait to make sure that the pixels are marked ready
   *    (from step #3 on the worker thread). Then we perform the actual upload to the texture.
   *    Finally, we call resetDeferredUploader, which deletes the uploader object, causing fPixels
   *    to be freed.
   */
  class GrDeferredProxyUploader : public SkNoncopyable {
   public:
    GrDeferredProxyUploader() : fScheduledUpload(false), fWaited(false) {}
  
    virtual ~GrDeferredProxyUploader() {
      // In normal usage (i.e., through GrTDeferredProxyUploader) this will be redundant
      this->wait();
    }
  
    void scheduleUpload(GrOpFlushState* flushState, GrTextureProxy* proxy) {
      if (fScheduledUpload) {
        // Multiple references to the owning proxy may have caused us to already execute
        return;
      }
  
      auto uploadMask = [this, proxy](GrDeferredTextureUploadWritePixelsFn& writePixelsFn) {
        this->wait();
        GrColorType pixelColorType = SkColorTypeToGrColorType(this->fPixels.info().colorType());
        // If the worker thread was unable to allocate pixels, this check will fail, and we'll
        // end up drawing with an uninitialized mask texture, but at least we won't crash.
        if (this->fPixels.addr()) {
          writePixelsFn(
              proxy, 0, 0, this->fPixels.width(), this->fPixels.height(), pixelColorType,
              this->fPixels.addr(), this->fPixels.rowBytes());
        }
        // Upload has finished, so tell the proxy to release this GrDeferredProxyUploader
        proxy->texPriv().resetDeferredUploader();
      };
      flushState->addASAPUpload(std::move(uploadMask));
      fScheduledUpload = true;
    }
  
    void signalAndFreeData() {
      this->freeData();
      fPixelsReady.signal();
    }
  
    SkAutoPixmapStorage* getPixels() { return &fPixels; }
  
   protected:
    void wait() {
      if (!fWaited) {
        fPixelsReady.wait();
        fWaited = true;
      }
    }
  
   private:
    virtual void freeData() {}
  
    SkAutoPixmapStorage fPixels;
    SkSemaphore fPixelsReady;
    bool fScheduledUpload;
    bool fWaited;
  };
  
  template <typename T>
  class GrTDeferredProxyUploader : public GrDeferredProxyUploader {
   public:
    template <typename... Args>
    GrTDeferredProxyUploader(Args&&... args)
        : fData(std::make_unique<T>(std::forward<Args>(args)...)) {}
  
    ~GrTDeferredProxyUploader() override {
      // We need to wait here, so that we don't free fData before the worker thread is done
      // with it. (This happens if the proxy is deleted early due to a full clear or failure
      // of an op list to instantiate).
      this->wait();
    }
  
    T& data() { return *fData; }
  
   private:
    void freeData() override { fData.reset(); }
  
    std::unique_ptr<T> fData;
  };
  
  #endif
  