blob: a8bc8c3db7318eb87db95a459afc62912bc5f145 [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 Daltona05ccc32021-06-29 19:42:13 -060014#include "src/gpu/glsl/GrGLSLProgramBuilder.h"
Chris Dalton2f733ec2021-06-01 12:11:57 -060015#include "src/gpu/glsl/GrGLSLVertexGeoBuilder.h"
Chris Dalton70a0d2c2021-01-26 12:01:21 -070016#include "src/gpu/tessellate/GrMiddleOutPolygonTriangulator.h"
Chris Daltond9bdc322021-06-01 19:22:05 -060017#include "src/gpu/tessellate/GrPathCurveTessellator.h"
Chris Daltond9bdc322021-06-01 19:22:05 -060018#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:
Chris Daltona05ccc32021-06-29 19:42:13 -060031 BoundingBoxShader(const SkMatrix& viewMatrix, SkPMColor4f color, const GrShaderCaps& shaderCaps)
Chris Dalton2f733ec2021-06-01 12:11:57 -060032 : GrPathTessellationShader(kTessellate_BoundingBoxShader_ClassID,
33 GrPrimitiveType::kTriangleStrip, 0, viewMatrix, color) {
Chris Daltona05ccc32021-06-29 19:42:13 -060034 constexpr static Attribute kPathBoundsAttrib("pathBounds", kFloat4_GrVertexAttribType,
35 kFloat4_GrSLType);
Chris Dalton2f733ec2021-06-01 12:11:57 -060036 this->setInstanceAttributes(&kPathBoundsAttrib, 1);
Chris Daltona05ccc32021-06-29 19:42:13 -060037 if (!shaderCaps.vertexIDSupport()) {
38 constexpr static Attribute kUnitCoordAttrib("unitCoord", kFloat2_GrVertexAttribType,
39 kFloat2_GrSLType);
40 this->setVertexAttributes(&kUnitCoordAttrib, 1);
41 }
Chris Dalton2f733ec2021-06-01 12:11:57 -060042 }
43
44private:
Chris Daltonb63711a2021-06-01 14:52:02 -060045 const char* name() const final { return "tessellate_BoundingBoxShader"; }
Chris Dalton2f733ec2021-06-01 12:11:57 -060046 void getGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const final {}
47 GrGLSLGeometryProcessor* createGLSLInstance(const GrShaderCaps&) const final;
48};
49
50GrGLSLGeometryProcessor* BoundingBoxShader::createGLSLInstance(const GrShaderCaps&) const {
51 class Impl : public GrPathTessellationShader::Impl {
Chris Daltond2b8ba32021-06-09 00:12:59 -060052 void emitVertexCode(const GrPathTessellationShader&, GrGLSLVertexBuilder* v,
53 GrGPArgs* gpArgs) override {
Chris Daltona05ccc32021-06-29 19:42:13 -060054 if (v->getProgramBuilder()->caps()->shaderCaps()->vertexIDSupport()) {
55 // If we don't have sk_VertexID support then "unitCoord" already came in as a vertex
56 // attrib.
57 v->codeAppendf(R"(
58 float2 unitCoord = float2(sk_VertexID & 1, sk_VertexID >> 1);)");
59 }
60
Chris Dalton2f733ec2021-06-01 12:11:57 -060061 v->codeAppend(R"(
62 // Bloat the bounding box by 1/4px to avoid potential T-junctions at the edges.
63 float2x2 M_ = inverse(AFFINE_MATRIX);
64 float2 bloat = float2(abs(M_[0]) + abs(M_[1])) * .25;
65
66 // Find the vertex position.
Chris Daltona05ccc32021-06-29 19:42:13 -060067 float2 localcoord = mix(pathBounds.xy - bloat, pathBounds.zw + bloat, unitCoord);
Chris Dalton2f733ec2021-06-01 12:11:57 -060068 float2 vertexpos = AFFINE_MATRIX * localcoord + TRANSLATE;)");
69 gpArgs->fLocalCoordVar.set(kFloat2_GrSLType, "localcoord");
70 gpArgs->fPositionVar.set(kFloat2_GrSLType, "vertexpos");
71 }
72 };
73 return new Impl;
74}
75
76} // namespace
77
Robert Phillips294723d2021-06-17 09:23:58 -040078void GrPathStencilCoverOp::visitProxies(const GrVisitProxyFunc& func) const {
Chris Dalton031d76b2021-06-08 16:32:00 -060079 if (fCoverBBoxProgram) {
Robert Phillips294723d2021-06-17 09:23:58 -040080 fCoverBBoxProgram->pipeline().visitProxies(func);
Chris Dalton70a0d2c2021-01-26 12:01:21 -070081 } else {
Robert Phillips294723d2021-06-17 09:23:58 -040082 fProcessors.visitProxies(func);
Chris Dalton70a0d2c2021-01-26 12:01:21 -070083 }
84}
85
Chris Dalton031d76b2021-06-08 16:32:00 -060086GrDrawOp::FixedFunctionFlags GrPathStencilCoverOp::fixedFunctionFlags() const {
Chris Dalton70a0d2c2021-01-26 12:01:21 -070087 auto flags = FixedFunctionFlags::kUsesStencil;
88 if (fAAType != GrAAType::kNone) {
89 flags |= FixedFunctionFlags::kUsesHWAA;
90 }
91 return flags;
92}
93
Chris Dalton031d76b2021-06-08 16:32:00 -060094GrProcessorSet::Analysis GrPathStencilCoverOp::finalize(const GrCaps& caps,
95 const GrAppliedClip* clip,
96 GrClampType clampType) {
Chris Dalton57ab06c2021-04-22 12:57:28 -060097 return fProcessors.finalize(fColor, GrProcessorAnalysisCoverage::kNone, clip, nullptr, caps,
98 clampType, &fColor);
Chris Dalton70a0d2c2021-01-26 12:01:21 -070099}
100
Chris Dalton031d76b2021-06-08 16:32:00 -0600101void GrPathStencilCoverOp::prePreparePrograms(const GrTessellationShader::ProgramArgs& args,
102 GrAppliedClip&& appliedClip) {
Chris Dalton569c01b2021-05-25 10:11:46 -0600103 SkASSERT(!fTessellator);
Chris Dalton70a0d2c2021-01-26 12:01:21 -0700104 SkASSERT(!fStencilFanProgram);
105 SkASSERT(!fStencilPathProgram);
Chris Dalton031d76b2021-06-08 16:32:00 -0600106 SkASSERT(!fCoverBBoxProgram);
Chris Dalton70a0d2c2021-01-26 12:01:21 -0700107
Chris Dalton2f733ec2021-06-01 12:11:57 -0600108 const GrPipeline* stencilPipeline = GrPathTessellationShader::MakeStencilOnlyPipeline(
Chris Dalton917f9192021-06-08 14:32:37 -0600109 args, fAAType, fPathFlags, appliedClip.hardClip());
Chris Dalton2f733ec2021-06-01 12:11:57 -0600110 const GrUserStencilSettings* stencilPathSettings =
Chris Daltonbaae2dd2021-06-25 14:52:49 -0600111 GrPathTessellationShader::StencilPathSettings(GrFillRuleForSkPath(fPath));
Chris Dalton569c01b2021-05-25 10:11:46 -0600112
Chris Dalton917f9192021-06-08 14:32:37 -0600113 if (fPath.countVerbs() > 50 && this->bounds().height() * this->bounds().width() > 256 * 256) {
Chris Daltond2b8ba32021-06-09 00:12:59 -0600114 // Large complex paths do better with a dedicated triangle shader for the inner fan.
115 // This takes less PCI bus bandwidth (6 floats per triangle instead of 8) and allows us
116 // to make sure it has an efficient middle-out topology.
Chris Dalton917f9192021-06-08 14:32:37 -0600117 auto shader = GrPathTessellationShader::MakeSimpleTriangleShader(
118 args.fArena, fViewMatrix, SK_PMColor4fTRANSPARENT);
119 fStencilFanProgram = GrTessellationShader::MakeProgram(args, shader, stencilPipeline,
120 stencilPathSettings);
Chris Daltond2b8ba32021-06-09 00:12:59 -0600121 fTessellator = GrPathCurveTessellator::Make(args.fArena, fViewMatrix,
122 SK_PMColor4fTRANSPARENT,
123 GrPathCurveTessellator::DrawInnerFan::kNo,
Chris Dalton198ac152021-06-09 13:49:43 -0600124 fPath.countVerbs(), *stencilPipeline,
125 *args.fCaps);
Chris Dalton917f9192021-06-08 14:32:37 -0600126 } else {
Chris Dalton2f733ec2021-06-01 12:11:57 -0600127 fTessellator = GrPathWedgeTessellator::Make(args.fArena, fViewMatrix,
Chris Daltond2b8ba32021-06-09 00:12:59 -0600128 SK_PMColor4fTRANSPARENT, fPath.countVerbs(),
Chris Dalton198ac152021-06-09 13:49:43 -0600129 *stencilPipeline, *args.fCaps);
Chris Dalton70a0d2c2021-01-26 12:01:21 -0700130 }
Chris Dalton2f733ec2021-06-01 12:11:57 -0600131 fStencilPathProgram = GrTessellationShader::MakeProgram(args, fTessellator->shader(),
132 stencilPipeline, stencilPathSettings);
Chris Dalton569c01b2021-05-25 10:11:46 -0600133
Chris Dalton917f9192021-06-08 14:32:37 -0600134 if (!(fPathFlags & PathFlags::kStencilOnly)) {
Chris Dalton70a0d2c2021-01-26 12:01:21 -0700135 // Create a program that draws a bounding box over the path and fills its stencil coverage
136 // into the color buffer.
Chris Daltona05ccc32021-06-29 19:42:13 -0600137 auto* bboxShader = args.fArena->make<BoundingBoxShader>(fViewMatrix, fColor,
138 *args.fCaps->shaderCaps());
Chris Dalton2f733ec2021-06-01 12:11:57 -0600139 auto* bboxPipeline = GrTessellationShader::MakePipeline(args, fAAType,
140 std::move(appliedClip),
141 std::move(fProcessors));
Chris Daltonbaae2dd2021-06-25 14:52:49 -0600142 auto* bboxStencil =
143 GrPathTessellationShader::TestAndResetStencilSettings(fPath.isInverseFillType());
Chris Dalton031d76b2021-06-08 16:32:00 -0600144 fCoverBBoxProgram = GrTessellationShader::MakeProgram(args, bboxShader, bboxPipeline,
145 bboxStencil);
Chris Dalton70a0d2c2021-01-26 12:01:21 -0700146 }
147}
148
Chris Dalton031d76b2021-06-08 16:32:00 -0600149void GrPathStencilCoverOp::onPrePrepare(GrRecordingContext* context,
150 const GrSurfaceProxyView& writeView, GrAppliedClip* clip,
151 const GrDstProxyView& dstProxyView,
152 GrXferBarrierFlags renderPassXferBarriers,
153 GrLoadOp colorLoadOp) {
Chris Dalton70a0d2c2021-01-26 12:01:21 -0700154 this->prePreparePrograms({context->priv().recordTimeAllocator(), writeView, &dstProxyView,
155 renderPassXferBarriers, colorLoadOp, context->priv().caps()},
156 (clip) ? std::move(*clip) : GrAppliedClip::Disabled());
157 if (fStencilFanProgram) {
158 context->priv().recordProgramInfo(fStencilFanProgram);
159 }
160 if (fStencilPathProgram) {
161 context->priv().recordProgramInfo(fStencilPathProgram);
162 }
Chris Dalton031d76b2021-06-08 16:32:00 -0600163 if (fCoverBBoxProgram) {
164 context->priv().recordProgramInfo(fCoverBBoxProgram);
Chris Dalton70a0d2c2021-01-26 12:01:21 -0700165 }
166}
167
Chris Daltona05ccc32021-06-29 19:42:13 -0600168GR_DECLARE_STATIC_UNIQUE_KEY(gUnitQuadBufferKey);
169
Chris Dalton031d76b2021-06-08 16:32:00 -0600170void GrPathStencilCoverOp::onPrepare(GrOpFlushState* flushState) {
Chris Dalton70a0d2c2021-01-26 12:01:21 -0700171 if (!fTessellator) {
172 this->prePreparePrograms({flushState->allocator(), flushState->writeView(),
173 &flushState->dstProxyView(), flushState->renderPassBarriers(),
174 flushState->colorLoadOp(), &flushState->caps()},
175 flushState->detachAppliedClip());
176 if (!fTessellator) {
177 return;
178 }
179 }
180
181 if (fStencilFanProgram) {
182 // The inner fan isn't built into the tessellator. Generate a standard Redbook fan with a
183 // middle-out topology.
184 GrEagerDynamicVertexAllocator vertexAlloc(flushState, &fFanBuffer, &fFanBaseVertex);
185 int maxFanTriangles = fPath.countVerbs() - 2; // n - 2 triangles make an n-gon.
Chris Dalton8731a712021-05-14 14:48:54 -0600186 GrVertexWriter triangleVertexWriter = vertexAlloc.lock<SkPoint>(maxFanTriangles * 3);
Chris Dalton70a0d2c2021-01-26 12:01:21 -0700187 fFanVertexCount = GrMiddleOutPolygonTriangulator::WritePathInnerFan(
Chris Daltondf2dbad2021-05-14 16:21:15 -0600188 &triangleVertexWriter, GrMiddleOutPolygonTriangulator::OutputType::kTriangles,
189 fPath) * 3;
Chris Dalton70a0d2c2021-01-26 12:01:21 -0700190 SkASSERT(fFanVertexCount <= maxFanTriangles * 3);
191 vertexAlloc.unlock(fFanVertexCount);
192 }
193
Chris Dalton569c01b2021-05-25 10:11:46 -0600194 fTessellator->prepare(flushState, this->bounds(), fPath);
Chris Daltonc91dd692021-05-24 15:04:47 -0600195
Chris Dalton031d76b2021-06-08 16:32:00 -0600196 if (fCoverBBoxProgram) {
Chris Daltonc91dd692021-05-24 15:04:47 -0600197 GrVertexWriter vertexWriter = flushState->makeVertexSpace(sizeof(SkRect), 1, &fBBoxBuffer,
198 &fBBoxBaseInstance);
Chris Daltonbaae2dd2021-06-25 14:52:49 -0600199 if (fPath.isInverseFillType()) {
200 // Fill the entire backing store to make sure we clear every stencil value back to 0. If
201 // there is a scissor it will have already clipped the stencil draw.
202 auto rtBounds = flushState->writeView().asRenderTargetProxy()->backingStoreBoundsRect();
203 SkASSERT(rtBounds == fOriginalDrawBounds);
204 SkRect pathSpaceRTBounds;
205 if (SkMatrixPriv::InverseMapRect(fViewMatrix, &pathSpaceRTBounds, rtBounds)) {
206 vertexWriter.write(pathSpaceRTBounds);
207 } else {
208 vertexWriter.write(fPath.getBounds());
209 }
210 } else {
211 vertexWriter.write(fPath.getBounds());
212 }
Chris Daltonc91dd692021-05-24 15:04:47 -0600213 }
Chris Daltona05ccc32021-06-29 19:42:13 -0600214
215 if (!flushState->caps().shaderCaps()->vertexIDSupport()) {
216 constexpr static SkPoint kUnitQuad[4] = {{0,0}, {0,1}, {1,0}, {1,1}};
217
218 GR_DEFINE_STATIC_UNIQUE_KEY(gUnitQuadBufferKey);
219
220 fBBoxVertexBufferIfNoIDSupport = flushState->resourceProvider()->findOrMakeStaticBuffer(
221 GrGpuBufferType::kVertex, sizeof(kUnitQuad), kUnitQuad, gUnitQuadBufferKey);
222 }
Chris Dalton70a0d2c2021-01-26 12:01:21 -0700223}
224
Chris Dalton031d76b2021-06-08 16:32:00 -0600225void GrPathStencilCoverOp::onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) {
Chris Dalton70a0d2c2021-01-26 12:01:21 -0700226 if (!fTessellator) {
227 return;
228 }
229
230 // Stencil the inner fan, if any.
231 if (fFanVertexCount > 0) {
232 SkASSERT(fStencilFanProgram);
233 SkASSERT(fFanBuffer);
234 flushState->bindPipelineAndScissorClip(*fStencilFanProgram, this->bounds());
235 flushState->bindBuffers(nullptr, nullptr, fFanBuffer);
236 flushState->draw(fFanVertexCount, fFanBaseVertex);
237 }
238
239 // Stencil the rest of the path.
240 SkASSERT(fStencilPathProgram);
241 flushState->bindPipelineAndScissorClip(*fStencilPathProgram, this->bounds());
242 fTessellator->draw(flushState);
Chris Daltond9bdc322021-06-01 19:22:05 -0600243 if (flushState->caps().requiresManualFBBarrierAfterTessellatedStencilDraw()) {
244 flushState->gpu()->insertManualFramebufferBarrier(); // http://skbug.com/9739
245 }
Chris Dalton70a0d2c2021-01-26 12:01:21 -0700246
247 // Fill in the bounding box (if not in stencil-only mode).
Chris Dalton031d76b2021-06-08 16:32:00 -0600248 if (fCoverBBoxProgram) {
249 flushState->bindPipelineAndScissorClip(*fCoverBBoxProgram, this->bounds());
250 flushState->bindTextures(fCoverBBoxProgram->geomProc(), nullptr,
251 fCoverBBoxProgram->pipeline());
Chris Daltona05ccc32021-06-29 19:42:13 -0600252 flushState->bindBuffers(nullptr, fBBoxBuffer, fBBoxVertexBufferIfNoIDSupport);
Chris Daltonc91dd692021-05-24 15:04:47 -0600253 flushState->drawInstanced(1, fBBoxBaseInstance, 4, 0);
Chris Dalton70a0d2c2021-01-26 12:01:21 -0700254 }
255}