1 /*
2  * Copyright 2019 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 #include "gm/gm.h"
9 
10 #include "src/gpu/GrCaps.h"
11 #include "src/gpu/GrDirectContextPriv.h"
12 #include "src/gpu/GrMemoryPool.h"
13 #include "src/gpu/GrOpFlushState.h"
14 #include "src/gpu/GrOpsRenderPass.h"
15 #include "src/gpu/GrPipeline.h"
16 #include "src/gpu/GrPrimitiveProcessor.h"
17 #include "src/gpu/GrProgramInfo.h"
18 #include "src/gpu/GrRecordingContextPriv.h"
19 #include "src/gpu/GrRenderTargetContext.h"
20 #include "src/gpu/GrRenderTargetContextPriv.h"
21 #include "src/gpu/GrShaderCaps.h"
22 #include "src/gpu/GrShaderVar.h"
23 #include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h"
24 #include "src/gpu/glsl/GrGLSLGeometryProcessor.h"
25 #include "src/gpu/glsl/GrGLSLPrimitiveProcessor.h"
26 #include "src/gpu/glsl/GrGLSLVarying.h"
27 #include "src/gpu/glsl/GrGLSLVertexGeoBuilder.h"
28 #include "src/gpu/ops/GrDrawOp.h"
29 
30 namespace skiagm {
31 
32 constexpr static GrGeometryProcessor::Attribute kPositionAttrib =
33         {"position", kFloat3_GrVertexAttribType, kFloat3_GrSLType};
34 
35 constexpr static std::array<float, 3> kTri1[3] = {
36         {20.5f,20.5f,1}, {170.5f,280.5f,4}, {320.5f,20.5f,1}};
37 constexpr static std::array<float, 3> kTri2[3] = {
38         {640.5f,280.5f,3}, {490.5f,20.5f,1}, {340.5f,280.5f,6}};
39 constexpr static SkRect kRect = {20.5f, 340.5f, 640.5f, 480.5f};
40 
41 constexpr static int kWidth = (int)kRect.fRight + 21;
42 constexpr static int kHeight = (int)kRect.fBottom + 21;
43 
44 /**
45  * This is a GPU-backend specific test. It ensures that tessellation works as expected by drawing
46  * several triangles. The test passes as long as the triangle tessellations match the reference
47  * images on gold.
48  */
49 class TessellationGM : public GpuGM {
onShortName()50     SkString onShortName() override { return SkString("tessellation"); }
onISize()51     SkISize onISize() override { return {kWidth, kHeight}; }
52     DrawResult onDraw(GrRecordingContext*, GrRenderTargetContext*, SkCanvas*, SkString*) override;
53 };
54 
55 
56 class TessellationTestTriShader : public GrGeometryProcessor {
57 public:
TessellationTestTriShader(const SkMatrix & viewMatrix)58     TessellationTestTriShader(const SkMatrix& viewMatrix)
59             : GrGeometryProcessor(kTessellationTestTriShader_ClassID), fViewMatrix(viewMatrix) {
60         this->setVertexAttributes(&kPositionAttrib, 1);
61         this->setWillUseTessellationShaders();
62     }
63 
64 private:
name() const65     const char* name() const final { return "TessellationTestTriShader"; }
getGLSLProcessorKey(const GrShaderCaps &,GrProcessorKeyBuilder * b) const66     void getGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder* b) const final {}
67 
68     class Impl : public GrGLSLGeometryProcessor {
onEmitCode(EmitArgs & args,GrGPArgs *)69         void onEmitCode(EmitArgs& args, GrGPArgs*) override {
70             args.fVaryingHandler->emitAttributes(args.fGP.cast<TessellationTestTriShader>());
71             const char* viewMatrix;
72             fViewMatrixUniform = args.fUniformHandler->addUniform(
73                     nullptr, kVertex_GrShaderFlag, kFloat3x3_GrSLType, "view_matrix", &viewMatrix);
74             args.fVertBuilder->declareGlobal(
75                     GrShaderVar("P_", kFloat3_GrSLType, GrShaderVar::TypeModifier::Out));
76             args.fVertBuilder->codeAppendf(R"(
77                     P_.xy = (%s * float3(position.xy, 1)).xy;
78                     P_.z = position.z;)", viewMatrix);
79             // GrGLProgramBuilder will call writeTess*ShaderGLSL when it is compiling.
80             this->writeFragmentShader(args.fFragBuilder, args.fOutputColor, args.fOutputCoverage);
81         }
82         void writeFragmentShader(GrGLSLFPFragmentBuilder*, const char* color, const char* coverage);
setData(const GrGLSLProgramDataManager & pdman,const GrPrimitiveProcessor & proc)83         void setData(const GrGLSLProgramDataManager& pdman,
84                      const GrPrimitiveProcessor& proc) override {
85             pdman.setSkMatrix(fViewMatrixUniform,
86                               proc.cast<TessellationTestTriShader>().fViewMatrix);
87         }
88         GrGLSLUniformHandler::UniformHandle fViewMatrixUniform;
89     };
90 
createGLSLInstance(const GrShaderCaps &) const91     GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override {
92         return new Impl;
93     }
94 
95     SkString getTessControlShaderGLSL(const GrGLSLPrimitiveProcessor*,
96                                       const char* versionAndExtensionDecls,
97                                       const GrGLSLUniformHandler&,
98                                       const GrShaderCaps&) const override;
99     SkString getTessEvaluationShaderGLSL(const GrGLSLPrimitiveProcessor*,
100                                          const char* versionAndExtensionDecls,
101                                          const GrGLSLUniformHandler&,
102                                          const GrShaderCaps&) const override;
103 
104     const SkMatrix fViewMatrix;
105 };
106 
getTessControlShaderGLSL(const GrGLSLPrimitiveProcessor *,const char * versionAndExtensionDecls,const GrGLSLUniformHandler &,const GrShaderCaps &) const107 SkString TessellationTestTriShader::getTessControlShaderGLSL(
108         const GrGLSLPrimitiveProcessor*, const char* versionAndExtensionDecls,
109         const GrGLSLUniformHandler&, const GrShaderCaps&) const {
110     SkString code(versionAndExtensionDecls);
111     code.append(R"(
112             layout(vertices = 3) out;
113 
114             in vec3 P_[];
115             out vec3 P[];
116 
117             void main() {
118                 P[gl_InvocationID] = P_[gl_InvocationID];
119                 gl_TessLevelOuter[gl_InvocationID] = P_[gl_InvocationID].z;
120                 gl_TessLevelInner[0] = 2.0;
121             })");
122 
123     return code;
124 }
125 
getTessEvaluationShaderGLSL(const GrGLSLPrimitiveProcessor *,const char * versionAndExtensionDecls,const GrGLSLUniformHandler &,const GrShaderCaps &) const126 SkString TessellationTestTriShader::getTessEvaluationShaderGLSL(
127         const GrGLSLPrimitiveProcessor*, const char* versionAndExtensionDecls,
128         const GrGLSLUniformHandler&, const GrShaderCaps&) const {
129     SkString code(versionAndExtensionDecls);
130     code.append(R"(
131             layout(triangles, equal_spacing, cw) in;
132 
133             uniform vec4 sk_RTAdjust;
134 
135             in vec3 P[];
136             out vec3 barycentric_coord;
137 
138             void main() {
139                 vec2 devcoord = mat3x2(P[0].xy, P[1].xy, P[2].xy) * gl_TessCoord.xyz;
140                 devcoord = round(devcoord - .5) + .5;  // Make horz and vert lines on px bounds.
141                 gl_Position = vec4(devcoord.xy * sk_RTAdjust.xz + sk_RTAdjust.yw, 0.0, 1.0);
142 
143                 float i = 0.0;
144                 if (gl_TessCoord.y == 0.0) {
145                     i += gl_TessCoord.z * P[1].z;
146                 } else {
147                     i += P[1].z;
148                     if (gl_TessCoord.x == 0.0) {
149                         i += gl_TessCoord.y * P[0].z;
150                     } else {
151                         i += P[0].z;
152                         if (gl_TessCoord.z == 0.0) {
153                             i += gl_TessCoord.x * P[2].z;
154                         } else {
155                             barycentric_coord = vec3(0, 1, 0);
156                             return;
157                         }
158                     }
159                 }
160                 i = abs(mod(i, 2.0) - 1.0);
161                 barycentric_coord = vec3(i, 0, 1.0 - i);
162             })");
163 
164     return code;
165 }
166 
writeFragmentShader(GrGLSLFPFragmentBuilder * f,const char * color,const char * coverage)167 void TessellationTestTriShader::Impl::writeFragmentShader(
168         GrGLSLFPFragmentBuilder* f, const char* color, const char* coverage) {
169     f->declareGlobal(
170             GrShaderVar("barycentric_coord", kFloat3_GrSLType, GrShaderVar::TypeModifier::In));
171     f->codeAppendf(R"(
172             half3 d = half3(1 - barycentric_coord/fwidth(barycentric_coord));
173             half coverage = max(max(d.x, d.y), d.z);
174             %s = half4(0, coverage, coverage, 1);
175             %s = half4(1);)", color, coverage);
176 }
177 
178 class TessellationTestRectShader : public GrGeometryProcessor {
179 public:
TessellationTestRectShader(const SkMatrix & viewMatrix)180     TessellationTestRectShader(const SkMatrix& viewMatrix)
181             : GrGeometryProcessor(kTessellationTestTriShader_ClassID), fViewMatrix(viewMatrix) {
182         this->setWillUseTessellationShaders();
183     }
184 
185 private:
name() const186     const char* name() const final { return "TessellationTestRectShader"; }
getGLSLProcessorKey(const GrShaderCaps &,GrProcessorKeyBuilder * b) const187     void getGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder* b) const final {}
188 
189     class Impl : public GrGLSLGeometryProcessor {
onEmitCode(EmitArgs & args,GrGPArgs * gpArgs)190         void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
191             const char* viewMatrix;
192             fViewMatrixUniform = args.fUniformHandler->addUniform(
193                     nullptr, kVertex_GrShaderFlag, kFloat3x3_GrSLType, "view_matrix", &viewMatrix);
194             args.fVertBuilder->declareGlobal(
195                     GrShaderVar("M_", kFloat3x3_GrSLType, GrShaderVar::TypeModifier::Out));
196             args.fVertBuilder->codeAppendf("M_ = %s;", viewMatrix);
197             // GrGLProgramBuilder will call writeTess*ShaderGLSL when it is compiling.
198             this->writeFragmentShader(args.fFragBuilder, args.fOutputColor, args.fOutputCoverage);
199         }
200         void writeFragmentShader(GrGLSLFPFragmentBuilder*, const char* color, const char* coverage);
setData(const GrGLSLProgramDataManager & pdman,const GrPrimitiveProcessor & proc)201         void setData(const GrGLSLProgramDataManager& pdman,
202                      const GrPrimitiveProcessor& proc) override {
203             pdman.setSkMatrix(fViewMatrixUniform,
204                               proc.cast<TessellationTestRectShader>().fViewMatrix);
205         }
206         GrGLSLUniformHandler::UniformHandle fViewMatrixUniform;
207     };
208 
createGLSLInstance(const GrShaderCaps &) const209     GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override {
210         return new Impl;
211     }
212 
213     SkString getTessControlShaderGLSL(const GrGLSLPrimitiveProcessor*,
214                                       const char* versionAndExtensionDecls,
215                                       const GrGLSLUniformHandler&,
216                                       const GrShaderCaps&) const override;
217     SkString getTessEvaluationShaderGLSL(const GrGLSLPrimitiveProcessor*,
218                                          const char* versionAndExtensionDecls,
219                                          const GrGLSLUniformHandler&,
220                                          const GrShaderCaps&) const override;
221 
222     const SkMatrix fViewMatrix;
223 };
224 
getTessControlShaderGLSL(const GrGLSLPrimitiveProcessor *,const char * versionAndExtensionDecls,const GrGLSLUniformHandler &,const GrShaderCaps & caps) const225 SkString TessellationTestRectShader::getTessControlShaderGLSL(
226         const GrGLSLPrimitiveProcessor*, const char* versionAndExtensionDecls,
227         const GrGLSLUniformHandler&, const GrShaderCaps& caps) const {
228     SkString code(versionAndExtensionDecls);
229     code.append(R"(
230             layout(vertices = 1) out;
231 
232             in mat3 M_[];
233             out mat3 M[];
234 
235             void main() {
236                 M[gl_InvocationID] = M_[gl_InvocationID];
237                 gl_TessLevelInner[0] = 8.0;
238                 gl_TessLevelInner[1] = 2.0;
239                 gl_TessLevelOuter[0] = 2.0;
240                 gl_TessLevelOuter[1] = 8.0;
241                 gl_TessLevelOuter[2] = 2.0;
242                 gl_TessLevelOuter[3] = 8.0;
243             })");
244 
245     return code;
246 }
247 
getTessEvaluationShaderGLSL(const GrGLSLPrimitiveProcessor *,const char * versionAndExtensionDecls,const GrGLSLUniformHandler &,const GrShaderCaps & caps) const248 SkString TessellationTestRectShader::getTessEvaluationShaderGLSL(
249         const GrGLSLPrimitiveProcessor*, const char* versionAndExtensionDecls,
250         const GrGLSLUniformHandler&, const GrShaderCaps& caps) const {
251     SkString code(versionAndExtensionDecls);
252     code.appendf(R"(
253             layout(quads, equal_spacing, cw) in;
254 
255             uniform vec4 sk_RTAdjust;
256 
257             in mat3 M[];
258             out vec4 barycentric_coord;
259 
260             void main() {
261                 vec4 R = vec4(%f, %f, %f, %f);
262                 vec2 localcoord = mix(R.xy, R.zw, gl_TessCoord.xy);
263                 vec2 devcoord = (M[0] * vec3(localcoord, 1)).xy;
264                 devcoord = round(devcoord - .5) + .5;  // Make horz and vert lines on px bounds.
265                 gl_Position = vec4(devcoord.xy * sk_RTAdjust.xz + sk_RTAdjust.yw, 0.0, 1.0);
266 
267                 float i = gl_TessCoord.x * 8.0;
268                 i = abs(mod(i, 2.0) - 1.0);
269                 if (gl_TessCoord.y == 0.0 || gl_TessCoord.y == 1.0) {
270                     barycentric_coord = vec4(i, 1.0 - i, 0, 0);
271                 } else {
272                     barycentric_coord = vec4(0, 0, i, 1.0 - i);
273                 }
274             })", kRect.left(), kRect.top(), kRect.right(), kRect.bottom());
275 
276     return code;
277 }
278 
writeFragmentShader(GrGLSLFPFragmentBuilder * f,const char * color,const char * coverage)279 void TessellationTestRectShader::Impl::writeFragmentShader(
280         GrGLSLFPFragmentBuilder* f, const char* color, const char* coverage) {
281     f->declareGlobal(GrShaderVar("barycentric_coord", kFloat4_GrSLType,
282                                  GrShaderVar::TypeModifier::In));
283     f->codeAppendf(R"(
284             float4 fwidths = fwidth(barycentric_coord);
285             half coverage = 0;
286             for (int i = 0; i < 4; ++i) {
287                 if (fwidths[i] != 0) {
288                     coverage = half(max(coverage, 1 - barycentric_coord[i]/fwidths[i]));
289                 }
290             }
291             %s = half4(coverage, 0, coverage, 1);
292             %s = half4(1);)", color, coverage);
293 }
294 
295 
296 class TessellationTestOp : public GrDrawOp {
297     DEFINE_OP_CLASS_ID
298 
299 public:
TessellationTestOp(const SkMatrix & viewMatrix,const std::array<float,3> * triPositions)300     TessellationTestOp(const SkMatrix& viewMatrix, const std::array<float, 3>* triPositions)
301             : GrDrawOp(ClassID()), fViewMatrix(viewMatrix), fTriPositions(triPositions) {
302         this->setBounds(SkRect::MakeIWH(kWidth, kHeight), HasAABloat::kNo, IsHairline::kNo);
303     }
304 
305 private:
name() const306     const char* name() const override { return "TessellationTestOp"; }
fixedFunctionFlags() const307     FixedFunctionFlags fixedFunctionFlags() const override { return FixedFunctionFlags::kNone; }
finalize(const GrCaps &,const GrAppliedClip *,bool hasMixedSampledCoverage,GrClampType)308     GrProcessorSet::Analysis finalize(const GrCaps&, const GrAppliedClip*,
309                                       bool hasMixedSampledCoverage, GrClampType) override {
310         return GrProcessorSet::EmptySetAnalysis();
311     }
312 
onPrePrepare(GrRecordingContext *,const GrSurfaceProxyView * writeView,GrAppliedClip *,const GrXferProcessor::DstProxyView &,GrXferBarrierFlags renderPassXferBarriers)313     void onPrePrepare(GrRecordingContext*,
314                       const GrSurfaceProxyView* writeView,
315                       GrAppliedClip*,
316                       const GrXferProcessor::DstProxyView&,
317                       GrXferBarrierFlags renderPassXferBarriers) override {}
318 
onPrepare(GrOpFlushState * flushState)319     void onPrepare(GrOpFlushState* flushState) override {
320         if (fTriPositions) {
321             if (void* vertexData = flushState->makeVertexSpace(sizeof(float) * 3, 3, &fVertexBuffer,
322                                                                &fBaseVertex)) {
323                 memcpy(vertexData, fTriPositions, sizeof(float) * 3 * 3);
324             }
325         }
326     }
327 
onExecute(GrOpFlushState * state,const SkRect & chainBounds)328     void onExecute(GrOpFlushState* state, const SkRect& chainBounds) override {
329         GrPipeline pipeline(GrScissorTest::kDisabled, SkBlendMode::kSrc,
330                             state->drawOpArgs().writeSwizzle());
331         int tessellationPatchVertexCount;
332         std::unique_ptr<GrGeometryProcessor> shader;
333         if (fTriPositions) {
334             if (!fVertexBuffer) {
335                 return;
336             }
337             tessellationPatchVertexCount = 3;
338             shader = std::make_unique<TessellationTestTriShader>(fViewMatrix);
339         } else {
340             // Use a mismatched number of vertices in the input patch vs output.
341             // (The tessellation control shader will output one vertex per patch.)
342             tessellationPatchVertexCount = 5;
343             shader = std::make_unique<TessellationTestRectShader>(fViewMatrix);
344         }
345 
346         GrProgramInfo programInfo(state->proxy()->numSamples(), state->proxy()->numStencilSamples(),
347                                   state->proxy()->backendFormat(), state->writeView()->origin(),
348                                   &pipeline, &GrUserStencilSettings::kUnused, shader.get(),
349                                   GrPrimitiveType::kPatches, tessellationPatchVertexCount,
350                                   state->renderPassBarriers());
351 
352         state->bindPipeline(programInfo, SkRect::MakeIWH(kWidth, kHeight));
353         state->bindBuffers(nullptr, nullptr, std::move(fVertexBuffer));
354         state->draw(tessellationPatchVertexCount, fBaseVertex);
355     }
356 
357     const SkMatrix fViewMatrix;
358     const std::array<float, 3>* const fTriPositions;
359     sk_sp<const GrBuffer> fVertexBuffer;
360     int fBaseVertex = 0;
361 };
362 
363 
build_outset_triangle(const std::array<float,3> * tri)364 static SkPath build_outset_triangle(const std::array<float, 3>* tri) {
365     SkPath outset;
366     for (int i = 0; i < 3; ++i) {
367         SkPoint p = {tri[i][0], tri[i][1]};
368         SkPoint left = {tri[(i + 2) % 3][0], tri[(i + 2) % 3][1]};
369         SkPoint right = {tri[(i + 1) % 3][0], tri[(i + 1) % 3][1]};
370         SkPoint n0, n1;
371         n0.setNormalize(left.y() - p.y(), p.x() - left.x());
372         n1.setNormalize(p.y() - right.y(), right.x() - p.x());
373         p += (n0 + n1) * 3;
374         if (0 == i) {
375             outset.moveTo(p);
376         } else {
377             outset.lineTo(p);
378         }
379     }
380     return outset;
381 }
382 
onDraw(GrRecordingContext * ctx,GrRenderTargetContext * rtc,SkCanvas * canvas,SkString * errorMsg)383 DrawResult TessellationGM::onDraw(GrRecordingContext* ctx, GrRenderTargetContext* rtc,
384                                   SkCanvas* canvas, SkString* errorMsg) {
385     if (!ctx->priv().caps()->shaderCaps()->tessellationSupport()) {
386         *errorMsg = "Requires GPU tessellation support.";
387         return DrawResult::kSkip;
388     }
389     if (!ctx->priv().caps()->shaderCaps()->shaderDerivativeSupport()) {
390         *errorMsg = "Requires shader derivatives."
391                     "(These are expected to always be present when there is tessellation!!)";
392         return DrawResult::kFail;
393     }
394 
395     canvas->clear(SK_ColorBLACK);
396     SkPaint borderPaint;
397     borderPaint.setColor4f({0,1,1,1});
398     borderPaint.setAntiAlias(true);
399     canvas->drawPath(build_outset_triangle(kTri1), borderPaint);
400     canvas->drawPath(build_outset_triangle(kTri2), borderPaint);
401 
402     borderPaint.setColor4f({1,0,1,1});
403     canvas->drawRect(kRect.makeOutset(1.5f, 1.5f), borderPaint);
404 
405     rtc->priv().testingOnly_addDrawOp(
406             GrOp::Make<TessellationTestOp>(ctx, canvas->getTotalMatrix(), kTri1));
407     rtc->priv().testingOnly_addDrawOp(
408             GrOp::Make<TessellationTestOp>(ctx, canvas->getTotalMatrix(), kTri2));
409     rtc->priv().testingOnly_addDrawOp(
410             GrOp::Make<TessellationTestOp>(ctx, canvas->getTotalMatrix(), nullptr));
411 
412     return skiagm::DrawResult::kOk;
413 }
414 
415 DEF_GM( return new TessellationGM(); )
416 
417 }  // namespace skiagm
418