Add clone methods to unit test GrFragmentProcessor classes

Also adds testing of copying ImageStorageAccess and ref counts of proxies held by cloned FPs.

Change-Id: Ia23220bf65b4df83d1c874b46d8525cc3540f716
Reviewed-on: https://skia-review.googlesource.com/28004
Commit-Queue: Brian Salomon <bsalomon@google.com>
Reviewed-by: Robert Phillips <robertphillips@google.com>
diff --git a/tests/GLProgramsTest.cpp b/tests/GLProgramsTest.cpp
index 8b07568..aca548c 100644
--- a/tests/GLProgramsTest.cpp
+++ b/tests/GLProgramsTest.cpp
@@ -75,6 +75,8 @@
         return new GLBigKeyProcessor;
     }
 
+    sk_sp<GrFragmentProcessor> clone() const override { return Make(); }
+
 private:
     BigKeyProcessor() : INHERITED(kNone_OptimizationFlags) { this->initClassID<BigKeyProcessor>(); }
     virtual void onGetGLSLProcessorKey(const GrShaderCaps& caps,
@@ -108,6 +110,11 @@
 
     GrGLSLFragmentProcessor* onCreateGLSLInstance() const override { return new GLFP; }
 
+    sk_sp<GrFragmentProcessor> clone() const override {
+        auto child = this->childProcessor(0).clone();
+        return child ? Make(std::move(child)) : nullptr;
+    }
+
 private:
     class GLFP : public GrGLSLFragmentProcessor {
     public:
diff --git a/tests/ImageStorageTest.cpp b/tests/ImageStorageTest.cpp
index f55553c..9a5f896 100644
--- a/tests/ImageStorageTest.cpp
+++ b/tests/ImageStorageTest.cpp
@@ -27,6 +27,10 @@
 
         const char* name() const override { return "Image Load Test FP"; }
 
+        sk_sp<GrFragmentProcessor> clone() const override {
+            return sk_sp<GrFragmentProcessor>(new TestFP(*this));
+        }
+
     private:
         TestFP(sk_sp<GrTextureProxy> proxy, GrSLMemoryModel mm, GrSLRestrict restrict)
                 : INHERITED(kNone_OptimizationFlags)
@@ -35,6 +39,13 @@
             this->addImageStorageAccess(&fImageStorageAccess);
         }
 
+        explicit TestFP(const TestFP& that)
+                : INHERITED(that.optimizationFlags())
+                , fImageStorageAccess(that.fImageStorageAccess) {
+            this->initClassID<TestFP>();
+            this->addImageStorageAccess(&fImageStorageAccess);
+        }
+
         void onGetGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const override {}
 
         bool onIsEqual(const GrFragmentProcessor& that) const override { return true; }
@@ -134,22 +145,28 @@
                 sk_sp<GrRenderTargetContext> rtContext =
                     context->makeDeferredRenderTargetContext(SkBackingFit::kExact, kS, kS,
                                                              kRGBA_8888_GrPixelConfig, nullptr);
-                GrPaint paint;
-                paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
-                paint.addColorFragmentProcessor(TestFP::Make(imageStorageTexture, mm, restrict));
-                rtContext->drawPaint(GrNoClip(), std::move(paint), SkMatrix::I());
-                std::unique_ptr<uint32_t[]> readData(new uint32_t[kS * kS]);
-                SkImageInfo info = SkImageInfo::Make(kS, kS, kRGBA_8888_SkColorType,
-                                                     kPremul_SkAlphaType);
-                rtContext->readPixels(info, readData.get(), 0, 0, 0);
-                int failed = false;
-                for (int j = 0; j < kS && !failed; ++j) {
-                    for (int i = 0; i < kS && !failed; ++i) {
-                        uint32_t d = test.fData[j * kS + i];
-                        uint32_t rd = readData[j * kS + i];
-                        if (d != rd) {
-                            failed = true;
-                            ERRORF(reporter, "Expected 0x%08x, got 0x%08x at %d, %d.", d, rd, i, j);
+                // We make a clone to test that copying GrFragmentProcessor::ImageStorageAccess
+                // copying works.
+                auto testFP = TestFP::Make(imageStorageTexture, mm, restrict);
+                for (auto fp : {testFP, testFP->clone()}) {
+                    GrPaint paint;
+                    paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
+                    paint.addColorFragmentProcessor(fp);
+                    rtContext->drawPaint(GrNoClip(), std::move(paint), SkMatrix::I());
+                    std::unique_ptr<uint32_t[]> readData(new uint32_t[kS * kS]);
+                    SkImageInfo info = SkImageInfo::Make(kS, kS, kRGBA_8888_SkColorType,
+                                                         kPremul_SkAlphaType);
+                    rtContext->readPixels(info, readData.get(), 0, 0, 0);
+                    int failed = false;
+                    for (int j = 0; j < kS && !failed; ++j) {
+                        for (int i = 0; i < kS && !failed; ++i) {
+                            uint32_t d = test.fData[j * kS + i];
+                            uint32_t rd = readData[j * kS + i];
+                            if (d != rd) {
+                                failed = true;
+                                ERRORF(reporter, "Expected 0x%08x, got 0x%08x at %d, %d.",
+                                       d, rd, i, j);
+                            }
                         }
                     }
                 }
diff --git a/tests/ProcessorTest.cpp b/tests/ProcessorTest.cpp
index ec157f9..3bfd62e 100644
--- a/tests/ProcessorTest.cpp
+++ b/tests/ProcessorTest.cpp
@@ -83,11 +83,24 @@
         b->add32(sk_atomic_inc(&gKey));
     }
 
+    sk_sp<GrFragmentProcessor> clone() const override {
+        sk_sp<GrFragmentProcessor> child;
+        if (this->numChildProcessors()) {
+            SkASSERT(1 == this->numChildProcessors());
+            child = this->childProcessor(0).clone();
+            if (!child) {
+                return nullptr;
+            }
+        }
+        return sk_sp<GrFragmentProcessor> (new TestFP(*this, std::move(child)));
+    }
+
 private:
     TestFP(const SkTArray<sk_sp<GrTextureProxy>>& proxies,
            const SkTArray<sk_sp<GrBuffer>>& buffers,
            const SkTArray<Image>& images)
             : INHERITED(kNone_OptimizationFlags), fSamplers(4), fBuffers(4), fImages(4) {
+        this->initClassID<TestFP>();
         for (const auto& proxy : proxies) {
             this->addTextureSampler(&fSamplers.emplace_back(proxy));
         }
@@ -103,9 +116,30 @@
 
     TestFP(sk_sp<GrFragmentProcessor> child)
             : INHERITED(kNone_OptimizationFlags), fSamplers(4), fBuffers(4), fImages(4) {
+        this->initClassID<TestFP>();
         this->registerChildProcessor(std::move(child));
     }
 
+    explicit TestFP(const TestFP& that, sk_sp<GrFragmentProcessor> child)
+            : INHERITED(that.optimizationFlags()), fSamplers(4), fBuffers(4), fImages(4) {
+        this->initClassID<TestFP>();
+        for (int i = 0; i < that.fSamplers.count(); ++i) {
+            fSamplers.emplace_back(that.fSamplers[i]);
+            this->addTextureSampler(&fSamplers.back());
+        }
+        for (int i = 0; i < that.fBuffers.count(); ++i) {
+            fBuffers.emplace_back(that.fBuffers[i]);
+            this->addBufferAccess(&fBuffers.back());
+        }
+        for (int i = 0; i < that.fImages.count(); ++i) {
+            fImages.emplace_back(that.fImages[i]);
+            this->addImageStorageAccess(&fImages.back());
+        }
+        if (child) {
+            this->registerChildProcessor(std::move(child));
+        }
+    }
+
     virtual GrGLSLFragmentProcessor* onCreateGLSLInstance() const override {
         class TestGLSLFP : public GrGLSLFragmentProcessor {
         public:
@@ -151,109 +185,127 @@
     desc.fHeight = 10;
     desc.fConfig = kRGBA_8888_GrPixelConfig;
 
-    for (int parentCnt = 0; parentCnt < 2; parentCnt++) {
-        sk_sp<GrRenderTargetContext> renderTargetContext(context->makeDeferredRenderTargetContext(
-                SkBackingFit::kApprox, 1, 1, kRGBA_8888_GrPixelConfig, nullptr));
-        {
-            bool texelBufferSupport = context->caps()->shaderCaps()->texelBufferSupport();
-            bool imageLoadStoreSupport = context->caps()->shaderCaps()->imageLoadStoreSupport();
-            sk_sp<GrTextureProxy> proxy1(GrSurfaceProxy::MakeDeferred(context->resourceProvider(),
-                                                                      desc, SkBackingFit::kExact,
-                                                                      SkBudgeted::kYes));
-            sk_sp<GrTextureProxy> proxy2(GrSurfaceProxy::MakeDeferred(context->resourceProvider(),
-                                                                      desc, SkBackingFit::kExact,
-                                                                      SkBudgeted::kYes));
-            sk_sp<GrTextureProxy> proxy3(GrSurfaceProxy::MakeDeferred(context->resourceProvider(),
-                                                                      desc, SkBackingFit::kExact,
-                                                                      SkBudgeted::kYes));
-            sk_sp<GrTextureProxy> proxy4(GrSurfaceProxy::MakeDeferred(context->resourceProvider(),
-                                                                      desc, SkBackingFit::kExact,
-                                                                      SkBudgeted::kYes));
-            sk_sp<GrBuffer> buffer(texelBufferSupport
-                                           ? context->resourceProvider()->createBuffer(
-                                                     1024, GrBufferType::kTexel_GrBufferType,
-                                                     GrAccessPattern::kStatic_GrAccessPattern, 0)
-                                           : nullptr);
+    for (bool makeClone : {false, true}) {
+        for (int parentCnt = 0; parentCnt < 2; parentCnt++) {
+            sk_sp<GrRenderTargetContext> renderTargetContext(
+                    context->makeDeferredRenderTargetContext( SkBackingFit::kApprox, 1, 1,
+                                                              kRGBA_8888_GrPixelConfig, nullptr));
             {
-                SkTArray<sk_sp<GrTextureProxy>> proxies;
-                SkTArray<sk_sp<GrBuffer>> buffers;
-                SkTArray<TestFP::Image> images;
-                proxies.push_back(proxy1);
+                bool texelBufferSupport = context->caps()->shaderCaps()->texelBufferSupport();
+                bool imageLoadStoreSupport = context->caps()->shaderCaps()->imageLoadStoreSupport();
+                sk_sp<GrTextureProxy> proxy1(
+                        GrSurfaceProxy::MakeDeferred(context->resourceProvider(),
+                                                     desc, SkBackingFit::kExact,
+                                                     SkBudgeted::kYes));
+                sk_sp<GrTextureProxy> proxy2
+                        (GrSurfaceProxy::MakeDeferred(context->resourceProvider(),
+                                                      desc, SkBackingFit::kExact,
+                                                      SkBudgeted::kYes));
+                sk_sp<GrTextureProxy> proxy3(
+                        GrSurfaceProxy::MakeDeferred(context->resourceProvider(),
+                                                     desc, SkBackingFit::kExact,
+                                                     SkBudgeted::kYes));
+                sk_sp<GrTextureProxy> proxy4(
+                        GrSurfaceProxy::MakeDeferred(context->resourceProvider(),
+                                                     desc, SkBackingFit::kExact,
+                                                     SkBudgeted::kYes));
+                sk_sp<GrBuffer> buffer(texelBufferSupport
+                        ? context->resourceProvider()->createBuffer(
+                                  1024, GrBufferType::kTexel_GrBufferType,
+                                  GrAccessPattern::kStatic_GrAccessPattern, 0)
+                        : nullptr);
+                {
+                    SkTArray<sk_sp<GrTextureProxy>> proxies;
+                    SkTArray<sk_sp<GrBuffer>> buffers;
+                    SkTArray<TestFP::Image> images;
+                    proxies.push_back(proxy1);
+                    if (texelBufferSupport) {
+                        buffers.push_back(buffer);
+                    }
+                    if (imageLoadStoreSupport) {
+                        images.emplace_back(proxy2, GrIOType::kRead_GrIOType);
+                        images.emplace_back(proxy3, GrIOType::kWrite_GrIOType);
+                        images.emplace_back(proxy4, GrIOType::kRW_GrIOType);
+                    }
+                    auto fp = TestFP::Make(std::move(proxies), std::move(buffers),
+                                           std::move(images));
+                    for (int i = 0; i < parentCnt; ++i) {
+                        fp = TestFP::Make(std::move(fp));
+                    }
+                    sk_sp<GrFragmentProcessor> clone;
+                    if (makeClone) {
+                        clone = fp->clone();
+                    }
+                    std::unique_ptr<GrDrawOp> op(TestOp::Make(std::move(fp)));
+                    renderTargetContext->priv().testingOnly_addDrawOp(std::move(op));
+                    if (clone) {
+                        op = TestOp::Make(std::move(clone));
+                        renderTargetContext->priv().testingOnly_addDrawOp(std::move(op));
+                    }
+                }
+                int refCnt, readCnt, writeCnt;
+
+                testingOnly_getIORefCnts(proxy1.get(), &refCnt, &readCnt, &writeCnt);
+                // IO counts should be double if there is a clone of the FP.
+                int ioRefMul = makeClone ? 2 : 1;
+                REPORTER_ASSERT(reporter, 1 == refCnt);
+                REPORTER_ASSERT(reporter, ioRefMul * 1 == readCnt);
+                REPORTER_ASSERT(reporter, ioRefMul * 0 == writeCnt);
+
                 if (texelBufferSupport) {
-                    buffers.push_back(buffer);
+                    testingOnly_getIORefCnts(buffer.get(), &refCnt, &readCnt, &writeCnt);
+                    REPORTER_ASSERT(reporter, 1 == refCnt);
+                    REPORTER_ASSERT(reporter, ioRefMul * 1 == readCnt);
+                    REPORTER_ASSERT(reporter, ioRefMul *  0 == writeCnt);
                 }
+
                 if (imageLoadStoreSupport) {
-                    images.emplace_back(proxy2, GrIOType::kRead_GrIOType);
-                    images.emplace_back(proxy3, GrIOType::kWrite_GrIOType);
-                    images.emplace_back(proxy4, GrIOType::kRW_GrIOType);
+                    testingOnly_getIORefCnts(proxy2.get(), &refCnt, &readCnt, &writeCnt);
+                    REPORTER_ASSERT(reporter, 1 == refCnt);
+                    REPORTER_ASSERT(reporter, ioRefMul * 1 == readCnt);
+                    REPORTER_ASSERT(reporter, ioRefMul * 0 == writeCnt);
+
+                    testingOnly_getIORefCnts(proxy3.get(), &refCnt, &readCnt, &writeCnt);
+                    REPORTER_ASSERT(reporter, 1 == refCnt);
+                    REPORTER_ASSERT(reporter, ioRefMul * 0 == readCnt);
+                    REPORTER_ASSERT(reporter, ioRefMul * 1 == writeCnt);
+
+                    testingOnly_getIORefCnts(proxy4.get(), &refCnt, &readCnt, &writeCnt);
+                    REPORTER_ASSERT(reporter, 1 == refCnt);
+                    REPORTER_ASSERT(reporter, ioRefMul * 1 == readCnt);
+                    REPORTER_ASSERT(reporter, ioRefMul * 1 == writeCnt);
                 }
-                auto fp = TestFP::Make(std::move(proxies), std::move(buffers), std::move(images));
-                for (int i = 0; i < parentCnt; ++i) {
-                    fp = TestFP::Make(std::move(fp));
+
+                context->flush();
+
+                testingOnly_getIORefCnts(proxy1.get(), &refCnt, &readCnt, &writeCnt);
+                REPORTER_ASSERT(reporter, 1 == refCnt);
+                REPORTER_ASSERT(reporter, ioRefMul * 0 == readCnt);
+                REPORTER_ASSERT(reporter, ioRefMul * 0 == writeCnt);
+
+                if (texelBufferSupport) {
+                    testingOnly_getIORefCnts(buffer.get(), &refCnt, &readCnt, &writeCnt);
+                    REPORTER_ASSERT(reporter, 1 == refCnt);
+                    REPORTER_ASSERT(reporter, ioRefMul * 0 == readCnt);
+                    REPORTER_ASSERT(reporter, ioRefMul * 0 == writeCnt);
                 }
-                std::unique_ptr<GrDrawOp> op(TestOp::Make(std::move(fp)));
-                renderTargetContext->priv().testingOnly_addDrawOp(std::move(op));
-            }
-            int refCnt, readCnt, writeCnt;
 
-            testingOnly_getIORefCnts(proxy1.get(), &refCnt, &readCnt, &writeCnt);
-            REPORTER_ASSERT(reporter, 1 == refCnt);
-            REPORTER_ASSERT(reporter, 1 == readCnt);
-            REPORTER_ASSERT(reporter, 0 == writeCnt);
+                if (texelBufferSupport) {
+                    testingOnly_getIORefCnts(proxy2.get(), &refCnt, &readCnt, &writeCnt);
+                    REPORTER_ASSERT(reporter, 1 == refCnt);
+                    REPORTER_ASSERT(reporter, ioRefMul * 0 == readCnt);
+                    REPORTER_ASSERT(reporter, ioRefMul * 0 == writeCnt);
 
-            if (texelBufferSupport) {
-                testingOnly_getIORefCnts(buffer.get(), &refCnt, &readCnt, &writeCnt);
-                REPORTER_ASSERT(reporter, 1 == refCnt);
-                REPORTER_ASSERT(reporter, 1 == readCnt);
-                REPORTER_ASSERT(reporter, 0 == writeCnt);
-            }
+                    testingOnly_getIORefCnts(proxy3.get(), &refCnt, &readCnt, &writeCnt);
+                    REPORTER_ASSERT(reporter, 1 == refCnt);
+                    REPORTER_ASSERT(reporter, ioRefMul * 0 == readCnt);
+                    REPORTER_ASSERT(reporter, ioRefMul * 0 == writeCnt);
 
-            if (imageLoadStoreSupport) {
-                testingOnly_getIORefCnts(proxy2.get(), &refCnt, &readCnt, &writeCnt);
-                REPORTER_ASSERT(reporter, 1 == refCnt);
-                REPORTER_ASSERT(reporter, 1 == readCnt);
-                REPORTER_ASSERT(reporter, 0 == writeCnt);
-
-                testingOnly_getIORefCnts(proxy3.get(), &refCnt, &readCnt, &writeCnt);
-                REPORTER_ASSERT(reporter, 1 == refCnt);
-                REPORTER_ASSERT(reporter, 0 == readCnt);
-                REPORTER_ASSERT(reporter, 1 == writeCnt);
-
-                testingOnly_getIORefCnts(proxy4.get(), &refCnt, &readCnt, &writeCnt);
-                REPORTER_ASSERT(reporter, 1 == refCnt);
-                REPORTER_ASSERT(reporter, 1 == readCnt);
-                REPORTER_ASSERT(reporter, 1 == writeCnt);
-            }
-
-            context->flush();
-
-            testingOnly_getIORefCnts(proxy1.get(), &refCnt, &readCnt, &writeCnt);
-            REPORTER_ASSERT(reporter, 1 == refCnt);
-            REPORTER_ASSERT(reporter, 0 == readCnt);
-            REPORTER_ASSERT(reporter, 0 == writeCnt);
-
-            if (texelBufferSupport) {
-                testingOnly_getIORefCnts(buffer.get(), &refCnt, &readCnt, &writeCnt);
-                REPORTER_ASSERT(reporter, 1 == refCnt);
-                REPORTER_ASSERT(reporter, 0 == readCnt);
-                REPORTER_ASSERT(reporter, 0 == writeCnt);
-            }
-
-            if (texelBufferSupport) {
-                testingOnly_getIORefCnts(proxy2.get(), &refCnt, &readCnt, &writeCnt);
-                REPORTER_ASSERT(reporter, 1 == refCnt);
-                REPORTER_ASSERT(reporter, 0 == readCnt);
-                REPORTER_ASSERT(reporter, 0 == writeCnt);
-
-                testingOnly_getIORefCnts(proxy3.get(), &refCnt, &readCnt, &writeCnt);
-                REPORTER_ASSERT(reporter, 1 == refCnt);
-                REPORTER_ASSERT(reporter, 0 == readCnt);
-                REPORTER_ASSERT(reporter, 0 == writeCnt);
-
-                testingOnly_getIORefCnts(proxy4.get(), &refCnt, &readCnt, &writeCnt);
-                REPORTER_ASSERT(reporter, 1 == refCnt);
-                REPORTER_ASSERT(reporter, 0 == readCnt);
-                REPORTER_ASSERT(reporter, 0 == writeCnt);
+                    testingOnly_getIORefCnts(proxy4.get(), &refCnt, &readCnt, &writeCnt);
+                    REPORTER_ASSERT(reporter, 1 == refCnt);
+                    REPORTER_ASSERT(reporter, ioRefMul * 0 == readCnt);
+                    REPORTER_ASSERT(reporter, ioRefMul * 0 == writeCnt);
+                }
             }
         }
     }