xref: /aosp_15_r20/external/skia/src/gpu/ganesh/effects/GrBezierEffect.h (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1 /*
2  * Copyright 2013 Google Inc.
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 #ifndef GrBezierEffect_DEFINED
9 #define GrBezierEffect_DEFINED
10 
11 #include "include/core/SkMatrix.h"
12 #include "include/private/SkColorData.h"
13 #include "include/private/gpu/ganesh/GrTypesPriv.h"
14 #include "src/base/SkArenaAlloc.h"
15 #include "src/core/SkSLTypeShared.h"
16 #include "src/gpu/ganesh/GrCaps.h"
17 #include "src/gpu/ganesh/GrGeometryProcessor.h"
18 #include "src/gpu/ganesh/GrProcessorUnitTest.h"
19 #include "src/gpu/ganesh/GrShaderCaps.h"
20 
21 #include <cstdint>
22 #include <memory>
23 
24 namespace skgpu { class KeyBuilder; }
25 
26 /**
27  * Shader is based off of Loop-Blinn Quadratic GPU Rendering
28  * The output of this effect is a hairline edge for conics.
29  * Conics specified by implicit equation K^2 - LM.
30  * K, L, and M, are the first three values of the vertex attribute,
31  * the fourth value is not used. Distance is calculated using a
32  * first order approximation from the taylor series.
33  * Coverage for AA is max(0, 1-distance).
34  *
35  * Test were also run using a second order distance approximation.
36  * There were two versions of the second order approx. The first version
37  * is of roughly the form:
38  * f(q) = |f(p)| - ||f'(p)||*||q-p|| - ||f''(p)||*||q-p||^2.
39  * The second is similar:
40  * f(q) = |f(p)| + ||f'(p)||*||q-p|| + ||f''(p)||*||q-p||^2.
41  * The exact version of the equations can be found in the paper
42  * "Distance Approximations for Rasterizing Implicit Curves" by Gabriel Taubin
43  *
44  * In both versions we solve the quadratic for ||q-p||.
45  * Version 1:
46  * gFM is magnitude of first partials and gFM2 is magnitude of 2nd partials (as derived from paper)
47  * builder->fsCodeAppend("\t\tedgeAlpha = (sqrt(gFM*gFM+4.0*func*gF2M) - gFM)/(2.0*gF2M);\n");
48  * Version 2:
49  * builder->fsCodeAppend("\t\tedgeAlpha = (gFM - sqrt(gFM*gFM-4.0*func*gF2M))/(2.0*gF2M);\n");
50  *
51  * Also note that 2nd partials of k,l,m are zero
52  *
53  * When comparing the two second order approximations to the first order approximations,
54  * the following results were found. Version 1 tends to underestimate the distances, thus it
55  * basically increases all the error that we were already seeing in the first order
56  * approx. So this version is not the one to use. Version 2 has the opposite effect
57  * and tends to overestimate the distances. This is much closer to what we are
58  * looking for. It is able to render ellipses (even thin ones) without the need to chop.
59  * However, it can not handle thin hyperbolas well and thus would still rely on
60  * chopping to tighten the clipping. Another side effect of the overestimating is
61  * that the curves become much thinner and "ropey". If all that was ever rendered
62  * were "not too thin" curves and ellipses then 2nd order may have an advantage since
63  * only one geometry would need to be rendered. However no benches were run comparing
64  * chopped first order and non chopped 2nd order.
65  */
66 class GrConicEffect : public GrGeometryProcessor {
67 public:
68     static GrGeometryProcessor* Make(SkArenaAlloc* arena,
69                                      const SkPMColor4f& color,
70                                      const SkMatrix& viewMatrix,
71                                      const GrCaps& caps,
72                                      const SkMatrix& localMatrix,
73                                      bool usesLocalCoords,
74                                      uint8_t coverage = 0xff) {
75         if (!caps.shaderCaps()->fShaderDerivativeSupport) {
76             return nullptr;
77         }
78 
79         return arena->make([&](void* ptr) {
80             return new (ptr) GrConicEffect(color, viewMatrix, coverage, localMatrix,
81                                            usesLocalCoords);
82         });
83     }
84 
85     ~GrConicEffect() override;
86 
name()87     const char* name() const override { return "Conic"; }
88 
89     void addToKey(const GrShaderCaps& caps, skgpu::KeyBuilder* b) const override;
90 
91     std::unique_ptr<ProgramImpl> makeProgramImpl(const GrShaderCaps&) const override;
92 
93 private:
94     class Impl;
95 
96     GrConicEffect(const SkPMColor4f&, const SkMatrix& viewMatrix, uint8_t coverage,
97                   const SkMatrix& localMatrix, bool usesLocalCoords);
98 
inPosition()99     inline const Attribute& inPosition() const { return kAttributes[0]; }
inConicCoeffs()100     inline const Attribute& inConicCoeffs() const { return kAttributes[1]; }
101 
102     SkPMColor4f         fColor;
103     SkMatrix            fViewMatrix;
104     SkMatrix            fLocalMatrix;
105     bool                fUsesLocalCoords;
106     uint8_t             fCoverageScale;
107     inline static constexpr Attribute kAttributes[] = {
108         {"inPosition", kFloat2_GrVertexAttribType, SkSLType::kFloat2},
109         {"inConicCoeffs", kFloat4_GrVertexAttribType, SkSLType::kHalf4}
110     };
111 
112     GR_DECLARE_GEOMETRY_PROCESSOR_TEST
113 
114     using INHERITED = GrGeometryProcessor;
115 };
116 
117 ///////////////////////////////////////////////////////////////////////////////
118 /**
119  * The output of this effect is a hairline edge for quadratics.
120  * Quadratic specified by 0=u^2-v canonical coords. u and v are the first
121  * two components of the vertex attribute. At the three control points that define
122  * the Quadratic, u, v have the values {0,0}, {1/2, 0}, and {1, 1} respectively.
123  * Coverage for AA is min(0, 1-distance). 3rd & 4th cimponent unused.
124  * Requires shader derivative instruction support.
125  */
126 class GrQuadEffect : public GrGeometryProcessor {
127 public:
128     static GrGeometryProcessor* Make(SkArenaAlloc* arena,
129                                      const SkPMColor4f& color,
130                                      const SkMatrix& viewMatrix,
131                                      const GrCaps& caps,
132                                      const SkMatrix& localMatrix,
133                                      bool usesLocalCoords,
134                                      uint8_t coverage = 0xff) {
135         if (!caps.shaderCaps()->fShaderDerivativeSupport) {
136             return nullptr;
137         }
138 
139         return arena->make([&](void* ptr) {
140             return new (ptr) GrQuadEffect(color, viewMatrix, coverage, localMatrix,
141                                           usesLocalCoords);
142         });
143     }
144 
145     ~GrQuadEffect() override;
146 
name()147     const char* name() const override { return "Quad"; }
148 
149     void addToKey(const GrShaderCaps& caps, skgpu::KeyBuilder* b) const override;
150 
151     std::unique_ptr<ProgramImpl> makeProgramImpl(const GrShaderCaps&) const override;
152 
153 private:
154     class Impl;
155 
156     GrQuadEffect(const SkPMColor4f&, const SkMatrix& viewMatrix, uint8_t coverage,
157                  const SkMatrix& localMatrix, bool usesLocalCoords);
158 
inPosition()159     inline const Attribute& inPosition() const { return kAttributes[0]; }
inHairQuadEdge()160     inline const Attribute& inHairQuadEdge() const { return kAttributes[1]; }
161 
162     SkPMColor4f fColor;
163     SkMatrix fViewMatrix;
164     SkMatrix fLocalMatrix;
165     bool fUsesLocalCoords;
166     uint8_t fCoverageScale;
167 
168     inline static constexpr Attribute kAttributes[] = {
169         {"inPosition", kFloat2_GrVertexAttribType, SkSLType::kFloat2},
170         {"inHairQuadEdge", kFloat4_GrVertexAttribType, SkSLType::kHalf4}
171     };
172 
173     GR_DECLARE_GEOMETRY_PROCESSOR_TEST
174 
175     using INHERITED = GrGeometryProcessor;
176 };
177 
178 #endif
179