1 /*
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
8 #include "src/gpu/tessellate/GrStrokeTessellateOp.h"
9
10 #include "src/core/SkPathPriv.h"
11 #include "src/gpu/GrRecordingContextPriv.h"
12 #include "src/gpu/tessellate/GrFillPathShader.h"
13 #include "src/gpu/tessellate/GrStencilPathShader.h"
14 #include "src/gpu/tessellate/GrStrokeFixedCountTessellator.h"
15 #include "src/gpu/tessellate/GrStrokeHardwareTessellator.h"
16 #include "src/gpu/tessellate/GrStrokeIndirectTessellator.h"
17
18 using DynamicStroke = GrStrokeTessellateShader::DynamicStroke;
19
GrStrokeTessellateOp(GrAAType aaType,const SkMatrix & viewMatrix,const SkPath & path,const SkStrokeRec & stroke,GrPaint && paint)20 GrStrokeTessellateOp::GrStrokeTessellateOp(GrAAType aaType, const SkMatrix& viewMatrix,
21 const SkPath& path, const SkStrokeRec& stroke,
22 GrPaint&& paint)
23 : GrDrawOp(ClassID())
24 , fAAType(aaType)
25 , fViewMatrix(viewMatrix)
26 , fPathStrokeList(path, stroke, paint.getColor4f())
27 , fTotalCombinedVerbCnt(path.countVerbs())
28 , fProcessors(std::move(paint)) {
29 if (SkPathPriv::ConicWeightCnt(path) != 0) {
30 fShaderFlags |= ShaderFlags::kHasConics;
31 }
32 if (!this->headColor().fitsInBytes()) {
33 fShaderFlags |= ShaderFlags::kWideColor;
34 }
35 SkRect devBounds = path.getBounds();
36 float inflationRadius = stroke.getInflationRadius();
37 devBounds.outset(inflationRadius, inflationRadius);
38 viewMatrix.mapRect(&devBounds, devBounds);
39 this->setBounds(devBounds, HasAABloat::kNo, IsHairline::kNo);
40 }
41
visitProxies(const VisitProxyFunc & fn) const42 void GrStrokeTessellateOp::visitProxies(const VisitProxyFunc& fn) const {
43 if (fFillProgram) {
44 fFillProgram->visitFPProxies(fn);
45 } else if (fStencilProgram) {
46 fStencilProgram->visitFPProxies(fn);
47 } else {
48 fProcessors.visitProxies(fn);
49 }
50 }
51
fixedFunctionFlags() const52 GrDrawOp::FixedFunctionFlags GrStrokeTessellateOp::fixedFunctionFlags() const {
53 // 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;
56 if (GrAAType::kNone != fAAType) {
57 flags |= FixedFunctionFlags::kUsesHWAA;
58 }
59 return flags;
60 }
61
finalize(const GrCaps & caps,const GrAppliedClip * clip,GrClampType clampType)62 GrProcessorSet::Analysis GrStrokeTessellateOp::finalize(const GrCaps& caps,
63 const GrAppliedClip* clip,
64 GrClampType clampType) {
65 // Make sure the finalize happens before combining. We might change fNeedsStencil here.
66 SkASSERT(fPathStrokeList.fNext == nullptr);
67 const GrProcessorSet::Analysis& analysis = fProcessors.finalize(
68 this->headColor(), GrProcessorAnalysisCoverage::kNone, clip,
69 &GrUserStencilSettings::kUnused, caps, clampType, &this->headColor());
70 fNeedsStencil = !analysis.unaffectedByDstValue();
71 return analysis;
72 }
73
onCombineIfPossible(GrOp * grOp,SkArenaAlloc * alloc,const GrCaps & caps)74 GrOp::CombineResult GrStrokeTessellateOp::onCombineIfPossible(GrOp* grOp, SkArenaAlloc* alloc,
75 const GrCaps& caps) {
76 SkASSERT(grOp->classID() == this->classID());
77 auto* op = static_cast<GrStrokeTessellateOp*>(grOp);
78
79 if (fNeedsStencil ||
80 op->fNeedsStencil ||
81 fViewMatrix != op->fViewMatrix ||
82 fAAType != op->fAAType ||
83 fProcessors != op->fProcessors ||
84 this->headStroke().isHairlineStyle() != op->headStroke().isHairlineStyle()) {
85 return CombineResult::kCannotCombine;
86 }
87
88 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.
93 if (this->headStroke().isHairlineStyle()) {
94 return CombineResult::kCannotCombine; // Dynamic hairlines aren't supported.
95 }
96 combinedFlags |= ShaderFlags::kDynamicStroke;
97 }
98 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)) {
111 return CombineResult::kCannotCombine;
112 }
113 }
114
115 fShaderFlags = combinedFlags;
116
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;
125 return CombineResult::kMerged;
126 }
127
128 // Marks every stencil value as "1".
129 constexpr 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.
141 constexpr 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
canUseHardwareTessellation(int numVerbs,const GrCaps & caps)150 bool 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
prePrepareTessellator(GrPathShader::ProgramArgs && args,GrAppliedClip && clip)173 void GrStrokeTessellateOp::prePrepareTessellator(GrPathShader::ProgramArgs&& args,
174 GrAppliedClip&& clip) {
175 SkASSERT(!fTessellator);
176 SkASSERT(!fFillProgram);
177 SkASSERT(!fStencilProgram);
178
179 const GrCaps& caps = *args.fCaps;
180 SkArenaAlloc* arena = args.fArena;
181
182 if (this->canUseHardwareTessellation(fTotalCombinedVerbCnt, caps)) {
183 // 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.
185 fTessellator = arena->make<GrStrokeHardwareTessellator>(fShaderFlags, fViewMatrix,
186 &fPathStrokeList,
187 *caps.shaderCaps());
188 } 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);
195 } else {
196 fTessellator = arena->make<GrStrokeFixedCountTessellator>(fShaderFlags, fViewMatrix,
197 &fPathStrokeList);
198 }
199
200 auto* pipeline = GrFillPathShader::MakeFillPassPipeline(args, fAAType, std::move(clip),
201 std::move(fProcessors));
202 auto fillStencil = &GrUserStencilSettings::kUnused;
203 if (fNeedsStencil) {
204 fStencilProgram = GrPathShader::MakeProgram(args, fTessellator->shader(), pipeline,
205 &kMarkStencil);
206 fillStencil = &kTestAndResetStencil;
207 args.fXferBarrierFlags = GrXferBarrierFlags::kNone;
208 }
209
210 fFillProgram = GrPathShader::MakeProgram(args, fTessellator->shader(), pipeline, fillStencil);
211 }
212
onPrePrepare(GrRecordingContext * context,const GrSurfaceProxyView & writeView,GrAppliedClip * clip,const GrXferProcessor::DstProxyView & dstProxyView,GrXferBarrierFlags renderPassXferBarriers,GrLoadOp colorLoadOp)213 void GrStrokeTessellateOp::onPrePrepare(GrRecordingContext* context,
214 const GrSurfaceProxyView& writeView, GrAppliedClip* clip,
215 const GrXferProcessor::DstProxyView& dstProxyView,
216 GrXferBarrierFlags renderPassXferBarriers, GrLoadOp
217 colorLoadOp) {
218 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
onPrepare(GrOpFlushState * flushState)229 void GrStrokeTessellateOp::onPrepare(GrOpFlushState* flushState) {
230 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);
237 fTessellator->prepare(flushState, fTotalCombinedVerbCnt);
238 }
239
onExecute(GrOpFlushState * flushState,const SkRect & chainBounds)240 void GrStrokeTessellateOp::onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) {
241 if (fStencilProgram) {
242 flushState->bindPipelineAndScissorClip(*fStencilProgram, chainBounds);
243 flushState->bindTextures(fStencilProgram->geomProc(), nullptr, fStencilProgram->pipeline());
244 fTessellator->draw(flushState);
245 }
246 if (fFillProgram) {
247 flushState->bindPipelineAndScissorClip(*fFillProgram, chainBounds);
248 flushState->bindTextures(fFillProgram->geomProc(), nullptr, fFillProgram->pipeline());
249 fTessellator->draw(flushState);
250 }
251 }
252