Flutter Engine
The Flutter Engine
StrokeTessellateOp.cpp
Go to the documentation of this file.
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
9
10#include "src/base/SkMathPriv.h"
11#include "src/core/SkPathPriv.h"
17
18namespace skgpu::ganesh {
19
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 (!this->headColor().fitsInBytes()) {
30 fPatchAttribs |= PatchAttribs::kWideColorIfEnabled;
31 }
32 SkRect devBounds = path.getBounds();
33 if (!this->headStroke().isHairlineStyle()) {
34 // Non-hairlines inflate in local path space (pre-transform).
35 float r = stroke.getInflationRadius();
36 devBounds.outset(r, r);
37 }
38 viewMatrix.mapRect(&devBounds, devBounds);
39 if (this->headStroke().isHairlineStyle()) {
40 // Hairlines inflate in device space (post-transform).
41 float r = SkStrokeRec::GetInflationRadius(stroke.getJoin(), stroke.getMiter(),
42 stroke.getCap(), 1);
43 devBounds.outset(r, r);
44 }
45 this->setBounds(devBounds, HasAABloat::kNo, IsHairline::kNo);
46}
47
48void StrokeTessellateOp::visitProxies(const GrVisitProxyFunc& func) const {
49 if (fFillProgram) {
50 fFillProgram->visitFPProxies(func);
51 } else if (fStencilProgram) {
52 fStencilProgram->visitFPProxies(func);
53 } else {
54 fProcessors.visitProxies(func);
55 }
56}
57
58GrProcessorSet::Analysis StrokeTessellateOp::finalize(const GrCaps& caps,
59 const GrAppliedClip* clip,
60 GrClampType clampType) {
61 // Make sure the finalize happens before combining. We might change fNeedsStencil here.
62 SkASSERT(fPathStrokeList.fNext == nullptr);
63 if (!caps.shaderCaps()->fInfinitySupport) {
64 // The GPU can't infer curve type based in infinity, so we need to send in an attrib
65 // explicitly stating the curve type.
66 fPatchAttribs |= PatchAttribs::kExplicitCurveType;
67 }
68 const GrProcessorSet::Analysis& analysis = fProcessors.finalize(
69 this->headColor(), GrProcessorAnalysisCoverage::kNone, clip,
70 &GrUserStencilSettings::kUnused, caps, clampType, &this->headColor());
71 fNeedsStencil = !analysis.unaffectedByDstValue();
72 return analysis;
73}
74
75GrOp::CombineResult StrokeTessellateOp::onCombineIfPossible(GrOp* grOp, SkArenaAlloc* alloc,
76 const GrCaps& caps) {
77 SkASSERT(grOp->classID() == this->classID());
78 auto* op = static_cast<StrokeTessellateOp*>(grOp);
79
80 // This must be called after finalize(). fNeedsStencil can change in finalize().
81 SkASSERT(fProcessors.isFinalized());
82 SkASSERT(op->fProcessors.isFinalized());
83
84 if (fNeedsStencil ||
85 op->fNeedsStencil ||
86 fViewMatrix != op->fViewMatrix ||
87 fAAType != op->fAAType ||
88 fProcessors != op->fProcessors ||
89 this->headStroke().isHairlineStyle() != op->headStroke().isHairlineStyle()) {
91 }
92
93 auto combinedAttribs = fPatchAttribs | op->fPatchAttribs;
94 if (!(combinedAttribs & PatchAttribs::kStrokeParams) &&
95 !tess::StrokesHaveEqualParams(this->headStroke(), op->headStroke())) {
96 // The paths have different stroke properties. We will need to enable dynamic stroke if we
97 // still decide to combine them.
98 if (this->headStroke().isHairlineStyle()) {
99 return CombineResult::kCannotCombine; // Dynamic hairlines aren't supported.
100 }
101 combinedAttribs |= PatchAttribs::kStrokeParams;
102 }
103 if (!(combinedAttribs & PatchAttribs::kColor) && this->headColor() != op->headColor()) {
104 // The paths have different colors. We will need to enable dynamic color if we still decide
105 // to combine them.
106 combinedAttribs |= PatchAttribs::kColor;
107 }
108
109 // Don't actually enable new dynamic state on ops that already have lots of verbs.
110 constexpr static GrTFlagsMask<PatchAttribs> kDynamicStatesMask(PatchAttribs::kStrokeParams |
112 PatchAttribs neededDynamicStates = combinedAttribs & kDynamicStatesMask;
113 if (neededDynamicStates != PatchAttribs::kNone) {
114 if (!this->shouldUseDynamicStates(neededDynamicStates) ||
115 !op->shouldUseDynamicStates(neededDynamicStates)) {
117 }
118 }
119
120 fPatchAttribs = combinedAttribs;
121
122 // Concat the op's PathStrokeList. Since the head element is allocated inside the op, we need to
123 // copy it.
124 auto* headCopy = alloc->make<PathStrokeList>(std::move(op->fPathStrokeList));
125 *fPathStrokeTail = headCopy;
126 fPathStrokeTail = (op->fPathStrokeTail == &op->fPathStrokeList.fNext) ? &headCopy->fNext
127 : op->fPathStrokeTail;
128
129 fTotalCombinedVerbCnt += op->fTotalCombinedVerbCnt;
131}
132
133// Marks every stencil value as "1".
136 0x0001,
137 GrUserStencilTest::kLessIfInClip, // Match kTestAndResetStencil.
138 0x0000, // Always fail.
141 0xffff>());
142
143// Passes if the stencil value is nonzero. Also resets the stencil value to zero on pass. This is
144// formulated to match kMarkStencil everywhere except the ref and compare mask. This will allow us
145// to use the same pipeline for both stencil and fill if dynamic stencil state is supported.
148 0x0000,
149 GrUserStencilTest::kLessIfInClip, // i.e., "not equal to zero, if in clip".
150 0x0001,
153 0xffff>());
154
155void StrokeTessellateOp::prePrepareTessellator(GrTessellationShader::ProgramArgs&& args,
157 SkASSERT(!fTessellator);
158 SkASSERT(!fFillProgram);
159 SkASSERT(!fStencilProgram);
160 // GrOp::setClippedBounds() should have been called by now.
161 SkASSERT(SkRect::MakeIWH(args.fWriteView.width(),
162 args.fWriteView.height()).contains(this->bounds()));
163
164 const GrCaps& caps = *args.fCaps;
165 SkArenaAlloc* arena = args.fArena;
166
167 auto* pipeline = GrTessellationShader::MakePipeline(args, fAAType, std::move(clip),
168 std::move(fProcessors));
169
170 fTessellator = arena->make<StrokeTessellator>(fPatchAttribs);
171 fTessellationShader = args.fArena->make<GrStrokeTessellationShader>(
172 *caps.shaderCaps(),
173 fPatchAttribs,
174 fViewMatrix,
175 this->headStroke(),
176 this->headColor());
177
178 auto fillStencil = &GrUserStencilSettings::kUnused;
179 if (fNeedsStencil) {
180 fStencilProgram = GrTessellationShader::MakeProgram(args, fTessellationShader, pipeline,
181 &kMarkStencil);
182 fillStencil = &kTestAndResetStencil;
183 // TODO: Currently if we have a texture barrier for a dst read it will get put in before
184 // both the stencil draw and the fill draw. In reality we only really need the barrier
185 // once to guard the reads of the color buffer in the fill from the previous writes. Maybe
186 // we can investigate how to remove one of these barriers but it is probably not something
187 // that is required a lot and thus the extra barrier shouldn't be too much of a perf hit to
188 // general Skia use.
189 }
190
191 fFillProgram = GrTessellationShader::MakeProgram(args, fTessellationShader, pipeline,
192 fillStencil);
193}
194
195void StrokeTessellateOp::onPrePrepare(GrRecordingContext* context,
196 const GrSurfaceProxyView& writeView, GrAppliedClip* clip,
197 const GrDstProxyView& dstProxyView,
198 GrXferBarrierFlags renderPassXferBarriers, GrLoadOp
199 colorLoadOp) {
200 // DMSAA is not supported on DDL.
201 bool usesMSAASurface = writeView.asRenderTargetProxy()->numSamples() > 1;
202 this->prePrepareTessellator({context->priv().recordTimeAllocator(), writeView, usesMSAASurface,
203 &dstProxyView, renderPassXferBarriers, colorLoadOp,
204 context->priv().caps()},
205 (clip) ? std::move(*clip) : GrAppliedClip::Disabled());
206 if (fStencilProgram) {
207 context->priv().recordProgramInfo(fStencilProgram);
208 }
209 if (fFillProgram) {
210 context->priv().recordProgramInfo(fFillProgram);
211 }
212}
213
214void StrokeTessellateOp::onPrepare(GrOpFlushState* flushState) {
215 if (!fTessellator) {
216 this->prePrepareTessellator({flushState->allocator(), flushState->writeView(),
217 flushState->usesMSAASurface(), &flushState->dstProxyView(),
218 flushState->renderPassBarriers(), flushState->colorLoadOp(),
219 &flushState->caps()}, flushState->detachAppliedClip());
220 }
221 SkASSERT(fTessellator);
222 fTessellator->prepare(flushState,
223 fViewMatrix,
224 &fPathStrokeList,
225 fTotalCombinedVerbCnt);
226}
227
228void StrokeTessellateOp::onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) {
229 if (fStencilProgram) {
230 flushState->bindPipelineAndScissorClip(*fStencilProgram, chainBounds);
231 flushState->bindTextures(fStencilProgram->geomProc(), nullptr, fStencilProgram->pipeline());
232 fTessellator->draw(flushState);
233 }
234 if (fFillProgram) {
235 flushState->bindPipelineAndScissorClip(*fFillProgram, chainBounds);
236 flushState->bindTextures(fFillProgram->geomProc(), nullptr, fFillProgram->pipeline());
237 fTessellator->draw(flushState);
238 }
239}
240
241} // namespace skgpu::ganesh
SkMatrix fViewMatrix
GrClampType
Definition: GrTypesPriv.h:228
std::function< void(GrSurfaceProxy *, skgpu::Mipmapped)> GrVisitProxyFunc
Definition: GrTypesPriv.h:943
GrAAType
Definition: GrTypesPriv.h:200
GrLoadOp
Definition: GrTypesPriv.h:155
GrXferBarrierFlags
#define SkASSERT(cond)
Definition: SkAssert.h:116
static SkPath clip(const SkPath &path, const SkHalfPlane &plane)
Definition: SkPath.cpp:3892
static GrAppliedClip Disabled()
Definition: GrAppliedClip.h:96
const GrCaps * caps() const
Definition: GrCaps.h:57
const GrShaderCaps * shaderCaps() const
Definition: GrCaps.h:63
GrLoadOp colorLoadOp() const final
const GrDstProxyView & dstProxyView() const final
GrXferBarrierFlags renderPassBarriers() const final
SkArenaAlloc * allocator() override
void bindPipelineAndScissorClip(const GrProgramInfo &programInfo, const SkRect &drawBounds)
const GrSurfaceProxyView & writeView() const final
GrAppliedClip detachAppliedClip() final
const GrCaps & caps() const final
void bindTextures(const GrGeometryProcessor &geomProc, const GrSurfaceProxy &singleGeomProcTexture, const GrPipeline &pipeline)
bool usesMSAASurface() const final
Definition: GrOp.h:70
CombineResult
Definition: GrOp.h:99
uint32_t classID() const
Definition: GrOp.h:158
void setBounds(const SkRect &newBounds, HasAABloat aabloat, IsHairline zeroArea)
Definition: GrOp.h:279
bool unaffectedByDstValue() const
void visitProxies(const GrVisitProxyFunc &) const
bool isFinalized() const
Analysis finalize(const GrProcessorAnalysisColor &, const GrProcessorAnalysisCoverage, const GrAppliedClip *, const GrUserStencilSettings *, const GrCaps &, GrClampType, SkPMColor4f *inputColorOverride)
const GrPipeline & pipeline() const
Definition: GrProgramInfo.h:39
const GrGeometryProcessor & geomProc() const
Definition: GrProgramInfo.h:40
void visitFPProxies(const GrVisitProxyFunc &func) const
Definition: GrProgramInfo.h:64
void recordProgramInfo(const GrProgramInfo *programInfo)
GrRecordingContextPriv priv()
GrRenderTargetProxy * asRenderTargetProxy() const
static const GrPipeline * MakePipeline(const ProgramArgs &, GrAAType, GrAppliedClip &&, GrProcessorSet &&)
static GrProgramInfo * MakeProgram(const ProgramArgs &args, const GrTessellationShader *shader, const GrPipeline *pipeline, const GrUserStencilSettings *stencil)
auto make(Ctor &&ctor) -> decltype(ctor(nullptr))
Definition: SkArenaAlloc.h:120
bool mapRect(SkRect *dst, const SkRect &src, SkApplyPerspectiveClip pc=SkApplyPerspectiveClip::kYes) const
Definition: SkMatrix.cpp:1141
Definition: SkPath.h:59
static SkScalar GetInflationRadius(const SkPaint &, SkPaint::Style)
StrokeTessellateOp(GrAAType, const SkMatrix &, const SkPath &, const SkStrokeRec &, GrPaint &&)
void draw(GrOpFlushState *) const
void prepare(GrMeshDrawTarget *, const SkMatrix &shaderMatrix, PathStrokeList *, int totalCombinedStrokeVerbCnt)
const Paint & paint
Definition: color_source.cc:38
G_BEGIN_DECLS G_MODULE_EXPORT FlValue * args
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir path
Definition: switches.h:57
static constexpr GrUserStencilSettings kMarkStencil(GrUserStencilSettings::StaticInit< 0x0001, GrUserStencilTest::kLessIfInClip, 0x0000, GrUserStencilOp::kZero, GrUserStencilOp::kReplace, 0xffff >())
static constexpr GrUserStencilSettings kTestAndResetStencil(GrUserStencilSettings::StaticInit< 0x0000, GrUserStencilTest::kLessIfInClip, 0x0001, GrUserStencilOp::kZero, GrUserStencilOp::kReplace, 0xffff >())
bool StrokesHaveEqualParams(const SkStrokeRec &a, const SkStrokeRec &b)
Definition: Tessellation.h:171
Definition: ref_ptr.h:256
static constexpr Init< Ref, Test, TestMask, PassOp, FailOp, WriteMask > StaticInit()
static const GrUserStencilSettings & kUnused
static SkRect MakeIWH(int w, int h)
Definition: SkRect.h:623
void outset(float dx, float dy)
Definition: SkRect.h:1077
bool contains(SkScalar x, SkScalar y) const
Definition: extension.cpp:19
bool fInfinitySupport
Definition: SkSLUtil.h:103