blob: b68d96f0ce15b36d31d1df4164fc3bec00f0a61a [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"
Robert Phillips1a82a4e2021-07-01 10:27:44 -040014#include "src/gpu/GrResourceProvider.h"
Chris Daltona05ccc32021-06-29 19:42:13 -060015#include "src/gpu/glsl/GrGLSLProgramBuilder.h"
Chris Dalton2f733ec2021-06-01 12:11:57 -060016#include "src/gpu/glsl/GrGLSLVertexGeoBuilder.h"
Chris Dalton70a0d2c2021-01-26 12:01:21 -070017#include "src/gpu/tessellate/GrMiddleOutPolygonTriangulator.h"
Chris Daltond9bdc322021-06-01 19:22:05 -060018#include "src/gpu/tessellate/GrPathCurveTessellator.h"
Chris Daltond9bdc322021-06-01 19:22:05 -060019#include "src/gpu/tessellate/GrPathWedgeTessellator.h"
Chris Dalton70a0d2c2021-01-26 12:01:21 -070020#include "src/gpu/tessellate/GrTessellationPathRenderer.h"
Chris Dalton3b412782021-06-01 13:40:03 -060021#include "src/gpu/tessellate/shaders/GrPathTessellationShader.h"
Chris Dalton70a0d2c2021-01-26 12:01:21 -070022
Chris Dalton917f9192021-06-08 14:32:37 -060023using PathFlags = GrTessellationPathRenderer::PathFlags;
Chris Dalton70a0d2c2021-01-26 12:01:21 -070024
Chris Dalton2f733ec2021-06-01 12:11:57 -060025namespace {
26
27// Fills a path's bounding box, with subpixel outset to avoid possible T-junctions with extreme
28// edges of the path.
29// NOTE: The emitted geometry may not be axis-aligned, depending on the view matrix.
30class BoundingBoxShader : public GrPathTessellationShader {
31public:
Chris Daltona05ccc32021-06-29 19:42:13 -060032 BoundingBoxShader(const SkMatrix& viewMatrix, SkPMColor4f color, const GrShaderCaps& shaderCaps)
Chris Dalton2f733ec2021-06-01 12:11:57 -060033 : GrPathTessellationShader(kTessellate_BoundingBoxShader_ClassID,
34 GrPrimitiveType::kTriangleStrip, 0, viewMatrix, color) {
Chris Daltona05ccc32021-06-29 19:42:13 -060035 constexpr static Attribute kPathBoundsAttrib("pathBounds", kFloat4_GrVertexAttribType,
36 kFloat4_GrSLType);
Chris Dalton2f733ec2021-06-01 12:11:57 -060037 this->setInstanceAttributes(&kPathBoundsAttrib, 1);
Chris Daltona05ccc32021-06-29 19:42:13 -060038 if (!shaderCaps.vertexIDSupport()) {
39 constexpr static Attribute kUnitCoordAttrib("unitCoord", kFloat2_GrVertexAttribType,
40 kFloat2_GrSLType);
41 this->setVertexAttributes(&kUnitCoordAttrib, 1);
42 }
Chris Dalton2f733ec2021-06-01 12:11:57 -060043 }
44
45private:
Chris Daltonb63711a2021-06-01 14:52:02 -060046 const char* name() const final { return "tessellate_BoundingBoxShader"; }
Chris Dalton2f733ec2021-06-01 12:11:57 -060047 void getGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const final {}
48 GrGLSLGeometryProcessor* createGLSLInstance(const GrShaderCaps&) const final;
49};
50
51GrGLSLGeometryProcessor* BoundingBoxShader::createGLSLInstance(const GrShaderCaps&) const {
52 class Impl : public GrPathTessellationShader::Impl {
Chris Daltond2b8ba32021-06-09 00:12:59 -060053 void emitVertexCode(const GrPathTessellationShader&, GrGLSLVertexBuilder* v,
54 GrGPArgs* gpArgs) override {
Chris Daltona05ccc32021-06-29 19:42:13 -060055 if (v->getProgramBuilder()->caps()->shaderCaps()->vertexIDSupport()) {
56 // If we don't have sk_VertexID support then "unitCoord" already came in as a vertex
57 // attrib.
58 v->codeAppendf(R"(
59 float2 unitCoord = float2(sk_VertexID & 1, sk_VertexID >> 1);)");
60 }
61
Chris Dalton2f733ec2021-06-01 12:11:57 -060062 v->codeAppend(R"(
63 // Bloat the bounding box by 1/4px to avoid potential T-junctions at the edges.
64 float2x2 M_ = inverse(AFFINE_MATRIX);
65 float2 bloat = float2(abs(M_[0]) + abs(M_[1])) * .25;
66
67 // Find the vertex position.
Chris Daltona05ccc32021-06-29 19:42:13 -060068 float2 localcoord = mix(pathBounds.xy - bloat, pathBounds.zw + bloat, unitCoord);
Chris Dalton2f733ec2021-06-01 12:11:57 -060069 float2 vertexpos = AFFINE_MATRIX * localcoord + TRANSLATE;)");
70 gpArgs->fLocalCoordVar.set(kFloat2_GrSLType, "localcoord");
71 gpArgs->fPositionVar.set(kFloat2_GrSLType, "vertexpos");
72 }
73 };
74 return new Impl;
75}
76
77} // namespace
78
Robert Phillips294723d2021-06-17 09:23:58 -040079void GrPathStencilCoverOp::visitProxies(const GrVisitProxyFunc& func) const {
Chris Dalton031d76b2021-06-08 16:32:00 -060080 if (fCoverBBoxProgram) {
Robert Phillips294723d2021-06-17 09:23:58 -040081 fCoverBBoxProgram->pipeline().visitProxies(func);
Chris Dalton70a0d2c2021-01-26 12:01:21 -070082 } else {
Robert Phillips294723d2021-06-17 09:23:58 -040083 fProcessors.visitProxies(func);
Chris Dalton70a0d2c2021-01-26 12:01:21 -070084 }
85}
86
Chris Dalton031d76b2021-06-08 16:32:00 -060087GrDrawOp::FixedFunctionFlags GrPathStencilCoverOp::fixedFunctionFlags() const {
Chris Dalton70a0d2c2021-01-26 12:01:21 -070088 auto flags = FixedFunctionFlags::kUsesStencil;
89 if (fAAType != GrAAType::kNone) {
90 flags |= FixedFunctionFlags::kUsesHWAA;
91 }
92 return flags;
93}
94
Chris Dalton031d76b2021-06-08 16:32:00 -060095GrProcessorSet::Analysis GrPathStencilCoverOp::finalize(const GrCaps& caps,
96 const GrAppliedClip* clip,
97 GrClampType clampType) {
Chris Dalton57ab06c2021-04-22 12:57:28 -060098 return fProcessors.finalize(fColor, GrProcessorAnalysisCoverage::kNone, clip, nullptr, caps,
99 clampType, &fColor);
Chris Dalton70a0d2c2021-01-26 12:01:21 -0700100}
101
Chris Dalton031d76b2021-06-08 16:32:00 -0600102void GrPathStencilCoverOp::prePreparePrograms(const GrTessellationShader::ProgramArgs& args,
103 GrAppliedClip&& appliedClip) {
Chris Dalton569c01b2021-05-25 10:11:46 -0600104 SkASSERT(!fTessellator);
Chris Dalton70a0d2c2021-01-26 12:01:21 -0700105 SkASSERT(!fStencilFanProgram);
106 SkASSERT(!fStencilPathProgram);
Chris Dalton031d76b2021-06-08 16:32:00 -0600107 SkASSERT(!fCoverBBoxProgram);
Chris Dalton70a0d2c2021-01-26 12:01:21 -0700108
Chris Dalton2f733ec2021-06-01 12:11:57 -0600109 const GrPipeline* stencilPipeline = GrPathTessellationShader::MakeStencilOnlyPipeline(
Chris Dalton917f9192021-06-08 14:32:37 -0600110 args, fAAType, fPathFlags, appliedClip.hardClip());
Chris Dalton2f733ec2021-06-01 12:11:57 -0600111 const GrUserStencilSettings* stencilPathSettings =
Chris Daltonbaae2dd2021-06-25 14:52:49 -0600112 GrPathTessellationShader::StencilPathSettings(GrFillRuleForSkPath(fPath));
Chris Dalton569c01b2021-05-25 10:11:46 -0600113
Chris Dalton917f9192021-06-08 14:32:37 -0600114 if (fPath.countVerbs() > 50 && this->bounds().height() * this->bounds().width() > 256 * 256) {
Chris Daltond2b8ba32021-06-09 00:12:59 -0600115 // Large complex paths do better with a dedicated triangle shader for the inner fan.
116 // This takes less PCI bus bandwidth (6 floats per triangle instead of 8) and allows us
117 // to make sure it has an efficient middle-out topology.
Chris Dalton917f9192021-06-08 14:32:37 -0600118 auto shader = GrPathTessellationShader::MakeSimpleTriangleShader(
119 args.fArena, fViewMatrix, SK_PMColor4fTRANSPARENT);
120 fStencilFanProgram = GrTessellationShader::MakeProgram(args, shader, stencilPipeline,
121 stencilPathSettings);
Chris Daltond2b8ba32021-06-09 00:12:59 -0600122 fTessellator = GrPathCurveTessellator::Make(args.fArena, fViewMatrix,
123 SK_PMColor4fTRANSPARENT,
124 GrPathCurveTessellator::DrawInnerFan::kNo,
Chris Dalton198ac152021-06-09 13:49:43 -0600125 fPath.countVerbs(), *stencilPipeline,
126 *args.fCaps);
Chris Dalton917f9192021-06-08 14:32:37 -0600127 } else {
Chris Dalton2f733ec2021-06-01 12:11:57 -0600128 fTessellator = GrPathWedgeTessellator::Make(args.fArena, fViewMatrix,
Chris Daltond2b8ba32021-06-09 00:12:59 -0600129 SK_PMColor4fTRANSPARENT, fPath.countVerbs(),
Chris Dalton198ac152021-06-09 13:49:43 -0600130 *stencilPipeline, *args.fCaps);
Chris Dalton70a0d2c2021-01-26 12:01:21 -0700131 }
Chris Dalton2f733ec2021-06-01 12:11:57 -0600132 fStencilPathProgram = GrTessellationShader::MakeProgram(args, fTessellator->shader(),
133 stencilPipeline, stencilPathSettings);
Chris Dalton569c01b2021-05-25 10:11:46 -0600134
Chris Dalton917f9192021-06-08 14:32:37 -0600135 if (!(fPathFlags & PathFlags::kStencilOnly)) {
Chris Dalton70a0d2c2021-01-26 12:01:21 -0700136 // Create a program that draws a bounding box over the path and fills its stencil coverage
137 // into the color buffer.
Chris Daltona05ccc32021-06-29 19:42:13 -0600138 auto* bboxShader = args.fArena->make<BoundingBoxShader>(fViewMatrix, fColor,
139 *args.fCaps->shaderCaps());
Chris Dalton2f733ec2021-06-01 12:11:57 -0600140 auto* bboxPipeline = GrTessellationShader::MakePipeline(args, fAAType,
141 std::move(appliedClip),
142 std::move(fProcessors));
Chris Daltonbaae2dd2021-06-25 14:52:49 -0600143 auto* bboxStencil =
144 GrPathTessellationShader::TestAndResetStencilSettings(fPath.isInverseFillType());
Chris Dalton031d76b2021-06-08 16:32:00 -0600145 fCoverBBoxProgram = GrTessellationShader::MakeProgram(args, bboxShader, bboxPipeline,
146 bboxStencil);
Chris Dalton70a0d2c2021-01-26 12:01:21 -0700147 }
148}
149
Chris Dalton031d76b2021-06-08 16:32:00 -0600150void GrPathStencilCoverOp::onPrePrepare(GrRecordingContext* context,
151 const GrSurfaceProxyView& writeView, GrAppliedClip* clip,
152 const GrDstProxyView& dstProxyView,
153 GrXferBarrierFlags renderPassXferBarriers,
154 GrLoadOp colorLoadOp) {
Chris Dalton70a0d2c2021-01-26 12:01:21 -0700155 this->prePreparePrograms({context->priv().recordTimeAllocator(), writeView, &dstProxyView,
156 renderPassXferBarriers, colorLoadOp, context->priv().caps()},
157 (clip) ? std::move(*clip) : GrAppliedClip::Disabled());
158 if (fStencilFanProgram) {
159 context->priv().recordProgramInfo(fStencilFanProgram);
160 }
161 if (fStencilPathProgram) {
162 context->priv().recordProgramInfo(fStencilPathProgram);
163 }
Chris Dalton031d76b2021-06-08 16:32:00 -0600164 if (fCoverBBoxProgram) {
165 context->priv().recordProgramInfo(fCoverBBoxProgram);
Chris Dalton70a0d2c2021-01-26 12:01:21 -0700166 }
167}
168
Chris Daltona05ccc32021-06-29 19:42:13 -0600169GR_DECLARE_STATIC_UNIQUE_KEY(gUnitQuadBufferKey);
170
Chris Dalton031d76b2021-06-08 16:32:00 -0600171void GrPathStencilCoverOp::onPrepare(GrOpFlushState* flushState) {
Chris Dalton70a0d2c2021-01-26 12:01:21 -0700172 if (!fTessellator) {
173 this->prePreparePrograms({flushState->allocator(), flushState->writeView(),
174 &flushState->dstProxyView(), flushState->renderPassBarriers(),
175 flushState->colorLoadOp(), &flushState->caps()},
176 flushState->detachAppliedClip());
177 if (!fTessellator) {
178 return;
179 }
180 }
181
182 if (fStencilFanProgram) {
183 // The inner fan isn't built into the tessellator. Generate a standard Redbook fan with a
184 // middle-out topology.
185 GrEagerDynamicVertexAllocator vertexAlloc(flushState, &fFanBuffer, &fFanBaseVertex);
186 int maxFanTriangles = fPath.countVerbs() - 2; // n - 2 triangles make an n-gon.
Chris Dalton8731a712021-05-14 14:48:54 -0600187 GrVertexWriter triangleVertexWriter = vertexAlloc.lock<SkPoint>(maxFanTriangles * 3);
Chris Dalton70a0d2c2021-01-26 12:01:21 -0700188 fFanVertexCount = GrMiddleOutPolygonTriangulator::WritePathInnerFan(
Chris Daltondf2dbad2021-05-14 16:21:15 -0600189 &triangleVertexWriter, GrMiddleOutPolygonTriangulator::OutputType::kTriangles,
190 fPath) * 3;
Chris Dalton70a0d2c2021-01-26 12:01:21 -0700191 SkASSERT(fFanVertexCount <= maxFanTriangles * 3);
192 vertexAlloc.unlock(fFanVertexCount);
193 }
194
Chris Dalton569c01b2021-05-25 10:11:46 -0600195 fTessellator->prepare(flushState, this->bounds(), fPath);
Chris Daltonc91dd692021-05-24 15:04:47 -0600196
Chris Dalton031d76b2021-06-08 16:32:00 -0600197 if (fCoverBBoxProgram) {
Chris Daltonc91dd692021-05-24 15:04:47 -0600198 GrVertexWriter vertexWriter = flushState->makeVertexSpace(sizeof(SkRect), 1, &fBBoxBuffer,
199 &fBBoxBaseInstance);
Chris Daltonbaae2dd2021-06-25 14:52:49 -0600200 if (fPath.isInverseFillType()) {
201 // Fill the entire backing store to make sure we clear every stencil value back to 0. If
202 // there is a scissor it will have already clipped the stencil draw.
203 auto rtBounds = flushState->writeView().asRenderTargetProxy()->backingStoreBoundsRect();
204 SkASSERT(rtBounds == fOriginalDrawBounds);
205 SkRect pathSpaceRTBounds;
206 if (SkMatrixPriv::InverseMapRect(fViewMatrix, &pathSpaceRTBounds, rtBounds)) {
207 vertexWriter.write(pathSpaceRTBounds);
208 } else {
209 vertexWriter.write(fPath.getBounds());
210 }
211 } else {
212 vertexWriter.write(fPath.getBounds());
213 }
Chris Daltonc91dd692021-05-24 15:04:47 -0600214 }
Chris Daltona05ccc32021-06-29 19:42:13 -0600215
216 if (!flushState->caps().shaderCaps()->vertexIDSupport()) {
217 constexpr static SkPoint kUnitQuad[4] = {{0,0}, {0,1}, {1,0}, {1,1}};
218
219 GR_DEFINE_STATIC_UNIQUE_KEY(gUnitQuadBufferKey);
220
221 fBBoxVertexBufferIfNoIDSupport = flushState->resourceProvider()->findOrMakeStaticBuffer(
222 GrGpuBufferType::kVertex, sizeof(kUnitQuad), kUnitQuad, gUnitQuadBufferKey);
223 }
Chris Dalton70a0d2c2021-01-26 12:01:21 -0700224}
225
Chris Dalton031d76b2021-06-08 16:32:00 -0600226void GrPathStencilCoverOp::onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) {
Chris Dalton70a0d2c2021-01-26 12:01:21 -0700227 if (!fTessellator) {
228 return;
229 }
230
231 // Stencil the inner fan, if any.
232 if (fFanVertexCount > 0) {
233 SkASSERT(fStencilFanProgram);
234 SkASSERT(fFanBuffer);
235 flushState->bindPipelineAndScissorClip(*fStencilFanProgram, this->bounds());
236 flushState->bindBuffers(nullptr, nullptr, fFanBuffer);
237 flushState->draw(fFanVertexCount, fFanBaseVertex);
238 }
239
240 // Stencil the rest of the path.
241 SkASSERT(fStencilPathProgram);
242 flushState->bindPipelineAndScissorClip(*fStencilPathProgram, this->bounds());
243 fTessellator->draw(flushState);
Chris Daltond9bdc322021-06-01 19:22:05 -0600244 if (flushState->caps().requiresManualFBBarrierAfterTessellatedStencilDraw()) {
245 flushState->gpu()->insertManualFramebufferBarrier(); // http://skbug.com/9739
246 }
Chris Dalton70a0d2c2021-01-26 12:01:21 -0700247
248 // Fill in the bounding box (if not in stencil-only mode).
Chris Dalton031d76b2021-06-08 16:32:00 -0600249 if (fCoverBBoxProgram) {
250 flushState->bindPipelineAndScissorClip(*fCoverBBoxProgram, this->bounds());
251 flushState->bindTextures(fCoverBBoxProgram->geomProc(), nullptr,
252 fCoverBBoxProgram->pipeline());
Chris Daltona05ccc32021-06-29 19:42:13 -0600253 flushState->bindBuffers(nullptr, fBBoxBuffer, fBBoxVertexBufferIfNoIDSupport);
Chris Daltonc91dd692021-05-24 15:04:47 -0600254 flushState->drawInstanced(1, fBBoxBaseInstance, 4, 0);
Chris Dalton70a0d2c2021-01-26 12:01:21 -0700255 }
256}