| /* |
| * 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 GrPathTessellationShader_DEFINED |
| #define GrPathTessellationShader_DEFINED |
| |
| #include "src/gpu/ganesh/tessellate/GrTessellationShader.h" |
| #include "src/gpu/tessellate/Tessellation.h" |
| |
| // This is the base class for shaders in the GPU tessellator that fill paths. |
| class GrPathTessellationShader : public GrTessellationShader { |
| protected: |
| using PatchAttribs = skgpu::tess::PatchAttribs; |
| |
| public: |
| // Draws a simple array of triangles. |
| static GrPathTessellationShader* MakeSimpleTriangleShader(SkArenaAlloc*, |
| const SkMatrix& viewMatrix, |
| const SkPMColor4f&); |
| |
| // Uses instanced draws to triangulate curves with a "middle-out" topology. Middle-out draws a |
| // triangle with vertices at T=[0, 1/2, 1] and then recurses breadth first: |
| // |
| // depth=0: T=[0, 1/2, 1] |
| // depth=1: T=[0, 1/4, 2/4], T=[2/4, 3/4, 1] |
| // depth=2: T=[0, 1/8, 2/8], T=[2/8, 3/8, 4/8], T=[4/8, 5/8, 6/8], T=[6/8, 7/8, 1] |
| // ... |
| // |
| // The shader determines how many segments are required to render each individual curve |
| // smoothly, and emits empty triangles at any vertices whose sk_VertexIDs are higher than |
| // necessary. It is the caller's responsibility to draw enough vertices per instance for the |
| // most complex curve in the batch to render smoothly (i.e., NumTrianglesAtResolveLevel() * 3). |
| // |
| // If PatchAttribs::kFanPoint is set, an additional triangle is added, connecting the base of |
| // the curve to the fan point. |
| static GrPathTessellationShader* Make(const GrShaderCaps&, |
| SkArenaAlloc*, |
| const SkMatrix& viewMatrix, |
| const SkPMColor4f&, |
| PatchAttribs); |
| |
| // Returns the stencil settings to use for a standard Redbook "stencil" pass. |
| static const GrUserStencilSettings* StencilPathSettings(GrFillRule fillRule) { |
| // 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>()); |
| |
| return (fillRule == GrFillRule::kNonzero) ? &kIncrDecrStencil : &kInvertStencil; |
| } |
| |
| // Returns the stencil settings to use for a standard Redbook "fill" pass. Allows non-zero |
| // stencil values to pass and write a color, and resets the stencil value back to zero; discards |
| // immediately on stencil values of zero. |
| static const GrUserStencilSettings* TestAndResetStencilSettings(bool isInverseFill = false) { |
| constexpr static GrUserStencilSettings kTestAndResetStencil( |
| GrUserStencilSettings::StaticInit< |
| 0x0000, |
| // No need to check the clip because the previous stencil pass will have only |
| // written to samples already inside the clip. |
| GrUserStencilTest::kNotEqual, |
| 0xffff, |
| GrUserStencilOp::kZero, |
| GrUserStencilOp::kKeep, |
| 0xffff>()); |
| |
| constexpr static GrUserStencilSettings kTestAndResetStencilInverted( |
| GrUserStencilSettings::StaticInit< |
| 0x0000, |
| // No need to check the clip because the previous stencil pass will have only |
| // written to samples already inside the clip. |
| GrUserStencilTest::kEqual, |
| 0xffff, |
| GrUserStencilOp::kKeep, |
| GrUserStencilOp::kZero, |
| 0xffff>()); |
| |
| return isInverseFill ? &kTestAndResetStencilInverted : &kTestAndResetStencil; |
| } |
| |
| // Creates a pipeline that does not write to the color buffer. |
| static const GrPipeline* MakeStencilOnlyPipeline( |
| const ProgramArgs&, |
| GrAAType, |
| const GrAppliedHardClip&, |
| GrPipeline::InputFlags = GrPipeline::InputFlags::kNone); |
| |
| protected: |
| constexpr static size_t kMiddleOutVertexStride = 2 * sizeof(float); |
| |
| GrPathTessellationShader(ClassID classID, |
| GrPrimitiveType primitiveType, |
| const SkMatrix& viewMatrix, |
| const SkPMColor4f& color, |
| PatchAttribs attribs) |
| : GrTessellationShader(classID, primitiveType, viewMatrix, color) |
| , fAttribs(attribs) { |
| } |
| |
| // Default path tessellation shader implementation that manages a uniform matrix and color. |
| class Impl : public ProgramImpl { |
| public: |
| void onEmitCode(EmitArgs&, GrGPArgs*) final; |
| void setData(const GrGLSLProgramDataManager&, const GrShaderCaps&, |
| const GrGeometryProcessor&) override; |
| |
| protected: |
| // float4x3 unpack_rational_cubic(float2 p0, float2 p1, float2 p2, float2 p3) { ... |
| // |
| // Evaluate our point of interest using numerically stable linear interpolations. We add our |
| // own "safe_mix" method to guarantee we get exactly "b" when T=1. The builtin mix() |
| // function seems spec'd to behave this way, but empirical results results have shown it |
| // does not always. |
| static const char* kEvalRationalCubicFn; |
| |
| virtual void emitVertexCode(const GrShaderCaps&, |
| const GrPathTessellationShader&, |
| GrGLSLVertexBuilder*, |
| GrGLSLVaryingHandler*, |
| GrGPArgs*) = 0; |
| |
| GrGLSLUniformHandler::UniformHandle fAffineMatrixUniform; |
| GrGLSLUniformHandler::UniformHandle fTranslateUniform; |
| GrGLSLUniformHandler::UniformHandle fColorUniform; |
| SkString fVaryingColorName; |
| }; |
| |
| const PatchAttribs fAttribs; |
| }; |
| |
| #endif |