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