• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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