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/GrDrawAtlasPathOp.cpp b/src/gpu/ops/GrDrawAtlasPathOp.cpp
new file mode 100644
index 0000000..a05999c
--- /dev/null
+++ b/src/gpu/ops/GrDrawAtlasPathOp.cpp
@@ -0,0 +1,219 @@
+/*
+ * Copyright 2020 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "src/gpu/ops/GrDrawAtlasPathOp.h"
+
+#include "src/gpu/GrOpFlushState.h"
+#include "src/gpu/GrOpsRenderPass.h"
+#include "src/gpu/GrProgramInfo.h"
+#include "src/gpu/GrResourceProvider.h"
+#include "src/gpu/GrVertexWriter.h"
+#include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h"
+#include "src/gpu/glsl/GrGLSLGeometryProcessor.h"
+#include "src/gpu/glsl/GrGLSLVarying.h"
+#include "src/gpu/glsl/GrGLSLVertexGeoBuilder.h"
+
+namespace {
+
+class DrawAtlasPathShader : public GrGeometryProcessor {
+public:
+ DrawAtlasPathShader(bool usesLocalCoords, const GrAtlasInstancedHelper* atlasHelper,
+ const GrShaderCaps& shaderCaps)
+ : GrGeometryProcessor(kDrawAtlasPathShader_ClassID)
+ , fUsesLocalCoords(usesLocalCoords)
+ , fAtlasHelper(atlasHelper)
+ , fAtlasAccess(GrSamplerState::Filter::kNearest, fAtlasHelper->proxy()->backendFormat(),
+ fAtlasHelper->atlasSwizzle()) {
+ if (!shaderCaps.vertexIDSupport()) {
+ constexpr static Attribute kUnitCoordAttrib("unitCoord", kFloat2_GrVertexAttribType,
+ kFloat2_GrSLType);
+ this->setVertexAttributes(&kUnitCoordAttrib, 1);
+ }
+ fAttribs.emplace_back("fillBounds", kFloat4_GrVertexAttribType, kFloat4_GrSLType);
+ if (fUsesLocalCoords) {
+ fAttribs.emplace_back("affineMatrix", kFloat4_GrVertexAttribType, kFloat4_GrSLType);
+ fAttribs.emplace_back("translate", kFloat2_GrVertexAttribType, kFloat2_GrSLType);
+ }
+ SkASSERT(fAttribs.count() == this->colorAttribIdx());
+ fAttribs.emplace_back("color", kFloat4_GrVertexAttribType, kHalf4_GrSLType);
+ fAtlasHelper->appendInstanceAttribs(&fAttribs);
+ SkASSERT(fAttribs.count() <= kMaxInstanceAttribs);
+ this->setInstanceAttributes(fAttribs.data(), fAttribs.count());
+ this->setTextureSamplerCnt(1);
+ }
+
+private:
+ int colorAttribIdx() const { return fUsesLocalCoords ? 3 : 1; }
+ const char* name() const override { return "DrawAtlasPathShader"; }
+ void getGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder* b) const override {
+ b->addBits(1, fUsesLocalCoords, "localCoords");
+ fAtlasHelper->getKeyBits(b);
+ }
+ const TextureSampler& onTextureSampler(int) const override { return fAtlasAccess; }
+ GrGLSLGeometryProcessor* createGLSLInstance(const GrShaderCaps&) const override;
+
+ const bool fUsesLocalCoords;
+ const GrAtlasInstancedHelper* const fAtlasHelper;
+ TextureSampler fAtlasAccess;
+ constexpr static int kMaxInstanceAttribs = 6;
+ SkSTArray<kMaxInstanceAttribs, GrGeometryProcessor::Attribute> fAttribs;
+
+ class Impl;
+};
+
+class DrawAtlasPathShader::Impl : public GrGLSLGeometryProcessor {
+ void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
+ const auto& shader = args.fGeomProc.cast<DrawAtlasPathShader>();
+ args.fVaryingHandler->emitAttributes(shader);
+
+ if (args.fShaderCaps->vertexIDSupport()) {
+ // If we don't have sk_VertexID support then "unitCoord" already came in as a vertex
+ // attrib.
+ args.fVertBuilder->codeAppendf(R"(
+ float2 unitCoord = float2(sk_VertexID & 1, sk_VertexID >> 1);)");
+ }
+
+ args.fVertBuilder->codeAppendf(R"(
+ float2 devCoord = mix(fillBounds.xy, fillBounds.zw, unitCoord);)");
+ gpArgs->fPositionVar.set(kFloat2_GrSLType, "devCoord");
+
+ if (shader.fUsesLocalCoords) {
+ args.fVertBuilder->codeAppendf(R"(
+ float2x2 M = float2x2(affineMatrix);
+ float2 localCoord = inverse(M) * (devCoord - translate);)");
+ gpArgs->fLocalCoordVar.set(kFloat2_GrSLType, "localCoord");
+ }
+
+ args.fFragBuilder->codeAppendf("half4 %s = half4(1);", args.fOutputCoverage);
+ shader.fAtlasHelper->injectShaderCode(args, gpArgs->fPositionVar, &fAtlasAdjustUniform);
+
+ args.fFragBuilder->codeAppendf("half4 %s;", args.fOutputColor);
+ args.fVaryingHandler->addPassThroughAttribute(
+ shader.fAttribs[shader.colorAttribIdx()], args.fOutputColor,
+ GrGLSLVaryingHandler::Interpolation::kCanBeFlat);
+ }
+
+ void setData(const GrGLSLProgramDataManager& pdman,
+ const GrShaderCaps&,
+ const GrGeometryProcessor& geomProc) override {
+ auto* atlasHelper = geomProc.cast<DrawAtlasPathShader>().fAtlasHelper;
+ atlasHelper->setUniformData(pdman, fAtlasAdjustUniform);
+ }
+
+ GrGLSLUniformHandler::UniformHandle fAtlasAdjustUniform;
+};
+
+GrGLSLGeometryProcessor* DrawAtlasPathShader::createGLSLInstance(const GrShaderCaps&) const {
+ return new Impl();
+}
+
+} // namespace
+
+GrProcessorSet::Analysis GrDrawAtlasPathOp::finalize(const GrCaps& caps, const GrAppliedClip* clip,
+ GrClampType clampType) {
+ const GrProcessorSet::Analysis& analysis = fProcessors.finalize(
+ fHeadInstance->fColor, GrProcessorAnalysisCoverage::kSingleChannel, clip,
+ &GrUserStencilSettings::kUnused, caps, clampType, &fHeadInstance->fColor);
+ fUsesLocalCoords = analysis.usesLocalCoords();
+ return analysis;
+}
+
+GrOp::CombineResult GrDrawAtlasPathOp::onCombineIfPossible(GrOp* op, SkArenaAlloc*, const GrCaps&) {
+ auto* that = op->cast<GrDrawAtlasPathOp>();
+
+ if (!fAtlasHelper.isCompatible(that->fAtlasHelper) ||
+ fProcessors != that->fProcessors) {
+ return CombineResult::kCannotCombine;
+ }
+
+ SkASSERT(fUsesLocalCoords == that->fUsesLocalCoords);
+ *fTailInstance = that->fHeadInstance;
+ fTailInstance = that->fTailInstance;
+ fInstanceCount += that->fInstanceCount;
+ return CombineResult::kMerged;
+}
+
+void GrDrawAtlasPathOp::prepareProgram(const GrCaps& caps, SkArenaAlloc* arena,
+ const GrSurfaceProxyView& writeView, bool usesMSAASurface,
+ GrAppliedClip&& appliedClip,
+ const GrDstProxyView& dstProxyView,
+ GrXferBarrierFlags renderPassXferBarriers,
+ GrLoadOp colorLoadOp) {
+ SkASSERT(!fProgram);
+ GrPipeline::InitArgs initArgs;
+ if (usesMSAASurface) {
+ initArgs.fInputFlags |= GrPipeline::InputFlags::kHWAntialias;
+ }
+ initArgs.fCaps = ∩︀
+ initArgs.fDstProxyView = dstProxyView;
+ initArgs.fWriteSwizzle = writeView.swizzle();
+ auto pipeline = arena->make<GrPipeline>(initArgs, std::move(fProcessors),
+ std::move(appliedClip));
+ auto shader = arena->make<DrawAtlasPathShader>(fUsesLocalCoords, &fAtlasHelper,
+ *caps.shaderCaps());
+ fProgram = arena->make<GrProgramInfo>(writeView, pipeline, &GrUserStencilSettings::kUnused,
+ shader, GrPrimitiveType::kTriangleStrip, 0,
+ renderPassXferBarriers, colorLoadOp);
+}
+
+void GrDrawAtlasPathOp::onPrePrepare(GrRecordingContext* rContext,
+ const GrSurfaceProxyView& writeView,
+ GrAppliedClip* appliedClip, const GrDstProxyView& dstProxyView,
+ GrXferBarrierFlags renderPassXferBarriers,
+ GrLoadOp colorLoadOp) {
+ this->prepareProgram(*rContext->priv().caps(), rContext->priv().recordTimeAllocator(),
+ writeView, writeView.asRenderTargetProxy()->numSamples() > 1,
+ std::move(*appliedClip), dstProxyView, renderPassXferBarriers,
+ colorLoadOp);
+ SkASSERT(fProgram);
+ rContext->priv().recordProgramInfo(fProgram);
+}
+
+GR_DECLARE_STATIC_UNIQUE_KEY(gUnitQuadBufferKey);
+
+void GrDrawAtlasPathOp::onPrepare(GrOpFlushState* flushState) {
+ if (!fProgram) {
+ this->prepareProgram(flushState->caps(), flushState->allocator(), flushState->writeView(),
+ flushState->usesMSAASurface(), flushState->detachAppliedClip(),
+ flushState->dstProxyView(), flushState->renderPassBarriers(),
+ flushState->colorLoadOp());
+ SkASSERT(fProgram);
+ }
+
+ // FIXME(skbug.com/12201): Our draw's MSAA state should match the render target, but DDL doesn't
+ // yet communicate DMSAA state to onPrePrepare.
+ SkASSERT(fProgram->pipeline().isHWAntialiasState() == flushState->usesMSAASurface());
+
+ if (GrVertexWriter instanceWriter = flushState->makeVertexSpace(
+ fProgram->geomProc().instanceStride(), fInstanceCount, &fInstanceBuffer,
+ &fBaseInstance)) {
+ for (const Instance* i = fHeadInstance; i; i = i->fNext) {
+ instanceWriter.write(
+ SkRect::Make(i->fFillBounds),
+ GrVertexWriter::If(fUsesLocalCoords,
+ i->fLocalToDeviceIfUsingLocalCoords),
+ i->fColor);
+ fAtlasHelper.writeInstanceData(&instanceWriter, &i->fAtlasInstance);
+ }
+ }
+
+ if (!flushState->caps().shaderCaps()->vertexIDSupport()) {
+ constexpr static SkPoint kUnitQuad[4] = {{0,0}, {0,1}, {1,0}, {1,1}};
+
+ GR_DEFINE_STATIC_UNIQUE_KEY(gUnitQuadBufferKey);
+
+ fVertexBufferIfNoIDSupport = flushState->resourceProvider()->findOrMakeStaticBuffer(
+ GrGpuBufferType::kVertex, sizeof(kUnitQuad), kUnitQuad, gUnitQuadBufferKey);
+ }
+}
+
+void GrDrawAtlasPathOp::onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) {
+ flushState->bindPipelineAndScissorClip(*fProgram, this->bounds());
+ flushState->bindTextures(fProgram->geomProc(), *fAtlasHelper.proxy(), fProgram->pipeline());
+ flushState->bindBuffers(nullptr, std::move(fInstanceBuffer), fVertexBufferIfNoIDSupport);
+ flushState->drawInstanced(fInstanceCount, fBaseInstance, 4, 0);
+}