blob: 72009f56ba333c4bae8b263af6faaddb77a9bc40 [file] [log] [blame]
Chris Dalton0e543092020-11-03 14:09:16 -07001/*
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
Chris Dalton05007df2021-02-04 00:24:52 -07008#include "src/gpu/tessellate/GrStrokeTessellateOp.h"
Chris Dalton0e543092020-11-03 14:09:16 -07009
10#include "src/core/SkPathPriv.h"
11#include "src/gpu/GrRecordingContextPriv.h"
Chris Dalton22241002021-02-04 09:47:40 -070012#include "src/gpu/tessellate/GrFillPathShader.h"
13#include "src/gpu/tessellate/GrStencilPathShader.h"
14#include "src/gpu/tessellate/GrStrokeHardwareTessellator.h"
15#include "src/gpu/tessellate/GrStrokeIndirectTessellator.h"
Chris Dalton0e543092020-11-03 14:09:16 -070016
Chris Dalton05007df2021-02-04 00:24:52 -070017GrStrokeTessellateOp::GrStrokeTessellateOp(GrAAType aaType, const SkMatrix& viewMatrix,
18 const SkPath& path, const SkStrokeRec& stroke,
19 GrPaint&& paint)
Chris Dalton22241002021-02-04 09:47:40 -070020 : GrDrawOp(ClassID())
Chris Dalton0e543092020-11-03 14:09:16 -070021 , fAAType(aaType)
22 , fViewMatrix(viewMatrix)
23 , fStroke(stroke)
Chris Dalton55abaf52020-12-08 10:25:13 -070024 , fColor(paint.getColor4f())
Chris Dalton0e543092020-11-03 14:09:16 -070025 , fProcessors(std::move(paint))
26 , fPathList(path)
Chris Dalton7b807262020-12-10 10:22:50 -070027 , fTotalCombinedVerbCnt(path.countVerbs())
Chris Dalton22241002021-02-04 09:47:40 -070028 , fHasConics(SkPathPriv::ConicWeightCnt(path) != 0) {
Chris Dalton0e543092020-11-03 14:09:16 -070029 SkRect devBounds = path.getBounds();
30 float inflationRadius = fStroke.getInflationRadius();
31 devBounds.outset(inflationRadius, inflationRadius);
32 viewMatrix.mapRect(&devBounds, devBounds);
33 this->setBounds(devBounds, HasAABloat(GrAAType::kCoverage == fAAType), IsHairline::kNo);
34}
35
Chris Dalton05007df2021-02-04 00:24:52 -070036void GrStrokeTessellateOp::visitProxies(const VisitProxyFunc& fn) const {
Chris Daltonb0643342020-12-15 01:04:12 -070037 if (fFillProgram) {
38 fFillProgram->visitFPProxies(fn);
39 } else if (fStencilProgram) {
40 fStencilProgram->visitFPProxies(fn);
41 } else {
42 fProcessors.visitProxies(fn);
43 }
44}
45
Chris Dalton05007df2021-02-04 00:24:52 -070046GrDrawOp::FixedFunctionFlags GrStrokeTessellateOp::fixedFunctionFlags() const {
Chris Dalton55abaf52020-12-08 10:25:13 -070047 // We might not actually end up needing stencil, but won't know for sure until finalize().
48 // Request it just in case we do end up needing it.
49 auto flags = FixedFunctionFlags::kUsesStencil;
Chris Dalton0e543092020-11-03 14:09:16 -070050 if (GrAAType::kNone != fAAType) {
51 flags |= FixedFunctionFlags::kUsesHWAA;
52 }
53 return flags;
54}
55
Chris Dalton05007df2021-02-04 00:24:52 -070056GrProcessorSet::Analysis GrStrokeTessellateOp::finalize(const GrCaps& caps,
57 const GrAppliedClip* clip,
58 bool hasMixedSampledCoverage,
59 GrClampType clampType) {
Chris Dalton55abaf52020-12-08 10:25:13 -070060 // Make sure the finalize happens before combining. We might change fNeedsStencil here.
61 SkASSERT(fPathList.begin().fCurr->fNext == nullptr);
62 const GrProcessorSet::Analysis& analysis = fProcessors.finalize(
63 fColor, GrProcessorAnalysisCoverage::kNone, clip, &GrUserStencilSettings::kUnused,
64 hasMixedSampledCoverage, caps, clampType, &fColor);
65 fNeedsStencil = !analysis.unaffectedByDstValue();
66 return analysis;
Chris Dalton0e543092020-11-03 14:09:16 -070067}
68
Chris Dalton05007df2021-02-04 00:24:52 -070069GrOp::CombineResult GrStrokeTessellateOp::onCombineIfPossible(GrOp* grOp, SkArenaAlloc* alloc,
70 const GrCaps&) {
Chris Dalton0e543092020-11-03 14:09:16 -070071 SkASSERT(grOp->classID() == this->classID());
Chris Dalton05007df2021-02-04 00:24:52 -070072 auto* op = static_cast<GrStrokeTessellateOp*>(grOp);
Chris Dalton55abaf52020-12-08 10:25:13 -070073 if (fNeedsStencil ||
74 op->fNeedsStencil ||
75 fColor != op->fColor ||
Chris Dalton0e543092020-11-03 14:09:16 -070076 fViewMatrix != op->fViewMatrix ||
77 fAAType != op->fAAType ||
78 !fStroke.hasEqualEffect(op->fStroke) ||
79 fProcessors != op->fProcessors) {
80 return CombineResult::kCannotCombine;
81 }
82
83 fPathList.concat(std::move(op->fPathList), alloc);
84 fTotalCombinedVerbCnt += op->fTotalCombinedVerbCnt;
Chris Dalton22241002021-02-04 09:47:40 -070085 fHasConics |= op->fHasConics;
Chris Dalton0e543092020-11-03 14:09:16 -070086
87 return CombineResult::kMerged;
88}
89
Chris Dalton55abaf52020-12-08 10:25:13 -070090// Marks every stencil value as "1".
91constexpr static GrUserStencilSettings kMarkStencil(
92 GrUserStencilSettings::StaticInit<
93 0x0001,
94 GrUserStencilTest::kLessIfInClip, // Match kTestAndResetStencil.
95 0x0000, // Always fail.
96 GrUserStencilOp::kZero,
97 GrUserStencilOp::kReplace,
98 0xffff>());
99
100// Passes if the stencil value is nonzero. Also resets the stencil value to zero on pass. This is
101// formulated to match kMarkStencil everywhere except the ref and compare mask. This will allow us
102// to use the same pipeline for both stencil and fill if dynamic stencil state is supported.
103constexpr static GrUserStencilSettings kTestAndResetStencil(
104 GrUserStencilSettings::StaticInit<
105 0x0000,
106 GrUserStencilTest::kLessIfInClip, // i.e., "not equal to zero, if in clip".
107 0x0001,
108 GrUserStencilOp::kZero,
109 GrUserStencilOp::kReplace,
110 0xffff>());
111
Chris Dalton05007df2021-02-04 00:24:52 -0700112void GrStrokeTessellateOp::prePrepareTessellator(GrPathShader::ProgramArgs&& args,
113 GrAppliedClip&& clip) {
Chris Dalton22241002021-02-04 09:47:40 -0700114 SkASSERT(!fTessellator);
Chris Dalton55abaf52020-12-08 10:25:13 -0700115 SkASSERT(!fFillProgram);
116 SkASSERT(!fStencilProgram);
117
Chris Dalton22241002021-02-04 09:47:40 -0700118 const GrCaps& caps = *args.fCaps;
119 SkArenaAlloc* arena = args.fArena;
Chris Dalton55abaf52020-12-08 10:25:13 -0700120
Chris Dalton22241002021-02-04 09:47:40 -0700121 // Only use hardware tessellation if the path has a somewhat large number of verbs. Otherwise we
122 // seem to be better off using indirect draws. Our back door for HW tessellation shaders isn't
123 // currently capable of passing varyings to the fragment shader either, so if the processors
124 // have varyings we need to use indirect draws.
125 GrStrokeTessellateShader::Mode shaderMode;
126 if (caps.shaderCaps()->tessellationSupport() &&
127 fTotalCombinedVerbCnt > 50 &&
128 !fProcessors.usesVaryingCoords()) {
129 fTessellator = arena->make<GrStrokeHardwareTessellator>(*caps.shaderCaps(), fViewMatrix,
130 fStroke);
131 shaderMode = GrStrokeTessellateShader::Mode::kTessellation;
132 } else {
133 fTessellator = arena->make<GrStrokeIndirectTessellator>(fViewMatrix, fPathList, fStroke,
134 fTotalCombinedVerbCnt, arena);
135 shaderMode = GrStrokeTessellateShader::Mode::kIndirect;
136 }
137
138 // If we are mixed sampled then we need a separate pipeline for the stencil pass. This is
139 // because mixed samples either needs conservative raster enabled or MSAA disabled during fill.
140 const GrPipeline* mixedSampledStencilPipeline = nullptr;
141 if (fAAType == GrAAType::kCoverage) {
142 SkASSERT(args.fWriteView.asRenderTargetProxy()->numSamples() == 1);
143 SkASSERT(fNeedsStencil); // Mixed samples always needs stencil.
144 mixedSampledStencilPipeline = GrStencilPathShader::MakeStencilPassPipeline(
145 args, fAAType, GrTessellationPathRenderer::OpFlags::kNone, clip.hardClip());
Chris Dalton0e543092020-11-03 14:09:16 -0700146 }
Chris Dalton55abaf52020-12-08 10:25:13 -0700147
Chris Dalton06b52ad2020-12-15 10:01:35 -0700148 auto* strokeTessellateShader = arena->make<GrStrokeTessellateShader>(
Chris Dalton22241002021-02-04 09:47:40 -0700149 shaderMode, fHasConics, fStroke, fViewMatrix, fColor);
150 auto* fillPipeline = GrFillPathShader::MakeFillPassPipeline(args, fAAType, std::move(clip),
151 std::move(fProcessors));
Chris Dalton55abaf52020-12-08 10:25:13 -0700152 auto fillStencil = &GrUserStencilSettings::kUnused;
Chris Dalton55abaf52020-12-08 10:25:13 -0700153 if (fNeedsStencil) {
Chris Dalton22241002021-02-04 09:47:40 -0700154 auto* stencilPipeline = (mixedSampledStencilPipeline) ? mixedSampledStencilPipeline
155 : fillPipeline;
156 fStencilProgram = GrPathShader::MakeProgram(args, strokeTessellateShader, stencilPipeline,
157 &kMarkStencil);
Chris Dalton55abaf52020-12-08 10:25:13 -0700158 fillStencil = &kTestAndResetStencil;
Chris Dalton22241002021-02-04 09:47:40 -0700159 args.fXferBarrierFlags = GrXferBarrierFlags::kNone;
Chris Dalton55abaf52020-12-08 10:25:13 -0700160 }
Chris Dalton22241002021-02-04 09:47:40 -0700161
162 fFillProgram = GrPathShader::MakeProgram(args, strokeTessellateShader, fillPipeline,
163 fillStencil);
164}
165
Chris Dalton05007df2021-02-04 00:24:52 -0700166void GrStrokeTessellateOp::onPrePrepare(GrRecordingContext* context,
167 const GrSurfaceProxyView& writeView, GrAppliedClip* clip,
168 const GrXferProcessor::DstProxyView& dstProxyView,
169 GrXferBarrierFlags renderPassXferBarriers, GrLoadOp
170 colorLoadOp) {
Chris Dalton22241002021-02-04 09:47:40 -0700171 this->prePrepareTessellator({context->priv().recordTimeAllocator(), writeView, &dstProxyView,
172 renderPassXferBarriers, colorLoadOp, context->priv().caps()},
173 (clip) ? std::move(*clip) : GrAppliedClip::Disabled());
174 if (fStencilProgram) {
175 context->priv().recordProgramInfo(fStencilProgram);
176 }
177 if (fFillProgram) {
178 context->priv().recordProgramInfo(fFillProgram);
179 }
180}
181
Chris Dalton05007df2021-02-04 00:24:52 -0700182void GrStrokeTessellateOp::onPrepare(GrOpFlushState* flushState) {
Chris Dalton22241002021-02-04 09:47:40 -0700183 if (!fTessellator) {
184 this->prePrepareTessellator({flushState->allocator(), flushState->writeView(),
185 &flushState->dstProxyView(), flushState->renderPassBarriers(),
186 flushState->colorLoadOp(), &flushState->caps()},
187 flushState->detachAppliedClip());
188 }
189 SkASSERT(fTessellator);
190 fTessellator->prepare(flushState, fViewMatrix, fPathList, fStroke, fTotalCombinedVerbCnt);
191}
192
Chris Dalton05007df2021-02-04 00:24:52 -0700193void GrStrokeTessellateOp::onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) {
Chris Dalton22241002021-02-04 09:47:40 -0700194 SkASSERT(chainBounds == this->bounds());
195 if (fStencilProgram) {
196 flushState->bindPipelineAndScissorClip(*fStencilProgram, this->bounds());
197 flushState->bindTextures(fStencilProgram->primProc(), nullptr, fStencilProgram->pipeline());
198 fTessellator->draw(flushState);
199 }
200 if (fFillProgram) {
201 flushState->bindPipelineAndScissorClip(*fFillProgram, this->bounds());
202 flushState->bindTextures(fFillProgram->primProc(), nullptr, fFillProgram->pipeline());
203 fTessellator->draw(flushState);
204 }
Chris Dalton0e543092020-11-03 14:09:16 -0700205}