blob: 29f11280de7c3fadeec9260981cb23fee08deac3 [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
8#include "src/gpu/tessellate/GrStrokeOp.h"
9
10#include "src/core/SkPathPriv.h"
11#include "src/gpu/GrRecordingContextPriv.h"
12#include "src/gpu/tessellate/GrStrokeTessellateOp.h"
Chris Dalton0e543092020-11-03 14:09:16 -070013
Chris Dalton0e543092020-11-03 14:09:16 -070014GrStrokeOp::GrStrokeOp(uint32_t classID, GrAAType aaType, const SkMatrix& viewMatrix,
15 const SkStrokeRec& stroke, const SkPath& path, GrPaint&& paint)
16 : GrDrawOp(classID)
17 , fAAType(aaType)
18 , fViewMatrix(viewMatrix)
19 , fStroke(stroke)
Chris Dalton55abaf52020-12-08 10:25:13 -070020 , fColor(paint.getColor4f())
Chris Dalton0e543092020-11-03 14:09:16 -070021 , fProcessors(std::move(paint))
22 , fPathList(path)
Chris Dalton7b807262020-12-10 10:22:50 -070023 , fTotalCombinedVerbCnt(path.countVerbs())
24 , fTotalConicWeightCnt(SkPathPriv::ConicWeightCnt(path)) {
Chris Dalton0e543092020-11-03 14:09:16 -070025 SkRect devBounds = path.getBounds();
26 float inflationRadius = fStroke.getInflationRadius();
27 devBounds.outset(inflationRadius, inflationRadius);
28 viewMatrix.mapRect(&devBounds, devBounds);
29 this->setBounds(devBounds, HasAABloat(GrAAType::kCoverage == fAAType), IsHairline::kNo);
30}
31
Chris Daltonb0643342020-12-15 01:04:12 -070032void GrStrokeOp::visitProxies(const VisitProxyFunc& fn) const {
33 if (fFillProgram) {
34 fFillProgram->visitFPProxies(fn);
35 } else if (fStencilProgram) {
36 fStencilProgram->visitFPProxies(fn);
37 } else {
38 fProcessors.visitProxies(fn);
39 }
40}
41
Chris Dalton0e543092020-11-03 14:09:16 -070042GrDrawOp::FixedFunctionFlags GrStrokeOp::fixedFunctionFlags() const {
Chris Dalton55abaf52020-12-08 10:25:13 -070043 // We might not actually end up needing stencil, but won't know for sure until finalize().
44 // Request it just in case we do end up needing it.
45 auto flags = FixedFunctionFlags::kUsesStencil;
Chris Dalton0e543092020-11-03 14:09:16 -070046 if (GrAAType::kNone != fAAType) {
47 flags |= FixedFunctionFlags::kUsesHWAA;
48 }
49 return flags;
50}
51
52GrProcessorSet::Analysis GrStrokeOp::finalize(const GrCaps& caps, const GrAppliedClip* clip,
53 bool hasMixedSampledCoverage, GrClampType clampType) {
Chris Dalton55abaf52020-12-08 10:25:13 -070054 // Make sure the finalize happens before combining. We might change fNeedsStencil here.
55 SkASSERT(fPathList.begin().fCurr->fNext == nullptr);
56 const GrProcessorSet::Analysis& analysis = fProcessors.finalize(
57 fColor, GrProcessorAnalysisCoverage::kNone, clip, &GrUserStencilSettings::kUnused,
58 hasMixedSampledCoverage, caps, clampType, &fColor);
59 fNeedsStencil = !analysis.unaffectedByDstValue();
60 return analysis;
Chris Dalton0e543092020-11-03 14:09:16 -070061}
62
63GrOp::CombineResult GrStrokeOp::onCombineIfPossible(GrOp* grOp, SkArenaAlloc* alloc,
64 const GrCaps&) {
65 SkASSERT(grOp->classID() == this->classID());
66 auto* op = static_cast<GrStrokeOp*>(grOp);
Chris Dalton55abaf52020-12-08 10:25:13 -070067 if (fNeedsStencil ||
68 op->fNeedsStencil ||
69 fColor != op->fColor ||
Chris Dalton0e543092020-11-03 14:09:16 -070070 fViewMatrix != op->fViewMatrix ||
71 fAAType != op->fAAType ||
72 !fStroke.hasEqualEffect(op->fStroke) ||
73 fProcessors != op->fProcessors) {
74 return CombineResult::kCannotCombine;
75 }
76
77 fPathList.concat(std::move(op->fPathList), alloc);
78 fTotalCombinedVerbCnt += op->fTotalCombinedVerbCnt;
Chris Dalton7b807262020-12-10 10:22:50 -070079 fTotalConicWeightCnt += op->fTotalConicWeightCnt;
Chris Dalton0e543092020-11-03 14:09:16 -070080
81 return CombineResult::kMerged;
82}
83
Chris Dalton55abaf52020-12-08 10:25:13 -070084// Marks every stencil value as "1".
85constexpr static GrUserStencilSettings kMarkStencil(
86 GrUserStencilSettings::StaticInit<
87 0x0001,
88 GrUserStencilTest::kLessIfInClip, // Match kTestAndResetStencil.
89 0x0000, // Always fail.
90 GrUserStencilOp::kZero,
91 GrUserStencilOp::kReplace,
92 0xffff>());
93
94// Passes if the stencil value is nonzero. Also resets the stencil value to zero on pass. This is
95// formulated to match kMarkStencil everywhere except the ref and compare mask. This will allow us
96// to use the same pipeline for both stencil and fill if dynamic stencil state is supported.
97constexpr static GrUserStencilSettings kTestAndResetStencil(
98 GrUserStencilSettings::StaticInit<
99 0x0000,
100 GrUserStencilTest::kLessIfInClip, // i.e., "not equal to zero, if in clip".
101 0x0001,
102 GrUserStencilOp::kZero,
103 GrUserStencilOp::kReplace,
104 0xffff>());
105
Chris Dalton06b52ad2020-12-15 10:01:35 -0700106void GrStrokeOp::prePreparePrograms(GrStrokeTessellateShader::Mode shaderMode, SkArenaAlloc* arena,
Chris Dalton55abaf52020-12-08 10:25:13 -0700107 const GrSurfaceProxyView& writeView, GrAppliedClip&& clip,
108 const GrXferProcessor::DstProxyView& dstProxyView,
109 GrXferBarrierFlags renderPassXferBarriers,
110 GrLoadOp colorLoadOp, const GrCaps& caps) {
111 using InputFlags = GrPipeline::InputFlags;
112 SkASSERT(!fFillProgram);
113 SkASSERT(!fStencilProgram);
114
115 // This will be created iff the stencil pass can't share a pipeline with the fill pass.
116 GrPipeline* standaloneStencilPipeline = nullptr;
117
118 GrPipeline::InitArgs fillArgs;
119 fillArgs.fCaps = &caps;
120 fillArgs.fDstProxyView = dstProxyView;
121 fillArgs.fWriteSwizzle = writeView.swizzle();
122 if (fAAType != GrAAType::kNone) {
123 if (writeView.asRenderTargetProxy()->numSamples() == 1) {
124 // We are mixed sampled. We need to either enable conservative raster (preferred) or
125 // disable MSAA in order to avoid double blend artifacts. (Even if we disable MSAA for
126 // the cover geometry, the stencil test is still multisampled and will still produce
127 // smooth results.)
128 SkASSERT(GrAAType::kCoverage == fAAType);
129 if (caps.conservativeRasterSupport()) {
130 fillArgs.fInputFlags |= InputFlags::kHWAntialias | InputFlags::kConservativeRaster;
131 }
132 // Since we either need conservative raster enabled or MSAA disabled during fill, we
133 // need a separate pipeline for the stencil pass.
134 SkASSERT(fNeedsStencil); // Mixed samples always needs stencil.
135 GrPipeline::InitArgs stencilArgs;
136 stencilArgs.fCaps = &caps;
137 stencilArgs.fInputFlags = InputFlags::kHWAntialias;
138 stencilArgs.fWriteSwizzle = writeView.swizzle();
139 standaloneStencilPipeline = arena->make<GrPipeline>(
140 stencilArgs, GrDisableColorXPFactory::MakeXferProcessor(), clip.hardClip());
141 } else {
142 // We are standard MSAA. Leave MSAA enabled for both the fill and stencil passes.
143 fillArgs.fInputFlags |= InputFlags::kHWAntialias;
144 }
Chris Dalton0e543092020-11-03 14:09:16 -0700145 }
Chris Dalton55abaf52020-12-08 10:25:13 -0700146
Chris Dalton06b52ad2020-12-15 10:01:35 -0700147 auto* strokeTessellateShader = arena->make<GrStrokeTessellateShader>(
148 shaderMode, fTotalConicWeightCnt, fStroke, fViewMatrix, fColor);
Chris Dalton55abaf52020-12-08 10:25:13 -0700149 auto fillPipeline = arena->make<GrPipeline>(fillArgs, std::move(fProcessors), std::move(clip));
150 auto fillStencil = &GrUserStencilSettings::kUnused;
151 auto fillXferFlags = renderPassXferBarriers;
152 if (fNeedsStencil) {
153 auto* stencilPipeline = (standaloneStencilPipeline) ? standaloneStencilPipeline
154 : fillPipeline;
155 fStencilProgram = GrPathShader::MakeProgramInfo(strokeTessellateShader, arena, writeView,
156 stencilPipeline, dstProxyView,
157 renderPassXferBarriers, colorLoadOp,
158 &kMarkStencil, caps);
159 fillStencil = &kTestAndResetStencil;
160 fillXferFlags = GrXferBarrierFlags::kNone;
161 }
162 fFillProgram = GrPathShader::MakeProgramInfo(strokeTessellateShader, arena, writeView,
163 fillPipeline, dstProxyView, fillXferFlags,
164 colorLoadOp, fillStencil, caps);
Chris Dalton0e543092020-11-03 14:09:16 -0700165}