blob: c340ae97bb67df403b3a8d218dd1a2fe8091e85e [file] [log] [blame]
Chris Daltonebb37e72021-01-27 17:59:45 -07001/*
2 * Copyright 2019 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
8#include "src/gpu/tessellate/GrPathInnerTriangulateOp.h"
9
10#include "src/gpu/GrEagerVertexAllocator.h"
Chris Daltond9bdc322021-06-01 19:22:05 -060011#include "src/gpu/GrGpu.h"
Chris Daltonebb37e72021-01-27 17:59:45 -070012#include "src/gpu/GrInnerFanTriangulator.h"
13#include "src/gpu/GrOpFlushState.h"
14#include "src/gpu/GrRecordingContextPriv.h"
Chris Dalton2f733ec2021-06-01 12:11:57 -060015#include "src/gpu/glsl/GrGLSLVertexGeoBuilder.h"
Chris Daltond9bdc322021-06-01 19:22:05 -060016#include "src/gpu/tessellate/GrPathCurveTessellator.h"
17#include "src/gpu/tessellate/GrPathIndirectTessellator.h"
Chris Daltonebb37e72021-01-27 17:59:45 -070018#include "src/gpu/tessellate/GrTessellationPathRenderer.h"
Chris Dalton3b412782021-06-01 13:40:03 -060019#include "src/gpu/tessellate/shaders/GrPathTessellationShader.h"
Chris Daltonebb37e72021-01-27 17:59:45 -070020
Chris Dalton917f9192021-06-08 14:32:37 -060021using PathFlags = GrTessellationPathRenderer::PathFlags;
Chris Daltonebb37e72021-01-27 17:59:45 -070022
Chris Dalton2f733ec2021-06-01 12:11:57 -060023namespace {
24
25// Fills an array of convex hulls surrounding 4-point cubic or conic instances. This shader is used
26// for the "fill" pass after the curves have been fully stencilled.
27class HullShader : public GrPathTessellationShader {
28public:
29 HullShader(const SkMatrix& viewMatrix, SkPMColor4f color)
30 : GrPathTessellationShader(kTessellate_HullShader_ClassID,
31 GrPrimitiveType::kTriangleStrip, 0, viewMatrix, color) {
32 constexpr static Attribute kPtsAttribs[] = {
33 {"input_points_0_1", kFloat4_GrVertexAttribType, kFloat4_GrSLType},
34 {"input_points_2_3", kFloat4_GrVertexAttribType, kFloat4_GrSLType}};
35 this->setInstanceAttributes(kPtsAttribs, SK_ARRAY_COUNT(kPtsAttribs));
36 }
37
38private:
Chris Daltonb63711a2021-06-01 14:52:02 -060039 const char* name() const final { return "tessellate_HullShader"; }
Chris Dalton2f733ec2021-06-01 12:11:57 -060040 void getGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const final {}
41 GrGLSLGeometryProcessor* createGLSLInstance(const GrShaderCaps&) const final;
42};
43
44GrGLSLGeometryProcessor* HullShader::createGLSLInstance(const GrShaderCaps&) const {
45 class Impl : public GrPathTessellationShader::Impl {
46 void emitVertexCode(GrGLSLVertexBuilder* v, GrGPArgs* gpArgs) override {
47 v->codeAppend(R"(
48 float4x2 P = float4x2(input_points_0_1, input_points_2_3);
49 if (isinf(P[3].y)) { // Is the curve a conic?
50 float w = P[3].x;
51 if (isinf(w)) {
52 // A conic with w=Inf is an exact triangle.
53 P = float4x2(P[0], P[1], P[2], P[2]);
54 } else {
55 // Convert the points to a trapeziodal hull that circumcscribes the conic.
56 float2 p1w = P[1] * w;
57 float T = .51; // Bias outward a bit to ensure we cover the outermost samples.
58 float2 c1 = mix(P[0], p1w, T);
59 float2 c2 = mix(P[2], p1w, T);
60 float iw = 1 / mix(1, w, T);
61 P = float4x2(P[0], c1 * iw, c2 * iw, P[2]);
62 }
63 }
64
65 // Translate the points to v0..3 where v0=0.
66 float2 v1 = P[1] - P[0], v2 = P[2] - P[0], v3 = P[3] - P[0];
67
68 // Reorder the points so v2 bisects v1 and v3.
69 if (sign(determinant(float2x2(v2,v1))) == sign(determinant(float2x2(v2,v3)))) {
70 float2 tmp = P[2];
71 if (sign(determinant(float2x2(v1,v2))) != sign(determinant(float2x2(v1,v3)))) {
72 P[2] = P[1]; // swap(P2, P1)
73 P[1] = tmp;
74 } else {
75 P[2] = P[3]; // swap(P2, P3)
76 P[3] = tmp;
77 }
78 }
79
80 // sk_VertexID comes in fan order. Convert to strip order.
81 int vertexidx = sk_VertexID;
82 vertexidx ^= vertexidx >> 1;
83
84 // Find the "turn direction" of each corner and net turn direction.
85 float vertexdir = 0;
86 float netdir = 0;
87 for (int i = 0; i < 4; ++i) {
88 float2 prev = P[i] - P[(i + 3) & 3], next = P[(i + 1) & 3] - P[i];
89 float dir = sign(determinant(float2x2(prev, next)));
90 if (i == vertexidx) {
91 vertexdir = dir;
92 }
93 netdir += dir;
94 }
95
96 // Remove the non-convex vertex, if any.
97 if (vertexdir != sign(netdir)) {
98 vertexidx = (vertexidx + 1) & 3;
99 }
100
101 float2 localcoord = P[vertexidx];
102 float2 vertexpos = AFFINE_MATRIX * localcoord + TRANSLATE;)");
103 gpArgs->fLocalCoordVar.set(kFloat2_GrSLType, "localcoord");
104 gpArgs->fPositionVar.set(kFloat2_GrSLType, "vertexpos");
105 }
106 };
107 return new Impl;
108}
109
110} // namespace
111
Chris Daltonebb37e72021-01-27 17:59:45 -0700112void GrPathInnerTriangulateOp::visitProxies(const VisitProxyFunc& fn) const {
113 if (fPipelineForFills) {
114 fPipelineForFills->visitProxies(fn);
115 } else {
116 fProcessors.visitProxies(fn);
117 }
118}
119
120GrDrawOp::FixedFunctionFlags GrPathInnerTriangulateOp::fixedFunctionFlags() const {
121 auto flags = FixedFunctionFlags::kUsesStencil;
122 if (GrAAType::kNone != fAAType) {
123 flags |= FixedFunctionFlags::kUsesHWAA;
124 }
125 return flags;
126}
127
128GrProcessorSet::Analysis GrPathInnerTriangulateOp::finalize(const GrCaps& caps,
129 const GrAppliedClip* clip,
Chris Daltonebb37e72021-01-27 17:59:45 -0700130 GrClampType clampType) {
Chris Dalton57ab06c2021-04-22 12:57:28 -0600131 return fProcessors.finalize(fColor, GrProcessorAnalysisCoverage::kNone, clip, nullptr, caps,
132 clampType, &fColor);
Chris Daltonebb37e72021-01-27 17:59:45 -0700133}
134
Chris Dalton2f733ec2021-06-01 12:11:57 -0600135void GrPathInnerTriangulateOp::pushFanStencilProgram(const GrTessellationShader::ProgramArgs& args,
Chris Daltonebb37e72021-01-27 17:59:45 -0700136 const GrPipeline* pipelineForStencils,
137 const GrUserStencilSettings* stencil) {
138 SkASSERT(pipelineForStencils);
Chris Daltonb63711a2021-06-01 14:52:02 -0600139 auto shader = GrPathTessellationShader::MakeSimpleTriangleShader(args.fArena, fViewMatrix,
140 SK_PMColor4fTRANSPARENT);
Chris Dalton2f733ec2021-06-01 12:11:57 -0600141 fFanPrograms.push_back(GrTessellationShader::MakeProgram(args, shader, pipelineForStencils,
142 stencil)); }
Chris Daltonebb37e72021-01-27 17:59:45 -0700143
Chris Dalton2f733ec2021-06-01 12:11:57 -0600144void GrPathInnerTriangulateOp::pushFanFillProgram(const GrTessellationShader::ProgramArgs& args,
Chris Daltonebb37e72021-01-27 17:59:45 -0700145 const GrUserStencilSettings* stencil) {
146 SkASSERT(fPipelineForFills);
Chris Daltonb63711a2021-06-01 14:52:02 -0600147 auto shader = GrPathTessellationShader::MakeSimpleTriangleShader(args.fArena, fViewMatrix,
148 fColor);
Chris Dalton2f733ec2021-06-01 12:11:57 -0600149 fFanPrograms.push_back(GrTessellationShader::MakeProgram(args, shader, fPipelineForFills,
150 stencil));
Chris Daltonebb37e72021-01-27 17:59:45 -0700151}
152
Chris Dalton2f733ec2021-06-01 12:11:57 -0600153void GrPathInnerTriangulateOp::prePreparePrograms(const GrTessellationShader::ProgramArgs& args,
Chris Daltonebb37e72021-01-27 17:59:45 -0700154 GrAppliedClip&& appliedClip) {
155 SkASSERT(!fFanTriangulator);
156 SkASSERT(!fFanPolys);
157 SkASSERT(!fPipelineForFills);
158 SkASSERT(!fTessellator);
159 SkASSERT(!fStencilCurvesProgram);
160 SkASSERT(fFanPrograms.empty());
161 SkASSERT(!fFillHullsProgram);
162
163 if (fPath.countVerbs() <= 0) {
164 return;
165 }
166
Chris Dalton57ab06c2021-04-22 12:57:28 -0600167 // If using wireframe, we have to fall back on a standard Redbook "stencil then fill" algorithm
168 // instead of bypassing the stencil buffer to fill the fan directly.
Chris Dalton917f9192021-06-08 14:32:37 -0600169 bool forceRedbookStencilPass = (fPathFlags & (PathFlags::kStencilOnly | PathFlags::kWireframe));
170 bool doFill = !(fPathFlags & PathFlags::kStencilOnly);
Chris Daltonebb37e72021-01-27 17:59:45 -0700171
172 bool isLinear;
173 fFanTriangulator = args.fArena->make<GrInnerFanTriangulator>(fPath, args.fArena);
174 fFanPolys = fFanTriangulator->pathToPolys(&fFanBreadcrumbs, &isLinear);
175
176 // Create a pipeline for stencil passes if needed.
177 const GrPipeline* pipelineForStencils = nullptr;
178 if (forceRedbookStencilPass || !isLinear) { // Curves always get stencilled.
Chris Dalton2f733ec2021-06-01 12:11:57 -0600179 pipelineForStencils = GrPathTessellationShader::MakeStencilOnlyPipeline(
Chris Dalton917f9192021-06-08 14:32:37 -0600180 args, fAAType, fPathFlags, appliedClip.hardClip());
Chris Daltonebb37e72021-01-27 17:59:45 -0700181 }
182
183 // Create a pipeline for fill passes if needed.
184 if (doFill) {
Chris Dalton2f733ec2021-06-01 12:11:57 -0600185 fPipelineForFills = GrTessellationShader::MakePipeline(args, fAAType,
186 std::move(appliedClip),
187 std::move(fProcessors));
Chris Daltonebb37e72021-01-27 17:59:45 -0700188 }
189
190 // Pass 1: Tessellate the outer curves into the stencil buffer.
191 if (!isLinear) {
Chris Daltond9bdc322021-06-01 19:22:05 -0600192 if (args.fCaps->shaderCaps()->tessellationSupport() &&
193 fPath.countVerbs() >= args.fCaps->minPathVerbsForHwTessellation()) {
194 fTessellator = GrPathCurveTessellator::Make(args.fArena, fViewMatrix,
195 SK_PMColor4fTRANSPARENT,
196 GrPathTessellator::DrawInnerFan::kNo);
197 } else {
198 fTessellator = GrPathIndirectTessellator::Make(args.fArena, fPath, fViewMatrix,
199 SK_PMColor4fTRANSPARENT,
200 GrPathTessellator::DrawInnerFan::kNo);
201 }
Chris Dalton2f733ec2021-06-01 12:11:57 -0600202 const GrUserStencilSettings* stencilPathSettings =
203 GrPathTessellationShader::StencilPathSettings(fPath.getFillType());
204 fStencilCurvesProgram = GrTessellationShader::MakeProgram(args, fTessellator->shader(),
205 pipelineForStencils,
206 stencilPathSettings);
Chris Daltonebb37e72021-01-27 17:59:45 -0700207 }
208
209 // Pass 2: Fill the path's inner fan with a stencil test against the curves.
210 if (fFanPolys) {
211 if (forceRedbookStencilPass) {
212 // Use a standard Redbook "stencil then fill" algorithm instead of bypassing the stencil
213 // buffer to fill the fan directly.
Chris Dalton2f733ec2021-06-01 12:11:57 -0600214 const GrUserStencilSettings* stencilPathSettings =
215 GrPathTessellationShader::StencilPathSettings(fPath.getFillType());
216 this->pushFanStencilProgram(args, pipelineForStencils, stencilPathSettings);
Chris Daltonebb37e72021-01-27 17:59:45 -0700217 if (doFill) {
Chris Dalton2f733ec2021-06-01 12:11:57 -0600218 this->pushFanFillProgram(args,
219 GrPathTessellationShader::TestAndResetStencilSettings());
Chris Daltonebb37e72021-01-27 17:59:45 -0700220 }
221 } else if (isLinear) {
222 // There are no outer curves! Ignore stencil and fill the path directly.
223 SkASSERT(!pipelineForStencils);
224 this->pushFanFillProgram(args, &GrUserStencilSettings::kUnused);
225 } else if (!fPipelineForFills->hasStencilClip()) {
226 // These are a twist on the standard Redbook stencil settings that allow us to fill the
227 // inner polygon directly to the final render target. By the time these programs
228 // execute, the outer curves will already be stencilled in. So if the stencil value is
229 // zero, then it means the sample in question is not affected by any curves and we can
230 // fill it in directly. If the stencil value is nonzero, then we don't fill and instead
231 // continue the standard Redbook counting process.
232 constexpr static GrUserStencilSettings kFillOrIncrDecrStencil(
233 GrUserStencilSettings::StaticInitSeparate<
234 0x0000, 0x0000,
235 GrUserStencilTest::kEqual, GrUserStencilTest::kEqual,
236 0xffff, 0xffff,
237 GrUserStencilOp::kKeep, GrUserStencilOp::kKeep,
238 GrUserStencilOp::kIncWrap, GrUserStencilOp::kDecWrap,
239 0xffff, 0xffff>());
240
241 constexpr static GrUserStencilSettings kFillOrInvertStencil(
242 GrUserStencilSettings::StaticInit<
243 0x0000,
244 GrUserStencilTest::kEqual,
245 0xffff,
246 GrUserStencilOp::kKeep,
247 // "Zero" instead of "Invert" because the fan only touches any given pixel once.
248 GrUserStencilOp::kZero,
249 0xffff>());
250
251 auto* stencil = (fPath.getFillType() == SkPathFillType::kWinding)
252 ? &kFillOrIncrDecrStencil
253 : &kFillOrInvertStencil;
254 this->pushFanFillProgram(args, stencil);
255 } else {
256 // This is the same idea as above, but we use two passes instead of one because there is
257 // a stencil clip. The stencil test isn't expressive enough to do the above tests and
258 // also check the clip bit in a single pass.
259 constexpr static GrUserStencilSettings kFillIfZeroAndInClip(
260 GrUserStencilSettings::StaticInit<
261 0x0000,
262 GrUserStencilTest::kEqualIfInClip,
263 0xffff,
264 GrUserStencilOp::kKeep,
265 GrUserStencilOp::kKeep,
266 0xffff>());
267
268 constexpr static GrUserStencilSettings kIncrDecrStencilIfNonzero(
269 GrUserStencilSettings::StaticInitSeparate<
270 0x0000, 0x0000,
271 // No need to check the clip because the previous stencil pass will have only
272 // written to samples already inside the clip.
273 GrUserStencilTest::kNotEqual, GrUserStencilTest::kNotEqual,
274 0xffff, 0xffff,
275 GrUserStencilOp::kIncWrap, GrUserStencilOp::kDecWrap,
276 GrUserStencilOp::kKeep, GrUserStencilOp::kKeep,
277 0xffff, 0xffff>());
278
279 constexpr static GrUserStencilSettings kInvertStencilIfNonZero(
280 GrUserStencilSettings::StaticInit<
281 0x0000,
282 // No need to check the clip because the previous stencil pass will have only
283 // written to samples already inside the clip.
284 GrUserStencilTest::kNotEqual,
285 0xffff,
286 // "Zero" instead of "Invert" because the fan only touches any given pixel once.
287 GrUserStencilOp::kZero,
288 GrUserStencilOp::kKeep,
289 0xffff>());
290
291 // Pass 2a: Directly fill fan samples whose stencil values (from curves) are zero.
292 this->pushFanFillProgram(args, &kFillIfZeroAndInClip);
293
294 // Pass 2b: Redbook counting on fan samples whose stencil values (from curves) != 0.
295 auto* stencil = (fPath.getFillType() == SkPathFillType::kWinding)
296 ? &kIncrDecrStencilIfNonzero
297 : &kInvertStencilIfNonZero;
298 this->pushFanStencilProgram(args, pipelineForStencils, stencil);
299 }
300 }
301
302 // Pass 3: Draw convex hulls around each curve.
303 if (doFill && !isLinear) {
304 // By the time this program executes, every pixel will be filled in except the ones touched
305 // by curves. We issue a final cover pass over the curves by drawing their convex hulls.
306 // This will fill in any remaining samples and reset the stencil values back to zero.
307 SkASSERT(fTessellator);
Chris Dalton2f733ec2021-06-01 12:11:57 -0600308 auto* hullShader = args.fArena->make<HullShader>(fViewMatrix, fColor);
309 fFillHullsProgram = GrTessellationShader::MakeProgram(
Chris Daltonebb37e72021-01-27 17:59:45 -0700310 args, hullShader, fPipelineForFills,
Chris Dalton2f733ec2021-06-01 12:11:57 -0600311 GrPathTessellationShader::TestAndResetStencilSettings());
Chris Daltonebb37e72021-01-27 17:59:45 -0700312 }
313}
314
315void GrPathInnerTriangulateOp::onPrePrepare(GrRecordingContext* context,
316 const GrSurfaceProxyView& writeView,
317 GrAppliedClip* clip,
John Stiles52cb1d02021-06-02 11:58:05 -0400318 const GrDstProxyView& dstProxyView,
Chris Daltonebb37e72021-01-27 17:59:45 -0700319 GrXferBarrierFlags renderPassXferBarriers,
320 GrLoadOp colorLoadOp) {
321 this->prePreparePrograms({context->priv().recordTimeAllocator(), writeView, &dstProxyView,
322 renderPassXferBarriers, colorLoadOp, context->priv().caps()},
323 (clip) ? std::move(*clip) : GrAppliedClip::Disabled());
324 if (fStencilCurvesProgram) {
325 context->priv().recordProgramInfo(fStencilCurvesProgram);
326 }
327 for (const GrProgramInfo* fanProgram : fFanPrograms) {
328 context->priv().recordProgramInfo(fanProgram);
329 }
330 if (fFillHullsProgram) {
331 context->priv().recordProgramInfo(fFillHullsProgram);
332 }
333}
334
335void GrPathInnerTriangulateOp::onPrepare(GrOpFlushState* flushState) {
336 if (!fFanTriangulator) {
337 this->prePreparePrograms({flushState->allocator(), flushState->writeView(),
338 &flushState->dstProxyView(), flushState->renderPassBarriers(),
339 flushState->colorLoadOp(), &flushState->caps()},
340 flushState->detachAppliedClip());
341 if (!fFanTriangulator) {
342 return;
343 }
344 }
345
346 if (fFanPolys) {
347 GrEagerDynamicVertexAllocator alloc(flushState, &fFanBuffer, &fBaseFanVertex);
348 fFanVertexCount = fFanTriangulator->polysToTriangles(fFanPolys, &alloc, &fFanBreadcrumbs);
349 }
350
351 if (fTessellator) {
352 // Must be called after polysToTriangles() in order for fFanBreadcrumbs to be complete.
Chris Dalton569c01b2021-05-25 10:11:46 -0600353 fTessellator->prepare(flushState, this->bounds(), fPath, &fFanBreadcrumbs);
Chris Daltonebb37e72021-01-27 17:59:45 -0700354 }
355}
356
357void GrPathInnerTriangulateOp::onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) {
358 if (fStencilCurvesProgram) {
359 SkASSERT(fTessellator);
360 flushState->bindPipelineAndScissorClip(*fStencilCurvesProgram, this->bounds());
361 fTessellator->draw(flushState);
Chris Daltond9bdc322021-06-01 19:22:05 -0600362 if (flushState->caps().requiresManualFBBarrierAfterTessellatedStencilDraw()) {
363 flushState->gpu()->insertManualFramebufferBarrier(); // http://skbug.com/9739
364 }
Chris Daltonebb37e72021-01-27 17:59:45 -0700365 }
366
367 for (const GrProgramInfo* fanProgram : fFanPrograms) {
368 SkASSERT(fFanBuffer);
369 flushState->bindPipelineAndScissorClip(*fanProgram, this->bounds());
Robert Phillips787fd9d2021-03-22 14:48:09 -0400370 flushState->bindTextures(fanProgram->geomProc(), nullptr, fanProgram->pipeline());
Chris Daltonebb37e72021-01-27 17:59:45 -0700371 flushState->bindBuffers(nullptr, nullptr, fFanBuffer);
372 flushState->draw(fFanVertexCount, fBaseFanVertex);
373 }
374
375 if (fFillHullsProgram) {
376 SkASSERT(fTessellator);
377 flushState->bindPipelineAndScissorClip(*fFillHullsProgram, this->bounds());
Robert Phillips787fd9d2021-03-22 14:48:09 -0400378 flushState->bindTextures(fFillHullsProgram->geomProc(), nullptr, *fPipelineForFills);
Chris Daltonebb37e72021-01-27 17:59:45 -0700379 fTessellator->drawHullInstances(flushState);
380 }
381}