Switch GrConfigConversionEffect over to taking GrTextureProxies

Change-Id: Ic8be773e210e1ac05dcb9aad6c89dcd63e9e4ba2
Reviewed-on: https://skia-review.googlesource.com/7521
Reviewed-by: Brian Salomon <bsalomon@google.com>
Commit-Queue: Robert Phillips <robertphillips@google.com>
diff --git a/gn/gpu.gni b/gn/gpu.gni
index 7df3e4d..3013270 100644
--- a/gn/gpu.gni
+++ b/gn/gpu.gni
@@ -187,6 +187,7 @@
   "$_src/gpu/GrStyle.cpp",
   "$_src/gpu/GrStyle.h",
   "$_src/gpu/GrSurfaceContextPriv.h",
+  "$_src/gpu/GrSurfaceProxyPriv.h",
   "$_src/gpu/GrTessellator.cpp",
   "$_src/gpu/GrTessellator.h",
   "$_src/gpu/GrTextureOpList.cpp",
diff --git a/include/gpu/GrContext.h b/include/gpu/GrContext.h
index 7e8d4e8..4fe3e72 100644
--- a/include/gpu/GrContext.h
+++ b/include/gpu/GrContext.h
@@ -430,10 +430,11 @@
      * of effects that make a readToUPM->writeToPM->readToUPM cycle invariant. Otherwise, they
      * return NULL. They also can perform a swizzle as part of the draw.
      */
-    sk_sp<GrFragmentProcessor> createPMToUPMEffect(GrTexture*, const GrSwizzle&,
-                                                   const SkMatrix&) const;
-    sk_sp<GrFragmentProcessor> createUPMToPMEffect(GrTexture*, const GrSwizzle&,
-                                                   const SkMatrix&) const;
+    sk_sp<GrFragmentProcessor> createPMToUPMEffect(GrTexture*, const GrSwizzle&, const SkMatrix&);
+    sk_sp<GrFragmentProcessor> createPMToUPMEffect(sk_sp<GrTextureProxy>, const GrSwizzle&,
+                                                   const SkMatrix&);
+    sk_sp<GrFragmentProcessor> createUPMToPMEffect(sk_sp<GrTextureProxy>, const GrSwizzle&,
+                                                   const SkMatrix&);
     /** Called before either of the above two functions to determine the appropriate fragment
         processors for conversions. */
     void testPMConversionsIfNecessary(uint32_t flags);
diff --git a/include/gpu/GrProcessorUnitTest.h b/include/gpu/GrProcessorUnitTest.h
index 0826e3d..49f26fd 100644
--- a/include/gpu/GrProcessorUnitTest.h
+++ b/include/gpu/GrProcessorUnitTest.h
@@ -8,6 +8,7 @@
 #ifndef GrProcessorUnitTest_DEFINED
 #define GrProcessorUnitTest_DEFINED
 
+#include "../private/GrTextureProxy.h"
 #include "../private/SkTArray.h"
 #include "GrTestUtils.h"
 #include "SkTypes.h"
@@ -53,12 +54,24 @@
         , fRenderTargetContext(renderTargetContext) {
         fTextures[0] = textures[0];
         fTextures[1] = textures[1];
+
+        fProxies[0] = GrSurfaceProxy::MakeWrapped(sk_ref_sp(textures[0]));
+        fProxies[1] = GrSurfaceProxy::MakeWrapped(sk_ref_sp(textures[1]));
     }
     SkRandom* fRandom;
     GrContext* fContext;
     const GrCaps* fCaps;
     const GrRenderTargetContext* fRenderTargetContext;
     GrTexture* fTextures[2];
+
+    GrContext* context() { return fContext; }
+    GrTexture* texture(int index) { return fTextures[index]; }
+    sk_sp<GrTextureProxy> textureProxy(int index) {
+        return sk_ref_sp(fProxies[index]->asTextureProxy());
+    }
+
+private:
+    sk_sp<GrSurfaceProxy> fProxies[2];
 };
 
 #if SK_ALLOW_STATIC_GLOBAL_INITIALIZERS
diff --git a/include/private/GrSurfaceProxy.h b/include/private/GrSurfaceProxy.h
index 9363b82..6f2a751 100644
--- a/include/private/GrSurfaceProxy.h
+++ b/include/private/GrSurfaceProxy.h
@@ -17,6 +17,7 @@
 class GrRenderTargetOpList;
 class GrRenderTargetProxy;
 class GrSurfaceContext;
+class GrSurfaceProxyPriv;
 class GrTextureOpList;
 class GrTextureProvider;
 class GrTextureProxy;
@@ -98,6 +99,14 @@
         fPendingWrites = 0;
     }
 
+    bool internalHasPendingIO() const {
+        if (fTarget) {
+            return fTarget->internalHasPendingIO();
+        }
+
+        return SkToBool(fPendingWrites | fPendingReads);
+    }
+
     // For deferred proxies this will be null. For wrapped proxies it will point to the
     // wrapped resource.
     GrSurface* fTarget;
@@ -285,6 +294,10 @@
 
     SkDEBUGCODE(void validate(GrContext*) const;)
 
+    // Provides access to functions that aren't part of the public API.
+    GrSurfaceProxyPriv priv();
+    const GrSurfaceProxyPriv priv() const;
+
 protected:
     // Deferred version
     GrSurfaceProxy(const GrSurfaceDesc& desc, SkBackingFit fit, SkBudgeted budgeted)
@@ -301,6 +314,13 @@
 
     virtual ~GrSurfaceProxy();
 
+    friend class GrSurfaceProxyPriv;
+
+    // Methods made available via GrSurfaceProxyPriv
+    bool hasPendingIO() const {
+        return this->internalHasPendingIO();
+    }
+
     // For wrapped resources, 'fDesc' will always be filled in from the wrapped resource.
     const GrSurfaceDesc  fDesc;
     const SkBackingFit   fFit;      // always exact for wrapped resources
diff --git a/src/gpu/GrContext.cpp b/src/gpu/GrContext.cpp
index 1322e25..1733e28 100644
--- a/src/gpu/GrContext.cpp
+++ b/src/gpu/GrContext.cpp
@@ -16,6 +16,7 @@
 #include "GrSoftwarePathRenderer.h"
 #include "GrSurfaceContext.h"
 #include "GrSurfacePriv.h"
+#include "GrSurfaceProxyPriv.h"
 #include "GrTextureContext.h"
 
 #include "SkConfig8888.h"
@@ -295,42 +296,49 @@
         this->flush();
     }
 
-    sk_sp<GrTexture> tempTexture;
+    sk_sp<GrTextureProxy> tempTextureProxy;
     if (GrGpu::kNoDraw_DrawPreference != drawPreference) {
-        tempTexture.reset(
-            this->textureProvider()->createApproxTexture(tempDrawInfo.fTempSurfaceDesc));
-        if (!tempTexture && GrGpu::kRequireDraw_DrawPreference == drawPreference) {
+        sk_sp<GrSurfaceProxy> temp = GrSurfaceProxy::MakeDeferred(*this->caps(),
+                                                                  tempDrawInfo.fTempSurfaceDesc,
+                                                                  SkBackingFit::kApprox,
+                                                                  SkBudgeted::kYes);
+        if (temp) {
+            tempTextureProxy = sk_ref_sp(temp->asTextureProxy());
+        }
+        if (!tempTextureProxy && GrGpu::kRequireDraw_DrawPreference == drawPreference) {
             return false;
         }
     }
 
     // temp buffer for doing sw premul conversion, if needed.
     SkAutoSTMalloc<128 * 128, uint32_t> tmpPixels(0);
-    if (tempTexture) {
+    if (tempTextureProxy) {
         sk_sp<GrFragmentProcessor> fp;
         if (applyPremulToSrc) {
-            fp = this->createUPMToPMEffect(tempTexture.get(), tempDrawInfo.fSwizzle, SkMatrix::I());
+            fp = this->createUPMToPMEffect(tempTextureProxy, tempDrawInfo.fSwizzle, SkMatrix::I());
             // If premultiplying was the only reason for the draw, fall back to a straight write.
             if (!fp) {
                 if (GrGpu::kCallerPrefersDraw_DrawPreference == drawPreference) {
-                    tempTexture.reset(nullptr);
+                    tempTextureProxy.reset(nullptr);
                 }
             } else {
                 applyPremulToSrc = false;
             }
         }
-        if (tempTexture) {
+        if (tempTextureProxy) {
             if (!fp) {
-                fp = GrConfigConversionEffect::Make(tempTexture.get(), tempDrawInfo.fSwizzle,
+                fp = GrConfigConversionEffect::Make(this, tempTextureProxy, tempDrawInfo.fSwizzle,
                                                     GrConfigConversionEffect::kNone_PMConversion,
                                                     SkMatrix::I());
                 if (!fp) {
                     return false;
                 }
             }
-            GrRenderTarget* renderTarget = surface->asRenderTarget();
-            SkASSERT(renderTarget);
-            if (tempTexture->surfacePriv().hasPendingIO()) {
+            GrTexture* texture = tempTextureProxy->instantiate(this->textureProvider());
+            if (!texture) {
+                return false;
+            }
+            if (texture->surfacePriv().hasPendingIO()) {
                 this->flush();
             }
             if (applyPremulToSrc) {
@@ -344,7 +352,7 @@
                 buffer = tmpPixels.get();
                 applyPremulToSrc = false;
             }
-            if (!fGpu->writePixels(tempTexture.get(), 0, 0, width, height,
+            if (!fGpu->writePixels(texture, 0, 0, width, height,
                                    tempDrawInfo.fWriteConfig, buffer,
                                    rowBytes)) {
                 return false;
@@ -354,6 +362,8 @@
             // TODO: Need to decide the semantics of this function for color spaces. Do we support
             // conversion from a passed-in color space? For now, specifying nullptr means that this
             // path will do no conversion, so it will match the behavior of the non-draw path.
+            GrRenderTarget* renderTarget = surface->asRenderTarget();
+            SkASSERT(renderTarget);
             sk_sp<GrRenderTargetContext> renderTargetContext(
                 this->contextPriv().makeWrappedRenderTargetContext(sk_ref_sp(renderTarget),
                                                                    nullptr));
@@ -373,7 +383,7 @@
             }
         }
     }
-    if (!tempTexture) {
+    if (!tempTextureProxy) {
         if (applyPremulToSrc) {
             size_t tmpRowBytes = 4 * width;
             tmpPixels.reset(width * height);
@@ -817,7 +827,7 @@
 
 sk_sp<GrFragmentProcessor> GrContext::createPMToUPMEffect(GrTexture* texture,
                                                           const GrSwizzle& swizzle,
-                                                          const SkMatrix& matrix) const {
+                                                          const SkMatrix& matrix) {
     ASSERT_SINGLE_OWNER
     // We should have already called this->testPMConversionsIfNecessary().
     SkASSERT(fDidTestPMConversions);
@@ -830,16 +840,31 @@
     }
 }
 
-sk_sp<GrFragmentProcessor> GrContext::createUPMToPMEffect(GrTexture* texture,
+sk_sp<GrFragmentProcessor> GrContext::createPMToUPMEffect(sk_sp<GrTextureProxy> proxy,
                                                           const GrSwizzle& swizzle,
-                                                          const SkMatrix& matrix) const {
+                                                          const SkMatrix& matrix) {
+    ASSERT_SINGLE_OWNER
+    // We should have already called this->testPMConversionsIfNecessary().
+    SkASSERT(fDidTestPMConversions);
+    GrConfigConversionEffect::PMConversion pmToUPM =
+        static_cast<GrConfigConversionEffect::PMConversion>(fPMToUPMConversion);
+    if (GrConfigConversionEffect::kNone_PMConversion != pmToUPM) {
+        return GrConfigConversionEffect::Make(this, proxy, swizzle, pmToUPM, matrix);
+    } else {
+        return nullptr;
+    }
+}
+
+sk_sp<GrFragmentProcessor> GrContext::createUPMToPMEffect(sk_sp<GrTextureProxy> proxy,
+                                                          const GrSwizzle& swizzle,
+                                                          const SkMatrix& matrix) {
     ASSERT_SINGLE_OWNER
     // We should have already called this->testPMConversionsIfNecessary().
     SkASSERT(fDidTestPMConversions);
     GrConfigConversionEffect::PMConversion upmToPM =
         static_cast<GrConfigConversionEffect::PMConversion>(fUPMToPMConversion);
     if (GrConfigConversionEffect::kNone_PMConversion != upmToPM) {
-        return GrConfigConversionEffect::Make(texture, swizzle, upmToPM, matrix);
+        return GrConfigConversionEffect::Make(this, std::move(proxy), swizzle, upmToPM, matrix);
     } else {
         return nullptr;
     }
diff --git a/src/gpu/GrSurfaceProxyPriv.h b/src/gpu/GrSurfaceProxyPriv.h
new file mode 100644
index 0000000..19b8171
--- /dev/null
+++ b/src/gpu/GrSurfaceProxyPriv.h
@@ -0,0 +1,43 @@
+/*
+ * 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 GrSurfaceProxyPriv_DEFINED
+#define GrSurfaceProxyPriv_DEFINED
+
+#include "GrSurfaceProxy.h"
+
+/** Class that adds methods to GrSurfaceProxy that are only intended for use internal to Skia.
+    This class is purely a privileged window into GrSurfaceProxy. It should never have additional
+    data members or virtual methods. */
+class GrSurfaceProxyPriv {
+public:
+    // Beware! This call is only guaranteed to tell you if the proxy in question has
+    // any pending IO in its current state. It won't tell you about the IO state in the
+    // future when the proxy is actually used/instantiated.
+    bool hasPendingIO() const { return fProxy->hasPendingIO(); }
+
+private:
+    explicit GrSurfaceProxyPriv(GrSurfaceProxy* proxy) : fProxy(proxy) {}
+    GrSurfaceProxyPriv(const GrSurfaceProxyPriv&) {} // unimpl
+    GrSurfaceProxyPriv& operator=(const GrSurfaceProxyPriv&); // unimpl
+
+    // No taking addresses of this type.
+    const GrSurfaceProxyPriv* operator&() const;
+    GrSurfaceProxyPriv* operator&();
+
+    GrSurfaceProxy* fProxy;
+
+    friend class GrSurfaceProxy; // to construct/copy this type.
+};
+
+inline GrSurfaceProxyPriv GrSurfaceProxy::priv() { return GrSurfaceProxyPriv(this); }
+
+inline const GrSurfaceProxyPriv GrSurfaceProxy::priv () const {
+    return GrSurfaceProxyPriv(const_cast<GrSurfaceProxy*>(this));
+}
+
+#endif
diff --git a/src/gpu/effects/GrConfigConversionEffect.cpp b/src/gpu/effects/GrConfigConversionEffect.cpp
index a11be2b..2380ad3 100644
--- a/src/gpu/effects/GrConfigConversionEffect.cpp
+++ b/src/gpu/effects/GrConfigConversionEffect.cpp
@@ -94,7 +94,6 @@
 };
 
 ///////////////////////////////////////////////////////////////////////////////
-
 GrConfigConversionEffect::GrConfigConversionEffect(GrTexture* texture,
                                                    const GrSwizzle& swizzle,
                                                    PMConversion pmConversion,
@@ -112,6 +111,24 @@
     SkASSERT(swizzle != GrSwizzle::RGBA() || kNone_PMConversion != pmConversion);
 }
 
+GrConfigConversionEffect::GrConfigConversionEffect(GrContext* context,
+                                                   sk_sp<GrTextureProxy> proxy,
+                                                   const GrSwizzle& swizzle,
+                                                   PMConversion pmConversion,
+                                                   const SkMatrix& matrix)
+    : INHERITED(context, proxy, nullptr, matrix)
+    , fSwizzle(swizzle)
+    , fPMConversion(pmConversion) {
+    this->initClassID<GrConfigConversionEffect>();
+    // We expect to get here with non-BGRA/RGBA only if we're doing not doing a premul/unpremul
+    // conversion.
+    SkASSERT((kRGBA_8888_GrPixelConfig == proxy->config() ||
+              kBGRA_8888_GrPixelConfig == proxy->config()) ||
+              kNone_PMConversion == pmConversion);
+    // Why did we pollute our texture cache instead of using a GrSingleTextureEffect?
+    SkASSERT(swizzle != GrSwizzle::RGBA() || kNone_PMConversion != pmConversion);
+}
+
 bool GrConfigConversionEffect::onIsEqual(const GrFragmentProcessor& s) const {
     const GrConfigConversionEffect& other = s.cast<GrConfigConversionEffect>();
     return other.fSwizzle == fSwizzle &&
@@ -138,9 +155,10 @@
     do {
         swizzle = GrSwizzle::CreateRandom(d->fRandom);
     } while (pmConv == kNone_PMConversion && swizzle == GrSwizzle::RGBA());
-    return sk_sp<GrFragmentProcessor>(
-        new GrConfigConversionEffect(d->fTextures[GrProcessorUnitTest::kSkiaPMTextureIdx],
-                                     swizzle, pmConv, GrTest::TestMatrix(d->fRandom)));
+    return sk_sp<GrFragmentProcessor>(new GrConfigConversionEffect(
+                            d->context(),
+                            d->textureProxy(GrProcessorUnitTest::kSkiaPMTextureIdx),
+                            swizzle, pmConv, GrTest::TestMatrix(d->fRandom)));
 }
 
 #if !defined(__clang__) && _MSC_FULL_VER >= 190024213
@@ -201,9 +219,11 @@
     desc.fWidth = kSize;
     desc.fHeight = kSize;
     desc.fConfig = kConfig;
-    sk_sp<GrTexture> dataTex(context->textureProvider()->createTexture(
-        desc, SkBudgeted::kYes, data, 0));
-    if (!dataTex.get()) {
+
+    sk_sp<GrSurfaceProxy> dataProxy = GrSurfaceProxy::MakeDeferred(*context->caps(),
+                                                                   context->textureProvider(),
+                                                                   desc, SkBudgeted::kYes, data, 0);
+    if (!dataProxy || !dataProxy->asTextureProxy()) {
         return;
     }
 
@@ -231,11 +251,14 @@
         GrPaint paint2;
         GrPaint paint3;
         sk_sp<GrFragmentProcessor> pmToUPM1(new GrConfigConversionEffect(
-                dataTex.get(), GrSwizzle::RGBA(), *pmToUPMRule, SkMatrix::I()));
+                context, sk_ref_sp(dataProxy->asTextureProxy()), GrSwizzle::RGBA(),
+                *pmToUPMRule, SkMatrix::I()));
         sk_sp<GrFragmentProcessor> upmToPM(new GrConfigConversionEffect(
-                readRTC->asTexture().get(), GrSwizzle::RGBA(), *upmToPMRule, SkMatrix::I()));
+                context, sk_ref_sp(readRTC->asDeferredTexture()), GrSwizzle::RGBA(),
+                *upmToPMRule, SkMatrix::I()));
         sk_sp<GrFragmentProcessor> pmToUPM2(new GrConfigConversionEffect(
-                tempRTC->asTexture().get(), GrSwizzle::RGBA(), *pmToUPMRule, SkMatrix::I()));
+                context, sk_ref_sp(tempRTC->asDeferredTexture()), GrSwizzle::RGBA(),
+                *pmToUPMRule, SkMatrix::I()));
 
         paint1.addColorFragmentProcessor(std::move(pmToUPM1));
         paint1.setPorterDuffXPFactory(SkBlendMode::kSrc);
@@ -299,3 +322,25 @@
             new GrConfigConversionEffect(texture, swizzle, pmConversion, matrix));
     }
 }
+
+sk_sp<GrFragmentProcessor> GrConfigConversionEffect::Make(GrContext* context,
+                                                          sk_sp<GrTextureProxy> proxy,
+                                                          const GrSwizzle& swizzle,
+                                                          PMConversion pmConversion,
+                                                          const SkMatrix& matrix) {
+    if (swizzle == GrSwizzle::RGBA() && kNone_PMConversion == pmConversion) {
+        // If we returned a GrConfigConversionEffect that was equivalent to a GrSimpleTextureEffect
+        // then we may pollute our texture cache with redundant shaders. So in the case that no
+        // conversions were requested we instead return a GrSimpleTextureEffect.
+        return GrSimpleTextureEffect::Make(context, std::move(proxy), nullptr, matrix);
+    } else {
+        if (kRGBA_8888_GrPixelConfig != proxy->config() &&
+            kBGRA_8888_GrPixelConfig != proxy->config() &&
+            kNone_PMConversion != pmConversion) {
+            // The PM conversions assume colors are 0..255
+            return nullptr;
+        }
+        return sk_sp<GrFragmentProcessor>(
+            new GrConfigConversionEffect(context, std::move(proxy), swizzle, pmConversion, matrix));
+    }
+}
diff --git a/src/gpu/effects/GrConfigConversionEffect.h b/src/gpu/effects/GrConfigConversionEffect.h
index 27361f4..46180bb 100644
--- a/src/gpu/effects/GrConfigConversionEffect.h
+++ b/src/gpu/effects/GrConfigConversionEffect.h
@@ -36,6 +36,9 @@
     static sk_sp<GrFragmentProcessor> Make(GrTexture*, const GrSwizzle&, PMConversion,
                                            const SkMatrix&);
 
+    static sk_sp<GrFragmentProcessor> Make(GrContext*, sk_sp<GrTextureProxy>,
+                                           const GrSwizzle&, PMConversion, const SkMatrix&);
+
     const char* name() const override { return "Config Conversion"; }
 
     const GrSwizzle& swizzle() const { return fSwizzle; }
@@ -51,9 +54,9 @@
                                                PMConversion* UPMToPMRule);
 
 private:
-    GrConfigConversionEffect(GrTexture*,
-                             const GrSwizzle&,
-                             PMConversion pmConversion,
+    GrConfigConversionEffect(GrTexture*, const GrSwizzle&, PMConversion, const SkMatrix& matrix);
+
+    GrConfigConversionEffect(GrContext*, sk_sp<GrTextureProxy>, const GrSwizzle&, PMConversion,
                              const SkMatrix& matrix);
 
     GrGLSLFragmentProcessor* onCreateGLSLInstance() const override;