• 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/ops/StrokeTessellateOp.h"
9 
10 #include "src/core/SkMathPriv.h"
11 #include "src/core/SkPathPriv.h"
12 #include "src/gpu/GrAppliedClip.h"
13 #include "src/gpu/GrOpFlushState.h"
14 #include "src/gpu/GrRecordingContextPriv.h"
15 #include "src/gpu/tessellate/StrokeFixedCountTessellator.h"
16 #include "src/gpu/tessellate/StrokeHardwareTessellator.h"
17 #include "src/gpu/tessellate/shaders/GrStrokeTessellationShader.h"
18 
19 namespace {
20 
can_use_hardware_tessellation(int numVerbs,const GrPipeline & pipeline,const GrCaps & caps)21 bool can_use_hardware_tessellation(int numVerbs, const GrPipeline& pipeline, const GrCaps& caps) {
22     if (!caps.shaderCaps()->tessellationSupport() ||
23         !caps.shaderCaps()->infinitySupport() /* The hw tessellation shaders use infinity. */) {
24         return false;
25     }
26     if (pipeline.usesLocalCoords()) {
27         // Our back door for HW tessellation shaders isn't currently capable of passing varyings to
28         // the fragment shader, so if the processors have varyings, we need to use instanced draws
29         // instead.
30         return false;
31     }
32     // Only use hardware tessellation if we're drawing a somewhat large number of verbs. Otherwise
33     // we seem to be better off using instanced draws.
34     return numVerbs >= caps.minStrokeVerbsForHwTessellation();
35 }
36 
37 } // anonymous namespace
38 
39 namespace skgpu::v1 {
40 
StrokeTessellateOp(GrAAType aaType,const SkMatrix & viewMatrix,const SkPath & path,const SkStrokeRec & stroke,GrPaint && paint)41 StrokeTessellateOp::StrokeTessellateOp(GrAAType aaType, const SkMatrix& viewMatrix,
42                                        const SkPath& path, const SkStrokeRec& stroke,
43                                        GrPaint&& paint)
44         : GrDrawOp(ClassID())
45         , fAAType(aaType)
46         , fViewMatrix(viewMatrix)
47         , fPathStrokeList(path, stroke, paint.getColor4f())
48         , fTotalCombinedVerbCnt(path.countVerbs())
49         , fProcessors(std::move(paint)) {
50     if (!this->headColor().fitsInBytes()) {
51         fPatchAttribs |= PatchAttribs::kWideColorIfEnabled;
52     }
53     SkRect devBounds = path.getBounds();
54     if (!this->headStroke().isHairlineStyle()) {
55         // Non-hairlines inflate in local path space (pre-transform).
56         float r = stroke.getInflationRadius();
57         devBounds.outset(r, r);
58     }
59     viewMatrix.mapRect(&devBounds, devBounds);
60     if (this->headStroke().isHairlineStyle()) {
61         // Hairlines inflate in device space (post-transform).
62         float r = SkStrokeRec::GetInflationRadius(stroke.getJoin(), stroke.getMiter(),
63                                                   stroke.getCap(), 1);
64         devBounds.outset(r, r);
65     }
66     this->setBounds(devBounds, HasAABloat::kNo, IsHairline::kNo);
67 }
68 
visitProxies(const GrVisitProxyFunc & func) const69 void StrokeTessellateOp::visitProxies(const GrVisitProxyFunc& func) const {
70     if (fFillProgram) {
71         fFillProgram->visitFPProxies(func);
72     } else if (fStencilProgram) {
73         fStencilProgram->visitFPProxies(func);
74     } else {
75         fProcessors.visitProxies(func);
76     }
77 }
78 
finalize(const GrCaps & caps,const GrAppliedClip * clip,GrClampType clampType)79 GrProcessorSet::Analysis StrokeTessellateOp::finalize(const GrCaps& caps,
80                                                       const GrAppliedClip* clip,
81                                                       GrClampType clampType) {
82     // Make sure the finalize happens before combining. We might change fNeedsStencil here.
83     SkASSERT(fPathStrokeList.fNext == nullptr);
84     if (!caps.shaderCaps()->infinitySupport()) {
85         // The GPU can't infer curve type based in infinity, so we need to send in an attrib
86         // explicitly stating the curve type.
87         fPatchAttribs |= PatchAttribs::kExplicitCurveType;
88     }
89     const GrProcessorSet::Analysis& analysis = fProcessors.finalize(
90             this->headColor(), GrProcessorAnalysisCoverage::kNone, clip,
91             &GrUserStencilSettings::kUnused, caps, clampType, &this->headColor());
92     fNeedsStencil = !analysis.unaffectedByDstValue();
93     return analysis;
94 }
95 
onCombineIfPossible(GrOp * grOp,SkArenaAlloc * alloc,const GrCaps & caps)96 GrOp::CombineResult StrokeTessellateOp::onCombineIfPossible(GrOp* grOp, SkArenaAlloc* alloc,
97                                                             const GrCaps& caps) {
98     SkASSERT(grOp->classID() == this->classID());
99     auto* op = static_cast<StrokeTessellateOp*>(grOp);
100 
101     // This must be called after finalize(). fNeedsStencil can change in finalize().
102     SkASSERT(fProcessors.isFinalized());
103     SkASSERT(op->fProcessors.isFinalized());
104 
105     if (fNeedsStencil ||
106         op->fNeedsStencil ||
107         fViewMatrix != op->fViewMatrix ||
108         fAAType != op->fAAType ||
109         fProcessors != op->fProcessors ||
110         this->headStroke().isHairlineStyle() != op->headStroke().isHairlineStyle()) {
111         return CombineResult::kCannotCombine;
112     }
113 
114     auto combinedAttribs = fPatchAttribs | op->fPatchAttribs;
115     if (!(combinedAttribs & PatchAttribs::kStrokeParams) &&
116         !StrokeParams::StrokesHaveEqualParams(this->headStroke(), op->headStroke())) {
117         // The paths have different stroke properties. We will need to enable dynamic stroke if we
118         // still decide to combine them.
119         if (this->headStroke().isHairlineStyle()) {
120             return CombineResult::kCannotCombine;  // Dynamic hairlines aren't supported.
121         }
122         combinedAttribs |= PatchAttribs::kStrokeParams;
123     }
124     if (!(combinedAttribs & PatchAttribs::kColor) && this->headColor() != op->headColor()) {
125         // The paths have different colors. We will need to enable dynamic color if we still decide
126         // to combine them.
127         combinedAttribs |= PatchAttribs::kColor;
128     }
129 
130     // Don't actually enable new dynamic state on ops that already have lots of verbs.
131     constexpr static GrTFlagsMask<PatchAttribs> kDynamicStatesMask(PatchAttribs::kStrokeParams |
132                                                                    PatchAttribs::kColor);
133     PatchAttribs neededDynamicStates = combinedAttribs & kDynamicStatesMask;
134     if (neededDynamicStates != PatchAttribs::kNone) {
135         if (!this->shouldUseDynamicStates(neededDynamicStates) ||
136             !op->shouldUseDynamicStates(neededDynamicStates)) {
137             return CombineResult::kCannotCombine;
138         }
139     }
140 
141     fPatchAttribs = combinedAttribs;
142 
143     // Concat the op's PathStrokeList. Since the head element is allocated inside the op, we need to
144     // copy it.
145     auto* headCopy = alloc->make<PathStrokeList>(std::move(op->fPathStrokeList));
146     *fPathStrokeTail = headCopy;
147     fPathStrokeTail = (op->fPathStrokeTail == &op->fPathStrokeList.fNext) ? &headCopy->fNext
148                                                                           : op->fPathStrokeTail;
149 
150     fTotalCombinedVerbCnt += op->fTotalCombinedVerbCnt;
151     return CombineResult::kMerged;
152 }
153 
154 // Marks every stencil value as "1".
155 constexpr static GrUserStencilSettings kMarkStencil(
156     GrUserStencilSettings::StaticInit<
157         0x0001,
158         GrUserStencilTest::kLessIfInClip,  // Match kTestAndResetStencil.
159         0x0000,  // Always fail.
160         GrUserStencilOp::kZero,
161         GrUserStencilOp::kReplace,
162         0xffff>());
163 
164 // Passes if the stencil value is nonzero. Also resets the stencil value to zero on pass. This is
165 // formulated to match kMarkStencil everywhere except the ref and compare mask. This will allow us
166 // to use the same pipeline for both stencil and fill if dynamic stencil state is supported.
167 constexpr static GrUserStencilSettings kTestAndResetStencil(
168     GrUserStencilSettings::StaticInit<
169         0x0000,
170         GrUserStencilTest::kLessIfInClip,  // i.e., "not equal to zero, if in clip".
171         0x0001,
172         GrUserStencilOp::kZero,
173         GrUserStencilOp::kReplace,
174         0xffff>());
175 
prePrepareTessellator(GrTessellationShader::ProgramArgs && args,GrAppliedClip && clip)176 void StrokeTessellateOp::prePrepareTessellator(GrTessellationShader::ProgramArgs&& args,
177                                                GrAppliedClip&& clip) {
178     SkASSERT(!fTessellator);
179     SkASSERT(!fFillProgram);
180     SkASSERT(!fStencilProgram);
181     // GrOp::setClippedBounds() should have been called by now.
182     SkASSERT(SkRect::MakeIWH(args.fWriteView.width(),
183                              args.fWriteView.height()).contains(this->bounds()));
184 
185     const GrCaps& caps = *args.fCaps;
186     SkArenaAlloc* arena = args.fArena;
187 
188     auto* pipeline = GrTessellationShader::MakePipeline(args, fAAType, std::move(clip),
189                                                         std::move(fProcessors));
190 
191     GrStrokeTessellationShader::Mode shaderMode;
192     int maxParametricSegments_log2;
193     if (can_use_hardware_tessellation(fTotalCombinedVerbCnt, *pipeline, caps)) {
194         // 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.
196         fTessellator = arena->make<StrokeHardwareTessellator>(fPatchAttribs);
197         shaderMode = GrStrokeTessellationShader::Mode::kHardwareTessellation;
198         // This sets a limit on the number of binary search iterations inside the shader, so we
199         // round up to the next log2 to guarantee it makes enough.
200         maxParametricSegments_log2 = SkNextLog2(caps.shaderCaps()->maxTessellationSegments());
201     } else {
202         fTessellator = arena->make<StrokeFixedCountTessellator>(fPatchAttribs);
203         shaderMode = GrStrokeTessellationShader::Mode::kFixedCount;
204         maxParametricSegments_log2 = StrokeFixedCountTessellator::kMaxParametricSegments_log2;
205     }
206 
207     fTessellationShader = args.fArena->make<GrStrokeTessellationShader>(*caps.shaderCaps(),
208                                                                         shaderMode,
209                                                                         fPatchAttribs,
210                                                                         fViewMatrix,
211                                                                         this->headStroke(),
212                                                                         this->headColor(),
213                                                                         maxParametricSegments_log2);
214 
215     auto fillStencil = &GrUserStencilSettings::kUnused;
216     if (fNeedsStencil) {
217         fStencilProgram = GrTessellationShader::MakeProgram(args, fTessellationShader, pipeline,
218                                                             &kMarkStencil);
219         fillStencil = &kTestAndResetStencil;
220         args.fXferBarrierFlags = GrXferBarrierFlags::kNone;
221     }
222 
223     fFillProgram = GrTessellationShader::MakeProgram(args, fTessellationShader, pipeline,
224                                                      fillStencil);
225 }
226 
onPrePrepare(GrRecordingContext * context,const GrSurfaceProxyView & writeView,GrAppliedClip * clip,const GrDstProxyView & dstProxyView,GrXferBarrierFlags renderPassXferBarriers,GrLoadOp colorLoadOp)227 void StrokeTessellateOp::onPrePrepare(GrRecordingContext* context,
228                                       const GrSurfaceProxyView& writeView, GrAppliedClip* clip,
229                                       const GrDstProxyView& dstProxyView,
230                                       GrXferBarrierFlags renderPassXferBarriers, GrLoadOp
231                                       colorLoadOp) {
232     // DMSAA is not supported on DDL.
233     bool usesMSAASurface = writeView.asRenderTargetProxy()->numSamples() > 1;
234     this->prePrepareTessellator({context->priv().recordTimeAllocator(), writeView, usesMSAASurface,
235                                 &dstProxyView, renderPassXferBarriers, colorLoadOp,
236                                 context->priv().caps()},
237                                 (clip) ? std::move(*clip) : GrAppliedClip::Disabled());
238     if (fStencilProgram) {
239         context->priv().recordProgramInfo(fStencilProgram);
240     }
241     if (fFillProgram) {
242         context->priv().recordProgramInfo(fFillProgram);
243     }
244 }
245 
onPrepare(GrOpFlushState * flushState)246 void StrokeTessellateOp::onPrepare(GrOpFlushState* flushState) {
247     if (!fTessellator) {
248         this->prePrepareTessellator({flushState->allocator(), flushState->writeView(),
249                                     flushState->usesMSAASurface(), &flushState->dstProxyView(),
250                                     flushState->renderPassBarriers(), flushState->colorLoadOp(),
251                                     &flushState->caps()}, flushState->detachAppliedClip());
252     }
253     SkASSERT(fTessellator);
254     std::array<float, 2> matrixMinMaxScales;
255     if (!fViewMatrix.getMinMaxScales(matrixMinMaxScales.data())) {
256         matrixMinMaxScales.fill(1);
257     }
258     int fixedEdgeCount = fTessellator->prepare(flushState,
259                                                fViewMatrix,
260                                                matrixMinMaxScales,
261                                                &fPathStrokeList,
262                                                fTotalCombinedVerbCnt);
263     if (!fTessellationShader->willUseTessellationShaders()) {
264         fTessellationShader->setFixedCountNumTotalEdges(fixedEdgeCount);
265     }
266 }
267 
onExecute(GrOpFlushState * flushState,const SkRect & chainBounds)268 void StrokeTessellateOp::onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) {
269     if (fStencilProgram) {
270         flushState->bindPipelineAndScissorClip(*fStencilProgram, chainBounds);
271         flushState->bindTextures(fStencilProgram->geomProc(), nullptr, fStencilProgram->pipeline());
272         fTessellator->draw(flushState);
273     }
274     if (fFillProgram) {
275         flushState->bindPipelineAndScissorClip(*fFillProgram, chainBounds);
276         flushState->bindTextures(fFillProgram->geomProc(), nullptr, fFillProgram->pipeline());
277         fTessellator->draw(flushState);
278     }
279 }
280 
281 } // namespace skgpu::v1
282