blob: e7bf11ef0c0dedc0e874b3f03a75b8e693a4437c [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
10#include "src/gpu/GrEagerVertexAllocator.h"
Chris Daltond9bdc322021-06-01 19:22:05 -060011#include "src/gpu/GrGpu.h"
Chris Dalton70a0d2c2021-01-26 12:01:21 -070012#include "src/gpu/GrOpFlushState.h"
13#include "src/gpu/GrRecordingContextPriv.h"
Robert Phillips1a82a4e2021-07-01 10:27:44 -040014#include "src/gpu/GrResourceProvider.h"
Chris Dalton8aec1242021-07-19 11:10:45 -060015#include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h"
16#include "src/gpu/glsl/GrGLSLVarying.h"
Chris Dalton2f733ec2021-06-01 12:11:57 -060017#include "src/gpu/glsl/GrGLSLVertexGeoBuilder.h"
Chris Dalton70a0d2c2021-01-26 12:01:21 -070018#include "src/gpu/tessellate/GrMiddleOutPolygonTriangulator.h"
Chris Daltond9bdc322021-06-01 19:22:05 -060019#include "src/gpu/tessellate/GrPathCurveTessellator.h"
Chris Daltond9bdc322021-06-01 19:22:05 -060020#include "src/gpu/tessellate/GrPathWedgeTessellator.h"
Chris Dalton70a0d2c2021-01-26 12:01:21 -070021#include "src/gpu/tessellate/GrTessellationPathRenderer.h"
Chris Dalton3b412782021-06-01 13:40:03 -060022#include "src/gpu/tessellate/shaders/GrPathTessellationShader.h"
Chris Dalton70a0d2c2021-01-26 12:01:21 -070023
Chris Dalton917f9192021-06-08 14:32:37 -060024using PathFlags = GrTessellationPathRenderer::PathFlags;
Chris Dalton70a0d2c2021-01-26 12:01:21 -070025
Chris Dalton2f733ec2021-06-01 12:11:57 -060026namespace {
27
28// Fills a path's bounding box, with subpixel outset to avoid possible T-junctions with extreme
29// edges of the path.
30// NOTE: The emitted geometry may not be axis-aligned, depending on the view matrix.
Chris Dalton8aec1242021-07-19 11:10:45 -060031class BoundingBoxShader : public GrGeometryProcessor {
Chris Dalton2f733ec2021-06-01 12:11:57 -060032public:
Chris Daltone909e1e2021-07-27 13:23:18 -060033 BoundingBoxShader(SkPMColor4f color, const GrShaderCaps& shaderCaps)
Chris Dalton8aec1242021-07-19 11:10:45 -060034 : GrGeometryProcessor(kTessellate_BoundingBoxShader_ClassID)
Chris Dalton8aec1242021-07-19 11:10:45 -060035 , fColor(color) {
Chris Daltona05ccc32021-06-29 19:42:13 -060036 if (!shaderCaps.vertexIDSupport()) {
37 constexpr static Attribute kUnitCoordAttrib("unitCoord", kFloat2_GrVertexAttribType,
38 kFloat2_GrSLType);
39 this->setVertexAttributes(&kUnitCoordAttrib, 1);
40 }
Chris Daltone909e1e2021-07-27 13:23:18 -060041 constexpr static Attribute kInstanceAttribs[] = {
42 {"matrix2d", kFloat4_GrVertexAttribType, kFloat4_GrSLType},
43 {"translate", kFloat2_GrVertexAttribType, kFloat2_GrSLType},
44 {"pathBounds", kFloat4_GrVertexAttribType, kFloat4_GrSLType}
45 };
46 this->setInstanceAttributes(kInstanceAttribs, SK_ARRAY_COUNT(kInstanceAttribs));
Chris Dalton2f733ec2021-06-01 12:11:57 -060047 }
48
49private:
Chris Daltonb63711a2021-06-01 14:52:02 -060050 const char* name() const final { return "tessellate_BoundingBoxShader"; }
Chris Dalton2f733ec2021-06-01 12:11:57 -060051 void getGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const final {}
52 GrGLSLGeometryProcessor* createGLSLInstance(const GrShaderCaps&) const final;
Chris Dalton8aec1242021-07-19 11:10:45 -060053
Chris Dalton8aec1242021-07-19 11:10:45 -060054 const SkPMColor4f fColor;
Chris Dalton2f733ec2021-06-01 12:11:57 -060055};
56
57GrGLSLGeometryProcessor* BoundingBoxShader::createGLSLInstance(const GrShaderCaps&) const {
Chris Dalton8aec1242021-07-19 11:10:45 -060058 class Impl : public GrGLSLGeometryProcessor {
59 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) final {
60 args.fVaryingHandler->emitAttributes(args.fGeomProc);
61
62 // Vertex shader.
Chris Dalton8aec1242021-07-19 11:10:45 -060063 if (args.fShaderCaps->vertexIDSupport()) {
Chris Daltona05ccc32021-06-29 19:42:13 -060064 // If we don't have sk_VertexID support then "unitCoord" already came in as a vertex
65 // attrib.
Chris Daltone909e1e2021-07-27 13:23:18 -060066 args.fVertBuilder->codeAppend(R"(
Chris Daltona05ccc32021-06-29 19:42:13 -060067 float2 unitCoord = float2(sk_VertexID & 1, sk_VertexID >> 1);)");
68 }
Chris Daltone909e1e2021-07-27 13:23:18 -060069 args.fVertBuilder->codeAppend(R"(
Chris Dalton8aec1242021-07-19 11:10:45 -060070 // Bloat the bounding box by 1/4px to be certain we will reset every stencil value.
Chris Daltone909e1e2021-07-27 13:23:18 -060071 float2x2 M_ = inverse(float2x2(matrix2d));
Chris Dalton2f733ec2021-06-01 12:11:57 -060072 float2 bloat = float2(abs(M_[0]) + abs(M_[1])) * .25;
73
74 // Find the vertex position.
Chris Daltona05ccc32021-06-29 19:42:13 -060075 float2 localcoord = mix(pathBounds.xy - bloat, pathBounds.zw + bloat, unitCoord);
Chris Daltone909e1e2021-07-27 13:23:18 -060076 float2 vertexpos = float2x2(matrix2d) * localcoord + translate;)");
Chris Dalton2f733ec2021-06-01 12:11:57 -060077 gpArgs->fLocalCoordVar.set(kFloat2_GrSLType, "localcoord");
78 gpArgs->fPositionVar.set(kFloat2_GrSLType, "vertexpos");
Chris Dalton8aec1242021-07-19 11:10:45 -060079
80 // Fragment shader.
81 const char* color;
82 fColorUniform = args.fUniformHandler->addUniform(nullptr, kFragment_GrShaderFlag,
83 kHalf4_GrSLType, "color", &color);
84 args.fFragBuilder->codeAppendf("half4 %s = %s;", args.fOutputColor, color);
85 args.fFragBuilder->codeAppendf("const half4 %s = half4(1);", args.fOutputCoverage);
Chris Dalton2f733ec2021-06-01 12:11:57 -060086 }
Chris Dalton8aec1242021-07-19 11:10:45 -060087
88 void setData(const GrGLSLProgramDataManager& pdman, const GrShaderCaps&,
89 const GrGeometryProcessor& gp) override {
Chris Daltone909e1e2021-07-27 13:23:18 -060090 const SkPMColor4f& color = gp.cast<BoundingBoxShader>().fColor;
Chris Dalton8aec1242021-07-19 11:10:45 -060091 pdman.set4f(fColorUniform, color.fR, color.fG, color.fB, color.fA);
92 }
93
Chris Dalton8aec1242021-07-19 11:10:45 -060094 GrGLSLUniformHandler::UniformHandle fColorUniform;
Chris Dalton2f733ec2021-06-01 12:11:57 -060095 };
Chris Dalton8aec1242021-07-19 11:10:45 -060096
Chris Dalton2f733ec2021-06-01 12:11:57 -060097 return new Impl;
98}
99
100} // namespace
101
Robert Phillips294723d2021-06-17 09:23:58 -0400102void GrPathStencilCoverOp::visitProxies(const GrVisitProxyFunc& func) const {
Chris Dalton031d76b2021-06-08 16:32:00 -0600103 if (fCoverBBoxProgram) {
Robert Phillips294723d2021-06-17 09:23:58 -0400104 fCoverBBoxProgram->pipeline().visitProxies(func);
Chris Dalton70a0d2c2021-01-26 12:01:21 -0700105 } else {
Robert Phillips294723d2021-06-17 09:23:58 -0400106 fProcessors.visitProxies(func);
Chris Dalton70a0d2c2021-01-26 12:01:21 -0700107 }
108}
109
Chris Dalton031d76b2021-06-08 16:32:00 -0600110GrDrawOp::FixedFunctionFlags GrPathStencilCoverOp::fixedFunctionFlags() const {
Chris Dalton70a0d2c2021-01-26 12:01:21 -0700111 auto flags = FixedFunctionFlags::kUsesStencil;
112 if (fAAType != GrAAType::kNone) {
113 flags |= FixedFunctionFlags::kUsesHWAA;
114 }
115 return flags;
116}
117
Chris Dalton031d76b2021-06-08 16:32:00 -0600118GrProcessorSet::Analysis GrPathStencilCoverOp::finalize(const GrCaps& caps,
119 const GrAppliedClip* clip,
120 GrClampType clampType) {
Chris Dalton57ab06c2021-04-22 12:57:28 -0600121 return fProcessors.finalize(fColor, GrProcessorAnalysisCoverage::kNone, clip, nullptr, caps,
122 clampType, &fColor);
Chris Dalton70a0d2c2021-01-26 12:01:21 -0700123}
124
Chris Dalton031d76b2021-06-08 16:32:00 -0600125void GrPathStencilCoverOp::prePreparePrograms(const GrTessellationShader::ProgramArgs& args,
126 GrAppliedClip&& appliedClip) {
Chris Dalton569c01b2021-05-25 10:11:46 -0600127 SkASSERT(!fTessellator);
Chris Dalton70a0d2c2021-01-26 12:01:21 -0700128 SkASSERT(!fStencilFanProgram);
129 SkASSERT(!fStencilPathProgram);
Chris Dalton031d76b2021-06-08 16:32:00 -0600130 SkASSERT(!fCoverBBoxProgram);
Chris Dalton70a0d2c2021-01-26 12:01:21 -0700131
Chris Dalton69669812021-07-27 10:00:12 -0600132 // We transform paths on the CPU. This allows for better batching.
133 const SkMatrix& shaderMatrix = SkMatrix::I();
Chris Dalton2f733ec2021-06-01 12:11:57 -0600134 const GrPipeline* stencilPipeline = GrPathTessellationShader::MakeStencilOnlyPipeline(
Chris Dalton917f9192021-06-08 14:32:37 -0600135 args, fAAType, fPathFlags, appliedClip.hardClip());
Chris Dalton2f733ec2021-06-01 12:11:57 -0600136 const GrUserStencilSettings* stencilPathSettings =
Chris Daltonbaae2dd2021-06-25 14:52:49 -0600137 GrPathTessellationShader::StencilPathSettings(GrFillRuleForSkPath(fPath));
Chris Dalton569c01b2021-05-25 10:11:46 -0600138
Chris Dalton917f9192021-06-08 14:32:37 -0600139 if (fPath.countVerbs() > 50 && this->bounds().height() * this->bounds().width() > 256 * 256) {
Chris Daltond2b8ba32021-06-09 00:12:59 -0600140 // Large complex paths do better with a dedicated triangle shader for the inner fan.
141 // This takes less PCI bus bandwidth (6 floats per triangle instead of 8) and allows us
142 // to make sure it has an efficient middle-out topology.
Chris Dalton69669812021-07-27 10:00:12 -0600143 auto shader = GrPathTessellationShader::MakeSimpleTriangleShader(args.fArena,
144 shaderMatrix,
145 SK_PMColor4fTRANSPARENT);
146 fStencilFanProgram = GrTessellationShader::MakeProgram(args,
147 shader,
148 stencilPipeline,
Chris Daltona8c4de92021-07-26 21:46:28 +0000149 stencilPathSettings);
Chris Dalton69669812021-07-27 10:00:12 -0600150 fTessellator = GrPathCurveTessellator::Make(args.fArena,
151 shaderMatrix,
Chris Daltond2b8ba32021-06-09 00:12:59 -0600152 SK_PMColor4fTRANSPARENT,
153 GrPathCurveTessellator::DrawInnerFan::kNo,
Chris Dalton69669812021-07-27 10:00:12 -0600154 fPath.countVerbs(),
155 *stencilPipeline,
Chris Dalton198ac152021-06-09 13:49:43 -0600156 *args.fCaps);
Chris Dalton917f9192021-06-08 14:32:37 -0600157 } else {
Chris Dalton69669812021-07-27 10:00:12 -0600158 fTessellator = GrPathWedgeTessellator::Make(args.fArena,
159 shaderMatrix,
160 SK_PMColor4fTRANSPARENT,
161 fPath.countVerbs(),
162 *stencilPipeline,
163 *args.fCaps);
Chris Dalton70a0d2c2021-01-26 12:01:21 -0700164 }
Chris Dalton2f733ec2021-06-01 12:11:57 -0600165 fStencilPathProgram = GrTessellationShader::MakeProgram(args, fTessellator->shader(),
166 stencilPipeline, stencilPathSettings);
Chris Dalton569c01b2021-05-25 10:11:46 -0600167
Chris Dalton917f9192021-06-08 14:32:37 -0600168 if (!(fPathFlags & PathFlags::kStencilOnly)) {
Chris Dalton70a0d2c2021-01-26 12:01:21 -0700169 // Create a program that draws a bounding box over the path and fills its stencil coverage
170 // into the color buffer.
Chris Daltone909e1e2021-07-27 13:23:18 -0600171 auto* bboxShader = args.fArena->make<BoundingBoxShader>(fColor, *args.fCaps->shaderCaps());
Chris Dalton2f733ec2021-06-01 12:11:57 -0600172 auto* bboxPipeline = GrTessellationShader::MakePipeline(args, fAAType,
173 std::move(appliedClip),
174 std::move(fProcessors));
Chris Daltonbaae2dd2021-06-25 14:52:49 -0600175 auto* bboxStencil =
176 GrPathTessellationShader::TestAndResetStencilSettings(fPath.isInverseFillType());
Chris Dalton8aec1242021-07-19 11:10:45 -0600177 fCoverBBoxProgram = GrSimpleMeshDrawOpHelper::CreateProgramInfo(
178 args.fArena,
179 bboxPipeline,
180 args.fWriteView,
181 bboxShader,
182 GrPrimitiveType::kTriangleStrip,
183 args.fXferBarrierFlags,
184 args.fColorLoadOp,
185 bboxStencil);
Chris Dalton70a0d2c2021-01-26 12:01:21 -0700186 }
187}
188
Chris Dalton031d76b2021-06-08 16:32:00 -0600189void GrPathStencilCoverOp::onPrePrepare(GrRecordingContext* context,
190 const GrSurfaceProxyView& writeView, GrAppliedClip* clip,
191 const GrDstProxyView& dstProxyView,
192 GrXferBarrierFlags renderPassXferBarriers,
193 GrLoadOp colorLoadOp) {
Chris Dalton70a0d2c2021-01-26 12:01:21 -0700194 this->prePreparePrograms({context->priv().recordTimeAllocator(), writeView, &dstProxyView,
195 renderPassXferBarriers, colorLoadOp, context->priv().caps()},
196 (clip) ? std::move(*clip) : GrAppliedClip::Disabled());
197 if (fStencilFanProgram) {
198 context->priv().recordProgramInfo(fStencilFanProgram);
199 }
200 if (fStencilPathProgram) {
201 context->priv().recordProgramInfo(fStencilPathProgram);
202 }
Chris Dalton031d76b2021-06-08 16:32:00 -0600203 if (fCoverBBoxProgram) {
204 context->priv().recordProgramInfo(fCoverBBoxProgram);
Chris Dalton70a0d2c2021-01-26 12:01:21 -0700205 }
206}
207
Chris Daltona05ccc32021-06-29 19:42:13 -0600208GR_DECLARE_STATIC_UNIQUE_KEY(gUnitQuadBufferKey);
209
Chris Dalton031d76b2021-06-08 16:32:00 -0600210void GrPathStencilCoverOp::onPrepare(GrOpFlushState* flushState) {
Chris Dalton70a0d2c2021-01-26 12:01:21 -0700211 if (!fTessellator) {
212 this->prePreparePrograms({flushState->allocator(), flushState->writeView(),
213 &flushState->dstProxyView(), flushState->renderPassBarriers(),
214 flushState->colorLoadOp(), &flushState->caps()},
215 flushState->detachAppliedClip());
216 if (!fTessellator) {
217 return;
218 }
219 }
220
Chris Dalton69669812021-07-27 10:00:12 -0600221 // We transform paths on the CPU. This allows for better batching.
222 const SkMatrix& pathMatrix = fViewMatrix;
223
Chris Dalton70a0d2c2021-01-26 12:01:21 -0700224 if (fStencilFanProgram) {
225 // The inner fan isn't built into the tessellator. Generate a standard Redbook fan with a
226 // middle-out topology.
227 GrEagerDynamicVertexAllocator vertexAlloc(flushState, &fFanBuffer, &fFanBaseVertex);
228 int maxFanTriangles = fPath.countVerbs() - 2; // n - 2 triangles make an n-gon.
Chris Dalton8731a712021-05-14 14:48:54 -0600229 GrVertexWriter triangleVertexWriter = vertexAlloc.lock<SkPoint>(maxFanTriangles * 3);
Chris Dalton40c906f2021-07-26 11:27:05 -0600230 int numTrianglesWritten;
Chris Dalton69669812021-07-27 10:00:12 -0600231 GrMiddleOutPolygonTriangulator::WritePathInnerFan(std::move(triangleVertexWriter),
232 0,
233 0,
234 pathMatrix,
235 fPath,
236 &numTrianglesWritten);
Chris Dalton40c906f2021-07-26 11:27:05 -0600237 fFanVertexCount = 3 * numTrianglesWritten;
Chris Dalton70a0d2c2021-01-26 12:01:21 -0700238 SkASSERT(fFanVertexCount <= maxFanTriangles * 3);
239 vertexAlloc.unlock(fFanVertexCount);
240 }
241
Chris Dalton69669812021-07-27 10:00:12 -0600242 fTessellator->prepare(flushState, this->bounds(), pathMatrix, fPath);
Chris Daltonc91dd692021-05-24 15:04:47 -0600243
Chris Dalton031d76b2021-06-08 16:32:00 -0600244 if (fCoverBBoxProgram) {
Chris Daltone909e1e2021-07-27 13:23:18 -0600245 size_t instanceStride = fCoverBBoxProgram->geomProc().instanceStride();
246 GrVertexWriter vertexWriter = flushState->makeVertexSpace(
247 instanceStride,
248 1,
249 &fBBoxBuffer,
250 &fBBoxBaseInstance);
251 SkDEBUGCODE(auto end = vertexWriter.makeOffset(instanceStride));
252 vertexWriter.write(fViewMatrix.getScaleX(),
253 fViewMatrix.getSkewY(),
254 fViewMatrix.getSkewX(),
255 fViewMatrix.getScaleY(),
256 fViewMatrix.getTranslateX(),
257 fViewMatrix.getTranslateY());
Chris Daltonbaae2dd2021-06-25 14:52:49 -0600258 if (fPath.isInverseFillType()) {
259 // Fill the entire backing store to make sure we clear every stencil value back to 0. If
260 // there is a scissor it will have already clipped the stencil draw.
261 auto rtBounds = flushState->writeView().asRenderTargetProxy()->backingStoreBoundsRect();
262 SkASSERT(rtBounds == fOriginalDrawBounds);
263 SkRect pathSpaceRTBounds;
264 if (SkMatrixPriv::InverseMapRect(fViewMatrix, &pathSpaceRTBounds, rtBounds)) {
265 vertexWriter.write(pathSpaceRTBounds);
266 } else {
267 vertexWriter.write(fPath.getBounds());
268 }
269 } else {
270 vertexWriter.write(fPath.getBounds());
271 }
Chris Daltone909e1e2021-07-27 13:23:18 -0600272 SkASSERT(vertexWriter == end);
Chris Daltonc91dd692021-05-24 15:04:47 -0600273 }
Chris Daltona05ccc32021-06-29 19:42:13 -0600274
275 if (!flushState->caps().shaderCaps()->vertexIDSupport()) {
276 constexpr static SkPoint kUnitQuad[4] = {{0,0}, {0,1}, {1,0}, {1,1}};
277
278 GR_DEFINE_STATIC_UNIQUE_KEY(gUnitQuadBufferKey);
279
280 fBBoxVertexBufferIfNoIDSupport = flushState->resourceProvider()->findOrMakeStaticBuffer(
281 GrGpuBufferType::kVertex, sizeof(kUnitQuad), kUnitQuad, gUnitQuadBufferKey);
282 }
Chris Dalton70a0d2c2021-01-26 12:01:21 -0700283}
284
Chris Dalton031d76b2021-06-08 16:32:00 -0600285void GrPathStencilCoverOp::onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) {
Chris Dalton70a0d2c2021-01-26 12:01:21 -0700286 if (!fTessellator) {
287 return;
288 }
289
290 // Stencil the inner fan, if any.
291 if (fFanVertexCount > 0) {
292 SkASSERT(fStencilFanProgram);
293 SkASSERT(fFanBuffer);
294 flushState->bindPipelineAndScissorClip(*fStencilFanProgram, this->bounds());
295 flushState->bindBuffers(nullptr, nullptr, fFanBuffer);
296 flushState->draw(fFanVertexCount, fFanBaseVertex);
297 }
298
299 // Stencil the rest of the path.
300 SkASSERT(fStencilPathProgram);
301 flushState->bindPipelineAndScissorClip(*fStencilPathProgram, this->bounds());
302 fTessellator->draw(flushState);
Chris Daltond9bdc322021-06-01 19:22:05 -0600303 if (flushState->caps().requiresManualFBBarrierAfterTessellatedStencilDraw()) {
304 flushState->gpu()->insertManualFramebufferBarrier(); // http://skbug.com/9739
305 }
Chris Dalton70a0d2c2021-01-26 12:01:21 -0700306
307 // Fill in the bounding box (if not in stencil-only mode).
Chris Dalton031d76b2021-06-08 16:32:00 -0600308 if (fCoverBBoxProgram) {
309 flushState->bindPipelineAndScissorClip(*fCoverBBoxProgram, this->bounds());
310 flushState->bindTextures(fCoverBBoxProgram->geomProc(), nullptr,
311 fCoverBBoxProgram->pipeline());
Chris Daltona05ccc32021-06-29 19:42:13 -0600312 flushState->bindBuffers(nullptr, fBBoxBuffer, fBBoxVertexBufferIfNoIDSupport);
Chris Daltonc91dd692021-05-24 15:04:47 -0600313 flushState->drawInstanced(1, fBBoxBaseInstance, 4, 0);
Chris Dalton70a0d2c2021-01-26 12:01:21 -0700314 }
315}