blob: 13a0df56bb91f0d2a944f609ea452c32952077cf [file] [log] [blame]
Chris Dalton70a0d2c2021-01-26 12:01:21 -07001/*
2 * Copyright 2021 Google LLC.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
Chris Dalton031d76b2021-06-08 16:32:00 -06008#include "src/gpu/tessellate/GrPathStencilCoverOp.h"
Chris Dalton70a0d2c2021-01-26 12:01:21 -07009
10#include "src/gpu/GrEagerVertexAllocator.h"
Chris Daltond9bdc322021-06-01 19:22:05 -060011#include "src/gpu/GrGpu.h"
Chris Dalton70a0d2c2021-01-26 12:01:21 -070012#include "src/gpu/GrOpFlushState.h"
13#include "src/gpu/GrRecordingContextPriv.h"
Chris Dalton2f733ec2021-06-01 12:11:57 -060014#include "src/gpu/glsl/GrGLSLVertexGeoBuilder.h"
Chris Dalton70a0d2c2021-01-26 12:01:21 -070015#include "src/gpu/tessellate/GrMiddleOutPolygonTriangulator.h"
Chris Daltond9bdc322021-06-01 19:22:05 -060016#include "src/gpu/tessellate/GrPathCurveTessellator.h"
Chris Daltond9bdc322021-06-01 19:22:05 -060017#include "src/gpu/tessellate/GrPathWedgeTessellator.h"
Chris Dalton70a0d2c2021-01-26 12:01:21 -070018#include "src/gpu/tessellate/GrTessellationPathRenderer.h"
Chris Dalton3b412782021-06-01 13:40:03 -060019#include "src/gpu/tessellate/shaders/GrPathTessellationShader.h"
Chris Dalton70a0d2c2021-01-26 12:01:21 -070020
Chris Dalton917f9192021-06-08 14:32:37 -060021using PathFlags = GrTessellationPathRenderer::PathFlags;
Chris Dalton70a0d2c2021-01-26 12:01:21 -070022
Chris Dalton2f733ec2021-06-01 12:11:57 -060023namespace {
24
25// Fills a path's bounding box, with subpixel outset to avoid possible T-junctions with extreme
26// edges of the path.
27// NOTE: The emitted geometry may not be axis-aligned, depending on the view matrix.
28class BoundingBoxShader : public GrPathTessellationShader {
29public:
30 BoundingBoxShader(const SkMatrix& viewMatrix, SkPMColor4f color)
31 : GrPathTessellationShader(kTessellate_BoundingBoxShader_ClassID,
32 GrPrimitiveType::kTriangleStrip, 0, viewMatrix, color) {
33 constexpr static Attribute kPathBoundsAttrib = {"pathBounds", kFloat4_GrVertexAttribType,
34 kFloat4_GrSLType};
35 this->setInstanceAttributes(&kPathBoundsAttrib, 1);
36 }
37
38private:
Chris Daltonb63711a2021-06-01 14:52:02 -060039 const char* name() const final { return "tessellate_BoundingBoxShader"; }
Chris Dalton2f733ec2021-06-01 12:11:57 -060040 void getGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const final {}
41 GrGLSLGeometryProcessor* createGLSLInstance(const GrShaderCaps&) const final;
42};
43
44GrGLSLGeometryProcessor* BoundingBoxShader::createGLSLInstance(const GrShaderCaps&) const {
45 class Impl : public GrPathTessellationShader::Impl {
46 void emitVertexCode(GrGLSLVertexBuilder* v, GrGPArgs* gpArgs) override {
47 v->codeAppend(R"(
48 // Bloat the bounding box by 1/4px to avoid potential T-junctions at the edges.
49 float2x2 M_ = inverse(AFFINE_MATRIX);
50 float2 bloat = float2(abs(M_[0]) + abs(M_[1])) * .25;
51
52 // Find the vertex position.
53 float2 T = float2(sk_VertexID & 1, sk_VertexID >> 1);
54 float2 localcoord = mix(pathBounds.xy - bloat, pathBounds.zw + bloat, T);
55 float2 vertexpos = AFFINE_MATRIX * localcoord + TRANSLATE;)");
56 gpArgs->fLocalCoordVar.set(kFloat2_GrSLType, "localcoord");
57 gpArgs->fPositionVar.set(kFloat2_GrSLType, "vertexpos");
58 }
59 };
60 return new Impl;
61}
62
63} // namespace
64
Chris Dalton031d76b2021-06-08 16:32:00 -060065void GrPathStencilCoverOp::visitProxies(const VisitProxyFunc& fn) const {
66 if (fCoverBBoxProgram) {
67 fCoverBBoxProgram->pipeline().visitProxies(fn);
Chris Dalton70a0d2c2021-01-26 12:01:21 -070068 } else {
69 fProcessors.visitProxies(fn);
70 }
71}
72
Chris Dalton031d76b2021-06-08 16:32:00 -060073GrDrawOp::FixedFunctionFlags GrPathStencilCoverOp::fixedFunctionFlags() const {
Chris Dalton70a0d2c2021-01-26 12:01:21 -070074 auto flags = FixedFunctionFlags::kUsesStencil;
75 if (fAAType != GrAAType::kNone) {
76 flags |= FixedFunctionFlags::kUsesHWAA;
77 }
78 return flags;
79}
80
Chris Dalton031d76b2021-06-08 16:32:00 -060081GrProcessorSet::Analysis GrPathStencilCoverOp::finalize(const GrCaps& caps,
82 const GrAppliedClip* clip,
83 GrClampType clampType) {
Chris Dalton57ab06c2021-04-22 12:57:28 -060084 return fProcessors.finalize(fColor, GrProcessorAnalysisCoverage::kNone, clip, nullptr, caps,
85 clampType, &fColor);
Chris Dalton70a0d2c2021-01-26 12:01:21 -070086}
87
Chris Dalton031d76b2021-06-08 16:32:00 -060088void GrPathStencilCoverOp::prePreparePrograms(const GrTessellationShader::ProgramArgs& args,
89 GrAppliedClip&& appliedClip) {
Chris Dalton569c01b2021-05-25 10:11:46 -060090 SkASSERT(!fTessellator);
Chris Dalton70a0d2c2021-01-26 12:01:21 -070091 SkASSERT(!fStencilFanProgram);
92 SkASSERT(!fStencilPathProgram);
Chris Dalton031d76b2021-06-08 16:32:00 -060093 SkASSERT(!fCoverBBoxProgram);
Chris Dalton70a0d2c2021-01-26 12:01:21 -070094
Chris Dalton569c01b2021-05-25 10:11:46 -060095 if (fPath.countVerbs() <= 0) {
Chris Dalton70a0d2c2021-01-26 12:01:21 -070096 return;
97 }
98
Chris Dalton2f733ec2021-06-01 12:11:57 -060099 const GrPipeline* stencilPipeline = GrPathTessellationShader::MakeStencilOnlyPipeline(
Chris Dalton917f9192021-06-08 14:32:37 -0600100 args, fAAType, fPathFlags, appliedClip.hardClip());
Chris Dalton2f733ec2021-06-01 12:11:57 -0600101 const GrUserStencilSettings* stencilPathSettings =
102 GrPathTessellationShader::StencilPathSettings(fPath.getFillType());
Chris Dalton569c01b2021-05-25 10:11:46 -0600103
Chris Dalton917f9192021-06-08 14:32:37 -0600104 auto drawFanWithTessellator = GrPathTessellator::DrawInnerFan::kYes;
105 if (fPath.countVerbs() > 50 && this->bounds().height() * this->bounds().width() > 256 * 256) {
106 // Large complex paths do better with a dedicated triangle shader for the inner fan. This
107 // takes less PCI bus bandwidth (6 floats per triangle instead of 8) and allows us to make
108 // sure it has an efficient middle-out topology.
109 auto shader = GrPathTessellationShader::MakeSimpleTriangleShader(
110 args.fArena, fViewMatrix, SK_PMColor4fTRANSPARENT);
111 fStencilFanProgram = GrTessellationShader::MakeProgram(args, shader, stencilPipeline,
112 stencilPathSettings);
113 drawFanWithTessellator = GrPathTessellator::DrawInnerFan::kNo;
114 }
115 if (!args.fCaps->shaderCaps()->tessellationSupport() ||
116 fPath.countVerbs() < args.fCaps->minPathVerbsForHwTessellation()) {
Chris Dalton26666bd2021-06-08 16:25:46 -0600117 fTessellator = GrPathCurveTessellator::Make(
118 args.fArena, fViewMatrix, SK_PMColor4fTRANSPARENT, drawFanWithTessellator,
119 GrPathCurveTessellator::ShaderType::kFixedCountMiddleOut);
Chris Dalton917f9192021-06-08 14:32:37 -0600120 } else if (drawFanWithTessellator == GrPathTessellator::DrawInnerFan::kNo) {
Chris Dalton26666bd2021-06-08 16:25:46 -0600121 fTessellator = GrPathCurveTessellator::Make(
122 args.fArena, fViewMatrix, SK_PMColor4fTRANSPARENT, drawFanWithTessellator,
123 GrPathCurveTessellator::ShaderType::kHardwareTessellation);
Chris Dalton917f9192021-06-08 14:32:37 -0600124 } else {
Chris Dalton2f733ec2021-06-01 12:11:57 -0600125 fTessellator = GrPathWedgeTessellator::Make(args.fArena, fViewMatrix,
126 SK_PMColor4fTRANSPARENT);
Chris Dalton70a0d2c2021-01-26 12:01:21 -0700127 }
Chris Dalton2f733ec2021-06-01 12:11:57 -0600128 fStencilPathProgram = GrTessellationShader::MakeProgram(args, fTessellator->shader(),
129 stencilPipeline, stencilPathSettings);
Chris Dalton569c01b2021-05-25 10:11:46 -0600130
Chris Dalton917f9192021-06-08 14:32:37 -0600131 if (!(fPathFlags & PathFlags::kStencilOnly)) {
Chris Dalton70a0d2c2021-01-26 12:01:21 -0700132 // Create a program that draws a bounding box over the path and fills its stencil coverage
133 // into the color buffer.
Chris Dalton2f733ec2021-06-01 12:11:57 -0600134 auto* bboxShader = args.fArena->make<BoundingBoxShader>(fViewMatrix, fColor);
135 auto* bboxPipeline = GrTessellationShader::MakePipeline(args, fAAType,
136 std::move(appliedClip),
137 std::move(fProcessors));
138 auto* bboxStencil = GrPathTessellationShader::TestAndResetStencilSettings();
Chris Dalton031d76b2021-06-08 16:32:00 -0600139 fCoverBBoxProgram = GrTessellationShader::MakeProgram(args, bboxShader, bboxPipeline,
140 bboxStencil);
Chris Dalton70a0d2c2021-01-26 12:01:21 -0700141 }
142}
143
Chris Dalton031d76b2021-06-08 16:32:00 -0600144void GrPathStencilCoverOp::onPrePrepare(GrRecordingContext* context,
145 const GrSurfaceProxyView& writeView, GrAppliedClip* clip,
146 const GrDstProxyView& dstProxyView,
147 GrXferBarrierFlags renderPassXferBarriers,
148 GrLoadOp colorLoadOp) {
Chris Dalton70a0d2c2021-01-26 12:01:21 -0700149 this->prePreparePrograms({context->priv().recordTimeAllocator(), writeView, &dstProxyView,
150 renderPassXferBarriers, colorLoadOp, context->priv().caps()},
151 (clip) ? std::move(*clip) : GrAppliedClip::Disabled());
152 if (fStencilFanProgram) {
153 context->priv().recordProgramInfo(fStencilFanProgram);
154 }
155 if (fStencilPathProgram) {
156 context->priv().recordProgramInfo(fStencilPathProgram);
157 }
Chris Dalton031d76b2021-06-08 16:32:00 -0600158 if (fCoverBBoxProgram) {
159 context->priv().recordProgramInfo(fCoverBBoxProgram);
Chris Dalton70a0d2c2021-01-26 12:01:21 -0700160 }
161}
162
Chris Dalton031d76b2021-06-08 16:32:00 -0600163void GrPathStencilCoverOp::onPrepare(GrOpFlushState* flushState) {
Chris Dalton70a0d2c2021-01-26 12:01:21 -0700164 if (!fTessellator) {
165 this->prePreparePrograms({flushState->allocator(), flushState->writeView(),
166 &flushState->dstProxyView(), flushState->renderPassBarriers(),
167 flushState->colorLoadOp(), &flushState->caps()},
168 flushState->detachAppliedClip());
169 if (!fTessellator) {
170 return;
171 }
172 }
173
174 if (fStencilFanProgram) {
175 // The inner fan isn't built into the tessellator. Generate a standard Redbook fan with a
176 // middle-out topology.
177 GrEagerDynamicVertexAllocator vertexAlloc(flushState, &fFanBuffer, &fFanBaseVertex);
178 int maxFanTriangles = fPath.countVerbs() - 2; // n - 2 triangles make an n-gon.
Chris Dalton8731a712021-05-14 14:48:54 -0600179 GrVertexWriter triangleVertexWriter = vertexAlloc.lock<SkPoint>(maxFanTriangles * 3);
Chris Dalton70a0d2c2021-01-26 12:01:21 -0700180 fFanVertexCount = GrMiddleOutPolygonTriangulator::WritePathInnerFan(
Chris Daltondf2dbad2021-05-14 16:21:15 -0600181 &triangleVertexWriter, GrMiddleOutPolygonTriangulator::OutputType::kTriangles,
182 fPath) * 3;
Chris Dalton70a0d2c2021-01-26 12:01:21 -0700183 SkASSERT(fFanVertexCount <= maxFanTriangles * 3);
184 vertexAlloc.unlock(fFanVertexCount);
185 }
186
Chris Dalton569c01b2021-05-25 10:11:46 -0600187 fTessellator->prepare(flushState, this->bounds(), fPath);
Chris Daltonc91dd692021-05-24 15:04:47 -0600188
Chris Dalton031d76b2021-06-08 16:32:00 -0600189 if (fCoverBBoxProgram) {
Chris Daltonc91dd692021-05-24 15:04:47 -0600190 GrVertexWriter vertexWriter = flushState->makeVertexSpace(sizeof(SkRect), 1, &fBBoxBuffer,
191 &fBBoxBaseInstance);
192 vertexWriter.write(fPath.getBounds());
193 }
Chris Dalton70a0d2c2021-01-26 12:01:21 -0700194}
195
Chris Dalton031d76b2021-06-08 16:32:00 -0600196void GrPathStencilCoverOp::onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) {
Chris Dalton70a0d2c2021-01-26 12:01:21 -0700197 if (!fTessellator) {
198 return;
199 }
200
201 // Stencil the inner fan, if any.
202 if (fFanVertexCount > 0) {
203 SkASSERT(fStencilFanProgram);
204 SkASSERT(fFanBuffer);
205 flushState->bindPipelineAndScissorClip(*fStencilFanProgram, this->bounds());
206 flushState->bindBuffers(nullptr, nullptr, fFanBuffer);
207 flushState->draw(fFanVertexCount, fFanBaseVertex);
208 }
209
210 // Stencil the rest of the path.
211 SkASSERT(fStencilPathProgram);
212 flushState->bindPipelineAndScissorClip(*fStencilPathProgram, this->bounds());
213 fTessellator->draw(flushState);
Chris Daltond9bdc322021-06-01 19:22:05 -0600214 if (flushState->caps().requiresManualFBBarrierAfterTessellatedStencilDraw()) {
215 flushState->gpu()->insertManualFramebufferBarrier(); // http://skbug.com/9739
216 }
Chris Dalton70a0d2c2021-01-26 12:01:21 -0700217
218 // Fill in the bounding box (if not in stencil-only mode).
Chris Dalton031d76b2021-06-08 16:32:00 -0600219 if (fCoverBBoxProgram) {
220 flushState->bindPipelineAndScissorClip(*fCoverBBoxProgram, this->bounds());
221 flushState->bindTextures(fCoverBBoxProgram->geomProc(), nullptr,
222 fCoverBBoxProgram->pipeline());
Chris Daltonc91dd692021-05-24 15:04:47 -0600223 flushState->bindBuffers(nullptr, fBBoxBuffer, nullptr);
224 flushState->drawInstanced(1, fBBoxBaseInstance, 4, 0);
Chris Dalton70a0d2c2021-01-26 12:01:21 -0700225 }
226}