Revert "Remove deferred proxy machinery"

This reverts commit a3987cc5873c9bce758fa3843bb801c95d282bbd.

Reason for revert: Keeping it

Original change's description:
> Remove deferred proxy machinery
>
> These aren't used any more in favor of lazy proxies.
>
> Bug: skia:11288
> Change-Id: I992e1a3dd343e0ebc7f3a4f18c0054453dfebbaf
> Reviewed-on: https://skia-review.googlesource.com/c/skia/+/366896
> Commit-Queue: Adlai Holler <adlai@google.com>
> Reviewed-by: Brian Salomon <bsalomon@google.com>

TBR=bsalomon@google.com,robertphillips@google.com,adlai@google.com

Bug: skia:11288
Change-Id: Iafa9ed18a772e451349dfad61ee462df963028be
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/368376
Reviewed-by: Brian Salomon <bsalomon@google.com>
Reviewed-by: Adlai Holler <adlai@google.com>
Commit-Queue: Adlai Holler <adlai@google.com>
diff --git a/src/gpu/GrBackendTextureImageGenerator.cpp b/src/gpu/GrBackendTextureImageGenerator.cpp
index ab7d2ba..4e08ecc 100644
--- a/src/gpu/GrBackendTextureImageGenerator.cpp
+++ b/src/gpu/GrBackendTextureImageGenerator.cpp
@@ -19,6 +19,7 @@
 #include "src/gpu/GrSemaphore.h"
 #include "src/gpu/GrSurfaceDrawContext.h"
 #include "src/gpu/GrTexture.h"
+#include "src/gpu/GrTextureProxyPriv.h"
 #include "src/gpu/SkGr.h"
 #include "src/gpu/gl/GrGLTexture.h"
 
diff --git a/src/gpu/GrClipStack.cpp b/src/gpu/GrClipStack.cpp
index 8ba5a8b..8664fa8 100644
--- a/src/gpu/GrClipStack.cpp
+++ b/src/gpu/GrClipStack.cpp
@@ -12,6 +12,7 @@
 #include "src/core/SkRectPriv.h"
 #include "src/core/SkTaskGroup.h"
 #include "src/gpu/GrClip.h"
+#include "src/gpu/GrDeferredProxyUploader.h"
 #include "src/gpu/GrDirectContextPriv.h"
 #include "src/gpu/GrProxyProvider.h"
 #include "src/gpu/GrRecordingContextPriv.h"
diff --git a/src/gpu/GrClipStackClip.cpp b/src/gpu/GrClipStackClip.cpp
index 0a6b39e..54b29c3 100644
--- a/src/gpu/GrClipStackClip.cpp
+++ b/src/gpu/GrClipStackClip.cpp
@@ -14,6 +14,7 @@
 #include "src/core/SkTraceEvent.h"
 #include "src/gpu/GrAppliedClip.h"
 #include "src/gpu/GrAttachment.h"
+#include "src/gpu/GrDeferredProxyUploader.h"
 #include "src/gpu/GrDirectContextPriv.h"
 #include "src/gpu/GrDrawingManager.h"
 #include "src/gpu/GrGpuResourcePriv.h"
diff --git a/src/gpu/GrDeferredProxyUploader.h b/src/gpu/GrDeferredProxyUploader.h
new file mode 100644
index 0000000..33e62a5
--- /dev/null
+++ b/src/gpu/GrDeferredProxyUploader.h
@@ -0,0 +1,120 @@
+/*
+ * 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
diff --git a/src/gpu/GrDrawingManager.cpp b/src/gpu/GrDrawingManager.cpp
index 71125b1..9b44856 100644
--- a/src/gpu/GrDrawingManager.cpp
+++ b/src/gpu/GrDrawingManager.cpp
@@ -37,6 +37,7 @@
 #include "src/gpu/GrTTopoSort.h"
 #include "src/gpu/GrTexture.h"
 #include "src/gpu/GrTextureProxy.h"
+#include "src/gpu/GrTextureProxyPriv.h"
 #include "src/gpu/GrTextureResolveRenderTask.h"
 #include "src/gpu/GrTracing.h"
 #include "src/gpu/GrTransferFromRenderTask.h"
@@ -170,9 +171,10 @@
             onFlushRenderTask->makeClosed(*fContext->priv().caps());
 #ifdef SK_DEBUG
             // OnFlush callbacks are invoked during flush, and are therefore expected to handle
-            // resource allocation & usage on their own. (No lazy proxies!)
+            // resource allocation & usage on their own. (No deferred or lazy proxies!)
             onFlushRenderTask->visitTargetAndSrcProxies_debugOnly(
                     [](GrSurfaceProxy* p, GrMipmapped mipMapped) {
+                SkASSERT(!p->asTextureProxy() || !p->asTextureProxy()->texPriv().isDeferred());
                 SkASSERT(!p->isLazy());
                 if (p->requiresManualMSAAResolve()) {
                     // The onFlush callback is responsible for ensuring MSAA gets resolved.
@@ -316,6 +318,8 @@
              continue;
         }
 
+        SkASSERT(renderTask->deferredProxiesAreInstantiated());
+
         renderTask->prepare(flushState);
     }
 
diff --git a/src/gpu/GrGpu.cpp b/src/gpu/GrGpu.cpp
index 22012d0..43df47f 100644
--- a/src/gpu/GrGpu.cpp
+++ b/src/gpu/GrGpu.cpp
@@ -31,6 +31,7 @@
 #include "src/gpu/GrSemaphore.h"
 #include "src/gpu/GrStagingBufferManager.h"
 #include "src/gpu/GrStencilSettings.h"
+#include "src/gpu/GrTextureProxyPriv.h"
 #include "src/gpu/GrTracing.h"
 #include "src/sksl/SkSLCompiler.h"
 #include "src/utils/SkJSONWriter.h"
diff --git a/src/gpu/GrOpsRenderPass.h b/src/gpu/GrOpsRenderPass.h
index 99fb87c..25da976 100644
--- a/src/gpu/GrOpsRenderPass.h
+++ b/src/gpu/GrOpsRenderPass.h
@@ -9,7 +9,6 @@
 #define GrOpsRenderPass_DEFINED
 
 #include "include/core/SkDrawable.h"
-#include "src/gpu/GrDeferredUpload.h"
 #include "src/gpu/GrPipeline.h"
 #include "src/gpu/ops/GrDrawOp.h"
 
diff --git a/src/gpu/GrOpsTask.cpp b/src/gpu/GrOpsTask.cpp
index 8eb03cc..e9add24 100644
--- a/src/gpu/GrOpsTask.cpp
+++ b/src/gpu/GrOpsTask.cpp
@@ -432,6 +432,7 @@
     this->deleteOps();
     fClipAllocators.reset();
 
+    fDeferredProxies.reset();
     fSampledProxies.reset();
     fAuditTrail = nullptr;
 
@@ -674,6 +675,7 @@
 }
 
 void GrOpsTask::reset() {
+    fDeferredProxies.reset();
     fSampledProxies.reset();
     fClipAllocators.reset();
     fClippedContentBounds = SkIRect::MakeEmpty();
@@ -722,9 +724,11 @@
         fColorLoadOp = GrLoadOp::kClear;
         fLoadClearColor = opsTasks.front()->fLoadClearColor;
     }
+    int addlDeferredProxyCount = 0;
     int addlProxyCount = 0;
     int addlOpChainCount = 0;
     for (const auto& opsTask : opsTasks) {
+        addlDeferredProxyCount += opsTask->fDeferredProxies.count();
         addlProxyCount += opsTask->fSampledProxies.count();
         addlOpChainCount += opsTask->fOpChains.count();
         fClippedContentBounds.join(opsTask->fClippedContentBounds);
@@ -734,10 +738,13 @@
     }
 
     fLastClipStackGenID = SK_InvalidUniqueID;
+    fDeferredProxies.reserve_back(addlDeferredProxyCount);
     fSampledProxies.reserve_back(addlProxyCount);
     fOpChains.reserve_back(addlOpChainCount);
     fClipAllocators.reserve_back(opsTasks.count());
     for (const auto& opsTask : opsTasks) {
+        fDeferredProxies.move_back_n(opsTask->fDeferredProxies.count(),
+                                     opsTask->fDeferredProxies.data());
         fSampledProxies.move_back_n(opsTask->fSampledProxies.count(),
                                     opsTask->fSampledProxies.data());
         fOpChains.move_back_n(opsTask->fOpChains.count(),
@@ -745,6 +752,7 @@
         SkASSERT(1 == opsTask->fClipAllocators.count());
         fClipAllocators.push_back(std::move(opsTask->fClipAllocators[0]));
         opsTask->fClipAllocators.reset();
+        opsTask->fDeferredProxies.reset();
         opsTask->fSampledProxies.reset();
         opsTask->fOpChains.reset();
     }
@@ -755,6 +763,7 @@
 bool GrOpsTask::resetForFullscreenClear(CanDiscardPreviousOps canDiscardPreviousOps) {
     if (CanDiscardPreviousOps::kYes == canDiscardPreviousOps || this->isEmpty()) {
         this->deleteOps();
+        fDeferredProxies.reset();
         fSampledProxies.reset();
 
         // If the opsTask is using a render target which wraps a vulkan command buffer, we can't do
@@ -892,6 +901,16 @@
 }
 
 void GrOpsTask::gatherProxyIntervals(GrResourceAllocator* alloc) const {
+    for (int i = 0; i < fDeferredProxies.count(); ++i) {
+        SkASSERT(!fDeferredProxies[i]->isInstantiated());
+        // We give all the deferred proxies a write usage at the very start of flushing. This
+        // locks them out of being reused for the entire flush until they are read - and then
+        // they can be recycled. This is a bit unfortunate because a flush can proceed in waves
+        // with sub-flushes. The deferred proxies only need to be pinned from the start of
+        // the sub-flush in which they appear.
+        alloc->addInterval(fDeferredProxies[i], 0, 0, GrResourceAllocator::ActualUse::kNo);
+    }
+
     GrSurfaceProxy* targetProxy = this->target(0);
 
     // Add the interval for all the writes to this GrOpsTasks's target
diff --git a/src/gpu/GrRenderTask.cpp b/src/gpu/GrRenderTask.cpp
index f939455..074f4a9 100644
--- a/src/gpu/GrRenderTask.cpp
+++ b/src/gpu/GrRenderTask.cpp
@@ -9,6 +9,7 @@
 
 #include "src/gpu/GrAttachment.h"
 #include "src/gpu/GrRenderTarget.h"
+#include "src/gpu/GrTextureProxyPriv.h"
 #include "src/gpu/GrTextureResolveRenderTask.h"
 
 uint32_t GrRenderTask::CreateUniqueID() {
@@ -46,6 +47,15 @@
     SkASSERT(this->isSetFlag(kDisowned_Flag));
 }
 
+bool GrRenderTask::deferredProxiesAreInstantiated() const {
+    for (int i = 0; i < fDeferredProxies.count(); ++i) {
+        if (!fDeferredProxies[i]->isInstantiated()) {
+            return false;
+        }
+    }
+
+    return true;
+}
 #endif
 
 void GrRenderTask::makeClosed(const GrCaps& caps) {
@@ -75,6 +85,14 @@
     this->setFlag(kClosed_Flag);
 }
 
+void GrRenderTask::prepare(GrOpFlushState* flushState) {
+    for (int i = 0; i < fDeferredProxies.count(); ++i) {
+        fDeferredProxies[i]->texPriv().scheduleUpload(flushState);
+    }
+
+    this->onPrepare(flushState);
+}
+
 // Add a GrRenderTask-based dependency
 void GrRenderTask::addDependency(GrRenderTask* dependedOn) {
     SkASSERT(!dependedOn->dependsOn(this));  // loops are bad
@@ -113,6 +131,8 @@
         SkASSERT(GrMipmapped::kNo == mipMapped);
         // We should never attempt a self-read on a surface that has a separate MSAA renderbuffer.
         SkASSERT(!dependedOn->requiresManualMSAAResolve());
+        SkASSERT(!dependedOn->asTextureProxy() ||
+                 !dependedOn->asTextureProxy()->texPriv().isDeferred());
         return;
     }
 
@@ -166,6 +186,9 @@
         if (dependedOnTask) {
             SkASSERT(fTextureResolveTask->dependsOn(dependedOnTask));
         }
+        if (textureProxy && textureProxy->texPriv().isDeferred()) {
+            SkASSERT(fTextureResolveTask->fDeferredProxies.back() == textureProxy);
+        }
 
         // The GrTextureResolveRenderTask factory should have also marked the proxy clean, set the
         // last renderTask on the textureProxy to textureResolveTask, and closed textureResolveTask.
@@ -180,6 +203,10 @@
         return;
     }
 
+    if (textureProxy && textureProxy->texPriv().isDeferred()) {
+        fDeferredProxies.push_back(textureProxy);
+    }
+
     if (dependedOnTask) {
         this->addDependency(dependedOnTask);
     }
diff --git a/src/gpu/GrRenderTask.h b/src/gpu/GrRenderTask.h
index 0df5d9f..218267f 100644
--- a/src/gpu/GrRenderTask.h
+++ b/src/gpu/GrRenderTask.h
@@ -35,7 +35,7 @@
     void prePrepare(GrRecordingContext* context) { this->onPrePrepare(context); }
 
     // These two methods are only invoked at flush time
-    void prepare(GrOpFlushState* flushState) { this->onPrepare(flushState); }
+    void prepare(GrOpFlushState* flushState);
     bool execute(GrOpFlushState* flushState) { return this->onExecute(flushState); }
 
     virtual bool requiresExplicitCleanup() const { return false; }
@@ -133,6 +133,8 @@
     SK_DECLARE_INTERNAL_LLIST_INTERFACE(GrRenderTask);
 
 protected:
+    SkDEBUGCODE(bool deferredProxiesAreInstantiated() const;)
+
     // Add a target surface proxy to the list of targets for this task.
     // This also informs the drawing manager to update the lastRenderTask association.
     void addTarget(GrDrawingManager*, sk_sp<GrSurfaceProxy>);
@@ -156,6 +158,11 @@
 
     SkSTArray<1, sk_sp<GrSurfaceProxy>> fTargets;
 
+    // List of texture proxies whose contents are being prepared on a worker thread
+    // TODO: this list exists so we can fire off the proper upload when an renderTask begins
+    // executing. Can this be replaced?
+    SkTArray<GrTextureProxy*, true> fDeferredProxies;
+
     enum Flags {
         kClosed_Flag    = 0x01,   //!< This task can't accept any more dependencies.
         kDisowned_Flag  = 0x02,   //!< This task is disowned by its creating GrDrawingManager.
diff --git a/src/gpu/GrResourceAllocator.cpp b/src/gpu/GrResourceAllocator.cpp
index d7e6851..2a1d44d 100644
--- a/src/gpu/GrResourceAllocator.cpp
+++ b/src/gpu/GrResourceAllocator.cpp
@@ -94,7 +94,12 @@
     if (Interval* intvl = fIntvlHash.find(proxy->uniqueID().asUInt())) {
         // Revise the interval for an existing use
 #ifdef SK_DEBUG
-        if (isDirectDstRead) {
+        if (0 == start && 0 == end) {
+            // This interval is for the initial upload to a deferred proxy. Due to the vagaries
+            // of how deferred proxies are collected they can appear as uploads multiple times
+            // in a single opsTasks' list and as uploads in several opsTasks.
+            SkASSERT(0 == intvl->start());
+        } else if (isDirectDstRead) {
             // Direct reads from the render target itself should occur w/in the existing
             // interval
             SkASSERT(intvl->start() <= start && intvl->end() >= end);
diff --git a/src/gpu/GrSoftwarePathRenderer.cpp b/src/gpu/GrSoftwarePathRenderer.cpp
index d3138f1..0f43d27 100644
--- a/src/gpu/GrSoftwarePathRenderer.cpp
+++ b/src/gpu/GrSoftwarePathRenderer.cpp
@@ -14,6 +14,7 @@
 #include "src/gpu/GrAuditTrail.h"
 #include "src/gpu/GrCaps.h"
 #include "src/gpu/GrClip.h"
+#include "src/gpu/GrDeferredProxyUploader.h"
 #include "src/gpu/GrDirectContextPriv.h"
 #include "src/gpu/GrGpuResourcePriv.h"
 #include "src/gpu/GrOpFlushState.h"
diff --git a/src/gpu/GrTextureProxy.cpp b/src/gpu/GrTextureProxy.cpp
index 4884d45..d7be2fe 100644
--- a/src/gpu/GrTextureProxy.cpp
+++ b/src/gpu/GrTextureProxy.cpp
@@ -6,12 +6,13 @@
  */
 
 #include "src/gpu/GrTextureProxy.h"
+#include "src/gpu/GrTextureProxyPriv.h"
 
 #include "include/gpu/GrDirectContext.h"
+#include "src/gpu/GrDeferredProxyUploader.h"
 #include "src/gpu/GrDirectContextPriv.h"
 #include "src/gpu/GrProxyProvider.h"
 #include "src/gpu/GrSurface.h"
-#include "src/gpu/GrSurfaceProxyPriv.h"
 #include "src/gpu/GrTexture.h"
 
 // Deferred version - no data
@@ -30,7 +31,8 @@
         , fMipmapStatus(mipmapStatus)
         SkDEBUGCODE(, fInitialMipmapStatus(fMipmapStatus))
         , fCreatingProvider(creatingProvider)
-        , fProxyProvider(nullptr) {
+        , fProxyProvider(nullptr)
+        , fDeferredUploader(nullptr) {
     SkASSERT(!(fSurfaceFlags & GrInternalSurfaceFlags::kFramebufferOnly));
     if (this->textureType() == GrTextureType::kExternal) {
         fSurfaceFlags |= GrInternalSurfaceFlags::kReadOnly;
@@ -55,7 +57,8 @@
         , fMipmapStatus(mipmapStatus)
         SkDEBUGCODE(, fInitialMipmapStatus(fMipmapStatus))
         , fCreatingProvider(creatingProvider)
-        , fProxyProvider(nullptr) {
+        , fProxyProvider(nullptr)
+        , fDeferredUploader(nullptr) {
     SkASSERT(!(fSurfaceFlags & GrInternalSurfaceFlags::kFramebufferOnly));
     if (this->textureType() == GrTextureType::kExternal) {
         fSurfaceFlags |= GrInternalSurfaceFlags::kReadOnly;
@@ -71,7 +74,8 @@
         , fMipmapStatus(fTarget->asTexture()->mipmapStatus())
         SkDEBUGCODE(, fInitialMipmapStatus(fMipmapStatus))
         , fCreatingProvider(creatingProvider)
-        , fProxyProvider(nullptr) {
+        , fProxyProvider(nullptr)
+        , fDeferredUploader(nullptr) {
     if (fTarget->getUniqueKey().isValid()) {
         fProxyProvider = fTarget->asTexture()->getContext()->priv().proxyProvider();
         fProxyProvider->adoptUniqueKeyFromSurface(this, fTarget.get());
@@ -123,6 +127,23 @@
     return surface;
 }
 
+void GrTextureProxyPriv::setDeferredUploader(std::unique_ptr<GrDeferredProxyUploader> uploader) {
+    SkASSERT(!fTextureProxy->fDeferredUploader);
+    fTextureProxy->fDeferredUploader = std::move(uploader);
+}
+
+void GrTextureProxyPriv::scheduleUpload(GrOpFlushState* flushState) {
+    // The texture proxy's contents may already have been uploaded or instantiation may have failed
+    if (fTextureProxy->fDeferredUploader && fTextureProxy->isInstantiated()) {
+        fTextureProxy->fDeferredUploader->scheduleUpload(flushState, fTextureProxy);
+    }
+}
+
+void GrTextureProxyPriv::resetDeferredUploader() {
+    SkASSERT(fTextureProxy->fDeferredUploader);
+    fTextureProxy->fDeferredUploader.reset();
+}
+
 GrMipmapped GrTextureProxy::mipmapped() const {
     if (this->isInstantiated()) {
         return this->peekTexture()->mipmapped();
diff --git a/src/gpu/GrTextureProxy.h b/src/gpu/GrTextureProxy.h
index cf8cbe5..3839e52 100644
--- a/src/gpu/GrTextureProxy.h
+++ b/src/gpu/GrTextureProxy.h
@@ -12,8 +12,10 @@
 #include "src/gpu/GrSurfaceProxy.h"
 
 class GrCaps;
+class GrDeferredProxyUploader;
 class GrProxyProvider;
 class GrResourceProvider;
+class GrTextureProxyPriv;
 
 // This class delays the acquisition of textures until they are actually required
 class GrTextureProxy : virtual public GrSurfaceProxy {
@@ -92,12 +94,17 @@
     inline CacheAccess cacheAccess();
     inline const CacheAccess cacheAccess() const;  // NOLINT(readability-const-return-type)
 
+    // Provides access to special purpose functions.
+    GrTextureProxyPriv texPriv();
+    const GrTextureProxyPriv texPriv() const;  // NOLINT(readability-const-return-type)
+
     SkDEBUGCODE(GrDDLProvider creatingProvider() const { return fCreatingProvider; })
 
 protected:
     // DDL TODO: rm the GrSurfaceProxy friending
     friend class GrSurfaceProxy;   // for ctors
     friend class GrProxyProvider;  // for ctors
+    friend class GrTextureProxyPriv;
     friend class GrSurfaceProxyPriv;  // ability to change key sync state after lazy instantiation.
 
     // Deferred version - no data.
@@ -182,6 +189,11 @@
 
     LazySurfaceDesc callbackDesc() const override;
 
+    // Only used for proxies whose contents are being prepared on a worker thread. This object
+    // stores the texture data, allowing the proxy to remain uninstantiated until flush. At that
+    // point, the proxy is instantiated, and this data is used to perform an ASAP upload.
+    std::unique_ptr<GrDeferredProxyUploader> fDeferredUploader;
+
     size_t onUninstantiatedGpuMemorySize() const override;
 
     // Methods made available via GrTextureProxy::CacheAccess
diff --git a/src/gpu/GrTextureProxyPriv.h b/src/gpu/GrTextureProxyPriv.h
new file mode 100644
index 0000000..cb99688
--- /dev/null
+++ b/src/gpu/GrTextureProxyPriv.h
@@ -0,0 +1,51 @@
+/*
+ * 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 GrTextureProxyPriv_DEFINED
+#define GrTextureProxyPriv_DEFINED
+
+#include "src/gpu/GrTextureProxy.h"
+
+class GrDeferredProxyUploader;
+class GrOpFlushState;
+
+/**
+ * This class hides the more specialized capabilities of GrTextureProxy.
+ */
+class GrTextureProxyPriv {
+public:
+    // Attach a deferred uploader to the proxy. Holds data being prepared by a worker thread.
+    void setDeferredUploader(std::unique_ptr<GrDeferredProxyUploader>);
+    bool isDeferred() const { return SkToBool(fTextureProxy->fDeferredUploader.get()); }
+    // For a deferred proxy (one that has a deferred uploader attached), this schedules an ASAP
+    // upload of that data to the instantiated texture.
+    void scheduleUpload(GrOpFlushState*);
+    // Clears any deferred uploader object on the proxy. Used to free the CPU data after the
+    // contents have been uploaded.
+    void resetDeferredUploader();
+
+private:
+    explicit GrTextureProxyPriv(GrTextureProxy* textureProxy) : fTextureProxy(textureProxy) {}
+    GrTextureProxyPriv(const GrTextureProxyPriv&) = delete;
+    GrTextureProxyPriv& operator=(const GrTextureProxyPriv&) = delete;
+
+    // No taking addresses of this type.
+    const GrTextureProxyPriv* operator&() const;
+    GrTextureProxyPriv* operator&();
+
+    GrTextureProxy* fTextureProxy;
+
+    friend class GrTextureProxy;  // to construct/copy this type.
+};
+
+inline GrTextureProxyPriv GrTextureProxy::texPriv() { return GrTextureProxyPriv(this); }
+
+inline const GrTextureProxyPriv GrTextureProxy::texPriv() const {  // NOLINT(readability-const-return-type)
+    return GrTextureProxyPriv(const_cast<GrTextureProxy*>(this));
+}
+
+#endif
diff --git a/src/gpu/GrTextureRenderTargetProxy.cpp b/src/gpu/GrTextureRenderTargetProxy.cpp
index 7145e7f..8cf8266 100644
--- a/src/gpu/GrTextureRenderTargetProxy.cpp
+++ b/src/gpu/GrTextureRenderTargetProxy.cpp
@@ -12,6 +12,7 @@
 #include "src/gpu/GrSurface.h"
 #include "src/gpu/GrSurfaceProxyPriv.h"
 #include "src/gpu/GrTexture.h"
+#include "src/gpu/GrTextureProxyPriv.h"
 
 #ifdef SK_DEBUG
 #include "include/gpu/GrDirectContext.h"
diff --git a/src/gpu/gl/GrGLCaps.cpp b/src/gpu/gl/GrGLCaps.cpp
index 55c0b9a..ceef385 100644
--- a/src/gpu/gl/GrGLCaps.cpp
+++ b/src/gpu/gl/GrGLCaps.cpp
@@ -16,6 +16,7 @@
 #include "src/gpu/GrProgramDesc.h"
 #include "src/gpu/GrShaderCaps.h"
 #include "src/gpu/GrSurfaceProxyPriv.h"
+#include "src/gpu/GrTextureProxyPriv.h"
 #include "src/gpu/SkGr.h"
 #include "src/gpu/gl/GrGLContext.h"
 #include "src/gpu/gl/GrGLRenderTarget.h"
diff --git a/src/gpu/ops/GrDrawOp.h b/src/gpu/ops/GrDrawOp.h
index 33fb95c..f023eb3 100644
--- a/src/gpu/ops/GrDrawOp.h
+++ b/src/gpu/ops/GrDrawOp.h
@@ -9,6 +9,7 @@
 #define GrDrawOp_DEFINED
 
 #include <functional>
+#include "src/gpu/GrDeferredUpload.h"
 #include "src/gpu/GrPipeline.h"
 #include "src/gpu/ops/GrOp.h"