• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2017 Google Inc.
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7 
8 #include "include/core/SkBitmap.h"
9 #include "include/core/SkBlendMode.h"
10 #include "include/core/SkCanvas.h"
11 #include "include/core/SkColorSpace.h"
12 #include "include/core/SkPaint.h"
13 #include "include/core/SkRect.h"
14 #include "include/core/SkRefCnt.h"
15 #include "include/core/SkSurfaceProps.h"
16 #include "include/core/SkTypes.h"
17 #include "include/gpu/GrDirectContext.h"
18 #include "include/private/SkColorData.h"
19 #include "include/private/base/SkAlignedStorage.h"
20 #include "include/private/base/SkOnce.h"
21 #include "include/private/base/SkTArray.h"
22 #include "include/private/base/SkTo.h"
23 #include "include/private/gpu/ganesh/GrTypesPriv.h"
24 #include "src/base/SkArenaAlloc.h"
25 #include "src/core/SkSLTypeShared.h"
26 #include "src/gpu/KeyBuilder.h"
27 #include "src/gpu/ResourceKey.h"
28 #include "src/gpu/SkBackingFit.h"
29 #include "src/gpu/ganesh/GrBuffer.h"
30 #include "src/gpu/ganesh/GrCaps.h"
31 #include "src/gpu/ganesh/GrColor.h"
32 #include "src/gpu/ganesh/GrDirectContextPriv.h"
33 #include "src/gpu/ganesh/GrDrawIndirectCommand.h"
34 #include "src/gpu/ganesh/GrGeometryProcessor.h"
35 #include "src/gpu/ganesh/GrMeshDrawTarget.h"
36 #include "src/gpu/ganesh/GrOpFlushState.h"
37 #include "src/gpu/ganesh/GrOpsRenderPass.h"
38 #include "src/gpu/ganesh/GrPipeline.h"
39 #include "src/gpu/ganesh/GrPixmap.h"
40 #include "src/gpu/ganesh/GrProcessorAnalysis.h"
41 #include "src/gpu/ganesh/GrProcessorSet.h"
42 #include "src/gpu/ganesh/GrProgramInfo.h"
43 #include "src/gpu/ganesh/GrResourceProvider.h"
44 #include "src/gpu/ganesh/GrShaderCaps.h"
45 #include "src/gpu/ganesh/GrShaderVar.h"
46 #include "src/gpu/ganesh/GrUserStencilSettings.h"
47 #include "src/gpu/ganesh/SurfaceDrawContext.h"
48 #include "src/gpu/ganesh/glsl/GrGLSLFragmentShaderBuilder.h"
49 #include "src/gpu/ganesh/glsl/GrGLSLVarying.h"
50 #include "src/gpu/ganesh/glsl/GrGLSLVertexGeoBuilder.h"
51 #include "src/gpu/ganesh/ops/GrDrawOp.h"
52 #include "src/gpu/ganesh/ops/GrOp.h"
53 #include "src/gpu/ganesh/ops/GrSimpleMeshDrawOpHelper.h"
54 #include "tests/CtsEnforcement.h"
55 #include "tests/Test.h"
56 
57 #include <algorithm>
58 #include <array>
59 #include <cstddef>
60 #include <cstdint>
61 #include <functional>
62 #include <initializer_list>
63 #include <memory>
64 #include <utility>
65 #include <vector>
66 
67 class GrAppliedClip;
68 class GrDstProxyView;
69 class GrGLSLProgramDataManager;
70 class GrRecordingContext;
71 class GrSurfaceProxyView;
72 enum class GrXferBarrierFlags;
73 struct GrContextOptions;
74 
75 #if 0
76 #include "tools/ToolUtils.h"
77 #define WRITE_PNG_CONTEXT_TYPE kANGLE_D3D11_ES3_ContextType
78 #endif
79 
80 SKGPU_DECLARE_STATIC_UNIQUE_KEY(gIndexBufferKey);
81 
82 static constexpr int kBoxSize = 2;
83 static constexpr int kBoxCountY = 8;
84 static constexpr int kBoxCountX = 8;
85 static constexpr int kBoxCount = kBoxCountY * kBoxCountX;
86 
87 static constexpr int kImageWidth = kBoxCountY * kBoxSize;
88 static constexpr int kImageHeight = kBoxCountX * kBoxSize;
89 
90 static constexpr int kIndexPatternRepeatCount = 3;
91 constexpr uint16_t kIndexPattern[6] = {0, 1, 2, 1, 2, 3};
92 
93 
94 class DrawMeshHelper {
95 public:
DrawMeshHelper(GrOpFlushState * state)96     DrawMeshHelper(GrOpFlushState* state) : fState(state) {}
97 
98     sk_sp<const GrBuffer> getIndexBuffer();
99 
100     sk_sp<const GrBuffer> makeIndexBuffer(const uint16_t[], int count);
101 
makeVertexBuffer(const SkTArray<T> & data)102     template<typename T> sk_sp<const GrBuffer> makeVertexBuffer(const SkTArray<T>& data) {
103         return this->makeVertexBuffer(data.begin(), data.size());
104     }
makeVertexBuffer(const std::vector<T> & data)105     template<typename T> sk_sp<const GrBuffer> makeVertexBuffer(const std::vector<T>& data) {
106         return this->makeVertexBuffer(data.data(), data.size());
107     }
108     template<typename T> sk_sp<const GrBuffer> makeVertexBuffer(const T* data, int count);
109 
target()110     GrMeshDrawTarget* target() { return fState; }
111 
112     sk_sp<const GrBuffer> fIndexBuffer;
113     sk_sp<const GrBuffer> fIndexBuffer2;
114     sk_sp<const GrBuffer> fInstBuffer;
115     sk_sp<const GrBuffer> fVertBuffer;
116     sk_sp<const GrBuffer> fVertBuffer2;
117     sk_sp<const GrBuffer> fDrawIndirectBuffer;
118     size_t fDrawIndirectBufferOffset;
119 
120     GrOpsRenderPass* bindPipeline(GrPrimitiveType, bool isInstanced, bool hasVertexBuffer);
121 
122 private:
123     GrOpFlushState* fState;
124 };
125 
126 struct Box {
127     float fX, fY;
128     GrColor fColor;
129 };
130 
131 ////////////////////////////////////////////////////////////////////////////////////////////////////
132 
133 /**
134  * This is a GPU-backend specific test. It tries to test all possible usecases of
135  * GrOpsRenderPass::draw*. The test works by drawing checkerboards of colored boxes, reading back
136  * the pixels, and comparing with expected results. The boxes are drawn on integer boundaries and
137  * the (opaque) colors are chosen from the set (r,g,b) = (0,255)^3, so the GPU renderings ought to
138  * produce exact matches.
139  */
140 
141 static void run_test(GrDirectContext*, const char* testName, skiatest::Reporter*,
142                      const std::unique_ptr<skgpu::v1::SurfaceDrawContext>&, const SkBitmap& gold,
143                      std::function<void(DrawMeshHelper*)> prepareFn,
144                      std::function<void(DrawMeshHelper*)> executeFn);
145 
146 #ifdef WRITE_PNG_CONTEXT_TYPE
IsContextTypeForOutputPNGs(skiatest::GrContextFactoryContextType type)147 static bool IsContextTypeForOutputPNGs(skiatest::GrContextFactoryContextType type) {
148     return type == skiatest::GrContextFactoryContextType::WRITE_PNG_CONTEXT_TYPE;
149 }
DEF_GANESH_TEST_FOR_CONTEXTS(GrMeshTest,IsContextTypeForOutputPNGs,reporter,ctxInfo,nullptr)150 DEF_GANESH_TEST_FOR_CONTEXTS(GrMeshTest, IsContextTypeForOutputPNGs, reporter, ctxInfo, nullptr) {
151 #else
152 DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(GrMeshTest, reporter, ctxInfo, CtsEnforcement::kApiLevel_T) {
153 #endif
154     auto dContext = ctxInfo.directContext();
155 
156     auto sdc = skgpu::v1::SurfaceDrawContext::Make(
157             dContext, GrColorType::kRGBA_8888, nullptr, SkBackingFit::kExact,
158             {kImageWidth, kImageHeight}, SkSurfaceProps(), /*label=*/{});
159     if (!sdc) {
160         ERRORF(reporter, "could not create render target context.");
161         return;
162     }
163 
164     SkTArray<Box> boxes;
165     SkTArray<std::array<Box, 4>> vertexData;
166     SkBitmap gold;
167 
168     // ---- setup ----------
169 
170     SkPaint paint;
171     paint.setBlendMode(SkBlendMode::kSrc);
172     gold.allocN32Pixels(kImageWidth, kImageHeight);
173 
174     SkCanvas goldCanvas(gold);
175 
176     for (int y = 0; y < kBoxCountY; ++y) {
177         for (int x = 0; x < kBoxCountX; ++x) {
178             int c = y + x;
179             int rgb[3] = {-(c & 1) & 0xff, -((c >> 1) & 1) & 0xff, -((c >> 2) & 1) & 0xff};
180 
181             const Box box = boxes.push_back() = {
182                     float(x * kBoxSize),
183                     float(y * kBoxSize),
184                     GrColorPackRGBA(rgb[0], rgb[1], rgb[2], 255)
185             };
186 
187             std::array<Box, 4>& boxVertices = vertexData.push_back();
188             for (int i = 0; i < 4; ++i) {
189                 boxVertices[i] = {
190                         box.fX + (i / 2) * kBoxSize,
191                         box.fY + (i % 2) * kBoxSize,
192                         box.fColor
193                 };
194             }
195 
196             paint.setARGB(255, rgb[0], rgb[1], rgb[2]);
197             goldCanvas.drawRect(SkRect::MakeXYWH(box.fX, box.fY, kBoxSize, kBoxSize), paint);
198         }
199     }
200 
201     // ---- tests ----------
202 
203 #define VALIDATE(buff)                           \
204     do {                                         \
205         if (!buff) {                             \
206             ERRORF(reporter, #buff " is null."); \
207             return;                              \
208         }                                        \
209     } while (0)
210 
211     run_test(dContext, "draw", reporter, sdc, gold,
212              [&](DrawMeshHelper* helper) {
213                  SkTArray<Box> expandedVertexData;
214                  for (int i = 0; i < kBoxCount; ++i) {
215                      for (int j = 0; j < 6; ++j) {
216                          expandedVertexData.push_back(vertexData[i][kIndexPattern[j]]);
217                      }
218                  }
219 
220                  // Draw boxes one line at a time to exercise base vertex.
221                  helper->fVertBuffer = helper->makeVertexBuffer(expandedVertexData);
222                  VALIDATE(helper->fVertBuffer);
223              },
224              [&](DrawMeshHelper* helper) {
225                  for (int y = 0; y < kBoxCountY; ++y) {
226                      auto pass = helper->bindPipeline(GrPrimitiveType::kTriangles, false, true);
227                      pass->bindBuffers(nullptr, nullptr, helper->fVertBuffer);
228                      pass->draw(kBoxCountX * 6, y * kBoxCountX * 6);
229                  }
230              });
231 
232     run_test(dContext, "drawIndexed", reporter, sdc, gold,
233              [&](DrawMeshHelper* helper) {
234                 helper->fIndexBuffer = helper->getIndexBuffer();
235                 VALIDATE(helper->fIndexBuffer);
236                 helper->fVertBuffer = helper->makeVertexBuffer(vertexData);
237                 VALIDATE(helper->fVertBuffer);
238              },
239              [&](DrawMeshHelper* helper) {
240                 int baseRepetition = 0;
241                 int i = 0;
242                 // Start at various repetitions within the patterned index buffer to exercise base
243                 // index.
244                 while (i < kBoxCount) {
245                     static_assert(kIndexPatternRepeatCount >= 3);
246                     int repetitionCount = std::min(3 - baseRepetition, kBoxCount - i);
247 
248                     auto pass = helper->bindPipeline(GrPrimitiveType::kTriangles, false, true);
249                     pass->bindBuffers(helper->fIndexBuffer, nullptr, helper->fVertBuffer);
250                     pass->drawIndexed(repetitionCount * 6, baseRepetition * 6, baseRepetition * 4,
251                                       (baseRepetition + repetitionCount) * 4 - 1,
252                                       (i - baseRepetition) * 4);
253 
254                     baseRepetition = (baseRepetition + 1) % 3;
255                     i += repetitionCount;
256                 }
257             });
258 
259     run_test(dContext, "drawIndexPattern", reporter, sdc, gold,
260              [&](DrawMeshHelper* helper) {
261                  helper->fIndexBuffer = helper->getIndexBuffer();
262                  VALIDATE(helper->fIndexBuffer);
263                  helper->fVertBuffer = helper->makeVertexBuffer(vertexData);
264                  VALIDATE(helper->fVertBuffer);
265              },
266              [&](DrawMeshHelper* helper) {
267                 // Draw boxes one line at a time to exercise base vertex. drawIndexPattern does
268                 // not support a base index.
269                 for (int y = 0; y < kBoxCountY; ++y) {
270                     auto pass = helper->bindPipeline(GrPrimitiveType::kTriangles, false, true);
271                     pass->bindBuffers(helper->fIndexBuffer, nullptr, helper->fVertBuffer);
272                     pass->drawIndexPattern(6, kBoxCountX, kIndexPatternRepeatCount, 4,
273                                            y * kBoxCountX * 4);
274 
275                 }
276              });
277 
278     for (bool indexed : {false, true}) {
279         if (!dContext->priv().caps()->drawInstancedSupport()) {
280             break;
281         }
282 
283         run_test(dContext, indexed ? "drawIndexedInstanced" : "drawInstanced",
284                  reporter, sdc, gold,
285                  [&](DrawMeshHelper* helper) {
286                      helper->fIndexBuffer = indexed ? helper->getIndexBuffer() : nullptr;
287                      SkTArray<uint16_t> baseIndexData;
288                      baseIndexData.push_back(kBoxCountX/2 * 6); // for testing base index.
289                      for (int i = 0; i < 6; ++i) {
290                          baseIndexData.push_back(kIndexPattern[i]);
291                      }
292                      helper->fIndexBuffer2 = helper->makeIndexBuffer(baseIndexData.begin(),
293                                                                      baseIndexData.size());
294                      helper->fInstBuffer = helper->makeVertexBuffer(boxes);
295                      VALIDATE(helper->fInstBuffer);
296                      helper->fVertBuffer =
297                              helper->makeVertexBuffer(std::vector<float>{0,0, 0,1, 1,0, 1,1});
298                      VALIDATE(helper->fVertBuffer);
299                      helper->fVertBuffer2 = helper->makeVertexBuffer( // for testing base vertex.
300                          std::vector<float>{-1,-1, -1,-1, 0,0, 0,1, 1,0, 1,1});
301                      VALIDATE(helper->fVertBuffer2);
302                  },
303                  [&](DrawMeshHelper* helper) {
304                      // Draw boxes one line at a time to exercise base instance, base vertex, and
305                      // null vertex buffer.
306                      for (int y = 0; y < kBoxCountY; ++y) {
307                          sk_sp<const GrBuffer> vertexBuffer;
308                          int baseVertex = 0;
309                          switch (y % 3) {
310                              case 0:
311                                  if (dContext->priv().caps()->shaderCaps()->fVertexIDSupport) {
312                                      break;
313                                  }
314                                  [[fallthrough]];
315                              case 1:
316                                  vertexBuffer = helper->fVertBuffer;
317                                  break;
318                              case 2:
319                                  vertexBuffer = helper->fVertBuffer2;
320                                  baseVertex = 2;
321                                  break;
322                          }
323 
324                          GrPrimitiveType primitiveType = indexed ? GrPrimitiveType::kTriangles
325                                                                  : GrPrimitiveType::kTriangleStrip;
326                          auto pass = helper->bindPipeline(primitiveType, true,
327                                                           SkToBool(vertexBuffer));
328                          if (indexed) {
329                              sk_sp<const GrBuffer> indexBuffer = (y % 2) ?
330                                      helper->fIndexBuffer2 : helper->fIndexBuffer;
331                              VALIDATE(indexBuffer);
332                              int baseIndex = (y % 2);
333                              pass->bindBuffers(std::move(indexBuffer), helper->fInstBuffer,
334                                                std::move(vertexBuffer));
335                              pass->drawIndexedInstanced(6, baseIndex, kBoxCountX, y * kBoxCountX,
336                                                         baseVertex);
337                          } else {
338                              pass->bindBuffers(nullptr, helper->fInstBuffer,
339                                                std::move(vertexBuffer));
340                              pass->drawInstanced(kBoxCountX, y * kBoxCountY, 4, baseVertex);
341                          }
342                      }
343                  });
344     }
345 
346     for (bool indexed : {false, true}) {
347         if (!dContext->priv().caps()->drawInstancedSupport()) {
348             break;
349         }
350 
351         run_test(dContext, (indexed) ? "drawIndexedIndirect" : "drawIndirect",
352                  reporter, sdc, gold,
353                  [&](DrawMeshHelper* helper) {
354                      SkTArray<uint16_t> baseIndexData;
355                      baseIndexData.push_back(kBoxCountX/2 * 6); // for testing base index.
356                      for (int j = 0; j < kBoxCountY; ++j) {
357                          for (int i = 0; i < 6; ++i) {
358                              baseIndexData.push_back(kIndexPattern[i]);
359                          }
360                      }
361                      helper->fIndexBuffer2 = helper->makeIndexBuffer(baseIndexData.begin(),
362                                                                      baseIndexData.size());
363                      VALIDATE(helper->fIndexBuffer2);
364                      helper->fInstBuffer = helper->makeVertexBuffer(boxes);
365                      VALIDATE(helper->fInstBuffer);
366                      helper->fVertBuffer = helper->makeVertexBuffer(std::vector<float>{
367                              -1,-1, 0,0, 0,1, 1,0, 1,1, -1,-1, 0,0, 1,0, 0,1, 1,1});
368                      VALIDATE(helper->fVertBuffer);
369 
370                      GrDrawIndirectWriter indirectWriter;
371                      GrDrawIndexedIndirectWriter indexedIndirectWriter;
372                      if (indexed) {
373                          // Make helper->fDrawIndirectBufferOffset nonzero.
374                          sk_sp<const GrBuffer> ignoredBuff;
375                          size_t ignoredOffset;
376                          // Make a superfluous call to makeDrawIndirectSpace in order to test
377                          // "offsetInBytes!=0" for the actual call to makeDrawIndexedIndirectSpace.
378                          helper->target()->makeDrawIndirectSpace(29, &ignoredBuff, &ignoredOffset);
379                          indexedIndirectWriter = helper->target()->makeDrawIndexedIndirectSpace(
380                                  kBoxCountY, &helper->fDrawIndirectBuffer,
381                                  &helper->fDrawIndirectBufferOffset);
382                      } else {
383                          // Make helper->fDrawIndirectBufferOffset nonzero.
384                          sk_sp<const GrBuffer> ignoredBuff;
385                          size_t ignoredOffset;
386                          // Make a superfluous call to makeDrawIndexedIndirectSpace in order to test
387                          // "offsetInBytes!=0" for the actual call to makeDrawIndirectSpace.
388                          helper->target()->makeDrawIndexedIndirectSpace(7, &ignoredBuff,
389                                                                         &ignoredOffset);
390                          indirectWriter = helper->target()->makeDrawIndirectSpace(
391                                  kBoxCountY, &helper->fDrawIndirectBuffer,
392                                  &helper->fDrawIndirectBufferOffset);
393                      }
394 
395                      // Draw boxes one line at a time to exercise multiple draws.
396                      for (int y = 0; y < kBoxCountY; ++y) {
397                          int baseVertex = (y % 2) ? 1 : 6;
398                          if (indexed) {
399                              int baseIndex = 1 + y * 6;
400                              indexedIndirectWriter.writeIndexed(6, baseIndex, kBoxCountX,
401                                                                 y * kBoxCountX, baseVertex);
402                          } else {
403                              indirectWriter.write(kBoxCountX, y * kBoxCountX, 4, baseVertex);
404                          }
405                      }
406                  },
407                  [&](DrawMeshHelper* helper) {
408                      GrOpsRenderPass* pass;
409                      if (indexed) {
410                          pass = helper->bindPipeline(GrPrimitiveType::kTriangles, true, true);
411                          pass->bindBuffers(helper->fIndexBuffer2, helper->fInstBuffer,
412                                            helper->fVertBuffer);
413                          for (int i = 0; i < 3; ++i) {
414                              int start = kBoxCountY * i / 3;
415                              int end = kBoxCountY * (i + 1) / 3;
416                              size_t offset = helper->fDrawIndirectBufferOffset + start *
417                                              sizeof(GrDrawIndexedIndirectCommand);
418                              pass->drawIndexedIndirect(helper->fDrawIndirectBuffer.get(), offset,
419                                                        end - start);
420                          }
421                      } else {
422                          pass = helper->bindPipeline(GrPrimitiveType::kTriangleStrip, true, true);
423                          pass->bindBuffers(nullptr, helper->fInstBuffer, helper->fVertBuffer);
424                          for (int i = 0; i < 2; ++i) {
425                              int start = kBoxCountY * i / 2;
426                              int end = kBoxCountY * (i + 1) / 2;
427                              size_t offset = helper->fDrawIndirectBufferOffset + start *
428                                              sizeof(GrDrawIndirectCommand);
429                              pass->drawIndirect(helper->fDrawIndirectBuffer.get(), offset,
430                                                 end - start);
431                          }
432                      }
433                  });
434     }
435 }
436 
437 ////////////////////////////////////////////////////////////////////////////////////////////////////
438 
439 namespace {
440 class MeshTestOp : public GrDrawOp {
441 public:
442     DEFINE_OP_CLASS_ID
443 
444     static GrOp::Owner Make(GrRecordingContext* rContext,
445                             std::function<void(DrawMeshHelper*)> prepareFn,
446                             std::function<void(DrawMeshHelper*)> executeFn) {
447         return GrOp::Make<MeshTestOp>(rContext, prepareFn, executeFn);
448     }
449 
450 private:
451     friend class GrOp;  // for ctor
452 
453     MeshTestOp(std::function<void(DrawMeshHelper*)> prepareFn,
454                std::function<void(DrawMeshHelper*)> executeFn)
455             : INHERITED(ClassID()), fPrepareFn(prepareFn), fExecuteFn(executeFn) {
456         this->setBounds(
457                 SkRect::MakeIWH(kImageWidth, kImageHeight), HasAABloat::kNo, IsHairline::kNo);
458     }
459 
460     const char* name() const override { return "GrMeshTestOp"; }
461     FixedFunctionFlags fixedFunctionFlags() const override { return FixedFunctionFlags::kNone; }
462     GrProcessorSet::Analysis finalize(const GrCaps&, const GrAppliedClip*, GrClampType) override {
463         return GrProcessorSet::EmptySetAnalysis();
464     }
465 
466     void onPrePrepare(GrRecordingContext*,
467                       const GrSurfaceProxyView& writeView,
468                       GrAppliedClip*,
469                       const GrDstProxyView&,
470                       GrXferBarrierFlags renderPassXferBarriers,
471                       GrLoadOp colorLoadOp) override {}
472     void onPrepare(GrOpFlushState* state) override {
473         fHelper = std::make_unique<DrawMeshHelper>(state);
474         fPrepareFn(fHelper.get());
475     }
476     void onExecute(GrOpFlushState* state, const SkRect& chainBounds) override {
477         fExecuteFn(fHelper.get());
478     }
479 
480     std::unique_ptr<DrawMeshHelper> fHelper;
481     std::function<void(DrawMeshHelper*)> fPrepareFn;
482     std::function<void(DrawMeshHelper*)> fExecuteFn;
483 
484     using INHERITED = GrDrawOp;
485 };
486 
487 class MeshTestProcessor : public GrGeometryProcessor {
488 public:
489     static GrGeometryProcessor* Make(SkArenaAlloc* arena, bool instanced, bool hasVertexBuffer) {
490         return arena->make([&](void* ptr) {
491             return new (ptr) MeshTestProcessor(instanced, hasVertexBuffer);
492         });
493     }
494 
495     const char* name() const override { return "GrMeshTestProcessor"; }
496 
497     void addToKey(const GrShaderCaps&, skgpu::KeyBuilder* b) const final {
498         b->add32(fInstanceLocation.isInitialized());
499         b->add32(fVertexPosition.isInitialized());
500     }
501 
502     std::unique_ptr<ProgramImpl> makeProgramImpl(const GrShaderCaps&) const final;
503 
504 private:
505     class Impl;
506 
507     const Attribute& inColor() const {
508         return fVertexColor.isInitialized() ? fVertexColor : fInstanceColor;
509     }
510 
511     MeshTestProcessor(bool instanced, bool hasVertexBuffer)
512             : INHERITED(kGrMeshTestProcessor_ClassID) {
513         if (instanced) {
514             fInstanceLocation = {"location", kFloat2_GrVertexAttribType, SkSLType::kHalf2};
515             fInstanceColor = {"color", kUByte4_norm_GrVertexAttribType, SkSLType::kHalf4};
516             this->setInstanceAttributesWithImplicitOffsets(&fInstanceLocation, 2);
517             if (hasVertexBuffer) {
518                 fVertexPosition = {"vertex", kFloat2_GrVertexAttribType, SkSLType::kHalf2};
519                 this->setVertexAttributesWithImplicitOffsets(&fVertexPosition, 1);
520             }
521         } else {
522             fVertexPosition = {"vertex", kFloat2_GrVertexAttribType, SkSLType::kHalf2};
523             fVertexColor = {"color", kUByte4_norm_GrVertexAttribType, SkSLType::kHalf4};
524             this->setVertexAttributesWithImplicitOffsets(&fVertexPosition, 2);
525         }
526     }
527 
528     Attribute fVertexPosition;
529     Attribute fVertexColor;
530 
531     Attribute fInstanceLocation;
532     Attribute fInstanceColor;
533 
534     using INHERITED = GrGeometryProcessor;
535 };
536 }  // anonymous namespace
537 
538 std::unique_ptr<GrGeometryProcessor::ProgramImpl> MeshTestProcessor::makeProgramImpl(
539         const GrShaderCaps&) const {
540     class Impl : public ProgramImpl {
541     public:
542         void setData(const GrGLSLProgramDataManager&,
543                      const GrShaderCaps&,
544                      const GrGeometryProcessor&) final {}
545 
546     private:
547         void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) final {
548             const MeshTestProcessor& mp = args.fGeomProc.cast<MeshTestProcessor>();
549             GrGLSLVertexBuilder* v = args.fVertBuilder;
550             GrGLSLFPFragmentBuilder* f = args.fFragBuilder;
551 
552             GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
553             varyingHandler->emitAttributes(mp);
554             f->codeAppendf("half4 %s;", args.fOutputColor);
555             varyingHandler->addPassThroughAttribute(mp.inColor().asShaderVar(), args.fOutputColor);
556 
557             if (!mp.fInstanceLocation.isInitialized()) {
558                 v->codeAppendf("float2 vertex = %s;", mp.fVertexPosition.name());
559             } else {
560                 if (mp.fVertexPosition.isInitialized()) {
561                     v->codeAppendf("float2 offset = %s;", mp.fVertexPosition.name());
562                 } else {
563                     v->codeAppend("float2 offset = float2(sk_VertexID / 2, sk_VertexID % 2);");
564                 }
565                 v->codeAppendf("float2 vertex = %s + offset * %i;", mp.fInstanceLocation.name(),
566                                kBoxSize);
567             }
568             gpArgs->fPositionVar.set(SkSLType::kFloat2, "vertex");
569 
570             f->codeAppendf("const half4 %s = half4(1);", args.fOutputCoverage);
571         }
572     };
573 
574     return std::make_unique<Impl>();
575 }
576 
577 ////////////////////////////////////////////////////////////////////////////////////////////////////
578 
579 sk_sp<const GrBuffer> DrawMeshHelper::makeIndexBuffer(const uint16_t indices[], int count) {
580     return fState->resourceProvider()->createBuffer(indices,
581                                                     count*sizeof(uint16_t),
582                                                     GrGpuBufferType::kIndex,
583                                                     kDynamic_GrAccessPattern);
584 }
585 
586 template<typename T>
587 sk_sp<const GrBuffer> DrawMeshHelper::makeVertexBuffer(const T* data, int count) {
588     return fState->resourceProvider()->createBuffer(data,
589                                                     count*sizeof(T),
590                                                     GrGpuBufferType::kVertex,
591                                                     kDynamic_GrAccessPattern);
592 }
593 
594 sk_sp<const GrBuffer> DrawMeshHelper::getIndexBuffer() {
595     SKGPU_DEFINE_STATIC_UNIQUE_KEY(gIndexBufferKey);
596     return fState->resourceProvider()->findOrCreatePatternedIndexBuffer(
597             kIndexPattern, 6, kIndexPatternRepeatCount, 4, gIndexBufferKey);
598 }
599 
600 GrOpsRenderPass* DrawMeshHelper::bindPipeline(GrPrimitiveType primitiveType, bool isInstanced,
601                                               bool hasVertexBuffer) {
602     GrProcessorSet processorSet(SkBlendMode::kSrc);
603 
604     // TODO: add a GrProcessorSet testing helper to make this easier
605     SkPMColor4f overrideColor;
606     processorSet.finalize(GrProcessorAnalysisColor(),
607                           GrProcessorAnalysisCoverage::kNone,
608                           fState->appliedClip(),
609                           nullptr,
610                           fState->caps(),
611                           GrClampType::kAuto,
612                           &overrideColor);
613 
614     auto pipeline = GrSimpleMeshDrawOpHelper::CreatePipeline(fState,
615                                                              std::move(processorSet),
616                                                              GrPipeline::InputFlags::kNone);
617 
618     GrGeometryProcessor* mtp = MeshTestProcessor::Make(fState->allocator(), isInstanced,
619                                                        hasVertexBuffer);
620 
621     GrProgramInfo programInfo(fState->caps(), fState->writeView(), fState->usesMSAASurface(),
622                               pipeline, &GrUserStencilSettings::kUnused, mtp, primitiveType,
623                               fState->renderPassBarriers(), fState->colorLoadOp());
624 
625     fState->opsRenderPass()->bindPipeline(programInfo, SkRect::MakeIWH(kImageWidth, kImageHeight));
626     return fState->opsRenderPass();
627 }
628 
629 static void run_test(GrDirectContext* dContext,
630                      const char* testName,
631                      skiatest::Reporter* reporter,
632                      const std::unique_ptr<skgpu::v1::SurfaceDrawContext>& sdc,
633                      const SkBitmap& gold,
634                      std::function<void(DrawMeshHelper*)> prepareFn,
635                      std::function<void(DrawMeshHelper*)> executeFn) {
636     const int w = gold.width(), h = gold.height();
637     const uint32_t* goldPx = reinterpret_cast<const uint32_t*>(gold.getPixels());
638     if (h != sdc->height() || w != sdc->width()) {
639         ERRORF(reporter, "[%s] expectation and rtc not compatible (?).", testName);
640         return;
641     }
642     if (sizeof(uint32_t) * kImageWidth != gold.rowBytes()) {
643         ERRORF(reporter, "[%s] unexpected row bytes in gold image", testName);
644         return;
645     }
646 
647     GrPixmap resultPM = GrPixmap::Allocate(gold.info());
648     sdc->clear(SkPMColor4f::FromBytes_RGBA(0xbaaaaaad));
649     sdc->addDrawOp(MeshTestOp::Make(dContext, prepareFn, executeFn));
650 
651     sdc->readPixels(dContext, resultPM, {0, 0});
652 
653 #ifdef WRITE_PNG_CONTEXT_TYPE
654 #define STRINGIFY(X) #X
655 #define TOSTRING(X) STRINGIFY(X)
656     SkString filename;
657     filename.printf("GrMeshTest_%s_%s.png", TOSTRING(WRITE_PNG_CONTEXT_TYPE), testName);
658     SkDebugf("writing %s...\n", filename.c_str());
659     ToolUtils::EncodeImageToFile(filename.c_str(), resultPM, SkEncodedImageFormat::kPNG, 100);
660 #endif
661 
662     for (int y = 0; y < h; ++y) {
663         for (int x = 0; x < w; ++x) {
664             uint32_t expected = goldPx[y * kImageWidth + x];
665             uint32_t actual = static_cast<uint32_t*>(resultPM.addr())[y * kImageWidth + x];
666             if (expected != actual) {
667                 ERRORF(reporter, "[%s] pixel (%i,%i): got 0x%x expected 0x%x",
668                        testName, x, y, actual, expected);
669                 return;
670             }
671         }
672     }
673 }
674