Lift the tessellation atlas into its own path renderer

Creates a new path renderer, GrAtlasPathRenderer, that handles all the
atlasing. Managing the atlas in its own path renderer gives us more
control over when atlasing happens in the chain, will allow us to more
easily use the atlas in kCoverage mode, and makes the clipping code
cleaner.

Bug: skia:12258
Change-Id: Ie0b669974936c23895c8ab794e2d97206ed140f8
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/431896
Commit-Queue: Chris Dalton <csmartdalton@google.com>
Reviewed-by: Michael Ludwig <michaelludwig@google.com>
diff --git a/src/gpu/ops/GrAtlasInstancedHelper.cpp b/src/gpu/ops/GrAtlasInstancedHelper.cpp
new file mode 100644
index 0000000..082c240
--- /dev/null
+++ b/src/gpu/ops/GrAtlasInstancedHelper.cpp
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2020 Google LLC.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "src/gpu/ops/GrAtlasInstancedHelper.h"
+
+#include "src/gpu/GrVertexWriter.h"
+#include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h"
+#include "src/gpu/glsl/GrGLSLVarying.h"
+#include "src/gpu/glsl/GrGLSLVertexGeoBuilder.h"
+
+void GrAtlasInstancedHelper::appendInstanceAttribs(
+        SkTArray<GrGeometryProcessor::Attribute>* instanceAttribs) const {
+    instanceAttribs->emplace_back("locations", kFloat4_GrVertexAttribType, kFloat4_GrSLType);
+    if (fShaderFlags & ShaderFlags::kCheckBounds) {
+        instanceAttribs->emplace_back("sizeInAtlas", kFloat2_GrVertexAttribType, kFloat2_GrSLType);
+    }
+}
+
+void GrAtlasInstancedHelper::writeInstanceData(GrVertexWriter* instanceWriter,
+                                               const Instance* i) const {
+    SkASSERT(i->fLocationInAtlas.x() >= 0);
+    SkASSERT(i->fLocationInAtlas.y() >= 0);
+    instanceWriter->write(
+            // A negative x coordinate in the atlas indicates that the path is transposed.
+            // Also add 1 since we can't negate zero.
+            (float)(i->fTransposedInAtlas ? -i->fLocationInAtlas.x() - 1
+                                          : i->fLocationInAtlas.x() + 1),
+            (float)i->fLocationInAtlas.y(),
+            (float)i->fPathDevIBounds.left(),
+            (float)i->fPathDevIBounds.top(),
+            GrVertexWriter::If(fShaderFlags & ShaderFlags::kCheckBounds,
+                               SkSize::Make(i->fPathDevIBounds.size())));
+}
+
+void GrAtlasInstancedHelper::injectShaderCode(
+        const GrGLSLGeometryProcessor::EmitArgs& args, const GrShaderVar& devCoord,
+        GrGLSLUniformHandler::UniformHandle* atlasAdjustUniformHandle) const {
+    GrGLSLVarying atlasCoord(kFloat2_GrSLType);
+    args.fVaryingHandler->addVarying("atlasCoord", &atlasCoord);
+
+    const char* atlasAdjustName;
+    *atlasAdjustUniformHandle = args.fUniformHandler->addUniform(
+            nullptr, kVertex_GrShaderFlag, kFloat2_GrSLType, "atlas_adjust", &atlasAdjustName);
+
+    args.fVertBuilder->codeAppendf(R"(
+    // A negative x coordinate in the atlas indicates that the path is transposed.
+    // We also added 1 since we can't negate zero.
+    float2 atlasTopLeft = float2(abs(locations.x) - 1, locations.y);
+    float2 devTopLeft = locations.zw;
+    bool transposed = locations.x < 0;
+    float2 atlasCoord = %s - devTopLeft;
+    if (transposed) {
+        atlasCoord = atlasCoord.yx;
+    }
+    atlasCoord += atlasTopLeft;
+    %s = atlasCoord * %s;)", devCoord.c_str(), atlasCoord.vsOut(), atlasAdjustName);
+
+    if (fShaderFlags & ShaderFlags::kCheckBounds) {
+        GrGLSLVarying atlasBounds(kFloat4_GrSLType);
+        args.fVaryingHandler->addVarying("atlasbounds", &atlasBounds,
+                                         GrGLSLVaryingHandler::Interpolation::kCanBeFlat);
+        args.fVertBuilder->codeAppendf(R"(
+        float4 atlasBounds = atlasTopLeft.xyxy + (transposed ? sizeInAtlas.00yx
+                                                             : sizeInAtlas.00xy);
+        %s = atlasBounds * %s.xyxy;)", atlasBounds.vsOut(), atlasAdjustName);
+
+        args.fFragBuilder->codeAppendf(R"(
+        half atlasCoverage = 0;
+        float2 atlasCoord = %s;
+        float4 atlasBounds = %s;
+        if (all(greaterThan(atlasCoord, atlasBounds.xy)) &&
+            all(lessThan(atlasCoord, atlasBounds.zw))) {
+            atlasCoverage = )", atlasCoord.fsIn(), atlasBounds.fsIn());
+        args.fFragBuilder->appendTextureLookup(args.fTexSamplers[0], "atlasCoord");
+        args.fFragBuilder->codeAppendf(R"(.a;
+        })");
+    } else {
+        args.fFragBuilder->codeAppendf("half atlasCoverage = ");
+        args.fFragBuilder->appendTextureLookup(args.fTexSamplers[0], atlasCoord.fsIn());
+        args.fFragBuilder->codeAppendf(".a;");
+    }
+
+    if (fShaderFlags & ShaderFlags::kInvertCoverage) {
+        args.fFragBuilder->codeAppendf("%s *= (1 - atlasCoverage);", args.fOutputCoverage);
+    } else {
+        args.fFragBuilder->codeAppendf("%s *= atlasCoverage;", args.fOutputCoverage);
+    }
+}
+
+void GrAtlasInstancedHelper::setUniformData(
+        const GrGLSLProgramDataManager& pdman,
+        const GrGLSLUniformHandler::UniformHandle& atlasAdjustUniformHandle) const {
+    SkASSERT(fAtlasProxy->isInstantiated());
+    SkISize dimensions = fAtlasProxy->backingStoreDimensions();
+    pdman.set2f(atlasAdjustUniformHandle, 1.f / dimensions.width(), 1.f / dimensions.height());
+}