Implement inverse fill support in GrTessellationPathRenderer

Bug: skia:10419
Bug: skia:11396
Change-Id: I489236ec5dff59b0e7c4c4e22b83b2b5ec4e9a26
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/421796
Reviewed-by: Brian Salomon <bsalomon@google.com>
Commit-Queue: Chris Dalton <csmartdalton@google.com>
diff --git a/src/gpu/tessellate/GrDrawAtlasPathOp.cpp b/src/gpu/tessellate/GrDrawAtlasPathOp.cpp
index d2cb90e..78c5253 100644
--- a/src/gpu/tessellate/GrDrawAtlasPathOp.cpp
+++ b/src/gpu/tessellate/GrDrawAtlasPathOp.cpp
@@ -10,6 +10,7 @@
 #include "src/gpu/GrOpFlushState.h"
 #include "src/gpu/GrOpsRenderPass.h"
 #include "src/gpu/GrProgramInfo.h"
+#include "src/gpu/GrVertexWriter.h"
 #include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h"
 #include "src/gpu/glsl/GrGLSLGeometryProcessor.h"
 #include "src/gpu/glsl/GrGLSLVarying.h"
@@ -17,25 +18,27 @@
 
 namespace {
 
-constexpr static GrGeometryProcessor::Attribute kInstanceAttribs[] = {
-        {"dev_xywh", kInt4_GrVertexAttribType, kInt4_GrSLType},
-        {"atlas_xy", kInt2_GrVertexAttribType, kInt2_GrSLType},
-        {"color", kFloat4_GrVertexAttribType, kHalf4_GrSLType},
-        {"viewmatrix_scaleskew", kFloat4_GrVertexAttribType, kFloat4_GrSLType},
-        {"viewmatrix_trans", kFloat2_GrVertexAttribType, kFloat2_GrSLType}};
-
 class DrawAtlasPathShader : public GrGeometryProcessor {
 public:
-    DrawAtlasPathShader(const GrTextureProxy* atlasProxy, GrSwizzle swizzle, bool usesLocalCoords)
+    DrawAtlasPathShader(const GrTextureProxy* atlasProxy, GrSwizzle swizzle, bool isInverseFill,
+                        bool usesLocalCoords)
             : GrGeometryProcessor(kDrawAtlasPathShader_ClassID)
             , fAtlasAccess(GrSamplerState::Filter::kNearest, atlasProxy->backendFormat(), swizzle)
             , fAtlasDimensions(atlasProxy->backingStoreDimensions())
+            , fIsInverseFill(isInverseFill)
             , fUsesLocalCoords(usesLocalCoords) {
-        int numInstanceAttribs = SK_ARRAY_COUNT(kInstanceAttribs);
-        if (!fUsesLocalCoords) {
-            numInstanceAttribs -= 2;
+        fAttribs.emplace_back("dev_xywh", kInt4_GrVertexAttribType, kInt4_GrSLType);
+        fAttribs.emplace_back("atlas_xy", kInt2_GrVertexAttribType, kInt2_GrSLType);
+        fAttribs.emplace_back("color", kFloat4_GrVertexAttribType, kHalf4_GrSLType);
+        if (fIsInverseFill) {
+            fAttribs.emplace_back("drawbounds", kFloat4_GrVertexAttribType, kFloat4_GrSLType);
         }
-        this->setInstanceAttributes(kInstanceAttribs, numInstanceAttribs);
+        if (fUsesLocalCoords) {
+            fAttribs.emplace_back("viewmatrix_scaleskew", kFloat4_GrVertexAttribType,
+                                  kFloat4_GrSLType);
+            fAttribs.emplace_back("viewmatrix_trans", kFloat2_GrVertexAttribType, kFloat2_GrSLType);
+        }
+        this->setInstanceAttributes(fAttribs.data(), fAttribs.count());
         this->setTextureSamplerCnt(1);
     }
 
@@ -49,7 +52,9 @@
 
     const TextureSampler fAtlasAccess;
     const SkISize fAtlasDimensions;
+    const bool fIsInverseFill;
     const bool fUsesLocalCoords;
+    SkSTArray<6, GrGeometryProcessor::Attribute> fAttribs;
 
     class Impl;
 };
@@ -65,7 +70,7 @@
         GrGLSLVarying color(kHalf4_GrSLType);
         args.fFragBuilder->codeAppendf("half4 %s;", args.fOutputColor);
         args.fVaryingHandler->addPassThroughAttribute(
-                kInstanceAttribs[2], args.fOutputColor,
+                shader.fAttribs[2], args.fOutputColor,
                 GrGLSLVaryingHandler::Interpolation::kCanBeFlat);
 
         const char* atlasAdjust;
@@ -73,29 +78,61 @@
                 nullptr, kVertex_GrShaderFlag, kFloat2_GrSLType, "atlas_adjust", &atlasAdjust);
 
         args.fVertBuilder->codeAppendf(R"(
-                float2 T = float2(sk_VertexID & 1, sk_VertexID >> 1);
-                float2 devtopleft = float2(dev_xywh.xy);
-                float2 devcoord = abs(float2(dev_xywh.zw)) * T + devtopleft;
-                float2 atlascoord = devcoord - devtopleft;
-                if (dev_xywh.w < 0) {  // Negative height indicates that the path is transposed.
-                    atlascoord = atlascoord.yx;
-                }
-                atlascoord += float2(atlas_xy);
-                %s = atlascoord * %s;)",
-                atlasCoord.vsOut(), atlasAdjust);
+        float2 T = float2(sk_VertexID & 1, sk_VertexID >> 1);
+        float2 devtopleft = float2(dev_xywh.xy);)");
+
+        if (shader.fIsInverseFill) {
+            args.fVertBuilder->codeAppendf(R"(
+            float2 devcoord = mix(drawbounds.xy, drawbounds.zw, T);)");
+        } else {
+            args.fVertBuilder->codeAppendf(R"(
+            float2 devcoord = abs(float2(dev_xywh.zw)) * T + devtopleft;)");
+        }
+
+        args.fVertBuilder->codeAppendf(R"(
+        float2 atlascoord = devcoord - devtopleft;
+        bool transposed = dev_xywh.w < 0;  // Negative height means the path is transposed.
+        if (transposed) {
+            atlascoord = atlascoord.yx;
+        }
+        atlascoord += float2(atlas_xy);
+        %s = atlascoord * %s;)",
+        atlasCoord.vsOut(), atlasAdjust);
 
         gpArgs->fPositionVar.set(kFloat2_GrSLType, "devcoord");
 
         if (shader.fUsesLocalCoords) {
             args.fVertBuilder->codeAppendf(R"(
-                    float2x2 M = float2x2(viewmatrix_scaleskew);
-                    float2 localcoord = inverse(M) * (devcoord - viewmatrix_trans);)");
+            float2x2 M = float2x2(viewmatrix_scaleskew);
+            float2 localcoord = inverse(M) * (devcoord - viewmatrix_trans);)");
             gpArgs->fLocalCoordVar.set(kFloat2_GrSLType, "localcoord");
         }
 
-        args.fFragBuilder->codeAppendf("half4 %s = ", args.fOutputCoverage);
-        args.fFragBuilder->appendTextureLookup(args.fTexSamplers[0], atlasCoord.fsIn());
-        args.fFragBuilder->codeAppendf(".aaaa;");
+        if (shader.fIsInverseFill) {
+            GrGLSLVarying atlasBounds(kFloat4_GrSLType);
+            args.fVaryingHandler->addVarying("atlasbounds", &atlasBounds,
+                                             GrGLSLVaryingHandler::Interpolation::kCanBeFlat);
+            args.fVertBuilder->codeAppendf(R"(
+            int2 atlas_wh = (transposed) ? abs(dev_xywh.wz) : dev_xywh.zw;
+            %s = float4(atlas_xy, atlas_xy + atlas_wh) * %s.xyxy;)", atlasBounds.vsOut(),
+                    atlasAdjust);
+
+            args.fFragBuilder->codeAppendf(R"(
+            half coverage = 0;
+            float2 atlascoord = %s;
+            float4 atlasbounds = %s;
+            if (all(greaterThan(atlascoord, atlasbounds.xy)) &&
+                all(lessThan(atlascoord, atlasbounds.zw))) {
+                coverage = )", atlasCoord.fsIn(), atlasBounds.fsIn());
+            args.fFragBuilder->appendTextureLookup(args.fTexSamplers[0], "atlascoord");
+            args.fFragBuilder->codeAppendf(R"(.a;
+            }
+            half4 %s = half4(1 - coverage);)", args.fOutputCoverage);
+        } else {
+            args.fFragBuilder->codeAppendf("half4 %s = ", args.fOutputCoverage);
+            args.fFragBuilder->appendTextureLookup(args.fTexSamplers[0], atlasCoord.fsIn());
+            args.fFragBuilder->codeAppendf(".aaaa;");
+        }
     }
 
     void setData(const GrGLSLProgramDataManager& pdman,
@@ -117,8 +154,8 @@
 GrProcessorSet::Analysis GrDrawAtlasPathOp::finalize(const GrCaps& caps, const GrAppliedClip* clip,
                                                      GrClampType clampType) {
     const GrProcessorSet::Analysis& analysis = fProcessors.finalize(
-            fInstanceList.fInstance.fColor, GrProcessorAnalysisCoverage::kSingleChannel, clip,
-            &GrUserStencilSettings::kUnused, caps, clampType, &fInstanceList.fInstance.fColor);
+            fHeadInstance.fColor, GrProcessorAnalysisCoverage::kSingleChannel, clip,
+            &GrUserStencilSettings::kUnused, caps, clampType, &fHeadInstance.fColor);
     fUsesLocalCoords = analysis.usesLocalCoords();
     return analysis;
 }
@@ -129,40 +166,27 @@
     SkASSERT(fAtlasProxy == that->fAtlasProxy);
     SkASSERT(fEnableHWAA == that->fEnableHWAA);
 
-    if (fProcessors != that->fProcessors) {
+    if (fIsInverseFill != that->fIsInverseFill || fProcessors != that->fProcessors) {
         return CombineResult::kCannotCombine;
     }
 
     SkASSERT(fUsesLocalCoords == that->fUsesLocalCoords);
-    auto* copy = alloc->make<InstanceList>(that->fInstanceList);
-    *fInstanceTail = copy;
-    fInstanceTail = (!copy->fNext) ? &copy->fNext : that->fInstanceTail;
+    auto* copy = alloc->make<Instance>(that->fHeadInstance);
+    *fTailInstance = copy;
+    fTailInstance = (!copy->fNext) ? &copy->fNext : that->fTailInstance;
     fInstanceCount += that->fInstanceCount;
     return CombineResult::kMerged;
 }
 
-void GrDrawAtlasPathOp::onPrePrepare(GrRecordingContext*,
-                                     const GrSurfaceProxyView& writeView,
-                                     GrAppliedClip*,
-                                     const GrDstProxyView&,
+void GrDrawAtlasPathOp::onPrePrepare(GrRecordingContext*, const GrSurfaceProxyView& writeView,
+                                     GrAppliedClip*, const GrDstProxyView&,
                                      GrXferBarrierFlags renderPassXferBarriers,
-                                     GrLoadOp colorLoadOp) {}
-
-void GrDrawAtlasPathOp::onPrepare(GrOpFlushState* state) {
-    size_t instanceStride = Instance::Stride(fUsesLocalCoords);
-    if (char* instanceData = (char*)state->makeVertexSpace(
-            instanceStride, fInstanceCount, &fInstanceBuffer, &fBaseInstance)) {
-        SkDEBUGCODE(char* end = instanceData + fInstanceCount * instanceStride);
-        for (const InstanceList* list = &fInstanceList; list; list = list->fNext) {
-            memcpy(instanceData, &list->fInstance, instanceStride);
-            instanceData += instanceStride;
-        }
-        SkASSERT(instanceData == end);
-    }
+                                     GrLoadOp colorLoadOp) {
+    SK_ABORT("DDL support not implemented for GrDrawAtlasPathOp.");
 }
 
-void GrDrawAtlasPathOp::onExecute(GrOpFlushState* state, const SkRect& chainBounds) {
-    SkASSERT(fAtlasProxy->isInstantiated());
+void GrDrawAtlasPathOp::onPrepare(GrOpFlushState* state) {
+    SkArenaAlloc* arena = state->allocator();
 
     GrPipeline::InitArgs initArgs;
     if (fEnableHWAA) {
@@ -171,20 +195,34 @@
     initArgs.fCaps = &state->caps();
     initArgs.fDstProxyView = state->drawOpArgs().dstProxyView();
     initArgs.fWriteSwizzle = state->drawOpArgs().writeView().swizzle();
-    GrPipeline pipeline(initArgs, std::move(fProcessors), state->detachAppliedClip());
-
+    auto pipeline = arena->make<GrPipeline>(initArgs, std::move(fProcessors),
+                                            state->detachAppliedClip());
     GrSwizzle swizzle = state->caps().getReadSwizzle(fAtlasProxy->backendFormat(),
                                                      GrColorType::kAlpha_8);
+    auto shader = arena->make<DrawAtlasPathShader>(fAtlasProxy.get(), swizzle, fIsInverseFill,
+                                                   fUsesLocalCoords);
+    fProgram = arena->make<GrProgramInfo>(state->writeView(), pipeline,
+                                          &GrUserStencilSettings::kUnused, shader,
+                                          GrPrimitiveType::kTriangleStrip, 0,
+                                          state->renderPassBarriers(), state->colorLoadOp());
 
-    DrawAtlasPathShader shader(fAtlasProxy.get(), swizzle, fUsesLocalCoords);
-    SkASSERT(shader.instanceStride() == Instance::Stride(fUsesLocalCoords));
+    if (GrVertexWriter instanceWriter = state->makeVertexSpace(
+                shader->instanceStride(), fInstanceCount, &fInstanceBuffer, &fBaseInstance)) {
+        for (const Instance* instance = &fHeadInstance; instance; instance = instance->fNext) {
+            instanceWriter.write(
+                    instance->fDevXYWH,
+                    instance->fAtlasXY,
+                    instance->fColor,
+                    GrVertexWriter::If(fIsInverseFill, instance->fDrawBoundsIfInverseFilled),
+                    GrVertexWriter::If(fUsesLocalCoords, instance->fViewMatrixIfUsingLocalCoords));
+        }
+    }
+}
 
-    GrProgramInfo programInfo(state->writeView(), &pipeline, &GrUserStencilSettings::kUnused,
-                              &shader, GrPrimitiveType::kTriangleStrip, 0,
-                              state->renderPassBarriers(), state->colorLoadOp());
-
-    state->bindPipelineAndScissorClip(programInfo, this->bounds());
-    state->bindTextures(shader, *fAtlasProxy, pipeline);
+void GrDrawAtlasPathOp::onExecute(GrOpFlushState* state, const SkRect& chainBounds) {
+    SkASSERT(fAtlasProxy->isInstantiated());
+    state->bindPipelineAndScissorClip(*fProgram, this->bounds());
+    state->bindTextures(fProgram->geomProc(), *fAtlasProxy, fProgram->pipeline());
     state->bindBuffers(nullptr, std::move(fInstanceBuffer), nullptr);
     state->drawInstanced(fInstanceCount, fBaseInstance, 4, 0);
 }