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