/* * Copyright 2019 Google LLC. * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #ifndef GrStencilPathShader_DEFINED #define GrStencilPathShader_DEFINED #include "src/gpu/GrDrawIndirectCommand.h" #include "src/gpu/tessellate/GrPathShader.h" #include "src/gpu/tessellate/GrTessellationPathRenderer.h" // This is the base class for shaders that stencil path elements, namely, triangles, standalone // cubics, and wedges. class GrStencilPathShader : public GrPathShader { public: GrStencilPathShader(ClassID classID, const SkMatrix& viewMatrix, GrPrimitiveType primitiveType, int tessellationPatchVertexCount = 0) : GrPathShader(classID, viewMatrix, primitiveType, tessellationPatchVertexCount) { } // Creates a pipeline that can be used for normal Redbook stencil draws. static const GrPipeline* MakeStencilPassPipeline(const GrPathShader::ProgramArgs& args, GrAAType aaType, GrTessellationPathRenderer::OpFlags opFlags, const GrAppliedHardClip& hardClip) { using OpFlags = GrTessellationPathRenderer::OpFlags; GrPipeline::InitArgs pipelineArgs; if (aaType != GrAAType::kNone) { pipelineArgs.fInputFlags |= GrPipeline::InputFlags::kHWAntialias; } if (args.fCaps->wireframeSupport() && (opFlags & OpFlags::kWireframe)) { pipelineArgs.fInputFlags |= GrPipeline::InputFlags::kWireframe; } pipelineArgs.fCaps = args.fCaps; return args.fArena->make(pipelineArgs, GrDisableColorXPFactory::MakeXferProcessor(), hardClip); } // Returns the stencil settings to use for a standard Redbook stencil draw. static const GrUserStencilSettings* StencilPassSettings(SkPathFillType fillType) { // Increments clockwise triangles and decrements counterclockwise. Used for "winding" fill. constexpr static GrUserStencilSettings kIncrDecrStencil( GrUserStencilSettings::StaticInitSeparate< 0x0000, 0x0000, GrUserStencilTest::kAlwaysIfInClip, GrUserStencilTest::kAlwaysIfInClip, 0xffff, 0xffff, GrUserStencilOp::kIncWrap, GrUserStencilOp::kDecWrap, GrUserStencilOp::kKeep, GrUserStencilOp::kKeep, 0xffff, 0xffff>()); // Inverts the bottom stencil bit. Used for "even/odd" fill. constexpr static GrUserStencilSettings kInvertStencil( GrUserStencilSettings::StaticInit< 0x0000, GrUserStencilTest::kAlwaysIfInClip, 0xffff, GrUserStencilOp::kInvert, GrUserStencilOp::kKeep, 0x0001>()); SkASSERT(fillType == SkPathFillType::kWinding || fillType == SkPathFillType::kEvenOdd); return (fillType == SkPathFillType::kWinding) ? &kIncrDecrStencil : &kInvertStencil; } template static GrProgramInfo* MakeStencilProgram(const ProgramArgs& args, const SkMatrix& viewMatrix, const GrPipeline* pipeline, const GrUserStencilSettings* stencil) { const auto* shader = args.fArena->make(viewMatrix); return GrPathShader::MakeProgram(args, shader, pipeline, stencil); } template static GrProgramInfo* MakeStencilProgram(const ProgramArgs& args, const SkMatrix& viewMatrix, const GrPipeline* pipeline, const SkPathFillType fillType) { return MakeStencilProgram(args, viewMatrix, pipeline, StencilPassSettings(fillType)); } protected: constexpr static Attribute kSinglePointAttrib{"inputPoint", kFloat2_GrVertexAttribType, kFloat2_GrSLType}; void getGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder* b) const override { b->add32(this->viewMatrix().isIdentity()); } GrGLSLGeometryProcessor* createGLSLInstance(const GrShaderCaps&) const override; class Impl; }; // Draws simple triangles to the stencil buffer. class GrStencilTriangleShader : public GrStencilPathShader { public: GrStencilTriangleShader(const SkMatrix& viewMatrix) : GrStencilPathShader( kTessellate_GrStencilTriangleShader_ClassID, viewMatrix, GrPrimitiveType::kTriangles) { this->setVertexAttributes(&kSinglePointAttrib, 1); } const char* name() const override { return "tessellate_GrStencilTriangleShader"; } }; // Uses GPU tessellation shaders to linearize, triangulate, and render standalone closed cubics. // TODO: Eventually we want to use rational cubic wedges in order to support perspective and conics. class GrCubicTessellateShader : public GrStencilPathShader { public: GrCubicTessellateShader(const SkMatrix& viewMatrix) : GrStencilPathShader( kTessellate_GrCubicTessellateShader_ClassID, viewMatrix, GrPrimitiveType::kPatches, 4) { this->setVertexAttributes(&kSinglePointAttrib, 1); } const char* name() const override { return "tessellate_GrCubicTessellateShader"; } private: GrGLSLGeometryProcessor* createGLSLInstance(const GrShaderCaps&) const override; }; // Uses GPU tessellation shaders to linearize, triangulate, and render cubic "wedge" patches. A // wedge is a 5-point patch consisting of 4 cubic control points, plus an anchor point fanning from // the center of the curve's resident contour. // TODO: Eventually we want to use rational cubic wedges in order to support perspective and conics. class GrWedgeTessellateShader : public GrStencilPathShader { public: GrWedgeTessellateShader(const SkMatrix& viewMatrix) : GrStencilPathShader( kTessellate_GrWedgeTessellateShader_ClassID, viewMatrix, GrPrimitiveType::kPatches, 5) { this->setVertexAttributes(&kSinglePointAttrib, 1); } const char* name() const override { return "tessellate_GrWedgeTessellateShader"; } private: GrGLSLGeometryProcessor* createGLSLInstance(const GrShaderCaps&) const override; }; // Uses indirect (instanced) draws to triangulate standalone closed cubics with a "middle-out" // topology. The caller must compute each cubic's resolveLevel on the CPU (i.e., the log2 number of // line segments it will be divided into; see GrWangsFormula::cubic_log2/quadratic_log2), and then // sort the instance buffer by resolveLevel for efficient batching of indirect draws. class GrMiddleOutCubicShader : public GrStencilPathShader { public: // How many vertices do we need to draw in order to triangulate a cubic with 2^resolveLevel // line segments? constexpr static int NumVerticesAtResolveLevel(int resolveLevel) { // resolveLevel=0 -> 0 line segments -> 0 triangles -> 0 vertices // resolveLevel=1 -> 2 line segments -> 1 triangle -> 3 vertices // resolveLevel=2 -> 4 line segments -> 3 triangles -> 9 vertices // resolveLevel=3 -> 8 line segments -> 7 triangles -> 21 vertices // ... return ((1 << resolveLevel) - 1) * 3; } // Configures an indirect draw to render cubic instances with 2^resolveLevel evenly-spaced (in // the parametric sense) line segments. static void WriteDrawCubicsIndirectCmd(GrDrawIndexedIndirectWriter* indirectWriter, int resolveLevel, uint32_t instanceCount, uint32_t baseInstance) { SkASSERT(resolveLevel > 0 && resolveLevel <= GrTessellationPathRenderer::kMaxResolveLevel); // Starting at baseIndex=3, the index buffer triangulates a cubic with 2^kMaxResolveLevel // line segments. Each index value corresponds to a parametric T value on the curve. Since // the triangles are arranged in "middle-out" order, we can conveniently control the // resolveLevel by changing only the indexCount. uint32_t indexCount = NumVerticesAtResolveLevel(resolveLevel); indirectWriter->writeIndexed(indexCount, 3, instanceCount, baseInstance, 0); } // For performance reasons we can often express triangles as an indirect cubic draw and sneak // them in alongside the other indirect draws. This method configures an indirect draw to emit // the triangle [P0, P1, P2] from a 4-point instance. static void WriteDrawTrianglesIndirectCmd(GrDrawIndexedIndirectWriter* indirectWriter, uint32_t instanceCount, uint32_t baseInstance) { // Indices 0,1,2 have special index values that emit points P0, P1, and P2 respectively. indirectWriter->writeIndexed(3, 0, instanceCount, baseInstance, 0); } // Returns the index buffer that should be bound when drawing with this shader. // (Our vertex shader uses raw index values directly, so there is no vertex buffer.) static sk_sp FindOrMakeMiddleOutIndexBuffer(GrResourceProvider*); GrMiddleOutCubicShader(const SkMatrix& viewMatrix) : GrStencilPathShader(kTessellate_GrMiddleOutCubicShader_ClassID, viewMatrix, GrPrimitiveType::kTriangles) { constexpr static Attribute kInputPtsAttribs[] = { {"inputPoints_0_1", kFloat4_GrVertexAttribType, kFloat4_GrSLType}, {"inputPoints_2_3", kFloat4_GrVertexAttribType, kFloat4_GrSLType}}; this->setInstanceAttributes(kInputPtsAttribs, 2); } const char* name() const override { return "tessellate_GrMiddleOutCubicShader"; } private: GrGLSLGeometryProcessor* createGLSLInstance(const GrShaderCaps&) const override; class Impl; }; #endif