1 /*
2 * Copyright 2021 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 "gm/gm.h"
9 #include "include/core/SkBlender.h"
10 #include "include/core/SkCanvas.h"
11 #include "include/core/SkColor.h"
12 #include "include/core/SkColorSpace.h"
13 #include "include/core/SkData.h"
14 #include "include/core/SkMesh.h"
15 #include "include/core/SkPicture.h"
16 #include "include/core/SkPictureRecorder.h"
17 #include "include/core/SkPoint.h"
18 #include "include/core/SkString.h"
19 #include "include/core/SkSurface.h"
20 #include "include/effects/SkGradientShader.h"
21 #include "include/gpu/ganesh/GrDirectContext.h"
22 #include "include/gpu/ganesh/SkMeshGanesh.h"
23 #include "include/private/base/SkAssert.h"
24 #include "src/base/SkRandom.h"
25 #include "src/core/SkCanvasPriv.h"
26 #include "tools/DecodeUtils.h"
27 #include "tools/timer/TimeUtils.h"
28
29 using namespace skia_private;
30
31 namespace skiagm {
32
33 class MeshGM : public skiagm::GM {
34 public:
MeshGM()35 MeshGM() {}
36
37 protected:
38 using Attribute = SkMeshSpecification::Attribute;
39 using Varying = SkMeshSpecification::Varying;
40
getISize()41 SkISize getISize() override { return {435, 1180}; }
42
onOnceBeforeDraw()43 void onOnceBeforeDraw() override {
44 {
45 static const Attribute kAttributes[]{
46 {Attribute::Type::kFloat4, 8, SkString{"xuyv"}},
47 {Attribute::Type::kUByte4_unorm, 4, SkString{"brag"}},
48 };
49 static const Varying kVaryings[]{
50 {Varying::Type::kHalf4, SkString{"color"}},
51 {Varying::Type::kFloat2, SkString{"uv"} },
52 };
53 static constexpr char kVS[] = R"(
54 half4 unswizzle_color(half4 color) { return color.garb; }
55
56 Varyings main(const in Attributes attributes) {
57 Varyings varyings;
58 varyings.color = unswizzle_color(attributes.brag);
59 varyings.uv = attributes.xuyv.yw;
60 varyings.position = attributes.xuyv.xz;
61 return varyings;
62 }
63 )";
64 static constexpr char kFS[] = R"(
65 uniform colorFilter filter;
66
67 float2 main(const in Varyings varyings, out float4 color) {
68 color = filter.eval(varyings.color);
69 return varyings.uv;
70 }
71 )";
72 auto [spec, error] = SkMeshSpecification::Make(kAttributes,
73 sizeof(ColorVertex),
74 kVaryings,
75 SkString(kVS),
76 SkString(kFS));
77 if (!spec) {
78 SkDebugf("%s\n", error.c_str());
79 }
80 fSpecWithColor = std::move(spec);
81 }
82 {
83 static const Attribute kAttributes[]{
84 {Attribute::Type::kFloat4, 0, SkString{"xuyv"}},
85 };
86 static const Varying kVaryings[]{
87 {Varying::Type::kFloat2, SkString{"vux2"}},
88 };
89 static constexpr char kVS[] = R"(
90 Varyings main(const in Attributes a) {
91 Varyings v;
92 v.vux2 = 2*a.xuyv.wy;
93 v.position = a.xuyv.xz;
94 return v;
95 }
96 )";
97 static constexpr char kFS[] = R"(
98 float2 helper(in float2 vux2) { return vux2.yx/2; }
99 float2 main(const in Varyings varyings) {
100 return helper(varyings.vux2);
101 }
102 )";
103 auto [spec, error] = SkMeshSpecification::Make(kAttributes,
104 sizeof(NoColorVertex),
105 kVaryings,
106 SkString(kVS),
107 SkString(kFS));
108 if (!spec) {
109 SkDebugf("%s\n", error.c_str());
110 }
111 fSpecWithNoColor = std::move(spec);
112 }
113
114 static constexpr SkColor kColors[] = {SK_ColorTRANSPARENT, SK_ColorWHITE};
115 fShader = SkGradientShader::MakeRadial({10, 10},
116 3,
117 kColors,
118 nullptr,
119 2,
120 SkTileMode::kMirror);
121 }
122
onGpuSetup(SkCanvas * canvas,SkString * string,GraphiteTestContext *)123 DrawResult onGpuSetup(SkCanvas* canvas, SkString* string, GraphiteTestContext*) override {
124 auto dc = GrAsDirectContext(canvas->recordingContext());
125 this->ensureBuffers();
126 if (!dc || dc->abandoned()) {
127 return DrawResult::kOk;
128 }
129
130 fColorVB = SkMeshes::CopyVertexBuffer(dc, fColorVB);
131 fColorIndexedVB = SkMeshes::CopyVertexBuffer(dc, fColorIndexedVB);
132 fIB[1] = SkMeshes::CopyIndexBuffer (dc, fIB[0]);
133 if (!fColorVB || !fColorIndexedVB || !fIB[1]) {
134 return DrawResult::kFail;
135 }
136 return DrawResult::kOk;
137 }
138
onGpuTeardown()139 void onGpuTeardown() override {
140 // Destroy the GPU buffers and recreate on CPU
141 fColorVB = nullptr;
142 fColorIndexedVB = nullptr;
143 fIB[1] = nullptr;
144 this->ensureBuffers();
145 }
146
getName() const147 SkString getName() const override { return SkString("custommesh"); }
148
onDraw(SkCanvas * canvas,SkString *)149 DrawResult onDraw(SkCanvas* canvas, SkString*) override {
150 SkRuntimeEffect::ChildPtr nullChild[1] = {};
151 int i = 0;
152 for (const sk_sp<SkBlender>& blender : {SkBlender::Mode(SkBlendMode::kDst),
153 SkBlender::Mode(SkBlendMode::kSrc),
154 SkBlender::Mode(SkBlendMode::kSaturation)}) {
155 canvas->save();
156 for (uint8_t alpha : {0xFF , 0x40})
157 for (bool colors : {false, true})
158 for (bool shader : {false, true}) {
159 SkMesh::Result result;
160 // Rather than pile onto the combinatorics we draw every other test case indexed.
161 if ((i & 1) == 0) {
162 if (colors) {
163 result = SkMesh::Make(fSpecWithColor,
164 SkMesh::Mode::kTriangleStrip,
165 fColorVB,
166 /*vertexCount=*/4,
167 /*vertexOffset=*/0,
168 /*uniforms=*/nullptr,
169 /*children=*/nullChild,
170 kRect);
171 } else {
172 result = SkMesh::Make(fSpecWithNoColor,
173 SkMesh::Mode::kTriangleStrip,
174 fNoColorVB,
175 /*vertexCount=*/4,
176 kNoColorOffset,
177 /*uniforms=*/nullptr,
178 /*children=*/{},
179 kRect);
180 }
181 } else {
182 // Alternate between CPU and GPU-backend index buffers.
183 auto ib = (i % 4 == 0) ? fIB[0] : fIB[1];
184 if (colors) {
185 result = SkMesh::MakeIndexed(fSpecWithColor,
186 SkMesh::Mode::kTriangles,
187 fColorIndexedVB,
188 /*vertexCount=*/6,
189 kColorIndexedOffset,
190 std::move(ib),
191 /*indexCount=*/6,
192 kIndexOffset,
193 /*uniforms=*/nullptr,
194 /*children=*/nullChild,
195 kRect);
196 } else {
197 result = SkMesh::MakeIndexed(fSpecWithNoColor,
198 SkMesh::Mode::kTriangles,
199 fNoColorIndexedVB,
200 /*vertexCount=*/6,
201 /*vertexOffset=*/0,
202 std::move(ib),
203 /*indexCount=*/6,
204 kIndexOffset,
205 /*uniforms=*/nullptr,
206 /*children=*/{},
207 kRect);
208 }
209 }
210 if (!result.mesh.isValid()) {
211 SkDebugf("Mesh creation failed: %s\n", result.error.c_str());
212 return DrawResult::kFail;
213 }
214
215 SkPaint paint;
216 paint.setColor(SK_ColorGREEN);
217 paint.setShader(shader ? fShader : nullptr);
218 paint.setAlpha(alpha);
219
220 canvas->drawMesh(result.mesh, blender, paint);
221
222 canvas->translate(0, 150);
223 ++i;
224 }
225 canvas->restore();
226 canvas->translate(150, 0);
227 }
228 return DrawResult::kOk;
229 }
230
231 private:
ensureBuffers()232 void ensureBuffers() {
233 if (!fColorVB) {
234 fColorVB = SkMeshes::MakeVertexBuffer(kColorQuad, sizeof(kColorQuad));
235 }
236
237 if (!fNoColorVB) {
238 // Make this one such that the data is offset into the buffer.
239 auto data = SkData::MakeUninitialized(sizeof(kNoColorQuad) + kNoColorOffset);
240 std::memcpy(SkTAddOffset<void>(data->writable_data(), kNoColorOffset),
241 kNoColorQuad,
242 sizeof(kNoColorQuad));
243 fNoColorVB = SkMeshes::MakeVertexBuffer(data->data(), data->size());
244 }
245
246 if (!fColorIndexedVB) {
247 // This buffer also has an offset.
248 auto data = SkData::MakeUninitialized(sizeof(kColorIndexedQuad) + kColorIndexedOffset);
249 std::memcpy(SkTAddOffset<void>(data->writable_data(), kColorIndexedOffset),
250 kColorIndexedQuad,
251 sizeof(kColorIndexedQuad));
252 fColorIndexedVB = SkMeshes::MakeVertexBuffer(data->data(), data->size());
253 }
254
255 if (!fNoColorIndexedVB) {
256 fNoColorIndexedVB =
257 SkMeshes::MakeVertexBuffer(kNoColorIndexedQuad, sizeof(kNoColorIndexedQuad));
258 }
259
260 if (!fIB[0]) {
261 // The index buffer has an offset.
262 auto data = SkData::MakeUninitialized(sizeof(kIndices) + kIndexOffset);
263 std::memcpy(SkTAddOffset<void>(data->writable_data(), kIndexOffset),
264 kIndices,
265 sizeof(kIndices));
266 fIB[0] = SkMeshes::MakeIndexBuffer(data->data(), data->size());
267 }
268
269 if (!fIB[1]) {
270 // On CPU we always use the same CPU-backed index buffer.
271 fIB[1] = fIB[0];
272 }
273 }
274
275 struct ColorVertex {
276 uint32_t pad;
277 uint32_t brag;
278 float xuyv[4];
279 };
280
281 struct NoColorVertex {
282 float xuyv[4];
283 };
284
285 static constexpr auto kRect = SkRect::MakeLTRB(20, 20, 120, 120);
286 static constexpr auto kUV = SkRect::MakeLTRB( 0, 0, 20, 20);
287
288 static constexpr ColorVertex kColorQuad[] {
289 {0, 0x00FFFF00, {kRect.left(), kUV.left(), kRect.top(), kUV.top() }},
290 {0, 0x00FFFFFF, {kRect.right(), kUV.right(), kRect.top(), kUV.top() }},
291 {0, 0xFFFF00FF, {kRect.left(), kUV.left(), kRect.bottom(), kUV.bottom()}},
292 {0, 0xFFFFFF00, {kRect.right(), kUV.right(), kRect.bottom(), kUV.bottom()}},
293 };
294
295 static constexpr NoColorVertex kNoColorQuad[]{
296 {{kRect.left(), kUV.left(), kRect.top(), kUV.top() }},
297 {{kRect.right(), kUV.right(), kRect.top(), kUV.top() }},
298 {{kRect.left(), kUV.left(), kRect.bottom(), kUV.bottom()}},
299 {{kRect.right(), kUV.right(), kRect.bottom(), kUV.bottom()}},
300 };
301
302 // The indexed quads draw the same as the non-indexed. They just have unused vertices that the
303 // index buffer skips over draw with triangles instead of a triangle strip.
304 static constexpr ColorVertex kColorIndexedQuad[] {
305 {0, 0x00FFFF00, {kRect.left(), kUV.left(), kRect.top(), kUV.top() }},
306 {0, 0x00000000, { 100.f, 0.f, 100.f, 5.f }}, // unused
307 {0, 0x00FFFFFF, {kRect.right(), kUV.right(), kRect.top(), kUV.top() }},
308 {0, 0x00000000, { 200.f, 10.f, 200.f, 10.f }}, // unused
309 {0, 0xFFFF00FF, {kRect.left(), kUV.left(), kRect.bottom(), kUV.bottom()}},
310 {0, 0xFFFFFF00, {kRect.right(), kUV.right(), kRect.bottom(), kUV.bottom()}},
311 };
312
313 static constexpr NoColorVertex kNoColorIndexedQuad[]{
314 {{kRect.left(), kUV.left(), kRect.top(), kUV.top() }},
315 {{ 100.f, 0.f, 100.f, 5.f }}, // unused
316 {{kRect.right(), kUV.right(), kRect.top(), kUV.top() }},
317 {{ 200.f, 10.f, 200.f, 10.f }}, // unused
318 {{kRect.left(), kUV.left(), kRect.bottom(), kUV.bottom()}},
319 {{kRect.right(), kUV.right(), kRect.bottom(), kUV.bottom()}},
320 };
321
322 static constexpr uint16_t kIndices[]{0, 2, 4, 2, 5, 4};
323
324 // For some buffers we add an offset to ensure we're exercising drawing from mid-buffer.
325 static constexpr size_t kNoColorOffset = sizeof(NoColorVertex);
326 static constexpr size_t kColorIndexedOffset = 2*sizeof(ColorVertex);
327 static constexpr size_t kIndexOffset = 6;
328
329 sk_sp<SkShader> fShader;
330
331 sk_sp<SkMeshSpecification> fSpecWithColor;
332 sk_sp<SkMeshSpecification> fSpecWithNoColor;
333
334 // On GPU the first IB is a CPU buffer and the second is a GPU buffer.
335 sk_sp<SkMesh::IndexBuffer> fIB[2];
336
337 sk_sp<SkMesh::VertexBuffer> fColorVB;
338 sk_sp<SkMesh::VertexBuffer> fNoColorVB;
339 sk_sp<SkMesh::VertexBuffer> fColorIndexedVB;
340 sk_sp<SkMesh::VertexBuffer> fNoColorIndexedVB;
341 };
342
343 DEF_GM(return new MeshGM;)
344
345 class MeshColorSpaceGM : public skiagm::GM {
346 public:
MeshColorSpaceGM()347 MeshColorSpaceGM() {}
348
349 protected:
350 using Attribute = SkMeshSpecification::Attribute;
351 using Varying = SkMeshSpecification::Varying;
352
getISize()353 SkISize getISize() override { return {468, 258}; }
354
onOnceBeforeDraw()355 void onOnceBeforeDraw() override {
356 static const Attribute kAttributes[]{
357 {Attribute::Type::kFloat2, 0, SkString{"pos"} },
358 {Attribute::Type::kFloat4, 8, SkString{"color"}},
359 };
360 static const Varying kVaryings[]{
361 {Varying::Type::kHalf4, SkString{"color"}},
362 };
363 static constexpr char kPremulVS[] = R"(
364 Varyings main(const in Attributes attributes) {
365 Varyings varyings;
366 varyings.color = half4(attributes.color.a*attributes.color.rgb,
367 attributes.color.a);
368 varyings.position = attributes.pos;
369 return varyings;
370 }
371 )";
372 static constexpr char kUnpremulVS[] = R"(
373 Varyings main(const in Attributes attributes) {
374 Varyings varyings;
375 varyings.color = attributes.color;
376 varyings.position = attributes.pos;
377 return varyings;
378 }
379 )";
380 static constexpr char kFS[] = R"(
381 float2 main(in const Varyings varyings, out half4 color) {
382 color = varyings.color;
383 return varyings.position;
384 }
385 )";
386 for (bool unpremul : {false, true}) {
387 auto at = unpremul ? kUnpremul_SkAlphaType : kPremul_SkAlphaType;
388 auto vs = unpremul ? kUnpremulVS : kPremulVS;
389 for (bool spin : {false, true}) {
390 auto cs = SkColorSpace::MakeSRGB();
391 if (spin) {
392 cs = cs->makeColorSpin();
393 }
394
395 auto [spec, error] = SkMeshSpecification::Make(
396 kAttributes,
397 sizeof(Vertex),
398 kVaryings,
399 SkString(vs),
400 SkString(kFS),
401 std::move(cs),
402 at);
403 if (!spec) {
404 SkDebugf("%s\n", error.c_str());
405 }
406 fSpecs[SpecIndex(unpremul, spin)] = std::move(spec);
407 }
408 }
409 SkPoint pts[] = {{kRect.fLeft, 0}, {kRect.centerX(), 0}};
410 SkColor colors[] = {SK_ColorWHITE, SK_ColorTRANSPARENT};
411 fShader = SkGradientShader::MakeLinear(pts, colors, nullptr, 2, SkTileMode::kMirror);
412
413 fVB = SkMeshes::MakeVertexBuffer(kQuad, sizeof(kQuad));
414 }
415
getName() const416 SkString getName() const override { return SkString("custommesh_cs"); }
417
onDraw(SkCanvas * canvas,SkString * error)418 DrawResult onDraw(SkCanvas* canvas, SkString* error) override {
419 // Force an intermediate surface if the canvas is in "legacy" mode
420 SkCanvas* c = canvas;
421 sk_sp<SkSurface> surface;
422 if (!c->imageInfo().colorSpace()) {
423 SkImageInfo info = canvas->imageInfo().makeColorSpace(SkColorSpace::MakeSRGB());
424 surface = canvas->makeSurface(info);
425 if (!surface) {
426 // This GM won't work on configs that use a recording canvas.
427 return DrawResult::kSkip;
428 }
429 c = surface->getCanvas();
430 c->clear(SK_ColorWHITE);
431 }
432 for (bool useShader : {false, true})
433 for (bool unpremul : {false, true}) {
434 c->save();
435 for (bool spin : {false, true}) {
436 auto result = SkMesh::Make(fSpecs[SpecIndex(unpremul, spin)],
437 SkMesh::Mode::kTriangleStrip,
438 fVB,
439 /*vertexCount=*/4,
440 /*vertexOffset=*/0,
441 /*uniforms=*/nullptr,
442 /*children=*/{},
443 kRect);
444 if (!result.mesh.isValid()) {
445 SkDebugf("Mesh creation failed: %s\n", result.error.c_str());
446 return DrawResult::kFail;
447 }
448
449 SkPaint paint;
450 paint.setShader(useShader ? fShader : nullptr);
451 SkBlendMode mode = useShader ? SkBlendMode::kModulate : SkBlendMode::kDst;
452 canvas->drawMesh(result.mesh, SkBlender::Mode(mode), paint);
453
454 c->translate(0, kRect.height() + 10);
455 }
456 c->restore();
457 c->translate(kRect.width() + 10, 0);
458 c->save();
459 }
460 if (surface) {
461 surface->draw(canvas, 0, 0);
462 }
463 return DrawResult::kOk;
464 }
465
466 private:
467 struct Vertex {
468 SkPoint pos;
469 SkColor4f color;
470 };
471
SpecIndex(bool spin,bool unpremul)472 static int SpecIndex(bool spin, bool unpremul) {
473 return static_cast<int>(spin) + 2*static_cast<int>(unpremul);
474 }
475
476 static constexpr auto kRect = SkRect::MakeLTRB(20, 20, 120, 120);
477
478 static constexpr Vertex kQuad[] {
479 {{kRect.left() , kRect.top() }, {1, 0, 0, 1}},
480 {{kRect.right(), kRect.top() }, {0, 1, 0, 0}},
481 {{kRect.left() , kRect.bottom()}, {1, 1, 0, 0}},
482 {{kRect.right(), kRect.bottom()}, {0, 0, 1, 1}},
483 };
484
485 sk_sp<SkMesh::VertexBuffer> fVB;
486
487 sk_sp<SkMeshSpecification> fSpecs[4];
488
489 sk_sp<SkShader> fShader;
490 };
491
492 // helpers for cases when ctx could be nullptr
make_vertex_buffer(GrDirectContext * ctx,const void * data,size_t size)493 static sk_sp<SkMesh::VertexBuffer> make_vertex_buffer(GrDirectContext* ctx,
494 const void* data,
495 size_t size) {
496 if (ctx) {
497 return SkMeshes::MakeVertexBuffer(ctx, data, size);
498 }
499 return SkMeshes::MakeVertexBuffer(data, size);
500 }
501
make_index_buffer(GrDirectContext * ctx,const void * data,size_t size)502 static sk_sp<SkMesh::IndexBuffer> make_index_buffer(GrDirectContext* ctx,
503 const void* data,
504 size_t size) {
505 if (ctx) {
506 return SkMeshes::MakeIndexBuffer(ctx, data, size);
507 }
508 return SkMeshes::MakeIndexBuffer(data, size);
509 }
510
511 DEF_GM(return new MeshColorSpaceGM;)
512
513 class MeshUniformsGM : public skiagm::GM {
514 public:
MeshUniformsGM()515 MeshUniformsGM() { this->onAnimate(0); }
516
517 protected:
518 using Attribute = SkMeshSpecification::Attribute;
519 using Varying = SkMeshSpecification::Varying;
520
getISize()521 SkISize getISize() override { return {140, 250}; }
522
onOnceBeforeDraw()523 void onOnceBeforeDraw() override {
524 static const Attribute kAttributes[]{
525 {Attribute::Type::kFloat2, 0, SkString{"pos"} },
526 {Attribute::Type::kFloat2, 8, SkString{"coords"}},
527 };
528 static const Varying kVaryings[]{
529 {Varying::Type::kFloat2, SkString{"coords"}},
530 };
531 // To exercise shared VS/FS uniforms we have a matrix that is applied twice, once in each
532 // stage.
533 static constexpr char kVS[] = R"(
534 uniform float t[2];
535 uniform half3x3 m;
536 Varyings main(in const Attributes attributes) {
537 Varyings varyings;
538 varyings.coords = (m*float3(attributes.coords + float2(t[0], t[1]), 1)).xy;
539 varyings.position = attributes.pos;
540 return varyings;
541 }
542 )";
543 static constexpr char kFS[] = R"(
544 uniform half3x3 m;
545 layout(color) uniform half4 color;
546 float2 main(const Varyings varyings, out half4 c) {
547 c = color;
548 return (m*float3(varyings.coords, 1)).xy;
549 }
550 )";
551 auto [spec, error] =
552 SkMeshSpecification::Make(kAttributes,
553 sizeof(Vertex),
554 kVaryings,
555 SkString(kVS),
556 SkString(kFS),
557 SkColorSpace::MakeSRGB(),
558 kPremul_SkAlphaType);
559 if (!spec) {
560 SkDebugf("%s\n", error.c_str());
561 }
562 fSpec = std::move(spec);
563
564 SkColor colors[] = {SK_ColorWHITE, SK_ColorBLACK};
565 fShader = SkGradientShader::MakeRadial(kGradCenter,
566 .4f,
567 colors,
568 nullptr,
569 2,
570 SkTileMode::kMirror);
571
572 fVB = SkMeshes::MakeVertexBuffer(kQuad, sizeof(kQuad));
573 }
574
getName() const575 SkString getName() const override { return SkString("custommesh_uniforms"); }
576
onDraw(SkCanvas * canvas,SkString * error)577 DrawResult onDraw(SkCanvas* canvas, SkString* error) override {
578 SkMatrix matrices[] {
579 SkMatrix::MakeAll(-1, 0, 0, // self inverse so no effect.
580 0, -1, 0,
581 0, 0, 1),
582 SkMatrix::RotateDeg(fDegrees/2.f, {0.5f, 0.5f}),
583 };
584 for (const auto& m : matrices) {
585 auto unis = SkData::MakeUninitialized(fSpec->uniformSize());
586
587 SkPoint trans = -kCoordTrans;
588 static_assert(sizeof(SkPoint) == 2*sizeof(float));
589
590 const SkMeshSpecification::Uniform* u = fSpec->findUniform("t");
591 SkASSERT(u);
592 std::memcpy(SkTAddOffset<void>(unis->writable_data(), u->offset),
593 (void*)&trans,
594 2*sizeof(float));
595
596 u = fSpec->findUniform("m");
597 SkASSERT(u);
598 for (size_t offset = u->offset, col = 0; col < 3; ++col) {
599 for (size_t row = 0; row < 3; ++row, offset += sizeof(float)) {
600 *SkTAddOffset<float>(unis->writable_data(), offset) = m.rc(row, col);
601 }
602 }
603
604 u = fSpec->findUniform("color");
605 SkASSERT(u);
606 std::memcpy(SkTAddOffset<void>(unis->writable_data(), u->offset),
607 fColor.vec(),
608 4*sizeof(float));
609
610 auto result = SkMesh::Make(fSpec,
611 SkMesh::Mode::kTriangleStrip,
612 fVB,
613 /*vertexCount=*/4,
614 /*vertexOffset=*/0,
615 /*uniforms=*/std::move(unis),
616 /*children=*/{},
617 kRect);
618
619 if (!result.mesh.isValid()) {
620 SkDebugf("Mesh creation failed: %s\n", result.error.c_str());
621 return DrawResult::kFail;
622 }
623
624 SkPaint paint;
625 paint.setShader(fShader);
626 canvas->drawMesh(result.mesh, SkBlender::Mode(SkBlendMode::kModulate), paint);
627
628 canvas->translate(0, kRect.height() + 10);
629 }
630 return DrawResult::kOk;
631 }
632
onAnimate(double nanos)633 bool onAnimate(double nanos) override {
634 fDegrees = TimeUtils::NanosToSeconds(nanos) * 360.f/10.f + 45.f;
635 // prime number periods, like locusts.
636 fColor.fR = TimeUtils::SineWave(nanos, 13.f, 0.f, 0.f, 1.f);
637 fColor.fG = TimeUtils::SineWave(nanos, 23.f, 5.f, 0.f, 1.f);
638 fColor.fB = TimeUtils::SineWave(nanos, 11.f, 0.f, 0.f, 1.f);
639 fColor.fA = 1.f;
640 return true;
641 }
642
643 private:
644 struct Vertex {
645 SkPoint pos;
646 SkPoint tex;
647 };
648
649 static constexpr auto kRect = SkRect::MakeLTRB(20, 20, 120, 120);
650
651 // Our logical tex coords are [0..1] but we insert an arbitrary translation that gets undone
652 // with a uniform.
653 static constexpr SkPoint kCoordTrans = {75, -37};
654 static constexpr auto kCoordRect = SkRect::MakeXYWH(kCoordTrans.x(), kCoordTrans.y(), 1, 1);
655
656 static constexpr SkPoint kGradCenter = {0.3f, 0.2f};
657
658 static constexpr Vertex kQuad[] {
659 {{kRect.left() , kRect.top() }, {kCoordRect.left() , kCoordRect.top()} },
660 {{kRect.right(), kRect.top() }, {kCoordRect.right(), kCoordRect.top()} },
661 {{kRect.left() , kRect.bottom()}, {kCoordRect.left() , kCoordRect.bottom()}},
662 {{kRect.right(), kRect.bottom()}, {kCoordRect.right(), kCoordRect.bottom()}},
663 };
664
665 float fDegrees;
666
667 SkColor4f fColor;
668
669 sk_sp<SkMesh::VertexBuffer> fVB;
670
671 sk_sp<SkMeshSpecification> fSpec;
672
673 sk_sp<SkShader> fShader;
674 };
675
676 DEF_GM(return new MeshUniformsGM())
677
678 class MeshUpdateGM : public skiagm::GM {
679 public:
680 MeshUpdateGM() = default;
681
682 protected:
683 using Attribute = SkMeshSpecification::Attribute;
684 using Varying = SkMeshSpecification::Varying;
685
686 static constexpr int kWidth = 270;
687 static constexpr int kHeight = 490;
688 static constexpr int kVerticalPadding = 10;
689
getISize()690 SkISize getISize() override { return {kWidth, kHeight}; }
691
onOnceBeforeDraw()692 void onOnceBeforeDraw() override {
693 static const Attribute kAttributes[]{
694 {Attribute::Type::kFloat2, 0, SkString{"pos"}},
695 {Attribute::Type::kFloat2, 8, SkString{"coords"}},
696 };
697 static const Varying kVaryings[]{
698 {Varying::Type::kFloat2, SkString{"coords"}},
699 };
700 static constexpr char kVS[] = R"(
701 Varyings main(const in Attributes attributes) {
702 Varyings varyings;
703 varyings.coords = attributes.coords;
704 varyings.position = attributes.pos;
705 return varyings;
706 }
707 )";
708 static constexpr char kFS[] = R"(
709 float2 main(const Varyings varyings) { return varyings.coords; }
710 )";
711 auto [spec, error] = SkMeshSpecification::Make(kAttributes,
712 sizeof(Vertex),
713 kVaryings,
714 SkString(kVS),
715 SkString(kFS),
716 SkColorSpace::MakeSRGB(),
717 kPremul_SkAlphaType);
718 if (!spec) {
719 SkDebugf("%s\n", error.c_str());
720 }
721 fSpec = std::move(spec);
722
723 uint32_t colors[] = {SK_ColorYELLOW, SK_ColorMAGENTA, SK_ColorCYAN, SK_ColorWHITE};
724 SkPixmap pixmap(SkImageInfo::Make({2, 2}, kBGRA_8888_SkColorType, kPremul_SkAlphaType),
725 colors,
726 /*rowBytes=*/8);
727 fShader = SkImages::RasterFromPixmapCopy(pixmap)->makeShader(
728 SkTileMode::kClamp, SkTileMode::kClamp, SkFilterMode::kLinear);
729 }
730
getName() const731 SkString getName() const override { return SkString("mesh_updates"); }
732
733 // The top 4 rows are CPU buffers - the bottom 4 rows are GPU buffers
734 // Within each set of 4, the top 3 rows are vertex updates while the 4th row is an index update.
onDraw(SkCanvas * canvas,SkString * error)735 DrawResult onDraw(SkCanvas* canvas, SkString* error) override {
736 canvas->clear(SK_ColorBLACK);
737
738 GrRecordingContext* rc = canvas->recordingContext();
739 GrDirectContext* dc = GrAsDirectContext(rc);
740 if (rc && !dc) {
741 // On GPU this relies on using the DC to update the GPU backed vertex/index buffers.
742 return DrawResult::kSkip;
743 }
744
745 if (dc && dc->abandoned()) {
746 return DrawResult::kSkip;
747 }
748
749 SkPaint paint;
750 paint.setShader(fShader);
751
752 const SkRect r = SkRect::MakeXYWH(10.f, 10.f, 50.f, 50.f);
753
754 // We test updating CPU and GPU buffers.
755 for (bool gpuBuffer : {false, true}) {
756 auto ctx = gpuBuffer ? dc : nullptr;
757
758 // How many rects worth of storage is in the vertex buffer?
759 static constexpr int kVBRects = 2;
760
761 // How many times do we update the vertex buffer? Wraps to start of buffer if
762 // > kVBRects.
763 static constexpr int kUpdatesRects = 3;
764
765 auto vb = make_vertex_buffer(ctx, /*data=*/nullptr, kVBRects * 6 * sizeof(Vertex));
766 SkASSERT(vb);
767
768 SkRect bounds;
769 for (int i = 0; i < kUpdatesRects; ++i) {
770 auto p = r.makeOffset(100.f*i, 0.f);
771 if (i) {
772 bounds.join(p);
773 } else {
774 bounds = p;
775 }
776
777 SkPoint t[4];
778 SkRect::MakeWH(2.f, 2.f).toQuad(t);
779 SkMatrix::RotateDeg(90.f*i, {1.f, 1.f}).mapPoints(t, std::size(t));
780
781 Vertex vertices[6];
782 vertices[0] = {{p.left(), p.top()}, t[0]};
783 vertices[1] = {{p.left(), p.bottom()}, t[3]};
784 vertices[2] = {{p.right(), p.top()}, t[1]};
785 vertices[3] = vertices[2];
786 vertices[4] = vertices[1];
787 vertices[5] = {{p.right(), p.bottom()}, t[2]};
788
789 size_t offset = 6*(i % kVBRects)*sizeof(Vertex);
790 SkAssertResult(vb->update(ctx, vertices, offset, 6*sizeof(Vertex)));
791 // Make there aren't accidentally deferred reads of the client data.
792 std::memset(vertices, 0, sizeof(vertices));
793
794 int rectCount = std::min(i + 1, kVBRects);
795 auto result = SkMesh::Make(fSpec,
796 SkMesh::Mode::kTriangles,
797 vb,
798 /*vertexCount=*/6 * rectCount,
799 /*vertexOffset=*/0,
800 /*uniforms=*/nullptr,
801 /*children=*/{},
802 bounds);
803
804 if (!result.mesh.isValid()) {
805 SkDebugf("Mesh creation failed: %s\n", result.error.c_str());
806 return DrawResult::kFail;
807 }
808
809 canvas->drawMesh(result.mesh, SkBlender::Mode(SkBlendMode::kDst), paint);
810
811 canvas->translate(0, r.height() + kVerticalPadding);
812 }
813
814 // Now test updating an IB.
815
816 // How many rects worth of storage is in the index buffer?
817 static constexpr int kIBRects = 2;
818
819 // How many times do we update the index buffer? Wraps to start of buffer if > kIBRects.
820 static constexpr int kNumIBUpdates = 3;
821
822 // Make the vertex buffer large enough to hold all the rects and populate.
823 vb = make_vertex_buffer(ctx, /*data=*/nullptr, kNumIBUpdates * 4 * sizeof(Vertex));
824 SkASSERT(vb);
825 for (int i = 0; i < kNumIBUpdates; ++i) {
826 SkPoint p[4];
827 auto rect = r.makeOffset(100*i, 0);
828 rect.toQuad(p);
829 if (i) {
830 bounds.join(rect);
831 } else {
832 bounds = rect;
833 }
834
835 SkPoint t[4];
836 SkRect::MakeWH(2.f, 2.f).toQuad(t);
837 SkMatrix::RotateDeg(90.f*i, {1.f, 1.f}).mapPoints(t, std::size(t));
838 Vertex vertices[4]{{p[0], t[0]}, {p[1], t[1]}, {p[2], t[2]}, {p[3], t[3]}};
839 SkAssertResult(
840 vb->update(ctx, vertices, i*4*sizeof(Vertex), 4*sizeof(Vertex)));
841 }
842
843 auto ib = make_index_buffer(
844 ctx, /*data=*/nullptr, kIBRects * 6 * sizeof(uint16_t));
845 SkASSERT(ib);
846 for (int i = 0; i < kNumIBUpdates; ++i) {
847 uint16_t indices[6] = {SkToU16(0 + 4*i),
848 SkToU16(3 + 4*i),
849 SkToU16(1 + 4*i),
850 SkToU16(1 + 4*i),
851 SkToU16(3 + 4*i),
852 SkToU16(2 + 4*i)};
853 size_t offset = 6*(i % kIBRects)*sizeof(uint16_t);
854 SkAssertResult(ib->update(ctx, indices, offset, 6*sizeof(uint16_t)));
855 std::memset(indices, 0, 6*sizeof(uint16_t));
856
857 auto result = SkMesh::MakeIndexed(fSpec,
858 SkMesh::Mode::kTriangles,
859 vb,
860 /*vertexCount=*/4 * kNumIBUpdates,
861 /*vertexOffset=*/0,
862 ib,
863 /*indexCount=*/6,
864 /*indexOffset=*/offset,
865 /*uniforms=*/nullptr,
866 /*children=*/{},
867 bounds);
868
869 if (!result.mesh.isValid()) {
870 SkDebugf("Mesh creation failed: %s\n", result.error.c_str());
871 return DrawResult::kFail;
872 }
873
874 canvas->drawMesh(result.mesh, SkBlender::Mode(SkBlendMode::kDst), paint);
875 }
876 canvas->translate(0, r.height() + kVerticalPadding);
877 }
878
879 return DrawResult::kOk;
880 }
881
882 private:
883 struct Vertex {
884 SkPoint pos;
885 SkPoint tex;
886 };
887
888 sk_sp<SkMeshSpecification> fSpec;
889
890 sk_sp<SkShader> fShader;
891 };
892
893 DEF_GM(return new MeshUpdateGM())
894
895 class MeshZeroInitGM : public skiagm::GM {
896 public:
897 MeshZeroInitGM() = default;
898
899 protected:
900 using Attribute = SkMeshSpecification::Attribute;
901 using Varying = SkMeshSpecification::Varying;
902
getISize()903 SkISize getISize() override { return {90, 30}; }
904
onOnceBeforeDraw()905 void onOnceBeforeDraw() override {
906 static const Attribute kAttributes1[]{
907 {Attribute::Type::kUByte4_unorm, 0, SkString{"color"}},
908 {Attribute::Type::kFloat2, 4, SkString{"pos" }},
909 };
910 static const Attribute kAttributes2[]{
911 {Attribute::Type::kFloat2, 0, SkString{"pos" }},
912 {Attribute::Type::kUByte4_unorm, 8, SkString{"color"}},
913 };
914 static const Varying kVaryings[]{{Varying::Type::kHalf4, SkString{"color"}}};
915 static constexpr char kVS[] = R"(
916 Varyings main(const in Attributes attributes) {
917 Varyings varyings;
918 varyings.color = attributes.color;
919 varyings.position = attributes.pos;
920 return varyings;
921 }
922 )";
923 static constexpr char kFS[] = R"(
924 float2 main(const Varyings varyings, out half4 color) {
925 color = varyings.color;
926 return varyings.position;
927 }
928 )";
929 auto result = SkMeshSpecification::Make(kAttributes1,
930 /*vertexStride==*/12,
931 kVaryings,
932 SkString(kVS),
933 SkString(kFS),
934 SkColorSpace::MakeSRGB(),
935 kPremul_SkAlphaType);
936 if (!result.specification) {
937 SkDebugf("%s\n", result.error.c_str());
938 }
939 fSpec[0] = std::move(result.specification);
940
941 result = SkMeshSpecification::Make(kAttributes1,
942 /*vertexStride=*/12,
943 kVaryings,
944 SkString(kVS),
945 SkString(kFS),
946 SkColorSpace::MakeSRGB(),
947 kPremul_SkAlphaType);
948 if (!result.specification) {
949 SkDebugf("%s\n", result.error.c_str());
950 }
951 fSpec[1] = std::move(result.specification);
952 }
953
getName() const954 SkString getName() const override { return SkString("mesh_zero_init"); }
955
onDraw(SkCanvas * canvas,SkString * error)956 DrawResult onDraw(SkCanvas* canvas, SkString* error) override {
957 GrRecordingContext* rc = canvas->recordingContext();
958 GrDirectContext* dc = GrAsDirectContext(rc);
959 if (rc && !dc) {
960 // On GPU this relies on using the DC to update the GPU backed vertex/index buffers.
961 return DrawResult::kSkip;
962 }
963
964 if (dc && dc->abandoned()) {
965 return DrawResult::kSkip;
966 }
967
968 static constexpr SkPoint kTri[]{{10, 10}, {20, 10}, {10, 20}};
969 // The zero will come from the uninit part of the buffer.
970 static constexpr uint16_t kTiIndices[]{1, 2};
971
972 // We test updating CPU and GPU buffers.
973 for (bool gpuBuffer : {false, true}) {
974 auto ctx = gpuBuffer ? dc : nullptr;
975 for (int i = 0; i < 2; ++i) {
976 const auto& spec = fSpec[i];
977
978 size_t posOffset = spec->findAttribute("pos")->offset;
979 auto vb = make_vertex_buffer(ctx, nullptr, spec->stride() * std::size(kTri));
980 SkASSERT(vb);
981 for (size_t j = 0; j < std::size(kTri); ++j) {
982 SkAssertResult(vb->update(ctx,
983 &kTri[j],
984 spec->stride()*j + posOffset,
985 sizeof(kTri[j])));
986 }
987
988 // The first time we make the indices be 0,1,2 using the zero'ed buffer for the
989 // first. However, because uploads must be 4 byte aligned it's actually 0,0,1,2.
990 // The second time we upload 1,2 to beginning of the buffer to form 1,2,0.
991 size_t indexUploadOffset = i == 0 ? 4 : 0;
992 size_t indexMeshOffset = i == 0 ? 2 : 0;
993
994 auto ib = make_index_buffer(ctx, nullptr, sizeof(uint16_t) * 4);
995 SkASSERT(ib);
996 SkAssertResult(ib->update(ctx, kTiIndices, indexUploadOffset, sizeof(kTiIndices)));
997
998 SkRect bounds;
999 bounds.setBounds(kTri, std::size(kTri));
1000 auto result = SkMesh::MakeIndexed(spec,
1001 SkMesh::Mode::kTriangles,
1002 std::move(vb),
1003 /*vertexCount=*/std::size(kTri),
1004 /*vertexOffset=*/0,
1005 std::move(ib),
1006 /*indexCount=*/std::size(kTiIndices) + 1,
1007 indexMeshOffset,
1008 /*uniforms=*/nullptr,
1009 /*children=*/{},
1010 bounds);
1011 if (!result.mesh.isValid()) {
1012 SkDebugf("Mesh creation failed: %s\n", result.error.c_str());
1013 return DrawResult::kFail;
1014 }
1015
1016 SkPaint paint;
1017 // The color will be transparent black. Set the blender to kDstOver so when combined
1018 // with the paint's opaque black we get opaque black.
1019 canvas->drawMesh(result.mesh, SkBlender::Mode(SkBlendMode::kDstOver), paint);
1020 canvas->translate(bounds.width() + 10, 0);
1021 if (ctx) {
1022 // Free up the buffers for recycling in the cache. This helps test that
1023 // a recycled buffer gets zero'ed.
1024 result.mesh = {};
1025 SkASSERT(!ib); // NOLINT - bugprone-use-after-move. We're asserting it's moved.
1026 SkASSERT(!vb); // NOLINT
1027 ctx->flushAndSubmit(GrSyncCpu::kYes);
1028 }
1029 }
1030 }
1031
1032 return DrawResult::kOk;
1033 }
1034
1035 private:
1036 sk_sp<SkMeshSpecification> fSpec[2];
1037 };
1038
1039 DEF_GM(return new MeshZeroInitGM())
1040
1041 // We have a special GM for testing SkMesh through SkPicture because all of SkPicture GM testing
1042 // uses the CPU backend and SkMesh only works on GPU.
1043 class PictureMesh : public skiagm::GM {
1044 public:
1045 PictureMesh() = default;
1046
1047 protected:
1048 using Attribute = SkMeshSpecification::Attribute;
1049 using Varying = SkMeshSpecification::Varying;
1050
getISize()1051 SkISize getISize() override { return {390, 90}; }
1052
onOnceBeforeDraw()1053 void onOnceBeforeDraw() override {
1054 static const Attribute kAttributes[]{
1055 {Attribute::Type::kFloat2, 0, SkString{"pos"}},
1056 };
1057 static const Varying kVaryings[]{
1058 {Varying::Type::kFloat2, SkString{"coords"}},
1059 };
1060 static constexpr char kVS[] = R"(
1061 Varyings main(in const Attributes attributes) {
1062 Varyings varyings;
1063 varyings.position = attributes.pos;
1064 return varyings;
1065 }
1066 )";
1067 static const SkString kFS = SkStringPrintf(R"(
1068 uniform float2 offset;
1069 float2 main(const Varyings varyings, out float4 color) {
1070 float2 tl = float2(%f, %f);
1071 float2 wh = float2(%f, %f);
1072 float2 c = tl + wh/2;
1073 float r = length(wh)/4;
1074 color.rba = float3(1);
1075 color.g = min(1, length(varyings.position - c + offset) / r);
1076 return varyings.position;
1077 }
1078 )", kRect.x(), kRect.y(), kRect.width(), kRect.height());
1079 auto [spec, error] =
1080 SkMeshSpecification::Make(kAttributes,
1081 sizeof(Vertex),
1082 kVaryings,
1083 SkString(kVS),
1084 kFS,
1085 SkColorSpace::MakeSRGB()->makeColorSpin(),
1086 kPremul_SkAlphaType);
1087 if (!spec) {
1088 SkDebugf("%s\n", error.c_str());
1089 }
1090 fSpec = std::move(spec);
1091
1092 fVB = SkMeshes::MakeVertexBuffer(kQuad, sizeof(kQuad));
1093 fIB = SkMeshes::MakeIndexBuffer(kIndices, sizeof(kIndices));
1094
1095 SkRandom random;
1096 SkColor4f colors[6];
1097 for (size_t i = 0; i < std::size(colors) - 1; ++i) {
1098 colors[i] = {random.nextF(), random.nextF(), random.nextF(), 1.f};
1099 }
1100 colors[std::size(colors) - 1] = colors[0];
1101 SkPaint paint;
1102 SkGradientShader::Interpolation interpolation;
1103 interpolation.fColorSpace = SkGradientShader::Interpolation::ColorSpace::kHSL;
1104 fShader = SkGradientShader::MakeSweep(kRect.centerX(), kRect.centerY(),
1105 colors,
1106 SkColorSpace::MakeSRGB(),
1107 nullptr,
1108 std::size(colors),
1109 SkTileMode::kRepeat,
1110 0,
1111 360.f,
1112 interpolation,
1113 /*localMatrix=*/nullptr);
1114 }
1115
getName() const1116 SkString getName() const override { return SkString("picture_mesh"); }
1117
onDraw(SkCanvas * canvas,SkString * error)1118 DrawResult onDraw(SkCanvas* canvas, SkString* error) override {
1119 SkPaint paint;
1120 paint.setShader(fShader);
1121
1122 auto dc = GrAsDirectContext(canvas->recordingContext());
1123 for (bool picture : {false, true}) {
1124 canvas->save();
1125 for (bool gpu : {false, true}) {
1126 auto vb = gpu ? SkMeshes::CopyVertexBuffer(dc, fVB) : fVB;
1127 auto ib = gpu ? SkMeshes::CopyIndexBuffer (dc, fIB) : fIB;
1128
1129 float offset[2] = {8, 8};
1130 for (size_t i = 0; i < 4; ++i) {
1131 auto uniforms = SkData::MakeWithCopy(&offset, sizeof(offset));
1132 SkMesh::Result r;
1133 switch (i) {
1134 case 0:
1135 r = SkMesh::Make(fSpec,
1136 SkMesh::Mode::kTriangles,
1137 fVB,
1138 6,
1139 1 * sizeof(Vertex),
1140 std::move(uniforms),
1141 /*children=*/{},
1142 kRect);
1143 break;
1144 case 1:
1145 r = SkMesh::Make(fSpec,
1146 SkMesh::Mode::kTriangleStrip,
1147 fVB,
1148 4,
1149 1 * sizeof(Vertex),
1150 std::move(uniforms),
1151 /*children=*/{},
1152 kRect);
1153 break;
1154 case 2:
1155 r = SkMesh::MakeIndexed(fSpec,
1156 SkMesh::Mode::kTriangles,
1157 fVB,
1158 std::size(kQuad),
1159 0,
1160 fIB,
1161 6,
1162 2 * (sizeof(uint16_t)),
1163 std::move(uniforms),
1164 /*children=*/{},
1165 kRect);
1166 break;
1167 case 3:
1168 r = SkMesh::MakeIndexed(fSpec,
1169 SkMesh::Mode::kTriangleStrip,
1170 fVB,
1171 std::size(kQuad),
1172 0,
1173 fIB,
1174 6,
1175 2 * sizeof(uint16_t),
1176 std::move(uniforms),
1177 /*children=*/{},
1178 kRect);
1179 break;
1180 }
1181
1182 if (!r.mesh.isValid()) {
1183 *error = r.error;
1184 return DrawResult::kFail;
1185 }
1186
1187 auto draw = [&](SkCanvas* c) {
1188 c->drawMesh(r.mesh, SkBlender::Mode(SkBlendMode::kDifference), paint);
1189 };
1190 if (picture) {
1191 SkPictureRecorder recorder;
1192 draw(recorder.beginRecording(SkRect::Make(this->getISize()),
1193 /*bbhFactory=*/nullptr));
1194 canvas->drawPicture(recorder.finishRecordingAsPicture());
1195 } else {
1196 draw(canvas);
1197 }
1198 offset[i%2] *= -1;
1199 canvas->translate(kRect.width() + 10, 0);
1200 }
1201 }
1202 canvas->restore();
1203 canvas->translate(0, kRect.height() + 10);
1204 }
1205 return DrawResult::kOk;
1206 }
1207
1208 private:
1209 struct Vertex {
1210 SkPoint pos;
1211 };
1212
1213 static constexpr auto kRect = SkRect::MakeWH(40, 40);
1214
1215 static constexpr Vertex kQuad[] {
1216 {1000, 1000}, // skip
1217 {{kRect.left() , kRect.top() }},
1218 {{kRect.right(), kRect.top() }},
1219 {{kRect.left() , kRect.bottom()}},
1220 {{kRect.right(), kRect.bottom()}},
1221 {{kRect.left() , kRect.bottom()}},
1222 {{kRect.right(), kRect.top() }},
1223 };
1224
1225 static constexpr uint16_t kIndices[] = {1000, 2000, 1, 2, 3, 4, 5, 6};
1226
1227 sk_sp<SkMesh::VertexBuffer> fVB;
1228
1229 sk_sp<SkMesh::IndexBuffer> fIB;
1230
1231 sk_sp<SkMeshSpecification> fSpec;
1232
1233 sk_sp<SkShader> fShader;
1234 };
1235
1236 DEF_GM(return new PictureMesh())
1237
1238 class MeshWithShadersGM : public skiagm::GM {
1239 public:
1240 enum class Type {
1241 kMeshWithImage,
1242 kMeshWithPaintColor,
1243 kMeshWithPaintImage,
1244 kMeshWithEffects,
1245 };
1246
MeshWithShadersGM(Type type)1247 MeshWithShadersGM(Type type) : fType(type) {
1248 // Create a grid of evenly spaced points for our mesh
1249 this->onAnimate(0.0);
1250
1251 // Create an index buffer of triangles over our point mesh.
1252 for (int y = 0; y < kMeshSize - 1; ++y) {
1253 for (int x = 0; x < kMeshSize - 1; ++x) {
1254 SkASSERT(((y + 1) * kMeshSize + x + 1) < fVerts.size());
1255
1256 uint16_t TL = y * kMeshSize + x;
1257 uint16_t TR = y * kMeshSize + x + 1;
1258 uint16_t BL = (y + 1) * kMeshSize + x;
1259 uint16_t BR = (y + 1) * kMeshSize + x + 1;
1260
1261 fIndices.push_back(TL);
1262 fIndices.push_back(TR);
1263 fIndices.push_back(BL);
1264
1265 fIndices.push_back(BR);
1266 fIndices.push_back(BL);
1267 fIndices.push_back(TR);
1268 }
1269 }
1270 }
1271
1272 protected:
1273 using Attribute = SkMeshSpecification::Attribute;
1274 using Varying = SkMeshSpecification::Varying;
1275
getISize()1276 SkISize getISize() override { return {320, 320}; }
1277
onOnceBeforeDraw()1278 void onOnceBeforeDraw() override {
1279 {
1280 static const Attribute kAttributes[] = {
1281 {Attribute::Type::kFloat2, 0, SkString{"position"}},
1282 {Attribute::Type::kFloat2, 8, SkString{"uv"}},
1283 };
1284 static const Varying kVaryings[] = {
1285 {Varying::Type::kFloat2, SkString{"uv"}},
1286 };
1287 static constexpr char kVS[] = R"(
1288 Varyings main(const in Attributes attributes) {
1289 Varyings varyings;
1290 varyings.uv = attributes.uv;
1291 varyings.position = attributes.position;
1292 return varyings;
1293 }
1294 )";
1295 static constexpr char kFS[] = R"(
1296 uniform shader myShader1;
1297 uniform shader myShader2;
1298 uniform colorFilter myColorFilter;
1299 uniform blender myBlend;
1300
1301 float2 main(const in Varyings varyings, out half4 color) {
1302 half4 color1 = myShader1.eval(varyings.uv);
1303 half4 color2 = myShader2.eval(varyings.uv);
1304
1305 // Apply a inverse color filter to the first image.
1306 color1 = myColorFilter.eval(color1);
1307
1308 // Fade in the second image horizontally, leveraging the UVs.
1309 color2 *= varyings.uv.x / 128.0;
1310
1311 // Combine the two images by using a blender (set to dst-over).
1312 color = myBlend.eval(color1, color2);
1313
1314 return varyings.uv;
1315 }
1316 )";
1317 auto [spec, error] = SkMeshSpecification::Make(kAttributes,
1318 sizeof(Vertex),
1319 kVaryings,
1320 SkString(kVS),
1321 SkString(kFS));
1322 if (!spec) {
1323 SkDebugf("%s\n", error.c_str());
1324 }
1325 fSpec = std::move(spec);
1326 }
1327
1328
1329 switch (fType) {
1330 case Type::kMeshWithImage: {
1331 fShader1 = ToolUtils::GetResourceAsImage("images/mandrill_128.png")
1332 ->makeShader(SkSamplingOptions(SkFilterMode::kLinear));
1333 fShader2 = nullptr;
1334 fColorFilter = nullptr;
1335 fBlender = nullptr;
1336 fPaintShader = nullptr;
1337 break;
1338 }
1339 case Type::kMeshWithEffects: {
1340 uint8_t inverseTable[256];
1341 for (int index = 0; index < 256; ++index) {
1342 inverseTable[index] = 255 - index;
1343 }
1344
1345 fShader1 = ToolUtils::GetResourceAsImage("images/mandrill_128.png")
1346 ->makeShader(SkSamplingOptions(SkFilterMode::kLinear));
1347 fShader2 = ToolUtils::GetResourceAsImage("images/color_wheel.png")
1348 ->makeShader(SkSamplingOptions(SkFilterMode::kLinear));
1349 fColorFilter = SkColorFilters::TableARGB(/*tableA=*/nullptr,
1350 inverseTable,
1351 inverseTable,
1352 inverseTable);
1353 fBlender = SkBlender::Mode(SkBlendMode::kDstOver);
1354 fPaintShader = nullptr;
1355 break;
1356 }
1357 case Type::kMeshWithPaintColor: {
1358 fShader1 = nullptr;
1359 fShader2 = ToolUtils::GetResourceAsImage("images/mandrill_128.png")
1360 ->makeShader(SkSamplingOptions(SkFilterMode::kLinear));
1361 fColorFilter = nullptr;
1362 fBlender = SkBlender::Mode(SkBlendMode::kDst);
1363 fPaintShader = SkShaders::Color(SK_ColorGREEN);
1364 break;
1365 }
1366 case Type::kMeshWithPaintImage: {
1367 fShader1 = ToolUtils::GetResourceAsImage("images/color_wheel.png")
1368 ->makeShader(SkSamplingOptions(SkFilterMode::kLinear));
1369 fShader2 = nullptr;
1370 fColorFilter = nullptr;
1371 fBlender = nullptr;
1372 fPaintShader = ToolUtils::GetResourceAsImage("images/mandrill_128.png")
1373 ->makeShader(SkSamplingOptions(SkFilterMode::kLinear));
1374 break;
1375 }
1376 default:
1377 SkUNREACHABLE;
1378 }
1379 }
1380
onGpuSetup(SkCanvas * canvas,SkString * string,GraphiteTestContext *)1381 DrawResult onGpuSetup(SkCanvas* canvas, SkString* string, GraphiteTestContext*) override {
1382 auto dc = GrAsDirectContext(canvas->recordingContext());
1383 this->ensureBuffers();
1384 if (!dc || dc->abandoned()) {
1385 return DrawResult::kOk;
1386 }
1387
1388 fVB = SkMeshes::CopyVertexBuffer(dc, fVB);
1389 fIB = SkMeshes::CopyIndexBuffer (dc, fIB);
1390 return (!fVB || !fIB) ? DrawResult::kFail
1391 : DrawResult::kOk;
1392 }
1393
onGpuTeardown()1394 void onGpuTeardown() override {
1395 // Destroy the GPU buffers and recreate on CPU
1396 fVB = nullptr;
1397 fIB = nullptr;
1398 this->ensureBuffers();
1399 }
1400
getName() const1401 SkString getName() const override {
1402 switch (fType) {
1403 case Type::kMeshWithImage: return SkString("mesh_with_image");
1404 case Type::kMeshWithEffects: return SkString("mesh_with_effects");
1405 case Type::kMeshWithPaintColor: return SkString("mesh_with_paint_color");
1406 case Type::kMeshWithPaintImage: return SkString("mesh_with_paint_image");
1407 default: SkUNREACHABLE;
1408 }
1409 }
1410
onAnimate(double nanos)1411 bool onAnimate(double nanos) override {
1412 // `periodic` goes from zero to 2π every four seconds, then wraps around.
1413 double periodic = nanos / 4'000'000'000.;
1414 periodic -= std::floor(periodic);
1415 periodic *= 2 * SK_DoublePI;
1416
1417 double xOff[kMeshSize], yOff[kMeshSize];
1418 for (int index = 0; index < kMeshSize; ++index) {
1419 xOff[index] = std::sin(periodic) * kRippleSize;
1420 yOff[index] = std::sin(periodic + 10.0) * kRippleSize;
1421 periodic += 0.8;
1422 }
1423
1424 fVerts.clear();
1425 for (int y = 0; y < kMeshSize; ++y) {
1426 float yf = (float)y / (kMeshSize - 1); // yf = 0 .. 1
1427 for (int x = 0; x < kMeshSize; ++x) {
1428 float xf = (float)x / (kMeshSize - 1); // xf = 0 .. 1
1429
1430 Vertex* vert = &fVerts.push_back();
1431 vert->pos[0] = kRect.left() + xf * kRect.width() + xOff[y];
1432 vert->pos[1] = kRect.top() + yf * kRect.height() + yOff[x];
1433 vert->uv[0] = kUV.left() + xf * kUV.width();
1434 vert->uv[1] = kUV.top() + yf * kUV.height();
1435 }
1436 }
1437
1438 return true;
1439 }
1440
onDraw(SkCanvas * canvas,SkString *)1441 DrawResult onDraw(SkCanvas* canvas, SkString*) override {
1442 SkRuntimeEffect::ChildPtr child[4] = {fShader1, fShader2, fColorFilter, fBlender};
1443
1444 GrRecordingContext* rc = canvas->recordingContext();
1445 GrDirectContext* dc = GrAsDirectContext(rc);
1446 fVB->update(dc, fVerts.data(), /*offset=*/0, fVerts.size_bytes());
1447
1448 SkMesh::Result result = SkMesh::MakeIndexed(fSpec,
1449 SkMesh::Mode::kTriangles,
1450 fVB,
1451 fVerts.size(),
1452 /*vertexOffset=*/0,
1453 fIB,
1454 fIndices.size(),
1455 /*indexOffset=*/0,
1456 /*uniforms=*/nullptr,
1457 /*children=*/child,
1458 kRect.makeOutset(kRippleSize, kRippleSize));
1459 if (!result.mesh.isValid()) {
1460 SkDebugf("Mesh creation failed: %s\n", result.error.c_str());
1461 return DrawResult::kFail;
1462 }
1463
1464 SkPaint paint;
1465 paint.setShader(fPaintShader);
1466 canvas->drawMesh(result.mesh, SkBlender::Mode(SkBlendMode::kDstOver), paint);
1467
1468 return DrawResult::kOk;
1469 }
1470
1471 private:
ensureBuffers()1472 void ensureBuffers() {
1473 if (!fVB) {
1474 fVB = SkMeshes::MakeVertexBuffer(fVerts.data(), fVerts.size_bytes());
1475 }
1476 if (!fIB) {
1477 fIB = SkMeshes::MakeIndexBuffer(fIndices.data(), fIndices.size_bytes());
1478 }
1479 }
1480
1481 struct Vertex {
1482 float pos[2];
1483 float uv[2];
1484 };
1485
1486 static constexpr auto kRect = SkRect::MakeLTRB(20, 20, 300, 300);
1487 static constexpr auto kUV = SkRect::MakeLTRB( 0, 0, 128, 128);
1488 static constexpr int kMeshSize = 16;
1489 static constexpr float kRippleSize = 6.0f;
1490
1491 Type fType;
1492
1493 TArray<Vertex> fVerts;
1494 TArray<uint16_t> fIndices;
1495
1496 sk_sp<SkShader> fShader1, fShader2, fPaintShader;
1497 sk_sp<SkColorFilter> fColorFilter;
1498 sk_sp<SkBlender> fBlender;
1499
1500 sk_sp<SkMeshSpecification> fSpec;
1501
1502 sk_sp<SkMesh::VertexBuffer> fVB;
1503 sk_sp<SkMesh::IndexBuffer> fIB;
1504 };
1505
1506 DEF_GM(return new MeshWithShadersGM(MeshWithShadersGM::Type::kMeshWithImage))
DEF_GM(return new MeshWithShadersGM (MeshWithShadersGM::Type::kMeshWithPaintColor))1507 DEF_GM(return new MeshWithShadersGM(MeshWithShadersGM::Type::kMeshWithPaintColor))
1508 DEF_GM(return new MeshWithShadersGM(MeshWithShadersGM::Type::kMeshWithPaintImage))
1509 DEF_GM(return new MeshWithShadersGM(MeshWithShadersGM::Type::kMeshWithEffects))
1510
1511 DEF_SIMPLE_GM_CAN_FAIL(custommesh_cs_uniforms, canvas, errorMsg, 200, 900) {
1512 if (!canvas->recordingContext() && !canvas->recorder()) {
1513 *errorMsg = GM::kErrorMsg_DrawSkippedGpuOnly;
1514 return DrawResult::kSkip;
1515 }
1516
1517 // Shared data
1518 static constexpr SkRect kRect = SkRect::MakeLTRB(20, 20, 80, 80);
1519 static constexpr SkPoint kQuad[]{
1520 {kRect.left(), kRect.top()},
1521 {kRect.right(), kRect.top()},
1522 {kRect.left(), kRect.bottom()},
1523 {kRect.right(), kRect.bottom()},
1524 };
1525 sk_sp<SkMesh::VertexBuffer> vb = SkMeshes::MakeVertexBuffer(kQuad, sizeof(kQuad));
1526 sk_sp<SkData> unis = SkData::MakeWithCopy(&SkColors::kRed, sizeof(SkColor4f));
1527
1528 // Surface helper
1529 auto makeSurface = [=](sk_sp<SkColorSpace> cs) {
1530 SkImageInfo ii = SkImageInfo::MakeN32Premul(200, 100, cs);
1531 sk_sp<SkSurface> surface = canvas->makeSurface(ii);
1532 return surface ? surface : SkSurfaces::Raster(ii);
1533 };
1534
1535 // Mesh helper
1536 enum class Managed : bool { kNo, kYes };
1537 auto makeMesh = [&](Managed managed, sk_sp<SkColorSpace> workingCS) {
1538 static const SkMeshSpecification::Attribute kAttributes[]{
1539 {SkMeshSpecification::Attribute::Type::kFloat2, 0, SkString{"pos"}},
1540 };
1541
1542 static constexpr char kVS[] = R"(
1543 Varyings main(in const Attributes attributes) {
1544 Varyings varyings;
1545 varyings.position = attributes.pos;
1546 return varyings;
1547 }
1548 )";
1549 static constexpr char kManagedFS[] = R"(
1550 layout(color) uniform half4 color;
1551 float2 main(const Varyings varyings, out half4 c) {
1552 c = color;
1553 return varyings.position;
1554 }
1555 )";
1556 static constexpr char kRawFS[] = R"(
1557 uniform half4 color;
1558 float2 main(const Varyings varyings, out half4 c) {
1559 c = color;
1560 return varyings.position;
1561 }
1562 )";
1563
1564 auto [spec, error] = SkMeshSpecification::Make(
1565 kAttributes,
1566 sizeof(SkPoint),
1567 /*varyings=*/{},
1568 SkString(kVS),
1569 SkString(managed == Managed::kYes ? kManagedFS : kRawFS),
1570 std::move(workingCS),
1571 kPremul_SkAlphaType);
1572 SkASSERT(spec);
1573
1574 SkMesh::Result result = SkMesh::Make(std::move(spec),
1575 SkMesh::Mode::kTriangleStrip,
1576 vb,
1577 /*vertexCount=*/4,
1578 /*vertexOffset=*/0,
1579 /*uniforms=*/unis,
1580 /*children=*/{},
1581 kRect);
1582 SkASSERT(result.mesh.isValid());
1583 return result.mesh;
1584 };
1585
1586 sk_sp<SkColorSpace> null = nullptr,
1587 srgb = SkColorSpace::MakeSRGB(),
1588 spin = SkColorSpace::MakeSRGB()->makeColorSpin(),
1589 wide = SkColorSpace::MakeRGB(SkNamedTransferFn::k2Dot2,
1590 SkNamedGamut::kRec2020);
1591
1592 struct Config {
1593 sk_sp<SkColorSpace> fMeshCS;
1594 sk_sp<SkColorSpace> fSurfaceCS;
1595 Managed fManaged;
1596 SkColor fExpectedColor = SK_ColorRED;
1597 };
1598 static const Config kConfigs[] = {
1599 // Uniforms should remain in sRGB mode, then get converted to destination after mesh FS
1600 // Before b/316594914 was fixed, these would get double-converted:
1601 {srgb, null, Managed::kYes},
1602 {srgb, srgb, Managed::kYes},
1603 {srgb, spin, Managed::kYes},
1604 {srgb, wide, Managed::kYes},
1605
1606 // Uniforms should be converted to working space (spun), then converted to destination
1607 {spin, srgb, Managed::kYes},
1608 {spin, spin, Managed::kYes},
1609 {spin, wide, Managed::kYes},
1610
1611 // Non-managed uniforms serve as a control group. The red uniforms are not converted to
1612 // the working space. The mesh FS returns "red" {1, 0, 0, 1}, but that's actually green,
1613 // because the working space of the mesh is `spin`. That output is converted to dest,
1614 // rendering as green. Therefore, we manually change the control color's box to green.
1615 {spin, srgb, Managed::kNo, SK_ColorGREEN},
1616 {spin, wide, Managed::kNo, SK_ColorGREEN},
1617 };
1618
1619 for (const Config& config : kConfigs) {
1620 SkMesh mesh = makeMesh(config.fManaged, config.fMeshCS);
1621
1622 sk_sp<SkSurface> offscreen = makeSurface(config.fSurfaceCS);
1623 SkCanvas* offscreenCanvas = offscreen->getCanvas();
1624
1625 SkPaint paint;
1626 offscreenCanvas->drawMesh(mesh, SkBlender::Mode(SkBlendMode::kDst), paint);
1627 offscreenCanvas->translate(100, 0);
1628 paint.setColor(config.fExpectedColor);
1629 offscreenCanvas->drawRect(kRect, paint);
1630
1631 offscreen->draw(canvas, 0, 0);
1632 canvas->translate(0, 100);
1633 }
1634
1635 return DrawResult::kOk;
1636 }
1637
1638 } // namespace skiagm
1639