blob: 77b6c07d09f3a45683eb931cc58bfe5ee755ede3 [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"
11#include "src/gpu/GrOpFlushState.h"
12#include "src/gpu/GrRecordingContextPriv.h"
13#include "src/gpu/tessellate/GrFillPathShader.h"
14#include "src/gpu/tessellate/GrMiddleOutPolygonTriangulator.h"
15#include "src/gpu/tessellate/GrPathTessellator.h"
16#include "src/gpu/tessellate/GrStencilPathShader.h"
17#include "src/gpu/tessellate/GrTessellationPathRenderer.h"
18
19using OpFlags = GrTessellationPathRenderer::OpFlags;
20
Chris Dalton2ed22fa2021-05-06 16:08:30 -060021void GrPathStencilFillOp::visitProxies(const VisitProxyFunc& fn) const {
Chris Dalton70a0d2c2021-01-26 12:01:21 -070022 if (fFillBBoxProgram) {
23 fFillBBoxProgram->pipeline().visitProxies(fn);
24 } else {
25 fProcessors.visitProxies(fn);
26 }
27}
28
Chris Dalton2ed22fa2021-05-06 16:08:30 -060029GrDrawOp::FixedFunctionFlags GrPathStencilFillOp::fixedFunctionFlags() const {
Chris Dalton70a0d2c2021-01-26 12:01:21 -070030 auto flags = FixedFunctionFlags::kUsesStencil;
31 if (fAAType != GrAAType::kNone) {
32 flags |= FixedFunctionFlags::kUsesHWAA;
33 }
34 return flags;
35}
36
Chris Dalton2ed22fa2021-05-06 16:08:30 -060037GrProcessorSet::Analysis GrPathStencilFillOp::finalize(const GrCaps& caps,
38 const GrAppliedClip* clip,
39 GrClampType clampType) {
Chris Dalton57ab06c2021-04-22 12:57:28 -060040 return fProcessors.finalize(fColor, GrProcessorAnalysisCoverage::kNone, clip, nullptr, caps,
41 clampType, &fColor);
Chris Dalton70a0d2c2021-01-26 12:01:21 -070042}
43
Chris Dalton2ed22fa2021-05-06 16:08:30 -060044void GrPathStencilFillOp::prePreparePrograms(const GrPathShader::ProgramArgs& args,
45 GrAppliedClip&& appliedClip) {
Chris Dalton70a0d2c2021-01-26 12:01:21 -070046 using DrawInnerFan = GrPathIndirectTessellator::DrawInnerFan;
47 SkASSERT(!fStencilFanProgram);
48 SkASSERT(!fStencilPathProgram);
49 SkASSERT(!fFillBBoxProgram);
50
51 int numVerbs = fPath.countVerbs();
52 if (numVerbs <= 0) {
53 return;
54 }
55
56 // When there are only a few verbs, it seems to always be fastest to make a single indirect draw
57 // that contains both the inner triangles and the outer curves, instead of using hardware
58 // tessellation. Also take this path if tessellation is not supported.
59 bool drawTrianglesAsIndirectCurveDraw = (numVerbs < 50);
60 const GrPipeline* stencilPassPipeline = GrStencilPathShader::MakeStencilPassPipeline(
61 args, fAAType, fOpFlags, appliedClip.hardClip());
62 if (drawTrianglesAsIndirectCurveDraw || (fOpFlags & OpFlags::kDisableHWTessellation)) {
63 fTessellator = args.fArena->make<GrPathIndirectTessellator>(
64 fViewMatrix, fPath, DrawInnerFan(drawTrianglesAsIndirectCurveDraw));
65 if (!drawTrianglesAsIndirectCurveDraw) {
66 fStencilFanProgram = GrStencilPathShader::MakeStencilProgram<GrStencilTriangleShader>(
67 args, fViewMatrix, stencilPassPipeline, fPath.getFillType());
68 }
69 fStencilPathProgram = GrStencilPathShader::MakeStencilProgram<GrMiddleOutCubicShader>(
70 args, fViewMatrix, stencilPassPipeline, fPath.getFillType());
71 } else {
72 // The caller should have sent Flags::kDisableHWTessellation if it was not supported.
73 SkASSERT(args.fCaps->shaderCaps()->tessellationSupport());
74 // Next see if we can split up the inner triangles and outer curves into two draw calls.
75 // This allows for a more efficient inner fan topology that can reduce the rasterizer load
76 // by a large margin on complex paths, but also causes greater CPU overhead due to the extra
77 // shader switches and draw calls.
78 // NOTE: Raster-edge work is 1-dimensional, so we sum height and width instead of
79 // multiplying.
80 SkScalar scales[2];
81 SkAssertResult(fViewMatrix.getMinMaxScales(scales)); // Will fail if perspective.
82 const SkRect& bounds = fPath.getBounds();
83 float rasterEdgeWork = (bounds.height() + bounds.width()) * scales[1] * fPath.countVerbs();
84 if (rasterEdgeWork > 300 * 300) {
85 fTessellator = args.fArena->make<GrPathOuterCurveTessellator>();
86 fStencilFanProgram = GrStencilPathShader::MakeStencilProgram<GrStencilTriangleShader>(
87 args, fViewMatrix, stencilPassPipeline, fPath.getFillType());
88 fStencilPathProgram = GrStencilPathShader::MakeStencilProgram<GrCubicTessellateShader>(
89 args, fViewMatrix, stencilPassPipeline, fPath.getFillType());
90 } else {
91 // Fastest CPU approach: emit one cubic wedge per verb, fanning out from the center.
92 fTessellator = args.fArena->make<GrPathWedgeTessellator>();
93 fStencilPathProgram = GrStencilPathShader::MakeStencilProgram<GrWedgeTessellateShader>(
94 args, fViewMatrix, stencilPassPipeline, fPath.getFillType());
95 }
96 }
97
98 if (!(fOpFlags & OpFlags::kStencilOnly)) {
99 // Create a program that draws a bounding box over the path and fills its stencil coverage
100 // into the color buffer.
101 auto* bboxShader = args.fArena->make<GrFillBoundingBoxShader>(fViewMatrix, fColor,
102 fPath.getBounds());
103 auto* bboxPipeline = GrFillPathShader::MakeFillPassPipeline(args, fAAType,
104 std::move(appliedClip),
105 std::move(fProcessors));
106 auto* bboxStencil = GrFillPathShader::TestAndResetStencilSettings();
107 fFillBBoxProgram = GrPathShader::MakeProgram(args, bboxShader, bboxPipeline, bboxStencil);
108 }
109}
110
Chris Dalton2ed22fa2021-05-06 16:08:30 -0600111void GrPathStencilFillOp::onPrePrepare(GrRecordingContext* context,
112 const GrSurfaceProxyView& writeView, GrAppliedClip* clip,
113 const GrXferProcessor::DstProxyView& dstProxyView,
114 GrXferBarrierFlags renderPassXferBarriers,
115 GrLoadOp colorLoadOp) {
Chris Dalton70a0d2c2021-01-26 12:01:21 -0700116 this->prePreparePrograms({context->priv().recordTimeAllocator(), writeView, &dstProxyView,
117 renderPassXferBarriers, colorLoadOp, context->priv().caps()},
118 (clip) ? std::move(*clip) : GrAppliedClip::Disabled());
119 if (fStencilFanProgram) {
120 context->priv().recordProgramInfo(fStencilFanProgram);
121 }
122 if (fStencilPathProgram) {
123 context->priv().recordProgramInfo(fStencilPathProgram);
124 }
125 if (fFillBBoxProgram) {
126 context->priv().recordProgramInfo(fFillBBoxProgram);
127 }
128}
129
Chris Dalton2ed22fa2021-05-06 16:08:30 -0600130void GrPathStencilFillOp::onPrepare(GrOpFlushState* flushState) {
Chris Dalton70a0d2c2021-01-26 12:01:21 -0700131 if (!fTessellator) {
132 this->prePreparePrograms({flushState->allocator(), flushState->writeView(),
133 &flushState->dstProxyView(), flushState->renderPassBarriers(),
134 flushState->colorLoadOp(), &flushState->caps()},
135 flushState->detachAppliedClip());
136 if (!fTessellator) {
137 return;
138 }
139 }
140
141 if (fStencilFanProgram) {
142 // The inner fan isn't built into the tessellator. Generate a standard Redbook fan with a
143 // middle-out topology.
144 GrEagerDynamicVertexAllocator vertexAlloc(flushState, &fFanBuffer, &fFanBaseVertex);
145 int maxFanTriangles = fPath.countVerbs() - 2; // n - 2 triangles make an n-gon.
Chris Dalton8731a712021-05-14 14:48:54 -0600146 GrVertexWriter triangleVertexWriter = vertexAlloc.lock<SkPoint>(maxFanTriangles * 3);
Chris Dalton70a0d2c2021-01-26 12:01:21 -0700147 fFanVertexCount = GrMiddleOutPolygonTriangulator::WritePathInnerFan(
Chris Dalton8731a712021-05-14 14:48:54 -0600148 &triangleVertexWriter, 3/*perTriangleVertexAdvance*/, fPath) * 3;
Chris Dalton70a0d2c2021-01-26 12:01:21 -0700149 SkASSERT(fFanVertexCount <= maxFanTriangles * 3);
150 vertexAlloc.unlock(fFanVertexCount);
151 }
152
153 fTessellator->prepare(flushState, fViewMatrix, fPath);
154}
155
Chris Dalton2ed22fa2021-05-06 16:08:30 -0600156void GrPathStencilFillOp::onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) {
Chris Dalton70a0d2c2021-01-26 12:01:21 -0700157 if (!fTessellator) {
158 return;
159 }
160
161 // Stencil the inner fan, if any.
162 if (fFanVertexCount > 0) {
163 SkASSERT(fStencilFanProgram);
164 SkASSERT(fFanBuffer);
165 flushState->bindPipelineAndScissorClip(*fStencilFanProgram, this->bounds());
166 flushState->bindBuffers(nullptr, nullptr, fFanBuffer);
167 flushState->draw(fFanVertexCount, fFanBaseVertex);
168 }
169
170 // Stencil the rest of the path.
171 SkASSERT(fStencilPathProgram);
172 flushState->bindPipelineAndScissorClip(*fStencilPathProgram, this->bounds());
173 fTessellator->draw(flushState);
174
175 // Fill in the bounding box (if not in stencil-only mode).
176 if (fFillBBoxProgram) {
177 flushState->bindPipelineAndScissorClip(*fFillBBoxProgram, this->bounds());
Robert Phillips787fd9d2021-03-22 14:48:09 -0400178 flushState->bindTextures(fFillBBoxProgram->geomProc(), nullptr,
Chris Dalton70a0d2c2021-01-26 12:01:21 -0700179 fFillBBoxProgram->pipeline());
180 flushState->bindBuffers(nullptr, nullptr, nullptr);
181 flushState->draw(4, 0);
182 }
183}