blob: 73913bdac06cef012f72555311c08692b6050e50 [file] [log] [blame]
Chris Dalton0e543092020-11-03 14:09:16 -07001/*
2 * Copyright 2020 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
Robert Phillipse453fa02021-08-19 14:57:05 -04008#include "src/gpu/ops/StrokeTessellateOp.h"
Chris Dalton0e543092020-11-03 14:09:16 -07009
10#include "src/core/SkPathPriv.h"
Robert Phillips06273bc2021-08-11 15:43:50 -040011#include "src/gpu/GrAppliedClip.h"
12#include "src/gpu/GrOpFlushState.h"
Chris Dalton0e543092020-11-03 14:09:16 -070013#include "src/gpu/GrRecordingContextPriv.h"
Chris Dalton82007f52021-04-20 00:45:50 -060014#include "src/gpu/tessellate/GrStrokeFixedCountTessellator.h"
Chris Dalton22241002021-02-04 09:47:40 -070015#include "src/gpu/tessellate/GrStrokeHardwareTessellator.h"
Chris Dalton3b412782021-06-01 13:40:03 -060016#include "src/gpu/tessellate/shaders/GrTessellationShader.h"
Chris Dalton0e543092020-11-03 14:09:16 -070017
Chris Dalton3b412782021-06-01 13:40:03 -060018using DynamicStroke = GrStrokeTessellationShader::DynamicStroke;
Chris Dalton82094cd2021-02-19 01:11:27 -070019
Chris Dalton05007df2021-02-04 00:24:52 -070020GrStrokeTessellateOp::GrStrokeTessellateOp(GrAAType aaType, const SkMatrix& viewMatrix,
21 const SkPath& path, const SkStrokeRec& stroke,
22 GrPaint&& paint)
Chris Dalton22241002021-02-04 09:47:40 -070023 : GrDrawOp(ClassID())
Chris Dalton0e543092020-11-03 14:09:16 -070024 , fAAType(aaType)
25 , fViewMatrix(viewMatrix)
Chris Dalton1017a352021-02-18 15:22:54 -070026 , fPathStrokeList(path, stroke, paint.getColor4f())
27 , fTotalCombinedVerbCnt(path.countVerbs())
28 , fProcessors(std::move(paint)) {
Chris Dalton1017a352021-02-18 15:22:54 -070029 if (!this->headColor().fitsInBytes()) {
30 fShaderFlags |= ShaderFlags::kWideColor;
31 }
Chris Dalton0e543092020-11-03 14:09:16 -070032 SkRect devBounds = path.getBounds();
Chris Dalton18d49642021-05-13 16:38:10 -060033 if (!this->headStroke().isHairlineStyle()) {
34 // Non-hairlines inflate in local path space (pre-transform).
Chris Dalton0638df12021-05-14 15:57:39 -060035 fInflationRadius = stroke.getInflationRadius();
36 devBounds.outset(fInflationRadius, fInflationRadius);
Chris Dalton18d49642021-05-13 16:38:10 -060037 }
Chris Dalton0e543092020-11-03 14:09:16 -070038 viewMatrix.mapRect(&devBounds, devBounds);
Chris Dalton18d49642021-05-13 16:38:10 -060039 if (this->headStroke().isHairlineStyle()) {
40 // Hairlines inflate in device space (post-transform).
Chris Dalton0638df12021-05-14 15:57:39 -060041 fInflationRadius = SkStrokeRec::GetInflationRadius(stroke.getJoin(), stroke.getMiter(),
42 stroke.getCap(), 1);
43 devBounds.outset(fInflationRadius, fInflationRadius);
Chris Dalton18d49642021-05-13 16:38:10 -060044 }
Chris Dalton57ab06c2021-04-22 12:57:28 -060045 this->setBounds(devBounds, HasAABloat::kNo, IsHairline::kNo);
Chris Dalton0e543092020-11-03 14:09:16 -070046}
47
Robert Phillips294723d2021-06-17 09:23:58 -040048void GrStrokeTessellateOp::visitProxies(const GrVisitProxyFunc& func) const {
Chris Daltonb0643342020-12-15 01:04:12 -070049 if (fFillProgram) {
Robert Phillips294723d2021-06-17 09:23:58 -040050 fFillProgram->visitFPProxies(func);
Chris Daltonb0643342020-12-15 01:04:12 -070051 } else if (fStencilProgram) {
Robert Phillips294723d2021-06-17 09:23:58 -040052 fStencilProgram->visitFPProxies(func);
Chris Daltonb0643342020-12-15 01:04:12 -070053 } else {
Robert Phillips294723d2021-06-17 09:23:58 -040054 fProcessors.visitProxies(func);
Chris Daltonb0643342020-12-15 01:04:12 -070055 }
56}
57
Chris Dalton05007df2021-02-04 00:24:52 -070058GrProcessorSet::Analysis GrStrokeTessellateOp::finalize(const GrCaps& caps,
59 const GrAppliedClip* clip,
Chris Dalton05007df2021-02-04 00:24:52 -070060 GrClampType clampType) {
Chris Dalton55abaf52020-12-08 10:25:13 -070061 // Make sure the finalize happens before combining. We might change fNeedsStencil here.
Chris Daltoned826862021-02-22 12:01:12 -070062 SkASSERT(fPathStrokeList.fNext == nullptr);
Chris Dalton55abaf52020-12-08 10:25:13 -070063 const GrProcessorSet::Analysis& analysis = fProcessors.finalize(
Chris Dalton1017a352021-02-18 15:22:54 -070064 this->headColor(), GrProcessorAnalysisCoverage::kNone, clip,
Chris Dalton57ab06c2021-04-22 12:57:28 -060065 &GrUserStencilSettings::kUnused, caps, clampType, &this->headColor());
Chris Dalton55abaf52020-12-08 10:25:13 -070066 fNeedsStencil = !analysis.unaffectedByDstValue();
67 return analysis;
Chris Dalton0e543092020-11-03 14:09:16 -070068}
69
Chris Dalton05007df2021-02-04 00:24:52 -070070GrOp::CombineResult GrStrokeTessellateOp::onCombineIfPossible(GrOp* grOp, SkArenaAlloc* alloc,
Chris Dalton1017a352021-02-18 15:22:54 -070071 const GrCaps& caps) {
Chris Dalton0e543092020-11-03 14:09:16 -070072 SkASSERT(grOp->classID() == this->classID());
Chris Dalton05007df2021-02-04 00:24:52 -070073 auto* op = static_cast<GrStrokeTessellateOp*>(grOp);
Chris Dalton42582fc2021-02-18 11:29:49 -070074
Chris Dalton2a63f822021-05-27 14:14:11 -060075 // This must be called after finalize(). fNeedsStencil can change in finalize().
76 SkASSERT(fProcessors.isFinalized());
77 SkASSERT(op->fProcessors.isFinalized());
78
Chris Dalton55abaf52020-12-08 10:25:13 -070079 if (fNeedsStencil ||
80 op->fNeedsStencil ||
Chris Dalton0e543092020-11-03 14:09:16 -070081 fViewMatrix != op->fViewMatrix ||
82 fAAType != op->fAAType ||
Chris Dalton42582fc2021-02-18 11:29:49 -070083 fProcessors != op->fProcessors ||
84 this->headStroke().isHairlineStyle() != op->headStroke().isHairlineStyle()) {
Chris Dalton0e543092020-11-03 14:09:16 -070085 return CombineResult::kCannotCombine;
86 }
87
Chris Dalton42582fc2021-02-18 11:29:49 -070088 auto combinedFlags = fShaderFlags | op->fShaderFlags;
89 if (!(combinedFlags & ShaderFlags::kDynamicStroke) &&
90 !DynamicStroke::StrokesHaveEqualDynamicState(this->headStroke(), op->headStroke())) {
91 // The paths have different stroke properties. We will need to enable dynamic stroke if we
92 // still decide to combine them.
Chris Daltonbb33be22021-02-24 16:30:34 -070093 if (this->headStroke().isHairlineStyle()) {
94 return CombineResult::kCannotCombine; // Dynamic hairlines aren't supported.
95 }
Chris Dalton42582fc2021-02-18 11:29:49 -070096 combinedFlags |= ShaderFlags::kDynamicStroke;
97 }
Chris Dalton1017a352021-02-18 15:22:54 -070098 if (!(combinedFlags & ShaderFlags::kDynamicColor) && this->headColor() != op->headColor()) {
99 // The paths have different colors. We will need to enable dynamic color if we still decide
100 // to combine them.
101 combinedFlags |= ShaderFlags::kDynamicColor;
102 }
103
104 // Don't actually enable new dynamic state on ops that already have lots of verbs.
105 constexpr static GrTFlagsMask<ShaderFlags> kDynamicStatesMask(ShaderFlags::kDynamicStroke |
106 ShaderFlags::kDynamicColor);
107 ShaderFlags neededDynamicStates = combinedFlags & kDynamicStatesMask;
108 if (neededDynamicStates != ShaderFlags::kNone) {
109 if (!this->shouldUseDynamicStates(neededDynamicStates) ||
110 !op->shouldUseDynamicStates(neededDynamicStates)) {
Chris Dalton42582fc2021-02-18 11:29:49 -0700111 return CombineResult::kCannotCombine;
112 }
113 }
Chris Dalton0e543092020-11-03 14:09:16 -0700114
Chris Dalton42582fc2021-02-18 11:29:49 -0700115 fShaderFlags = combinedFlags;
Chris Daltoned826862021-02-22 12:01:12 -0700116
117 // Concat the op's PathStrokeList. Since the head element is allocated inside the op, we need to
118 // copy it.
119 auto* headCopy = alloc->make<PathStrokeList>(std::move(op->fPathStrokeList));
120 *fPathStrokeTail = headCopy;
121 fPathStrokeTail = (op->fPathStrokeTail == &op->fPathStrokeList.fNext) ? &headCopy->fNext
122 : op->fPathStrokeTail;
123
Chris Dalton0638df12021-05-14 15:57:39 -0600124 fInflationRadius = std::max(fInflationRadius, op->fInflationRadius);
Chris Daltoned826862021-02-22 12:01:12 -0700125 fTotalCombinedVerbCnt += op->fTotalCombinedVerbCnt;
Chris Dalton0e543092020-11-03 14:09:16 -0700126 return CombineResult::kMerged;
127}
128
Chris Dalton55abaf52020-12-08 10:25:13 -0700129// Marks every stencil value as "1".
130constexpr static GrUserStencilSettings kMarkStencil(
131 GrUserStencilSettings::StaticInit<
132 0x0001,
133 GrUserStencilTest::kLessIfInClip, // Match kTestAndResetStencil.
134 0x0000, // Always fail.
135 GrUserStencilOp::kZero,
136 GrUserStencilOp::kReplace,
137 0xffff>());
138
139// Passes if the stencil value is nonzero. Also resets the stencil value to zero on pass. This is
140// formulated to match kMarkStencil everywhere except the ref and compare mask. This will allow us
141// to use the same pipeline for both stencil and fill if dynamic stencil state is supported.
142constexpr static GrUserStencilSettings kTestAndResetStencil(
143 GrUserStencilSettings::StaticInit<
144 0x0000,
145 GrUserStencilTest::kLessIfInClip, // i.e., "not equal to zero, if in clip".
146 0x0001,
147 GrUserStencilOp::kZero,
148 GrUserStencilOp::kReplace,
149 0xffff>());
150
Chris Dalton198ac152021-06-09 13:49:43 -0600151bool can_use_hardware_tessellation(int numVerbs, const GrPipeline& pipeline, const GrCaps& caps) {
Chris Dalton50516f32021-07-18 22:26:27 -0600152 if (!caps.shaderCaps()->tessellationSupport() ||
153 !caps.shaderCaps()->infinitySupport() /* The hw tessellation shaders use infinity. */) {
Chris Dalton7d979912021-05-11 12:36:51 -0600154 return false;
155 }
Brian Salomon66b500a2021-08-02 12:37:14 -0400156 if (pipeline.usesLocalCoords()) {
Chris Dalton7d979912021-05-11 12:36:51 -0600157 // Our back door for HW tessellation shaders isn't currently capable of passing varyings to
158 // the fragment shader, so if the processors have varyings, we need to use instanced draws
159 // instead.
160 return false;
161 }
Chris Dalton7d979912021-05-11 12:36:51 -0600162 // Only use hardware tessellation if we're drawing a somewhat large number of verbs. Otherwise
163 // we seem to be better off using instanced draws.
Chris Dalton569c01b2021-05-25 10:11:46 -0600164 return numVerbs >= caps.minStrokeVerbsForHwTessellation();
Chris Dalton7d979912021-05-11 12:36:51 -0600165}
166
Chris Dalton2f733ec2021-06-01 12:11:57 -0600167void GrStrokeTessellateOp::prePrepareTessellator(GrTessellationShader::ProgramArgs&& args,
Chris Dalton05007df2021-02-04 00:24:52 -0700168 GrAppliedClip&& clip) {
Chris Dalton22241002021-02-04 09:47:40 -0700169 SkASSERT(!fTessellator);
Chris Dalton55abaf52020-12-08 10:25:13 -0700170 SkASSERT(!fFillProgram);
171 SkASSERT(!fStencilProgram);
Chris Dalton0638df12021-05-14 15:57:39 -0600172 // GrOp::setClippedBounds() should have been called by now.
173 SkASSERT(SkRect::MakeIWH(args.fWriteView.width(),
174 args.fWriteView.height()).contains(this->bounds()));
Chris Dalton55abaf52020-12-08 10:25:13 -0700175
Chris Dalton22241002021-02-04 09:47:40 -0700176 const GrCaps& caps = *args.fCaps;
177 SkArenaAlloc* arena = args.fArena;
Chris Dalton55abaf52020-12-08 10:25:13 -0700178
Chris Dalton0638df12021-05-14 15:57:39 -0600179 std::array<float, 2> matrixMinMaxScales;
180 if (!fViewMatrix.getMinMaxScales(matrixMinMaxScales.data())) {
181 matrixMinMaxScales.fill(1);
182 }
183
184 float devInflationRadius = fInflationRadius;
185 if (!this->headStroke().isHairlineStyle()) {
186 devInflationRadius *= matrixMinMaxScales[1];
187 }
188 SkRect strokeCullBounds = this->bounds().makeOutset(devInflationRadius, devInflationRadius);
189
Chris Dalton198ac152021-06-09 13:49:43 -0600190 auto* pipeline = GrTessellationShader::MakePipeline(args, fAAType, std::move(clip),
191 std::move(fProcessors));
192
193 if (can_use_hardware_tessellation(fTotalCombinedVerbCnt, *pipeline, caps)) {
Chris Dalton82007f52021-04-20 00:45:50 -0600194 // Only use hardware tessellation if we're drawing a somewhat large number of verbs.
195 // Otherwise we seem to be better off using instanced draws.
Chris Dalton69043032021-07-01 11:17:53 -0600196 fTessellator = arena->make<GrStrokeHardwareTessellator>(*caps.shaderCaps(), fShaderFlags,
Chris Dalton13adb4a2021-05-26 10:21:56 -0600197 fViewMatrix, &fPathStrokeList,
Chris Dalton0638df12021-05-14 15:57:39 -0600198 matrixMinMaxScales,
199 strokeCullBounds);
Chris Dalton22241002021-02-04 09:47:40 -0700200 } else {
Chris Dalton69043032021-07-01 11:17:53 -0600201 fTessellator = arena->make<GrStrokeFixedCountTessellator>(*caps.shaderCaps(), fShaderFlags,
202 fViewMatrix, &fPathStrokeList,
Chris Dalton0638df12021-05-14 15:57:39 -0600203 matrixMinMaxScales,
Chris Dalton69043032021-07-01 11:17:53 -0600204 strokeCullBounds);
Chris Dalton22241002021-02-04 09:47:40 -0700205 }
206
Chris Dalton55abaf52020-12-08 10:25:13 -0700207 auto fillStencil = &GrUserStencilSettings::kUnused;
Chris Dalton55abaf52020-12-08 10:25:13 -0700208 if (fNeedsStencil) {
Chris Dalton2f733ec2021-06-01 12:11:57 -0600209 fStencilProgram = GrTessellationShader::MakeProgram(args, fTessellator->shader(), pipeline,
210 &kMarkStencil);
Chris Dalton55abaf52020-12-08 10:25:13 -0700211 fillStencil = &kTestAndResetStencil;
Chris Dalton22241002021-02-04 09:47:40 -0700212 args.fXferBarrierFlags = GrXferBarrierFlags::kNone;
Chris Dalton55abaf52020-12-08 10:25:13 -0700213 }
Chris Dalton22241002021-02-04 09:47:40 -0700214
Chris Dalton2f733ec2021-06-01 12:11:57 -0600215 fFillProgram = GrTessellationShader::MakeProgram(args, fTessellator->shader(), pipeline,
216 fillStencil);
Chris Dalton22241002021-02-04 09:47:40 -0700217}
218
Chris Dalton05007df2021-02-04 00:24:52 -0700219void GrStrokeTessellateOp::onPrePrepare(GrRecordingContext* context,
220 const GrSurfaceProxyView& writeView, GrAppliedClip* clip,
John Stiles52cb1d02021-06-02 11:58:05 -0400221 const GrDstProxyView& dstProxyView,
Chris Dalton05007df2021-02-04 00:24:52 -0700222 GrXferBarrierFlags renderPassXferBarriers, GrLoadOp
223 colorLoadOp) {
Chris Dalton22241002021-02-04 09:47:40 -0700224 this->prePrepareTessellator({context->priv().recordTimeAllocator(), writeView, &dstProxyView,
225 renderPassXferBarriers, colorLoadOp, context->priv().caps()},
226 (clip) ? std::move(*clip) : GrAppliedClip::Disabled());
227 if (fStencilProgram) {
228 context->priv().recordProgramInfo(fStencilProgram);
229 }
230 if (fFillProgram) {
231 context->priv().recordProgramInfo(fFillProgram);
232 }
233}
234
Chris Dalton05007df2021-02-04 00:24:52 -0700235void GrStrokeTessellateOp::onPrepare(GrOpFlushState* flushState) {
Chris Dalton22241002021-02-04 09:47:40 -0700236 if (!fTessellator) {
237 this->prePrepareTessellator({flushState->allocator(), flushState->writeView(),
238 &flushState->dstProxyView(), flushState->renderPassBarriers(),
239 flushState->colorLoadOp(), &flushState->caps()},
240 flushState->detachAppliedClip());
241 }
242 SkASSERT(fTessellator);
Chris Dalton6ca9e182021-04-19 20:49:45 -0600243 fTessellator->prepare(flushState, fTotalCombinedVerbCnt);
Chris Dalton22241002021-02-04 09:47:40 -0700244}
245
Chris Dalton05007df2021-02-04 00:24:52 -0700246void GrStrokeTessellateOp::onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) {
Chris Dalton22241002021-02-04 09:47:40 -0700247 if (fStencilProgram) {
Chris Dalton82094cd2021-02-19 01:11:27 -0700248 flushState->bindPipelineAndScissorClip(*fStencilProgram, chainBounds);
Robert Phillips787fd9d2021-03-22 14:48:09 -0400249 flushState->bindTextures(fStencilProgram->geomProc(), nullptr, fStencilProgram->pipeline());
Chris Dalton22241002021-02-04 09:47:40 -0700250 fTessellator->draw(flushState);
251 }
252 if (fFillProgram) {
Chris Dalton82094cd2021-02-19 01:11:27 -0700253 flushState->bindPipelineAndScissorClip(*fFillProgram, chainBounds);
Robert Phillips787fd9d2021-03-22 14:48:09 -0400254 flushState->bindTextures(fFillProgram->geomProc(), nullptr, fFillProgram->pipeline());
Chris Dalton22241002021-02-04 09:47:40 -0700255 fTessellator->draw(flushState);
256 }
Chris Dalton0e543092020-11-03 14:09:16 -0700257}