blob: 0b7152782b6c7ac8561655151ec11315d799e4ed [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"
11#include "src/gpu/GrInnerFanTriangulator.h"
12#include "src/gpu/GrOpFlushState.h"
13#include "src/gpu/GrRecordingContextPriv.h"
Chris Dalton2f733ec2021-06-01 12:11:57 -060014#include "src/gpu/glsl/GrGLSLVertexGeoBuilder.h"
Chris Daltonebb37e72021-01-27 17:59:45 -070015#include "src/gpu/tessellate/GrPathTessellator.h"
Chris Daltonebb37e72021-01-27 17:59:45 -070016#include "src/gpu/tessellate/GrTessellationPathRenderer.h"
Chris Dalton3b412782021-06-01 13:40:03 -060017#include "src/gpu/tessellate/shaders/GrPathTessellationShader.h"
Chris Daltonebb37e72021-01-27 17:59:45 -070018
19using OpFlags = GrTessellationPathRenderer::OpFlags;
20
Chris Dalton2f733ec2021-06-01 12:11:57 -060021namespace {
22
23// Fills an array of convex hulls surrounding 4-point cubic or conic instances. This shader is used
24// for the "fill" pass after the curves have been fully stencilled.
25class HullShader : public GrPathTessellationShader {
26public:
27 HullShader(const SkMatrix& viewMatrix, SkPMColor4f color)
28 : GrPathTessellationShader(kTessellate_HullShader_ClassID,
29 GrPrimitiveType::kTriangleStrip, 0, viewMatrix, color) {
30 constexpr static Attribute kPtsAttribs[] = {
31 {"input_points_0_1", kFloat4_GrVertexAttribType, kFloat4_GrSLType},
32 {"input_points_2_3", kFloat4_GrVertexAttribType, kFloat4_GrSLType}};
33 this->setInstanceAttributes(kPtsAttribs, SK_ARRAY_COUNT(kPtsAttribs));
34 }
35
36private:
Chris Daltonb63711a2021-06-01 14:52:02 -060037 const char* name() const final { return "tessellate_HullShader"; }
Chris Dalton2f733ec2021-06-01 12:11:57 -060038 void getGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const final {}
39 GrGLSLGeometryProcessor* createGLSLInstance(const GrShaderCaps&) const final;
40};
41
42GrGLSLGeometryProcessor* HullShader::createGLSLInstance(const GrShaderCaps&) const {
43 class Impl : public GrPathTessellationShader::Impl {
44 void emitVertexCode(GrGLSLVertexBuilder* v, GrGPArgs* gpArgs) override {
45 v->codeAppend(R"(
46 float4x2 P = float4x2(input_points_0_1, input_points_2_3);
47 if (isinf(P[3].y)) { // Is the curve a conic?
48 float w = P[3].x;
49 if (isinf(w)) {
50 // A conic with w=Inf is an exact triangle.
51 P = float4x2(P[0], P[1], P[2], P[2]);
52 } else {
53 // Convert the points to a trapeziodal hull that circumcscribes the conic.
54 float2 p1w = P[1] * w;
55 float T = .51; // Bias outward a bit to ensure we cover the outermost samples.
56 float2 c1 = mix(P[0], p1w, T);
57 float2 c2 = mix(P[2], p1w, T);
58 float iw = 1 / mix(1, w, T);
59 P = float4x2(P[0], c1 * iw, c2 * iw, P[2]);
60 }
61 }
62
63 // Translate the points to v0..3 where v0=0.
64 float2 v1 = P[1] - P[0], v2 = P[2] - P[0], v3 = P[3] - P[0];
65
66 // Reorder the points so v2 bisects v1 and v3.
67 if (sign(determinant(float2x2(v2,v1))) == sign(determinant(float2x2(v2,v3)))) {
68 float2 tmp = P[2];
69 if (sign(determinant(float2x2(v1,v2))) != sign(determinant(float2x2(v1,v3)))) {
70 P[2] = P[1]; // swap(P2, P1)
71 P[1] = tmp;
72 } else {
73 P[2] = P[3]; // swap(P2, P3)
74 P[3] = tmp;
75 }
76 }
77
78 // sk_VertexID comes in fan order. Convert to strip order.
79 int vertexidx = sk_VertexID;
80 vertexidx ^= vertexidx >> 1;
81
82 // Find the "turn direction" of each corner and net turn direction.
83 float vertexdir = 0;
84 float netdir = 0;
85 for (int i = 0; i < 4; ++i) {
86 float2 prev = P[i] - P[(i + 3) & 3], next = P[(i + 1) & 3] - P[i];
87 float dir = sign(determinant(float2x2(prev, next)));
88 if (i == vertexidx) {
89 vertexdir = dir;
90 }
91 netdir += dir;
92 }
93
94 // Remove the non-convex vertex, if any.
95 if (vertexdir != sign(netdir)) {
96 vertexidx = (vertexidx + 1) & 3;
97 }
98
99 float2 localcoord = P[vertexidx];
100 float2 vertexpos = AFFINE_MATRIX * localcoord + TRANSLATE;)");
101 gpArgs->fLocalCoordVar.set(kFloat2_GrSLType, "localcoord");
102 gpArgs->fPositionVar.set(kFloat2_GrSLType, "vertexpos");
103 }
104 };
105 return new Impl;
106}
107
108} // namespace
109
Chris Daltonebb37e72021-01-27 17:59:45 -0700110void GrPathInnerTriangulateOp::visitProxies(const VisitProxyFunc& fn) const {
111 if (fPipelineForFills) {
112 fPipelineForFills->visitProxies(fn);
113 } else {
114 fProcessors.visitProxies(fn);
115 }
116}
117
118GrDrawOp::FixedFunctionFlags GrPathInnerTriangulateOp::fixedFunctionFlags() const {
119 auto flags = FixedFunctionFlags::kUsesStencil;
120 if (GrAAType::kNone != fAAType) {
121 flags |= FixedFunctionFlags::kUsesHWAA;
122 }
123 return flags;
124}
125
126GrProcessorSet::Analysis GrPathInnerTriangulateOp::finalize(const GrCaps& caps,
127 const GrAppliedClip* clip,
Chris Daltonebb37e72021-01-27 17:59:45 -0700128 GrClampType clampType) {
Chris Dalton57ab06c2021-04-22 12:57:28 -0600129 return fProcessors.finalize(fColor, GrProcessorAnalysisCoverage::kNone, clip, nullptr, caps,
130 clampType, &fColor);
Chris Daltonebb37e72021-01-27 17:59:45 -0700131}
132
Chris Dalton2f733ec2021-06-01 12:11:57 -0600133void GrPathInnerTriangulateOp::pushFanStencilProgram(const GrTessellationShader::ProgramArgs& args,
Chris Daltonebb37e72021-01-27 17:59:45 -0700134 const GrPipeline* pipelineForStencils,
135 const GrUserStencilSettings* stencil) {
136 SkASSERT(pipelineForStencils);
Chris Daltonb63711a2021-06-01 14:52:02 -0600137 auto shader = GrPathTessellationShader::MakeSimpleTriangleShader(args.fArena, fViewMatrix,
138 SK_PMColor4fTRANSPARENT);
Chris Dalton2f733ec2021-06-01 12:11:57 -0600139 fFanPrograms.push_back(GrTessellationShader::MakeProgram(args, shader, pipelineForStencils,
140 stencil)); }
Chris Daltonebb37e72021-01-27 17:59:45 -0700141
Chris Dalton2f733ec2021-06-01 12:11:57 -0600142void GrPathInnerTriangulateOp::pushFanFillProgram(const GrTessellationShader::ProgramArgs& args,
Chris Daltonebb37e72021-01-27 17:59:45 -0700143 const GrUserStencilSettings* stencil) {
144 SkASSERT(fPipelineForFills);
Chris Daltonb63711a2021-06-01 14:52:02 -0600145 auto shader = GrPathTessellationShader::MakeSimpleTriangleShader(args.fArena, fViewMatrix,
146 fColor);
Chris Dalton2f733ec2021-06-01 12:11:57 -0600147 fFanPrograms.push_back(GrTessellationShader::MakeProgram(args, shader, fPipelineForFills,
148 stencil));
Chris Daltonebb37e72021-01-27 17:59:45 -0700149}
150
Chris Dalton2f733ec2021-06-01 12:11:57 -0600151void GrPathInnerTriangulateOp::prePreparePrograms(const GrTessellationShader::ProgramArgs& args,
Chris Daltonebb37e72021-01-27 17:59:45 -0700152 GrAppliedClip&& appliedClip) {
153 SkASSERT(!fFanTriangulator);
154 SkASSERT(!fFanPolys);
155 SkASSERT(!fPipelineForFills);
156 SkASSERT(!fTessellator);
157 SkASSERT(!fStencilCurvesProgram);
158 SkASSERT(fFanPrograms.empty());
159 SkASSERT(!fFillHullsProgram);
160
161 if (fPath.countVerbs() <= 0) {
162 return;
163 }
164
Chris Dalton57ab06c2021-04-22 12:57:28 -0600165 // If using wireframe, we have to fall back on a standard Redbook "stencil then fill" algorithm
166 // instead of bypassing the stencil buffer to fill the fan directly.
167 bool forceRedbookStencilPass = (fOpFlags & (OpFlags::kStencilOnly | OpFlags::kWireframe));
Chris Daltonebb37e72021-01-27 17:59:45 -0700168 bool doFill = !(fOpFlags & OpFlags::kStencilOnly);
169
170 bool isLinear;
171 fFanTriangulator = args.fArena->make<GrInnerFanTriangulator>(fPath, args.fArena);
172 fFanPolys = fFanTriangulator->pathToPolys(&fFanBreadcrumbs, &isLinear);
173
174 // Create a pipeline for stencil passes if needed.
175 const GrPipeline* pipelineForStencils = nullptr;
176 if (forceRedbookStencilPass || !isLinear) { // Curves always get stencilled.
Chris Dalton2f733ec2021-06-01 12:11:57 -0600177 pipelineForStencils = GrPathTessellationShader::MakeStencilOnlyPipeline(
178 args, fAAType, fOpFlags, appliedClip.hardClip());
Chris Daltonebb37e72021-01-27 17:59:45 -0700179 }
180
181 // Create a pipeline for fill passes if needed.
182 if (doFill) {
Chris Dalton2f733ec2021-06-01 12:11:57 -0600183 fPipelineForFills = GrTessellationShader::MakePipeline(args, fAAType,
184 std::move(appliedClip),
185 std::move(fProcessors));
Chris Daltonebb37e72021-01-27 17:59:45 -0700186 }
187
188 // Pass 1: Tessellate the outer curves into the stencil buffer.
189 if (!isLinear) {
190 // Always use indirect draws for now. Our goal in this op is to maximize GPU performance,
191 // and the middle-out topology used by indirect draws is easier on the rasterizer than what
192 // we can do with hw tessellation. So far we haven't found any platforms where trying to use
193 // hw tessellation here is worth it.
Chris Dalton2f733ec2021-06-01 12:11:57 -0600194 fTessellator = GrPathTessellator::Make(args.fArena, fPath, fViewMatrix,
195 SK_PMColor4fTRANSPARENT,
Chris Dalton569c01b2021-05-25 10:11:46 -0600196 GrPathTessellator::DrawInnerFan::kNo, *args.fCaps);
Chris Dalton2f733ec2021-06-01 12:11:57 -0600197 const GrUserStencilSettings* stencilPathSettings =
198 GrPathTessellationShader::StencilPathSettings(fPath.getFillType());
199 fStencilCurvesProgram = GrTessellationShader::MakeProgram(args, fTessellator->shader(),
200 pipelineForStencils,
201 stencilPathSettings);
Chris Daltonebb37e72021-01-27 17:59:45 -0700202 }
203
204 // Pass 2: Fill the path's inner fan with a stencil test against the curves.
205 if (fFanPolys) {
206 if (forceRedbookStencilPass) {
207 // Use a standard Redbook "stencil then fill" algorithm instead of bypassing the stencil
208 // buffer to fill the fan directly.
Chris Dalton2f733ec2021-06-01 12:11:57 -0600209 const GrUserStencilSettings* stencilPathSettings =
210 GrPathTessellationShader::StencilPathSettings(fPath.getFillType());
211 this->pushFanStencilProgram(args, pipelineForStencils, stencilPathSettings);
Chris Daltonebb37e72021-01-27 17:59:45 -0700212 if (doFill) {
Chris Dalton2f733ec2021-06-01 12:11:57 -0600213 this->pushFanFillProgram(args,
214 GrPathTessellationShader::TestAndResetStencilSettings());
Chris Daltonebb37e72021-01-27 17:59:45 -0700215 }
216 } else if (isLinear) {
217 // There are no outer curves! Ignore stencil and fill the path directly.
218 SkASSERT(!pipelineForStencils);
219 this->pushFanFillProgram(args, &GrUserStencilSettings::kUnused);
220 } else if (!fPipelineForFills->hasStencilClip()) {
221 // These are a twist on the standard Redbook stencil settings that allow us to fill the
222 // inner polygon directly to the final render target. By the time these programs
223 // execute, the outer curves will already be stencilled in. So if the stencil value is
224 // zero, then it means the sample in question is not affected by any curves and we can
225 // fill it in directly. If the stencil value is nonzero, then we don't fill and instead
226 // continue the standard Redbook counting process.
227 constexpr static GrUserStencilSettings kFillOrIncrDecrStencil(
228 GrUserStencilSettings::StaticInitSeparate<
229 0x0000, 0x0000,
230 GrUserStencilTest::kEqual, GrUserStencilTest::kEqual,
231 0xffff, 0xffff,
232 GrUserStencilOp::kKeep, GrUserStencilOp::kKeep,
233 GrUserStencilOp::kIncWrap, GrUserStencilOp::kDecWrap,
234 0xffff, 0xffff>());
235
236 constexpr static GrUserStencilSettings kFillOrInvertStencil(
237 GrUserStencilSettings::StaticInit<
238 0x0000,
239 GrUserStencilTest::kEqual,
240 0xffff,
241 GrUserStencilOp::kKeep,
242 // "Zero" instead of "Invert" because the fan only touches any given pixel once.
243 GrUserStencilOp::kZero,
244 0xffff>());
245
246 auto* stencil = (fPath.getFillType() == SkPathFillType::kWinding)
247 ? &kFillOrIncrDecrStencil
248 : &kFillOrInvertStencil;
249 this->pushFanFillProgram(args, stencil);
250 } else {
251 // This is the same idea as above, but we use two passes instead of one because there is
252 // a stencil clip. The stencil test isn't expressive enough to do the above tests and
253 // also check the clip bit in a single pass.
254 constexpr static GrUserStencilSettings kFillIfZeroAndInClip(
255 GrUserStencilSettings::StaticInit<
256 0x0000,
257 GrUserStencilTest::kEqualIfInClip,
258 0xffff,
259 GrUserStencilOp::kKeep,
260 GrUserStencilOp::kKeep,
261 0xffff>());
262
263 constexpr static GrUserStencilSettings kIncrDecrStencilIfNonzero(
264 GrUserStencilSettings::StaticInitSeparate<
265 0x0000, 0x0000,
266 // No need to check the clip because the previous stencil pass will have only
267 // written to samples already inside the clip.
268 GrUserStencilTest::kNotEqual, GrUserStencilTest::kNotEqual,
269 0xffff, 0xffff,
270 GrUserStencilOp::kIncWrap, GrUserStencilOp::kDecWrap,
271 GrUserStencilOp::kKeep, GrUserStencilOp::kKeep,
272 0xffff, 0xffff>());
273
274 constexpr static GrUserStencilSettings kInvertStencilIfNonZero(
275 GrUserStencilSettings::StaticInit<
276 0x0000,
277 // No need to check the clip because the previous stencil pass will have only
278 // written to samples already inside the clip.
279 GrUserStencilTest::kNotEqual,
280 0xffff,
281 // "Zero" instead of "Invert" because the fan only touches any given pixel once.
282 GrUserStencilOp::kZero,
283 GrUserStencilOp::kKeep,
284 0xffff>());
285
286 // Pass 2a: Directly fill fan samples whose stencil values (from curves) are zero.
287 this->pushFanFillProgram(args, &kFillIfZeroAndInClip);
288
289 // Pass 2b: Redbook counting on fan samples whose stencil values (from curves) != 0.
290 auto* stencil = (fPath.getFillType() == SkPathFillType::kWinding)
291 ? &kIncrDecrStencilIfNonzero
292 : &kInvertStencilIfNonZero;
293 this->pushFanStencilProgram(args, pipelineForStencils, stencil);
294 }
295 }
296
297 // Pass 3: Draw convex hulls around each curve.
298 if (doFill && !isLinear) {
299 // By the time this program executes, every pixel will be filled in except the ones touched
300 // by curves. We issue a final cover pass over the curves by drawing their convex hulls.
301 // This will fill in any remaining samples and reset the stencil values back to zero.
302 SkASSERT(fTessellator);
Chris Dalton2f733ec2021-06-01 12:11:57 -0600303 auto* hullShader = args.fArena->make<HullShader>(fViewMatrix, fColor);
304 fFillHullsProgram = GrTessellationShader::MakeProgram(
Chris Daltonebb37e72021-01-27 17:59:45 -0700305 args, hullShader, fPipelineForFills,
Chris Dalton2f733ec2021-06-01 12:11:57 -0600306 GrPathTessellationShader::TestAndResetStencilSettings());
Chris Daltonebb37e72021-01-27 17:59:45 -0700307 }
308}
309
310void GrPathInnerTriangulateOp::onPrePrepare(GrRecordingContext* context,
311 const GrSurfaceProxyView& writeView,
312 GrAppliedClip* clip,
John Stiles52cb1d02021-06-02 11:58:05 -0400313 const GrDstProxyView& dstProxyView,
Chris Daltonebb37e72021-01-27 17:59:45 -0700314 GrXferBarrierFlags renderPassXferBarriers,
315 GrLoadOp colorLoadOp) {
316 this->prePreparePrograms({context->priv().recordTimeAllocator(), writeView, &dstProxyView,
317 renderPassXferBarriers, colorLoadOp, context->priv().caps()},
318 (clip) ? std::move(*clip) : GrAppliedClip::Disabled());
319 if (fStencilCurvesProgram) {
320 context->priv().recordProgramInfo(fStencilCurvesProgram);
321 }
322 for (const GrProgramInfo* fanProgram : fFanPrograms) {
323 context->priv().recordProgramInfo(fanProgram);
324 }
325 if (fFillHullsProgram) {
326 context->priv().recordProgramInfo(fFillHullsProgram);
327 }
328}
329
330void GrPathInnerTriangulateOp::onPrepare(GrOpFlushState* flushState) {
331 if (!fFanTriangulator) {
332 this->prePreparePrograms({flushState->allocator(), flushState->writeView(),
333 &flushState->dstProxyView(), flushState->renderPassBarriers(),
334 flushState->colorLoadOp(), &flushState->caps()},
335 flushState->detachAppliedClip());
336 if (!fFanTriangulator) {
337 return;
338 }
339 }
340
341 if (fFanPolys) {
342 GrEagerDynamicVertexAllocator alloc(flushState, &fFanBuffer, &fBaseFanVertex);
343 fFanVertexCount = fFanTriangulator->polysToTriangles(fFanPolys, &alloc, &fFanBreadcrumbs);
344 }
345
346 if (fTessellator) {
347 // Must be called after polysToTriangles() in order for fFanBreadcrumbs to be complete.
Chris Dalton569c01b2021-05-25 10:11:46 -0600348 fTessellator->prepare(flushState, this->bounds(), fPath, &fFanBreadcrumbs);
Chris Daltonebb37e72021-01-27 17:59:45 -0700349 }
350}
351
352void GrPathInnerTriangulateOp::onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) {
353 if (fStencilCurvesProgram) {
354 SkASSERT(fTessellator);
355 flushState->bindPipelineAndScissorClip(*fStencilCurvesProgram, this->bounds());
356 fTessellator->draw(flushState);
357 }
358
359 for (const GrProgramInfo* fanProgram : fFanPrograms) {
360 SkASSERT(fFanBuffer);
361 flushState->bindPipelineAndScissorClip(*fanProgram, this->bounds());
Robert Phillips787fd9d2021-03-22 14:48:09 -0400362 flushState->bindTextures(fanProgram->geomProc(), nullptr, fanProgram->pipeline());
Chris Daltonebb37e72021-01-27 17:59:45 -0700363 flushState->bindBuffers(nullptr, nullptr, fFanBuffer);
364 flushState->draw(fFanVertexCount, fBaseFanVertex);
365 }
366
367 if (fFillHullsProgram) {
368 SkASSERT(fTessellator);
369 flushState->bindPipelineAndScissorClip(*fFillHullsProgram, this->bounds());
Robert Phillips787fd9d2021-03-22 14:48:09 -0400370 flushState->bindTextures(fFillHullsProgram->geomProc(), nullptr, *fPipelineForFills);
Chris Daltonebb37e72021-01-27 17:59:45 -0700371 fTessellator->drawHullInstances(flushState);
372 }
373}