blob: ee3e5c3bdd37cb997b070037ac1ab0fe9c8ea200 [file] [log] [blame]
Chris Dalton114a3c02017-05-26 15:17:19 -06001/*
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
Mike Kleinc0bd9f92019-04-23 12:05:21 -05008#include "tests/Test.h"
Chris Dalton114a3c02017-05-26 15:17:19 -06009
Brian Salomonc7fe0f72018-05-11 10:14:21 -040010#include <array>
John Stilesfbd050b2020-08-03 13:21:46 -040011#include <memory>
Brian Salomonc7fe0f72018-05-11 10:14:21 -040012#include <vector>
Mike Kleinc0bd9f92019-04-23 12:05:21 -050013#include "include/core/SkBitmap.h"
Robert Phillips6d344c32020-07-06 10:56:46 -040014#include "include/gpu/GrDirectContext.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050015#include "include/private/GrResourceKey.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050016#include "src/gpu/GrCaps.h"
17#include "src/gpu/GrContextPriv.h"
18#include "src/gpu/GrGeometryProcessor.h"
Brian Salomonf2ebdd92019-09-30 12:15:30 -040019#include "src/gpu/GrImageInfo.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050020#include "src/gpu/GrMemoryPool.h"
21#include "src/gpu/GrOpFlushState.h"
Greg Daniel2d41d0d2019-08-26 11:08:51 -040022#include "src/gpu/GrOpsRenderPass.h"
Robert Phillips901aff02019-10-08 12:32:56 -040023#include "src/gpu/GrProgramInfo.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050024#include "src/gpu/GrRenderTargetContext.h"
25#include "src/gpu/GrRenderTargetContextPriv.h"
26#include "src/gpu/GrResourceProvider.h"
27#include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h"
28#include "src/gpu/glsl/GrGLSLGeometryProcessor.h"
29#include "src/gpu/glsl/GrGLSLVarying.h"
30#include "src/gpu/glsl/GrGLSLVertexGeoBuilder.h"
Robert Phillips3968fcb2019-12-05 16:40:31 -050031#include "src/gpu/ops/GrSimpleMeshDrawOpHelper.h"
Chris Dalton114a3c02017-05-26 15:17:19 -060032
Chris Daltond4dec972020-04-03 11:01:30 -060033#if 0
34#include "tools/ToolUtils.h"
35#define WRITE_PNG_CONTEXT_TYPE kANGLE_D3D11_ES3_ContextType
36#endif
37
Chris Dalton114a3c02017-05-26 15:17:19 -060038GR_DECLARE_STATIC_UNIQUE_KEY(gIndexBufferKey);
39
40static constexpr int kBoxSize = 2;
41static constexpr int kBoxCountY = 8;
42static constexpr int kBoxCountX = 8;
43static constexpr int kBoxCount = kBoxCountY * kBoxCountX;
44
45static constexpr int kImageWidth = kBoxCountY * kBoxSize;
46static constexpr int kImageHeight = kBoxCountX * kBoxSize;
47
48static constexpr int kIndexPatternRepeatCount = 3;
49constexpr uint16_t kIndexPattern[6] = {0, 1, 2, 1, 2, 3};
50
51
52class DrawMeshHelper {
53public:
54 DrawMeshHelper(GrOpFlushState* state) : fState(state) {}
55
56 sk_sp<const GrBuffer> getIndexBuffer();
57
Chris Daltonbb768422020-03-12 12:13:29 -060058 sk_sp<const GrBuffer> makeIndexBuffer(const uint16_t[], int count);
59
Chris Dalton114a3c02017-05-26 15:17:19 -060060 template<typename T> sk_sp<const GrBuffer> makeVertexBuffer(const SkTArray<T>& data) {
61 return this->makeVertexBuffer(data.begin(), data.count());
62 }
Chris Dalton1d616352017-05-31 12:51:23 -060063 template<typename T> sk_sp<const GrBuffer> makeVertexBuffer(const std::vector<T>& data) {
64 return this->makeVertexBuffer(data.data(), data.size());
65 }
Chris Dalton114a3c02017-05-26 15:17:19 -060066 template<typename T> sk_sp<const GrBuffer> makeVertexBuffer(const T* data, int count);
67
Chris Daltonb8d7e002020-05-13 11:02:10 -060068 GrMeshDrawOp::Target* target() { return fState; }
Chris Dalton03fdf6a2020-04-07 12:31:59 -060069
Chris Daltonbb768422020-03-12 12:13:29 -060070 sk_sp<const GrBuffer> fIndexBuffer;
71 sk_sp<const GrBuffer> fIndexBuffer2;
72 sk_sp<const GrBuffer> fInstBuffer;
Greg Danielf793de12019-09-05 13:23:23 -040073 sk_sp<const GrBuffer> fVertBuffer;
74 sk_sp<const GrBuffer> fVertBuffer2;
Chris Dalton03fdf6a2020-04-07 12:31:59 -060075 sk_sp<const GrBuffer> fDrawIndirectBuffer;
76 size_t fDrawIndirectBufferOffset;
Greg Danielf793de12019-09-05 13:23:23 -040077
Chris Daltonbb768422020-03-12 12:13:29 -060078 GrOpsRenderPass* bindPipeline(GrPrimitiveType, bool isInstanced, bool hasVertexBuffer);
Chris Dalton114a3c02017-05-26 15:17:19 -060079
80private:
81 GrOpFlushState* fState;
82};
83
84struct Box {
85 float fX, fY;
86 GrColor fColor;
87};
88
89////////////////////////////////////////////////////////////////////////////////////////////////////
90
91/**
Chris Daltoneb694b72020-03-16 09:25:50 -060092 * This is a GPU-backend specific test. It tries to test all possible usecases of
93 * GrOpsRenderPass::draw*. The test works by drawing checkerboards of colored boxes, reading back
94 * the pixels, and comparing with expected results. The boxes are drawn on integer boundaries and
95 * the (opaque) colors are chosen from the set (r,g,b) = (0,255)^3, so the GPU renderings ought to
96 * produce exact matches.
Chris Dalton114a3c02017-05-26 15:17:19 -060097 */
98
Adlai Hollerc95b5892020-08-11 12:02:22 -040099static void run_test(GrDirectContext*, const char* testName, skiatest::Reporter*,
Brian Salomonbf6b9792019-08-21 09:38:10 -0400100 const std::unique_ptr<GrRenderTargetContext>&, const SkBitmap& gold,
Greg Danielf793de12019-09-05 13:23:23 -0400101 std::function<void(DrawMeshHelper*)> prepareFn,
102 std::function<void(DrawMeshHelper*)> executeFn);
Chris Dalton114a3c02017-05-26 15:17:19 -0600103
Chris Daltond4dec972020-04-03 11:01:30 -0600104#ifdef WRITE_PNG_CONTEXT_TYPE
105static bool IsContextTypeForOutputPNGs(skiatest::GrContextFactoryContextType type) {
106 return type == skiatest::GrContextFactoryContextType::WRITE_PNG_CONTEXT_TYPE;
107}
108DEF_GPUTEST_FOR_CONTEXTS(GrMeshTest, IsContextTypeForOutputPNGs, reporter, ctxInfo, nullptr) {
109#else
Chris Dalton114a3c02017-05-26 15:17:19 -0600110DEF_GPUTEST_FOR_RENDERING_CONTEXTS(GrMeshTest, reporter, ctxInfo) {
Chris Daltond4dec972020-04-03 11:01:30 -0600111#endif
Robert Phillipseffd13f2020-07-20 15:00:36 -0400112 auto dContext = ctxInfo.directContext();
Chris Dalton114a3c02017-05-26 15:17:19 -0600113
Greg Daniele20fcad2020-01-08 11:52:34 -0500114 auto rtc = GrRenderTargetContext::Make(
Robert Phillipseffd13f2020-07-20 15:00:36 -0400115 dContext, GrColorType::kRGBA_8888, nullptr, SkBackingFit::kExact,
Greg Daniele20fcad2020-01-08 11:52:34 -0500116 {kImageWidth, kImageHeight});
Chris Dalton114a3c02017-05-26 15:17:19 -0600117 if (!rtc) {
118 ERRORF(reporter, "could not create render target context.");
119 return;
120 }
121
122 SkTArray<Box> boxes;
123 SkTArray<std::array<Box, 4>> vertexData;
124 SkBitmap gold;
125
126 // ---- setup ----------
127
128 SkPaint paint;
129 paint.setBlendMode(SkBlendMode::kSrc);
130 gold.allocN32Pixels(kImageWidth, kImageHeight);
131
132 SkCanvas goldCanvas(gold);
133
134 for (int y = 0; y < kBoxCountY; ++y) {
135 for (int x = 0; x < kBoxCountX; ++x) {
136 int c = y + x;
137 int rgb[3] = {-(c & 1) & 0xff, -((c >> 1) & 1) & 0xff, -((c >> 2) & 1) & 0xff};
138
139 const Box box = boxes.push_back() = {
Brian Salomon23356442018-11-30 15:33:19 -0500140 float(x * kBoxSize),
141 float(y * kBoxSize),
142 GrColorPackRGBA(rgb[0], rgb[1], rgb[2], 255)
Chris Dalton114a3c02017-05-26 15:17:19 -0600143 };
144
145 std::array<Box, 4>& boxVertices = vertexData.push_back();
146 for (int i = 0; i < 4; ++i) {
147 boxVertices[i] = {
Brian Salomon23356442018-11-30 15:33:19 -0500148 box.fX + (i / 2) * kBoxSize,
149 box.fY + (i % 2) * kBoxSize,
150 box.fColor
Chris Dalton114a3c02017-05-26 15:17:19 -0600151 };
152 }
153
154 paint.setARGB(255, rgb[0], rgb[1], rgb[2]);
155 goldCanvas.drawRect(SkRect::MakeXYWH(box.fX, box.fY, kBoxSize, kBoxSize), paint);
156 }
157 }
158
Chris Dalton114a3c02017-05-26 15:17:19 -0600159 // ---- tests ----------
160
Brian Salomon23356442018-11-30 15:33:19 -0500161#define VALIDATE(buff) \
162 do { \
163 if (!buff) { \
164 ERRORF(reporter, #buff " is null."); \
165 return; \
166 } \
167 } while (0)
Chris Dalton114a3c02017-05-26 15:17:19 -0600168
Robert Phillipseffd13f2020-07-20 15:00:36 -0400169 run_test(dContext, "draw", reporter, rtc, gold,
Robert Phillips88a32ef2018-06-07 11:05:56 -0400170 [&](DrawMeshHelper* helper) {
171 SkTArray<Box> expandedVertexData;
172 for (int i = 0; i < kBoxCount; ++i) {
173 for (int j = 0; j < 6; ++j) {
174 expandedVertexData.push_back(vertexData[i][kIndexPattern[j]]);
175 }
176 }
Chris Dalton114a3c02017-05-26 15:17:19 -0600177
Robert Phillips88a32ef2018-06-07 11:05:56 -0400178 // Draw boxes one line at a time to exercise base vertex.
Greg Danielf793de12019-09-05 13:23:23 -0400179 helper->fVertBuffer = helper->makeVertexBuffer(expandedVertexData);
180 VALIDATE(helper->fVertBuffer);
181 },
182 [&](DrawMeshHelper* helper) {
Robert Phillips88a32ef2018-06-07 11:05:56 -0400183 for (int y = 0; y < kBoxCountY; ++y) {
Chris Daltonbb768422020-03-12 12:13:29 -0600184 auto pass = helper->bindPipeline(GrPrimitiveType::kTriangles, false, true);
Greg Daniel426274b2020-07-20 11:37:38 -0400185 pass->bindBuffers(nullptr, nullptr, helper->fVertBuffer);
Chris Daltonbb768422020-03-12 12:13:29 -0600186 pass->draw(kBoxCountX * 6, y * kBoxCountX * 6);
Robert Phillips88a32ef2018-06-07 11:05:56 -0400187 }
188 });
Chris Dalton114a3c02017-05-26 15:17:19 -0600189
Robert Phillipseffd13f2020-07-20 15:00:36 -0400190 run_test(dContext, "drawIndexed", reporter, rtc, gold,
Greg Danielf793de12019-09-05 13:23:23 -0400191 [&](DrawMeshHelper* helper) {
192 helper->fIndexBuffer = helper->getIndexBuffer();
193 VALIDATE(helper->fIndexBuffer);
194 helper->fVertBuffer = helper->makeVertexBuffer(vertexData);
195 VALIDATE(helper->fVertBuffer);
196 },
197 [&](DrawMeshHelper* helper) {
198 int baseRepetition = 0;
199 int i = 0;
200 // Start at various repetitions within the patterned index buffer to exercise base
201 // index.
202 while (i < kBoxCount) {
Brian Salomon4dea72a2019-12-18 10:43:10 -0500203 static_assert(kIndexPatternRepeatCount >= 3);
Brian Osman788b9162020-02-07 10:36:46 -0500204 int repetitionCount = std::min(3 - baseRepetition, kBoxCount - i);
Chris Dalton114a3c02017-05-26 15:17:19 -0600205
Chris Daltonbb768422020-03-12 12:13:29 -0600206 auto pass = helper->bindPipeline(GrPrimitiveType::kTriangles, false, true);
Greg Daniel426274b2020-07-20 11:37:38 -0400207 pass->bindBuffers(helper->fIndexBuffer, nullptr, helper->fVertBuffer);
Chris Daltonbb768422020-03-12 12:13:29 -0600208 pass->drawIndexed(repetitionCount * 6, baseRepetition * 6, baseRepetition * 4,
209 (baseRepetition + repetitionCount) * 4 - 1,
210 (i - baseRepetition) * 4);
Chris Dalton114a3c02017-05-26 15:17:19 -0600211
Greg Danielf793de12019-09-05 13:23:23 -0400212 baseRepetition = (baseRepetition + 1) % 3;
213 i += repetitionCount;
214 }
215 });
Chris Dalton114a3c02017-05-26 15:17:19 -0600216
Robert Phillipseffd13f2020-07-20 15:00:36 -0400217 run_test(dContext, "drawIndexPattern", reporter, rtc, gold,
Greg Danielf793de12019-09-05 13:23:23 -0400218 [&](DrawMeshHelper* helper) {
219 helper->fIndexBuffer = helper->getIndexBuffer();
220 VALIDATE(helper->fIndexBuffer);
221 helper->fVertBuffer = helper->makeVertexBuffer(vertexData);
222 VALIDATE(helper->fVertBuffer);
223 },
224 [&](DrawMeshHelper* helper) {
Chris Daltonbb768422020-03-12 12:13:29 -0600225 // Draw boxes one line at a time to exercise base vertex. drawIndexPattern does
Greg Danielf793de12019-09-05 13:23:23 -0400226 // not support a base index.
227 for (int y = 0; y < kBoxCountY; ++y) {
Chris Daltonbb768422020-03-12 12:13:29 -0600228 auto pass = helper->bindPipeline(GrPrimitiveType::kTriangles, false, true);
Greg Daniel426274b2020-07-20 11:37:38 -0400229 pass->bindBuffers(helper->fIndexBuffer, nullptr, helper->fVertBuffer);
Chris Daltonbb768422020-03-12 12:13:29 -0600230 pass->drawIndexPattern(6, kBoxCountX, kIndexPatternRepeatCount, 4,
231 y * kBoxCountX * 4);
232
Greg Danielf793de12019-09-05 13:23:23 -0400233 }
234 });
Chris Dalton1d616352017-05-31 12:51:23 -0600235
236 for (bool indexed : {false, true}) {
Robert Phillipseffd13f2020-07-20 15:00:36 -0400237 if (!dContext->priv().caps()->drawInstancedSupport()) {
Chris Dalton1d616352017-05-31 12:51:23 -0600238 break;
239 }
240
Robert Phillipseffd13f2020-07-20 15:00:36 -0400241 run_test(dContext, indexed ? "drawIndexedInstanced" : "drawInstanced",
Greg Danielf793de12019-09-05 13:23:23 -0400242 reporter, rtc, gold,
243 [&](DrawMeshHelper* helper) {
244 helper->fIndexBuffer = indexed ? helper->getIndexBuffer() : nullptr;
Chris Daltonbb768422020-03-12 12:13:29 -0600245 SkTArray<uint16_t> baseIndexData;
246 baseIndexData.push_back(kBoxCountX/2 * 6); // for testing base index.
Chris Dalton03fdf6a2020-04-07 12:31:59 -0600247 for (int i = 0; i < 6; ++i) {
248 baseIndexData.push_back(kIndexPattern[i]);
Chris Daltonbb768422020-03-12 12:13:29 -0600249 }
250 helper->fIndexBuffer2 = helper->makeIndexBuffer(baseIndexData.begin(),
251 baseIndexData.count());
Greg Danielf793de12019-09-05 13:23:23 -0400252 helper->fInstBuffer = helper->makeVertexBuffer(boxes);
253 VALIDATE(helper->fInstBuffer);
254 helper->fVertBuffer =
255 helper->makeVertexBuffer(std::vector<float>{0,0, 0,1, 1,0, 1,1});
256 VALIDATE(helper->fVertBuffer);
257 helper->fVertBuffer2 = helper->makeVertexBuffer( // for testing base vertex.
258 std::vector<float>{-1,-1, -1,-1, 0,0, 0,1, 1,0, 1,1});
259 VALIDATE(helper->fVertBuffer2);
260 },
261 [&](DrawMeshHelper* helper) {
262 // Draw boxes one line at a time to exercise base instance, base vertex, and
Chris Daltonbb768422020-03-12 12:13:29 -0600263 // null vertex buffer.
Greg Danielf793de12019-09-05 13:23:23 -0400264 for (int y = 0; y < kBoxCountY; ++y) {
Greg Daniel426274b2020-07-20 11:37:38 -0400265 sk_sp<const GrBuffer> vertexBuffer;
Chris Daltonbb768422020-03-12 12:13:29 -0600266 int baseVertex = 0;
Greg Danielf793de12019-09-05 13:23:23 -0400267 switch (y % 3) {
268 case 0:
Robert Phillipseffd13f2020-07-20 15:00:36 -0400269 if (dContext->priv().caps()->shaderCaps()->vertexIDSupport()) {
Greg Danielf793de12019-09-05 13:23:23 -0400270 break;
271 }
John Stiles30212b72020-06-11 17:55:07 -0400272 [[fallthrough]];
Greg Danielf793de12019-09-05 13:23:23 -0400273 case 1:
Greg Daniel426274b2020-07-20 11:37:38 -0400274 vertexBuffer = helper->fVertBuffer;
Greg Danielf793de12019-09-05 13:23:23 -0400275 break;
276 case 2:
Greg Daniel426274b2020-07-20 11:37:38 -0400277 vertexBuffer = helper->fVertBuffer2;
Chris Daltonbb768422020-03-12 12:13:29 -0600278 baseVertex = 2;
Greg Danielf793de12019-09-05 13:23:23 -0400279 break;
280 }
Chris Daltonbb768422020-03-12 12:13:29 -0600281
282 GrPrimitiveType primitiveType = indexed ? GrPrimitiveType::kTriangles
283 : GrPrimitiveType::kTriangleStrip;
284 auto pass = helper->bindPipeline(primitiveType, true,
285 SkToBool(vertexBuffer));
286 if (indexed) {
Greg Daniel426274b2020-07-20 11:37:38 -0400287 sk_sp<const GrBuffer> indexBuffer = (y % 2) ?
288 helper->fIndexBuffer2 : helper->fIndexBuffer;
Chris Daltonbb768422020-03-12 12:13:29 -0600289 VALIDATE(indexBuffer);
290 int baseIndex = (y % 2);
Greg Daniel426274b2020-07-20 11:37:38 -0400291 pass->bindBuffers(std::move(indexBuffer), helper->fInstBuffer,
292 std::move(vertexBuffer));
Chris Dalton03fdf6a2020-04-07 12:31:59 -0600293 pass->drawIndexedInstanced(6, baseIndex, kBoxCountX, y * kBoxCountX,
Chris Daltonbb768422020-03-12 12:13:29 -0600294 baseVertex);
295 } else {
Greg Daniel426274b2020-07-20 11:37:38 -0400296 pass->bindBuffers(nullptr, helper->fInstBuffer,
297 std::move(vertexBuffer));
Chris Daltonbb768422020-03-12 12:13:29 -0600298 pass->drawInstanced(kBoxCountX, y * kBoxCountY, 4, baseVertex);
299 }
Greg Danielf793de12019-09-05 13:23:23 -0400300 }
301 });
Chris Dalton1d616352017-05-31 12:51:23 -0600302 }
Chris Dalton03fdf6a2020-04-07 12:31:59 -0600303
304 for (bool indexed : {false, true}) {
Robert Phillipseffd13f2020-07-20 15:00:36 -0400305 if (!dContext->priv().caps()->drawInstancedSupport()) {
Chris Dalton03fdf6a2020-04-07 12:31:59 -0600306 break;
307 }
308
Robert Phillipseffd13f2020-07-20 15:00:36 -0400309 run_test(dContext, (indexed) ? "drawIndexedIndirect" : "drawIndirect",
Chris Dalton03fdf6a2020-04-07 12:31:59 -0600310 reporter, rtc, gold,
311 [&](DrawMeshHelper* helper) {
312 SkTArray<uint16_t> baseIndexData;
313 baseIndexData.push_back(kBoxCountX/2 * 6); // for testing base index.
314 for (int j = 0; j < kBoxCountY; ++j) {
315 for (int i = 0; i < 6; ++i) {
316 baseIndexData.push_back(kIndexPattern[i]);
317 }
318 }
319 helper->fIndexBuffer2 = helper->makeIndexBuffer(baseIndexData.begin(),
320 baseIndexData.count());
321 VALIDATE(helper->fIndexBuffer2);
322 helper->fInstBuffer = helper->makeVertexBuffer(boxes);
323 VALIDATE(helper->fInstBuffer);
324 helper->fVertBuffer = helper->makeVertexBuffer(std::vector<float>{
325 -1,-1, 0,0, 0,1, 1,0, 1,1, -1,-1, 0,0, 1,0, 0,1, 1,1});
326 VALIDATE(helper->fVertBuffer);
327
328 GrDrawIndirectCommand* drawIndirect = nullptr;
329 GrDrawIndexedIndirectCommand* drawIndexedIndirect = nullptr;
330 if (indexed) {
331 // Make helper->fDrawIndirectBufferOffset nonzero.
332 sk_sp<const GrBuffer> dummyBuff;
333 size_t dummyOffset;
Chris Daltonb8d7e002020-05-13 11:02:10 -0600334 // Make a superfluous call to makeDrawIndirectSpace in order to test
335 // "offsetInBytes!=0" for the actual call to makeDrawIndexedIndirectSpace.
336 helper->target()->makeDrawIndirectSpace(29, &dummyBuff, &dummyOffset);
337 drawIndexedIndirect = helper->target()->makeDrawIndexedIndirectSpace(
Chris Dalton03fdf6a2020-04-07 12:31:59 -0600338 kBoxCountY, &helper->fDrawIndirectBuffer,
339 &helper->fDrawIndirectBufferOffset);
340 } else {
341 // Make helper->fDrawIndirectBufferOffset nonzero.
342 sk_sp<const GrBuffer> dummyBuff;
343 size_t dummyOffset;
Chris Daltonb8d7e002020-05-13 11:02:10 -0600344 // Make a superfluous call to makeDrawIndexedIndirectSpace in order to test
345 // "offsetInBytes!=0" for the actual call to makeDrawIndirectSpace.
346 helper->target()->makeDrawIndexedIndirectSpace(7, &dummyBuff, &dummyOffset);
347 drawIndirect = helper->target()->makeDrawIndirectSpace(
Chris Dalton03fdf6a2020-04-07 12:31:59 -0600348 kBoxCountY, &helper->fDrawIndirectBuffer,
349 &helper->fDrawIndirectBufferOffset);
350 }
351
352 // Draw boxes one line at a time to exercise multiple draws.
353 for (int y = 0; y < kBoxCountY; ++y) {
354 int baseVertex = (y % 2) ? 1 : 6;
355 if (indexed) {
356 int baseIndex = 1 + y * 6;
357 drawIndexedIndirect->fIndexCount = 6;
358 drawIndexedIndirect->fBaseIndex = baseIndex;
359 drawIndexedIndirect->fInstanceCount = kBoxCountX;
360 drawIndexedIndirect->fBaseInstance = y * kBoxCountX;
361 drawIndexedIndirect->fBaseVertex = baseVertex;
362 ++drawIndexedIndirect;
363 } else {
364 drawIndirect->fInstanceCount = kBoxCountX;
365 drawIndirect->fBaseInstance = y * kBoxCountX;
366 drawIndirect->fVertexCount = 4;
367 drawIndirect->fBaseVertex = baseVertex;
368 ++drawIndirect;
369 }
370 }
371 },
372 [&](DrawMeshHelper* helper) {
373 GrOpsRenderPass* pass;
374 if (indexed) {
375 pass = helper->bindPipeline(GrPrimitiveType::kTriangles, true, true);
Greg Daniel426274b2020-07-20 11:37:38 -0400376 pass->bindBuffers(helper->fIndexBuffer2, helper->fInstBuffer,
377 helper->fVertBuffer);
Chris Dalton03fdf6a2020-04-07 12:31:59 -0600378 for (int i = 0; i < 3; ++i) {
379 int start = kBoxCountY * i / 3;
380 int end = kBoxCountY * (i + 1) / 3;
381 size_t offset = helper->fDrawIndirectBufferOffset + start *
382 sizeof(GrDrawIndexedIndirectCommand);
383 pass->drawIndexedIndirect(helper->fDrawIndirectBuffer.get(), offset,
384 end - start);
385 }
386 } else {
387 pass = helper->bindPipeline(GrPrimitiveType::kTriangleStrip, true, true);
Greg Daniel426274b2020-07-20 11:37:38 -0400388 pass->bindBuffers(nullptr, helper->fInstBuffer, helper->fVertBuffer);
Chris Dalton03fdf6a2020-04-07 12:31:59 -0600389 for (int i = 0; i < 2; ++i) {
390 int start = kBoxCountY * i / 2;
391 int end = kBoxCountY * (i + 1) / 2;
392 size_t offset = helper->fDrawIndirectBufferOffset + start *
393 sizeof(GrDrawIndirectCommand);
394 pass->drawIndirect(helper->fDrawIndirectBuffer.get(), offset,
395 end - start);
396 }
397 }
398 });
399 }
Chris Dalton114a3c02017-05-26 15:17:19 -0600400}
401
402////////////////////////////////////////////////////////////////////////////////////////////////////
403
404class GrMeshTestOp : public GrDrawOp {
405public:
406 DEFINE_OP_CLASS_ID
407
Robert Phillipseffd13f2020-07-20 15:00:36 -0400408 static std::unique_ptr<GrDrawOp> Make(GrRecordingContext* rContext,
Greg Danielf793de12019-09-05 13:23:23 -0400409 std::function<void(DrawMeshHelper*)> prepareFn,
410 std::function<void(DrawMeshHelper*)> executeFn) {
Robert Phillipseffd13f2020-07-20 15:00:36 -0400411 GrOpMemoryPool* pool = rContext->priv().opMemoryPool();
Robert Phillipsc994a932018-06-19 13:09:54 -0400412
Greg Danielf793de12019-09-05 13:23:23 -0400413 return pool->allocate<GrMeshTestOp>(prepareFn, executeFn);
Robert Phillips88a32ef2018-06-07 11:05:56 -0400414 }
415
416private:
Robert Phillips7c525e62018-06-12 10:11:12 -0400417 friend class GrOpMemoryPool; // for ctor
418
Greg Danielf793de12019-09-05 13:23:23 -0400419 GrMeshTestOp(std::function<void(DrawMeshHelper*)> prepareFn,
420 std::function<void(DrawMeshHelper*)> executeFn)
Chris Dalton114a3c02017-05-26 15:17:19 -0600421 : INHERITED(ClassID())
Greg Danielf793de12019-09-05 13:23:23 -0400422 , fPrepareFn(prepareFn)
423 , fExecuteFn(executeFn){
Chris Dalton114a3c02017-05-26 15:17:19 -0600424 this->setBounds(SkRect::MakeIWH(kImageWidth, kImageHeight),
Greg Daniel5faf4742019-10-01 15:14:44 -0400425 HasAABloat::kNo, IsHairline::kNo);
Chris Dalton114a3c02017-05-26 15:17:19 -0600426 }
427
Chris Dalton114a3c02017-05-26 15:17:19 -0600428 const char* name() const override { return "GrMeshTestOp"; }
429 FixedFunctionFlags fixedFunctionFlags() const override { return FixedFunctionFlags::kNone; }
Chris Dalton6ce447a2019-06-23 18:07:38 -0600430 GrProcessorSet::Analysis finalize(const GrCaps&, const GrAppliedClip*,
431 bool hasMixedSampledCoverage, GrClampType) override {
Chris Dalton4b62aed2019-01-15 11:53:00 -0700432 return GrProcessorSet::EmptySetAnalysis();
Brian Salomonf86d37b2017-06-16 10:04:34 -0400433 }
Robert Phillipsc655c3a2020-03-18 13:23:45 -0400434
435 void onPrePrepare(GrRecordingContext*,
Brian Salomon8afde5f2020-04-01 16:22:00 -0400436 const GrSurfaceProxyView* writeView,
Robert Phillipsc655c3a2020-03-18 13:23:45 -0400437 GrAppliedClip*,
Greg Danield358cbe2020-09-11 09:33:54 -0400438 const GrXferProcessor::DstProxyView&,
439 GrXferBarrierFlags renderPassXferBarriers) override {}
Greg Danielf793de12019-09-05 13:23:23 -0400440 void onPrepare(GrOpFlushState* state) override {
John Stilesfbd050b2020-08-03 13:21:46 -0400441 fHelper = std::make_unique<DrawMeshHelper>(state);
Greg Danielf793de12019-09-05 13:23:23 -0400442 fPrepareFn(fHelper.get());
443 }
Brian Salomon588cec72018-11-14 13:56:37 -0500444 void onExecute(GrOpFlushState* state, const SkRect& chainBounds) override {
Greg Danielf793de12019-09-05 13:23:23 -0400445 fExecuteFn(fHelper.get());
Chris Dalton114a3c02017-05-26 15:17:19 -0600446 }
447
Greg Danielf793de12019-09-05 13:23:23 -0400448 std::unique_ptr<DrawMeshHelper> fHelper;
449 std::function<void(DrawMeshHelper*)> fPrepareFn;
450 std::function<void(DrawMeshHelper*)> fExecuteFn;
Chris Dalton114a3c02017-05-26 15:17:19 -0600451
John Stiles7571f9e2020-09-02 22:42:33 -0400452 using INHERITED = GrDrawOp;
Chris Dalton114a3c02017-05-26 15:17:19 -0600453};
454
455class GrMeshTestProcessor : public GrGeometryProcessor {
456public:
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500457 static GrGeometryProcessor* Make(SkArenaAlloc* arena, bool instanced, bool hasVertexBuffer) {
458 return arena->make<GrMeshTestProcessor>(instanced, hasVertexBuffer);
459 }
460
461 const char* name() const override { return "GrMeshTestProcessor"; }
462
463 const Attribute& inColor() const {
464 return fVertexColor.isInitialized() ? fVertexColor : fInstanceColor;
465 }
466
467 void getGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder* b) const final {
468 b->add32(fInstanceLocation.isInitialized());
469 b->add32(fVertexPosition.isInitialized());
470 }
471
472 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const final;
473
474private:
475 friend class GLSLMeshTestProcessor;
476 friend class ::SkArenaAlloc; // for access to ctor
477
Chris Dalton1d616352017-05-31 12:51:23 -0600478 GrMeshTestProcessor(bool instanced, bool hasVertexBuffer)
Brian Salomon92be2f72018-06-19 14:33:47 -0400479 : INHERITED(kGrMeshTestProcessor_ClassID) {
Chris Dalton1d616352017-05-31 12:51:23 -0600480 if (instanced) {
Brian Osmand4c29702018-09-14 16:16:55 -0400481 fInstanceLocation = {"location", kFloat2_GrVertexAttribType, kHalf2_GrSLType};
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500482 fInstanceColor = {"color", kUByte4_norm_GrVertexAttribType, kHalf4_GrSLType};
483 this->setInstanceAttributes(&fInstanceLocation, 2);
Chris Dalton1d616352017-05-31 12:51:23 -0600484 if (hasVertexBuffer) {
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500485 fVertexPosition = {"vertex", kFloat2_GrVertexAttribType, kHalf2_GrSLType};
486 this->setVertexAttributes(&fVertexPosition, 1);
Chris Dalton1d616352017-05-31 12:51:23 -0600487 }
Chris Dalton1d616352017-05-31 12:51:23 -0600488 } else {
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500489 fVertexPosition = {"vertex", kFloat2_GrVertexAttribType, kHalf2_GrSLType};
490 fVertexColor = {"color", kUByte4_norm_GrVertexAttribType, kHalf4_GrSLType};
491 this->setVertexAttributes(&fVertexPosition, 2);
Chris Dalton1d616352017-05-31 12:51:23 -0600492 }
Chris Dalton114a3c02017-05-26 15:17:19 -0600493 }
494
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500495 Attribute fVertexPosition;
496 Attribute fVertexColor;
Brian Salomon92be2f72018-06-19 14:33:47 -0400497
498 Attribute fInstanceLocation;
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500499 Attribute fInstanceColor;
Chris Dalton114a3c02017-05-26 15:17:19 -0600500
John Stiles7571f9e2020-09-02 22:42:33 -0400501 using INHERITED = GrGeometryProcessor;
Chris Dalton114a3c02017-05-26 15:17:19 -0600502};
503
504class GLSLMeshTestProcessor : public GrGLSLGeometryProcessor {
Brian Osman609f1592020-07-01 15:14:39 -0400505 void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor&) final {}
Chris Dalton114a3c02017-05-26 15:17:19 -0600506
507 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) final {
508 const GrMeshTestProcessor& mp = args.fGP.cast<GrMeshTestProcessor>();
509
510 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
511 varyingHandler->emitAttributes(mp);
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500512 varyingHandler->addPassThroughAttribute(mp.inColor(), args.fOutputColor);
Chris Dalton114a3c02017-05-26 15:17:19 -0600513
514 GrGLSLVertexBuilder* v = args.fVertBuilder;
Brian Salomon92be2f72018-06-19 14:33:47 -0400515 if (!mp.fInstanceLocation.isInitialized()) {
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500516 v->codeAppendf("float2 vertex = %s;", mp.fVertexPosition.name());
Chris Dalton1d616352017-05-31 12:51:23 -0600517 } else {
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500518 if (mp.fVertexPosition.isInitialized()) {
519 v->codeAppendf("float2 offset = %s;", mp.fVertexPosition.name());
Chris Dalton1d616352017-05-31 12:51:23 -0600520 } else {
Ethan Nicholas8aa45692017-09-20 11:24:15 -0400521 v->codeAppend ("float2 offset = float2(sk_VertexID / 2, sk_VertexID % 2);");
Chris Dalton1d616352017-05-31 12:51:23 -0600522 }
Brian Salomon92be2f72018-06-19 14:33:47 -0400523 v->codeAppendf("float2 vertex = %s + offset * %i;", mp.fInstanceLocation.name(),
Brian Salomon70132d02018-05-29 15:33:06 -0400524 kBoxSize);
Chris Dalton1d616352017-05-31 12:51:23 -0600525 }
Ethan Nicholas8aa45692017-09-20 11:24:15 -0400526 gpArgs->fPositionVar.set(kFloat2_GrSLType, "vertex");
Chris Dalton114a3c02017-05-26 15:17:19 -0600527
Chris Dalton60283612018-02-14 13:38:14 -0700528 GrGLSLFPFragmentBuilder* f = args.fFragBuilder;
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400529 f->codeAppendf("%s = half4(1);", args.fOutputCoverage);
Chris Dalton114a3c02017-05-26 15:17:19 -0600530 }
531};
532
533GrGLSLPrimitiveProcessor* GrMeshTestProcessor::createGLSLInstance(const GrShaderCaps&) const {
534 return new GLSLMeshTestProcessor;
535}
536
537////////////////////////////////////////////////////////////////////////////////////////////////////
538
Chris Daltonbb768422020-03-12 12:13:29 -0600539sk_sp<const GrBuffer> DrawMeshHelper::makeIndexBuffer(const uint16_t indices[], int count) {
540 return sk_sp<const GrBuffer>(fState->resourceProvider()->createBuffer(
541 count * sizeof(uint16_t), GrGpuBufferType::kIndex, kDynamic_GrAccessPattern, indices));
542}
543
Chris Dalton114a3c02017-05-26 15:17:19 -0600544template<typename T>
545sk_sp<const GrBuffer> DrawMeshHelper::makeVertexBuffer(const T* data, int count) {
Brian Salomonae64c192019-02-05 09:41:37 -0500546 return sk_sp<const GrBuffer>(fState->resourceProvider()->createBuffer(
Brian Salomondbf70722019-02-07 11:31:24 -0500547 count * sizeof(T), GrGpuBufferType::kVertex, kDynamic_GrAccessPattern, data));
Chris Dalton114a3c02017-05-26 15:17:19 -0600548}
549
550sk_sp<const GrBuffer> DrawMeshHelper::getIndexBuffer() {
551 GR_DEFINE_STATIC_UNIQUE_KEY(gIndexBufferKey);
Brian Salomond28a79d2017-10-16 13:01:07 -0400552 return fState->resourceProvider()->findOrCreatePatternedIndexBuffer(
553 kIndexPattern, 6, kIndexPatternRepeatCount, 4, gIndexBufferKey);
Chris Dalton114a3c02017-05-26 15:17:19 -0600554}
555
Chris Daltonbb768422020-03-12 12:13:29 -0600556GrOpsRenderPass* DrawMeshHelper::bindPipeline(GrPrimitiveType primitiveType, bool isInstanced,
557 bool hasVertexBuffer) {
Robert Phillips3968fcb2019-12-05 16:40:31 -0500558 GrProcessorSet processorSet(SkBlendMode::kSrc);
559
560 // TODO: add a GrProcessorSet testing helper to make this easier
561 SkPMColor4f overrideColor;
562 processorSet.finalize(GrProcessorAnalysisColor(),
563 GrProcessorAnalysisCoverage::kNone,
564 fState->appliedClip(),
565 nullptr,
566 false,
567 fState->caps(),
568 GrClampType::kAuto,
569 &overrideColor);
570
571 auto pipeline = GrSimpleMeshDrawOpHelper::CreatePipeline(fState,
572 std::move(processorSet),
573 GrPipeline::InputFlags::kNone);
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500574
Chris Daltonbb768422020-03-12 12:13:29 -0600575 GrGeometryProcessor* mtp = GrMeshTestProcessor::Make(fState->allocator(), isInstanced,
576 hasVertexBuffer);
Robert Phillips901aff02019-10-08 12:32:56 -0400577
Chris Daltonbb768422020-03-12 12:13:29 -0600578 GrProgramInfo programInfo(fState->proxy()->numSamples(), fState->proxy()->numStencilSamples(),
Brian Salomon8afde5f2020-04-01 16:22:00 -0400579 fState->proxy()->backendFormat(), fState->writeView()->origin(),
Greg Danield358cbe2020-09-11 09:33:54 -0400580 pipeline, mtp, primitiveType, 0, fState->renderPassBarriers());
Robert Phillips901aff02019-10-08 12:32:56 -0400581
Chris Dalton4386ad12020-02-19 16:42:06 -0700582 fState->opsRenderPass()->bindPipeline(programInfo, SkRect::MakeIWH(kImageWidth, kImageHeight));
Chris Daltonbb768422020-03-12 12:13:29 -0600583 return fState->opsRenderPass();
Chris Dalton114a3c02017-05-26 15:17:19 -0600584}
585
Adlai Hollerc95b5892020-08-11 12:02:22 -0400586static void run_test(GrDirectContext* dContext, const char* testName,
Robert Phillipseffd13f2020-07-20 15:00:36 -0400587 skiatest::Reporter* reporter,
Brian Salomonbf6b9792019-08-21 09:38:10 -0400588 const std::unique_ptr<GrRenderTargetContext>& rtc, const SkBitmap& gold,
Greg Danielf793de12019-09-05 13:23:23 -0400589 std::function<void(DrawMeshHelper*)> prepareFn,
590 std::function<void(DrawMeshHelper*)> executeFn) {
Chris Dalton114a3c02017-05-26 15:17:19 -0600591 const int w = gold.width(), h = gold.height(), rowBytes = gold.rowBytes();
592 const uint32_t* goldPx = reinterpret_cast<const uint32_t*>(gold.getPixels());
593 if (h != rtc->height() || w != rtc->width()) {
594 ERRORF(reporter, "[%s] expectation and rtc not compatible (?).", testName);
595 return;
596 }
597 if (sizeof(uint32_t) * kImageWidth != gold.rowBytes()) {
Adlai Holler684838f2020-05-12 10:41:04 -0400598 ERRORF(reporter, "[%s] unexpected row bytes in gold image", testName);
Chris Dalton114a3c02017-05-26 15:17:19 -0600599 return;
600 }
601
602 SkAutoSTMalloc<kImageHeight * kImageWidth, uint32_t> resultPx(h * rowBytes);
Michael Ludwig81d41722020-05-26 16:57:38 -0400603 rtc->clear(SkPMColor4f::FromBytes_RGBA(0xbaaaaaad));
Adlai Hollerc95b5892020-08-11 12:02:22 -0400604 rtc->priv().testingOnly_addDrawOp(GrMeshTestOp::Make(dContext, prepareFn, executeFn));
Chris Daltond4dec972020-04-03 11:01:30 -0600605
Adlai Hollerc95b5892020-08-11 12:02:22 -0400606 rtc->readPixels(dContext, gold.info(), resultPx, rowBytes, {0, 0});
Chris Daltond4dec972020-04-03 11:01:30 -0600607
608#ifdef WRITE_PNG_CONTEXT_TYPE
609#define STRINGIFY(X) #X
610#define TOSTRING(X) STRINGIFY(X)
611 SkString filename;
612 filename.printf("GrMeshTest_%s_%s.png", TOSTRING(WRITE_PNG_CONTEXT_TYPE), testName);
613 SkDebugf("writing %s...\n", filename.c_str());
614 ToolUtils::EncodeImageToFile(filename.c_str(), SkPixmap(gold.info(), resultPx, rowBytes),
615 SkEncodedImageFormat::kPNG, 100);
616#endif
617
Chris Dalton114a3c02017-05-26 15:17:19 -0600618 for (int y = 0; y < h; ++y) {
619 for (int x = 0; x < w; ++x) {
620 uint32_t expected = goldPx[y * kImageWidth + x];
621 uint32_t actual = resultPx[y * kImageWidth + x];
622 if (expected != actual) {
623 ERRORF(reporter, "[%s] pixel (%i,%i): got 0x%x expected 0x%x",
624 testName, x, y, actual, expected);
625 return;
626 }
627 }
628 }
629}