Some changes to GrYUVToRGBEffect.

Don't implement domains, use child effects instead.

Don't do a 4x4 matrix mul. Do 3x3 + a translate.

Undo some of effects of this effect's stint as .fp generated code.

Specify optimization flags using resulting alpha type.

Multiply computed color with input color (so this is more interchangeable
with GrSimpleTextureEffect used for non-planar texture images)

Change-Id: I9d3d0ff7aed9177cd32ab16e9ceb67d458c3d88c
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/257883
Reviewed-by: Michael Ludwig <michaelludwig@google.com>
Commit-Queue: Brian Salomon <bsalomon@google.com>
diff --git a/src/gpu/effects/GrYUVtoRGBEffect.cpp b/src/gpu/effects/GrYUVtoRGBEffect.cpp
index f33c397..8af97f1 100644
--- a/src/gpu/effects/GrYUVtoRGBEffect.cpp
+++ b/src/gpu/effects/GrYUVtoRGBEffect.cpp
@@ -31,30 +31,68 @@
     GrSamplerState::Filter subsampledPlaneFilterMode = GrSamplerState::Filter::kMipMap == filterMode
                                                                ? GrSamplerState::Filter::kMipMap
                                                                : GrSamplerState::Filter::kBilerp;
-
-    GrSamplerState::Filter filterModes[4];
-    SkSize scales[4];
+    std::unique_ptr<GrFragmentProcessor> planeFPs[4];
     for (int i = 0; i < numPlanes; ++i) {
         SkISize dimensions = proxies[i]->dimensions();
-        // JPEG chroma subsampling of odd dimensions produces U and V planes with the ceiling of
-        // the image size divided by the subsampling factor (2). Our API for creating YUVA doesn't
-        // capture the intended subsampling (and we should fix that). This fixes up 2x subsampling
-        // for images with odd widths/heights (e.g. JPEG 420 or 422).
-        scales[i] = {dimensions.width()  / SkIntToScalar(YDimensions.width()),
-                     dimensions.height() / SkIntToScalar(YDimensions.height())};
-        if ((YDimensions.width() & 0b1) && dimensions.width() == YDimensions.width() / 2 + 1) {
-            scales[i].fWidth = 0.5f;
+        SkTCopyOnFirstWrite<SkMatrix> planeMatrix(&localMatrix);
+        GrSamplerState::Filter planeFilter = filterMode;
+        SkRect planeDomain;
+        if (dimensions != YDimensions) {
+            // JPEG chroma subsampling of odd dimensions produces U and V planes with the ceiling of
+            // the image size divided by the subsampling factor (2). Our API for creating YUVA
+            // doesn't capture the intended subsampling (and we should fix that). This fixes up 2x
+            // subsampling for images with odd widths/heights (e.g. JPEG 420 or 422).
+            float sx = (float)dimensions.width() / YDimensions.width();
+            float sy = (float)dimensions.height() / YDimensions.height();
+            if ((YDimensions.width() & 0b1) && dimensions.width() == YDimensions.width() / 2 + 1) {
+                sx = 0.5f;
+            }
+            if ((YDimensions.height() & 0b1) &&
+                dimensions.height() == YDimensions.height() / 2 + 1) {
+                sy = 0.5f;
+            }
+            *planeMatrix.writable() = SkMatrix::MakeScale(sx, sy);
+            planeMatrix.writable()->preConcat(localMatrix);
+            planeFilter = subsampledPlaneFilterMode;
+            if (domain) {
+                planeDomain = {domain->fLeft   * sx,
+                               domain->fTop    * sy,
+                               domain->fRight  * sx,
+                               domain->fBottom * sy};
+            }
+        } else if (domain) {
+            planeDomain = *domain;
         }
-        if ((YDimensions.height() & 0b1) && dimensions.height() == YDimensions.height() / 2 + 1) {
-            scales[i].fHeight = 0.5f;
+        planeFPs[i] = GrSimpleTextureEffect::Make(proxies[i], kUnknown_SkAlphaType, *planeMatrix,
+                                                  planeFilter);
+        if (domain) {
+            SkASSERT(planeFilter != GrSamplerState::Filter::kMipMap);
+            if (planeFilter != GrSamplerState::Filter::kNearest) {
+                // Inset by half a pixel for bilerp, after scaling to the size of the plane
+                planeDomain.inset(0.5f, 0.5f);
+            }
+            planeFPs[i] = GrDomainEffect::Make(std::move(planeFPs[i]), planeDomain,
+                                               GrTextureDomain::kClamp_Mode, false);
         }
-        // It seems the assumption here is the plane dimensions are smaller than the Y plane.
-        filterModes[i] = (dimensions == YDimensions) ? filterMode : subsampledPlaneFilterMode;
     }
 
-    return std::unique_ptr<GrFragmentProcessor>(new GrYUVtoRGBEffect(
-            proxies, scales, filterModes, numPlanes, yuvaIndices, yuvColorSpace, localMatrix,
-            domain));
+    return std::unique_ptr<GrFragmentProcessor>(
+            new GrYUVtoRGBEffect(planeFPs, numPlanes, yuvaIndices, yuvColorSpace));
+}
+
+static SkAlphaType alpha_type(const SkYUVAIndex yuvaIndices[4]) {
+    return yuvaIndices[3].fIndex >= 0 ? kPremul_SkAlphaType : kOpaque_SkAlphaType;
+}
+
+GrYUVtoRGBEffect::GrYUVtoRGBEffect(std::unique_ptr<GrFragmentProcessor> planeFPs[4], int numPlanes,
+                                   const SkYUVAIndex yuvaIndices[4], SkYUVColorSpace yuvColorSpace)
+        : GrFragmentProcessor(kGrYUVtoRGBEffect_ClassID,
+                              ModulateForClampedSamplerOptFlags(alpha_type(yuvaIndices)))
+        , fYUVColorSpace(yuvColorSpace) {
+    for (int i = 0; i < numPlanes; ++i) {
+        this->registerChildProcessor(std::move(planeFPs[i]));
+    }
+    std::copy_n(yuvaIndices, 4, fYUVAIndices);
 }
 
 #ifdef SK_DEBUG
@@ -78,85 +116,77 @@
 
         void emitCode(EmitArgs& args) override {
             GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
-            const GrYUVtoRGBEffect& _outer = args.fFp.cast<GrYUVtoRGBEffect>();
-            (void)_outer;
+            const GrYUVtoRGBEffect& yuvEffect = args.fFp.cast<GrYUVtoRGBEffect>();
 
-            if (kIdentity_SkYUVColorSpace != _outer.yuvColorSpace()) {
-                fColorSpaceMatrixVar = args.fUniformHandler->addUniform(kFragment_GrShaderFlag,
-                                                                        kHalf4x4_GrSLType,
-                                                                        "colorSpaceMatrix");
-            }
-
-            int numSamplers = args.fTexSamplers.count();
+            int numPlanes = yuvEffect.numChildProcessors();
 
             SkString coords[4];
-            for (int i = 0; i < numSamplers; ++i) {
-                coords[i] = fragBuilder->ensureCoords2D(args.fTransformedCoords[i].fVaryingPoint);
+            fragBuilder->codeAppendf("half4 planes[%d];", numPlanes);
+            for (int i = 0; i < numPlanes; ++i) {
+                SkString tempVar;
+                tempVar.printf("tmp%d", i);
+                this->invokeChild(i, &tempVar, args);
+                fragBuilder->codeAppendf("planes[%d] = %s;", i, tempVar.c_str());
             }
 
-            for (int i = 0; i < numSamplers; ++i) {
-                SkString sampleVar;
-                sampleVar.printf("tmp%d", i);
-                fragBuilder->codeAppendf("half4 %s;", sampleVar.c_str());
-                fGLDomains[i].sampleTexture(fragBuilder, args.fUniformHandler, args.fShaderCaps,
-                        _outer.fDomains[i], sampleVar.c_str(), coords[i], args.fTexSamplers[i]);
+            bool hasAlpha = yuvEffect.fYUVAIndices[3].fIndex >= 0;
+            SkString rgba[4];
+            rgba[3] = "1";
+            for (int i = 0; i < (hasAlpha ? 4 : 3); ++i) {
+                auto info = yuvEffect.fYUVAIndices[i];
+                auto letter = "rgba"[static_cast<int>(info.fChannel)];
+                rgba[i].printf("planes[%d].%c", info.fIndex, letter);
             }
 
-            static const char kChannelToChar[4] = { 'x', 'y', 'z', 'w' };
+            fragBuilder->codeAppendf("half4 color = half4(%s, %s, %s, %s);",
+                    rgba[0].c_str(), rgba[1].c_str(), rgba[2].c_str(), rgba[3].c_str());
 
-            fragBuilder->codeAppendf(
-                "half4 yuvOne = half4(tmp%d.%c, tmp%d.%c, tmp%d.%c, 1.0);",
-                    _outer.yuvaIndex(0).fIndex, kChannelToChar[(int)_outer.yuvaIndex(0).fChannel],
-                    _outer.yuvaIndex(1).fIndex, kChannelToChar[(int)_outer.yuvaIndex(1).fChannel],
-                    _outer.yuvaIndex(2).fIndex, kChannelToChar[(int)_outer.yuvaIndex(2).fChannel]);
-
-            if (kIdentity_SkYUVColorSpace != _outer.yuvColorSpace()) {
-                SkASSERT(fColorSpaceMatrixVar.isValid());
+            if (kIdentity_SkYUVColorSpace != yuvEffect.fYUVColorSpace) {
+                fColorSpaceMatrixVar = args.fUniformHandler->addUniform(
+                        kFragment_GrShaderFlag, kHalf3x3_GrSLType, "colorSpaceMatrix");
+                fColorSpaceTranslateVar = args.fUniformHandler->addUniform(
+                        kFragment_GrShaderFlag, kHalf3_GrSLType, "colorSpaceTranslate");
                 fragBuilder->codeAppendf(
-                    "yuvOne *= %s;", args.fUniformHandler->getUniformCStr(fColorSpaceMatrixVar));
-                fragBuilder->codeAppend("yuvOne.xyz = clamp(yuvOne.xyz, 0, 1);");
+                        "color.rgb = saturate(color.rgb * %s + %s);",
+                        args.fUniformHandler->getUniformCStr(fColorSpaceMatrixVar),
+                        args.fUniformHandler->getUniformCStr(fColorSpaceTranslateVar));
             }
 
-            if (_outer.yuvaIndex(3).fIndex >= 0) {
-                fragBuilder->codeAppendf(
-                    "half a = tmp%d.%c;", _outer.yuvaIndex(3).fIndex,
-                                           kChannelToChar[(int)_outer.yuvaIndex(3).fChannel]);
+            if (hasAlpha) {
                 // premultiply alpha
-                fragBuilder->codeAppend("yuvOne *= a;");
-            } else {
-                fragBuilder->codeAppend("half a = 1.0;");
+                fragBuilder->codeAppendf("color.rgb *= color.a;");
             }
-
-            fragBuilder->codeAppendf("%s = half4(yuvOne.xyz, a);", args.fOutputColor);
+            fragBuilder->codeAppendf("%s = color;", args.fOutputColor);
         }
 
     private:
         void onSetData(const GrGLSLProgramDataManager& pdman,
-                       const GrFragmentProcessor& _proc) override {
-            const GrYUVtoRGBEffect& _outer = _proc.cast<GrYUVtoRGBEffect>();
+                       const GrFragmentProcessor& proc) override {
+            const GrYUVtoRGBEffect& yuvEffect = proc.cast<GrYUVtoRGBEffect>();
 
-            if (_outer.yuvColorSpace() != kIdentity_SkYUVColorSpace) {
+            if (yuvEffect.fYUVColorSpace != kIdentity_SkYUVColorSpace) {
                 SkASSERT(fColorSpaceMatrixVar.isValid());
                 float yuvM[20];
-                SkColorMatrix_YUV2RGB(_outer.yuvColorSpace(), yuvM);
-                // Need to drop the fourth column to go to 4x4
-                float mtx[16] = {
-                    yuvM[ 0], yuvM[ 1], yuvM[ 2], yuvM[ 4],
-                    yuvM[ 5], yuvM[ 6], yuvM[ 7], yuvM[ 9],
-                    yuvM[10], yuvM[11], yuvM[12], yuvM[14],
-                    yuvM[15], yuvM[16], yuvM[17], yuvM[19],
+                SkColorMatrix_YUV2RGB(yuvEffect.fYUVColorSpace, yuvM);
+                // We drop the fourth column entirely since the transformation
+                // should not depend on alpha. The fifth column is sent as a separate
+                // vector. The fourth row is also dropped entirely because alpha should
+                // never be modified.
+                SkASSERT(yuvM[3] == 0 && yuvM[8] == 0 && yuvM[13] == 0 && yuvM[18] == 1);
+                SkASSERT(yuvM[15] == 0 && yuvM[16] == 0 && yuvM[17] == 0 && yuvM[19] == 0);
+                float mtx[9] = {
+                    yuvM[ 0], yuvM[ 1], yuvM[ 2],
+                    yuvM[ 5], yuvM[ 6], yuvM[ 7],
+                    yuvM[10], yuvM[11], yuvM[12],
                 };
-                pdman.setMatrix4f(fColorSpaceMatrixVar, mtx);
-            }
-
-            int numSamplers = _outer.numTextureSamplers();
-            for (int i = 0; i < numSamplers; ++i) {
-                fGLDomains[i].setData(pdman, _outer.fDomains[i],
-                        _outer.textureSampler(i).proxy(), _outer.textureSampler(i).samplerState());
+                float v[3] = {yuvM[4], yuvM[9], yuvM[14]};
+                pdman.setMatrix3f(fColorSpaceMatrixVar, mtx);
+                pdman.set3fv(fColorSpaceTranslateVar, 1, v);
             }
         }
 
         UniformHandle fColorSpaceMatrixVar;
+        UniformHandle fColorSpaceTranslateVar;
         GrTextureDomain::GLDomain fGLDomains[4];
     };
 
@@ -164,33 +194,25 @@
 }
 void GrYUVtoRGBEffect::onGetGLSLProcessorKey(const GrShaderCaps& caps,
                                              GrProcessorKeyBuilder* b) const {
-    using Domain = GrTextureDomain::GLDomain;
-
-    b->add32(this->numTextureSamplers());
-
     uint32_t packed = 0;
-    uint32_t domain = 0;
     for (int i = 0; i < 4; ++i) {
-        if (this->yuvaIndex(i).fIndex < 0) {
+        if (fYUVAIndices[i].fIndex < 0) {
             continue;
         }
 
-        uint8_t index = this->yuvaIndex(i).fIndex;
-        uint8_t chann = (uint8_t) this->yuvaIndex(i).fChannel;
+        uint8_t index = fYUVAIndices[i].fIndex;
+        uint8_t chann = static_cast<int>(fYUVAIndices[i].fChannel);
 
         SkASSERT(index < 4 && chann < 4);
 
         packed |= (index | (chann << 2)) << (i * 4);
-
-        domain |= Domain::DomainKey(fDomains[i]) << (i * Domain::kDomainKeyBits);
     }
-    if (kIdentity_SkYUVColorSpace == this->yuvColorSpace()) {
+    if (fYUVColorSpace == kIdentity_SkYUVColorSpace) {
         packed |= 0x1 << 16;
     }
-
     b->add32(packed);
-    b->add32(domain);
 }
+
 bool GrYUVtoRGBEffect::onIsEqual(const GrFragmentProcessor& other) const {
     const GrYUVtoRGBEffect& that = other.cast<GrYUVtoRGBEffect>();
 
@@ -200,44 +222,23 @@
         }
     }
 
-    for (int i = 0; i < this->numTextureSamplers(); ++i) {
-        // 'fSamplers' is checked by the base class
-        if (fSamplerTransforms[i] != that.fSamplerTransforms[i]) {
-            return false;
-        }
-        if (!(fDomains[i] == that.fDomains[i])) {
-            return false;
-        }
-    }
-
     if (fYUVColorSpace != that.fYUVColorSpace) {
         return false;
     }
 
     return true;
 }
+
 GrYUVtoRGBEffect::GrYUVtoRGBEffect(const GrYUVtoRGBEffect& src)
-        : INHERITED(kGrYUVtoRGBEffect_ClassID, src.optimizationFlags())
-        , fDomains{src.fDomains[0], src.fDomains[1], src.fDomains[2], src.fDomains[3]}
+        : GrFragmentProcessor(kGrYUVtoRGBEffect_ClassID, src.optimizationFlags())
         , fYUVColorSpace(src.fYUVColorSpace) {
-    int numPlanes = src.numTextureSamplers();
+    int numPlanes = src.numChildProcessors();
     for (int i = 0; i < numPlanes; ++i) {
-        fSamplers[i].reset(sk_ref_sp(src.fSamplers[i].proxy()), src.fSamplers[i].samplerState());
-        fSamplerTransforms[i] = src.fSamplerTransforms[i];
-        fSamplerCoordTransforms[i] = src.fSamplerCoordTransforms[i];
+        this->registerChildProcessor(this->childProcessor(i).clone());
     }
-
-    this->setTextureSamplerCnt(numPlanes);
-    for (int i = 0; i < numPlanes; ++i) {
-        this->addCoordTransform(&fSamplerCoordTransforms[i]);
-    }
-
-    memcpy(fYUVAIndices, src.fYUVAIndices, sizeof(fYUVAIndices));
+    std::copy_n(src.fYUVAIndices, this->numChildProcessors(), fYUVAIndices);
 }
+
 std::unique_ptr<GrFragmentProcessor> GrYUVtoRGBEffect::clone() const {
     return std::unique_ptr<GrFragmentProcessor>(new GrYUVtoRGBEffect(*this));
 }
-const GrFragmentProcessor::TextureSampler& GrYUVtoRGBEffect::onTextureSampler(int index) const {
-    SkASSERT(index < this->numTextureSamplers());
-    return fSamplers[index];
-}
diff --git a/src/gpu/effects/GrYUVtoRGBEffect.h b/src/gpu/effects/GrYUVtoRGBEffect.h
index 338ea14..b99de32 100644
--- a/src/gpu/effects/GrYUVtoRGBEffect.h
+++ b/src/gpu/effects/GrYUVtoRGBEffect.h
@@ -32,65 +32,25 @@
     SkString dumpInfo() const override;
 #endif
 
-    SkYUVColorSpace yuvColorSpace() const { return fYUVColorSpace; }
-    const SkYUVAIndex& yuvaIndex(int i) const { return fYUVAIndices[i]; }
-
-    GrYUVtoRGBEffect(const GrYUVtoRGBEffect& src);
     std::unique_ptr<GrFragmentProcessor> clone() const override;
+
     const char* name() const override { return "YUVtoRGBEffect"; }
 
 private:
-    GrYUVtoRGBEffect(const sk_sp<GrTextureProxy> proxies[], const SkSize scales[],
-                     const GrSamplerState::Filter filterModes[], int numPlanes,
-                     const SkYUVAIndex yuvaIndices[4], SkYUVColorSpace yuvColorSpace,
-                     const SkMatrix& localMatrix, const SkRect* domain)
-            : INHERITED(kGrYUVtoRGBEffect_ClassID, kNone_OptimizationFlags)
-            , fDomains{GrTextureDomain::IgnoredDomain(), GrTextureDomain::IgnoredDomain(),
-                       GrTextureDomain::IgnoredDomain(), GrTextureDomain::IgnoredDomain()}
-            , fYUVColorSpace(yuvColorSpace) {
-        for (int i = 0; i < numPlanes; ++i) {
-            SkMatrix planeMatrix = SkMatrix::MakeScale(scales[i].width(), scales[i].height());
-            if (domain) {
-                SkASSERT(filterModes[i] != GrSamplerState::Filter::kMipMap);
+    GrYUVtoRGBEffect(std::unique_ptr<GrFragmentProcessor> planeFPs[4], int numPlanes,
+                     const SkYUVAIndex yuvaIndices[4], SkYUVColorSpace yuvColorSpace);
 
-                SkRect scaledDomain = planeMatrix.mapRect(*domain);
-                if (filterModes[i] != GrSamplerState::Filter::kNearest) {
-                    // Inset by half a pixel for bilerp, after scaling to the size of the plane
-                    scaledDomain.inset(0.5f, 0.5f);
-                }
+    GrYUVtoRGBEffect(const GrYUVtoRGBEffect& src);
 
-                fDomains[i] = GrTextureDomain(proxies[i].get(), scaledDomain,
-                        GrTextureDomain::kClamp_Mode, GrTextureDomain::kClamp_Mode, i);
-            }
-
-            planeMatrix.preConcat(localMatrix);
-            fSamplers[i].reset(std::move(proxies[i]),
-                               GrSamplerState(GrSamplerState::WrapMode::kClamp, filterModes[i]));
-            fSamplerTransforms[i] = planeMatrix;
-            fSamplerCoordTransforms[i] =
-                    GrCoordTransform(fSamplerTransforms[i], fSamplers[i].proxy());
-        }
-
-        this->setTextureSamplerCnt(numPlanes);
-        for (int i = 0; i < numPlanes; ++i) {
-            this->addCoordTransform(&fSamplerCoordTransforms[i]);
-        }
-
-        memcpy(fYUVAIndices, yuvaIndices, sizeof(fYUVAIndices));
-    }
     GrGLSLFragmentProcessor* onCreateGLSLInstance() const override;
+
     void onGetGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const override;
+
     bool onIsEqual(const GrFragmentProcessor&) const override;
-    const TextureSampler& onTextureSampler(int) const override;
+
     GR_DECLARE_FRAGMENT_PROCESSOR_TEST
 
-    TextureSampler   fSamplers[4];
-    SkMatrix44       fSamplerTransforms[4];
-    GrCoordTransform fSamplerCoordTransforms[4];
-    GrTextureDomain  fDomains[4];
     SkYUVAIndex      fYUVAIndices[4];
     SkYUVColorSpace  fYUVColorSpace;
-
-    typedef GrFragmentProcessor INHERITED;
 };
 #endif