Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
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 )";
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[]{
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 )";
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,
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
345class MeshColorSpaceGM : public skiagm::GM {
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(
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}};
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) {
497 return SkMeshes::MakeVertexBuffer(ctx, data, size);
498 }
499 return SkMeshes::MakeVertexBuffer(data, size);
500}
501
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
511DEF_GM(return new MeshColorSpaceGM;)
512
513class MeshUniformsGM : public skiagm::GM {
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] =
552 SkMeshSpecification::Make(kAttributes,
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
564 SkColor colors[] = {SK_ColorWHITE, SK_ColorBLACK};
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 )";
707 auto [spec, error] = SkMeshSpecification::Make(kAttributes,
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
732 GrRecordingContext* rc = canvas->recordingContext();
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
762 SkRect bounds;
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
951 GrRecordingContext* rc = canvas->recordingContext();
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
992 SkRect bounds;
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] =
1074 SkMeshSpecification::Make(kAttributes,
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;
1099 colors,
1101 nullptr,
1102 std::size(colors),
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 )";
1311 auto [spec, error] = SkMeshSpecification::Make(kAttributes,
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
1385 : DrawResult::kOk;
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
1438 GrRecordingContext* rc = canvas->recordingContext();
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()) {
1507 *errorMsg = GM::kErrorMsg_DrawSkippedGpuOnly;
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) {
1532 static const SkMeshSpecification::Attribute kAttributes[]{
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(
1559 kAttributes,
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;
1620 offscreenCanvas->drawMesh(mesh, SkBlender::Mode(SkBlendMode::kDst), 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
static GrDirectContext * GrAsDirectContext(GrContext_Base *base)
static const uint16_t kIndices[]
SkPoint pos
SkColor4f color
kUnpremul_SkAlphaType
@ kPremul_SkAlphaType
pixel components are premultiplied by alpha
Definition SkAlphaType.h:29
#define SkAssertResult(cond)
Definition SkAssert.h:123
#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.
SK_API SkString static SkString SkStringPrintf()
Definition SkString.h:287
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
constexpr SkRect kRect
bool abandoned() override
static sk_sp< SkBlender > Mode(SkBlendMode mode)
void drawRect(const SkRect &rect, const SkPaint &paint)
void restore()
Definition SkCanvas.cpp:465
void translate(SkScalar dx, SkScalar dy)
sk_sp< SkSurface > makeSurface(const SkImageInfo &info, const SkSurfaceProps *props=nullptr)
virtual GrRecordingContext * recordingContext() const
void drawMesh(const SkMesh &mesh, sk_sp< SkBlender > blender, const SkPaint &paint)
void clear(SkColor color)
Definition SkCanvas.h:1199
int save()
Definition SkCanvas.cpp:451
void drawPicture(const SkPicture *picture)
Definition SkCanvas.h:1961
SkImageInfo imageInfo() const
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
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
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
VkSurfaceKHR surface
Definition main.cc:49
const uint8_t uint32_t uint32_t GError ** error
GAsyncResult * result
#define DEF_GM(CODE)
Definition gm.h:40
#define DEF_SIMPLE_GM_CAN_FAIL(NAME, CANVAS, ERR_MSG, W, H)
Definition gm.h:62
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
static constexpr skcms_TransferFunction k2Dot2
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< SkImage > GetResourceAsImage(const char *resource)
Definition DecodeUtils.h:25
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot data
Definition switches.h:41
static sk_sp< SkMesh::VertexBuffer > make_vertex_buffer(GrDirectContext *ctx, const void *data, size_t size)
Definition mesh.cpp:493
static sk_sp< SkMesh::IndexBuffer > make_index_buffer(GrDirectContext *ctx, const void *data, size_t size)
Definition mesh.cpp:502
DrawResult
Definition gm.h:104
Point offset
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
constexpr float x() const
static SkRect Make(const SkISize &size)
Definition SkRect.h:669
void toQuad(SkPoint quad[4]) const
Definition SkRect.cpp:50
constexpr float left() const
Definition SkRect.h:734
constexpr SkRect makeOffset(float dx, float dy) const
Definition SkRect.h:965
constexpr float top() const
Definition SkRect.h:741
SkScalar fLeft
smaller x-axis bounds
Definition extension.cpp:14
constexpr float x() const
Definition SkRect.h:720
constexpr float y() const
Definition SkRect.h:727
SkRect makeOutset(float dx, float dy) const
Definition SkRect.h:1002
static constexpr SkRect MakeXYWH(float x, float y, float w, float h)
Definition SkRect.h:659
constexpr float centerX() const
Definition SkRect.h:776
constexpr float height() const
Definition SkRect.h:769
constexpr float right() const
Definition SkRect.h:748
constexpr float centerY() const
Definition SkRect.h:785
constexpr float width() const
Definition SkRect.h:762
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
constexpr float bottom() const
Definition SkRect.h:755
static constexpr SkScalar kMeshSize
Definition vertices.cpp:65
static sk_sp< SkColorFilter > spin(sk_sp< SkColorFilter > cf)