blob: d28597ec5fd1e20d8cad184ee70d1f54a712de43 [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
Chris Dalton05007df2021-02-04 00:24:52 -07008#include "src/gpu/tessellate/GrStrokeTessellateOp.h"
Chris Dalton0e543092020-11-03 14:09:16 -07009
10#include "src/core/SkPathPriv.h"
11#include "src/gpu/GrRecordingContextPriv.h"
Chris Dalton22241002021-02-04 09:47:40 -070012#include "src/gpu/tessellate/GrFillPathShader.h"
13#include "src/gpu/tessellate/GrStencilPathShader.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"
16#include "src/gpu/tessellate/GrStrokeIndirectTessellator.h"
Chris Dalton0e543092020-11-03 14:09:16 -070017
Chris Dalton82094cd2021-02-19 01:11:27 -070018using DynamicStroke = GrStrokeTessellateShader::DynamicStroke;
19
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 Dalton42582fc2021-02-18 11:29:49 -070029 if (SkPathPriv::ConicWeightCnt(path) != 0) {
30 fShaderFlags |= ShaderFlags::kHasConics;
31 }
Chris Dalton1017a352021-02-18 15:22:54 -070032 if (!this->headColor().fitsInBytes()) {
33 fShaderFlags |= ShaderFlags::kWideColor;
34 }
Chris Dalton0e543092020-11-03 14:09:16 -070035 SkRect devBounds = path.getBounds();
Chris Dalton42582fc2021-02-18 11:29:49 -070036 float inflationRadius = stroke.getInflationRadius();
Chris Dalton0e543092020-11-03 14:09:16 -070037 devBounds.outset(inflationRadius, inflationRadius);
38 viewMatrix.mapRect(&devBounds, devBounds);
Chris Dalton57ab06c2021-04-22 12:57:28 -060039 this->setBounds(devBounds, HasAABloat::kNo, IsHairline::kNo);
Chris Dalton0e543092020-11-03 14:09:16 -070040}
41
Chris Dalton05007df2021-02-04 00:24:52 -070042void GrStrokeTessellateOp::visitProxies(const VisitProxyFunc& fn) const {
Chris Daltonb0643342020-12-15 01:04:12 -070043 if (fFillProgram) {
44 fFillProgram->visitFPProxies(fn);
45 } else if (fStencilProgram) {
46 fStencilProgram->visitFPProxies(fn);
47 } else {
48 fProcessors.visitProxies(fn);
49 }
50}
51
Chris Dalton05007df2021-02-04 00:24:52 -070052GrDrawOp::FixedFunctionFlags GrStrokeTessellateOp::fixedFunctionFlags() const {
Chris Dalton55abaf52020-12-08 10:25:13 -070053 // We might not actually end up needing stencil, but won't know for sure until finalize().
54 // Request it just in case we do end up needing it.
55 auto flags = FixedFunctionFlags::kUsesStencil;
Chris Dalton0e543092020-11-03 14:09:16 -070056 if (GrAAType::kNone != fAAType) {
57 flags |= FixedFunctionFlags::kUsesHWAA;
58 }
59 return flags;
60}
61
Chris Dalton05007df2021-02-04 00:24:52 -070062GrProcessorSet::Analysis GrStrokeTessellateOp::finalize(const GrCaps& caps,
63 const GrAppliedClip* clip,
Chris Dalton05007df2021-02-04 00:24:52 -070064 GrClampType clampType) {
Chris Dalton55abaf52020-12-08 10:25:13 -070065 // Make sure the finalize happens before combining. We might change fNeedsStencil here.
Chris Daltoned826862021-02-22 12:01:12 -070066 SkASSERT(fPathStrokeList.fNext == nullptr);
Chris Dalton55abaf52020-12-08 10:25:13 -070067 const GrProcessorSet::Analysis& analysis = fProcessors.finalize(
Chris Dalton1017a352021-02-18 15:22:54 -070068 this->headColor(), GrProcessorAnalysisCoverage::kNone, clip,
Chris Dalton57ab06c2021-04-22 12:57:28 -060069 &GrUserStencilSettings::kUnused, caps, clampType, &this->headColor());
Chris Dalton55abaf52020-12-08 10:25:13 -070070 fNeedsStencil = !analysis.unaffectedByDstValue();
71 return analysis;
Chris Dalton0e543092020-11-03 14:09:16 -070072}
73
Chris Dalton05007df2021-02-04 00:24:52 -070074GrOp::CombineResult GrStrokeTessellateOp::onCombineIfPossible(GrOp* grOp, SkArenaAlloc* alloc,
Chris Dalton1017a352021-02-18 15:22:54 -070075 const GrCaps& caps) {
Chris Dalton0e543092020-11-03 14:09:16 -070076 SkASSERT(grOp->classID() == this->classID());
Chris Dalton05007df2021-02-04 00:24:52 -070077 auto* op = static_cast<GrStrokeTessellateOp*>(grOp);
Chris Dalton42582fc2021-02-18 11:29:49 -070078
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
124 fTotalCombinedVerbCnt += op->fTotalCombinedVerbCnt;
Chris Dalton0e543092020-11-03 14:09:16 -0700125 return CombineResult::kMerged;
126}
127
Chris Dalton55abaf52020-12-08 10:25:13 -0700128// Marks every stencil value as "1".
129constexpr static GrUserStencilSettings kMarkStencil(
130 GrUserStencilSettings::StaticInit<
131 0x0001,
132 GrUserStencilTest::kLessIfInClip, // Match kTestAndResetStencil.
133 0x0000, // Always fail.
134 GrUserStencilOp::kZero,
135 GrUserStencilOp::kReplace,
136 0xffff>());
137
138// Passes if the stencil value is nonzero. Also resets the stencil value to zero on pass. This is
139// formulated to match kMarkStencil everywhere except the ref and compare mask. This will allow us
140// to use the same pipeline for both stencil and fill if dynamic stencil state is supported.
141constexpr static GrUserStencilSettings kTestAndResetStencil(
142 GrUserStencilSettings::StaticInit<
143 0x0000,
144 GrUserStencilTest::kLessIfInClip, // i.e., "not equal to zero, if in clip".
145 0x0001,
146 GrUserStencilOp::kZero,
147 GrUserStencilOp::kReplace,
148 0xffff>());
149
Chris Dalton7d979912021-05-11 12:36:51 -0600150bool GrStrokeTessellateOp::canUseHardwareTessellation(int numVerbs, const GrCaps& caps) {
151 SkASSERT(!fStencilProgram && !fFillProgram); // Ensure we haven't std::moved fProcessors.
152 if (!caps.shaderCaps()->tessellationSupport()) {
153 return false;
154 }
155 if (fProcessors.usesVaryingCoords()) {
156 // Our back door for HW tessellation shaders isn't currently capable of passing varyings to
157 // the fragment shader, so if the processors have varyings, we need to use instanced draws
158 // instead.
159 return false;
160 }
161#if GR_TEST_UTILS
162 if (caps.shaderCaps()->maxTessellationSegments() < 64) {
163 // If maxTessellationSegments is lower than the spec minimum, it means we've overriden it
164 // for testing. Always use hardware tessellation if this is the case.
165 return true;
166 }
167#endif
168 // Only use hardware tessellation if we're drawing a somewhat large number of verbs. Otherwise
169 // we seem to be better off using instanced draws.
170 return numVerbs >= 50;
171}
172
Chris Dalton05007df2021-02-04 00:24:52 -0700173void GrStrokeTessellateOp::prePrepareTessellator(GrPathShader::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);
178
Chris Dalton22241002021-02-04 09:47:40 -0700179 const GrCaps& caps = *args.fCaps;
180 SkArenaAlloc* arena = args.fArena;
Chris Dalton55abaf52020-12-08 10:25:13 -0700181
Chris Dalton7d979912021-05-11 12:36:51 -0600182 if (this->canUseHardwareTessellation(fTotalCombinedVerbCnt, caps)) {
Chris Dalton82007f52021-04-20 00:45:50 -0600183 // Only use hardware tessellation if we're drawing a somewhat large number of verbs.
184 // Otherwise we seem to be better off using instanced draws.
Chris Dalton6ca9e182021-04-19 20:49:45 -0600185 fTessellator = arena->make<GrStrokeHardwareTessellator>(fShaderFlags, fViewMatrix,
186 &fPathStrokeList,
Chris Dalton82094cd2021-02-19 01:11:27 -0700187 *caps.shaderCaps());
Chris Dalton82007f52021-04-20 00:45:50 -0600188 } else if (fTotalCombinedVerbCnt > 50 && !(fShaderFlags & ShaderFlags::kDynamicColor)) {
189 // Only use the log2 indirect tessellator if we're drawing a somewhat large number of verbs
190 // and the stroke doesn't use dynamic color. (The log2 indirect tessellator can't support
191 // dynamic color without a z-buffer, due to how it reorders strokes.)
192 fTessellator = arena->make<GrStrokeIndirectTessellator>(fShaderFlags, fViewMatrix,
193 &fPathStrokeList,
194 fTotalCombinedVerbCnt, arena);
Chris Dalton22241002021-02-04 09:47:40 -0700195 } else {
Chris Dalton82007f52021-04-20 00:45:50 -0600196 fTessellator = arena->make<GrStrokeFixedCountTessellator>(fShaderFlags, fViewMatrix,
197 &fPathStrokeList);
Chris Dalton22241002021-02-04 09:47:40 -0700198 }
199
Chris Dalton57ab06c2021-04-22 12:57:28 -0600200 auto* pipeline = GrFillPathShader::MakeFillPassPipeline(args, fAAType, std::move(clip),
201 std::move(fProcessors));
Chris Dalton55abaf52020-12-08 10:25:13 -0700202 auto fillStencil = &GrUserStencilSettings::kUnused;
Chris Dalton55abaf52020-12-08 10:25:13 -0700203 if (fNeedsStencil) {
Chris Dalton57ab06c2021-04-22 12:57:28 -0600204 fStencilProgram = GrPathShader::MakeProgram(args, fTessellator->shader(), pipeline,
Chris Dalton22241002021-02-04 09:47:40 -0700205 &kMarkStencil);
Chris Dalton55abaf52020-12-08 10:25:13 -0700206 fillStencil = &kTestAndResetStencil;
Chris Dalton22241002021-02-04 09:47:40 -0700207 args.fXferBarrierFlags = GrXferBarrierFlags::kNone;
Chris Dalton55abaf52020-12-08 10:25:13 -0700208 }
Chris Dalton22241002021-02-04 09:47:40 -0700209
Chris Dalton57ab06c2021-04-22 12:57:28 -0600210 fFillProgram = GrPathShader::MakeProgram(args, fTessellator->shader(), pipeline, fillStencil);
Chris Dalton22241002021-02-04 09:47:40 -0700211}
212
Chris Dalton05007df2021-02-04 00:24:52 -0700213void GrStrokeTessellateOp::onPrePrepare(GrRecordingContext* context,
214 const GrSurfaceProxyView& writeView, GrAppliedClip* clip,
215 const GrXferProcessor::DstProxyView& dstProxyView,
216 GrXferBarrierFlags renderPassXferBarriers, GrLoadOp
217 colorLoadOp) {
Chris Dalton22241002021-02-04 09:47:40 -0700218 this->prePrepareTessellator({context->priv().recordTimeAllocator(), writeView, &dstProxyView,
219 renderPassXferBarriers, colorLoadOp, context->priv().caps()},
220 (clip) ? std::move(*clip) : GrAppliedClip::Disabled());
221 if (fStencilProgram) {
222 context->priv().recordProgramInfo(fStencilProgram);
223 }
224 if (fFillProgram) {
225 context->priv().recordProgramInfo(fFillProgram);
226 }
227}
228
Chris Dalton05007df2021-02-04 00:24:52 -0700229void GrStrokeTessellateOp::onPrepare(GrOpFlushState* flushState) {
Chris Dalton22241002021-02-04 09:47:40 -0700230 if (!fTessellator) {
231 this->prePrepareTessellator({flushState->allocator(), flushState->writeView(),
232 &flushState->dstProxyView(), flushState->renderPassBarriers(),
233 flushState->colorLoadOp(), &flushState->caps()},
234 flushState->detachAppliedClip());
235 }
236 SkASSERT(fTessellator);
Chris Dalton6ca9e182021-04-19 20:49:45 -0600237 fTessellator->prepare(flushState, fTotalCombinedVerbCnt);
Chris Dalton22241002021-02-04 09:47:40 -0700238}
239
Chris Dalton05007df2021-02-04 00:24:52 -0700240void GrStrokeTessellateOp::onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) {
Chris Dalton22241002021-02-04 09:47:40 -0700241 if (fStencilProgram) {
Chris Dalton82094cd2021-02-19 01:11:27 -0700242 flushState->bindPipelineAndScissorClip(*fStencilProgram, chainBounds);
Robert Phillips787fd9d2021-03-22 14:48:09 -0400243 flushState->bindTextures(fStencilProgram->geomProc(), nullptr, fStencilProgram->pipeline());
Chris Dalton22241002021-02-04 09:47:40 -0700244 fTessellator->draw(flushState);
245 }
246 if (fFillProgram) {
Chris Dalton82094cd2021-02-19 01:11:27 -0700247 flushState->bindPipelineAndScissorClip(*fFillProgram, chainBounds);
Robert Phillips787fd9d2021-03-22 14:48:09 -0400248 flushState->bindTextures(fFillProgram->geomProc(), nullptr, fFillProgram->pipeline());
Chris Dalton22241002021-02-04 09:47:40 -0700249 fTessellator->draw(flushState);
250 }
Chris Dalton0e543092020-11-03 14:09:16 -0700251}