Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
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
49 if (fFillProgram) {
50 fFillProgram->visitFPProxies(func);
51 } else if (fStencilProgram) {
52 fStencilProgram->visitFPProxies(func);
53 } else {
54 fProcessors.visitProxies(func);
55 }
56}
57
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
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 |
111 PatchAttribs::kColor);
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
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
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,
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
std::function< void(GrSurfaceProxy *, skgpu::Mipmapped)> GrVisitProxyFunc
GrAAType
GrLoadOp
GrXferBarrierFlags
#define SkASSERT(cond)
Definition SkAssert.h:116
static SkPath clip(const SkPath &path, const SkHalfPlane &plane)
Definition SkPath.cpp:3824
static GrAppliedClip Disabled()
const GrCaps * caps() const
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
const GrGeometryProcessor & geomProc() const
void visitFPProxies(const GrVisitProxyFunc &func) const
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))
bool mapRect(SkRect *dst, const SkRect &src, SkApplyPerspectiveClip pc=SkApplyPerspectiveClip::kYes) const
static SkScalar GetInflationRadius(const SkPaint &, SkPaint::Style)
SkScalar getInflationRadius() const
SkPaint::Join getJoin() const
Definition SkStrokeRec.h:45
SkPaint::Cap getCap() const
Definition SkStrokeRec.h:44
SkScalar getMiter() const
Definition SkStrokeRec.h:43
void onPrePrepare(GrRecordingContext *, const GrSurfaceProxyView &, GrAppliedClip *, const GrDstProxyView &, GrXferBarrierFlags, GrLoadOp colorLoadOp) override
void onExecute(GrOpFlushState *, const SkRect &chainBounds) override
CombineResult onCombineIfPossible(GrOp *, SkArenaAlloc *, const GrCaps &) override
StrokeTessellateOp(GrAAType, const SkMatrix &, const SkPath &, const SkStrokeRec &, GrPaint &&)
void onPrepare(GrOpFlushState *) override
void visitProxies(const GrVisitProxyFunc &) const override
GrProcessorSet::Analysis finalize(const GrCaps &, const GrAppliedClip *, GrClampType) override
void draw(GrOpFlushState *) const
void prepare(GrMeshDrawTarget *, const SkMatrix &shaderMatrix, PathStrokeList *, int totalCombinedStrokeVerbCnt)
const Paint & paint
G_BEGIN_DECLS G_MODULE_EXPORT FlValue * args
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 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