blob: 9671213853eb6f3d8f07d0e2724ce7297e4b6915 [file] [log] [blame]
Chris Dalton70a0d2c2021-01-26 12:01:21 -07001/*
2 * Copyright 2021 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 Dalton031d76b2021-06-08 16:32:00 -06008#include "src/gpu/tessellate/GrPathStencilCoverOp.h"
Chris Dalton70a0d2c2021-01-26 12:01:21 -07009
Chris Dalton09bc8af2021-07-19 11:02:39 -060010#include "src/gpu/GrDefaultGeoProcFactory.h"
Chris Dalton70a0d2c2021-01-26 12:01:21 -070011#include "src/gpu/GrEagerVertexAllocator.h"
Chris Daltond9bdc322021-06-01 19:22:05 -060012#include "src/gpu/GrGpu.h"
Chris Dalton70a0d2c2021-01-26 12:01:21 -070013#include "src/gpu/GrOpFlushState.h"
14#include "src/gpu/GrRecordingContextPriv.h"
Robert Phillips1a82a4e2021-07-01 10:27:44 -040015#include "src/gpu/GrResourceProvider.h"
Chris Dalton8aec1242021-07-19 11:10:45 -060016#include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h"
17#include "src/gpu/glsl/GrGLSLVarying.h"
Chris Dalton2f733ec2021-06-01 12:11:57 -060018#include "src/gpu/glsl/GrGLSLVertexGeoBuilder.h"
Chris Dalton70a0d2c2021-01-26 12:01:21 -070019#include "src/gpu/tessellate/GrMiddleOutPolygonTriangulator.h"
Chris Daltond9bdc322021-06-01 19:22:05 -060020#include "src/gpu/tessellate/GrPathCurveTessellator.h"
Chris Daltond9bdc322021-06-01 19:22:05 -060021#include "src/gpu/tessellate/GrPathWedgeTessellator.h"
Chris Dalton70a0d2c2021-01-26 12:01:21 -070022#include "src/gpu/tessellate/GrTessellationPathRenderer.h"
Chris Dalton3b412782021-06-01 13:40:03 -060023#include "src/gpu/tessellate/shaders/GrPathTessellationShader.h"
Chris Dalton70a0d2c2021-01-26 12:01:21 -070024
Chris Dalton917f9192021-06-08 14:32:37 -060025using PathFlags = GrTessellationPathRenderer::PathFlags;
Chris Dalton70a0d2c2021-01-26 12:01:21 -070026
Chris Dalton2f733ec2021-06-01 12:11:57 -060027namespace {
28
29// Fills a path's bounding box, with subpixel outset to avoid possible T-junctions with extreme
30// edges of the path.
31// NOTE: The emitted geometry may not be axis-aligned, depending on the view matrix.
Chris Dalton8aec1242021-07-19 11:10:45 -060032class BoundingBoxShader : public GrGeometryProcessor {
Chris Dalton2f733ec2021-06-01 12:11:57 -060033public:
Chris Daltona05ccc32021-06-29 19:42:13 -060034 BoundingBoxShader(const SkMatrix& viewMatrix, SkPMColor4f color, const GrShaderCaps& shaderCaps)
Chris Dalton8aec1242021-07-19 11:10:45 -060035 : GrGeometryProcessor(kTessellate_BoundingBoxShader_ClassID)
36 , fViewMatrix(viewMatrix)
37 , fColor(color) {
38 // The 1/4px outset logic does not work with perspective yet.
39 SkASSERT(!fViewMatrix.hasPerspective());
Chris Daltona05ccc32021-06-29 19:42:13 -060040 if (!shaderCaps.vertexIDSupport()) {
41 constexpr static Attribute kUnitCoordAttrib("unitCoord", kFloat2_GrVertexAttribType,
42 kFloat2_GrSLType);
43 this->setVertexAttributes(&kUnitCoordAttrib, 1);
44 }
Chris Dalton8aec1242021-07-19 11:10:45 -060045 constexpr static Attribute kPathBoundsAttrib("pathBounds", kFloat4_GrVertexAttribType,
46 kFloat4_GrSLType);
47 this->setInstanceAttributes(&kPathBoundsAttrib, 1);
Chris Dalton2f733ec2021-06-01 12:11:57 -060048 }
49
50private:
Chris Daltonb63711a2021-06-01 14:52:02 -060051 const char* name() const final { return "tessellate_BoundingBoxShader"; }
Chris Dalton2f733ec2021-06-01 12:11:57 -060052 void getGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const final {}
53 GrGLSLGeometryProcessor* createGLSLInstance(const GrShaderCaps&) const final;
Chris Dalton8aec1242021-07-19 11:10:45 -060054
55 const SkMatrix fViewMatrix;
56 const SkPMColor4f fColor;
Chris Dalton2f733ec2021-06-01 12:11:57 -060057};
58
59GrGLSLGeometryProcessor* BoundingBoxShader::createGLSLInstance(const GrShaderCaps&) const {
Chris Dalton8aec1242021-07-19 11:10:45 -060060 class Impl : public GrGLSLGeometryProcessor {
61 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) final {
62 args.fVaryingHandler->emitAttributes(args.fGeomProc);
63
64 // Vertex shader.
65 const char* viewMatrix;
66 fViewMatrixUniform = args.fUniformHandler->addUniform(nullptr, kVertex_GrShaderFlag,
67 kFloat3x3_GrSLType, "viewMatrix",
68 &viewMatrix);
69 if (args.fShaderCaps->vertexIDSupport()) {
Chris Daltona05ccc32021-06-29 19:42:13 -060070 // If we don't have sk_VertexID support then "unitCoord" already came in as a vertex
71 // attrib.
Chris Dalton8aec1242021-07-19 11:10:45 -060072 args.fVertBuilder->codeAppendf(R"(
Chris Daltona05ccc32021-06-29 19:42:13 -060073 float2 unitCoord = float2(sk_VertexID & 1, sk_VertexID >> 1);)");
74 }
Chris Dalton8aec1242021-07-19 11:10:45 -060075 args.fVertBuilder->codeAppendf(R"(
76 float3x3 VIEW_MATRIX = %s;
Chris Daltona05ccc32021-06-29 19:42:13 -060077
Chris Dalton8aec1242021-07-19 11:10:45 -060078 // Bloat the bounding box by 1/4px to be certain we will reset every stencil value.
79 float2x2 M_ = inverse(float2x2(VIEW_MATRIX));
Chris Dalton2f733ec2021-06-01 12:11:57 -060080 float2 bloat = float2(abs(M_[0]) + abs(M_[1])) * .25;
81
82 // Find the vertex position.
Chris Daltona05ccc32021-06-29 19:42:13 -060083 float2 localcoord = mix(pathBounds.xy - bloat, pathBounds.zw + bloat, unitCoord);
Chris Dalton8aec1242021-07-19 11:10:45 -060084 float2 vertexpos = (VIEW_MATRIX * float3(localcoord, 1)).xy;)", viewMatrix);
Chris Dalton2f733ec2021-06-01 12:11:57 -060085 gpArgs->fLocalCoordVar.set(kFloat2_GrSLType, "localcoord");
86 gpArgs->fPositionVar.set(kFloat2_GrSLType, "vertexpos");
Chris Dalton8aec1242021-07-19 11:10:45 -060087
88 // Fragment shader.
89 const char* color;
90 fColorUniform = args.fUniformHandler->addUniform(nullptr, kFragment_GrShaderFlag,
91 kHalf4_GrSLType, "color", &color);
92 args.fFragBuilder->codeAppendf("half4 %s = %s;", args.fOutputColor, color);
93 args.fFragBuilder->codeAppendf("const half4 %s = half4(1);", args.fOutputCoverage);
Chris Dalton2f733ec2021-06-01 12:11:57 -060094 }
Chris Dalton8aec1242021-07-19 11:10:45 -060095
96 void setData(const GrGLSLProgramDataManager& pdman, const GrShaderCaps&,
97 const GrGeometryProcessor& gp) override {
98 const auto& bboxShader = gp.cast<BoundingBoxShader>();
99 pdman.setSkMatrix(fViewMatrixUniform, bboxShader.fViewMatrix);
100 const SkPMColor4f& color = bboxShader.fColor;
101 pdman.set4f(fColorUniform, color.fR, color.fG, color.fB, color.fA);
102 }
103
104 GrGLSLUniformHandler::UniformHandle fViewMatrixUniform;
105 GrGLSLUniformHandler::UniformHandle fColorUniform;
Chris Dalton2f733ec2021-06-01 12:11:57 -0600106 };
Chris Dalton8aec1242021-07-19 11:10:45 -0600107
Chris Dalton2f733ec2021-06-01 12:11:57 -0600108 return new Impl;
109}
110
111} // namespace
112
Robert Phillips294723d2021-06-17 09:23:58 -0400113void GrPathStencilCoverOp::visitProxies(const GrVisitProxyFunc& func) const {
Chris Dalton031d76b2021-06-08 16:32:00 -0600114 if (fCoverBBoxProgram) {
Robert Phillips294723d2021-06-17 09:23:58 -0400115 fCoverBBoxProgram->pipeline().visitProxies(func);
Chris Dalton70a0d2c2021-01-26 12:01:21 -0700116 } else {
Robert Phillips294723d2021-06-17 09:23:58 -0400117 fProcessors.visitProxies(func);
Chris Dalton70a0d2c2021-01-26 12:01:21 -0700118 }
119}
120
Chris Dalton031d76b2021-06-08 16:32:00 -0600121GrDrawOp::FixedFunctionFlags GrPathStencilCoverOp::fixedFunctionFlags() const {
Chris Dalton70a0d2c2021-01-26 12:01:21 -0700122 auto flags = FixedFunctionFlags::kUsesStencil;
123 if (fAAType != GrAAType::kNone) {
124 flags |= FixedFunctionFlags::kUsesHWAA;
125 }
126 return flags;
127}
128
Chris Dalton031d76b2021-06-08 16:32:00 -0600129GrProcessorSet::Analysis GrPathStencilCoverOp::finalize(const GrCaps& caps,
130 const GrAppliedClip* clip,
131 GrClampType clampType) {
Chris Dalton57ab06c2021-04-22 12:57:28 -0600132 return fProcessors.finalize(fColor, GrProcessorAnalysisCoverage::kNone, clip, nullptr, caps,
133 clampType, &fColor);
Chris Dalton70a0d2c2021-01-26 12:01:21 -0700134}
135
Chris Dalton031d76b2021-06-08 16:32:00 -0600136void GrPathStencilCoverOp::prePreparePrograms(const GrTessellationShader::ProgramArgs& args,
137 GrAppliedClip&& appliedClip) {
Chris Dalton569c01b2021-05-25 10:11:46 -0600138 SkASSERT(!fTessellator);
Chris Dalton70a0d2c2021-01-26 12:01:21 -0700139 SkASSERT(!fStencilFanProgram);
140 SkASSERT(!fStencilPathProgram);
Chris Dalton031d76b2021-06-08 16:32:00 -0600141 SkASSERT(!fCoverBBoxProgram);
Chris Dalton70a0d2c2021-01-26 12:01:21 -0700142
Chris Dalton2f733ec2021-06-01 12:11:57 -0600143 const GrPipeline* stencilPipeline = GrPathTessellationShader::MakeStencilOnlyPipeline(
Chris Dalton917f9192021-06-08 14:32:37 -0600144 args, fAAType, fPathFlags, appliedClip.hardClip());
Chris Dalton2f733ec2021-06-01 12:11:57 -0600145 const GrUserStencilSettings* stencilPathSettings =
Chris Daltonbaae2dd2021-06-25 14:52:49 -0600146 GrPathTessellationShader::StencilPathSettings(GrFillRuleForSkPath(fPath));
Chris Dalton569c01b2021-05-25 10:11:46 -0600147
Chris Dalton917f9192021-06-08 14:32:37 -0600148 if (fPath.countVerbs() > 50 && this->bounds().height() * this->bounds().width() > 256 * 256) {
Chris Daltond2b8ba32021-06-09 00:12:59 -0600149 // Large complex paths do better with a dedicated triangle shader for the inner fan.
150 // This takes less PCI bus bandwidth (6 floats per triangle instead of 8) and allows us
151 // to make sure it has an efficient middle-out topology.
Chris Dalton09bc8af2021-07-19 11:02:39 -0600152 auto triangleGP = GrDefaultGeoProcFactory::Make(
153 args.fArena,
154 GrDefaultGeoProcFactory::Color(SK_PMColor4fTRANSPARENT),
155 GrDefaultGeoProcFactory::Coverage::kSolid_Type,
156 GrDefaultGeoProcFactory::LocalCoords::kUnused_Type,
157 fViewMatrix);
158 fStencilFanProgram = GrSimpleMeshDrawOpHelper::CreateProgramInfo(
159 args.fArena,
160 stencilPipeline,
161 args.fWriteView,
162 triangleGP,
163 GrPrimitiveType::kTriangles,
164 args.fXferBarrierFlags,
165 args.fColorLoadOp,
166 stencilPathSettings);
Chris Daltond2b8ba32021-06-09 00:12:59 -0600167 fTessellator = GrPathCurveTessellator::Make(args.fArena, fViewMatrix,
168 SK_PMColor4fTRANSPARENT,
169 GrPathCurveTessellator::DrawInnerFan::kNo,
Chris Dalton198ac152021-06-09 13:49:43 -0600170 fPath.countVerbs(), *stencilPipeline,
171 *args.fCaps);
Chris Dalton917f9192021-06-08 14:32:37 -0600172 } else {
Chris Dalton2f733ec2021-06-01 12:11:57 -0600173 fTessellator = GrPathWedgeTessellator::Make(args.fArena, fViewMatrix,
Chris Daltond2b8ba32021-06-09 00:12:59 -0600174 SK_PMColor4fTRANSPARENT, fPath.countVerbs(),
Chris Dalton198ac152021-06-09 13:49:43 -0600175 *stencilPipeline, *args.fCaps);
Chris Dalton70a0d2c2021-01-26 12:01:21 -0700176 }
Chris Dalton2f733ec2021-06-01 12:11:57 -0600177 fStencilPathProgram = GrTessellationShader::MakeProgram(args, fTessellator->shader(),
178 stencilPipeline, stencilPathSettings);
Chris Dalton569c01b2021-05-25 10:11:46 -0600179
Chris Dalton917f9192021-06-08 14:32:37 -0600180 if (!(fPathFlags & PathFlags::kStencilOnly)) {
Chris Dalton70a0d2c2021-01-26 12:01:21 -0700181 // Create a program that draws a bounding box over the path and fills its stencil coverage
182 // into the color buffer.
Chris Daltona05ccc32021-06-29 19:42:13 -0600183 auto* bboxShader = args.fArena->make<BoundingBoxShader>(fViewMatrix, fColor,
184 *args.fCaps->shaderCaps());
Chris Dalton2f733ec2021-06-01 12:11:57 -0600185 auto* bboxPipeline = GrTessellationShader::MakePipeline(args, fAAType,
186 std::move(appliedClip),
187 std::move(fProcessors));
Chris Daltonbaae2dd2021-06-25 14:52:49 -0600188 auto* bboxStencil =
189 GrPathTessellationShader::TestAndResetStencilSettings(fPath.isInverseFillType());
Chris Dalton8aec1242021-07-19 11:10:45 -0600190 fCoverBBoxProgram = GrSimpleMeshDrawOpHelper::CreateProgramInfo(
191 args.fArena,
192 bboxPipeline,
193 args.fWriteView,
194 bboxShader,
195 GrPrimitiveType::kTriangleStrip,
196 args.fXferBarrierFlags,
197 args.fColorLoadOp,
198 bboxStencil);
Chris Dalton70a0d2c2021-01-26 12:01:21 -0700199 }
200}
201
Chris Dalton031d76b2021-06-08 16:32:00 -0600202void GrPathStencilCoverOp::onPrePrepare(GrRecordingContext* context,
203 const GrSurfaceProxyView& writeView, GrAppliedClip* clip,
204 const GrDstProxyView& dstProxyView,
205 GrXferBarrierFlags renderPassXferBarriers,
206 GrLoadOp colorLoadOp) {
Chris Dalton70a0d2c2021-01-26 12:01:21 -0700207 this->prePreparePrograms({context->priv().recordTimeAllocator(), writeView, &dstProxyView,
208 renderPassXferBarriers, colorLoadOp, context->priv().caps()},
209 (clip) ? std::move(*clip) : GrAppliedClip::Disabled());
210 if (fStencilFanProgram) {
211 context->priv().recordProgramInfo(fStencilFanProgram);
212 }
213 if (fStencilPathProgram) {
214 context->priv().recordProgramInfo(fStencilPathProgram);
215 }
Chris Dalton031d76b2021-06-08 16:32:00 -0600216 if (fCoverBBoxProgram) {
217 context->priv().recordProgramInfo(fCoverBBoxProgram);
Chris Dalton70a0d2c2021-01-26 12:01:21 -0700218 }
219}
220
Chris Daltona05ccc32021-06-29 19:42:13 -0600221GR_DECLARE_STATIC_UNIQUE_KEY(gUnitQuadBufferKey);
222
Chris Dalton031d76b2021-06-08 16:32:00 -0600223void GrPathStencilCoverOp::onPrepare(GrOpFlushState* flushState) {
Chris Dalton70a0d2c2021-01-26 12:01:21 -0700224 if (!fTessellator) {
225 this->prePreparePrograms({flushState->allocator(), flushState->writeView(),
226 &flushState->dstProxyView(), flushState->renderPassBarriers(),
227 flushState->colorLoadOp(), &flushState->caps()},
228 flushState->detachAppliedClip());
229 if (!fTessellator) {
230 return;
231 }
232 }
233
234 if (fStencilFanProgram) {
235 // The inner fan isn't built into the tessellator. Generate a standard Redbook fan with a
236 // middle-out topology.
237 GrEagerDynamicVertexAllocator vertexAlloc(flushState, &fFanBuffer, &fFanBaseVertex);
238 int maxFanTriangles = fPath.countVerbs() - 2; // n - 2 triangles make an n-gon.
Chris Dalton8731a712021-05-14 14:48:54 -0600239 GrVertexWriter triangleVertexWriter = vertexAlloc.lock<SkPoint>(maxFanTriangles * 3);
Chris Dalton40c906f2021-07-26 11:27:05 -0600240 int numTrianglesWritten;
241 GrMiddleOutPolygonTriangulator::WritePathInnerFan(std::move(triangleVertexWriter), 0, 0,
242 fPath, &numTrianglesWritten);
243 fFanVertexCount = 3 * numTrianglesWritten;
Chris Dalton70a0d2c2021-01-26 12:01:21 -0700244 SkASSERT(fFanVertexCount <= maxFanTriangles * 3);
245 vertexAlloc.unlock(fFanVertexCount);
246 }
247
Chris Dalton569c01b2021-05-25 10:11:46 -0600248 fTessellator->prepare(flushState, this->bounds(), fPath);
Chris Daltonc91dd692021-05-24 15:04:47 -0600249
Chris Dalton031d76b2021-06-08 16:32:00 -0600250 if (fCoverBBoxProgram) {
Chris Daltonc91dd692021-05-24 15:04:47 -0600251 GrVertexWriter vertexWriter = flushState->makeVertexSpace(sizeof(SkRect), 1, &fBBoxBuffer,
252 &fBBoxBaseInstance);
Chris Daltonbaae2dd2021-06-25 14:52:49 -0600253 if (fPath.isInverseFillType()) {
254 // Fill the entire backing store to make sure we clear every stencil value back to 0. If
255 // there is a scissor it will have already clipped the stencil draw.
256 auto rtBounds = flushState->writeView().asRenderTargetProxy()->backingStoreBoundsRect();
257 SkASSERT(rtBounds == fOriginalDrawBounds);
258 SkRect pathSpaceRTBounds;
259 if (SkMatrixPriv::InverseMapRect(fViewMatrix, &pathSpaceRTBounds, rtBounds)) {
260 vertexWriter.write(pathSpaceRTBounds);
261 } else {
262 vertexWriter.write(fPath.getBounds());
263 }
264 } else {
265 vertexWriter.write(fPath.getBounds());
266 }
Chris Daltonc91dd692021-05-24 15:04:47 -0600267 }
Chris Daltona05ccc32021-06-29 19:42:13 -0600268
269 if (!flushState->caps().shaderCaps()->vertexIDSupport()) {
270 constexpr static SkPoint kUnitQuad[4] = {{0,0}, {0,1}, {1,0}, {1,1}};
271
272 GR_DEFINE_STATIC_UNIQUE_KEY(gUnitQuadBufferKey);
273
274 fBBoxVertexBufferIfNoIDSupport = flushState->resourceProvider()->findOrMakeStaticBuffer(
275 GrGpuBufferType::kVertex, sizeof(kUnitQuad), kUnitQuad, gUnitQuadBufferKey);
276 }
Chris Dalton70a0d2c2021-01-26 12:01:21 -0700277}
278
Chris Dalton031d76b2021-06-08 16:32:00 -0600279void GrPathStencilCoverOp::onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) {
Chris Dalton70a0d2c2021-01-26 12:01:21 -0700280 if (!fTessellator) {
281 return;
282 }
283
284 // Stencil the inner fan, if any.
285 if (fFanVertexCount > 0) {
286 SkASSERT(fStencilFanProgram);
287 SkASSERT(fFanBuffer);
288 flushState->bindPipelineAndScissorClip(*fStencilFanProgram, this->bounds());
289 flushState->bindBuffers(nullptr, nullptr, fFanBuffer);
290 flushState->draw(fFanVertexCount, fFanBaseVertex);
291 }
292
293 // Stencil the rest of the path.
294 SkASSERT(fStencilPathProgram);
295 flushState->bindPipelineAndScissorClip(*fStencilPathProgram, this->bounds());
296 fTessellator->draw(flushState);
Chris Daltond9bdc322021-06-01 19:22:05 -0600297 if (flushState->caps().requiresManualFBBarrierAfterTessellatedStencilDraw()) {
298 flushState->gpu()->insertManualFramebufferBarrier(); // http://skbug.com/9739
299 }
Chris Dalton70a0d2c2021-01-26 12:01:21 -0700300
301 // Fill in the bounding box (if not in stencil-only mode).
Chris Dalton031d76b2021-06-08 16:32:00 -0600302 if (fCoverBBoxProgram) {
303 flushState->bindPipelineAndScissorClip(*fCoverBBoxProgram, this->bounds());
304 flushState->bindTextures(fCoverBBoxProgram->geomProc(), nullptr,
305 fCoverBBoxProgram->pipeline());
Chris Daltona05ccc32021-06-29 19:42:13 -0600306 flushState->bindBuffers(nullptr, fBBoxBuffer, fBBoxVertexBufferIfNoIDSupport);
Chris Daltonc91dd692021-05-24 15:04:47 -0600307 flushState->drawInstanced(1, fBBoxBaseInstance, 4, 0);
Chris Dalton70a0d2c2021-01-26 12:01:21 -0700308 }
309}