blob: 8c1cc3fbfde9af74ab62a19d97db7ba05e242ce9 [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"
13#include "src/gpu/tessellate/GrFillPathShader.h"
14#include "src/gpu/tessellate/GrMiddleOutPolygonTriangulator.h"
15#include "src/gpu/tessellate/GrPathTessellator.h"
16#include "src/gpu/tessellate/GrStencilPathShader.h"
17#include "src/gpu/tessellate/GrTessellationPathRenderer.h"
18
19using OpFlags = GrTessellationPathRenderer::OpFlags;
20
Chris Dalton2ed22fa2021-05-06 16:08:30 -060021void GrPathStencilFillOp::visitProxies(const VisitProxyFunc& fn) const {
Chris Dalton70a0d2c2021-01-26 12:01:21 -070022 if (fFillBBoxProgram) {
23 fFillBBoxProgram->pipeline().visitProxies(fn);
24 } else {
25 fProcessors.visitProxies(fn);
26 }
27}
28
Chris Dalton2ed22fa2021-05-06 16:08:30 -060029GrDrawOp::FixedFunctionFlags GrPathStencilFillOp::fixedFunctionFlags() const {
Chris Dalton70a0d2c2021-01-26 12:01:21 -070030 auto flags = FixedFunctionFlags::kUsesStencil;
31 if (fAAType != GrAAType::kNone) {
32 flags |= FixedFunctionFlags::kUsesHWAA;
33 }
34 return flags;
35}
36
Chris Dalton2ed22fa2021-05-06 16:08:30 -060037GrProcessorSet::Analysis GrPathStencilFillOp::finalize(const GrCaps& caps,
38 const GrAppliedClip* clip,
39 GrClampType clampType) {
Chris Dalton57ab06c2021-04-22 12:57:28 -060040 return fProcessors.finalize(fColor, GrProcessorAnalysisCoverage::kNone, clip, nullptr, caps,
41 clampType, &fColor);
Chris Dalton70a0d2c2021-01-26 12:01:21 -070042}
43
Chris Dalton2ed22fa2021-05-06 16:08:30 -060044void GrPathStencilFillOp::prePreparePrograms(const GrPathShader::ProgramArgs& args,
45 GrAppliedClip&& appliedClip) {
Chris Dalton569c01b2021-05-25 10:11:46 -060046 SkASSERT(!fTessellator);
Chris Dalton70a0d2c2021-01-26 12:01:21 -070047 SkASSERT(!fStencilFanProgram);
48 SkASSERT(!fStencilPathProgram);
49 SkASSERT(!fFillBBoxProgram);
50
Chris Dalton569c01b2021-05-25 10:11:46 -060051 if (fPath.countVerbs() <= 0) {
Chris Dalton70a0d2c2021-01-26 12:01:21 -070052 return;
53 }
54
Chris Dalton70a0d2c2021-01-26 12:01:21 -070055 const GrPipeline* stencilPassPipeline = GrStencilPathShader::MakeStencilPassPipeline(
56 args, fAAType, fOpFlags, appliedClip.hardClip());
Chris Dalton569c01b2021-05-25 10:11:46 -060057
58 if ((fOpFlags & OpFlags::kPreferWedges) && args.fCaps->shaderCaps()->tessellationSupport()) {
59 // The path is an atlas with relatively small contours, or something else that does best
60 // with wedges.
61 fTessellator = GrPathWedgeTessellator::Make(args.fArena, fViewMatrix);
Chris Dalton70a0d2c2021-01-26 12:01:21 -070062 } else {
Chris Dalton569c01b2021-05-25 10:11:46 -060063 auto drawFanWithTessellator = GrPathTessellator::DrawInnerFan::kYes;
64 if (fPath.countVerbs() > 50 && this->bounds().height() * this->bounds().width() > 256*256) {
65 // Large complex paths do better with a dedicated triangle shader for the inner fan.
66 // This takes less PCI bus bandwidth (6 floats per triangle instead of 8) and allows us
67 // to make sure it has an efficient middle-out topology.
Chris Dalton70a0d2c2021-01-26 12:01:21 -070068 fStencilFanProgram = GrStencilPathShader::MakeStencilProgram<GrStencilTriangleShader>(
69 args, fViewMatrix, stencilPassPipeline, fPath.getFillType());
Chris Dalton569c01b2021-05-25 10:11:46 -060070 drawFanWithTessellator = GrPathTessellator::DrawInnerFan::kNo;
Chris Dalton70a0d2c2021-01-26 12:01:21 -070071 }
Chris Dalton569c01b2021-05-25 10:11:46 -060072 fTessellator = GrPathTessellator::Make(args.fArena, fViewMatrix, fPath,
73 drawFanWithTessellator, *args.fCaps);
Chris Dalton70a0d2c2021-01-26 12:01:21 -070074 }
75
Chris Dalton569c01b2021-05-25 10:11:46 -060076 fStencilPathProgram = GrPathShader::MakeProgram(
77 args, fTessellator->shader(), stencilPassPipeline,
78 GrStencilPathShader::StencilPassSettings(fPath.getFillType()));
79
Chris Dalton70a0d2c2021-01-26 12:01:21 -070080 if (!(fOpFlags & OpFlags::kStencilOnly)) {
81 // Create a program that draws a bounding box over the path and fills its stencil coverage
82 // into the color buffer.
83 auto* bboxShader = args.fArena->make<GrFillBoundingBoxShader>(fViewMatrix, fColor,
84 fPath.getBounds());
85 auto* bboxPipeline = GrFillPathShader::MakeFillPassPipeline(args, fAAType,
86 std::move(appliedClip),
87 std::move(fProcessors));
88 auto* bboxStencil = GrFillPathShader::TestAndResetStencilSettings();
89 fFillBBoxProgram = GrPathShader::MakeProgram(args, bboxShader, bboxPipeline, bboxStencil);
90 }
91}
92
Chris Dalton2ed22fa2021-05-06 16:08:30 -060093void GrPathStencilFillOp::onPrePrepare(GrRecordingContext* context,
94 const GrSurfaceProxyView& writeView, GrAppliedClip* clip,
95 const GrXferProcessor::DstProxyView& dstProxyView,
96 GrXferBarrierFlags renderPassXferBarriers,
97 GrLoadOp colorLoadOp) {
Chris Dalton70a0d2c2021-01-26 12:01:21 -070098 this->prePreparePrograms({context->priv().recordTimeAllocator(), writeView, &dstProxyView,
99 renderPassXferBarriers, colorLoadOp, context->priv().caps()},
100 (clip) ? std::move(*clip) : GrAppliedClip::Disabled());
101 if (fStencilFanProgram) {
102 context->priv().recordProgramInfo(fStencilFanProgram);
103 }
104 if (fStencilPathProgram) {
105 context->priv().recordProgramInfo(fStencilPathProgram);
106 }
107 if (fFillBBoxProgram) {
108 context->priv().recordProgramInfo(fFillBBoxProgram);
109 }
110}
111
Chris Dalton2ed22fa2021-05-06 16:08:30 -0600112void GrPathStencilFillOp::onPrepare(GrOpFlushState* flushState) {
Chris Dalton70a0d2c2021-01-26 12:01:21 -0700113 if (!fTessellator) {
114 this->prePreparePrograms({flushState->allocator(), flushState->writeView(),
115 &flushState->dstProxyView(), flushState->renderPassBarriers(),
116 flushState->colorLoadOp(), &flushState->caps()},
117 flushState->detachAppliedClip());
118 if (!fTessellator) {
119 return;
120 }
121 }
122
123 if (fStencilFanProgram) {
124 // The inner fan isn't built into the tessellator. Generate a standard Redbook fan with a
125 // middle-out topology.
126 GrEagerDynamicVertexAllocator vertexAlloc(flushState, &fFanBuffer, &fFanBaseVertex);
127 int maxFanTriangles = fPath.countVerbs() - 2; // n - 2 triangles make an n-gon.
Chris Dalton8731a712021-05-14 14:48:54 -0600128 GrVertexWriter triangleVertexWriter = vertexAlloc.lock<SkPoint>(maxFanTriangles * 3);
Chris Dalton70a0d2c2021-01-26 12:01:21 -0700129 fFanVertexCount = GrMiddleOutPolygonTriangulator::WritePathInnerFan(
Chris Daltondf2dbad2021-05-14 16:21:15 -0600130 &triangleVertexWriter, GrMiddleOutPolygonTriangulator::OutputType::kTriangles,
131 fPath) * 3;
Chris Dalton70a0d2c2021-01-26 12:01:21 -0700132 SkASSERT(fFanVertexCount <= maxFanTriangles * 3);
133 vertexAlloc.unlock(fFanVertexCount);
134 }
135
Chris Dalton569c01b2021-05-25 10:11:46 -0600136 fTessellator->prepare(flushState, this->bounds(), fPath);
Chris Daltonc91dd692021-05-24 15:04:47 -0600137
138 if (fFillBBoxProgram) {
139 GrVertexWriter vertexWriter = flushState->makeVertexSpace(sizeof(SkRect), 1, &fBBoxBuffer,
140 &fBBoxBaseInstance);
141 vertexWriter.write(fPath.getBounds());
142 }
Chris Dalton70a0d2c2021-01-26 12:01:21 -0700143}
144
Chris Dalton2ed22fa2021-05-06 16:08:30 -0600145void GrPathStencilFillOp::onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) {
Chris Dalton70a0d2c2021-01-26 12:01:21 -0700146 if (!fTessellator) {
147 return;
148 }
149
150 // Stencil the inner fan, if any.
151 if (fFanVertexCount > 0) {
152 SkASSERT(fStencilFanProgram);
153 SkASSERT(fFanBuffer);
154 flushState->bindPipelineAndScissorClip(*fStencilFanProgram, this->bounds());
155 flushState->bindBuffers(nullptr, nullptr, fFanBuffer);
156 flushState->draw(fFanVertexCount, fFanBaseVertex);
157 }
158
159 // Stencil the rest of the path.
160 SkASSERT(fStencilPathProgram);
161 flushState->bindPipelineAndScissorClip(*fStencilPathProgram, this->bounds());
162 fTessellator->draw(flushState);
163
164 // Fill in the bounding box (if not in stencil-only mode).
165 if (fFillBBoxProgram) {
166 flushState->bindPipelineAndScissorClip(*fFillBBoxProgram, this->bounds());
Robert Phillips787fd9d2021-03-22 14:48:09 -0400167 flushState->bindTextures(fFillBBoxProgram->geomProc(), nullptr,
Chris Dalton70a0d2c2021-01-26 12:01:21 -0700168 fFillBBoxProgram->pipeline());
Chris Daltonc91dd692021-05-24 15:04:47 -0600169 flushState->bindBuffers(nullptr, fBBoxBuffer, nullptr);
170 flushState->drawInstanced(1, fBBoxBaseInstance, 4, 0);
Chris Dalton70a0d2c2021-01-26 12:01:21 -0700171 }
172}