blob: 71f7b5e62e904f7df6f3e09a677587fb5b9fce3b [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 Daltonb03f4a12021-01-27 17:45:52 -07008#include "src/gpu/tessellate/GrTessellatingStencilFillOp.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 Daltonb03f4a12021-01-27 17:45:52 -070021void GrTessellatingStencilFillOp::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 Daltonb03f4a12021-01-27 17:45:52 -070029GrDrawOp::FixedFunctionFlags GrTessellatingStencilFillOp::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 Daltonb03f4a12021-01-27 17:45:52 -070037GrProcessorSet::Analysis GrTessellatingStencilFillOp::finalize(const GrCaps& caps,
38 const GrAppliedClip* clip,
39 bool hasMixedSampledCoverage,
40 GrClampType clampType) {
Chris Dalton70a0d2c2021-01-26 12:01:21 -070041 return fProcessors.finalize(fColor, GrProcessorAnalysisCoverage::kNone, clip, nullptr,
42 hasMixedSampledCoverage, caps, clampType, &fColor);
43}
44
Chris Daltonb03f4a12021-01-27 17:45:52 -070045void GrTessellatingStencilFillOp::prePreparePrograms(const GrPathShader::ProgramArgs& args,
46 GrAppliedClip&& appliedClip) {
Chris Dalton70a0d2c2021-01-26 12:01:21 -070047 using DrawInnerFan = GrPathIndirectTessellator::DrawInnerFan;
48 SkASSERT(!fStencilFanProgram);
49 SkASSERT(!fStencilPathProgram);
50 SkASSERT(!fFillBBoxProgram);
51
52 int numVerbs = fPath.countVerbs();
53 if (numVerbs <= 0) {
54 return;
55 }
56
57 // When there are only a few verbs, it seems to always be fastest to make a single indirect draw
58 // that contains both the inner triangles and the outer curves, instead of using hardware
59 // tessellation. Also take this path if tessellation is not supported.
60 bool drawTrianglesAsIndirectCurveDraw = (numVerbs < 50);
61 const GrPipeline* stencilPassPipeline = GrStencilPathShader::MakeStencilPassPipeline(
62 args, fAAType, fOpFlags, appliedClip.hardClip());
63 if (drawTrianglesAsIndirectCurveDraw || (fOpFlags & OpFlags::kDisableHWTessellation)) {
64 fTessellator = args.fArena->make<GrPathIndirectTessellator>(
65 fViewMatrix, fPath, DrawInnerFan(drawTrianglesAsIndirectCurveDraw));
66 if (!drawTrianglesAsIndirectCurveDraw) {
67 fStencilFanProgram = GrStencilPathShader::MakeStencilProgram<GrStencilTriangleShader>(
68 args, fViewMatrix, stencilPassPipeline, fPath.getFillType());
69 }
70 fStencilPathProgram = GrStencilPathShader::MakeStencilProgram<GrMiddleOutCubicShader>(
71 args, fViewMatrix, stencilPassPipeline, fPath.getFillType());
72 } else {
73 // The caller should have sent Flags::kDisableHWTessellation if it was not supported.
74 SkASSERT(args.fCaps->shaderCaps()->tessellationSupport());
75 // Next see if we can split up the inner triangles and outer curves into two draw calls.
76 // This allows for a more efficient inner fan topology that can reduce the rasterizer load
77 // by a large margin on complex paths, but also causes greater CPU overhead due to the extra
78 // shader switches and draw calls.
79 // NOTE: Raster-edge work is 1-dimensional, so we sum height and width instead of
80 // multiplying.
81 SkScalar scales[2];
82 SkAssertResult(fViewMatrix.getMinMaxScales(scales)); // Will fail if perspective.
83 const SkRect& bounds = fPath.getBounds();
84 float rasterEdgeWork = (bounds.height() + bounds.width()) * scales[1] * fPath.countVerbs();
85 if (rasterEdgeWork > 300 * 300) {
86 fTessellator = args.fArena->make<GrPathOuterCurveTessellator>();
87 fStencilFanProgram = GrStencilPathShader::MakeStencilProgram<GrStencilTriangleShader>(
88 args, fViewMatrix, stencilPassPipeline, fPath.getFillType());
89 fStencilPathProgram = GrStencilPathShader::MakeStencilProgram<GrCubicTessellateShader>(
90 args, fViewMatrix, stencilPassPipeline, fPath.getFillType());
91 } else {
92 // Fastest CPU approach: emit one cubic wedge per verb, fanning out from the center.
93 fTessellator = args.fArena->make<GrPathWedgeTessellator>();
94 fStencilPathProgram = GrStencilPathShader::MakeStencilProgram<GrWedgeTessellateShader>(
95 args, fViewMatrix, stencilPassPipeline, fPath.getFillType());
96 }
97 }
98
99 if (!(fOpFlags & OpFlags::kStencilOnly)) {
100 // Create a program that draws a bounding box over the path and fills its stencil coverage
101 // into the color buffer.
102 auto* bboxShader = args.fArena->make<GrFillBoundingBoxShader>(fViewMatrix, fColor,
103 fPath.getBounds());
104 auto* bboxPipeline = GrFillPathShader::MakeFillPassPipeline(args, fAAType,
105 std::move(appliedClip),
106 std::move(fProcessors));
107 auto* bboxStencil = GrFillPathShader::TestAndResetStencilSettings();
108 fFillBBoxProgram = GrPathShader::MakeProgram(args, bboxShader, bboxPipeline, bboxStencil);
109 }
110}
111
Chris Daltonb03f4a12021-01-27 17:45:52 -0700112void GrTessellatingStencilFillOp::onPrePrepare(GrRecordingContext* context,
113 const GrSurfaceProxyView& writeView,
114 GrAppliedClip* clip,
115 const GrXferProcessor::DstProxyView& dstProxyView,
116 GrXferBarrierFlags renderPassXferBarriers,
117 GrLoadOp colorLoadOp) {
Chris Dalton70a0d2c2021-01-26 12:01:21 -0700118 this->prePreparePrograms({context->priv().recordTimeAllocator(), writeView, &dstProxyView,
119 renderPassXferBarriers, colorLoadOp, context->priv().caps()},
120 (clip) ? std::move(*clip) : GrAppliedClip::Disabled());
121 if (fStencilFanProgram) {
122 context->priv().recordProgramInfo(fStencilFanProgram);
123 }
124 if (fStencilPathProgram) {
125 context->priv().recordProgramInfo(fStencilPathProgram);
126 }
127 if (fFillBBoxProgram) {
128 context->priv().recordProgramInfo(fFillBBoxProgram);
129 }
130}
131
Chris Daltonb03f4a12021-01-27 17:45:52 -0700132void GrTessellatingStencilFillOp::onPrepare(GrOpFlushState* flushState) {
Chris Dalton70a0d2c2021-01-26 12:01:21 -0700133 if (!fTessellator) {
134 this->prePreparePrograms({flushState->allocator(), flushState->writeView(),
135 &flushState->dstProxyView(), flushState->renderPassBarriers(),
136 flushState->colorLoadOp(), &flushState->caps()},
137 flushState->detachAppliedClip());
138 if (!fTessellator) {
139 return;
140 }
141 }
142
143 if (fStencilFanProgram) {
144 // The inner fan isn't built into the tessellator. Generate a standard Redbook fan with a
145 // middle-out topology.
146 GrEagerDynamicVertexAllocator vertexAlloc(flushState, &fFanBuffer, &fFanBaseVertex);
147 int maxFanTriangles = fPath.countVerbs() - 2; // n - 2 triangles make an n-gon.
148 auto* triangleVertexData = vertexAlloc.lock<SkPoint>(maxFanTriangles * 3);
149 fFanVertexCount = GrMiddleOutPolygonTriangulator::WritePathInnerFan(
150 triangleVertexData, 3/*perTriangleVertexAdvance*/, fPath) * 3;
151 SkASSERT(fFanVertexCount <= maxFanTriangles * 3);
152 vertexAlloc.unlock(fFanVertexCount);
153 }
154
155 fTessellator->prepare(flushState, fViewMatrix, fPath);
156}
157
Chris Daltonb03f4a12021-01-27 17:45:52 -0700158void GrTessellatingStencilFillOp::onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) {
Chris Dalton70a0d2c2021-01-26 12:01:21 -0700159 if (!fTessellator) {
160 return;
161 }
162
163 // Stencil the inner fan, if any.
164 if (fFanVertexCount > 0) {
165 SkASSERT(fStencilFanProgram);
166 SkASSERT(fFanBuffer);
167 flushState->bindPipelineAndScissorClip(*fStencilFanProgram, this->bounds());
168 flushState->bindBuffers(nullptr, nullptr, fFanBuffer);
169 flushState->draw(fFanVertexCount, fFanBaseVertex);
170 }
171
172 // Stencil the rest of the path.
173 SkASSERT(fStencilPathProgram);
174 flushState->bindPipelineAndScissorClip(*fStencilPathProgram, this->bounds());
175 fTessellator->draw(flushState);
176
177 // Fill in the bounding box (if not in stencil-only mode).
178 if (fFillBBoxProgram) {
179 flushState->bindPipelineAndScissorClip(*fFillBBoxProgram, this->bounds());
Robert Phillips787fd9d2021-03-22 14:48:09 -0400180 flushState->bindTextures(fFillBBoxProgram->geomProc(), nullptr,
Chris Dalton70a0d2c2021-01-26 12:01:21 -0700181 fFillBBoxProgram->pipeline());
182 flushState->bindBuffers(nullptr, nullptr, nullptr);
183 flushState->draw(4, 0);
184 }
185}