text: /*
   * 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/tessellate/GrTessTypes.h"
  #include "src/gpu/tessellate/shaders/GrTessellationShader.h"
  
  // This is the base class for shaders in the GPU tessellator that fill paths.
  class GrPathTessellationShader : public GrTessellationShader {
  public:
      // Draws a simple array of triangles.
      static GrPathTessellationShader* MakeSimpleTriangleShader(SkArenaAlloc*,
                                                                const SkMatrix& viewMatrix,
                                                                const SkPMColor4f&);
  
      // How many triangles are in a curve with 2^resolveLevel line segments?
      constexpr static int NumCurveTrianglesAtResolveLevel(int resolveLevel) {
          // resolveLevel=0 -> 0 line segments -> 0 triangles
          // resolveLevel=1 -> 2 line segments -> 1 triangle
          // resolveLevel=2 -> 4 line segments -> 3 triangles
          // resolveLevel=3 -> 8 line segments -> 7 triangles
          // ...
          return (1 << resolveLevel) - 1;
      }
  
      enum class PatchType : bool {
          // An ice cream cone shaped patch, with 4 curve control points on top of a triangle that
          // fans from a 5th point at the center of the contour. (5 points per patch.)
          kWedges,
          // A standalone closed curve made up 4 control points. (4 points per patch.)
          kCurves
      };
  
      // 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).
      static GrPathTessellationShader* MakeMiddleOutFixedCountShader(const GrShaderCaps&,
                                                                     SkArenaAlloc*,
                                                                     const SkMatrix& viewMatrix,
                                                                     const SkPMColor4f&, PatchType);
  
      // This is the largest number of segments the middle-out shader will accept in a single
      // instance. If a curve requires more segments, it needs to be chopped.
      constexpr static int kMaxFixedCountSegments = 32;
      constexpr static int kMaxFixedCountResolveLevel = 5;  // log2(kMaxFixedCountSegments)
      static_assert(kMaxFixedCountSegments == 1 << kMaxFixedCountResolveLevel);
  
      // These functions define the vertex and index buffers that should be bound when drawing with
      // the middle-out fixed count shader. The data sequence is identical for any length of
      // tessellation segments, so the caller can use them with any instance length (up to
      // kMaxFixedCountResolveLevel).
      //
      // The "curve" and "wedge" buffers are nearly identical, but we keep them separate for now in
      // case there is a perf hit in the curve case for not using index 0.
      constexpr static int SizeOfVertexBufferForMiddleOutCurves() {
          constexpr int kMaxVertexCount = (1 << kMaxFixedCountResolveLevel) + 1;
          return kMaxVertexCount * kMiddleOutVertexStride;
      }
      static void InitializeVertexBufferForMiddleOutCurves(GrVertexWriter, size_t bufferSize);
  
      constexpr static size_t SizeOfIndexBufferForMiddleOutCurves() {
          constexpr int kMaxTriangleCount =
                  NumCurveTrianglesAtResolveLevel(kMaxFixedCountResolveLevel);
          return kMaxTriangleCount * 3 * sizeof(uint16_t);
      }
      static void InitializeIndexBufferForMiddleOutCurves(GrVertexWriter, size_t bufferSize);
  
      constexpr static int SizeOfVertexBufferForMiddleOutWedges() {
          return SizeOfVertexBufferForMiddleOutCurves() + kMiddleOutVertexStride;
      }
      static void InitializeVertexBufferForMiddleOutWedges(GrVertexWriter, size_t bufferSize);
  
      constexpr static size_t SizeOfIndexBufferForMiddleOutWedges() {
          return SizeOfIndexBufferForMiddleOutCurves() + 3 * sizeof(uint16_t);
      }
      static void InitializeIndexBufferForMiddleOutWedges(GrVertexWriter, size_t bufferSize);
  
      // 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.
      static GrPathTessellationShader* MakeHardwareTessellationShader(SkArenaAlloc*,
                                                                      const SkMatrix& viewMatrix,
                                                                      const SkPMColor4f&, PatchType);
  
      // 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,
                                                       GrTessellationPathFlags,
                                                       const GrAppliedHardClip&);
  
  protected:
      constexpr static size_t kMiddleOutVertexStride = 2 * sizeof(float);
  
      GrPathTessellationShader(ClassID classID, GrPrimitiveType primitiveType,
                               int tessellationPatchVertexCount, const SkMatrix& viewMatrix,
                               const SkPMColor4f& color)
              : GrTessellationShader(classID, primitiveType, tessellationPatchVertexCount, viewMatrix,
                                     color) {
      }
  
      // 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*, GrGPArgs*) = 0;
  
          GrGLSLUniformHandler::UniformHandle fAffineMatrixUniform;
          GrGLSLUniformHandler::UniformHandle fTranslateUniform;
          GrGLSLUniformHandler::UniformHandle fColorUniform;
      };
  };
  
  #endif
  