1/*
2 * Copyright 2019 Google Inc.
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/ccpr/GrStencilAtlasOp.h"
9
10#include "include/gpu/GrRecordingContext.h"
11#include "src/gpu/GrOpFlushState.h"
12#include "src/gpu/GrOpsRenderPass.h"
13#include "src/gpu/GrProgramInfo.h"
14#include "src/gpu/GrRecordingContextPriv.h"
15#include "src/gpu/ccpr/GrCCPerFlushResources.h"
16#include "src/gpu/ccpr/GrSampleMaskProcessor.h"
17#include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h"
18#include "src/gpu/glsl/GrGLSLVertexGeoBuilder.h"
19
20namespace {
21
22class StencilResolveProcessor : public GrGeometryProcessor {
23public:
24 StencilResolveProcessor() : INHERITED(kStencilResolveProcessor_ClassID) {
25 static constexpr Attribute kIBounds = {
26 "ibounds", kShort4_GrVertexAttribType, kShort4_GrSLType};
27 this->setInstanceAttributes(&kIBounds, 1);
28 SkASSERT(this->instanceStride() == sizeof(GrStencilAtlasOp::ResolveRectInstance));
29 }
30
31private:
32 const char* name() const final { return "StencilResolveProcessor"; }
33 void getGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const final {}
34 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const final;
35 class Impl;
36
37 typedef GrGeometryProcessor INHERITED;
38};
39
40// This processor draws pixel-aligned rectangles directly on top of every path in the atlas.
41// The caller should have set up the instance data such that "Nonzero" paths get clockwise
42// rectangles (l < r) and "even/odd" paths get counter-clockwise (r < l). Its purpose
43// is to convert winding counts in the stencil buffer to A8 coverage in the color buffer.
44class StencilResolveProcessor::Impl : public GrGLSLGeometryProcessor {
45 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
46 args.fVaryingHandler->emitAttributes(args.fGP.cast<StencilResolveProcessor>());
47
48 GrGLSLVertexBuilder* v = args.fVertBuilder;
49 v->codeAppendf("short2 devcoord;");
50 v->codeAppendf("devcoord.x = (0 == (sk_VertexID & 1)) ? ibounds.x : ibounds.z;");
51 v->codeAppendf("devcoord.y = (sk_VertexID < 2) ? ibounds.y : ibounds.w;");
52
53 v->codeAppendf("float2 atlascoord = float2(devcoord);");
54 gpArgs->fPositionVar.set(kFloat2_GrSLType, "atlascoord");
55
56 // Just output "1" for coverage. This will be modulated by the MSAA stencil test.
57 GrGLSLFPFragmentBuilder* f = args.fFragBuilder;
58 f->codeAppendf("%s = %s = half4(1);", args.fOutputColor, args.fOutputCoverage);
59 }
60
61 void setData(const GrGLSLProgramDataManager&, const GrPrimitiveProcessor&) override {}
62};
63
64GrGLSLPrimitiveProcessor* StencilResolveProcessor::createGLSLInstance(const GrShaderCaps&) const {
65 return new Impl();
66}
67
68} // namespace
69
70std::unique_ptr<GrDrawOp> GrStencilAtlasOp::Make(
71 GrRecordingContext* context, sk_sp<const GrCCPerFlushResources> resources,
72 FillBatchID fillBatchID, StrokeBatchID strokeBatchID, int baseStencilResolveInstance,
73 int endStencilResolveInstance, const SkISize& drawBounds) {
74 GrOpMemoryPool* pool = context->priv().opMemoryPool();
75
76 return pool->allocate<GrStencilAtlasOp>(
77 std::move(resources), fillBatchID, strokeBatchID, baseStencilResolveInstance,
78 endStencilResolveInstance, drawBounds);
79}
80
81// Increments clockwise triangles and decrements counterclockwise. We use the same incr/decr
82// settings regardless of fill rule; fill rule is accounted for during the resolve step.
83static constexpr GrUserStencilSettings kIncrDecrStencil(
84 GrUserStencilSettings::StaticInitSeparate<
85 0x0000, 0x0000,
86 GrUserStencilTest::kNever, GrUserStencilTest::kNever,
87 0xffff, 0xffff,
88 GrUserStencilOp::kIncWrap, GrUserStencilOp::kDecWrap,
89 GrUserStencilOp::kIncWrap, GrUserStencilOp::kDecWrap,
90 0xffff, 0xffff>()
91);
92
93// Resolves stencil winding counts to A8 coverage. Leaves stencil values untouched.
94// NOTE: For the CCW face we intentionally use "1 == (stencil & 1)" because the contrapositive logic
95// (i.e. 0 != ...) causes bugs on Adreno Vulkan. http://skbug.com/9643
96static constexpr GrUserStencilSettings kResolveStencilCoverage(
97 GrUserStencilSettings::StaticInitSeparate<
98 0x0000, 0x0001,
99 GrUserStencilTest::kNotEqual, GrUserStencilTest::kEqual,
100 0xffff, 0x0001,
101 GrUserStencilOp::kKeep, GrUserStencilOp::kKeep,
102 GrUserStencilOp::kKeep, GrUserStencilOp::kKeep,
103 0xffff, 0xffff>()
104);
105
106// Same as above, but also resets stencil values to zero. This is better for non-tilers
107// where we prefer to not clear the stencil buffer at the beginning of every render pass.
108static constexpr GrUserStencilSettings kResolveStencilCoverageAndReset(
109 GrUserStencilSettings::StaticInitSeparate<
110 0x0000, 0x0000,
111 GrUserStencilTest::kNotEqual, GrUserStencilTest::kNotEqual,
112 0xffff, 0x0001,
113 GrUserStencilOp::kZero, GrUserStencilOp::kZero,
114 GrUserStencilOp::kKeep, GrUserStencilOp::kZero,
115 0xffff, 0xffff>()
116);
117
118// Same as above, but done in two passes for D3D, which doesn't support mismatched refs or masks on
119// dual sided stencil settings.
120static constexpr GrUserStencilSettings kResolveWindingCoverageAndReset(
121 GrUserStencilSettings::StaticInitSeparate<
122 0x0000, 0x0000,
123 GrUserStencilTest::kNotEqual, GrUserStencilTest::kNever,
124 0xffff, 0xffff,
125 GrUserStencilOp::kZero, GrUserStencilOp::kKeep,
126 GrUserStencilOp::kKeep, GrUserStencilOp::kKeep,
127 0xffff, 0xffff>()
128);
129static constexpr GrUserStencilSettings kResolveEvenOddCoverageAndReset(
130 GrUserStencilSettings::StaticInitSeparate<
131 0x0000, 0x0000,
132 GrUserStencilTest::kNever, GrUserStencilTest::kNotEqual,
133 0x0001, 0x0001,
134 GrUserStencilOp::kKeep, GrUserStencilOp::kZero,
135 GrUserStencilOp::kKeep, GrUserStencilOp::kZero,
136 0xffff, 0xffff>()
137);
138
139
140void GrStencilAtlasOp::onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) {
141 SkIRect drawBoundsRect = SkIRect::MakeWH(fDrawBounds.width(), fDrawBounds.height());
142
143 GrPipeline pipeline(GrScissorTest::kEnabled, GrDisableColorXPFactory::MakeXferProcessor(),
144 flushState->drawOpArgs().writeSwizzle(),
145 GrPipeline::InputFlags::kHWAntialias, &kIncrDecrStencil);
146
147 GrSampleMaskProcessor sampleMaskProc;
148
149 fResources->filler().drawFills(
150 flushState, &sampleMaskProc, pipeline, fFillBatchID, drawBoundsRect);
151
152 fResources->stroker().drawStrokes(
153 flushState, &sampleMaskProc, fStrokeBatchID, drawBoundsRect);
154
155 // We resolve the stencil coverage to alpha by drawing pixel-aligned boxes. Fine raster is
156 // not necessary, and will even cause artifacts if using mixed samples.
157 constexpr auto noHWAA = GrPipeline::InputFlags::kNone;
158
159 GrPipeline resolvePipeline(GrScissorTest::kEnabled, SkBlendMode::kSrc,
160 flushState->drawOpArgs().writeSwizzle(), noHWAA);
161 StencilResolveProcessor primProc;
162
163 if (!flushState->caps().twoSidedStencilRefsAndMasksMustMatch()) {
164 if (flushState->caps().discardStencilValuesAfterRenderPass()) {
165 resolvePipeline.setUserStencil(&kResolveStencilCoverage);
166 } else {
167 resolvePipeline.setUserStencil(&kResolveStencilCoverageAndReset);
168 }
169 this->drawResolve(flushState, resolvePipeline, primProc, drawBoundsRect);
170 return;
171 }
172
173 // If this ever becomes true then we should add new per-fill-type stencil settings that also
174 // don't reset back to zero.
175 SkASSERT(!flushState->caps().discardStencilValuesAfterRenderPass());
176
177 resolvePipeline.setUserStencil(&kResolveWindingCoverageAndReset);
178 this->drawResolve(flushState, resolvePipeline, primProc, drawBoundsRect);
179
180 resolvePipeline.setUserStencil(&kResolveEvenOddCoverageAndReset);
181 this->drawResolve(flushState, resolvePipeline, primProc, drawBoundsRect);
182}
183
184void GrStencilAtlasOp::drawResolve(GrOpFlushState* flushState, const GrPipeline& resolvePipeline,
185 const GrPrimitiveProcessor& primProc,
186 const SkIRect& drawBounds) const {
187 GrProgramInfo programInfo(flushState->proxy()->numSamples(),
188 flushState->proxy()->numStencilSamples(),
189 flushState->proxy()->backendFormat(),
190 flushState->writeView()->origin(), &resolvePipeline, &primProc,
191 GrPrimitiveType::kTriangleStrip);
192 flushState->bindPipeline(programInfo, SkRect::Make(drawBounds));
193 flushState->setScissorRect(drawBounds);
194 flushState->bindBuffers(nullptr, fResources->stencilResolveBuffer(), nullptr);
195 flushState->drawInstanced(fEndStencilResolveInstance - fBaseStencilResolveInstance,
196 fBaseStencilResolveInstance, 4, 0);
197}
198