Flutter Engine
The Flutter Engine
mesh.cpp
Go to the documentation of this file.
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"
13#include "include/core/SkData.h"
14#include "include/core/SkMesh.h"
24#include "src/base/SkRandom.h"
26#include "tools/DecodeUtils.h"
28
29using namespace skia_private;
30
31namespace skiagm {
32
33class MeshGM : public skiagm::GM {
34public:
35 MeshGM() {}
36
37protected:
40
41 SkISize getISize() override { return {435, 1180}; }
42
43 void onOnceBeforeDraw() override {
44 {
45 static const Attribute kAttributes[]{
48 };
49 static const Varying kVaryings[]{
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 )";
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[]{
85 };
86 static const Varying kVaryings[]{
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 )";
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,
121 }
122
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
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
147 SkString getName() const override { return SkString("custommesh"); }
148
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),
155 canvas->save();
156 for (uint8_t alpha : {0xFF , 0x40})
157 for (bool colors : {false, true})
158 for (bool shader : {false, true}) {
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,
165 fColorVB,
166 /*vertexCount=*/4,
167 /*vertexOffset=*/0,
168 /*uniforms=*/nullptr,
169 /*children=*/nullChild,
170 kRect);
171 } else {
172 result = SkMesh::Make(fSpecWithNoColor,
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,
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,
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
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
231private:
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.
336
339 sk_sp<SkMesh::VertexBuffer> fColorIndexedVB;
340 sk_sp<SkMesh::VertexBuffer> fNoColorIndexedVB;
341};
342
343DEF_GM(return new MeshGM;)
344
346public:
348
349protected:
352
353 SkISize getISize() override { return {468, 258}; }
354
355 void onOnceBeforeDraw() override {
356 static const Attribute kAttributes[]{
358 {Attribute::Type::kFloat4, 8, SkString{"color"}},
359 };
360 static const Varying kVaryings[]{
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(
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}};
411 fShader = SkGradientShader::MakeLinear(pts, colors, nullptr, 2, SkTileMode::kMirror);
412
413 fVB = SkMeshes::MakeVertexBuffer(kQuad, sizeof(kQuad));
414 }
415
416 SkString getName() const override { return SkString("custommesh_cs"); }
417
419 // Force an intermediate surface if the canvas is in "legacy" mode
420 SkCanvas* c = canvas;
422 if (!c->imageInfo().colorSpace()) {
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();
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)],
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
450 paint.setShader(useShader ? fShader : nullptr);
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
466private:
467 struct Vertex {
468 SkPoint pos;
470 };
471
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
486
488
489 sk_sp<SkShader> fShader;
490};
491
492// helpers for cases when ctx could be nullptr
494 const void* data,
495 size_t size) {
496 if (ctx) {
498 }
500}
501
503 const void* data,
504 size_t size) {
505 if (ctx) {
506 return SkMeshes::MakeIndexBuffer(ctx, data, size);
507 }
509}
510
511DEF_GM(return new MeshColorSpaceGM;)
512
514public:
515 MeshUniformsGM() { this->onAnimate(0); }
516
517protected:
520
521 SkISize getISize() override { return {140, 250}; }
522
523 void onOnceBeforeDraw() override {
524 static const Attribute kAttributes[]{
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] =
553 sizeof(Vertex),
554 kVaryings,
555 SkString(kVS),
556 SkString(kFS),
559 if (!spec) {
560 SkDebugf("%s\n", error.c_str());
561 }
562 fSpec = std::move(spec);
563
565 fShader = SkGradientShader::MakeRadial(kGradCenter,
566 .4f,
567 colors,
568 nullptr,
569 2,
571
572 fVB = SkMeshes::MakeVertexBuffer(kQuad, sizeof(kQuad));
573 }
574
575 SkString getName() const override { return SkString("custommesh_uniforms"); }
576
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,
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
625 paint.setShader(fShader);
627
628 canvas->translate(0, kRect.height() + 10);
629 }
630 return DrawResult::kOk;
631 }
632
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
643private:
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
670
672
673 sk_sp<SkShader> fShader;
674};
675
676DEF_GM(return new MeshUniformsGM())
677
678class MeshUpdateGM : public skiagm::GM {
679public:
680 MeshUpdateGM() = default;
681
682protected:
685
686 SkISize getISize() override { return {270, 490}; }
687
688 void onOnceBeforeDraw() override {
689 static const Attribute kAttributes[]{
691 {Attribute::Type::kFloat2, 8, SkString{"coords"}},
692 };
693 static const Varying kVaryings[]{
694 {Varying::Type::kFloat2, SkString{"coords"}},
695 };
696 static constexpr char kVS[] = R"(
697 Varyings main(const in Attributes attributes) {
698 Varyings varyings;
699 varyings.coords = attributes.coords;
700 varyings.position = attributes.pos;
701 return varyings;
702 }
703 )";
704 static constexpr char kFS[] = R"(
705 float2 main(const Varyings varyings) { return varyings.coords; }
706 )";
708 sizeof(Vertex),
709 kVaryings,
710 SkString(kVS),
711 SkString(kFS),
714 if (!spec) {
715 SkDebugf("%s\n", error.c_str());
716 }
717 fSpec = std::move(spec);
718
721 colors,
722 /*rowBytes=*/8);
725 }
726
727 SkString getName() const override { return SkString("mesh_updates"); }
728
730 canvas->clear(SK_ColorBLACK);
731
734 if (rc && !dc) {
735 // On GPU this relies on using the DC to update the GPU backed vertex/index buffers.
736 return DrawResult::kSkip;
737 }
738
739 if (dc && dc->abandoned()) {
740 return DrawResult::kSkip;
741 }
742
744 paint.setShader(fShader);
745
746 SkRect r = SkRect::MakeXYWH(10.f, 10.f, 50.f, 50.f);
747
748 // We test updating CPU and GPU buffers.
749 for (bool gpuBuffer : {false, true}) {
750 auto ctx = gpuBuffer ? dc : nullptr;
751
752 // How many rects worth of storage is in the vertex buffer?
753 static constexpr int kVBRects = 2;
754
755 // How many times do we update the vertex buffer? Wraps to start of buffer if
756 // > kVBRects.
757 static constexpr int kUpdatesRects = 3;
758
759 auto vb = make_vertex_buffer(ctx, /*data=*/nullptr, kVBRects * 6 * sizeof(Vertex));
760 SkASSERT(vb);
761
763 for (int i = 0; i < kUpdatesRects; ++i) {
764 auto p = r.makeOffset(100.f*i, 0.f);
765 if (i) {
766 bounds.join(p);
767 } else {
768 bounds = p;
769 }
770
771 SkPoint t[4];
772 SkRect::MakeWH(2.f, 2.f).toQuad(t);
773 SkMatrix::RotateDeg(90.f*i, {1.f, 1.f}).mapPoints(t, std::size(t));
774
775 Vertex vertices[6];
776 vertices[0] = {{p.left(), p.top()}, t[0]};
777 vertices[1] = {{p.left(), p.bottom()}, t[3]};
778 vertices[2] = {{p.right(), p.top()}, t[1]};
779 vertices[3] = vertices[2];
780 vertices[4] = vertices[1];
781 vertices[5] = {{p.right(), p.bottom()}, t[2]};
782
783 size_t offset = 6*(i % kVBRects)*sizeof(Vertex);
784 SkAssertResult(vb->update(ctx, vertices, offset, 6*sizeof(Vertex)));
785 // Make there aren't accidentally deferred reads of the client data.
786 std::memset(vertices, 0, sizeof(vertices));
787
788 int rectCount = std::min(i + 1, kVBRects);
789 auto result = SkMesh::Make(fSpec,
791 vb,
792 /*vertexCount=*/6 * rectCount,
793 /*vertexOffset=*/0,
794 /*uniforms=*/nullptr,
795 /*children=*/{},
796 bounds);
797
798 if (!result.mesh.isValid()) {
799 SkDebugf("Mesh creation failed: %s\n", result.error.c_str());
800 return DrawResult::kFail;
801 }
802
804
805 canvas->translate(0, r.height() + 10);
806 }
807
808 // Now test updating an IB.
809
810 // How many rects worth of storage is in the index buffer?
811 static constexpr int kIBRects = 2;
812
813 // How many times do we update the index buffer? Wraps to start of buffer if > kIBRects.
814 static constexpr int kNumIBUpdates = 3;
815
816 // Make the vertex buffer large enough to hold all the rects and populate.
817 vb = make_vertex_buffer(ctx, /*data=*/nullptr, kNumIBUpdates * 4 * sizeof(Vertex));
818 SkASSERT(vb);
819 for (int i = 0; i < kNumIBUpdates; ++i) {
820 SkPoint p[4];
821 auto rect = r.makeOffset(100*i, 0);
822 rect.toQuad(p);
823 if (i) {
824 bounds.join(rect);
825 } else {
826 bounds = rect;
827 }
828
829 SkPoint t[4];
830 SkRect::MakeWH(2.f, 2.f).toQuad(t);
831 SkMatrix::RotateDeg(90.f*i, {1.f, 1.f}).mapPoints(t, std::size(t));
832 Vertex vertices[4]{{p[0], t[0]}, {p[1], t[1]}, {p[2], t[2]}, {p[3], t[3]}};
834 vb->update(ctx, vertices, i*4*sizeof(Vertex), 4*sizeof(Vertex)));
835 }
836
837 auto ib = make_index_buffer(
838 ctx, /*data=*/nullptr, kIBRects * 6 * sizeof(uint16_t));
839 SkASSERT(ib);
840 for (int i = 0; i < kNumIBUpdates; ++i) {
841 uint16_t indices[6] = {SkToU16(0 + 4*i),
842 SkToU16(3 + 4*i),
843 SkToU16(1 + 4*i),
844 SkToU16(1 + 4*i),
845 SkToU16(3 + 4*i),
846 SkToU16(2 + 4*i)};
847 size_t offset = 6*(i % kIBRects)*sizeof(uint16_t);
848 SkAssertResult(ib->update(ctx, indices, offset, 6*sizeof(uint16_t)));
849 std::memset(indices, 0, 6*sizeof(uint16_t));
850
851 auto result = SkMesh::MakeIndexed(fSpec,
853 vb,
854 /*vertexCount=*/4 * kNumIBUpdates,
855 /*vertexOffset=*/0,
856 ib,
857 /*indexCount=*/6,
858 /*indexOffset=*/offset,
859 /*uniforms=*/nullptr,
860 /*children=*/{},
861 bounds);
862
863 if (!result.mesh.isValid()) {
864 SkDebugf("Mesh creation failed: %s\n", result.error.c_str());
865 return DrawResult::kFail;
866 }
867
869 }
870 canvas->translate(0, r.height() + 10);
871 }
872
873 return DrawResult::kOk;
874 }
875
876private:
877 struct Vertex {
878 SkPoint pos;
879 SkPoint tex;
880 };
881
883
884 sk_sp<SkShader> fShader;
885};
886
887DEF_GM(return new MeshUpdateGM())
888
889class MeshZeroInitGM : public skiagm::GM {
890public:
891 MeshZeroInitGM() = default;
892
893protected:
896
897 SkISize getISize() override { return {90, 30}; }
898
899 void onOnceBeforeDraw() override {
900 static const Attribute kAttributes1[]{
903 };
904 static const Attribute kAttributes2[]{
907 };
908 static const Varying kVaryings[]{{Varying::Type::kHalf4, SkString{"color"}}};
909 static constexpr char kVS[] = R"(
910 Varyings main(const in Attributes attributes) {
911 Varyings varyings;
912 varyings.color = attributes.color;
913 varyings.position = attributes.pos;
914 return varyings;
915 }
916 )";
917 static constexpr char kFS[] = R"(
918 float2 main(const Varyings varyings, out half4 color) {
919 color = varyings.color;
920 return varyings.position;
921 }
922 )";
923 auto result = SkMeshSpecification::Make(kAttributes1,
924 /*vertexStride==*/12,
925 kVaryings,
926 SkString(kVS),
927 SkString(kFS),
930 if (!result.specification) {
931 SkDebugf("%s\n", result.error.c_str());
932 }
933 fSpec[0] = std::move(result.specification);
934
935 result = SkMeshSpecification::Make(kAttributes1,
936 /*vertexStride=*/12,
937 kVaryings,
938 SkString(kVS),
939 SkString(kFS),
942 if (!result.specification) {
943 SkDebugf("%s\n", result.error.c_str());
944 }
945 fSpec[1] = std::move(result.specification);
946 }
947
948 SkString getName() const override { return SkString("mesh_zero_init"); }
949
953 if (rc && !dc) {
954 // On GPU this relies on using the DC to update the GPU backed vertex/index buffers.
955 return DrawResult::kSkip;
956 }
957
958 if (dc && dc->abandoned()) {
959 return DrawResult::kSkip;
960 }
961
962 static constexpr SkPoint kTri[]{{10, 10}, {20, 10}, {10, 20}};
963 // The zero will come from the uninit part of the buffer.
964 static constexpr uint16_t kTiIndices[]{1, 2};
965
966 // We test updating CPU and GPU buffers.
967 for (bool gpuBuffer : {false, true}) {
968 auto ctx = gpuBuffer ? dc : nullptr;
969 for (int i = 0; i < 2; ++i) {
970 const auto& spec = fSpec[i];
971
972 size_t posOffset = spec->findAttribute("pos")->offset;
973 auto vb = make_vertex_buffer(ctx, nullptr, spec->stride() * std::size(kTri));
974 SkASSERT(vb);
975 for (size_t j = 0; j < std::size(kTri); ++j) {
976 SkAssertResult(vb->update(ctx,
977 &kTri[j],
978 spec->stride()*j + posOffset,
979 sizeof(kTri[j])));
980 }
981
982 // The first time we make the indices be 0,1,2 using the zero'ed buffer for the
983 // first. However, because uploads must be 4 byte aligned it's actually 0,0,1,2.
984 // The second time we upload 1,2 to beginning of the buffer to form 1,2,0.
985 size_t indexUploadOffset = i == 0 ? 4 : 0;
986 size_t indexMeshOffset = i == 0 ? 2 : 0;
987
988 auto ib = make_index_buffer(ctx, nullptr, sizeof(uint16_t) * 4);
989 SkASSERT(ib);
990 SkAssertResult(ib->update(ctx, kTiIndices, indexUploadOffset, sizeof(kTiIndices)));
991
993 bounds.setBounds(kTri, std::size(kTri));
994 auto result = SkMesh::MakeIndexed(spec,
996 std::move(vb),
997 /*vertexCount=*/std::size(kTri),
998 /*vertexOffset=*/0,
999 std::move(ib),
1000 /*indexCount=*/std::size(kTiIndices) + 1,
1001 indexMeshOffset,
1002 /*uniforms=*/nullptr,
1003 /*children=*/{},
1004 bounds);
1005 if (!result.mesh.isValid()) {
1006 SkDebugf("Mesh creation failed: %s\n", result.error.c_str());
1007 return DrawResult::kFail;
1008 }
1009
1010 SkPaint paint;
1011 // The color will be transparent black. Set the blender to kDstOver so when combined
1012 // with the paint's opaque black we get opaque black.
1014 canvas->translate(bounds.width() + 10, 0);
1015 if (ctx) {
1016 // Free up the buffers for recycling in the cache. This helps test that
1017 // a recycled buffer gets zero'ed.
1018 result.mesh = {};
1019 SkASSERT(!ib); // NOLINT - bugprone-use-after-move. We're asserting it's moved.
1020 SkASSERT(!vb); // NOLINT
1021 ctx->flushAndSubmit(GrSyncCpu::kYes);
1022 }
1023 }
1024 }
1025
1026 return DrawResult::kOk;
1027 }
1028
1029private:
1031};
1032
1033DEF_GM(return new MeshZeroInitGM())
1034
1035// We have a special GM for testing SkMesh through SkPicture because all of SkPicture GM testing
1036// uses the CPU backend and SkMesh only works on GPU.
1037class PictureMesh : public skiagm::GM {
1038public:
1039 PictureMesh() = default;
1040
1041protected:
1044
1045 SkISize getISize() override { return {390, 90}; }
1046
1047 void onOnceBeforeDraw() override {
1048 static const Attribute kAttributes[]{
1050 };
1051 static const Varying kVaryings[]{
1052 {Varying::Type::kFloat2, SkString{"coords"}},
1053 };
1054 static constexpr char kVS[] = R"(
1055 Varyings main(in const Attributes attributes) {
1056 Varyings varyings;
1057 varyings.position = attributes.pos;
1058 return varyings;
1059 }
1060 )";
1061 static const SkString kFS = SkStringPrintf(R"(
1062 uniform float2 offset;
1063 float2 main(const Varyings varyings, out float4 color) {
1064 float2 tl = float2(%f, %f);
1065 float2 wh = float2(%f, %f);
1066 float2 c = tl + wh/2;
1067 float r = length(wh)/4;
1068 color.rba = float3(1);
1069 color.g = min(1, length(varyings.position - c + offset) / r);
1070 return varyings.position;
1071 }
1072 )", kRect.x(), kRect.y(), kRect.width(), kRect.height());
1073 auto [spec, error] =
1075 sizeof(Vertex),
1076 kVaryings,
1077 SkString(kVS),
1078 kFS,
1079 SkColorSpace::MakeSRGB()->makeColorSpin(),
1081 if (!spec) {
1082 SkDebugf("%s\n", error.c_str());
1083 }
1084 fSpec = std::move(spec);
1085
1086 fVB = SkMeshes::MakeVertexBuffer(kQuad, sizeof(kQuad));
1088
1089 SkRandom random;
1090 SkColor4f colors[6];
1091 for (size_t i = 0; i < std::size(colors) - 1; ++i) {
1092 colors[i] = {random.nextF(), random.nextF(), random.nextF(), 1.f};
1093 }
1094 colors[std::size(colors) - 1] = colors[0];
1095 SkPaint paint;
1096 SkGradientShader::Interpolation interpolation;
1098 fShader = SkGradientShader::MakeSweep(kRect.centerX(), kRect.centerY(),
1099 colors,
1101 nullptr,
1104 0,
1105 360.f,
1106 interpolation,
1107 /*localMatrix=*/nullptr);
1108 }
1109
1110 SkString getName() const override { return SkString("picture_mesh"); }
1111
1113 SkPaint paint;
1114 paint.setShader(fShader);
1115
1116 auto dc = GrAsDirectContext(canvas->recordingContext());
1117 for (bool picture : {false, true}) {
1118 canvas->save();
1119 for (bool gpu : {false, true}) {
1120 auto vb = gpu ? SkMeshes::CopyVertexBuffer(dc, fVB) : fVB;
1121 auto ib = gpu ? SkMeshes::CopyIndexBuffer (dc, fIB) : fIB;
1122
1123 float offset[2] = {8, 8};
1124 for (size_t i = 0; i < 4; ++i) {
1125 auto uniforms = SkData::MakeWithCopy(&offset, sizeof(offset));
1127 switch (i) {
1128 case 0:
1129 r = SkMesh::Make(fSpec,
1131 fVB,
1132 6,
1133 1 * sizeof(Vertex),
1134 std::move(uniforms),
1135 /*children=*/{},
1136 kRect);
1137 break;
1138 case 1:
1139 r = SkMesh::Make(fSpec,
1141 fVB,
1142 4,
1143 1 * sizeof(Vertex),
1144 std::move(uniforms),
1145 /*children=*/{},
1146 kRect);
1147 break;
1148 case 2:
1149 r = SkMesh::MakeIndexed(fSpec,
1151 fVB,
1152 std::size(kQuad),
1153 0,
1154 fIB,
1155 6,
1156 2 * (sizeof(uint16_t)),
1157 std::move(uniforms),
1158 /*children=*/{},
1159 kRect);
1160 break;
1161 case 3:
1162 r = SkMesh::MakeIndexed(fSpec,
1164 fVB,
1165 std::size(kQuad),
1166 0,
1167 fIB,
1168 6,
1169 2 * sizeof(uint16_t),
1170 std::move(uniforms),
1171 /*children=*/{},
1172 kRect);
1173 break;
1174 }
1175
1176 if (!r.mesh.isValid()) {
1177 *error = r.error;
1178 return DrawResult::kFail;
1179 }
1180
1181 auto draw = [&](SkCanvas* c) {
1183 };
1184 if (picture) {
1185 SkPictureRecorder recorder;
1186 draw(recorder.beginRecording(SkRect::Make(this->getISize()),
1187 /*bbhFactory=*/nullptr));
1188 canvas->drawPicture(recorder.finishRecordingAsPicture());
1189 } else {
1190 draw(canvas);
1191 }
1192 offset[i%2] *= -1;
1193 canvas->translate(kRect.width() + 10, 0);
1194 }
1195 }
1196 canvas->restore();
1197 canvas->translate(0, kRect.height() + 10);
1198 }
1199 return DrawResult::kOk;
1200 }
1201
1202private:
1203 struct Vertex {
1204 SkPoint pos;
1205 };
1206
1207 static constexpr auto kRect = SkRect::MakeWH(40, 40);
1208
1209 static constexpr Vertex kQuad[] {
1210 {1000, 1000}, // skip
1211 {{kRect.left() , kRect.top() }},
1212 {{kRect.right(), kRect.top() }},
1213 {{kRect.left() , kRect.bottom()}},
1214 {{kRect.right(), kRect.bottom()}},
1215 {{kRect.left() , kRect.bottom()}},
1216 {{kRect.right(), kRect.top() }},
1217 };
1218
1219 static constexpr uint16_t kIndices[] = {1000, 2000, 1, 2, 3, 4, 5, 6};
1220
1222
1224
1226
1227 sk_sp<SkShader> fShader;
1228};
1229
1230DEF_GM(return new PictureMesh())
1231
1232class MeshWithShadersGM : public skiagm::GM {
1233public:
1234 enum class Type {
1235 kMeshWithImage,
1236 kMeshWithPaintColor,
1237 kMeshWithPaintImage,
1238 kMeshWithEffects,
1239 };
1240
1242 // Create a grid of evenly spaced points for our mesh
1243 this->onAnimate(0.0);
1244
1245 // Create an index buffer of triangles over our point mesh.
1246 for (int y = 0; y < kMeshSize - 1; ++y) {
1247 for (int x = 0; x < kMeshSize - 1; ++x) {
1248 SkASSERT(((y + 1) * kMeshSize + x + 1) < fVerts.size());
1249
1250 uint16_t TL = y * kMeshSize + x;
1251 uint16_t TR = y * kMeshSize + x + 1;
1252 uint16_t BL = (y + 1) * kMeshSize + x;
1253 uint16_t BR = (y + 1) * kMeshSize + x + 1;
1254
1255 fIndices.push_back(TL);
1256 fIndices.push_back(TR);
1257 fIndices.push_back(BL);
1258
1259 fIndices.push_back(BR);
1260 fIndices.push_back(BL);
1261 fIndices.push_back(TR);
1262 }
1263 }
1264 }
1265
1266protected:
1269
1270 SkISize getISize() override { return {320, 320}; }
1271
1272 void onOnceBeforeDraw() override {
1273 {
1274 static const Attribute kAttributes[] = {
1275 {Attribute::Type::kFloat2, 0, SkString{"position"}},
1277 };
1278 static const Varying kVaryings[] = {
1280 };
1281 static constexpr char kVS[] = R"(
1282 Varyings main(const in Attributes attributes) {
1283 Varyings varyings;
1284 varyings.uv = attributes.uv;
1285 varyings.position = attributes.position;
1286 return varyings;
1287 }
1288 )";
1289 static constexpr char kFS[] = R"(
1290 uniform shader myShader1;
1291 uniform shader myShader2;
1292 uniform colorFilter myColorFilter;
1293 uniform blender myBlend;
1294
1295 float2 main(const in Varyings varyings, out half4 color) {
1296 half4 color1 = myShader1.eval(varyings.uv);
1297 half4 color2 = myShader2.eval(varyings.uv);
1298
1299 // Apply a inverse color filter to the first image.
1300 color1 = myColorFilter.eval(color1);
1301
1302 // Fade in the second image horizontally, leveraging the UVs.
1303 color2 *= varyings.uv.x / 128.0;
1304
1305 // Combine the two images by using a blender (set to dst-over).
1306 color = myBlend.eval(color1, color2);
1307
1308 return varyings.uv;
1309 }
1310 )";
1312 sizeof(Vertex),
1313 kVaryings,
1314 SkString(kVS),
1315 SkString(kFS));
1316 if (!spec) {
1317 SkDebugf("%s\n", error.c_str());
1318 }
1319 fSpec = std::move(spec);
1320 }
1321
1322
1323 switch (fType) {
1324 case Type::kMeshWithImage: {
1325 fShader1 = ToolUtils::GetResourceAsImage("images/mandrill_128.png")
1327 fShader2 = nullptr;
1328 fColorFilter = nullptr;
1329 fBlender = nullptr;
1330 fPaintShader = nullptr;
1331 break;
1332 }
1333 case Type::kMeshWithEffects: {
1334 uint8_t inverseTable[256];
1335 for (int index = 0; index < 256; ++index) {
1336 inverseTable[index] = 255 - index;
1337 }
1338
1339 fShader1 = ToolUtils::GetResourceAsImage("images/mandrill_128.png")
1341 fShader2 = ToolUtils::GetResourceAsImage("images/color_wheel.png")
1343 fColorFilter = SkColorFilters::TableARGB(/*tableA=*/nullptr,
1344 inverseTable,
1345 inverseTable,
1346 inverseTable);
1348 fPaintShader = nullptr;
1349 break;
1350 }
1351 case Type::kMeshWithPaintColor: {
1352 fShader1 = nullptr;
1353 fShader2 = ToolUtils::GetResourceAsImage("images/mandrill_128.png")
1355 fColorFilter = nullptr;
1357 fPaintShader = SkShaders::Color(SK_ColorGREEN);
1358 break;
1359 }
1360 case Type::kMeshWithPaintImage: {
1361 fShader1 = ToolUtils::GetResourceAsImage("images/color_wheel.png")
1363 fShader2 = nullptr;
1364 fColorFilter = nullptr;
1365 fBlender = nullptr;
1366 fPaintShader = ToolUtils::GetResourceAsImage("images/mandrill_128.png")
1368 break;
1369 }
1370 default:
1372 }
1373 }
1374
1376 auto dc = GrAsDirectContext(canvas->recordingContext());
1377 this->ensureBuffers();
1378 if (!dc || dc->abandoned()) {
1379 return DrawResult::kOk;
1380 }
1381
1382 fVB = SkMeshes::CopyVertexBuffer(dc, fVB);
1383 fIB = SkMeshes::CopyIndexBuffer (dc, fIB);
1384 return (!fVB || !fIB) ? DrawResult::kFail
1386 }
1387
1388 void onGpuTeardown() override {
1389 // Destroy the GPU buffers and recreate on CPU
1390 fVB = nullptr;
1391 fIB = nullptr;
1392 this->ensureBuffers();
1393 }
1394
1395 SkString getName() const override {
1396 switch (fType) {
1397 case Type::kMeshWithImage: return SkString("mesh_with_image");
1398 case Type::kMeshWithEffects: return SkString("mesh_with_effects");
1399 case Type::kMeshWithPaintColor: return SkString("mesh_with_paint_color");
1400 case Type::kMeshWithPaintImage: return SkString("mesh_with_paint_image");
1401 default: SkUNREACHABLE;
1402 }
1403 }
1404
1405 bool onAnimate(double nanos) override {
1406 // `periodic` goes from zero to 2Ï€ every four seconds, then wraps around.
1407 double periodic = nanos / 4'000'000'000.;
1408 periodic -= std::floor(periodic);
1409 periodic *= 2 * SK_DoublePI;
1410
1411 double xOff[kMeshSize], yOff[kMeshSize];
1412 for (int index = 0; index < kMeshSize; ++index) {
1413 xOff[index] = std::sin(periodic) * kRippleSize;
1414 yOff[index] = std::sin(periodic + 10.0) * kRippleSize;
1415 periodic += 0.8;
1416 }
1417
1418 fVerts.clear();
1419 for (int y = 0; y < kMeshSize; ++y) {
1420 float yf = (float)y / (kMeshSize - 1); // yf = 0 .. 1
1421 for (int x = 0; x < kMeshSize; ++x) {
1422 float xf = (float)x / (kMeshSize - 1); // xf = 0 .. 1
1423
1424 Vertex* vert = &fVerts.push_back();
1425 vert->pos[0] = kRect.left() + xf * kRect.width() + xOff[y];
1426 vert->pos[1] = kRect.top() + yf * kRect.height() + yOff[x];
1427 vert->uv[0] = kUV.left() + xf * kUV.width();
1428 vert->uv[1] = kUV.top() + yf * kUV.height();
1429 }
1430 }
1431
1432 return true;
1433 }
1434
1435 DrawResult onDraw(SkCanvas* canvas, SkString*) override {
1436 SkRuntimeEffect::ChildPtr child[4] = {fShader1, fShader2, fColorFilter, fBlender};
1437
1440 fVB->update(dc, fVerts.data(), /*offset=*/0, fVerts.size_bytes());
1441
1444 fVB,
1445 fVerts.size(),
1446 /*vertexOffset=*/0,
1447 fIB,
1448 fIndices.size(),
1449 /*indexOffset=*/0,
1450 /*uniforms=*/nullptr,
1451 /*children=*/child,
1452 kRect.makeOutset(kRippleSize, kRippleSize));
1453 if (!result.mesh.isValid()) {
1454 SkDebugf("Mesh creation failed: %s\n", result.error.c_str());
1455 return DrawResult::kFail;
1456 }
1457
1458 SkPaint paint;
1459 paint.setShader(fPaintShader);
1461
1462 return DrawResult::kOk;
1463 }
1464
1465private:
1466 void ensureBuffers() {
1467 if (!fVB) {
1468 fVB = SkMeshes::MakeVertexBuffer(fVerts.data(), fVerts.size_bytes());
1469 }
1470 if (!fIB) {
1471 fIB = SkMeshes::MakeIndexBuffer(fIndices.data(), fIndices.size_bytes());
1472 }
1473 }
1474
1475 struct Vertex {
1476 float pos[2];
1477 float uv[2];
1478 };
1479
1480 static constexpr auto kRect = SkRect::MakeLTRB(20, 20, 300, 300);
1481 static constexpr auto kUV = SkRect::MakeLTRB( 0, 0, 128, 128);
1482 static constexpr int kMeshSize = 16;
1483 static constexpr float kRippleSize = 6.0f;
1484
1485 Type fType;
1486
1487 TArray<Vertex> fVerts;
1488 TArray<uint16_t> fIndices;
1489
1490 sk_sp<SkShader> fShader1, fShader2, fPaintShader;
1491 sk_sp<SkColorFilter> fColorFilter;
1492 sk_sp<SkBlender> fBlender;
1493
1495
1498};
1499
1500DEF_GM(return new MeshWithShadersGM(MeshWithShadersGM::Type::kMeshWithImage))
1501DEF_GM(return new MeshWithShadersGM(MeshWithShadersGM::Type::kMeshWithPaintColor))
1502DEF_GM(return new MeshWithShadersGM(MeshWithShadersGM::Type::kMeshWithPaintImage))
1503DEF_GM(return new MeshWithShadersGM(MeshWithShadersGM::Type::kMeshWithEffects))
1504
1505DEF_SIMPLE_GM_CAN_FAIL(custommesh_cs_uniforms, canvas, errorMsg, 200, 900) {
1506 if (!canvas->recordingContext() && !canvas->recorder()) {
1508 return DrawResult::kSkip;
1509 }
1510
1511 // Shared data
1512 static constexpr SkRect kRect = SkRect::MakeLTRB(20, 20, 80, 80);
1513 static constexpr SkPoint kQuad[]{
1514 {kRect.left(), kRect.top()},
1515 {kRect.right(), kRect.top()},
1516 {kRect.left(), kRect.bottom()},
1517 {kRect.right(), kRect.bottom()},
1518 };
1521
1522 // Surface helper
1523 auto makeSurface = [=](sk_sp<SkColorSpace> cs) {
1524 SkImageInfo ii = SkImageInfo::MakeN32Premul(200, 100, cs);
1525 sk_sp<SkSurface> surface = canvas->makeSurface(ii);
1526 return surface ? surface : SkSurfaces::Raster(ii);
1527 };
1528
1529 // Mesh helper
1530 enum class Managed : bool { kNo, kYes };
1531 auto makeMesh = [&](Managed managed, sk_sp<SkColorSpace> workingCS) {
1534 };
1535
1536 static constexpr char kVS[] = R"(
1537 Varyings main(in const Attributes attributes) {
1538 Varyings varyings;
1539 varyings.position = attributes.pos;
1540 return varyings;
1541 }
1542 )";
1543 static constexpr char kManagedFS[] = R"(
1544 layout(color) uniform half4 color;
1545 float2 main(const Varyings varyings, out half4 c) {
1546 c = color;
1547 return varyings.position;
1548 }
1549 )";
1550 static constexpr char kRawFS[] = R"(
1551 uniform half4 color;
1552 float2 main(const Varyings varyings, out half4 c) {
1553 c = color;
1554 return varyings.position;
1555 }
1556 )";
1557
1558 auto [spec, error] = SkMeshSpecification::Make(
1560 sizeof(SkPoint),
1561 /*varyings=*/{},
1562 SkString(kVS),
1563 SkString(managed == Managed::kYes ? kManagedFS : kRawFS),
1564 std::move(workingCS),
1566 SkASSERT(spec);
1567
1568 SkMesh::Result result = SkMesh::Make(std::move(spec),
1570 vb,
1571 /*vertexCount=*/4,
1572 /*vertexOffset=*/0,
1573 /*uniforms=*/unis,
1574 /*children=*/{},
1575 kRect);
1576 SkASSERT(result.mesh.isValid());
1577 return result.mesh;
1578 };
1579
1580 sk_sp<SkColorSpace> null = nullptr,
1581 srgb = SkColorSpace::MakeSRGB(),
1585
1586 struct Config {
1587 sk_sp<SkColorSpace> fMeshCS;
1588 sk_sp<SkColorSpace> fSurfaceCS;
1589 Managed fManaged;
1590 SkColor fExpectedColor = SK_ColorRED;
1591 };
1592 static const Config kConfigs[] = {
1593 // Uniforms should remain in sRGB mode, then get converted to destination after mesh FS
1594 // Before b/316594914 was fixed, these would get double-converted:
1595 {srgb, null, Managed::kYes},
1596 {srgb, srgb, Managed::kYes},
1597 {srgb, spin, Managed::kYes},
1598 {srgb, wide, Managed::kYes},
1599
1600 // Uniforms should be converted to working space (spun), then converted to destination
1601 {spin, srgb, Managed::kYes},
1602 {spin, spin, Managed::kYes},
1603 {spin, wide, Managed::kYes},
1604
1605 // Non-managed uniforms serve as a control group. The red uniforms are not converted to
1606 // the working space. The mesh FS returns "red" {1, 0, 0, 1}, but that's actually green,
1607 // because the working space of the mesh is `spin`. That output is converted to dest,
1608 // rendering as green. Therefore, we manually change the control color's box to green.
1609 {spin, srgb, Managed::kNo, SK_ColorGREEN},
1610 {spin, wide, Managed::kNo, SK_ColorGREEN},
1611 };
1612
1613 for (const Config& config : kConfigs) {
1614 SkMesh mesh = makeMesh(config.fManaged, config.fMeshCS);
1615
1616 sk_sp<SkSurface> offscreen = makeSurface(config.fSurfaceCS);
1617 SkCanvas* offscreenCanvas = offscreen->getCanvas();
1618
1619 SkPaint paint;
1621 offscreenCanvas->translate(100, 0);
1622 paint.setColor(config.fExpectedColor);
1623 offscreenCanvas->drawRect(kRect, paint);
1624
1625 offscreen->draw(canvas, 0, 0);
1626 canvas->translate(0, 100);
1627 }
1628
1629 return DrawResult::kOk;
1630}
1631
1632} // namespace skiagm
static void info(const char *fmt,...) SK_PRINTF_LIKE(1
Definition: DM.cpp:213
SkAssertResult(font.textToGlyphs("Hello", 5, SkTextEncoding::kUTF8, glyphs, std::size(glyphs))==count)
static GrDirectContext * GrAsDirectContext(GrContext_Base *base)
static const uint16_t kIndices[]
SkPoint pos
kUnpremul_SkAlphaType
@ kPremul_SkAlphaType
pixel components are premultiplied by alpha
Definition: SkAlphaType.h:29
#define SkUNREACHABLE
Definition: SkAssert.h:135
#define SkASSERT(cond)
Definition: SkAssert.h:116
SkBlendMode
Definition: SkBlendMode.h:38
@ kSaturation
saturation of source with hue and luminosity of destination
@ kModulate
r = s*d
@ kDifference
rc = s + d - 2*(min(s*da, d*sa)), ra = kSrcOver
@ kDstOver
r = d + (1-da)*s
@ kBGRA_8888_SkColorType
pixel with 8 bits for blue, green, red, alpha; in 32-bit word
Definition: SkColorType.h:26
constexpr SkColor SK_ColorYELLOW
Definition: SkColor.h:139
constexpr SkColor SK_ColorMAGENTA
Definition: SkColor.h:147
uint32_t SkColor
Definition: SkColor.h:37
constexpr SkColor SK_ColorCYAN
Definition: SkColor.h:143
constexpr SkColor SK_ColorTRANSPARENT
Definition: SkColor.h:99
constexpr SkColor SK_ColorRED
Definition: SkColor.h:126
constexpr SkColor SK_ColorBLACK
Definition: SkColor.h:103
constexpr SkColor SK_ColorGREEN
Definition: SkColor.h:131
constexpr SkColor SK_ColorWHITE
Definition: SkColor.h:122
void SK_SPI SkDebugf(const char format[],...) SK_PRINTF_LIKE(1
constexpr double SK_DoublePI
@ kYes
Do pre-clip the geometry before applying the (perspective) matrix.
@ kNo
Don't pre-clip the geometry before applying the (perspective) matrix.
SkMeshSpecification::Attribute Attribute
Definition: SkMesh.cpp:54
SkMeshSpecification::Varying Varying
Definition: SkMesh.cpp:55
SK_API SkString SkStringPrintf(const char *format,...) SK_PRINTF_LIKE(1
Creates a new string and writes into it using a printf()-style format.
constexpr uint16_t SkToU16(S x)
Definition: SkTo.h:24
static void draw(SkCanvas *canvas, SkRect &target, int x, int y)
Definition: aaclip.cpp:27
GLenum type
bool abandoned() override
static sk_sp< SkBlender > Mode(SkBlendMode mode)
void drawRect(const SkRect &rect, const SkPaint &paint)
Definition: SkCanvas.cpp:1673
void restore()
Definition: SkCanvas.cpp:461
void translate(SkScalar dx, SkScalar dy)
Definition: SkCanvas.cpp:1278
sk_sp< SkSurface > makeSurface(const SkImageInfo &info, const SkSurfaceProps *props=nullptr)
Definition: SkCanvas.cpp:1195
virtual GrRecordingContext * recordingContext() const
Definition: SkCanvas.cpp:1637
void drawMesh(const SkMesh &mesh, sk_sp< SkBlender > blender, const SkPaint &paint)
Definition: SkCanvas.cpp:1739
void clear(SkColor color)
Definition: SkCanvas.h:1199
int save()
Definition: SkCanvas.cpp:447
void drawPicture(const SkPicture *picture)
Definition: SkCanvas.h:1961
SkImageInfo imageInfo() const
Definition: SkCanvas.cpp:1206
static sk_sp< SkColorFilter > TableARGB(const uint8_t tableA[256], const uint8_t tableR[256], const uint8_t tableG[256], const uint8_t tableB[256])
static sk_sp< SkColorSpace > MakeSRGB()
sk_sp< SkColorSpace > makeColorSpin() const
static sk_sp< SkColorSpace > MakeRGB(const skcms_TransferFunction &transferFn, const skcms_Matrix3x3 &toXYZ)
static sk_sp< SkData > MakeUninitialized(size_t length)
Definition: SkData.cpp:116
static sk_sp< SkData > MakeWithCopy(const void *data, size_t length)
Definition: SkData.cpp:111
static sk_sp< SkShader > MakeSweep(SkScalar cx, SkScalar cy, const SkColor colors[], const SkScalar pos[], int count, SkTileMode mode, SkScalar startAngle, SkScalar endAngle, uint32_t flags, const SkMatrix *localMatrix)
static sk_sp< SkShader > MakeRadial(const SkPoint &center, SkScalar radius, const SkColor colors[], const SkScalar pos[], int count, SkTileMode mode, uint32_t flags=0, const SkMatrix *localMatrix=nullptr)
static sk_sp< SkShader > MakeLinear(const SkPoint pts[2], const SkColor colors[], const SkScalar pos[], int count, SkTileMode mode, uint32_t flags=0, const SkMatrix *localMatrix=nullptr)
sk_sp< SkShader > makeShader(SkTileMode tmx, SkTileMode tmy, const SkSamplingOptions &, const SkMatrix *localMatrix=nullptr) const
Definition: SkImage.cpp:179
static SkMatrix RotateDeg(SkScalar deg)
Definition: SkMatrix.h:104
static SkMatrix MakeAll(SkScalar scaleX, SkScalar skewX, SkScalar transX, SkScalar skewY, SkScalar scaleY, SkScalar transY, SkScalar pers0, SkScalar pers1, SkScalar pers2)
Definition: SkMatrix.h:179
static Result Make(SkSpan< const Attribute > attributes, size_t vertexStride, SkSpan< const Varying > varyings, const SkString &vs, const SkString &fs)
Definition: SkMesh.cpp:389
Definition: SkMesh.h:263
bool isValid() const
Definition: SkMesh.cpp:753
static Result Make(sk_sp< SkMeshSpecification >, Mode, sk_sp< VertexBuffer >, size_t vertexCount, size_t vertexOffset, sk_sp< const SkData > uniforms, SkSpan< ChildPtr > children, const SkRect &bounds)
Definition: SkMesh.cpp:694
static Result MakeIndexed(sk_sp< SkMeshSpecification >, Mode, sk_sp< VertexBuffer >, size_t vertexCount, size_t vertexOffset, sk_sp< IndexBuffer >, size_t indexCount, size_t indexOffset, sk_sp< const SkData > uniforms, SkSpan< ChildPtr > children, const SkRect &bounds)
Definition: SkMesh.cpp:718
SkCanvas * beginRecording(const SkRect &bounds, sk_sp< SkBBoxHierarchy > bbh)
sk_sp< SkPicture > finishRecordingAsPicture()
float nextF()
Definition: SkRandom.h:55
SkCanvas * getCanvas()
Definition: SkSurface.cpp:82
void draw(SkCanvas *canvas, SkScalar x, SkScalar y, const SkSamplingOptions &sampling, const SkPaint *paint)
Definition: SkSurface.cpp:116
Definition: gm.h:110
static constexpr char kErrorMsg_DrawSkippedGpuOnly[]
Definition: gm.h:127
void onOnceBeforeDraw() override
Definition: mesh.cpp:355
SkString getName() const override
Definition: mesh.cpp:416
SkISize getISize() override
Definition: mesh.cpp:353
DrawResult onDraw(SkCanvas *canvas, SkString *error) override
Definition: mesh.cpp:418
DrawResult onGpuSetup(SkCanvas *canvas, SkString *string, GraphiteTestContext *) override
Definition: mesh.cpp:123
DrawResult onDraw(SkCanvas *canvas, SkString *) override
Definition: mesh.cpp:149
void onGpuTeardown() override
Definition: mesh.cpp:139
SkString getName() const override
Definition: mesh.cpp:147
SkISize getISize() override
Definition: mesh.cpp:41
void onOnceBeforeDraw() override
Definition: mesh.cpp:43
SkString getName() const override
Definition: mesh.cpp:575
DrawResult onDraw(SkCanvas *canvas, SkString *error) override
Definition: mesh.cpp:577
SkISize getISize() override
Definition: mesh.cpp:521
bool onAnimate(double nanos) override
Definition: mesh.cpp:633
void onOnceBeforeDraw() override
Definition: mesh.cpp:523
SkString getName() const override
Definition: mesh.cpp:727
DrawResult onDraw(SkCanvas *canvas, SkString *error) override
Definition: mesh.cpp:729
SkISize getISize() override
Definition: mesh.cpp:686
void onOnceBeforeDraw() override
Definition: mesh.cpp:688
void onOnceBeforeDraw() override
Definition: mesh.cpp:1272
DrawResult onGpuSetup(SkCanvas *canvas, SkString *string, GraphiteTestContext *) override
Definition: mesh.cpp:1375
bool onAnimate(double nanos) override
Definition: mesh.cpp:1405
SkString getName() const override
Definition: mesh.cpp:1395
MeshWithShadersGM(Type type)
Definition: mesh.cpp:1241
SkISize getISize() override
Definition: mesh.cpp:1270
void onGpuTeardown() override
Definition: mesh.cpp:1388
DrawResult onDraw(SkCanvas *canvas, SkString *) override
Definition: mesh.cpp:1435
SkISize getISize() override
Definition: mesh.cpp:897
DrawResult onDraw(SkCanvas *canvas, SkString *error) override
Definition: mesh.cpp:950
void onOnceBeforeDraw() override
Definition: mesh.cpp:899
SkString getName() const override
Definition: mesh.cpp:948
SkString getName() const override
Definition: mesh.cpp:1110
SkISize getISize() override
Definition: mesh.cpp:1045
DrawResult onDraw(SkCanvas *canvas, SkString *error) override
Definition: mesh.cpp:1112
void onOnceBeforeDraw() override
Definition: mesh.cpp:1047
const Paint & paint
Definition: color_source.cc:38
DlColor color
VkSurfaceKHR surface
Definition: main.cc:49
const uint8_t uint32_t uint32_t GError ** error
GAsyncResult * result
static float min(float r, float g, float b)
Definition: hsl.cpp:48
double y
double x
constexpr SkColor4f kRed
Definition: SkColor.h:440
SK_API sk_sp< SkImage > RasterFromPixmapCopy(const SkPixmap &pixmap)
SK_API sk_sp< SkMesh::IndexBuffer > CopyIndexBuffer(const sk_sp< SkMesh::IndexBuffer > &)
Definition: SkMesh.cpp:893
SK_API sk_sp< SkMesh::IndexBuffer > MakeIndexBuffer(const void *data, size_t size)
Definition: SkMesh.cpp:889
SK_API sk_sp< SkMesh::VertexBuffer > MakeVertexBuffer(const void *, size_t size)
Definition: SkMesh.cpp:905
SK_API sk_sp< SkMesh::VertexBuffer > CopyVertexBuffer(const sk_sp< SkMesh::VertexBuffer > &)
Definition: SkMesh.cpp:909
static constexpr skcms_Matrix3x3 kRec2020
Definition: SkColorSpace.h:93
static constexpr skcms_TransferFunction k2Dot2
Definition: SkColorSpace.h:48
Optional< SkRect > bounds
Definition: SkRecords.h:189
sk_sp< const SkPicture > picture
Definition: SkRecords.h:299
sk_sp< SkBlender > blender SkRect rect
Definition: SkRecords.h:350
SkMesh mesh
Definition: SkRecords.h:345
PODArray< SkColor > colors
Definition: SkRecords.h:276
SK_API sk_sp< SkShader > Color(SkColor)
SK_API sk_sp< SkSurface > Raster(const SkImageInfo &imageInfo, size_t rowBytes, const SkSurfaceProps *surfaceProps)
static float SineWave(double time, float periodInSecs, float phaseInSecs, float min, float max)
Definition: TimeUtils.h:48
static double NanosToSeconds(double nanos)
Definition: TimeUtils.h:22
sk_sp< SkSurface > makeSurface(SkCanvas *canvas, const SkImageInfo &info, const SkSurfaceProps *props)
Definition: ToolUtils.cpp:512
sk_sp< SkImage > GetResourceAsImage(const char *resource)
Definition: DecodeUtils.h:25
const DlColor kColors[]
constexpr std::array< std::array< float, 2 >, 2 > kRect
it will be possible to load the file into Perfetto s trace viewer disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive mode
Definition: switches.h:228
it will be possible to load the file into Perfetto s trace viewer disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive keep the shell running after the Dart script has completed enable serial On low power devices with low core running concurrent GC tasks on threads can cause them to contend with the UI thread which could potentially lead to jank This option turns off all concurrent GC activities domain network JSON encoded network policy per domain This overrides the DisallowInsecureConnections switch Embedder can specify whether to allow or disallow insecure connections at a domain level old gen heap size
Definition: switches.h:259
static const std::map< std::string, VerticesBuilder::AttributeType > kAttributes
const SkPoint kQuad[4]
static sk_sp< SkMesh::VertexBuffer > make_vertex_buffer(GrDirectContext *ctx, const void *data, size_t size)
Definition: mesh.cpp:493
DEF_GM(return F(C(clipbox), 0.0f, 0.0f, {})) DEF_GM(return F(C(clipbox)
static sk_sp< SkMesh::IndexBuffer > make_index_buffer(GrDirectContext *ctx, const void *data, size_t size)
Definition: mesh.cpp:502
SkSamplingOptions(SkFilterMode::kLinear))
DEF_SIMPLE_GM_CAN_FAIL(custommesh_cs_uniforms, canvas, errorMsg, 200, 900)
Definition: mesh.cpp:1505
DrawResult
Definition: gm.h:104
SIN Vec< N, float > floor(const Vec< N, float > &x)
Definition: SkVx.h:703
SeparatedVector2 offset
Config(int argc, char **argv)
Definition: SkSize.h:16
static SkImageInfo MakeN32Premul(int width, int height)
SkColorSpace * colorSpace() const
SkImageInfo makeColorSpace(sk_sp< SkColorSpace > cs) const
static SkImageInfo Make(int width, int height, SkColorType ct, SkAlphaType at)
SkString error
Definition: SkMesh.h:395
SkMesh mesh
Definition: SkMesh.h:395
constexpr float y() const
Definition: SkPoint_impl.h:187
constexpr float x() const
Definition: SkPoint_impl.h:181
static SkRect Make(const SkISize &size)
Definition: SkRect.h:669
void toQuad(SkPoint quad[4]) const
Definition: SkRect.cpp:50
constexpr SkRect makeOffset(float dx, float dy) const
Definition: SkRect.h:965
static constexpr SkRect MakeXYWH(float x, float y, float w, float h)
Definition: SkRect.h:659
constexpr float height() const
Definition: SkRect.h:769
static constexpr SkRect MakeWH(float w, float h)
Definition: SkRect.h:609
static constexpr SkRect MakeLTRB(float l, float t, float r, float b)
Definition: SkRect.h:646
std::shared_ptr< const fml::Mapping > data
Definition: texture_gles.cc:63
static constexpr SkScalar kMeshSize
Definition: vertices.cpp:65
static sk_sp< SkColorFilter > spin(sk_sp< SkColorFilter > cf)