blob: 37b4c751becf2b8a1c0f151da809a8d0ae9825c5 [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"
Chris Daltonebb37e72021-01-27 17:59:45 -070017#include "src/gpu/tessellate/GrTessellationPathRenderer.h"
Chris Dalton3b412782021-06-01 13:40:03 -060018#include "src/gpu/tessellate/shaders/GrPathTessellationShader.h"
Chris Daltonebb37e72021-01-27 17:59:45 -070019
Chris Dalton917f9192021-06-08 14:32:37 -060020using PathFlags = GrTessellationPathRenderer::PathFlags;
Chris Daltonebb37e72021-01-27 17:59:45 -070021
Chris Dalton2f733ec2021-06-01 12:11:57 -060022namespace {
23
24// Fills an array of convex hulls surrounding 4-point cubic or conic instances. This shader is used
25// for the "fill" pass after the curves have been fully stencilled.
26class HullShader : public GrPathTessellationShader {
27public:
28 HullShader(const SkMatrix& viewMatrix, SkPMColor4f color)
29 : GrPathTessellationShader(kTessellate_HullShader_ClassID,
30 GrPrimitiveType::kTriangleStrip, 0, viewMatrix, color) {
31 constexpr static Attribute kPtsAttribs[] = {
32 {"input_points_0_1", kFloat4_GrVertexAttribType, kFloat4_GrSLType},
33 {"input_points_2_3", kFloat4_GrVertexAttribType, kFloat4_GrSLType}};
34 this->setInstanceAttributes(kPtsAttribs, SK_ARRAY_COUNT(kPtsAttribs));
35 }
36
37private:
Chris Daltonb63711a2021-06-01 14:52:02 -060038 const char* name() const final { return "tessellate_HullShader"; }
Chris Dalton2f733ec2021-06-01 12:11:57 -060039 void getGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const final {}
40 GrGLSLGeometryProcessor* createGLSLInstance(const GrShaderCaps&) const final;
41};
42
43GrGLSLGeometryProcessor* HullShader::createGLSLInstance(const GrShaderCaps&) const {
44 class Impl : public GrPathTessellationShader::Impl {
45 void emitVertexCode(GrGLSLVertexBuilder* v, GrGPArgs* gpArgs) override {
46 v->codeAppend(R"(
47 float4x2 P = float4x2(input_points_0_1, input_points_2_3);
48 if (isinf(P[3].y)) { // Is the curve a conic?
49 float w = P[3].x;
50 if (isinf(w)) {
51 // A conic with w=Inf is an exact triangle.
52 P = float4x2(P[0], P[1], P[2], P[2]);
53 } else {
54 // Convert the points to a trapeziodal hull that circumcscribes the conic.
55 float2 p1w = P[1] * w;
56 float T = .51; // Bias outward a bit to ensure we cover the outermost samples.
57 float2 c1 = mix(P[0], p1w, T);
58 float2 c2 = mix(P[2], p1w, T);
59 float iw = 1 / mix(1, w, T);
60 P = float4x2(P[0], c1 * iw, c2 * iw, P[2]);
61 }
62 }
63
64 // Translate the points to v0..3 where v0=0.
65 float2 v1 = P[1] - P[0], v2 = P[2] - P[0], v3 = P[3] - P[0];
66
67 // Reorder the points so v2 bisects v1 and v3.
68 if (sign(determinant(float2x2(v2,v1))) == sign(determinant(float2x2(v2,v3)))) {
69 float2 tmp = P[2];
70 if (sign(determinant(float2x2(v1,v2))) != sign(determinant(float2x2(v1,v3)))) {
71 P[2] = P[1]; // swap(P2, P1)
72 P[1] = tmp;
73 } else {
74 P[2] = P[3]; // swap(P2, P3)
75 P[3] = tmp;
76 }
77 }
78
79 // sk_VertexID comes in fan order. Convert to strip order.
80 int vertexidx = sk_VertexID;
81 vertexidx ^= vertexidx >> 1;
82
83 // Find the "turn direction" of each corner and net turn direction.
84 float vertexdir = 0;
85 float netdir = 0;
86 for (int i = 0; i < 4; ++i) {
87 float2 prev = P[i] - P[(i + 3) & 3], next = P[(i + 1) & 3] - P[i];
88 float dir = sign(determinant(float2x2(prev, next)));
89 if (i == vertexidx) {
90 vertexdir = dir;
91 }
92 netdir += dir;
93 }
94
95 // Remove the non-convex vertex, if any.
96 if (vertexdir != sign(netdir)) {
97 vertexidx = (vertexidx + 1) & 3;
98 }
99
100 float2 localcoord = P[vertexidx];
101 float2 vertexpos = AFFINE_MATRIX * localcoord + TRANSLATE;)");
102 gpArgs->fLocalCoordVar.set(kFloat2_GrSLType, "localcoord");
103 gpArgs->fPositionVar.set(kFloat2_GrSLType, "vertexpos");
104 }
105 };
106 return new Impl;
107}
108
109} // namespace
110
Chris Daltonebb37e72021-01-27 17:59:45 -0700111void GrPathInnerTriangulateOp::visitProxies(const VisitProxyFunc& fn) const {
112 if (fPipelineForFills) {
113 fPipelineForFills->visitProxies(fn);
114 } else {
115 fProcessors.visitProxies(fn);
116 }
117}
118
119GrDrawOp::FixedFunctionFlags GrPathInnerTriangulateOp::fixedFunctionFlags() const {
120 auto flags = FixedFunctionFlags::kUsesStencil;
121 if (GrAAType::kNone != fAAType) {
122 flags |= FixedFunctionFlags::kUsesHWAA;
123 }
124 return flags;
125}
126
127GrProcessorSet::Analysis GrPathInnerTriangulateOp::finalize(const GrCaps& caps,
128 const GrAppliedClip* clip,
Chris Daltonebb37e72021-01-27 17:59:45 -0700129 GrClampType clampType) {
Chris Dalton57ab06c2021-04-22 12:57:28 -0600130 return fProcessors.finalize(fColor, GrProcessorAnalysisCoverage::kNone, clip, nullptr, caps,
131 clampType, &fColor);
Chris Daltonebb37e72021-01-27 17:59:45 -0700132}
133
Chris Dalton2f733ec2021-06-01 12:11:57 -0600134void GrPathInnerTriangulateOp::pushFanStencilProgram(const GrTessellationShader::ProgramArgs& args,
Chris Daltonebb37e72021-01-27 17:59:45 -0700135 const GrPipeline* pipelineForStencils,
136 const GrUserStencilSettings* stencil) {
137 SkASSERT(pipelineForStencils);
Chris Daltonb63711a2021-06-01 14:52:02 -0600138 auto shader = GrPathTessellationShader::MakeSimpleTriangleShader(args.fArena, fViewMatrix,
139 SK_PMColor4fTRANSPARENT);
Chris Dalton2f733ec2021-06-01 12:11:57 -0600140 fFanPrograms.push_back(GrTessellationShader::MakeProgram(args, shader, pipelineForStencils,
141 stencil)); }
Chris Daltonebb37e72021-01-27 17:59:45 -0700142
Chris Dalton2f733ec2021-06-01 12:11:57 -0600143void GrPathInnerTriangulateOp::pushFanFillProgram(const GrTessellationShader::ProgramArgs& args,
Chris Daltonebb37e72021-01-27 17:59:45 -0700144 const GrUserStencilSettings* stencil) {
145 SkASSERT(fPipelineForFills);
Chris Daltonb63711a2021-06-01 14:52:02 -0600146 auto shader = GrPathTessellationShader::MakeSimpleTriangleShader(args.fArena, fViewMatrix,
147 fColor);
Chris Dalton2f733ec2021-06-01 12:11:57 -0600148 fFanPrograms.push_back(GrTessellationShader::MakeProgram(args, shader, fPipelineForFills,
149 stencil));
Chris Daltonebb37e72021-01-27 17:59:45 -0700150}
151
Chris Dalton2f733ec2021-06-01 12:11:57 -0600152void GrPathInnerTriangulateOp::prePreparePrograms(const GrTessellationShader::ProgramArgs& args,
Chris Daltonebb37e72021-01-27 17:59:45 -0700153 GrAppliedClip&& appliedClip) {
154 SkASSERT(!fFanTriangulator);
155 SkASSERT(!fFanPolys);
156 SkASSERT(!fPipelineForFills);
157 SkASSERT(!fTessellator);
158 SkASSERT(!fStencilCurvesProgram);
159 SkASSERT(fFanPrograms.empty());
160 SkASSERT(!fFillHullsProgram);
161
162 if (fPath.countVerbs() <= 0) {
163 return;
164 }
165
Chris Dalton57ab06c2021-04-22 12:57:28 -0600166 // If using wireframe, we have to fall back on a standard Redbook "stencil then fill" algorithm
167 // instead of bypassing the stencil buffer to fill the fan directly.
Chris Dalton917f9192021-06-08 14:32:37 -0600168 bool forceRedbookStencilPass = (fPathFlags & (PathFlags::kStencilOnly | PathFlags::kWireframe));
169 bool doFill = !(fPathFlags & PathFlags::kStencilOnly);
Chris Daltonebb37e72021-01-27 17:59:45 -0700170
171 bool isLinear;
172 fFanTriangulator = args.fArena->make<GrInnerFanTriangulator>(fPath, args.fArena);
173 fFanPolys = fFanTriangulator->pathToPolys(&fFanBreadcrumbs, &isLinear);
174
175 // Create a pipeline for stencil passes if needed.
176 const GrPipeline* pipelineForStencils = nullptr;
177 if (forceRedbookStencilPass || !isLinear) { // Curves always get stencilled.
Chris Dalton2f733ec2021-06-01 12:11:57 -0600178 pipelineForStencils = GrPathTessellationShader::MakeStencilOnlyPipeline(
Chris Dalton917f9192021-06-08 14:32:37 -0600179 args, fAAType, fPathFlags, appliedClip.hardClip());
Chris Daltonebb37e72021-01-27 17:59:45 -0700180 }
181
182 // Create a pipeline for fill passes if needed.
183 if (doFill) {
Chris Dalton2f733ec2021-06-01 12:11:57 -0600184 fPipelineForFills = GrTessellationShader::MakePipeline(args, fAAType,
185 std::move(appliedClip),
186 std::move(fProcessors));
Chris Daltonebb37e72021-01-27 17:59:45 -0700187 }
188
189 // Pass 1: Tessellate the outer curves into the stencil buffer.
190 if (!isLinear) {
Chris Dalton26666bd2021-06-08 16:25:46 -0600191 fTessellator = GrPathCurveTessellator::Make(args.fArena, fViewMatrix,
192 SK_PMColor4fTRANSPARENT,
193 GrPathTessellator::DrawInnerFan::kNo,
194 fPath.countVerbs(), *args.fCaps);
Chris Dalton2f733ec2021-06-01 12:11:57 -0600195 const GrUserStencilSettings* stencilPathSettings =
196 GrPathTessellationShader::StencilPathSettings(fPath.getFillType());
197 fStencilCurvesProgram = GrTessellationShader::MakeProgram(args, fTessellator->shader(),
198 pipelineForStencils,
199 stencilPathSettings);
Chris Daltonebb37e72021-01-27 17:59:45 -0700200 }
201
202 // Pass 2: Fill the path's inner fan with a stencil test against the curves.
203 if (fFanPolys) {
204 if (forceRedbookStencilPass) {
205 // Use a standard Redbook "stencil then fill" algorithm instead of bypassing the stencil
206 // buffer to fill the fan directly.
Chris Dalton2f733ec2021-06-01 12:11:57 -0600207 const GrUserStencilSettings* stencilPathSettings =
208 GrPathTessellationShader::StencilPathSettings(fPath.getFillType());
209 this->pushFanStencilProgram(args, pipelineForStencils, stencilPathSettings);
Chris Daltonebb37e72021-01-27 17:59:45 -0700210 if (doFill) {
Chris Dalton2f733ec2021-06-01 12:11:57 -0600211 this->pushFanFillProgram(args,
212 GrPathTessellationShader::TestAndResetStencilSettings());
Chris Daltonebb37e72021-01-27 17:59:45 -0700213 }
214 } else if (isLinear) {
215 // There are no outer curves! Ignore stencil and fill the path directly.
216 SkASSERT(!pipelineForStencils);
217 this->pushFanFillProgram(args, &GrUserStencilSettings::kUnused);
218 } else if (!fPipelineForFills->hasStencilClip()) {
219 // These are a twist on the standard Redbook stencil settings that allow us to fill the
220 // inner polygon directly to the final render target. By the time these programs
221 // execute, the outer curves will already be stencilled in. So if the stencil value is
222 // zero, then it means the sample in question is not affected by any curves and we can
223 // fill it in directly. If the stencil value is nonzero, then we don't fill and instead
224 // continue the standard Redbook counting process.
225 constexpr static GrUserStencilSettings kFillOrIncrDecrStencil(
226 GrUserStencilSettings::StaticInitSeparate<
227 0x0000, 0x0000,
228 GrUserStencilTest::kEqual, GrUserStencilTest::kEqual,
229 0xffff, 0xffff,
230 GrUserStencilOp::kKeep, GrUserStencilOp::kKeep,
231 GrUserStencilOp::kIncWrap, GrUserStencilOp::kDecWrap,
232 0xffff, 0xffff>());
233
234 constexpr static GrUserStencilSettings kFillOrInvertStencil(
235 GrUserStencilSettings::StaticInit<
236 0x0000,
237 GrUserStencilTest::kEqual,
238 0xffff,
239 GrUserStencilOp::kKeep,
240 // "Zero" instead of "Invert" because the fan only touches any given pixel once.
241 GrUserStencilOp::kZero,
242 0xffff>());
243
244 auto* stencil = (fPath.getFillType() == SkPathFillType::kWinding)
245 ? &kFillOrIncrDecrStencil
246 : &kFillOrInvertStencil;
247 this->pushFanFillProgram(args, stencil);
248 } else {
249 // This is the same idea as above, but we use two passes instead of one because there is
250 // a stencil clip. The stencil test isn't expressive enough to do the above tests and
251 // also check the clip bit in a single pass.
252 constexpr static GrUserStencilSettings kFillIfZeroAndInClip(
253 GrUserStencilSettings::StaticInit<
254 0x0000,
255 GrUserStencilTest::kEqualIfInClip,
256 0xffff,
257 GrUserStencilOp::kKeep,
258 GrUserStencilOp::kKeep,
259 0xffff>());
260
261 constexpr static GrUserStencilSettings kIncrDecrStencilIfNonzero(
262 GrUserStencilSettings::StaticInitSeparate<
263 0x0000, 0x0000,
264 // No need to check the clip because the previous stencil pass will have only
265 // written to samples already inside the clip.
266 GrUserStencilTest::kNotEqual, GrUserStencilTest::kNotEqual,
267 0xffff, 0xffff,
268 GrUserStencilOp::kIncWrap, GrUserStencilOp::kDecWrap,
269 GrUserStencilOp::kKeep, GrUserStencilOp::kKeep,
270 0xffff, 0xffff>());
271
272 constexpr static GrUserStencilSettings kInvertStencilIfNonZero(
273 GrUserStencilSettings::StaticInit<
274 0x0000,
275 // No need to check the clip because the previous stencil pass will have only
276 // written to samples already inside the clip.
277 GrUserStencilTest::kNotEqual,
278 0xffff,
279 // "Zero" instead of "Invert" because the fan only touches any given pixel once.
280 GrUserStencilOp::kZero,
281 GrUserStencilOp::kKeep,
282 0xffff>());
283
284 // Pass 2a: Directly fill fan samples whose stencil values (from curves) are zero.
285 this->pushFanFillProgram(args, &kFillIfZeroAndInClip);
286
287 // Pass 2b: Redbook counting on fan samples whose stencil values (from curves) != 0.
288 auto* stencil = (fPath.getFillType() == SkPathFillType::kWinding)
289 ? &kIncrDecrStencilIfNonzero
290 : &kInvertStencilIfNonZero;
291 this->pushFanStencilProgram(args, pipelineForStencils, stencil);
292 }
293 }
294
295 // Pass 3: Draw convex hulls around each curve.
296 if (doFill && !isLinear) {
297 // By the time this program executes, every pixel will be filled in except the ones touched
298 // by curves. We issue a final cover pass over the curves by drawing their convex hulls.
299 // This will fill in any remaining samples and reset the stencil values back to zero.
300 SkASSERT(fTessellator);
Chris Dalton2f733ec2021-06-01 12:11:57 -0600301 auto* hullShader = args.fArena->make<HullShader>(fViewMatrix, fColor);
302 fFillHullsProgram = GrTessellationShader::MakeProgram(
Chris Daltonebb37e72021-01-27 17:59:45 -0700303 args, hullShader, fPipelineForFills,
Chris Dalton2f733ec2021-06-01 12:11:57 -0600304 GrPathTessellationShader::TestAndResetStencilSettings());
Chris Daltonebb37e72021-01-27 17:59:45 -0700305 }
306}
307
308void GrPathInnerTriangulateOp::onPrePrepare(GrRecordingContext* context,
309 const GrSurfaceProxyView& writeView,
310 GrAppliedClip* clip,
John Stiles52cb1d02021-06-02 11:58:05 -0400311 const GrDstProxyView& dstProxyView,
Chris Daltonebb37e72021-01-27 17:59:45 -0700312 GrXferBarrierFlags renderPassXferBarriers,
313 GrLoadOp colorLoadOp) {
314 this->prePreparePrograms({context->priv().recordTimeAllocator(), writeView, &dstProxyView,
315 renderPassXferBarriers, colorLoadOp, context->priv().caps()},
316 (clip) ? std::move(*clip) : GrAppliedClip::Disabled());
317 if (fStencilCurvesProgram) {
318 context->priv().recordProgramInfo(fStencilCurvesProgram);
319 }
320 for (const GrProgramInfo* fanProgram : fFanPrograms) {
321 context->priv().recordProgramInfo(fanProgram);
322 }
323 if (fFillHullsProgram) {
324 context->priv().recordProgramInfo(fFillHullsProgram);
325 }
326}
327
328void GrPathInnerTriangulateOp::onPrepare(GrOpFlushState* flushState) {
329 if (!fFanTriangulator) {
330 this->prePreparePrograms({flushState->allocator(), flushState->writeView(),
331 &flushState->dstProxyView(), flushState->renderPassBarriers(),
332 flushState->colorLoadOp(), &flushState->caps()},
333 flushState->detachAppliedClip());
334 if (!fFanTriangulator) {
335 return;
336 }
337 }
338
339 if (fFanPolys) {
340 GrEagerDynamicVertexAllocator alloc(flushState, &fFanBuffer, &fBaseFanVertex);
341 fFanVertexCount = fFanTriangulator->polysToTriangles(fFanPolys, &alloc, &fFanBreadcrumbs);
342 }
343
344 if (fTessellator) {
345 // Must be called after polysToTriangles() in order for fFanBreadcrumbs to be complete.
Chris Dalton569c01b2021-05-25 10:11:46 -0600346 fTessellator->prepare(flushState, this->bounds(), fPath, &fFanBreadcrumbs);
Chris Daltonebb37e72021-01-27 17:59:45 -0700347 }
348}
349
350void GrPathInnerTriangulateOp::onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) {
351 if (fStencilCurvesProgram) {
352 SkASSERT(fTessellator);
353 flushState->bindPipelineAndScissorClip(*fStencilCurvesProgram, this->bounds());
354 fTessellator->draw(flushState);
Chris Daltond9bdc322021-06-01 19:22:05 -0600355 if (flushState->caps().requiresManualFBBarrierAfterTessellatedStencilDraw()) {
356 flushState->gpu()->insertManualFramebufferBarrier(); // http://skbug.com/9739
357 }
Chris Daltonebb37e72021-01-27 17:59:45 -0700358 }
359
360 for (const GrProgramInfo* fanProgram : fFanPrograms) {
361 SkASSERT(fFanBuffer);
362 flushState->bindPipelineAndScissorClip(*fanProgram, this->bounds());
Robert Phillips787fd9d2021-03-22 14:48:09 -0400363 flushState->bindTextures(fanProgram->geomProc(), nullptr, fanProgram->pipeline());
Chris Daltonebb37e72021-01-27 17:59:45 -0700364 flushState->bindBuffers(nullptr, nullptr, fFanBuffer);
365 flushState->draw(fFanVertexCount, fBaseFanVertex);
366 }
367
368 if (fFillHullsProgram) {
369 SkASSERT(fTessellator);
370 flushState->bindPipelineAndScissorClip(*fFillHullsProgram, this->bounds());
Robert Phillips787fd9d2021-03-22 14:48:09 -0400371 flushState->bindTextures(fFillHullsProgram->geomProc(), nullptr, *fPipelineForFills);
Chris Daltonebb37e72021-01-27 17:59:45 -0700372 fTessellator->drawHullInstances(flushState);
373 }
374}