blob: a2a3d48399abc6ca20032d676c705cae14f06cbb [file] [log] [blame]
Chris Dalton5a2f9622019-12-27 14:56:38 -07001/*
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"
Adlai Hollera0693042020-10-14 11:23:11 -040011#include "src/gpu/GrDirectContextPriv.h"
Robert Phillips787fd9d2021-03-22 14:48:09 -040012#include "src/gpu/GrGeometryProcessor.h"
Chris Dalton5a2f9622019-12-27 14:56:38 -070013#include "src/gpu/GrMemoryPool.h"
Chris Dalton5a2f9622019-12-27 14:56:38 -070014#include "src/gpu/GrOpFlushState.h"
15#include "src/gpu/GrOpsRenderPass.h"
16#include "src/gpu/GrPipeline.h"
Chris Dalton5a2f9622019-12-27 14:56:38 -070017#include "src/gpu/GrProgramInfo.h"
18#include "src/gpu/GrRecordingContextPriv.h"
Chris Dalton5a2f9622019-12-27 14:56:38 -070019#include "src/gpu/GrShaderCaps.h"
20#include "src/gpu/GrShaderVar.h"
Brian Salomoneebe7352020-12-09 16:37:04 -050021#include "src/gpu/GrSurfaceDrawContext.h"
Chris Dalton5a2f9622019-12-27 14:56:38 -070022#include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h"
23#include "src/gpu/glsl/GrGLSLGeometryProcessor.h"
24#include "src/gpu/glsl/GrGLSLPrimitiveProcessor.h"
25#include "src/gpu/glsl/GrGLSLVarying.h"
26#include "src/gpu/glsl/GrGLSLVertexGeoBuilder.h"
27#include "src/gpu/ops/GrDrawOp.h"
28
29namespace skiagm {
30
31constexpr static GrGeometryProcessor::Attribute kPositionAttrib =
32 {"position", kFloat3_GrVertexAttribType, kFloat3_GrSLType};
33
34constexpr static std::array<float, 3> kTri1[3] = {
35 {20.5f,20.5f,1}, {170.5f,280.5f,4}, {320.5f,20.5f,1}};
36constexpr static std::array<float, 3> kTri2[3] = {
37 {640.5f,280.5f,3}, {490.5f,20.5f,1}, {340.5f,280.5f,6}};
38constexpr static SkRect kRect = {20.5f, 340.5f, 640.5f, 480.5f};
39
40constexpr static int kWidth = (int)kRect.fRight + 21;
41constexpr static int kHeight = (int)kRect.fBottom + 21;
42
43/**
44 * This is a GPU-backend specific test. It ensures that tessellation works as expected by drawing
45 * several triangles. The test passes as long as the triangle tessellations match the reference
46 * images on gold.
47 */
48class TessellationGM : public GpuGM {
49 SkString onShortName() override { return SkString("tessellation"); }
50 SkISize onISize() override { return {kWidth, kHeight}; }
Brian Salomoneebe7352020-12-09 16:37:04 -050051 DrawResult onDraw(GrRecordingContext*, GrSurfaceDrawContext*, SkCanvas*, SkString*) override;
Chris Dalton5a2f9622019-12-27 14:56:38 -070052};
53
54
55class TessellationTestTriShader : public GrGeometryProcessor {
56public:
57 TessellationTestTriShader(const SkMatrix& viewMatrix)
58 : GrGeometryProcessor(kTessellationTestTriShader_ClassID), fViewMatrix(viewMatrix) {
59 this->setVertexAttributes(&kPositionAttrib, 1);
60 this->setWillUseTessellationShaders();
61 }
62
Robert Phillips709e2402020-03-23 18:29:16 +000063private:
64 const char* name() const final { return "TessellationTestTriShader"; }
65 void getGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder* b) const final {}
66
Chris Dalton5a2f9622019-12-27 14:56:38 -070067 class Impl : public GrGLSLGeometryProcessor {
68 void onEmitCode(EmitArgs& args, GrGPArgs*) override {
Robert Phillips787fd9d2021-03-22 14:48:09 -040069 args.fVaryingHandler->emitAttributes(args.fGeomProc.cast<TessellationTestTriShader>());
Chris Dalton5a2f9622019-12-27 14:56:38 -070070 const char* viewMatrix;
71 fViewMatrixUniform = args.fUniformHandler->addUniform(
Ethan Nicholas16464c32020-04-06 13:53:05 -040072 nullptr, kVertex_GrShaderFlag, kFloat3x3_GrSLType, "view_matrix", &viewMatrix);
Chris Dalton5a2f9622019-12-27 14:56:38 -070073 args.fVertBuilder->declareGlobal(
Ben Wagnerdabd98c2020-03-25 15:17:18 -040074 GrShaderVar("P_", kFloat3_GrSLType, GrShaderVar::TypeModifier::Out));
Chris Dalton5a2f9622019-12-27 14:56:38 -070075 args.fVertBuilder->codeAppendf(R"(
76 P_.xy = (%s * float3(position.xy, 1)).xy;
77 P_.z = position.z;)", viewMatrix);
78 // GrGLProgramBuilder will call writeTess*ShaderGLSL when it is compiling.
79 this->writeFragmentShader(args.fFragBuilder, args.fOutputColor, args.fOutputCoverage);
80 }
81 void writeFragmentShader(GrGLSLFPFragmentBuilder*, const char* color, const char* coverage);
Brian Osman609f1592020-07-01 15:14:39 -040082 void setData(const GrGLSLProgramDataManager& pdman,
Robert Phillips787fd9d2021-03-22 14:48:09 -040083 const GrGeometryProcessor& geomProc) override {
Chris Dalton5a2f9622019-12-27 14:56:38 -070084 pdman.setSkMatrix(fViewMatrixUniform,
Robert Phillips787fd9d2021-03-22 14:48:09 -040085 geomProc.cast<TessellationTestTriShader>().fViewMatrix);
Chris Dalton5a2f9622019-12-27 14:56:38 -070086 }
87 GrGLSLUniformHandler::UniformHandle fViewMatrixUniform;
88 };
89
90 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override {
91 return new Impl;
92 }
93
Chris Dalton54c90932020-06-24 10:51:52 -060094 SkString getTessControlShaderGLSL(const GrGLSLPrimitiveProcessor*,
95 const char* versionAndExtensionDecls,
96 const GrGLSLUniformHandler&,
Chris Dalton5a2f9622019-12-27 14:56:38 -070097 const GrShaderCaps&) const override;
Chris Dalton54c90932020-06-24 10:51:52 -060098 SkString getTessEvaluationShaderGLSL(const GrGLSLPrimitiveProcessor*,
99 const char* versionAndExtensionDecls,
100 const GrGLSLUniformHandler&,
Chris Dalton5a2f9622019-12-27 14:56:38 -0700101 const GrShaderCaps&) const override;
102
103 const SkMatrix fViewMatrix;
104};
105
106SkString TessellationTestTriShader::getTessControlShaderGLSL(
Chris Dalton54c90932020-06-24 10:51:52 -0600107 const GrGLSLPrimitiveProcessor*, const char* versionAndExtensionDecls,
108 const GrGLSLUniformHandler&, const GrShaderCaps&) const {
Chris Dalton5a2f9622019-12-27 14:56:38 -0700109 SkString code(versionAndExtensionDecls);
110 code.append(R"(
111 layout(vertices = 3) out;
112
113 in vec3 P_[];
114 out vec3 P[];
115
116 void main() {
117 P[gl_InvocationID] = P_[gl_InvocationID];
118 gl_TessLevelOuter[gl_InvocationID] = P_[gl_InvocationID].z;
119 gl_TessLevelInner[0] = 2.0;
120 })");
121
122 return code;
123}
124
125SkString TessellationTestTriShader::getTessEvaluationShaderGLSL(
Chris Dalton54c90932020-06-24 10:51:52 -0600126 const GrGLSLPrimitiveProcessor*, const char* versionAndExtensionDecls,
127 const GrGLSLUniformHandler&, const GrShaderCaps&) const {
Chris Dalton5a2f9622019-12-27 14:56:38 -0700128 SkString code(versionAndExtensionDecls);
129 code.append(R"(
130 layout(triangles, equal_spacing, cw) in;
131
132 uniform vec4 sk_RTAdjust;
133
134 in vec3 P[];
135 out vec3 barycentric_coord;
136
137 void main() {
138 vec2 devcoord = mat3x2(P[0].xy, P[1].xy, P[2].xy) * gl_TessCoord.xyz;
139 devcoord = round(devcoord - .5) + .5; // Make horz and vert lines on px bounds.
140 gl_Position = vec4(devcoord.xy * sk_RTAdjust.xz + sk_RTAdjust.yw, 0.0, 1.0);
141
142 float i = 0.0;
143 if (gl_TessCoord.y == 0.0) {
144 i += gl_TessCoord.z * P[1].z;
145 } else {
146 i += P[1].z;
147 if (gl_TessCoord.x == 0.0) {
148 i += gl_TessCoord.y * P[0].z;
149 } else {
150 i += P[0].z;
Chris Dalton8dae7eb2019-12-27 22:20:55 -0700151 if (gl_TessCoord.z == 0.0) {
152 i += gl_TessCoord.x * P[2].z;
153 } else {
154 barycentric_coord = vec3(0, 1, 0);
155 return;
156 }
Chris Dalton5a2f9622019-12-27 14:56:38 -0700157 }
158 }
Chris Dalton8dae7eb2019-12-27 22:20:55 -0700159 i = abs(mod(i, 2.0) - 1.0);
160 barycentric_coord = vec3(i, 0, 1.0 - i);
161 })");
Chris Dalton5a2f9622019-12-27 14:56:38 -0700162
163 return code;
164}
165
166void TessellationTestTriShader::Impl::writeFragmentShader(
167 GrGLSLFPFragmentBuilder* f, const char* color, const char* coverage) {
168 f->declareGlobal(
Ben Wagnerdabd98c2020-03-25 15:17:18 -0400169 GrShaderVar("barycentric_coord", kFloat3_GrSLType, GrShaderVar::TypeModifier::In));
Chris Dalton5a2f9622019-12-27 14:56:38 -0700170 f->codeAppendf(R"(
171 half3 d = half3(1 - barycentric_coord/fwidth(barycentric_coord));
172 half coverage = max(max(d.x, d.y), d.z);
John Stiles4d7ac492021-03-09 20:16:43 -0500173 half4 %s = half4(0, coverage, coverage, 1);
174 const half4 %s = half4(1);)", color, coverage);
Chris Dalton5a2f9622019-12-27 14:56:38 -0700175}
176
177class TessellationTestRectShader : public GrGeometryProcessor {
178public:
179 TessellationTestRectShader(const SkMatrix& viewMatrix)
180 : GrGeometryProcessor(kTessellationTestTriShader_ClassID), fViewMatrix(viewMatrix) {
181 this->setWillUseTessellationShaders();
182 }
183
Robert Phillips709e2402020-03-23 18:29:16 +0000184private:
185 const char* name() const final { return "TessellationTestRectShader"; }
186 void getGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder* b) const final {}
187
Chris Dalton5a2f9622019-12-27 14:56:38 -0700188 class Impl : public GrGLSLGeometryProcessor {
189 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
190 const char* viewMatrix;
191 fViewMatrixUniform = args.fUniformHandler->addUniform(
Ethan Nicholas16464c32020-04-06 13:53:05 -0400192 nullptr, kVertex_GrShaderFlag, kFloat3x3_GrSLType, "view_matrix", &viewMatrix);
Chris Dalton5a2f9622019-12-27 14:56:38 -0700193 args.fVertBuilder->declareGlobal(
Ben Wagnerdabd98c2020-03-25 15:17:18 -0400194 GrShaderVar("M_", kFloat3x3_GrSLType, GrShaderVar::TypeModifier::Out));
Chris Dalton5a2f9622019-12-27 14:56:38 -0700195 args.fVertBuilder->codeAppendf("M_ = %s;", viewMatrix);
196 // GrGLProgramBuilder will call writeTess*ShaderGLSL when it is compiling.
197 this->writeFragmentShader(args.fFragBuilder, args.fOutputColor, args.fOutputCoverage);
198 }
199 void writeFragmentShader(GrGLSLFPFragmentBuilder*, const char* color, const char* coverage);
Brian Osman609f1592020-07-01 15:14:39 -0400200 void setData(const GrGLSLProgramDataManager& pdman,
Robert Phillips787fd9d2021-03-22 14:48:09 -0400201 const GrGeometryProcessor& geomProc) override {
Chris Dalton5a2f9622019-12-27 14:56:38 -0700202 pdman.setSkMatrix(fViewMatrixUniform,
Robert Phillips787fd9d2021-03-22 14:48:09 -0400203 geomProc.cast<TessellationTestRectShader>().fViewMatrix);
Chris Dalton5a2f9622019-12-27 14:56:38 -0700204 }
205 GrGLSLUniformHandler::UniformHandle fViewMatrixUniform;
206 };
207
208 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override {
209 return new Impl;
210 }
211
Chris Dalton54c90932020-06-24 10:51:52 -0600212 SkString getTessControlShaderGLSL(const GrGLSLPrimitiveProcessor*,
213 const char* versionAndExtensionDecls,
214 const GrGLSLUniformHandler&,
Chris Dalton5a2f9622019-12-27 14:56:38 -0700215 const GrShaderCaps&) const override;
Chris Dalton54c90932020-06-24 10:51:52 -0600216 SkString getTessEvaluationShaderGLSL(const GrGLSLPrimitiveProcessor*,
217 const char* versionAndExtensionDecls,
218 const GrGLSLUniformHandler&,
Chris Dalton5a2f9622019-12-27 14:56:38 -0700219 const GrShaderCaps&) const override;
220
221 const SkMatrix fViewMatrix;
222};
223
224SkString TessellationTestRectShader::getTessControlShaderGLSL(
Chris Dalton54c90932020-06-24 10:51:52 -0600225 const GrGLSLPrimitiveProcessor*, const char* versionAndExtensionDecls,
226 const GrGLSLUniformHandler&, const GrShaderCaps& caps) const {
Chris Dalton5a2f9622019-12-27 14:56:38 -0700227 SkString code(versionAndExtensionDecls);
228 code.append(R"(
229 layout(vertices = 1) out;
230
231 in mat3 M_[];
232 out mat3 M[];
233
234 void main() {
235 M[gl_InvocationID] = M_[gl_InvocationID];
236 gl_TessLevelInner[0] = 8.0;
237 gl_TessLevelInner[1] = 2.0;
238 gl_TessLevelOuter[0] = 2.0;
239 gl_TessLevelOuter[1] = 8.0;
240 gl_TessLevelOuter[2] = 2.0;
241 gl_TessLevelOuter[3] = 8.0;
242 })");
243
244 return code;
245}
246
247SkString TessellationTestRectShader::getTessEvaluationShaderGLSL(
Chris Dalton54c90932020-06-24 10:51:52 -0600248 const GrGLSLPrimitiveProcessor*, const char* versionAndExtensionDecls,
249 const GrGLSLUniformHandler&, const GrShaderCaps& caps) const {
Chris Dalton5a2f9622019-12-27 14:56:38 -0700250 SkString code(versionAndExtensionDecls);
251 code.appendf(R"(
252 layout(quads, equal_spacing, cw) in;
253
254 uniform vec4 sk_RTAdjust;
255
256 in mat3 M[];
257 out vec4 barycentric_coord;
258
259 void main() {
260 vec4 R = vec4(%f, %f, %f, %f);
261 vec2 localcoord = mix(R.xy, R.zw, gl_TessCoord.xy);
262 vec2 devcoord = (M[0] * vec3(localcoord, 1)).xy;
263 devcoord = round(devcoord - .5) + .5; // Make horz and vert lines on px bounds.
264 gl_Position = vec4(devcoord.xy * sk_RTAdjust.xz + sk_RTAdjust.yw, 0.0, 1.0);
265
266 float i = gl_TessCoord.x * 8.0;
267 i = abs(mod(i, 2.0) - 1.0);
268 if (gl_TessCoord.y == 0.0 || gl_TessCoord.y == 1.0) {
269 barycentric_coord = vec4(i, 1.0 - i, 0, 0);
270 } else {
271 barycentric_coord = vec4(0, 0, i, 1.0 - i);
272 }
273 })", kRect.left(), kRect.top(), kRect.right(), kRect.bottom());
274
275 return code;
276}
277
278void TessellationTestRectShader::Impl::writeFragmentShader(
279 GrGLSLFPFragmentBuilder* f, const char* color, const char* coverage) {
280 f->declareGlobal(GrShaderVar("barycentric_coord", kFloat4_GrSLType,
Ben Wagnerdabd98c2020-03-25 15:17:18 -0400281 GrShaderVar::TypeModifier::In));
Chris Dalton5a2f9622019-12-27 14:56:38 -0700282 f->codeAppendf(R"(
283 float4 fwidths = fwidth(barycentric_coord);
284 half coverage = 0;
285 for (int i = 0; i < 4; ++i) {
286 if (fwidths[i] != 0) {
287 coverage = half(max(coverage, 1 - barycentric_coord[i]/fwidths[i]));
288 }
289 }
John Stiles9c4cdef2021-03-10 12:01:10 -0500290 half4 %s = half4(coverage, 0, coverage, 1);
291 const half4 %s = half4(1);)", color, coverage);
Chris Dalton5a2f9622019-12-27 14:56:38 -0700292}
293
294
295class TessellationTestOp : public GrDrawOp {
296 DEFINE_OP_CLASS_ID
297
298public:
299 TessellationTestOp(const SkMatrix& viewMatrix, const std::array<float, 3>* triPositions)
Robert Phillips709e2402020-03-23 18:29:16 +0000300 : GrDrawOp(ClassID()), fViewMatrix(viewMatrix), fTriPositions(triPositions) {
Chris Dalton5a2f9622019-12-27 14:56:38 -0700301 this->setBounds(SkRect::MakeIWH(kWidth, kHeight), HasAABloat::kNo, IsHairline::kNo);
302 }
303
304private:
305 const char* name() const override { return "TessellationTestOp"; }
306 FixedFunctionFlags fixedFunctionFlags() const override { return FixedFunctionFlags::kNone; }
Robert Phillips709e2402020-03-23 18:29:16 +0000307 GrProcessorSet::Analysis finalize(const GrCaps&, const GrAppliedClip*,
308 bool hasMixedSampledCoverage, GrClampType) override {
309 return GrProcessorSet::EmptySetAnalysis();
Chris Dalton5a2f9622019-12-27 14:56:38 -0700310 }
311
Robert Phillips709e2402020-03-23 18:29:16 +0000312 void onPrePrepare(GrRecordingContext*,
Adlai Hollere2296f72020-11-19 13:41:26 -0500313 const GrSurfaceProxyView& writeView,
Robert Phillips709e2402020-03-23 18:29:16 +0000314 GrAppliedClip*,
Greg Danield358cbe2020-09-11 09:33:54 -0400315 const GrXferProcessor::DstProxyView&,
Greg Daniel42dbca52020-11-20 10:22:43 -0500316 GrXferBarrierFlags renderPassXferBarriers,
317 GrLoadOp colorLoadOp) override {}
Robert Phillipsc655c3a2020-03-18 13:23:45 -0400318
Chris Dalton5a2f9622019-12-27 14:56:38 -0700319 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
328 void onExecute(GrOpFlushState* state, const SkRect& chainBounds) override {
Robert Phillips709e2402020-03-23 18:29:16 +0000329 GrPipeline pipeline(GrScissorTest::kDisabled, SkBlendMode::kSrc,
Adlai Hollere2296f72020-11-19 13:41:26 -0500330 state->drawOpArgs().writeView().swizzle());
Robert Phillips709e2402020-03-23 18:29:16 +0000331 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);
Chris Dalton5a2f9622019-12-27 14:56:38 -0700344 }
345
Robert Phillips5c809642020-11-20 12:28:45 -0500346 GrProgramInfo programInfo(state->writeView(), &pipeline, &GrUserStencilSettings::kUnused,
347 shader.get(), GrPrimitiveType::kPatches,
348 tessellationPatchVertexCount, state->renderPassBarriers(),
349 state->colorLoadOp());
Chris Dalton5a2f9622019-12-27 14:56:38 -0700350
Robert Phillips709e2402020-03-23 18:29:16 +0000351 state->bindPipeline(programInfo, SkRect::MakeIWH(kWidth, kHeight));
Greg Daniel426274b2020-07-20 11:37:38 -0400352 state->bindBuffers(nullptr, nullptr, std::move(fVertexBuffer));
Robert Phillips709e2402020-03-23 18:29:16 +0000353 state->draw(tessellationPatchVertexCount, fBaseVertex);
Chris Dalton5a2f9622019-12-27 14:56:38 -0700354 }
355
356 const SkMatrix fViewMatrix;
357 const std::array<float, 3>* const fTriPositions;
358 sk_sp<const GrBuffer> fVertexBuffer;
359 int fBaseVertex = 0;
Chris Dalton5a2f9622019-12-27 14:56:38 -0700360};
361
362
363static SkPath build_outset_triangle(const std::array<float, 3>* tri) {
364 SkPath outset;
365 for (int i = 0; i < 3; ++i) {
366 SkPoint p = {tri[i][0], tri[i][1]};
367 SkPoint left = {tri[(i + 2) % 3][0], tri[(i + 2) % 3][1]};
368 SkPoint right = {tri[(i + 1) % 3][0], tri[(i + 1) % 3][1]};
369 SkPoint n0, n1;
370 n0.setNormalize(left.y() - p.y(), p.x() - left.x());
371 n1.setNormalize(p.y() - right.y(), right.x() - p.x());
372 p += (n0 + n1) * 3;
373 if (0 == i) {
374 outset.moveTo(p);
375 } else {
376 outset.lineTo(p);
377 }
378 }
379 return outset;
380}
381
Brian Salomoneebe7352020-12-09 16:37:04 -0500382DrawResult TessellationGM::onDraw(GrRecordingContext* ctx, GrSurfaceDrawContext* rtc,
Robert Phillips95c250c2020-06-29 15:36:12 -0400383 SkCanvas* canvas, SkString* errorMsg) {
Chris Dalton5a2f9622019-12-27 14:56:38 -0700384 if (!ctx->priv().caps()->shaderCaps()->tessellationSupport()) {
385 *errorMsg = "Requires GPU tessellation support.";
386 return DrawResult::kSkip;
387 }
388 if (!ctx->priv().caps()->shaderCaps()->shaderDerivativeSupport()) {
389 *errorMsg = "Requires shader derivatives."
390 "(These are expected to always be present when there is tessellation!!)";
391 return DrawResult::kFail;
392 }
393
394 canvas->clear(SK_ColorBLACK);
395 SkPaint borderPaint;
396 borderPaint.setColor4f({0,1,1,1});
397 borderPaint.setAntiAlias(true);
398 canvas->drawPath(build_outset_triangle(kTri1), borderPaint);
399 canvas->drawPath(build_outset_triangle(kTri2), borderPaint);
400
401 borderPaint.setColor4f({1,0,1,1});
402 canvas->drawRect(kRect.makeOutset(1.5f, 1.5f), borderPaint);
403
Brian Salomon70fe17e2020-11-30 14:33:58 -0500404 rtc->addDrawOp(GrOp::Make<TessellationTestOp>(ctx, canvas->getTotalMatrix(), kTri1));
405 rtc->addDrawOp(GrOp::Make<TessellationTestOp>(ctx, canvas->getTotalMatrix(), kTri2));
406 rtc->addDrawOp(GrOp::Make<TessellationTestOp>(ctx, canvas->getTotalMatrix(), nullptr));
Chris Dalton5a2f9622019-12-27 14:56:38 -0700407
408 return skiagm::DrawResult::kOk;
409}
410
411DEF_GM( return new TessellationGM(); )
412
John Stilesa6841be2020-08-06 14:11:56 -0400413} // namespace skiagm