blob: 2aa049b632e9a58489a969d77f7fbc427d2076b9 [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>
11#include <vector>
Mike Kleinc0bd9f92019-04-23 12:05:21 -050012#include "include/core/SkBitmap.h"
Robert Phillips6d344c32020-07-06 10:56:46 -040013#include "include/gpu/GrDirectContext.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050014#include "include/private/GrResourceKey.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050015#include "src/gpu/GrCaps.h"
16#include "src/gpu/GrContextPriv.h"
17#include "src/gpu/GrGeometryProcessor.h"
Brian Salomonf2ebdd92019-09-30 12:15:30 -040018#include "src/gpu/GrImageInfo.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050019#include "src/gpu/GrMemoryPool.h"
20#include "src/gpu/GrOpFlushState.h"
Greg Daniel2d41d0d2019-08-26 11:08:51 -040021#include "src/gpu/GrOpsRenderPass.h"
Robert Phillips901aff02019-10-08 12:32:56 -040022#include "src/gpu/GrProgramInfo.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050023#include "src/gpu/GrRenderTargetContext.h"
24#include "src/gpu/GrRenderTargetContextPriv.h"
25#include "src/gpu/GrResourceProvider.h"
26#include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h"
27#include "src/gpu/glsl/GrGLSLGeometryProcessor.h"
28#include "src/gpu/glsl/GrGLSLVarying.h"
29#include "src/gpu/glsl/GrGLSLVertexGeoBuilder.h"
Robert Phillips3968fcb2019-12-05 16:40:31 -050030#include "src/gpu/ops/GrSimpleMeshDrawOpHelper.h"
Chris Dalton114a3c02017-05-26 15:17:19 -060031
Chris Daltond4dec972020-04-03 11:01:30 -060032#if 0
33#include "tools/ToolUtils.h"
34#define WRITE_PNG_CONTEXT_TYPE kANGLE_D3D11_ES3_ContextType
35#endif
36
Chris Dalton114a3c02017-05-26 15:17:19 -060037GR_DECLARE_STATIC_UNIQUE_KEY(gIndexBufferKey);
38
39static constexpr int kBoxSize = 2;
40static constexpr int kBoxCountY = 8;
41static constexpr int kBoxCountX = 8;
42static constexpr int kBoxCount = kBoxCountY * kBoxCountX;
43
44static constexpr int kImageWidth = kBoxCountY * kBoxSize;
45static constexpr int kImageHeight = kBoxCountX * kBoxSize;
46
47static constexpr int kIndexPatternRepeatCount = 3;
48constexpr uint16_t kIndexPattern[6] = {0, 1, 2, 1, 2, 3};
49
50
51class DrawMeshHelper {
52public:
53 DrawMeshHelper(GrOpFlushState* state) : fState(state) {}
54
55 sk_sp<const GrBuffer> getIndexBuffer();
56
Chris Daltonbb768422020-03-12 12:13:29 -060057 sk_sp<const GrBuffer> makeIndexBuffer(const uint16_t[], int count);
58
Chris Dalton114a3c02017-05-26 15:17:19 -060059 template<typename T> sk_sp<const GrBuffer> makeVertexBuffer(const SkTArray<T>& data) {
60 return this->makeVertexBuffer(data.begin(), data.count());
61 }
Chris Dalton1d616352017-05-31 12:51:23 -060062 template<typename T> sk_sp<const GrBuffer> makeVertexBuffer(const std::vector<T>& data) {
63 return this->makeVertexBuffer(data.data(), data.size());
64 }
Chris Dalton114a3c02017-05-26 15:17:19 -060065 template<typename T> sk_sp<const GrBuffer> makeVertexBuffer(const T* data, int count);
66
Chris Daltonb8d7e002020-05-13 11:02:10 -060067 GrMeshDrawOp::Target* target() { return fState; }
Chris Dalton03fdf6a2020-04-07 12:31:59 -060068
Chris Daltonbb768422020-03-12 12:13:29 -060069 sk_sp<const GrBuffer> fIndexBuffer;
70 sk_sp<const GrBuffer> fIndexBuffer2;
71 sk_sp<const GrBuffer> fInstBuffer;
Greg Danielf793de12019-09-05 13:23:23 -040072 sk_sp<const GrBuffer> fVertBuffer;
73 sk_sp<const GrBuffer> fVertBuffer2;
Chris Dalton03fdf6a2020-04-07 12:31:59 -060074 sk_sp<const GrBuffer> fDrawIndirectBuffer;
75 size_t fDrawIndirectBufferOffset;
Greg Danielf793de12019-09-05 13:23:23 -040076
Chris Daltonbb768422020-03-12 12:13:29 -060077 GrOpsRenderPass* bindPipeline(GrPrimitiveType, bool isInstanced, bool hasVertexBuffer);
Chris Dalton114a3c02017-05-26 15:17:19 -060078
79private:
80 GrOpFlushState* fState;
81};
82
83struct Box {
84 float fX, fY;
85 GrColor fColor;
86};
87
88////////////////////////////////////////////////////////////////////////////////////////////////////
89
90/**
Chris Daltoneb694b72020-03-16 09:25:50 -060091 * This is a GPU-backend specific test. It tries to test all possible usecases of
92 * GrOpsRenderPass::draw*. The test works by drawing checkerboards of colored boxes, reading back
93 * the pixels, and comparing with expected results. The boxes are drawn on integer boundaries and
94 * the (opaque) colors are chosen from the set (r,g,b) = (0,255)^3, so the GPU renderings ought to
95 * produce exact matches.
Chris Dalton114a3c02017-05-26 15:17:19 -060096 */
97
Robert Phillips88a32ef2018-06-07 11:05:56 -040098static void run_test(GrContext* context, const char* testName, skiatest::Reporter*,
Brian Salomonbf6b9792019-08-21 09:38:10 -040099 const std::unique_ptr<GrRenderTargetContext>&, const SkBitmap& gold,
Greg Danielf793de12019-09-05 13:23:23 -0400100 std::function<void(DrawMeshHelper*)> prepareFn,
101 std::function<void(DrawMeshHelper*)> executeFn);
Chris Dalton114a3c02017-05-26 15:17:19 -0600102
Chris Daltond4dec972020-04-03 11:01:30 -0600103#ifdef WRITE_PNG_CONTEXT_TYPE
104static bool IsContextTypeForOutputPNGs(skiatest::GrContextFactoryContextType type) {
105 return type == skiatest::GrContextFactoryContextType::WRITE_PNG_CONTEXT_TYPE;
106}
107DEF_GPUTEST_FOR_CONTEXTS(GrMeshTest, IsContextTypeForOutputPNGs, reporter, ctxInfo, nullptr) {
108#else
Chris Dalton114a3c02017-05-26 15:17:19 -0600109DEF_GPUTEST_FOR_RENDERING_CONTEXTS(GrMeshTest, reporter, ctxInfo) {
Chris Daltond4dec972020-04-03 11:01:30 -0600110#endif
Robert Phillips6d344c32020-07-06 10:56:46 -0400111 auto context = ctxInfo.directContext();
Chris Dalton114a3c02017-05-26 15:17:19 -0600112
Greg Daniele20fcad2020-01-08 11:52:34 -0500113 auto rtc = GrRenderTargetContext::Make(
114 context, GrColorType::kRGBA_8888, nullptr, SkBackingFit::kExact,
115 {kImageWidth, kImageHeight});
Chris Dalton114a3c02017-05-26 15:17:19 -0600116 if (!rtc) {
117 ERRORF(reporter, "could not create render target context.");
118 return;
119 }
120
121 SkTArray<Box> boxes;
122 SkTArray<std::array<Box, 4>> vertexData;
123 SkBitmap gold;
124
125 // ---- setup ----------
126
127 SkPaint paint;
128 paint.setBlendMode(SkBlendMode::kSrc);
129 gold.allocN32Pixels(kImageWidth, kImageHeight);
130
131 SkCanvas goldCanvas(gold);
132
133 for (int y = 0; y < kBoxCountY; ++y) {
134 for (int x = 0; x < kBoxCountX; ++x) {
135 int c = y + x;
136 int rgb[3] = {-(c & 1) & 0xff, -((c >> 1) & 1) & 0xff, -((c >> 2) & 1) & 0xff};
137
138 const Box box = boxes.push_back() = {
Brian Salomon23356442018-11-30 15:33:19 -0500139 float(x * kBoxSize),
140 float(y * kBoxSize),
141 GrColorPackRGBA(rgb[0], rgb[1], rgb[2], 255)
Chris Dalton114a3c02017-05-26 15:17:19 -0600142 };
143
144 std::array<Box, 4>& boxVertices = vertexData.push_back();
145 for (int i = 0; i < 4; ++i) {
146 boxVertices[i] = {
Brian Salomon23356442018-11-30 15:33:19 -0500147 box.fX + (i / 2) * kBoxSize,
148 box.fY + (i % 2) * kBoxSize,
149 box.fColor
Chris Dalton114a3c02017-05-26 15:17:19 -0600150 };
151 }
152
153 paint.setARGB(255, rgb[0], rgb[1], rgb[2]);
154 goldCanvas.drawRect(SkRect::MakeXYWH(box.fX, box.fY, kBoxSize, kBoxSize), paint);
155 }
156 }
157
Chris Dalton114a3c02017-05-26 15:17:19 -0600158 // ---- tests ----------
159
Brian Salomon23356442018-11-30 15:33:19 -0500160#define VALIDATE(buff) \
161 do { \
162 if (!buff) { \
163 ERRORF(reporter, #buff " is null."); \
164 return; \
165 } \
166 } while (0)
Chris Dalton114a3c02017-05-26 15:17:19 -0600167
Chris Daltonbb768422020-03-12 12:13:29 -0600168 run_test(context, "draw", reporter, rtc, gold,
Robert Phillips88a32ef2018-06-07 11:05:56 -0400169 [&](DrawMeshHelper* helper) {
170 SkTArray<Box> expandedVertexData;
171 for (int i = 0; i < kBoxCount; ++i) {
172 for (int j = 0; j < 6; ++j) {
173 expandedVertexData.push_back(vertexData[i][kIndexPattern[j]]);
174 }
175 }
Chris Dalton114a3c02017-05-26 15:17:19 -0600176
Robert Phillips88a32ef2018-06-07 11:05:56 -0400177 // Draw boxes one line at a time to exercise base vertex.
Greg Danielf793de12019-09-05 13:23:23 -0400178 helper->fVertBuffer = helper->makeVertexBuffer(expandedVertexData);
179 VALIDATE(helper->fVertBuffer);
180 },
181 [&](DrawMeshHelper* helper) {
Robert Phillips88a32ef2018-06-07 11:05:56 -0400182 for (int y = 0; y < kBoxCountY; ++y) {
Chris Daltonbb768422020-03-12 12:13:29 -0600183 auto pass = helper->bindPipeline(GrPrimitiveType::kTriangles, false, true);
184 pass->bindBuffers(nullptr, nullptr, helper->fVertBuffer.get());
185 pass->draw(kBoxCountX * 6, y * kBoxCountX * 6);
Robert Phillips88a32ef2018-06-07 11:05:56 -0400186 }
187 });
Chris Dalton114a3c02017-05-26 15:17:19 -0600188
Chris Daltonbb768422020-03-12 12:13:29 -0600189 run_test(context, "drawIndexed", reporter, rtc, gold,
Greg Danielf793de12019-09-05 13:23:23 -0400190 [&](DrawMeshHelper* helper) {
191 helper->fIndexBuffer = helper->getIndexBuffer();
192 VALIDATE(helper->fIndexBuffer);
193 helper->fVertBuffer = helper->makeVertexBuffer(vertexData);
194 VALIDATE(helper->fVertBuffer);
195 },
196 [&](DrawMeshHelper* helper) {
197 int baseRepetition = 0;
198 int i = 0;
199 // Start at various repetitions within the patterned index buffer to exercise base
200 // index.
201 while (i < kBoxCount) {
Brian Salomon4dea72a2019-12-18 10:43:10 -0500202 static_assert(kIndexPatternRepeatCount >= 3);
Brian Osman788b9162020-02-07 10:36:46 -0500203 int repetitionCount = std::min(3 - baseRepetition, kBoxCount - i);
Chris Dalton114a3c02017-05-26 15:17:19 -0600204
Chris Daltonbb768422020-03-12 12:13:29 -0600205 auto pass = helper->bindPipeline(GrPrimitiveType::kTriangles, false, true);
206 pass->bindBuffers(helper->fIndexBuffer.get(), nullptr,
207 helper->fVertBuffer.get());
208 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
Chris Daltonbb768422020-03-12 12:13:29 -0600217 run_test(context, "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);
229 pass->bindBuffers(helper->fIndexBuffer.get(), nullptr,
230 helper->fVertBuffer.get());
231 pass->drawIndexPattern(6, kBoxCountX, kIndexPatternRepeatCount, 4,
232 y * kBoxCountX * 4);
233
Greg Danielf793de12019-09-05 13:23:23 -0400234 }
235 });
Chris Dalton1d616352017-05-31 12:51:23 -0600236
237 for (bool indexed : {false, true}) {
Chris Daltona77cdee2020-04-03 14:50:43 -0600238 if (!context->priv().caps()->drawInstancedSupport()) {
Chris Dalton1d616352017-05-31 12:51:23 -0600239 break;
240 }
241
Chris Daltonbb768422020-03-12 12:13:29 -0600242 run_test(context, indexed ? "drawIndexedInstanced" : "drawInstanced",
Greg Danielf793de12019-09-05 13:23:23 -0400243 reporter, rtc, gold,
244 [&](DrawMeshHelper* helper) {
245 helper->fIndexBuffer = indexed ? helper->getIndexBuffer() : nullptr;
Chris Daltonbb768422020-03-12 12:13:29 -0600246 SkTArray<uint16_t> baseIndexData;
247 baseIndexData.push_back(kBoxCountX/2 * 6); // for testing base index.
Chris Dalton03fdf6a2020-04-07 12:31:59 -0600248 for (int i = 0; i < 6; ++i) {
249 baseIndexData.push_back(kIndexPattern[i]);
Chris Daltonbb768422020-03-12 12:13:29 -0600250 }
251 helper->fIndexBuffer2 = helper->makeIndexBuffer(baseIndexData.begin(),
252 baseIndexData.count());
Greg Danielf793de12019-09-05 13:23:23 -0400253 helper->fInstBuffer = helper->makeVertexBuffer(boxes);
254 VALIDATE(helper->fInstBuffer);
255 helper->fVertBuffer =
256 helper->makeVertexBuffer(std::vector<float>{0,0, 0,1, 1,0, 1,1});
257 VALIDATE(helper->fVertBuffer);
258 helper->fVertBuffer2 = helper->makeVertexBuffer( // for testing base vertex.
259 std::vector<float>{-1,-1, -1,-1, 0,0, 0,1, 1,0, 1,1});
260 VALIDATE(helper->fVertBuffer2);
261 },
262 [&](DrawMeshHelper* helper) {
263 // Draw boxes one line at a time to exercise base instance, base vertex, and
Chris Daltonbb768422020-03-12 12:13:29 -0600264 // null vertex buffer.
Greg Danielf793de12019-09-05 13:23:23 -0400265 for (int y = 0; y < kBoxCountY; ++y) {
Chris Daltonbb768422020-03-12 12:13:29 -0600266 const GrBuffer* vertexBuffer = nullptr;
267 int baseVertex = 0;
Greg Danielf793de12019-09-05 13:23:23 -0400268 switch (y % 3) {
269 case 0:
270 if (context->priv().caps()->shaderCaps()->vertexIDSupport()) {
Greg Danielf793de12019-09-05 13:23:23 -0400271 break;
272 }
John Stiles30212b72020-06-11 17:55:07 -0400273 [[fallthrough]];
Greg Danielf793de12019-09-05 13:23:23 -0400274 case 1:
Chris Daltonbb768422020-03-12 12:13:29 -0600275 vertexBuffer = helper->fVertBuffer.get();
Greg Danielf793de12019-09-05 13:23:23 -0400276 break;
277 case 2:
Chris Daltonbb768422020-03-12 12:13:29 -0600278 vertexBuffer = helper->fVertBuffer2.get();
279 baseVertex = 2;
Greg Danielf793de12019-09-05 13:23:23 -0400280 break;
281 }
Chris Daltonbb768422020-03-12 12:13:29 -0600282
283 GrPrimitiveType primitiveType = indexed ? GrPrimitiveType::kTriangles
284 : GrPrimitiveType::kTriangleStrip;
285 auto pass = helper->bindPipeline(primitiveType, true,
286 SkToBool(vertexBuffer));
287 if (indexed) {
288 const GrBuffer* indexBuffer = (y % 2) ?
289 helper->fIndexBuffer2.get() : helper->fIndexBuffer.get();
290 VALIDATE(indexBuffer);
291 int baseIndex = (y % 2);
292 pass->bindBuffers(indexBuffer, helper->fInstBuffer.get(),
293 vertexBuffer);
Chris Dalton03fdf6a2020-04-07 12:31:59 -0600294 pass->drawIndexedInstanced(6, baseIndex, kBoxCountX, y * kBoxCountX,
Chris Daltonbb768422020-03-12 12:13:29 -0600295 baseVertex);
296 } else {
297 pass->bindBuffers(nullptr, helper->fInstBuffer.get(), vertexBuffer);
298 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}) {
305 if (!context->priv().caps()->drawInstancedSupport()) {
306 break;
307 }
308
309 run_test(context, (indexed) ? "drawIndexedIndirect" : "drawIndirect",
310 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);
376 pass->bindBuffers(helper->fIndexBuffer2.get(), helper->fInstBuffer.get(),
377 helper->fVertBuffer.get());
378 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);
388 pass->bindBuffers(nullptr, helper->fInstBuffer.get(),
389 helper->fVertBuffer.get());
390 for (int i = 0; i < 2; ++i) {
391 int start = kBoxCountY * i / 2;
392 int end = kBoxCountY * (i + 1) / 2;
393 size_t offset = helper->fDrawIndirectBufferOffset + start *
394 sizeof(GrDrawIndirectCommand);
395 pass->drawIndirect(helper->fDrawIndirectBuffer.get(), offset,
396 end - start);
397 }
398 }
399 });
400 }
Chris Dalton114a3c02017-05-26 15:17:19 -0600401}
402
403////////////////////////////////////////////////////////////////////////////////////////////////////
404
405class GrMeshTestOp : public GrDrawOp {
406public:
407 DEFINE_OP_CLASS_ID
408
Robert Phillips88a32ef2018-06-07 11:05:56 -0400409 static std::unique_ptr<GrDrawOp> Make(GrContext* context,
Greg Danielf793de12019-09-05 13:23:23 -0400410 std::function<void(DrawMeshHelper*)> prepareFn,
411 std::function<void(DrawMeshHelper*)> executeFn) {
Robert Phillips9da87e02019-02-04 13:26:26 -0500412 GrOpMemoryPool* pool = context->priv().opMemoryPool();
Robert Phillipsc994a932018-06-19 13:09:54 -0400413
Greg Danielf793de12019-09-05 13:23:23 -0400414 return pool->allocate<GrMeshTestOp>(prepareFn, executeFn);
Robert Phillips88a32ef2018-06-07 11:05:56 -0400415 }
416
417private:
Robert Phillips7c525e62018-06-12 10:11:12 -0400418 friend class GrOpMemoryPool; // for ctor
419
Greg Danielf793de12019-09-05 13:23:23 -0400420 GrMeshTestOp(std::function<void(DrawMeshHelper*)> prepareFn,
421 std::function<void(DrawMeshHelper*)> executeFn)
Chris Dalton114a3c02017-05-26 15:17:19 -0600422 : INHERITED(ClassID())
Greg Danielf793de12019-09-05 13:23:23 -0400423 , fPrepareFn(prepareFn)
424 , fExecuteFn(executeFn){
Chris Dalton114a3c02017-05-26 15:17:19 -0600425 this->setBounds(SkRect::MakeIWH(kImageWidth, kImageHeight),
Greg Daniel5faf4742019-10-01 15:14:44 -0400426 HasAABloat::kNo, IsHairline::kNo);
Chris Dalton114a3c02017-05-26 15:17:19 -0600427 }
428
Chris Dalton114a3c02017-05-26 15:17:19 -0600429 const char* name() const override { return "GrMeshTestOp"; }
430 FixedFunctionFlags fixedFunctionFlags() const override { return FixedFunctionFlags::kNone; }
Chris Dalton6ce447a2019-06-23 18:07:38 -0600431 GrProcessorSet::Analysis finalize(const GrCaps&, const GrAppliedClip*,
432 bool hasMixedSampledCoverage, GrClampType) override {
Chris Dalton4b62aed2019-01-15 11:53:00 -0700433 return GrProcessorSet::EmptySetAnalysis();
Brian Salomonf86d37b2017-06-16 10:04:34 -0400434 }
Robert Phillipsc655c3a2020-03-18 13:23:45 -0400435
436 void onPrePrepare(GrRecordingContext*,
Brian Salomon8afde5f2020-04-01 16:22:00 -0400437 const GrSurfaceProxyView* writeView,
Robert Phillipsc655c3a2020-03-18 13:23:45 -0400438 GrAppliedClip*,
439 const GrXferProcessor::DstProxyView&) override {}
Greg Danielf793de12019-09-05 13:23:23 -0400440 void onPrepare(GrOpFlushState* state) override {
441 fHelper.reset(new DrawMeshHelper(state));
442 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
452 typedef GrDrawOp INHERITED;
453};
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
Chris Dalton114a3c02017-05-26 15:17:19 -0600501 typedef GrGeometryProcessor INHERITED;
502};
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(),
Chris Dalton304e14d2020-03-17 14:29:06 -0600580 pipeline, mtp, primitiveType);
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
Robert Phillips88a32ef2018-06-07 11:05:56 -0400586static void run_test(GrContext* context, const char* testName, skiatest::Reporter* reporter,
Brian Salomonbf6b9792019-08-21 09:38:10 -0400587 const std::unique_ptr<GrRenderTargetContext>& rtc, const SkBitmap& gold,
Greg Danielf793de12019-09-05 13:23:23 -0400588 std::function<void(DrawMeshHelper*)> prepareFn,
589 std::function<void(DrawMeshHelper*)> executeFn) {
Chris Dalton114a3c02017-05-26 15:17:19 -0600590 const int w = gold.width(), h = gold.height(), rowBytes = gold.rowBytes();
591 const uint32_t* goldPx = reinterpret_cast<const uint32_t*>(gold.getPixels());
592 if (h != rtc->height() || w != rtc->width()) {
593 ERRORF(reporter, "[%s] expectation and rtc not compatible (?).", testName);
594 return;
595 }
596 if (sizeof(uint32_t) * kImageWidth != gold.rowBytes()) {
Adlai Holler684838f2020-05-12 10:41:04 -0400597 ERRORF(reporter, "[%s] unexpected row bytes in gold image", testName);
Chris Dalton114a3c02017-05-26 15:17:19 -0600598 return;
599 }
600
601 SkAutoSTMalloc<kImageHeight * kImageWidth, uint32_t> resultPx(h * rowBytes);
Michael Ludwig81d41722020-05-26 16:57:38 -0400602 rtc->clear(SkPMColor4f::FromBytes_RGBA(0xbaaaaaad));
Greg Danielf793de12019-09-05 13:23:23 -0400603 rtc->priv().testingOnly_addDrawOp(GrMeshTestOp::Make(context, prepareFn, executeFn));
Chris Daltond4dec972020-04-03 11:01:30 -0600604
Brian Salomon1d435302019-07-01 13:05:28 -0400605 rtc->readPixels(gold.info(), resultPx, rowBytes, {0, 0});
Chris Daltond4dec972020-04-03 11:01:30 -0600606
607#ifdef WRITE_PNG_CONTEXT_TYPE
608#define STRINGIFY(X) #X
609#define TOSTRING(X) STRINGIFY(X)
610 SkString filename;
611 filename.printf("GrMeshTest_%s_%s.png", TOSTRING(WRITE_PNG_CONTEXT_TYPE), testName);
612 SkDebugf("writing %s...\n", filename.c_str());
613 ToolUtils::EncodeImageToFile(filename.c_str(), SkPixmap(gold.info(), resultPx, rowBytes),
614 SkEncodedImageFormat::kPNG, 100);
615#endif
616
Chris Dalton114a3c02017-05-26 15:17:19 -0600617 for (int y = 0; y < h; ++y) {
618 for (int x = 0; x < w; ++x) {
619 uint32_t expected = goldPx[y * kImageWidth + x];
620 uint32_t actual = resultPx[y * kImageWidth + x];
621 if (expected != actual) {
622 ERRORF(reporter, "[%s] pixel (%i,%i): got 0x%x expected 0x%x",
623 testName, x, y, actual, expected);
624 return;
625 }
626 }
627 }
628}