blob: a7558028735978e63066681665091f6af8c7f71a [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
Robert Phillips62bd6332021-08-26 09:56:55 -040020namespace {
21
22bool can_use_hardware_tessellation(int numVerbs, const GrPipeline& pipeline, const GrCaps& caps) {
23 if (!caps.shaderCaps()->tessellationSupport() ||
24 !caps.shaderCaps()->infinitySupport() /* The hw tessellation shaders use infinity. */) {
25 return false;
26 }
27 if (pipeline.usesLocalCoords()) {
28 // Our back door for HW tessellation shaders isn't currently capable of passing varyings to
29 // the fragment shader, so if the processors have varyings, we need to use instanced draws
30 // instead.
31 return false;
32 }
33 // Only use hardware tessellation if we're drawing a somewhat large number of verbs. Otherwise
34 // we seem to be better off using instanced draws.
35 return numVerbs >= caps.minStrokeVerbsForHwTessellation();
36}
37
38} // anonymous namespace
39
40namespace skgpu::v1 {
41
42StrokeTessellateOp::StrokeTessellateOp(GrAAType aaType, const SkMatrix& viewMatrix,
43 const SkPath& path, const SkStrokeRec& stroke,
44 GrPaint&& paint)
Chris Dalton22241002021-02-04 09:47:40 -070045 : GrDrawOp(ClassID())
Chris Dalton0e543092020-11-03 14:09:16 -070046 , fAAType(aaType)
47 , fViewMatrix(viewMatrix)
Chris Dalton1017a352021-02-18 15:22:54 -070048 , fPathStrokeList(path, stroke, paint.getColor4f())
49 , fTotalCombinedVerbCnt(path.countVerbs())
50 , fProcessors(std::move(paint)) {
Chris Dalton1017a352021-02-18 15:22:54 -070051 if (!this->headColor().fitsInBytes()) {
52 fShaderFlags |= ShaderFlags::kWideColor;
53 }
Chris Dalton0e543092020-11-03 14:09:16 -070054 SkRect devBounds = path.getBounds();
Chris Dalton18d49642021-05-13 16:38:10 -060055 if (!this->headStroke().isHairlineStyle()) {
56 // Non-hairlines inflate in local path space (pre-transform).
Chris Dalton0638df12021-05-14 15:57:39 -060057 fInflationRadius = stroke.getInflationRadius();
58 devBounds.outset(fInflationRadius, fInflationRadius);
Chris Dalton18d49642021-05-13 16:38:10 -060059 }
Chris Dalton0e543092020-11-03 14:09:16 -070060 viewMatrix.mapRect(&devBounds, devBounds);
Chris Dalton18d49642021-05-13 16:38:10 -060061 if (this->headStroke().isHairlineStyle()) {
62 // Hairlines inflate in device space (post-transform).
Chris Dalton0638df12021-05-14 15:57:39 -060063 fInflationRadius = SkStrokeRec::GetInflationRadius(stroke.getJoin(), stroke.getMiter(),
64 stroke.getCap(), 1);
65 devBounds.outset(fInflationRadius, fInflationRadius);
Chris Dalton18d49642021-05-13 16:38:10 -060066 }
Chris Dalton57ab06c2021-04-22 12:57:28 -060067 this->setBounds(devBounds, HasAABloat::kNo, IsHairline::kNo);
Chris Dalton0e543092020-11-03 14:09:16 -070068}
69
Robert Phillips62bd6332021-08-26 09:56:55 -040070void StrokeTessellateOp::visitProxies(const GrVisitProxyFunc& func) const {
Chris Daltonb0643342020-12-15 01:04:12 -070071 if (fFillProgram) {
Robert Phillips294723d2021-06-17 09:23:58 -040072 fFillProgram->visitFPProxies(func);
Chris Daltonb0643342020-12-15 01:04:12 -070073 } else if (fStencilProgram) {
Robert Phillips294723d2021-06-17 09:23:58 -040074 fStencilProgram->visitFPProxies(func);
Chris Daltonb0643342020-12-15 01:04:12 -070075 } else {
Robert Phillips294723d2021-06-17 09:23:58 -040076 fProcessors.visitProxies(func);
Chris Daltonb0643342020-12-15 01:04:12 -070077 }
78}
79
Robert Phillips62bd6332021-08-26 09:56:55 -040080GrProcessorSet::Analysis StrokeTessellateOp::finalize(const GrCaps& caps,
81 const GrAppliedClip* clip,
82 GrClampType clampType) {
Chris Dalton55abaf52020-12-08 10:25:13 -070083 // Make sure the finalize happens before combining. We might change fNeedsStencil here.
Chris Daltoned826862021-02-22 12:01:12 -070084 SkASSERT(fPathStrokeList.fNext == nullptr);
Chris Dalton55abaf52020-12-08 10:25:13 -070085 const GrProcessorSet::Analysis& analysis = fProcessors.finalize(
Chris Dalton1017a352021-02-18 15:22:54 -070086 this->headColor(), GrProcessorAnalysisCoverage::kNone, clip,
Chris Dalton57ab06c2021-04-22 12:57:28 -060087 &GrUserStencilSettings::kUnused, caps, clampType, &this->headColor());
Chris Dalton55abaf52020-12-08 10:25:13 -070088 fNeedsStencil = !analysis.unaffectedByDstValue();
89 return analysis;
Chris Dalton0e543092020-11-03 14:09:16 -070090}
91
Robert Phillips62bd6332021-08-26 09:56:55 -040092GrOp::CombineResult StrokeTessellateOp::onCombineIfPossible(GrOp* grOp, SkArenaAlloc* alloc,
93 const GrCaps& caps) {
Chris Dalton0e543092020-11-03 14:09:16 -070094 SkASSERT(grOp->classID() == this->classID());
Robert Phillips62bd6332021-08-26 09:56:55 -040095 auto* op = static_cast<StrokeTessellateOp*>(grOp);
Chris Dalton42582fc2021-02-18 11:29:49 -070096
Chris Dalton2a63f822021-05-27 14:14:11 -060097 // This must be called after finalize(). fNeedsStencil can change in finalize().
98 SkASSERT(fProcessors.isFinalized());
99 SkASSERT(op->fProcessors.isFinalized());
100
Chris Dalton55abaf52020-12-08 10:25:13 -0700101 if (fNeedsStencil ||
102 op->fNeedsStencil ||
Chris Dalton0e543092020-11-03 14:09:16 -0700103 fViewMatrix != op->fViewMatrix ||
104 fAAType != op->fAAType ||
Chris Dalton42582fc2021-02-18 11:29:49 -0700105 fProcessors != op->fProcessors ||
106 this->headStroke().isHairlineStyle() != op->headStroke().isHairlineStyle()) {
Chris Dalton0e543092020-11-03 14:09:16 -0700107 return CombineResult::kCannotCombine;
108 }
109
Chris Dalton42582fc2021-02-18 11:29:49 -0700110 auto combinedFlags = fShaderFlags | op->fShaderFlags;
111 if (!(combinedFlags & ShaderFlags::kDynamicStroke) &&
112 !DynamicStroke::StrokesHaveEqualDynamicState(this->headStroke(), op->headStroke())) {
113 // The paths have different stroke properties. We will need to enable dynamic stroke if we
114 // still decide to combine them.
Chris Daltonbb33be22021-02-24 16:30:34 -0700115 if (this->headStroke().isHairlineStyle()) {
116 return CombineResult::kCannotCombine; // Dynamic hairlines aren't supported.
117 }
Chris Dalton42582fc2021-02-18 11:29:49 -0700118 combinedFlags |= ShaderFlags::kDynamicStroke;
119 }
Chris Dalton1017a352021-02-18 15:22:54 -0700120 if (!(combinedFlags & ShaderFlags::kDynamicColor) && this->headColor() != op->headColor()) {
121 // The paths have different colors. We will need to enable dynamic color if we still decide
122 // to combine them.
123 combinedFlags |= ShaderFlags::kDynamicColor;
124 }
125
126 // Don't actually enable new dynamic state on ops that already have lots of verbs.
127 constexpr static GrTFlagsMask<ShaderFlags> kDynamicStatesMask(ShaderFlags::kDynamicStroke |
128 ShaderFlags::kDynamicColor);
129 ShaderFlags neededDynamicStates = combinedFlags & kDynamicStatesMask;
130 if (neededDynamicStates != ShaderFlags::kNone) {
131 if (!this->shouldUseDynamicStates(neededDynamicStates) ||
132 !op->shouldUseDynamicStates(neededDynamicStates)) {
Chris Dalton42582fc2021-02-18 11:29:49 -0700133 return CombineResult::kCannotCombine;
134 }
135 }
Chris Dalton0e543092020-11-03 14:09:16 -0700136
Chris Dalton42582fc2021-02-18 11:29:49 -0700137 fShaderFlags = combinedFlags;
Chris Daltoned826862021-02-22 12:01:12 -0700138
139 // Concat the op's PathStrokeList. Since the head element is allocated inside the op, we need to
140 // copy it.
141 auto* headCopy = alloc->make<PathStrokeList>(std::move(op->fPathStrokeList));
142 *fPathStrokeTail = headCopy;
143 fPathStrokeTail = (op->fPathStrokeTail == &op->fPathStrokeList.fNext) ? &headCopy->fNext
144 : op->fPathStrokeTail;
145
Chris Dalton0638df12021-05-14 15:57:39 -0600146 fInflationRadius = std::max(fInflationRadius, op->fInflationRadius);
Chris Daltoned826862021-02-22 12:01:12 -0700147 fTotalCombinedVerbCnt += op->fTotalCombinedVerbCnt;
Chris Dalton0e543092020-11-03 14:09:16 -0700148 return CombineResult::kMerged;
149}
150
Chris Dalton55abaf52020-12-08 10:25:13 -0700151// Marks every stencil value as "1".
152constexpr static GrUserStencilSettings kMarkStencil(
153 GrUserStencilSettings::StaticInit<
154 0x0001,
155 GrUserStencilTest::kLessIfInClip, // Match kTestAndResetStencil.
156 0x0000, // Always fail.
157 GrUserStencilOp::kZero,
158 GrUserStencilOp::kReplace,
159 0xffff>());
160
161// Passes if the stencil value is nonzero. Also resets the stencil value to zero on pass. This is
162// formulated to match kMarkStencil everywhere except the ref and compare mask. This will allow us
163// to use the same pipeline for both stencil and fill if dynamic stencil state is supported.
164constexpr static GrUserStencilSettings kTestAndResetStencil(
165 GrUserStencilSettings::StaticInit<
166 0x0000,
167 GrUserStencilTest::kLessIfInClip, // i.e., "not equal to zero, if in clip".
168 0x0001,
169 GrUserStencilOp::kZero,
170 GrUserStencilOp::kReplace,
171 0xffff>());
172
Robert Phillips62bd6332021-08-26 09:56:55 -0400173void StrokeTessellateOp::prePrepareTessellator(GrTessellationShader::ProgramArgs&& args,
174 GrAppliedClip&& clip) {
Chris Dalton22241002021-02-04 09:47:40 -0700175 SkASSERT(!fTessellator);
Chris Dalton55abaf52020-12-08 10:25:13 -0700176 SkASSERT(!fFillProgram);
177 SkASSERT(!fStencilProgram);
Chris Dalton0638df12021-05-14 15:57:39 -0600178 // GrOp::setClippedBounds() should have been called by now.
179 SkASSERT(SkRect::MakeIWH(args.fWriteView.width(),
180 args.fWriteView.height()).contains(this->bounds()));
Chris Dalton55abaf52020-12-08 10:25:13 -0700181
Chris Dalton22241002021-02-04 09:47:40 -0700182 const GrCaps& caps = *args.fCaps;
183 SkArenaAlloc* arena = args.fArena;
Chris Dalton55abaf52020-12-08 10:25:13 -0700184
Chris Dalton0638df12021-05-14 15:57:39 -0600185 std::array<float, 2> matrixMinMaxScales;
186 if (!fViewMatrix.getMinMaxScales(matrixMinMaxScales.data())) {
187 matrixMinMaxScales.fill(1);
188 }
189
190 float devInflationRadius = fInflationRadius;
191 if (!this->headStroke().isHairlineStyle()) {
192 devInflationRadius *= matrixMinMaxScales[1];
193 }
194 SkRect strokeCullBounds = this->bounds().makeOutset(devInflationRadius, devInflationRadius);
195
Chris Dalton198ac152021-06-09 13:49:43 -0600196 auto* pipeline = GrTessellationShader::MakePipeline(args, fAAType, std::move(clip),
197 std::move(fProcessors));
198
199 if (can_use_hardware_tessellation(fTotalCombinedVerbCnt, *pipeline, caps)) {
Chris Dalton82007f52021-04-20 00:45:50 -0600200 // Only use hardware tessellation if we're drawing a somewhat large number of verbs.
201 // Otherwise we seem to be better off using instanced draws.
Chris Dalton69043032021-07-01 11:17:53 -0600202 fTessellator = arena->make<GrStrokeHardwareTessellator>(*caps.shaderCaps(), fShaderFlags,
Chris Dalton13adb4a2021-05-26 10:21:56 -0600203 fViewMatrix, &fPathStrokeList,
Chris Dalton0638df12021-05-14 15:57:39 -0600204 matrixMinMaxScales,
205 strokeCullBounds);
Chris Dalton22241002021-02-04 09:47:40 -0700206 } else {
Chris Dalton69043032021-07-01 11:17:53 -0600207 fTessellator = arena->make<GrStrokeFixedCountTessellator>(*caps.shaderCaps(), fShaderFlags,
208 fViewMatrix, &fPathStrokeList,
Chris Dalton0638df12021-05-14 15:57:39 -0600209 matrixMinMaxScales,
Chris Dalton69043032021-07-01 11:17:53 -0600210 strokeCullBounds);
Chris Dalton22241002021-02-04 09:47:40 -0700211 }
212
Chris Dalton55abaf52020-12-08 10:25:13 -0700213 auto fillStencil = &GrUserStencilSettings::kUnused;
Chris Dalton55abaf52020-12-08 10:25:13 -0700214 if (fNeedsStencil) {
Chris Dalton2f733ec2021-06-01 12:11:57 -0600215 fStencilProgram = GrTessellationShader::MakeProgram(args, fTessellator->shader(), pipeline,
216 &kMarkStencil);
Chris Dalton55abaf52020-12-08 10:25:13 -0700217 fillStencil = &kTestAndResetStencil;
Chris Dalton22241002021-02-04 09:47:40 -0700218 args.fXferBarrierFlags = GrXferBarrierFlags::kNone;
Chris Dalton55abaf52020-12-08 10:25:13 -0700219 }
Chris Dalton22241002021-02-04 09:47:40 -0700220
Chris Dalton2f733ec2021-06-01 12:11:57 -0600221 fFillProgram = GrTessellationShader::MakeProgram(args, fTessellator->shader(), pipeline,
222 fillStencil);
Chris Dalton22241002021-02-04 09:47:40 -0700223}
224
Robert Phillips62bd6332021-08-26 09:56:55 -0400225void StrokeTessellateOp::onPrePrepare(GrRecordingContext* context,
226 const GrSurfaceProxyView& writeView, GrAppliedClip* clip,
227 const GrDstProxyView& dstProxyView,
228 GrXferBarrierFlags renderPassXferBarriers, GrLoadOp
229 colorLoadOp) {
Chris Dalton2a26c502021-08-26 10:05:11 -0600230 // DMSAA is not supported on DDL.
231 bool usesMSAASurface = writeView.asRenderTargetProxy()->numSamples() > 1;
232 this->prePrepareTessellator({context->priv().recordTimeAllocator(), writeView, usesMSAASurface,
233 &dstProxyView, renderPassXferBarriers, colorLoadOp,
234 context->priv().caps()},
Chris Dalton22241002021-02-04 09:47:40 -0700235 (clip) ? std::move(*clip) : GrAppliedClip::Disabled());
236 if (fStencilProgram) {
237 context->priv().recordProgramInfo(fStencilProgram);
238 }
239 if (fFillProgram) {
240 context->priv().recordProgramInfo(fFillProgram);
241 }
242}
243
Robert Phillips62bd6332021-08-26 09:56:55 -0400244void StrokeTessellateOp::onPrepare(GrOpFlushState* flushState) {
Chris Dalton22241002021-02-04 09:47:40 -0700245 if (!fTessellator) {
246 this->prePrepareTessellator({flushState->allocator(), flushState->writeView(),
Chris Dalton2a26c502021-08-26 10:05:11 -0600247 flushState->usesMSAASurface(), &flushState->dstProxyView(),
248 flushState->renderPassBarriers(), flushState->colorLoadOp(),
249 &flushState->caps()}, flushState->detachAppliedClip());
Chris Dalton22241002021-02-04 09:47:40 -0700250 }
251 SkASSERT(fTessellator);
Chris Dalton6ca9e182021-04-19 20:49:45 -0600252 fTessellator->prepare(flushState, fTotalCombinedVerbCnt);
Chris Dalton22241002021-02-04 09:47:40 -0700253}
254
Robert Phillips62bd6332021-08-26 09:56:55 -0400255void StrokeTessellateOp::onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) {
Chris Dalton22241002021-02-04 09:47:40 -0700256 if (fStencilProgram) {
Chris Dalton82094cd2021-02-19 01:11:27 -0700257 flushState->bindPipelineAndScissorClip(*fStencilProgram, chainBounds);
Robert Phillips787fd9d2021-03-22 14:48:09 -0400258 flushState->bindTextures(fStencilProgram->geomProc(), nullptr, fStencilProgram->pipeline());
Chris Dalton22241002021-02-04 09:47:40 -0700259 fTessellator->draw(flushState);
260 }
261 if (fFillProgram) {
Chris Dalton82094cd2021-02-19 01:11:27 -0700262 flushState->bindPipelineAndScissorClip(*fFillProgram, chainBounds);
Robert Phillips787fd9d2021-03-22 14:48:09 -0400263 flushState->bindTextures(fFillProgram->geomProc(), nullptr, fFillProgram->pipeline());
Chris Dalton22241002021-02-04 09:47:40 -0700264 fTessellator->draw(flushState);
265 }
Chris Dalton0e543092020-11-03 14:09:16 -0700266}
Robert Phillips62bd6332021-08-26 09:56:55 -0400267
268} // namespace skgpu::v1