blob: f7a9c79eac2bbae6376ae9dc8d67f5946c5eeec3 [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
Robert Phillips7a0d3c32021-07-21 15:39:51 -040010#include "src/core/SkCanvasPriv.h"
Chris Dalton5a2f9622019-12-27 14:56:38 -070011#include "src/gpu/GrCaps.h"
Adlai Hollera0693042020-10-14 11:23:11 -040012#include "src/gpu/GrDirectContextPriv.h"
Robert Phillips787fd9d2021-03-22 14:48:09 -040013#include "src/gpu/GrGeometryProcessor.h"
Chris Dalton5a2f9622019-12-27 14:56:38 -070014#include "src/gpu/GrMemoryPool.h"
Chris Dalton5a2f9622019-12-27 14:56:38 -070015#include "src/gpu/GrOpFlushState.h"
16#include "src/gpu/GrOpsRenderPass.h"
17#include "src/gpu/GrPipeline.h"
Chris Dalton5a2f9622019-12-27 14:56:38 -070018#include "src/gpu/GrProgramInfo.h"
19#include "src/gpu/GrRecordingContextPriv.h"
Chris Dalton5a2f9622019-12-27 14:56:38 -070020#include "src/gpu/GrShaderCaps.h"
21#include "src/gpu/GrShaderVar.h"
22#include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h"
Chris Dalton5a2f9622019-12-27 14:56:38 -070023#include "src/gpu/glsl/GrGLSLVarying.h"
24#include "src/gpu/glsl/GrGLSLVertexGeoBuilder.h"
25#include "src/gpu/ops/GrDrawOp.h"
Robert Phillips4dca8312021-07-28 15:13:20 -040026#include "src/gpu/v1/SurfaceDrawContext_v1.h"
Chris Dalton5a2f9622019-12-27 14:56:38 -070027
28namespace skiagm {
29
30constexpr static GrGeometryProcessor::Attribute kPositionAttrib =
31 {"position", kFloat3_GrVertexAttribType, kFloat3_GrSLType};
32
33constexpr static std::array<float, 3> kTri1[3] = {
34 {20.5f,20.5f,1}, {170.5f,280.5f,4}, {320.5f,20.5f,1}};
35constexpr static std::array<float, 3> kTri2[3] = {
36 {640.5f,280.5f,3}, {490.5f,20.5f,1}, {340.5f,280.5f,6}};
37constexpr static SkRect kRect = {20.5f, 340.5f, 640.5f, 480.5f};
38
39constexpr static int kWidth = (int)kRect.fRight + 21;
40constexpr 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 */
47class TessellationGM : public GpuGM {
48 SkString onShortName() override { return SkString("tessellation"); }
49 SkISize onISize() override { return {kWidth, kHeight}; }
Robert Phillips7a0d3c32021-07-21 15:39:51 -040050 DrawResult onDraw(GrRecordingContext*, SkCanvas*, SkString*) override;
Chris Dalton5a2f9622019-12-27 14:56:38 -070051};
52
53
54class TessellationTestTriShader : public GrGeometryProcessor {
55public:
56 TessellationTestTriShader(const SkMatrix& viewMatrix)
57 : GrGeometryProcessor(kTessellationTestTriShader_ClassID), fViewMatrix(viewMatrix) {
58 this->setVertexAttributes(&kPositionAttrib, 1);
59 this->setWillUseTessellationShaders();
60 }
61
Robert Phillips709e2402020-03-23 18:29:16 +000062private:
63 const char* name() const final { return "TessellationTestTriShader"; }
Brian Salomon13b28732021-08-06 15:33:58 -040064 void addToKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const final {}
Robert Phillips709e2402020-03-23 18:29:16 +000065
Brian Salomonf95940b2021-08-09 15:56:24 -040066 class Impl : public ProgramImpl {
Brian Salomonbab2d112021-08-11 09:59:56 -040067 public:
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:
Chris Dalton5a2f9622019-12-27 14:56:38 -070076 void onEmitCode(EmitArgs& args, GrGPArgs*) override {
Robert Phillips787fd9d2021-03-22 14:48:09 -040077 args.fVaryingHandler->emitAttributes(args.fGeomProc.cast<TessellationTestTriShader>());
Chris Dalton5a2f9622019-12-27 14:56:38 -070078 const char* viewMatrix;
79 fViewMatrixUniform = args.fUniformHandler->addUniform(
Ethan Nicholas16464c32020-04-06 13:53:05 -040080 nullptr, kVertex_GrShaderFlag, kFloat3x3_GrSLType, "view_matrix", &viewMatrix);
Chris Dalton5a2f9622019-12-27 14:56:38 -070081 args.fVertBuilder->declareGlobal(
Ben Wagnerdabd98c2020-03-25 15:17:18 -040082 GrShaderVar("P_", kFloat3_GrSLType, GrShaderVar::TypeModifier::Out));
Chris Dalton5a2f9622019-12-27 14:56:38 -070083 args.fVertBuilder->codeAppendf(R"(
Chris Dalton85dbeb52021-05-10 14:10:05 -060084 P_.xy = (%s * float3(position.xy, 1)).xy;
85 P_.z = position.z;)", viewMatrix);
Chris Dalton5a2f9622019-12-27 14:56:38 -070086 // GrGLProgramBuilder will call writeTess*ShaderGLSL when it is compiling.
87 this->writeFragmentShader(args.fFragBuilder, args.fOutputColor, args.fOutputCoverage);
88 }
Brian Salomonbab2d112021-08-11 09:59:56 -040089
Chris Dalton85dbeb52021-05-10 14:10:05 -060090 SkString getTessControlShaderGLSL(const GrGeometryProcessor&,
91 const char* versionAndExtensionDecls,
92 const GrGLSLUniformHandler&,
93 const GrShaderCaps&) const override {
94 SkString code(versionAndExtensionDecls);
95 code.append(R"(
Chris Dalton5a2f9622019-12-27 14:56:38 -070096 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
Chris Dalton85dbeb52021-05-10 14:10:05 -0600107 return code;
108 }
Brian Salomonbab2d112021-08-11 09:59:56 -0400109
Chris Dalton85dbeb52021-05-10 14:10:05 -0600110 SkString getTessEvaluationShaderGLSL(const GrGeometryProcessor&,
111 const char* versionAndExtensionDecls,
112 const GrGLSLUniformHandler&,
113 const GrShaderCaps&) const override {
114 SkString code(versionAndExtensionDecls);
115 code.append(R"(
Chris Dalton5a2f9622019-12-27 14:56:38 -0700116 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;
Chris Dalton8dae7eb2019-12-27 22:20:55 -0700137 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 }
Chris Dalton5a2f9622019-12-27 14:56:38 -0700143 }
144 }
Chris Dalton8dae7eb2019-12-27 22:20:55 -0700145 i = abs(mod(i, 2.0) - 1.0);
146 barycentric_coord = vec3(i, 0, 1.0 - i);
147 })");
Chris Dalton5a2f9622019-12-27 14:56:38 -0700148
Chris Dalton85dbeb52021-05-10 14:10:05 -0600149 return code;
150 }
Brian Salomonbab2d112021-08-11 09:59:56 -0400151
Chris Dalton85dbeb52021-05-10 14:10:05 -0600152 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"(
Chris Dalton5a2f9622019-12-27 14:56:38 -0700157 half3 d = half3(1 - barycentric_coord/fwidth(barycentric_coord));
158 half coverage = max(max(d.x, d.y), d.z);
John Stiles4d7ac492021-03-09 20:16:43 -0500159 half4 %s = half4(0, coverage, coverage, 1);
160 const half4 %s = half4(1);)", color, coverage);
Chris Dalton85dbeb52021-05-10 14:10:05 -0600161 }
Brian Salomonbab2d112021-08-11 09:59:56 -0400162
Chris Dalton85dbeb52021-05-10 14:10:05 -0600163 GrGLSLUniformHandler::UniformHandle fViewMatrixUniform;
164 };
165
Brian Salomonf95940b2021-08-09 15:56:24 -0400166 std::unique_ptr<ProgramImpl> makeProgramImpl(const GrShaderCaps&) const final {
167 return std::make_unique<Impl>();
Chris Dalton85dbeb52021-05-10 14:10:05 -0600168 }
169
170 const SkMatrix fViewMatrix;
171};
Chris Dalton5a2f9622019-12-27 14:56:38 -0700172
173class TessellationTestRectShader : public GrGeometryProcessor {
174public:
175 TessellationTestRectShader(const SkMatrix& viewMatrix)
176 : GrGeometryProcessor(kTessellationTestTriShader_ClassID), fViewMatrix(viewMatrix) {
177 this->setWillUseTessellationShaders();
178 }
179
Robert Phillips709e2402020-03-23 18:29:16 +0000180 const char* name() const final { return "TessellationTestRectShader"; }
Brian Salomonbab2d112021-08-11 09:59:56 -0400181
Brian Salomon13b28732021-08-06 15:33:58 -0400182 void addToKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const final {}
Robert Phillips709e2402020-03-23 18:29:16 +0000183
Brian Salomonbab2d112021-08-11 09:59:56 -0400184private:
Brian Salomonf95940b2021-08-09 15:56:24 -0400185 class Impl : public ProgramImpl {
Brian Salomonbab2d112021-08-11 09:59:56 -0400186 public:
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:
Chris Dalton5a2f9622019-12-27 14:56:38 -0700195 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
196 const char* viewMatrix;
197 fViewMatrixUniform = args.fUniformHandler->addUniform(
Ethan Nicholas16464c32020-04-06 13:53:05 -0400198 nullptr, kVertex_GrShaderFlag, kFloat3x3_GrSLType, "view_matrix", &viewMatrix);
Chris Dalton5a2f9622019-12-27 14:56:38 -0700199 args.fVertBuilder->declareGlobal(
Ben Wagnerdabd98c2020-03-25 15:17:18 -0400200 GrShaderVar("M_", kFloat3x3_GrSLType, GrShaderVar::TypeModifier::Out));
Chris Dalton5a2f9622019-12-27 14:56:38 -0700201 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 }
Brian Salomonbab2d112021-08-11 09:59:56 -0400205
Chris Dalton85dbeb52021-05-10 14:10:05 -0600206 SkString getTessControlShaderGLSL(const GrGeometryProcessor&,
207 const char* versionAndExtensionDecls,
208 const GrGLSLUniformHandler&,
209 const GrShaderCaps&) const override {
210 SkString code(versionAndExtensionDecls);
211 code.append(R"(
Chris Dalton5a2f9622019-12-27 14:56:38 -0700212 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
Chris Dalton85dbeb52021-05-10 14:10:05 -0600227 return code;
228 }
Brian Salomonbab2d112021-08-11 09:59:56 -0400229
Chris Dalton85dbeb52021-05-10 14:10:05 -0600230 SkString getTessEvaluationShaderGLSL(const GrGeometryProcessor&,
231 const char* versionAndExtensionDecls,
232 const GrGLSLUniformHandler&,
233 const GrShaderCaps&) const override {
234 SkString code(versionAndExtensionDecls);
235 code.appendf(R"(
Chris Dalton5a2f9622019-12-27 14:56:38 -0700236 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
Chris Dalton85dbeb52021-05-10 14:10:05 -0600259 return code;
260 }
Brian Salomonbab2d112021-08-11 09:59:56 -0400261
Chris Dalton85dbeb52021-05-10 14:10:05 -0600262 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"(
Chris Dalton5a2f9622019-12-27 14:56:38 -0700267 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 }
John Stiles9c4cdef2021-03-10 12:01:10 -0500274 half4 %s = half4(coverage, 0, coverage, 1);
275 const half4 %s = half4(1);)", color, coverage);
Chris Dalton5a2f9622019-12-27 14:56:38 -0700276
Chris Dalton85dbeb52021-05-10 14:10:05 -0600277 }
Brian Salomonbab2d112021-08-11 09:59:56 -0400278
Chris Dalton85dbeb52021-05-10 14:10:05 -0600279 GrGLSLUniformHandler::UniformHandle fViewMatrixUniform;
280 };
281
Brian Salomonf95940b2021-08-09 15:56:24 -0400282 std::unique_ptr<ProgramImpl> makeProgramImpl(const GrShaderCaps&) const final {
283 return std::make_unique<Impl>();
Chris Dalton85dbeb52021-05-10 14:10:05 -0600284 }
285
286 const SkMatrix fViewMatrix;
287};
Chris Dalton5a2f9622019-12-27 14:56:38 -0700288
289class TessellationTestOp : public GrDrawOp {
290 DEFINE_OP_CLASS_ID
291
292public:
293 TessellationTestOp(const SkMatrix& viewMatrix, const std::array<float, 3>* triPositions)
Robert Phillips709e2402020-03-23 18:29:16 +0000294 : GrDrawOp(ClassID()), fViewMatrix(viewMatrix), fTriPositions(triPositions) {
Chris Dalton5a2f9622019-12-27 14:56:38 -0700295 this->setBounds(SkRect::MakeIWH(kWidth, kHeight), HasAABloat::kNo, IsHairline::kNo);
296 }
297
298private:
299 const char* name() const override { return "TessellationTestOp"; }
300 FixedFunctionFlags fixedFunctionFlags() const override { return FixedFunctionFlags::kNone; }
Chris Dalton57ab06c2021-04-22 12:57:28 -0600301 GrProcessorSet::Analysis finalize(const GrCaps&, const GrAppliedClip*, GrClampType) override {
Robert Phillips709e2402020-03-23 18:29:16 +0000302 return GrProcessorSet::EmptySetAnalysis();
Chris Dalton5a2f9622019-12-27 14:56:38 -0700303 }
304
Robert Phillips709e2402020-03-23 18:29:16 +0000305 void onPrePrepare(GrRecordingContext*,
Adlai Hollere2296f72020-11-19 13:41:26 -0500306 const GrSurfaceProxyView& writeView,
Robert Phillips709e2402020-03-23 18:29:16 +0000307 GrAppliedClip*,
John Stiles52cb1d02021-06-02 11:58:05 -0400308 const GrDstProxyView&,
Greg Daniel42dbca52020-11-20 10:22:43 -0500309 GrXferBarrierFlags renderPassXferBarriers,
310 GrLoadOp colorLoadOp) override {}
Robert Phillipsc655c3a2020-03-18 13:23:45 -0400311
Chris Dalton5a2f9622019-12-27 14:56:38 -0700312 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
321 void onExecute(GrOpFlushState* state, const SkRect& chainBounds) override {
Robert Phillips709e2402020-03-23 18:29:16 +0000322 GrPipeline pipeline(GrScissorTest::kDisabled, SkBlendMode::kSrc,
Adlai Hollere2296f72020-11-19 13:41:26 -0500323 state->drawOpArgs().writeView().swizzle());
Robert Phillips709e2402020-03-23 18:29:16 +0000324 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);
Chris Dalton5a2f9622019-12-27 14:56:38 -0700337 }
338
Chris Dalton2a26c502021-08-26 10:05:11 -0600339 GrProgramInfo programInfo(state->caps(), state->writeView(), state->usesMSAASurface(),
340 &pipeline, &GrUserStencilSettings::kUnused,
Robert Phillips5c809642020-11-20 12:28:45 -0500341 shader.get(), GrPrimitiveType::kPatches,
342 tessellationPatchVertexCount, state->renderPassBarriers(),
343 state->colorLoadOp());
Chris Dalton5a2f9622019-12-27 14:56:38 -0700344
Robert Phillips709e2402020-03-23 18:29:16 +0000345 state->bindPipeline(programInfo, SkRect::MakeIWH(kWidth, kHeight));
Greg Daniel426274b2020-07-20 11:37:38 -0400346 state->bindBuffers(nullptr, nullptr, std::move(fVertexBuffer));
Robert Phillips709e2402020-03-23 18:29:16 +0000347 state->draw(tessellationPatchVertexCount, fBaseVertex);
Chris Dalton5a2f9622019-12-27 14:56:38 -0700348 }
349
350 const SkMatrix fViewMatrix;
351 const std::array<float, 3>* const fTriPositions;
352 sk_sp<const GrBuffer> fVertexBuffer;
353 int fBaseVertex = 0;
Chris Dalton5a2f9622019-12-27 14:56:38 -0700354};
355
356
357static 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
Robert Phillips7a0d3c32021-07-21 15:39:51 -0400376DrawResult 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
Robert Phillipsedcd4312021-06-03 10:14:16 -0400385 if (!rContext->priv().caps()->shaderCaps()->tessellationSupport()) {
Chris Dalton5a2f9622019-12-27 14:56:38 -0700386 *errorMsg = "Requires GPU tessellation support.";
387 return DrawResult::kSkip;
388 }
Robert Phillipsedcd4312021-06-03 10:14:16 -0400389 if (!rContext->priv().caps()->shaderCaps()->shaderDerivativeSupport()) {
Chris Dalton5a2f9622019-12-27 14:56:38 -0700390 *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
Robert Phillipsedcd4312021-06-03 10:14:16 -0400405 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));
Chris Dalton5a2f9622019-12-27 14:56:38 -0700408
409 return skiagm::DrawResult::kOk;
410}
411
412DEF_GM( return new TessellationGM(); )
413
John Stilesa6841be2020-08-06 14:11:56 -0400414} // namespace skiagm