1/* 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 8#include "src/gpu/ops/PathStencilCoverOp.h" 9 10#include "src/gpu/GrEagerVertexAllocator.h" 11#include "src/gpu/GrGpu.h" 12#include "src/gpu/GrOpFlushState.h" 13#include "src/gpu/GrRecordingContextPriv.h" 14#include "src/gpu/GrResourceProvider.h" 15#include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h" 16#include "src/gpu/glsl/GrGLSLVarying.h" 17#include "src/gpu/glsl/GrGLSLVertexGeoBuilder.h" 18#include "src/gpu/ops/GrSimpleMeshDrawOpHelper.h" 19#include "src/gpu/tessellate/AffineMatrix.h" 20#include "src/gpu/tessellate/MiddleOutPolygonTriangulator.h" 21#include "src/gpu/tessellate/PathCurveTessellator.h" 22#include "src/gpu/tessellate/PathWedgeTessellator.h" 23#include "src/gpu/tessellate/Tessellation.h" 24#include "src/gpu/tessellate/shaders/GrPathTessellationShader.h" 25 26namespace { 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. 31class BoundingBoxShader : public GrGeometryProcessor { 32public: 33 BoundingBoxShader(SkPMColor4f color, const GrShaderCaps& shaderCaps) 34 : GrGeometryProcessor(kTessellate_BoundingBoxShader_ClassID) 35 , fColor(color) { 36 if (!shaderCaps.vertexIDSupport()) { 37 constexpr static Attribute kUnitCoordAttrib("unitCoord", kFloat2_GrVertexAttribType, 38 kFloat2_GrSLType); 39 this->setVertexAttributes(&kUnitCoordAttrib, 1); 40 } 41 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)); 47 } 48 49private: 50 const char* name() const final { return "tessellate_BoundingBoxShader"; } 51 SkString getShaderDfxInfo() const override { return SkString("ShaderDfx_BoundingBoxShader"); } 52 void addToKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const final {} 53 std::unique_ptr<ProgramImpl> makeProgramImpl(const GrShaderCaps&) const final; 54 55 const SkPMColor4f fColor; 56}; 57 58std::unique_ptr<GrGeometryProcessor::ProgramImpl> BoundingBoxShader::makeProgramImpl( 59 const GrShaderCaps&) const { 60 class Impl : public ProgramImpl { 61 public: 62 void setData(const GrGLSLProgramDataManager& pdman, 63 const GrShaderCaps&, 64 const GrGeometryProcessor& gp) override { 65 const SkPMColor4f& color = gp.cast<BoundingBoxShader>().fColor; 66 pdman.set4f(fColorUniform, color.fR, color.fG, color.fB, color.fA); 67 } 68 69 private: 70 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) final { 71 args.fVaryingHandler->emitAttributes(args.fGeomProc); 72 73 // Vertex shader. 74 if (args.fShaderCaps->vertexIDSupport()) { 75 // If we don't have sk_VertexID support then "unitCoord" already came in as a vertex 76 // attrib. 77 args.fVertBuilder->codeAppend(R"( 78 float2 unitCoord = float2(sk_VertexID & 1, sk_VertexID >> 1);)"); 79 } 80 args.fVertBuilder->codeAppend(R"( 81 // Bloat the bounding box by 1/4px to be certain we will reset every stencil value. 82 float2x2 M_ = inverse(float2x2(matrix2d)); 83 float2 bloat = float2(abs(M_[0]) + abs(M_[1])) * .25; 84 85 // Find the vertex position. 86 float2 localcoord = mix(pathBounds.xy - bloat, pathBounds.zw + bloat, unitCoord); 87 float2 vertexpos = float2x2(matrix2d) * localcoord + translate;)"); 88 gpArgs->fLocalCoordVar.set(kFloat2_GrSLType, "localcoord"); 89 gpArgs->fPositionVar.set(kFloat2_GrSLType, "vertexpos"); 90 91 // Fragment shader. 92 const char* color; 93 fColorUniform = args.fUniformHandler->addUniform(nullptr, kFragment_GrShaderFlag, 94 kHalf4_GrSLType, "color", &color); 95 args.fFragBuilder->codeAppendf("half4 %s = %s;", args.fOutputColor, color); 96 args.fFragBuilder->codeAppendf("const half4 %s = half4(1);", args.fOutputCoverage); 97 } 98 99 GrGLSLUniformHandler::UniformHandle fColorUniform; 100 }; 101 102 return std::make_unique<Impl>(); 103} 104 105} // anonymous namespace 106 107namespace skgpu::v1 { 108 109void PathStencilCoverOp::visitProxies(const GrVisitProxyFunc& func) const { 110 if (fCoverBBoxProgram) { 111 fCoverBBoxProgram->pipeline().visitProxies(func); 112 } else { 113 fProcessors.visitProxies(func); 114 } 115} 116 117GrDrawOp::FixedFunctionFlags PathStencilCoverOp::fixedFunctionFlags() const { 118 auto flags = FixedFunctionFlags::kUsesStencil; 119 if (fAAType != GrAAType::kNone) { 120 flags |= FixedFunctionFlags::kUsesHWAA; 121 } 122 return flags; 123} 124 125GrProcessorSet::Analysis PathStencilCoverOp::finalize(const GrCaps& caps, 126 const GrAppliedClip* clip, 127 GrClampType clampType) { 128 return fProcessors.finalize(fColor, GrProcessorAnalysisCoverage::kNone, clip, nullptr, caps, 129 clampType, &fColor); 130} 131 132void PathStencilCoverOp::prePreparePrograms(const GrTessellationShader::ProgramArgs& args, 133 GrAppliedClip&& appliedClip) { 134 SkASSERT(!fTessellator); 135 SkASSERT(!fStencilFanProgram); 136 SkASSERT(!fStencilPathProgram); 137 SkASSERT(!fCoverBBoxProgram); 138 139 // We transform paths on the CPU. This allows for better batching. 140 const SkMatrix& shaderMatrix = SkMatrix::I(); 141 auto pipelineFlags = (fPathFlags & FillPathFlags::kWireframe) 142 ? GrPipeline::InputFlags::kWireframe 143 : GrPipeline::InputFlags::kNone; 144 const GrPipeline* stencilPipeline = GrPathTessellationShader::MakeStencilOnlyPipeline( 145 args, fAAType, appliedClip.hardClip(), pipelineFlags); 146 const GrUserStencilSettings* stencilSettings = GrPathTessellationShader::StencilPathSettings( 147 GrFillRuleForPathFillType(this->pathFillType())); 148 149 if (fTotalCombinedPathVerbCnt > 50 && 150 this->bounds().height() * this->bounds().width() > 256 * 256) { 151 // Large complex paths do better with a dedicated triangle shader for the inner fan. 152 // This takes less PCI bus bandwidth (6 floats per triangle instead of 8) and allows us 153 // to make sure it has an efficient middle-out topology. 154 auto shader = GrPathTessellationShader::MakeSimpleTriangleShader(args.fArena, 155 shaderMatrix, 156 SK_PMColor4fTRANSPARENT); 157 fStencilFanProgram = GrTessellationShader::MakeProgram(args, 158 shader, 159 stencilPipeline, 160 stencilSettings); 161 fTessellator = PathCurveTessellator::Make(args.fArena, 162 args.fCaps->shaderCaps()->infinitySupport()); 163 } else { 164 fTessellator = PathWedgeTessellator::Make(args.fArena, 165 args.fCaps->shaderCaps()->infinitySupport()); 166 } 167 auto* tessShader = GrPathTessellationShader::Make(args.fArena, 168 shaderMatrix, 169 SK_PMColor4fTRANSPARENT, 170 fTotalCombinedPathVerbCnt, 171 *stencilPipeline, 172 fTessellator->patchAttribs(), 173 *args.fCaps); 174 fStencilPathProgram = GrTessellationShader::MakeProgram(args, 175 tessShader, 176 stencilPipeline, 177 stencilSettings); 178 179 if (!(fPathFlags & FillPathFlags::kStencilOnly)) { 180 // Create a program that draws a bounding box over the path and fills its stencil coverage 181 // into the color buffer. 182 auto* bboxShader = args.fArena->make<BoundingBoxShader>(fColor, *args.fCaps->shaderCaps()); 183 auto* bboxPipeline = GrTessellationShader::MakePipeline(args, fAAType, 184 std::move(appliedClip), 185 std::move(fProcessors)); 186 auto* bboxStencil = GrPathTessellationShader::TestAndResetStencilSettings( 187 SkPathFillType_IsInverse(this->pathFillType())); 188 fCoverBBoxProgram = GrSimpleMeshDrawOpHelper::CreateProgramInfo( 189 args.fCaps, 190 args.fArena, 191 bboxPipeline, 192 args.fWriteView, 193 args.fUsesMSAASurface, 194 bboxShader, 195 GrPrimitiveType::kTriangleStrip, 196 args.fXferBarrierFlags, 197 args.fColorLoadOp, 198 bboxStencil); 199 } 200} 201 202void PathStencilCoverOp::onPrePrepare(GrRecordingContext* context, 203 const GrSurfaceProxyView& writeView, GrAppliedClip* clip, 204 const GrDstProxyView& dstProxyView, 205 GrXferBarrierFlags renderPassXferBarriers, 206 GrLoadOp colorLoadOp) { 207 // DMSAA is not supported on DDL. 208 bool usesMSAASurface = writeView.asRenderTargetProxy()->numSamples() > 1; 209 this->prePreparePrograms({context->priv().recordTimeAllocator(), writeView, usesMSAASurface, 210 &dstProxyView, renderPassXferBarriers, colorLoadOp, 211 context->priv().caps()}, 212 (clip) ? std::move(*clip) : GrAppliedClip::Disabled()); 213 if (fStencilFanProgram) { 214 context->priv().recordProgramInfo(fStencilFanProgram); 215 } 216 if (fStencilPathProgram) { 217 context->priv().recordProgramInfo(fStencilPathProgram); 218 } 219 if (fCoverBBoxProgram) { 220 context->priv().recordProgramInfo(fCoverBBoxProgram); 221 } 222} 223 224GR_DECLARE_STATIC_UNIQUE_KEY(gUnitQuadBufferKey); 225 226void PathStencilCoverOp::onPrepare(GrOpFlushState* flushState) { 227 if (!fTessellator) { 228 this->prePreparePrograms({flushState->allocator(), flushState->writeView(), 229 flushState->usesMSAASurface(), &flushState->dstProxyView(), 230 flushState->renderPassBarriers(), flushState->colorLoadOp(), 231 &flushState->caps()}, flushState->detachAppliedClip()); 232 if (!fTessellator) { 233 return; 234 } 235 } 236 237 if (fStencilFanProgram) { 238 // The inner fan isn't built into the tessellator. Generate a standard Redbook fan with a 239 // middle-out topology. 240 GrEagerDynamicVertexAllocator vertexAlloc(flushState, &fFanBuffer, &fFanBaseVertex); 241 int maxCombinedFanEdges = 242 PathTessellator::MaxCombinedFanEdgesInPathDrawList(fTotalCombinedPathVerbCnt); 243 // A single n-sided polygon is fanned by n-2 triangles. Multiple polygons with a combined 244 // edge count of n are fanned by strictly fewer triangles. 245 int maxTrianglesInFans = std::max(maxCombinedFanEdges - 2, 0); 246 int fanTriangleCount = 0; 247 if (VertexWriter triangleVertexWriter = vertexAlloc.lock<SkPoint>(maxTrianglesInFans * 3)) { 248 for (auto [pathMatrix, path, color] : *fPathDrawList) { 249 AffineMatrix m(pathMatrix); 250 for (PathMiddleOutFanIter it(path); !it.done();) { 251 for (auto [p0, p1, p2] : it.nextStack()) { 252 triangleVertexWriter << m.map2Points(p0, p1) << m.mapPoint(p2); 253 ++fanTriangleCount; 254 } 255 } 256 } 257 } 258 SkASSERT(fanTriangleCount <= maxTrianglesInFans); 259 fFanVertexCount = fanTriangleCount * 3; 260 vertexAlloc.unlock(fFanVertexCount); 261 } 262 263 auto tessShader = &fStencilPathProgram->geomProc().cast<GrPathTessellationShader>(); 264 fTessellator->prepare(flushState, 265 tessShader->maxTessellationSegments(*flushState->caps().shaderCaps()), 266 tessShader->viewMatrix(), 267 *fPathDrawList, 268 fTotalCombinedPathVerbCnt, 269 tessShader->willUseTessellationShaders()); 270 271 if (fCoverBBoxProgram) { 272 size_t instanceStride = fCoverBBoxProgram->geomProc().instanceStride(); 273 VertexWriter vertexWriter = flushState->makeVertexSpace(instanceStride, 274 fPathCount, 275 &fBBoxBuffer, 276 &fBBoxBaseInstance); 277 SkDEBUGCODE(int pathCount = 0;) 278 for (auto [pathMatrix, path, color] : *fPathDrawList) { 279 SkDEBUGCODE(auto end = vertexWriter.makeOffset(instanceStride)); 280 vertexWriter << pathMatrix.getScaleX() 281 << pathMatrix.getSkewY() 282 << pathMatrix.getSkewX() 283 << pathMatrix.getScaleY() 284 << pathMatrix.getTranslateX() 285 << pathMatrix.getTranslateY(); 286 if (path.isInverseFillType()) { 287 // Fill the entire backing store to make sure we clear every stencil value back to 288 // 0. If there is a scissor it will have already clipped the stencil draw. 289 auto rtBounds = 290 flushState->writeView().asRenderTargetProxy()->backingStoreBoundsRect(); 291 SkASSERT(rtBounds == fOriginalDrawBounds); 292 SkRect pathSpaceRTBounds; 293 if (SkMatrixPriv::InverseMapRect(pathMatrix, &pathSpaceRTBounds, rtBounds)) { 294 vertexWriter << pathSpaceRTBounds; 295 } else { 296 vertexWriter << path.getBounds(); 297 } 298 } else { 299 vertexWriter << path.getBounds(); 300 } 301 SkASSERT(vertexWriter == end); 302 SkDEBUGCODE(++pathCount;) 303 } 304 SkASSERT(pathCount == fPathCount); 305 } 306 307 if (!flushState->caps().shaderCaps()->vertexIDSupport()) { 308 constexpr static SkPoint kUnitQuad[4] = {{0,0}, {0,1}, {1,0}, {1,1}}; 309 310 GR_DEFINE_STATIC_UNIQUE_KEY(gUnitQuadBufferKey); 311 312 fBBoxVertexBufferIfNoIDSupport = flushState->resourceProvider()->findOrMakeStaticBuffer( 313 GrGpuBufferType::kVertex, sizeof(kUnitQuad), kUnitQuad, gUnitQuadBufferKey); 314 } 315} 316 317void PathStencilCoverOp::onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) { 318 if (!fTessellator) { 319 return; 320 } 321 322 if (fCoverBBoxProgram && 323 fCoverBBoxProgram->geomProc().hasVertexAttributes() && 324 !fBBoxVertexBufferIfNoIDSupport) { 325 return; 326 } 327 328 // Stencil the inner fan, if any. 329 if (fFanVertexCount > 0) { 330 SkASSERT(fStencilFanProgram); 331 SkASSERT(fFanBuffer); 332 flushState->bindPipelineAndScissorClip(*fStencilFanProgram, this->bounds()); 333 flushState->bindBuffers(nullptr, nullptr, fFanBuffer); 334 flushState->draw(fFanVertexCount, fFanBaseVertex); 335 } 336 337 // Stencil the rest of the path. 338 SkASSERT(fStencilPathProgram); 339 flushState->bindPipelineAndScissorClip(*fStencilPathProgram, this->bounds()); 340 fTessellator->draw(flushState, fStencilPathProgram->geomProc().willUseTessellationShaders()); 341 if (flushState->caps().requiresManualFBBarrierAfterTessellatedStencilDraw()) { 342 flushState->gpu()->insertManualFramebufferBarrier(); // http://skbug.com/9739 343 } 344 345 // Fill in the bounding box (if not in stencil-only mode). 346 if (fCoverBBoxProgram) { 347 flushState->bindPipelineAndScissorClip(*fCoverBBoxProgram, this->bounds()); 348 flushState->bindTextures(fCoverBBoxProgram->geomProc(), nullptr, 349 fCoverBBoxProgram->pipeline()); 350 flushState->bindBuffers(nullptr, fBBoxBuffer, fBBoxVertexBufferIfNoIDSupport); 351 flushState->drawInstanced(fPathCount, fBBoxBaseInstance, 4, 0); 352 } 353} 354 355} // namespace skgpu::v1 356