blob: a92e1db04105b523f9ba20d0467ba2a83aa0dc45 [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 Dalton2ed22fa2021-05-06 16:08:30 -06008#include "src/gpu/tessellate/GrPathStencilFillOp.h"
Chris Dalton70a0d2c2021-01-26 12:01:21 -07009
10#include "src/gpu/GrEagerVertexAllocator.h"
11#include "src/gpu/GrOpFlushState.h"
12#include "src/gpu/GrRecordingContextPriv.h"
Chris Dalton2f733ec2021-06-01 12:11:57 -060013#include "src/gpu/glsl/GrGLSLVertexGeoBuilder.h"
Chris Dalton70a0d2c2021-01-26 12:01:21 -070014#include "src/gpu/tessellate/GrMiddleOutPolygonTriangulator.h"
15#include "src/gpu/tessellate/GrPathTessellator.h"
Chris Dalton70a0d2c2021-01-26 12:01:21 -070016#include "src/gpu/tessellate/GrTessellationPathRenderer.h"
Chris Dalton3b412782021-06-01 13:40:03 -060017#include "src/gpu/tessellate/shaders/GrPathTessellationShader.h"
Chris Dalton70a0d2c2021-01-26 12:01:21 -070018
19using OpFlags = GrTessellationPathRenderer::OpFlags;
20
Chris Dalton2f733ec2021-06-01 12:11:57 -060021namespace {
22
23// Fills a path's bounding box, with subpixel outset to avoid possible T-junctions with extreme
24// edges of the path.
25// NOTE: The emitted geometry may not be axis-aligned, depending on the view matrix.
26class BoundingBoxShader : public GrPathTessellationShader {
27public:
28 BoundingBoxShader(const SkMatrix& viewMatrix, SkPMColor4f color)
29 : GrPathTessellationShader(kTessellate_BoundingBoxShader_ClassID,
30 GrPrimitiveType::kTriangleStrip, 0, viewMatrix, color) {
31 constexpr static Attribute kPathBoundsAttrib = {"pathBounds", kFloat4_GrVertexAttribType,
32 kFloat4_GrSLType};
33 this->setInstanceAttributes(&kPathBoundsAttrib, 1);
34 }
35
36private:
Chris Daltonb63711a2021-06-01 14:52:02 -060037 const char* name() const final { return "tessellate_BoundingBoxShader"; }
Chris Dalton2f733ec2021-06-01 12:11:57 -060038 void getGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const final {}
39 GrGLSLGeometryProcessor* createGLSLInstance(const GrShaderCaps&) const final;
40};
41
42GrGLSLGeometryProcessor* BoundingBoxShader::createGLSLInstance(const GrShaderCaps&) const {
43 class Impl : public GrPathTessellationShader::Impl {
44 void emitVertexCode(GrGLSLVertexBuilder* v, GrGPArgs* gpArgs) override {
45 v->codeAppend(R"(
46 // Bloat the bounding box by 1/4px to avoid potential T-junctions at the edges.
47 float2x2 M_ = inverse(AFFINE_MATRIX);
48 float2 bloat = float2(abs(M_[0]) + abs(M_[1])) * .25;
49
50 // Find the vertex position.
51 float2 T = float2(sk_VertexID & 1, sk_VertexID >> 1);
52 float2 localcoord = mix(pathBounds.xy - bloat, pathBounds.zw + bloat, T);
53 float2 vertexpos = AFFINE_MATRIX * localcoord + TRANSLATE;)");
54 gpArgs->fLocalCoordVar.set(kFloat2_GrSLType, "localcoord");
55 gpArgs->fPositionVar.set(kFloat2_GrSLType, "vertexpos");
56 }
57 };
58 return new Impl;
59}
60
61} // namespace
62
Chris Dalton2ed22fa2021-05-06 16:08:30 -060063void GrPathStencilFillOp::visitProxies(const VisitProxyFunc& fn) const {
Chris Dalton70a0d2c2021-01-26 12:01:21 -070064 if (fFillBBoxProgram) {
65 fFillBBoxProgram->pipeline().visitProxies(fn);
66 } else {
67 fProcessors.visitProxies(fn);
68 }
69}
70
Chris Dalton2ed22fa2021-05-06 16:08:30 -060071GrDrawOp::FixedFunctionFlags GrPathStencilFillOp::fixedFunctionFlags() const {
Chris Dalton70a0d2c2021-01-26 12:01:21 -070072 auto flags = FixedFunctionFlags::kUsesStencil;
73 if (fAAType != GrAAType::kNone) {
74 flags |= FixedFunctionFlags::kUsesHWAA;
75 }
76 return flags;
77}
78
Chris Dalton2ed22fa2021-05-06 16:08:30 -060079GrProcessorSet::Analysis GrPathStencilFillOp::finalize(const GrCaps& caps,
80 const GrAppliedClip* clip,
81 GrClampType clampType) {
Chris Dalton57ab06c2021-04-22 12:57:28 -060082 return fProcessors.finalize(fColor, GrProcessorAnalysisCoverage::kNone, clip, nullptr, caps,
83 clampType, &fColor);
Chris Dalton70a0d2c2021-01-26 12:01:21 -070084}
85
Chris Dalton2f733ec2021-06-01 12:11:57 -060086void GrPathStencilFillOp::prePreparePrograms(const GrTessellationShader::ProgramArgs& args,
Chris Dalton2ed22fa2021-05-06 16:08:30 -060087 GrAppliedClip&& appliedClip) {
Chris Dalton569c01b2021-05-25 10:11:46 -060088 SkASSERT(!fTessellator);
Chris Dalton70a0d2c2021-01-26 12:01:21 -070089 SkASSERT(!fStencilFanProgram);
90 SkASSERT(!fStencilPathProgram);
91 SkASSERT(!fFillBBoxProgram);
92
Chris Dalton569c01b2021-05-25 10:11:46 -060093 if (fPath.countVerbs() <= 0) {
Chris Dalton70a0d2c2021-01-26 12:01:21 -070094 return;
95 }
96
Chris Dalton2f733ec2021-06-01 12:11:57 -060097 const GrPipeline* stencilPipeline = GrPathTessellationShader::MakeStencilOnlyPipeline(
Chris Dalton70a0d2c2021-01-26 12:01:21 -070098 args, fAAType, fOpFlags, appliedClip.hardClip());
Chris Dalton2f733ec2021-06-01 12:11:57 -060099 const GrUserStencilSettings* stencilPathSettings =
100 GrPathTessellationShader::StencilPathSettings(fPath.getFillType());
Chris Dalton569c01b2021-05-25 10:11:46 -0600101
102 if ((fOpFlags & OpFlags::kPreferWedges) && args.fCaps->shaderCaps()->tessellationSupport()) {
103 // The path is an atlas with relatively small contours, or something else that does best
104 // with wedges.
Chris Dalton2f733ec2021-06-01 12:11:57 -0600105 fTessellator = GrPathWedgeTessellator::Make(args.fArena, fViewMatrix,
106 SK_PMColor4fTRANSPARENT);
Chris Dalton70a0d2c2021-01-26 12:01:21 -0700107 } else {
Chris Dalton569c01b2021-05-25 10:11:46 -0600108 auto drawFanWithTessellator = GrPathTessellator::DrawInnerFan::kYes;
109 if (fPath.countVerbs() > 50 && this->bounds().height() * this->bounds().width() > 256*256) {
110 // Large complex paths do better with a dedicated triangle shader for the inner fan.
111 // This takes less PCI bus bandwidth (6 floats per triangle instead of 8) and allows us
112 // to make sure it has an efficient middle-out topology.
Chris Daltonb63711a2021-06-01 14:52:02 -0600113 auto shader = GrPathTessellationShader::MakeSimpleTriangleShader(
114 args.fArena, fViewMatrix, SK_PMColor4fTRANSPARENT);
Chris Dalton2f733ec2021-06-01 12:11:57 -0600115 fStencilFanProgram = GrTessellationShader::MakeProgram(args, shader, stencilPipeline,
116 stencilPathSettings);
Chris Dalton569c01b2021-05-25 10:11:46 -0600117 drawFanWithTessellator = GrPathTessellator::DrawInnerFan::kNo;
Chris Dalton70a0d2c2021-01-26 12:01:21 -0700118 }
Chris Dalton2f733ec2021-06-01 12:11:57 -0600119 fTessellator = GrPathTessellator::Make(args.fArena, fPath, fViewMatrix,
120 SK_PMColor4fTRANSPARENT, drawFanWithTessellator,
121 *args.fCaps);
Chris Dalton70a0d2c2021-01-26 12:01:21 -0700122 }
123
Chris Dalton2f733ec2021-06-01 12:11:57 -0600124 fStencilPathProgram = GrTessellationShader::MakeProgram(args, fTessellator->shader(),
125 stencilPipeline, stencilPathSettings);
Chris Dalton569c01b2021-05-25 10:11:46 -0600126
Chris Dalton70a0d2c2021-01-26 12:01:21 -0700127 if (!(fOpFlags & OpFlags::kStencilOnly)) {
128 // Create a program that draws a bounding box over the path and fills its stencil coverage
129 // into the color buffer.
Chris Dalton2f733ec2021-06-01 12:11:57 -0600130 auto* bboxShader = args.fArena->make<BoundingBoxShader>(fViewMatrix, fColor);
131 auto* bboxPipeline = GrTessellationShader::MakePipeline(args, fAAType,
132 std::move(appliedClip),
133 std::move(fProcessors));
134 auto* bboxStencil = GrPathTessellationShader::TestAndResetStencilSettings();
135 fFillBBoxProgram = GrTessellationShader::MakeProgram(args, bboxShader, bboxPipeline,
136 bboxStencil);
Chris Dalton70a0d2c2021-01-26 12:01:21 -0700137 }
138}
139
Chris Dalton2ed22fa2021-05-06 16:08:30 -0600140void GrPathStencilFillOp::onPrePrepare(GrRecordingContext* context,
141 const GrSurfaceProxyView& writeView, GrAppliedClip* clip,
142 const GrXferProcessor::DstProxyView& dstProxyView,
143 GrXferBarrierFlags renderPassXferBarriers,
144 GrLoadOp colorLoadOp) {
Chris Dalton70a0d2c2021-01-26 12:01:21 -0700145 this->prePreparePrograms({context->priv().recordTimeAllocator(), writeView, &dstProxyView,
146 renderPassXferBarriers, colorLoadOp, context->priv().caps()},
147 (clip) ? std::move(*clip) : GrAppliedClip::Disabled());
148 if (fStencilFanProgram) {
149 context->priv().recordProgramInfo(fStencilFanProgram);
150 }
151 if (fStencilPathProgram) {
152 context->priv().recordProgramInfo(fStencilPathProgram);
153 }
154 if (fFillBBoxProgram) {
155 context->priv().recordProgramInfo(fFillBBoxProgram);
156 }
157}
158
Chris Dalton2ed22fa2021-05-06 16:08:30 -0600159void GrPathStencilFillOp::onPrepare(GrOpFlushState* flushState) {
Chris Dalton70a0d2c2021-01-26 12:01:21 -0700160 if (!fTessellator) {
161 this->prePreparePrograms({flushState->allocator(), flushState->writeView(),
162 &flushState->dstProxyView(), flushState->renderPassBarriers(),
163 flushState->colorLoadOp(), &flushState->caps()},
164 flushState->detachAppliedClip());
165 if (!fTessellator) {
166 return;
167 }
168 }
169
170 if (fStencilFanProgram) {
171 // The inner fan isn't built into the tessellator. Generate a standard Redbook fan with a
172 // middle-out topology.
173 GrEagerDynamicVertexAllocator vertexAlloc(flushState, &fFanBuffer, &fFanBaseVertex);
174 int maxFanTriangles = fPath.countVerbs() - 2; // n - 2 triangles make an n-gon.
Chris Dalton8731a712021-05-14 14:48:54 -0600175 GrVertexWriter triangleVertexWriter = vertexAlloc.lock<SkPoint>(maxFanTriangles * 3);
Chris Dalton70a0d2c2021-01-26 12:01:21 -0700176 fFanVertexCount = GrMiddleOutPolygonTriangulator::WritePathInnerFan(
Chris Daltondf2dbad2021-05-14 16:21:15 -0600177 &triangleVertexWriter, GrMiddleOutPolygonTriangulator::OutputType::kTriangles,
178 fPath) * 3;
Chris Dalton70a0d2c2021-01-26 12:01:21 -0700179 SkASSERT(fFanVertexCount <= maxFanTriangles * 3);
180 vertexAlloc.unlock(fFanVertexCount);
181 }
182
Chris Dalton569c01b2021-05-25 10:11:46 -0600183 fTessellator->prepare(flushState, this->bounds(), fPath);
Chris Daltonc91dd692021-05-24 15:04:47 -0600184
185 if (fFillBBoxProgram) {
186 GrVertexWriter vertexWriter = flushState->makeVertexSpace(sizeof(SkRect), 1, &fBBoxBuffer,
187 &fBBoxBaseInstance);
188 vertexWriter.write(fPath.getBounds());
189 }
Chris Dalton70a0d2c2021-01-26 12:01:21 -0700190}
191
Chris Dalton2ed22fa2021-05-06 16:08:30 -0600192void GrPathStencilFillOp::onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) {
Chris Dalton70a0d2c2021-01-26 12:01:21 -0700193 if (!fTessellator) {
194 return;
195 }
196
197 // Stencil the inner fan, if any.
198 if (fFanVertexCount > 0) {
199 SkASSERT(fStencilFanProgram);
200 SkASSERT(fFanBuffer);
201 flushState->bindPipelineAndScissorClip(*fStencilFanProgram, this->bounds());
202 flushState->bindBuffers(nullptr, nullptr, fFanBuffer);
203 flushState->draw(fFanVertexCount, fFanBaseVertex);
204 }
205
206 // Stencil the rest of the path.
207 SkASSERT(fStencilPathProgram);
208 flushState->bindPipelineAndScissorClip(*fStencilPathProgram, this->bounds());
209 fTessellator->draw(flushState);
210
211 // Fill in the bounding box (if not in stencil-only mode).
212 if (fFillBBoxProgram) {
213 flushState->bindPipelineAndScissorClip(*fFillBBoxProgram, this->bounds());
Robert Phillips787fd9d2021-03-22 14:48:09 -0400214 flushState->bindTextures(fFillBBoxProgram->geomProc(), nullptr,
Chris Dalton70a0d2c2021-01-26 12:01:21 -0700215 fFillBBoxProgram->pipeline());
Chris Daltonc91dd692021-05-24 15:04:47 -0600216 flushState->bindBuffers(nullptr, fBBoxBuffer, nullptr);
217 flushState->drawInstanced(1, fBBoxBaseInstance, 4, 0);
Chris Dalton70a0d2c2021-01-26 12:01:21 -0700218 }
219}