blob: 7fb97f656219c68c00262da8a51efc763d09e6a4 [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 Dalton2ed22fa2021-05-06 16:08:30 -06008#include "src/gpu/tessellate/GrPathStencilFillOp.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"
17#include "src/gpu/tessellate/GrPathIndirectTessellator.h"
18#include "src/gpu/tessellate/GrPathWedgeTessellator.h"
Chris Dalton70a0d2c2021-01-26 12:01:21 -070019#include "src/gpu/tessellate/GrTessellationPathRenderer.h"
Chris Dalton3b412782021-06-01 13:40:03 -060020#include "src/gpu/tessellate/shaders/GrPathTessellationShader.h"
Chris Dalton70a0d2c2021-01-26 12:01:21 -070021
Chris Dalton917f9192021-06-08 14:32:37 -060022using PathFlags = GrTessellationPathRenderer::PathFlags;
Chris Dalton70a0d2c2021-01-26 12:01:21 -070023
Chris Dalton2f733ec2021-06-01 12:11:57 -060024namespace {
25
26// Fills a path's bounding box, with subpixel outset to avoid possible T-junctions with extreme
27// edges of the path.
28// NOTE: The emitted geometry may not be axis-aligned, depending on the view matrix.
29class BoundingBoxShader : public GrPathTessellationShader {
30public:
31 BoundingBoxShader(const SkMatrix& viewMatrix, SkPMColor4f color)
32 : GrPathTessellationShader(kTessellate_BoundingBoxShader_ClassID,
33 GrPrimitiveType::kTriangleStrip, 0, viewMatrix, color) {
34 constexpr static Attribute kPathBoundsAttrib = {"pathBounds", kFloat4_GrVertexAttribType,
35 kFloat4_GrSLType};
36 this->setInstanceAttributes(&kPathBoundsAttrib, 1);
37 }
38
39private:
Chris Daltonb63711a2021-06-01 14:52:02 -060040 const char* name() const final { return "tessellate_BoundingBoxShader"; }
Chris Dalton2f733ec2021-06-01 12:11:57 -060041 void getGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const final {}
42 GrGLSLGeometryProcessor* createGLSLInstance(const GrShaderCaps&) const final;
43};
44
45GrGLSLGeometryProcessor* BoundingBoxShader::createGLSLInstance(const GrShaderCaps&) const {
46 class Impl : public GrPathTessellationShader::Impl {
47 void emitVertexCode(GrGLSLVertexBuilder* v, GrGPArgs* gpArgs) override {
48 v->codeAppend(R"(
49 // Bloat the bounding box by 1/4px to avoid potential T-junctions at the edges.
50 float2x2 M_ = inverse(AFFINE_MATRIX);
51 float2 bloat = float2(abs(M_[0]) + abs(M_[1])) * .25;
52
53 // Find the vertex position.
54 float2 T = float2(sk_VertexID & 1, sk_VertexID >> 1);
55 float2 localcoord = mix(pathBounds.xy - bloat, pathBounds.zw + bloat, T);
56 float2 vertexpos = AFFINE_MATRIX * localcoord + TRANSLATE;)");
57 gpArgs->fLocalCoordVar.set(kFloat2_GrSLType, "localcoord");
58 gpArgs->fPositionVar.set(kFloat2_GrSLType, "vertexpos");
59 }
60 };
61 return new Impl;
62}
63
64} // namespace
65
Chris Dalton2ed22fa2021-05-06 16:08:30 -060066void GrPathStencilFillOp::visitProxies(const VisitProxyFunc& fn) const {
Chris Dalton70a0d2c2021-01-26 12:01:21 -070067 if (fFillBBoxProgram) {
68 fFillBBoxProgram->pipeline().visitProxies(fn);
69 } else {
70 fProcessors.visitProxies(fn);
71 }
72}
73
Chris Dalton2ed22fa2021-05-06 16:08:30 -060074GrDrawOp::FixedFunctionFlags GrPathStencilFillOp::fixedFunctionFlags() const {
Chris Dalton70a0d2c2021-01-26 12:01:21 -070075 auto flags = FixedFunctionFlags::kUsesStencil;
76 if (fAAType != GrAAType::kNone) {
77 flags |= FixedFunctionFlags::kUsesHWAA;
78 }
79 return flags;
80}
81
Chris Dalton2ed22fa2021-05-06 16:08:30 -060082GrProcessorSet::Analysis GrPathStencilFillOp::finalize(const GrCaps& caps,
83 const GrAppliedClip* clip,
84 GrClampType clampType) {
Chris Dalton57ab06c2021-04-22 12:57:28 -060085 return fProcessors.finalize(fColor, GrProcessorAnalysisCoverage::kNone, clip, nullptr, caps,
86 clampType, &fColor);
Chris Dalton70a0d2c2021-01-26 12:01:21 -070087}
88
Chris Dalton2f733ec2021-06-01 12:11:57 -060089void GrPathStencilFillOp::prePreparePrograms(const GrTessellationShader::ProgramArgs& args,
Chris Dalton2ed22fa2021-05-06 16:08:30 -060090 GrAppliedClip&& appliedClip) {
Chris Dalton569c01b2021-05-25 10:11:46 -060091 SkASSERT(!fTessellator);
Chris Dalton70a0d2c2021-01-26 12:01:21 -070092 SkASSERT(!fStencilFanProgram);
93 SkASSERT(!fStencilPathProgram);
94 SkASSERT(!fFillBBoxProgram);
95
Chris Dalton569c01b2021-05-25 10:11:46 -060096 if (fPath.countVerbs() <= 0) {
Chris Dalton70a0d2c2021-01-26 12:01:21 -070097 return;
98 }
99
Chris Dalton2f733ec2021-06-01 12:11:57 -0600100 const GrPipeline* stencilPipeline = GrPathTessellationShader::MakeStencilOnlyPipeline(
Chris Dalton917f9192021-06-08 14:32:37 -0600101 args, fAAType, fPathFlags, appliedClip.hardClip());
Chris Dalton2f733ec2021-06-01 12:11:57 -0600102 const GrUserStencilSettings* stencilPathSettings =
103 GrPathTessellationShader::StencilPathSettings(fPath.getFillType());
Chris Dalton569c01b2021-05-25 10:11:46 -0600104
Chris Dalton917f9192021-06-08 14:32:37 -0600105 auto drawFanWithTessellator = GrPathTessellator::DrawInnerFan::kYes;
106 if (fPath.countVerbs() > 50 && this->bounds().height() * this->bounds().width() > 256 * 256) {
107 // Large complex paths do better with a dedicated triangle shader for the inner fan. This
108 // takes less PCI bus bandwidth (6 floats per triangle instead of 8) and allows us to make
109 // sure it has an efficient middle-out topology.
110 auto shader = GrPathTessellationShader::MakeSimpleTriangleShader(
111 args.fArena, fViewMatrix, SK_PMColor4fTRANSPARENT);
112 fStencilFanProgram = GrTessellationShader::MakeProgram(args, shader, stencilPipeline,
113 stencilPathSettings);
114 drawFanWithTessellator = GrPathTessellator::DrawInnerFan::kNo;
115 }
116 if (!args.fCaps->shaderCaps()->tessellationSupport() ||
117 fPath.countVerbs() < args.fCaps->minPathVerbsForHwTessellation()) {
118 fTessellator = GrPathIndirectTessellator::Make(args.fArena, fPath, fViewMatrix,
119 SK_PMColor4fTRANSPARENT,
120 drawFanWithTessellator);
121 } else if (drawFanWithTessellator == GrPathTessellator::DrawInnerFan::kNo) {
122 fTessellator = GrPathCurveTessellator::Make(args.fArena, fViewMatrix,
123 SK_PMColor4fTRANSPARENT,
124 GrPathTessellator::DrawInnerFan::kNo);
125 } else {
Chris Dalton2f733ec2021-06-01 12:11:57 -0600126 fTessellator = GrPathWedgeTessellator::Make(args.fArena, fViewMatrix,
127 SK_PMColor4fTRANSPARENT);
Chris Dalton70a0d2c2021-01-26 12:01:21 -0700128 }
Chris Dalton2f733ec2021-06-01 12:11:57 -0600129 fStencilPathProgram = GrTessellationShader::MakeProgram(args, fTessellator->shader(),
130 stencilPipeline, stencilPathSettings);
Chris Dalton569c01b2021-05-25 10:11:46 -0600131
Chris Dalton917f9192021-06-08 14:32:37 -0600132 if (!(fPathFlags & PathFlags::kStencilOnly)) {
Chris Dalton70a0d2c2021-01-26 12:01:21 -0700133 // Create a program that draws a bounding box over the path and fills its stencil coverage
134 // into the color buffer.
Chris Dalton2f733ec2021-06-01 12:11:57 -0600135 auto* bboxShader = args.fArena->make<BoundingBoxShader>(fViewMatrix, fColor);
136 auto* bboxPipeline = GrTessellationShader::MakePipeline(args, fAAType,
137 std::move(appliedClip),
138 std::move(fProcessors));
139 auto* bboxStencil = GrPathTessellationShader::TestAndResetStencilSettings();
140 fFillBBoxProgram = GrTessellationShader::MakeProgram(args, bboxShader, bboxPipeline,
141 bboxStencil);
Chris Dalton70a0d2c2021-01-26 12:01:21 -0700142 }
143}
144
Chris Dalton2ed22fa2021-05-06 16:08:30 -0600145void GrPathStencilFillOp::onPrePrepare(GrRecordingContext* context,
146 const GrSurfaceProxyView& writeView, GrAppliedClip* clip,
John Stiles52cb1d02021-06-02 11:58:05 -0400147 const GrDstProxyView& dstProxyView,
Chris Dalton2ed22fa2021-05-06 16:08:30 -0600148 GrXferBarrierFlags renderPassXferBarriers,
149 GrLoadOp colorLoadOp) {
Chris Dalton70a0d2c2021-01-26 12:01:21 -0700150 this->prePreparePrograms({context->priv().recordTimeAllocator(), writeView, &dstProxyView,
151 renderPassXferBarriers, colorLoadOp, context->priv().caps()},
152 (clip) ? std::move(*clip) : GrAppliedClip::Disabled());
153 if (fStencilFanProgram) {
154 context->priv().recordProgramInfo(fStencilFanProgram);
155 }
156 if (fStencilPathProgram) {
157 context->priv().recordProgramInfo(fStencilPathProgram);
158 }
159 if (fFillBBoxProgram) {
160 context->priv().recordProgramInfo(fFillBBoxProgram);
161 }
162}
163
Chris Dalton2ed22fa2021-05-06 16:08:30 -0600164void GrPathStencilFillOp::onPrepare(GrOpFlushState* flushState) {
Chris Dalton70a0d2c2021-01-26 12:01:21 -0700165 if (!fTessellator) {
166 this->prePreparePrograms({flushState->allocator(), flushState->writeView(),
167 &flushState->dstProxyView(), flushState->renderPassBarriers(),
168 flushState->colorLoadOp(), &flushState->caps()},
169 flushState->detachAppliedClip());
170 if (!fTessellator) {
171 return;
172 }
173 }
174
175 if (fStencilFanProgram) {
176 // The inner fan isn't built into the tessellator. Generate a standard Redbook fan with a
177 // middle-out topology.
178 GrEagerDynamicVertexAllocator vertexAlloc(flushState, &fFanBuffer, &fFanBaseVertex);
179 int maxFanTriangles = fPath.countVerbs() - 2; // n - 2 triangles make an n-gon.
Chris Dalton8731a712021-05-14 14:48:54 -0600180 GrVertexWriter triangleVertexWriter = vertexAlloc.lock<SkPoint>(maxFanTriangles * 3);
Chris Dalton70a0d2c2021-01-26 12:01:21 -0700181 fFanVertexCount = GrMiddleOutPolygonTriangulator::WritePathInnerFan(
Chris Daltondf2dbad2021-05-14 16:21:15 -0600182 &triangleVertexWriter, GrMiddleOutPolygonTriangulator::OutputType::kTriangles,
183 fPath) * 3;
Chris Dalton70a0d2c2021-01-26 12:01:21 -0700184 SkASSERT(fFanVertexCount <= maxFanTriangles * 3);
185 vertexAlloc.unlock(fFanVertexCount);
186 }
187
Chris Dalton569c01b2021-05-25 10:11:46 -0600188 fTessellator->prepare(flushState, this->bounds(), fPath);
Chris Daltonc91dd692021-05-24 15:04:47 -0600189
190 if (fFillBBoxProgram) {
191 GrVertexWriter vertexWriter = flushState->makeVertexSpace(sizeof(SkRect), 1, &fBBoxBuffer,
192 &fBBoxBaseInstance);
193 vertexWriter.write(fPath.getBounds());
194 }
Chris Dalton70a0d2c2021-01-26 12:01:21 -0700195}
196
Chris Dalton2ed22fa2021-05-06 16:08:30 -0600197void GrPathStencilFillOp::onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) {
Chris Dalton70a0d2c2021-01-26 12:01:21 -0700198 if (!fTessellator) {
199 return;
200 }
201
202 // Stencil the inner fan, if any.
203 if (fFanVertexCount > 0) {
204 SkASSERT(fStencilFanProgram);
205 SkASSERT(fFanBuffer);
206 flushState->bindPipelineAndScissorClip(*fStencilFanProgram, this->bounds());
207 flushState->bindBuffers(nullptr, nullptr, fFanBuffer);
208 flushState->draw(fFanVertexCount, fFanBaseVertex);
209 }
210
211 // Stencil the rest of the path.
212 SkASSERT(fStencilPathProgram);
213 flushState->bindPipelineAndScissorClip(*fStencilPathProgram, this->bounds());
214 fTessellator->draw(flushState);
Chris Daltond9bdc322021-06-01 19:22:05 -0600215 if (flushState->caps().requiresManualFBBarrierAfterTessellatedStencilDraw()) {
216 flushState->gpu()->insertManualFramebufferBarrier(); // http://skbug.com/9739
217 }
Chris Dalton70a0d2c2021-01-26 12:01:21 -0700218
219 // Fill in the bounding box (if not in stencil-only mode).
220 if (fFillBBoxProgram) {
221 flushState->bindPipelineAndScissorClip(*fFillBBoxProgram, this->bounds());
Robert Phillips787fd9d2021-03-22 14:48:09 -0400222 flushState->bindTextures(fFillBBoxProgram->geomProc(), nullptr,
Chris Dalton70a0d2c2021-01-26 12:01:21 -0700223 fFillBBoxProgram->pipeline());
Chris Daltonc91dd692021-05-24 15:04:47 -0600224 flushState->bindBuffers(nullptr, fBBoxBuffer, nullptr);
225 flushState->drawInstanced(1, fBBoxBaseInstance, 4, 0);
Chris Dalton70a0d2c2021-01-26 12:01:21 -0700226 }
227}