1 /*
2 * Copyright 2019 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/tessellate/shaders/GrPathTessellationShader.h"
9
10 #include "src/gpu/effects/GrDisableColorXP.h"
11 #include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h"
12 #include "src/gpu/glsl/GrGLSLVarying.h"
13 #include "src/gpu/glsl/GrGLSLVertexGeoBuilder.h"
14
15 using skgpu::PatchAttribs;
16
17 namespace {
18
19 // Draws a simple array of triangles.
20 class SimpleTriangleShader : public GrPathTessellationShader {
21 public:
SimpleTriangleShader(const SkMatrix & viewMatrix,SkPMColor4f color)22 SimpleTriangleShader(const SkMatrix& viewMatrix, SkPMColor4f color)
23 : GrPathTessellationShader(kTessellate_SimpleTriangleShader_ClassID,
24 GrPrimitiveType::kTriangles, 0, viewMatrix, color,
25 PatchAttribs::kNone) {
26 constexpr static Attribute kInputPointAttrib{"inputPoint", kFloat2_GrVertexAttribType,
27 kFloat2_GrSLType};
28 this->setVertexAttributes(&kInputPointAttrib, 1);
29 }
30
maxTessellationSegments(const GrShaderCaps &) const31 int maxTessellationSegments(const GrShaderCaps&) const override { SkUNREACHABLE; }
32
33 private:
name() const34 const char* name() const final { return "tessellate_SimpleTriangleShader"; }
addToKey(const GrShaderCaps &,GrProcessorKeyBuilder *) const35 void addToKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const final {}
36 std::unique_ptr<ProgramImpl> makeProgramImpl(const GrShaderCaps&) const final;
37 };
38
makeProgramImpl(const GrShaderCaps &) const39 std::unique_ptr<GrGeometryProcessor::ProgramImpl> SimpleTriangleShader::makeProgramImpl(
40 const GrShaderCaps&) const {
41 class Impl : public GrPathTessellationShader::Impl {
42 void emitVertexCode(const GrShaderCaps&,
43 const GrPathTessellationShader&,
44 GrGLSLVertexBuilder* v,
45 GrGLSLVaryingHandler*,
46 GrGPArgs* gpArgs) override {
47 v->codeAppend(R"(
48 float2 localcoord = inputPoint;
49 float2 vertexpos = AFFINE_MATRIX * localcoord + TRANSLATE;)");
50 gpArgs->fLocalCoordVar.set(kFloat2_GrSLType, "localcoord");
51 gpArgs->fPositionVar.set(kFloat2_GrSLType, "vertexpos");
52 }
53 };
54 return std::make_unique<Impl>();
55 }
56
57 } // namespace
58
Make(SkArenaAlloc * arena,const SkMatrix & viewMatrix,const SkPMColor4f & color,int totalCombinedPathVerbCnt,const GrPipeline & pipeline,skgpu::PatchAttribs attribs,const GrCaps & caps)59 GrPathTessellationShader* GrPathTessellationShader::Make(SkArenaAlloc* arena,
60 const SkMatrix& viewMatrix,
61 const SkPMColor4f& color,
62 int totalCombinedPathVerbCnt,
63 const GrPipeline& pipeline,
64 skgpu::PatchAttribs attribs,
65 const GrCaps& caps) {
66 if (caps.shaderCaps()->tessellationSupport() &&
67 totalCombinedPathVerbCnt >= caps.minPathVerbsForHwTessellation() &&
68 !pipeline.usesLocalCoords() && // Our tessellation back door doesn't handle varyings.
69 // Input color and explicit curve type workarounds aren't implemented yet for tessellation.
70 !(attribs & (PatchAttribs::kColor | PatchAttribs::kExplicitCurveType))) {
71 return GrPathTessellationShader::MakeHardwareTessellationShader(arena, viewMatrix, color,
72 attribs);
73 } else {
74 return GrPathTessellationShader::MakeMiddleOutFixedCountShader(*caps.shaderCaps(), arena,
75 viewMatrix, color,
76 attribs);
77 }
78 }
79
MakeSimpleTriangleShader(SkArenaAlloc * arena,const SkMatrix & viewMatrix,const SkPMColor4f & color)80 GrPathTessellationShader* GrPathTessellationShader::MakeSimpleTriangleShader(
81 SkArenaAlloc* arena, const SkMatrix& viewMatrix, const SkPMColor4f& color) {
82 return arena->make<SimpleTriangleShader>(viewMatrix, color);
83 }
84
MakeStencilOnlyPipeline(const ProgramArgs & args,GrAAType aaType,const GrAppliedHardClip & hardClip,GrPipeline::InputFlags pipelineFlags)85 const GrPipeline* GrPathTessellationShader::MakeStencilOnlyPipeline(
86 const ProgramArgs& args,
87 GrAAType aaType,
88 const GrAppliedHardClip& hardClip,
89 GrPipeline::InputFlags pipelineFlags) {
90 GrPipeline::InitArgs pipelineArgs;
91 pipelineArgs.fInputFlags = pipelineFlags;
92 pipelineArgs.fCaps = args.fCaps;
93 return args.fArena->make<GrPipeline>(pipelineArgs,
94 GrDisableColorXPFactory::MakeXferProcessor(),
95 hardClip);
96 }
97
98 // Evaluate our point of interest using numerically stable linear interpolations. We add our own
99 // "safe_mix" method to guarantee we get exactly "b" when T=1. The builtin mix() function seems
100 // spec'd to behave this way, but empirical results results have shown it does not always.
101 const char* GrPathTessellationShader::Impl::kEvalRationalCubicFn = R"(
102 float3 safe_mix(float3 a, float3 b, float T, float one_minus_T) {
103 return a*one_minus_T + b*T;
104 }
105 float2 eval_rational_cubic(float4x3 P, float T) {
106 float one_minus_T = 1.0 - T;
107 float3 ab = safe_mix(P[0], P[1], T, one_minus_T);
108 float3 bc = safe_mix(P[1], P[2], T, one_minus_T);
109 float3 cd = safe_mix(P[2], P[3], T, one_minus_T);
110 float3 abc = safe_mix(ab, bc, T, one_minus_T);
111 float3 bcd = safe_mix(bc, cd, T, one_minus_T);
112 float3 abcd = safe_mix(abc, bcd, T, one_minus_T);
113 return abcd.xy / abcd.z;
114 })";
115
onEmitCode(EmitArgs & args,GrGPArgs * gpArgs)116 void GrPathTessellationShader::Impl::onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) {
117 const auto& shader = args.fGeomProc.cast<GrPathTessellationShader>();
118 args.fVaryingHandler->emitAttributes(shader);
119
120 // Vertex shader.
121 const char* affineMatrix, *translate;
122 fAffineMatrixUniform = args.fUniformHandler->addUniform(nullptr, kVertex_GrShaderFlag,
123 kFloat4_GrSLType, "affineMatrix",
124 &affineMatrix);
125 fTranslateUniform = args.fUniformHandler->addUniform(nullptr, kVertex_GrShaderFlag,
126 kFloat2_GrSLType, "translate", &translate);
127 args.fVertBuilder->codeAppendf("float2x2 AFFINE_MATRIX = float2x2(%s);", affineMatrix);
128 args.fVertBuilder->codeAppendf("float2 TRANSLATE = %s;", translate);
129 this->emitVertexCode(*args.fShaderCaps,
130 shader,
131 args.fVertBuilder,
132 args.fVaryingHandler,
133 gpArgs);
134
135 // Fragment shader.
136 if (!(shader.fAttribs & PatchAttribs::kColor)) {
137 const char* color;
138 fColorUniform = args.fUniformHandler->addUniform(nullptr, kFragment_GrShaderFlag,
139 kHalf4_GrSLType, "color", &color);
140 args.fFragBuilder->codeAppendf("half4 %s = %s;", args.fOutputColor, color);
141 } else {
142 args.fFragBuilder->codeAppendf("half4 %s = %s;",
143 args.fOutputColor, fVaryingColorName.c_str());
144 }
145 args.fFragBuilder->codeAppendf("const half4 %s = half4(1);", args.fOutputCoverage);
146 }
147
setData(const GrGLSLProgramDataManager & pdman,const GrShaderCaps &,const GrGeometryProcessor & geomProc)148 void GrPathTessellationShader::Impl::setData(const GrGLSLProgramDataManager& pdman, const
149 GrShaderCaps&, const GrGeometryProcessor& geomProc) {
150 const auto& shader = geomProc.cast<GrPathTessellationShader>();
151 const SkMatrix& m = shader.viewMatrix();
152 pdman.set4f(fAffineMatrixUniform, m.getScaleX(), m.getSkewY(), m.getSkewX(), m.getScaleY());
153 pdman.set2f(fTranslateUniform, m.getTranslateX(), m.getTranslateY());
154
155 if (!(shader.fAttribs & PatchAttribs::kColor)) {
156 const SkPMColor4f& color = shader.color();
157 pdman.set4f(fColorUniform, color.fR, color.fG, color.fB, color.fA);
158 }
159 }
160