blob: 47e6d48183ced6328847091e41254f1b42c1a590 [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"
Adlai Hollera0693042020-10-14 11:23:11 -040017#include "src/gpu/GrDirectContextPriv.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050018#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/GrResourceProvider.h"
25#include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050026#include "src/gpu/glsl/GrGLSLVarying.h"
27#include "src/gpu/glsl/GrGLSLVertexGeoBuilder.h"
Robert Phillips3968fcb2019-12-05 16:40:31 -050028#include "src/gpu/ops/GrSimpleMeshDrawOpHelper.h"
Robert Phillips4dca8312021-07-28 15:13:20 -040029#include "src/gpu/v1/SurfaceDrawContext_v1.h"
Chris Dalton114a3c02017-05-26 15:17:19 -060030
Chris Daltond4dec972020-04-03 11:01:30 -060031#if 0
32#include "tools/ToolUtils.h"
33#define WRITE_PNG_CONTEXT_TYPE kANGLE_D3D11_ES3_ContextType
34#endif
35
Chris Dalton114a3c02017-05-26 15:17:19 -060036GR_DECLARE_STATIC_UNIQUE_KEY(gIndexBufferKey);
37
38static constexpr int kBoxSize = 2;
39static constexpr int kBoxCountY = 8;
40static constexpr int kBoxCountX = 8;
41static constexpr int kBoxCount = kBoxCountY * kBoxCountX;
42
43static constexpr int kImageWidth = kBoxCountY * kBoxSize;
44static constexpr int kImageHeight = kBoxCountX * kBoxSize;
45
46static constexpr int kIndexPatternRepeatCount = 3;
47constexpr uint16_t kIndexPattern[6] = {0, 1, 2, 1, 2, 3};
48
49
50class DrawMeshHelper {
51public:
52 DrawMeshHelper(GrOpFlushState* state) : fState(state) {}
53
54 sk_sp<const GrBuffer> getIndexBuffer();
55
Chris Daltonbb768422020-03-12 12:13:29 -060056 sk_sp<const GrBuffer> makeIndexBuffer(const uint16_t[], int count);
57
Chris Dalton114a3c02017-05-26 15:17:19 -060058 template<typename T> sk_sp<const GrBuffer> makeVertexBuffer(const SkTArray<T>& data) {
59 return this->makeVertexBuffer(data.begin(), data.count());
60 }
Chris Dalton1d616352017-05-31 12:51:23 -060061 template<typename T> sk_sp<const GrBuffer> makeVertexBuffer(const std::vector<T>& data) {
62 return this->makeVertexBuffer(data.data(), data.size());
63 }
Chris Dalton114a3c02017-05-26 15:17:19 -060064 template<typename T> sk_sp<const GrBuffer> makeVertexBuffer(const T* data, int count);
65
Robert Phillips71143952021-06-17 14:55:07 -040066 GrMeshDrawTarget* target() { return fState; }
Chris Dalton03fdf6a2020-04-07 12:31:59 -060067
Chris Daltonbb768422020-03-12 12:13:29 -060068 sk_sp<const GrBuffer> fIndexBuffer;
69 sk_sp<const GrBuffer> fIndexBuffer2;
70 sk_sp<const GrBuffer> fInstBuffer;
Greg Danielf793de12019-09-05 13:23:23 -040071 sk_sp<const GrBuffer> fVertBuffer;
72 sk_sp<const GrBuffer> fVertBuffer2;
Chris Dalton03fdf6a2020-04-07 12:31:59 -060073 sk_sp<const GrBuffer> fDrawIndirectBuffer;
74 size_t fDrawIndirectBufferOffset;
Greg Danielf793de12019-09-05 13:23:23 -040075
Chris Daltonbb768422020-03-12 12:13:29 -060076 GrOpsRenderPass* bindPipeline(GrPrimitiveType, bool isInstanced, bool hasVertexBuffer);
Chris Dalton114a3c02017-05-26 15:17:19 -060077
78private:
79 GrOpFlushState* fState;
80};
81
82struct Box {
83 float fX, fY;
84 GrColor fColor;
85};
86
87////////////////////////////////////////////////////////////////////////////////////////////////////
88
89/**
Chris Daltoneb694b72020-03-16 09:25:50 -060090 * This is a GPU-backend specific test. It tries to test all possible usecases of
91 * GrOpsRenderPass::draw*. The test works by drawing checkerboards of colored boxes, reading back
92 * the pixels, and comparing with expected results. The boxes are drawn on integer boundaries and
93 * the (opaque) colors are chosen from the set (r,g,b) = (0,255)^3, so the GPU renderings ought to
94 * produce exact matches.
Chris Dalton114a3c02017-05-26 15:17:19 -060095 */
96
Adlai Hollerc95b5892020-08-11 12:02:22 -040097static void run_test(GrDirectContext*, const char* testName, skiatest::Reporter*,
Robert Phillips4dca8312021-07-28 15:13:20 -040098 const std::unique_ptr<skgpu::v1::SurfaceDrawContext>&, const SkBitmap& gold,
Greg Danielf793de12019-09-05 13:23:23 -040099 std::function<void(DrawMeshHelper*)> prepareFn,
100 std::function<void(DrawMeshHelper*)> executeFn);
Chris Dalton114a3c02017-05-26 15:17:19 -0600101
Chris Daltond4dec972020-04-03 11:01:30 -0600102#ifdef WRITE_PNG_CONTEXT_TYPE
103static bool IsContextTypeForOutputPNGs(skiatest::GrContextFactoryContextType type) {
104 return type == skiatest::GrContextFactoryContextType::WRITE_PNG_CONTEXT_TYPE;
105}
106DEF_GPUTEST_FOR_CONTEXTS(GrMeshTest, IsContextTypeForOutputPNGs, reporter, ctxInfo, nullptr) {
107#else
Chris Dalton114a3c02017-05-26 15:17:19 -0600108DEF_GPUTEST_FOR_RENDERING_CONTEXTS(GrMeshTest, reporter, ctxInfo) {
Chris Daltond4dec972020-04-03 11:01:30 -0600109#endif
Robert Phillipseffd13f2020-07-20 15:00:36 -0400110 auto dContext = ctxInfo.directContext();
Chris Dalton114a3c02017-05-26 15:17:19 -0600111
Robert Phillips4dca8312021-07-28 15:13:20 -0400112 auto sdc = skgpu::v1::SurfaceDrawContext::Make(
Robert Phillipseffd13f2020-07-20 15:00:36 -0400113 dContext, GrColorType::kRGBA_8888, nullptr, SkBackingFit::kExact,
Chris Daltonf5b87f92021-04-19 17:27:09 -0600114 {kImageWidth, kImageHeight}, SkSurfaceProps());
Robert Phillips4dca8312021-07-28 15:13:20 -0400115 if (!sdc) {
Chris Dalton114a3c02017-05-26 15:17:19 -0600116 ERRORF(reporter, "could not create render target context.");
117 return;
118 }
119
120 SkTArray<Box> boxes;
121 SkTArray<std::array<Box, 4>> vertexData;
122 SkBitmap gold;
123
124 // ---- setup ----------
125
126 SkPaint paint;
127 paint.setBlendMode(SkBlendMode::kSrc);
128 gold.allocN32Pixels(kImageWidth, kImageHeight);
129
130 SkCanvas goldCanvas(gold);
131
132 for (int y = 0; y < kBoxCountY; ++y) {
133 for (int x = 0; x < kBoxCountX; ++x) {
134 int c = y + x;
135 int rgb[3] = {-(c & 1) & 0xff, -((c >> 1) & 1) & 0xff, -((c >> 2) & 1) & 0xff};
136
137 const Box box = boxes.push_back() = {
Brian Salomon23356442018-11-30 15:33:19 -0500138 float(x * kBoxSize),
139 float(y * kBoxSize),
140 GrColorPackRGBA(rgb[0], rgb[1], rgb[2], 255)
Chris Dalton114a3c02017-05-26 15:17:19 -0600141 };
142
143 std::array<Box, 4>& boxVertices = vertexData.push_back();
144 for (int i = 0; i < 4; ++i) {
145 boxVertices[i] = {
Brian Salomon23356442018-11-30 15:33:19 -0500146 box.fX + (i / 2) * kBoxSize,
147 box.fY + (i % 2) * kBoxSize,
148 box.fColor
Chris Dalton114a3c02017-05-26 15:17:19 -0600149 };
150 }
151
152 paint.setARGB(255, rgb[0], rgb[1], rgb[2]);
153 goldCanvas.drawRect(SkRect::MakeXYWH(box.fX, box.fY, kBoxSize, kBoxSize), paint);
154 }
155 }
156
Chris Dalton114a3c02017-05-26 15:17:19 -0600157 // ---- tests ----------
158
Brian Salomon23356442018-11-30 15:33:19 -0500159#define VALIDATE(buff) \
160 do { \
161 if (!buff) { \
162 ERRORF(reporter, #buff " is null."); \
163 return; \
164 } \
165 } while (0)
Chris Dalton114a3c02017-05-26 15:17:19 -0600166
Robert Phillips4dca8312021-07-28 15:13:20 -0400167 run_test(dContext, "draw", reporter, sdc, gold,
Robert Phillips88a32ef2018-06-07 11:05:56 -0400168 [&](DrawMeshHelper* helper) {
169 SkTArray<Box> expandedVertexData;
170 for (int i = 0; i < kBoxCount; ++i) {
171 for (int j = 0; j < 6; ++j) {
172 expandedVertexData.push_back(vertexData[i][kIndexPattern[j]]);
173 }
174 }
Chris Dalton114a3c02017-05-26 15:17:19 -0600175
Robert Phillips88a32ef2018-06-07 11:05:56 -0400176 // Draw boxes one line at a time to exercise base vertex.
Greg Danielf793de12019-09-05 13:23:23 -0400177 helper->fVertBuffer = helper->makeVertexBuffer(expandedVertexData);
178 VALIDATE(helper->fVertBuffer);
179 },
180 [&](DrawMeshHelper* helper) {
Robert Phillips88a32ef2018-06-07 11:05:56 -0400181 for (int y = 0; y < kBoxCountY; ++y) {
Chris Daltonbb768422020-03-12 12:13:29 -0600182 auto pass = helper->bindPipeline(GrPrimitiveType::kTriangles, false, true);
Greg Daniel426274b2020-07-20 11:37:38 -0400183 pass->bindBuffers(nullptr, nullptr, helper->fVertBuffer);
Chris Daltonbb768422020-03-12 12:13:29 -0600184 pass->draw(kBoxCountX * 6, y * kBoxCountX * 6);
Robert Phillips88a32ef2018-06-07 11:05:56 -0400185 }
186 });
Chris Dalton114a3c02017-05-26 15:17:19 -0600187
Robert Phillips4dca8312021-07-28 15:13:20 -0400188 run_test(dContext, "drawIndexed", reporter, sdc, gold,
Greg Danielf793de12019-09-05 13:23:23 -0400189 [&](DrawMeshHelper* helper) {
190 helper->fIndexBuffer = helper->getIndexBuffer();
191 VALIDATE(helper->fIndexBuffer);
192 helper->fVertBuffer = helper->makeVertexBuffer(vertexData);
193 VALIDATE(helper->fVertBuffer);
194 },
195 [&](DrawMeshHelper* helper) {
196 int baseRepetition = 0;
197 int i = 0;
198 // Start at various repetitions within the patterned index buffer to exercise base
199 // index.
200 while (i < kBoxCount) {
Brian Salomon4dea72a2019-12-18 10:43:10 -0500201 static_assert(kIndexPatternRepeatCount >= 3);
Brian Osman788b9162020-02-07 10:36:46 -0500202 int repetitionCount = std::min(3 - baseRepetition, kBoxCount - i);
Chris Dalton114a3c02017-05-26 15:17:19 -0600203
Chris Daltonbb768422020-03-12 12:13:29 -0600204 auto pass = helper->bindPipeline(GrPrimitiveType::kTriangles, false, true);
Greg Daniel426274b2020-07-20 11:37:38 -0400205 pass->bindBuffers(helper->fIndexBuffer, nullptr, helper->fVertBuffer);
Chris Daltonbb768422020-03-12 12:13:29 -0600206 pass->drawIndexed(repetitionCount * 6, baseRepetition * 6, baseRepetition * 4,
207 (baseRepetition + repetitionCount) * 4 - 1,
208 (i - baseRepetition) * 4);
Chris Dalton114a3c02017-05-26 15:17:19 -0600209
Greg Danielf793de12019-09-05 13:23:23 -0400210 baseRepetition = (baseRepetition + 1) % 3;
211 i += repetitionCount;
212 }
213 });
Chris Dalton114a3c02017-05-26 15:17:19 -0600214
Robert Phillips4dca8312021-07-28 15:13:20 -0400215 run_test(dContext, "drawIndexPattern", reporter, sdc, gold,
Greg Danielf793de12019-09-05 13:23:23 -0400216 [&](DrawMeshHelper* helper) {
217 helper->fIndexBuffer = helper->getIndexBuffer();
218 VALIDATE(helper->fIndexBuffer);
219 helper->fVertBuffer = helper->makeVertexBuffer(vertexData);
220 VALIDATE(helper->fVertBuffer);
221 },
222 [&](DrawMeshHelper* helper) {
Chris Daltonbb768422020-03-12 12:13:29 -0600223 // Draw boxes one line at a time to exercise base vertex. drawIndexPattern does
Greg Danielf793de12019-09-05 13:23:23 -0400224 // not support a base index.
225 for (int y = 0; y < kBoxCountY; ++y) {
Chris Daltonbb768422020-03-12 12:13:29 -0600226 auto pass = helper->bindPipeline(GrPrimitiveType::kTriangles, false, true);
Greg Daniel426274b2020-07-20 11:37:38 -0400227 pass->bindBuffers(helper->fIndexBuffer, nullptr, helper->fVertBuffer);
Chris Daltonbb768422020-03-12 12:13:29 -0600228 pass->drawIndexPattern(6, kBoxCountX, kIndexPatternRepeatCount, 4,
229 y * kBoxCountX * 4);
230
Greg Danielf793de12019-09-05 13:23:23 -0400231 }
232 });
Chris Dalton1d616352017-05-31 12:51:23 -0600233
234 for (bool indexed : {false, true}) {
Robert Phillipseffd13f2020-07-20 15:00:36 -0400235 if (!dContext->priv().caps()->drawInstancedSupport()) {
Chris Dalton1d616352017-05-31 12:51:23 -0600236 break;
237 }
238
Robert Phillipseffd13f2020-07-20 15:00:36 -0400239 run_test(dContext, indexed ? "drawIndexedInstanced" : "drawInstanced",
Robert Phillips4dca8312021-07-28 15:13:20 -0400240 reporter, sdc, gold,
Greg Danielf793de12019-09-05 13:23:23 -0400241 [&](DrawMeshHelper* helper) {
242 helper->fIndexBuffer = indexed ? helper->getIndexBuffer() : nullptr;
Chris Daltonbb768422020-03-12 12:13:29 -0600243 SkTArray<uint16_t> baseIndexData;
244 baseIndexData.push_back(kBoxCountX/2 * 6); // for testing base index.
Chris Dalton03fdf6a2020-04-07 12:31:59 -0600245 for (int i = 0; i < 6; ++i) {
246 baseIndexData.push_back(kIndexPattern[i]);
Chris Daltonbb768422020-03-12 12:13:29 -0600247 }
248 helper->fIndexBuffer2 = helper->makeIndexBuffer(baseIndexData.begin(),
249 baseIndexData.count());
Greg Danielf793de12019-09-05 13:23:23 -0400250 helper->fInstBuffer = helper->makeVertexBuffer(boxes);
251 VALIDATE(helper->fInstBuffer);
252 helper->fVertBuffer =
253 helper->makeVertexBuffer(std::vector<float>{0,0, 0,1, 1,0, 1,1});
254 VALIDATE(helper->fVertBuffer);
255 helper->fVertBuffer2 = helper->makeVertexBuffer( // for testing base vertex.
256 std::vector<float>{-1,-1, -1,-1, 0,0, 0,1, 1,0, 1,1});
257 VALIDATE(helper->fVertBuffer2);
258 },
259 [&](DrawMeshHelper* helper) {
260 // Draw boxes one line at a time to exercise base instance, base vertex, and
Chris Daltonbb768422020-03-12 12:13:29 -0600261 // null vertex buffer.
Greg Danielf793de12019-09-05 13:23:23 -0400262 for (int y = 0; y < kBoxCountY; ++y) {
Greg Daniel426274b2020-07-20 11:37:38 -0400263 sk_sp<const GrBuffer> vertexBuffer;
Chris Daltonbb768422020-03-12 12:13:29 -0600264 int baseVertex = 0;
Greg Danielf793de12019-09-05 13:23:23 -0400265 switch (y % 3) {
266 case 0:
Robert Phillipseffd13f2020-07-20 15:00:36 -0400267 if (dContext->priv().caps()->shaderCaps()->vertexIDSupport()) {
Greg Danielf793de12019-09-05 13:23:23 -0400268 break;
269 }
John Stiles30212b72020-06-11 17:55:07 -0400270 [[fallthrough]];
Greg Danielf793de12019-09-05 13:23:23 -0400271 case 1:
Greg Daniel426274b2020-07-20 11:37:38 -0400272 vertexBuffer = helper->fVertBuffer;
Greg Danielf793de12019-09-05 13:23:23 -0400273 break;
274 case 2:
Greg Daniel426274b2020-07-20 11:37:38 -0400275 vertexBuffer = helper->fVertBuffer2;
Chris Daltonbb768422020-03-12 12:13:29 -0600276 baseVertex = 2;
Greg Danielf793de12019-09-05 13:23:23 -0400277 break;
278 }
Chris Daltonbb768422020-03-12 12:13:29 -0600279
280 GrPrimitiveType primitiveType = indexed ? GrPrimitiveType::kTriangles
281 : GrPrimitiveType::kTriangleStrip;
282 auto pass = helper->bindPipeline(primitiveType, true,
283 SkToBool(vertexBuffer));
284 if (indexed) {
Greg Daniel426274b2020-07-20 11:37:38 -0400285 sk_sp<const GrBuffer> indexBuffer = (y % 2) ?
286 helper->fIndexBuffer2 : helper->fIndexBuffer;
Chris Daltonbb768422020-03-12 12:13:29 -0600287 VALIDATE(indexBuffer);
288 int baseIndex = (y % 2);
Greg Daniel426274b2020-07-20 11:37:38 -0400289 pass->bindBuffers(std::move(indexBuffer), helper->fInstBuffer,
290 std::move(vertexBuffer));
Chris Dalton03fdf6a2020-04-07 12:31:59 -0600291 pass->drawIndexedInstanced(6, baseIndex, kBoxCountX, y * kBoxCountX,
Chris Daltonbb768422020-03-12 12:13:29 -0600292 baseVertex);
293 } else {
Greg Daniel426274b2020-07-20 11:37:38 -0400294 pass->bindBuffers(nullptr, helper->fInstBuffer,
295 std::move(vertexBuffer));
Chris Daltonbb768422020-03-12 12:13:29 -0600296 pass->drawInstanced(kBoxCountX, y * kBoxCountY, 4, baseVertex);
297 }
Greg Danielf793de12019-09-05 13:23:23 -0400298 }
299 });
Chris Dalton1d616352017-05-31 12:51:23 -0600300 }
Chris Dalton03fdf6a2020-04-07 12:31:59 -0600301
302 for (bool indexed : {false, true}) {
Robert Phillipseffd13f2020-07-20 15:00:36 -0400303 if (!dContext->priv().caps()->drawInstancedSupport()) {
Chris Dalton03fdf6a2020-04-07 12:31:59 -0600304 break;
305 }
306
Robert Phillipseffd13f2020-07-20 15:00:36 -0400307 run_test(dContext, (indexed) ? "drawIndexedIndirect" : "drawIndirect",
Robert Phillips4dca8312021-07-28 15:13:20 -0400308 reporter, sdc, gold,
Chris Dalton03fdf6a2020-04-07 12:31:59 -0600309 [&](DrawMeshHelper* helper) {
310 SkTArray<uint16_t> baseIndexData;
311 baseIndexData.push_back(kBoxCountX/2 * 6); // for testing base index.
312 for (int j = 0; j < kBoxCountY; ++j) {
313 for (int i = 0; i < 6; ++i) {
314 baseIndexData.push_back(kIndexPattern[i]);
315 }
316 }
317 helper->fIndexBuffer2 = helper->makeIndexBuffer(baseIndexData.begin(),
318 baseIndexData.count());
319 VALIDATE(helper->fIndexBuffer2);
320 helper->fInstBuffer = helper->makeVertexBuffer(boxes);
321 VALIDATE(helper->fInstBuffer);
322 helper->fVertBuffer = helper->makeVertexBuffer(std::vector<float>{
323 -1,-1, 0,0, 0,1, 1,0, 1,1, -1,-1, 0,0, 1,0, 0,1, 1,1});
324 VALIDATE(helper->fVertBuffer);
325
Chris Daltona6a3d052021-02-07 20:56:36 -0700326 GrDrawIndirectWriter indirectWriter;
327 GrDrawIndexedIndirectWriter indexedIndirectWriter;
Chris Dalton03fdf6a2020-04-07 12:31:59 -0600328 if (indexed) {
329 // Make helper->fDrawIndirectBufferOffset nonzero.
Kevin Lubickbe03ef12021-06-16 15:28:00 -0400330 sk_sp<const GrBuffer> ignoredBuff;
331 size_t ignoredOffset;
Chris Daltonb8d7e002020-05-13 11:02:10 -0600332 // Make a superfluous call to makeDrawIndirectSpace in order to test
333 // "offsetInBytes!=0" for the actual call to makeDrawIndexedIndirectSpace.
Kevin Lubickbe03ef12021-06-16 15:28:00 -0400334 helper->target()->makeDrawIndirectSpace(29, &ignoredBuff, &ignoredOffset);
Chris Daltona6a3d052021-02-07 20:56:36 -0700335 indexedIndirectWriter = helper->target()->makeDrawIndexedIndirectSpace(
Chris Dalton03fdf6a2020-04-07 12:31:59 -0600336 kBoxCountY, &helper->fDrawIndirectBuffer,
337 &helper->fDrawIndirectBufferOffset);
338 } else {
339 // Make helper->fDrawIndirectBufferOffset nonzero.
Kevin Lubickbe03ef12021-06-16 15:28:00 -0400340 sk_sp<const GrBuffer> ignoredBuff;
341 size_t ignoredOffset;
Chris Daltonb8d7e002020-05-13 11:02:10 -0600342 // Make a superfluous call to makeDrawIndexedIndirectSpace in order to test
343 // "offsetInBytes!=0" for the actual call to makeDrawIndirectSpace.
Kevin Lubickbe03ef12021-06-16 15:28:00 -0400344 helper->target()->makeDrawIndexedIndirectSpace(7, &ignoredBuff,
345 &ignoredOffset);
Chris Daltona6a3d052021-02-07 20:56:36 -0700346 indirectWriter = helper->target()->makeDrawIndirectSpace(
Chris Dalton03fdf6a2020-04-07 12:31:59 -0600347 kBoxCountY, &helper->fDrawIndirectBuffer,
348 &helper->fDrawIndirectBufferOffset);
349 }
350
351 // Draw boxes one line at a time to exercise multiple draws.
352 for (int y = 0; y < kBoxCountY; ++y) {
353 int baseVertex = (y % 2) ? 1 : 6;
354 if (indexed) {
355 int baseIndex = 1 + y * 6;
Chris Daltona6a3d052021-02-07 20:56:36 -0700356 indexedIndirectWriter.writeIndexed(6, baseIndex, kBoxCountX,
Chris Daltonb849f7a2021-02-10 12:55:48 -0700357 y * kBoxCountX, baseVertex);
Chris Dalton03fdf6a2020-04-07 12:31:59 -0600358 } else {
Chris Daltonb849f7a2021-02-10 12:55:48 -0700359 indirectWriter.write(kBoxCountX, y * kBoxCountX, 4, baseVertex);
Chris Dalton03fdf6a2020-04-07 12:31:59 -0600360 }
361 }
362 },
363 [&](DrawMeshHelper* helper) {
364 GrOpsRenderPass* pass;
365 if (indexed) {
366 pass = helper->bindPipeline(GrPrimitiveType::kTriangles, true, true);
Greg Daniel426274b2020-07-20 11:37:38 -0400367 pass->bindBuffers(helper->fIndexBuffer2, helper->fInstBuffer,
368 helper->fVertBuffer);
Chris Dalton03fdf6a2020-04-07 12:31:59 -0600369 for (int i = 0; i < 3; ++i) {
370 int start = kBoxCountY * i / 3;
371 int end = kBoxCountY * (i + 1) / 3;
372 size_t offset = helper->fDrawIndirectBufferOffset + start *
373 sizeof(GrDrawIndexedIndirectCommand);
374 pass->drawIndexedIndirect(helper->fDrawIndirectBuffer.get(), offset,
375 end - start);
376 }
377 } else {
378 pass = helper->bindPipeline(GrPrimitiveType::kTriangleStrip, true, true);
Greg Daniel426274b2020-07-20 11:37:38 -0400379 pass->bindBuffers(nullptr, helper->fInstBuffer, helper->fVertBuffer);
Chris Dalton03fdf6a2020-04-07 12:31:59 -0600380 for (int i = 0; i < 2; ++i) {
381 int start = kBoxCountY * i / 2;
382 int end = kBoxCountY * (i + 1) / 2;
383 size_t offset = helper->fDrawIndirectBufferOffset + start *
384 sizeof(GrDrawIndirectCommand);
385 pass->drawIndirect(helper->fDrawIndirectBuffer.get(), offset,
386 end - start);
387 }
388 }
389 });
390 }
Chris Dalton114a3c02017-05-26 15:17:19 -0600391}
392
393////////////////////////////////////////////////////////////////////////////////////////////////////
394
Brian Salomonbab2d112021-08-11 09:59:56 -0400395namespace {
396class MeshTestOp : public GrDrawOp {
Chris Dalton114a3c02017-05-26 15:17:19 -0600397public:
398 DEFINE_OP_CLASS_ID
399
Herb Derbyc76d4092020-10-07 16:46:15 -0400400 static GrOp::Owner Make(GrRecordingContext* rContext,
401 std::function<void(DrawMeshHelper*)> prepareFn,
402 std::function<void(DrawMeshHelper*)> executeFn) {
Brian Salomonbab2d112021-08-11 09:59:56 -0400403 return GrOp::Make<MeshTestOp>(rContext, prepareFn, executeFn);
Robert Phillips88a32ef2018-06-07 11:05:56 -0400404 }
405
406private:
Brian Salomonbab2d112021-08-11 09:59:56 -0400407 friend class GrOp; // for ctor
Robert Phillips7c525e62018-06-12 10:11:12 -0400408
Brian Salomonbab2d112021-08-11 09:59:56 -0400409 MeshTestOp(std::function<void(DrawMeshHelper*)> prepareFn,
410 std::function<void(DrawMeshHelper*)> executeFn)
411 : INHERITED(ClassID()), fPrepareFn(prepareFn), fExecuteFn(executeFn) {
412 this->setBounds(
413 SkRect::MakeIWH(kImageWidth, kImageHeight), HasAABloat::kNo, IsHairline::kNo);
Chris Dalton114a3c02017-05-26 15:17:19 -0600414 }
415
Chris Dalton114a3c02017-05-26 15:17:19 -0600416 const char* name() const override { return "GrMeshTestOp"; }
417 FixedFunctionFlags fixedFunctionFlags() const override { return FixedFunctionFlags::kNone; }
Chris Dalton57ab06c2021-04-22 12:57:28 -0600418 GrProcessorSet::Analysis finalize(const GrCaps&, const GrAppliedClip*, GrClampType) override {
Chris Dalton4b62aed2019-01-15 11:53:00 -0700419 return GrProcessorSet::EmptySetAnalysis();
Brian Salomonf86d37b2017-06-16 10:04:34 -0400420 }
Robert Phillipsc655c3a2020-03-18 13:23:45 -0400421
422 void onPrePrepare(GrRecordingContext*,
Adlai Hollere2296f72020-11-19 13:41:26 -0500423 const GrSurfaceProxyView& writeView,
Robert Phillipsc655c3a2020-03-18 13:23:45 -0400424 GrAppliedClip*,
John Stiles52cb1d02021-06-02 11:58:05 -0400425 const GrDstProxyView&,
Greg Daniel42dbca52020-11-20 10:22:43 -0500426 GrXferBarrierFlags renderPassXferBarriers,
427 GrLoadOp colorLoadOp) override {}
Greg Danielf793de12019-09-05 13:23:23 -0400428 void onPrepare(GrOpFlushState* state) override {
John Stilesfbd050b2020-08-03 13:21:46 -0400429 fHelper = std::make_unique<DrawMeshHelper>(state);
Greg Danielf793de12019-09-05 13:23:23 -0400430 fPrepareFn(fHelper.get());
431 }
Brian Salomon588cec72018-11-14 13:56:37 -0500432 void onExecute(GrOpFlushState* state, const SkRect& chainBounds) override {
Greg Danielf793de12019-09-05 13:23:23 -0400433 fExecuteFn(fHelper.get());
Chris Dalton114a3c02017-05-26 15:17:19 -0600434 }
435
Greg Danielf793de12019-09-05 13:23:23 -0400436 std::unique_ptr<DrawMeshHelper> fHelper;
437 std::function<void(DrawMeshHelper*)> fPrepareFn;
438 std::function<void(DrawMeshHelper*)> fExecuteFn;
Chris Dalton114a3c02017-05-26 15:17:19 -0600439
John Stiles7571f9e2020-09-02 22:42:33 -0400440 using INHERITED = GrDrawOp;
Chris Dalton114a3c02017-05-26 15:17:19 -0600441};
442
Brian Salomonbab2d112021-08-11 09:59:56 -0400443class MeshTestProcessor : public GrGeometryProcessor {
Chris Dalton114a3c02017-05-26 15:17:19 -0600444public:
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500445 static GrGeometryProcessor* Make(SkArenaAlloc* arena, bool instanced, bool hasVertexBuffer) {
Mike Kleinf1241082020-12-14 15:59:09 -0600446 return arena->make([&](void* ptr) {
Brian Salomonbab2d112021-08-11 09:59:56 -0400447 return new (ptr) MeshTestProcessor(instanced, hasVertexBuffer);
Mike Kleinf1241082020-12-14 15:59:09 -0600448 });
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500449 }
450
451 const char* name() const override { return "GrMeshTestProcessor"; }
452
Brian Salomon13b28732021-08-06 15:33:58 -0400453 void addToKey(const GrShaderCaps&, GrProcessorKeyBuilder* b) const final {
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500454 b->add32(fInstanceLocation.isInitialized());
455 b->add32(fVertexPosition.isInitialized());
456 }
457
Brian Salomonf95940b2021-08-09 15:56:24 -0400458 std::unique_ptr<ProgramImpl> makeProgramImpl(const GrShaderCaps&) const final;
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500459
460private:
Brian Salomonbab2d112021-08-11 09:59:56 -0400461 class Impl;
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500462
Brian Salomonbab2d112021-08-11 09:59:56 -0400463 const Attribute& inColor() const {
464 return fVertexColor.isInitialized() ? fVertexColor : fInstanceColor;
465 }
466
467 MeshTestProcessor(bool instanced, bool hasVertexBuffer)
Brian Salomon92be2f72018-06-19 14:33:47 -0400468 : INHERITED(kGrMeshTestProcessor_ClassID) {
Chris Dalton1d616352017-05-31 12:51:23 -0600469 if (instanced) {
Brian Osmand4c29702018-09-14 16:16:55 -0400470 fInstanceLocation = {"location", kFloat2_GrVertexAttribType, kHalf2_GrSLType};
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500471 fInstanceColor = {"color", kUByte4_norm_GrVertexAttribType, kHalf4_GrSLType};
472 this->setInstanceAttributes(&fInstanceLocation, 2);
Chris Dalton1d616352017-05-31 12:51:23 -0600473 if (hasVertexBuffer) {
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500474 fVertexPosition = {"vertex", kFloat2_GrVertexAttribType, kHalf2_GrSLType};
475 this->setVertexAttributes(&fVertexPosition, 1);
Chris Dalton1d616352017-05-31 12:51:23 -0600476 }
Chris Dalton1d616352017-05-31 12:51:23 -0600477 } else {
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500478 fVertexPosition = {"vertex", kFloat2_GrVertexAttribType, kHalf2_GrSLType};
479 fVertexColor = {"color", kUByte4_norm_GrVertexAttribType, kHalf4_GrSLType};
480 this->setVertexAttributes(&fVertexPosition, 2);
Chris Dalton1d616352017-05-31 12:51:23 -0600481 }
Chris Dalton114a3c02017-05-26 15:17:19 -0600482 }
483
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500484 Attribute fVertexPosition;
485 Attribute fVertexColor;
Brian Salomon92be2f72018-06-19 14:33:47 -0400486
487 Attribute fInstanceLocation;
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500488 Attribute fInstanceColor;
Chris Dalton114a3c02017-05-26 15:17:19 -0600489
John Stiles7571f9e2020-09-02 22:42:33 -0400490 using INHERITED = GrGeometryProcessor;
Chris Dalton114a3c02017-05-26 15:17:19 -0600491};
Brian Salomonbab2d112021-08-11 09:59:56 -0400492} // anonymous namespace
Chris Dalton114a3c02017-05-26 15:17:19 -0600493
Brian Salomonbab2d112021-08-11 09:59:56 -0400494std::unique_ptr<GrGeometryProcessor::ProgramImpl> MeshTestProcessor::makeProgramImpl(
Brian Salomonf95940b2021-08-09 15:56:24 -0400495 const GrShaderCaps&) const {
Brian Salomonbab2d112021-08-11 09:59:56 -0400496 class Impl : public ProgramImpl {
497 public:
498 void setData(const GrGLSLProgramDataManager&,
499 const GrShaderCaps&,
500 const GrGeometryProcessor&) final {}
501
502 private:
503 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) final {
504 const MeshTestProcessor& mp = args.fGeomProc.cast<MeshTestProcessor>();
505 GrGLSLVertexBuilder* v = args.fVertBuilder;
506 GrGLSLFPFragmentBuilder* f = args.fFragBuilder;
507
508 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
509 varyingHandler->emitAttributes(mp);
510 f->codeAppendf("half4 %s;", args.fOutputColor);
Brian Salomon48959462021-08-11 13:01:06 -0400511 varyingHandler->addPassThroughAttribute(mp.inColor().asShaderVar(), args.fOutputColor);
Brian Salomonbab2d112021-08-11 09:59:56 -0400512
513 if (!mp.fInstanceLocation.isInitialized()) {
514 v->codeAppendf("float2 vertex = %s;", mp.fVertexPosition.name());
515 } else {
516 if (mp.fVertexPosition.isInitialized()) {
517 v->codeAppendf("float2 offset = %s;", mp.fVertexPosition.name());
518 } else {
519 v->codeAppend("float2 offset = float2(sk_VertexID / 2, sk_VertexID % 2);");
520 }
521 v->codeAppendf("float2 vertex = %s + offset * %i;", mp.fInstanceLocation.name(),
522 kBoxSize);
523 }
524 gpArgs->fPositionVar.set(kFloat2_GrSLType, "vertex");
525
526 f->codeAppendf("const half4 %s = half4(1);", args.fOutputCoverage);
527 }
528 };
529
530 return std::make_unique<Impl>();
Chris Dalton114a3c02017-05-26 15:17:19 -0600531}
532
533////////////////////////////////////////////////////////////////////////////////////////////////////
534
Chris Daltonbb768422020-03-12 12:13:29 -0600535sk_sp<const GrBuffer> DrawMeshHelper::makeIndexBuffer(const uint16_t indices[], int count) {
536 return sk_sp<const GrBuffer>(fState->resourceProvider()->createBuffer(
537 count * sizeof(uint16_t), GrGpuBufferType::kIndex, kDynamic_GrAccessPattern, indices));
538}
539
Chris Dalton114a3c02017-05-26 15:17:19 -0600540template<typename T>
541sk_sp<const GrBuffer> DrawMeshHelper::makeVertexBuffer(const T* data, int count) {
Brian Salomonae64c192019-02-05 09:41:37 -0500542 return sk_sp<const GrBuffer>(fState->resourceProvider()->createBuffer(
Brian Salomondbf70722019-02-07 11:31:24 -0500543 count * sizeof(T), GrGpuBufferType::kVertex, kDynamic_GrAccessPattern, data));
Chris Dalton114a3c02017-05-26 15:17:19 -0600544}
545
546sk_sp<const GrBuffer> DrawMeshHelper::getIndexBuffer() {
547 GR_DEFINE_STATIC_UNIQUE_KEY(gIndexBufferKey);
Brian Salomond28a79d2017-10-16 13:01:07 -0400548 return fState->resourceProvider()->findOrCreatePatternedIndexBuffer(
549 kIndexPattern, 6, kIndexPatternRepeatCount, 4, gIndexBufferKey);
Chris Dalton114a3c02017-05-26 15:17:19 -0600550}
551
Chris Daltonbb768422020-03-12 12:13:29 -0600552GrOpsRenderPass* DrawMeshHelper::bindPipeline(GrPrimitiveType primitiveType, bool isInstanced,
553 bool hasVertexBuffer) {
Robert Phillips3968fcb2019-12-05 16:40:31 -0500554 GrProcessorSet processorSet(SkBlendMode::kSrc);
555
556 // TODO: add a GrProcessorSet testing helper to make this easier
557 SkPMColor4f overrideColor;
558 processorSet.finalize(GrProcessorAnalysisColor(),
559 GrProcessorAnalysisCoverage::kNone,
560 fState->appliedClip(),
561 nullptr,
Robert Phillips3968fcb2019-12-05 16:40:31 -0500562 fState->caps(),
563 GrClampType::kAuto,
564 &overrideColor);
565
566 auto pipeline = GrSimpleMeshDrawOpHelper::CreatePipeline(fState,
567 std::move(processorSet),
568 GrPipeline::InputFlags::kNone);
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500569
Brian Salomonbab2d112021-08-11 09:59:56 -0400570 GrGeometryProcessor* mtp = MeshTestProcessor::Make(fState->allocator(), isInstanced,
571 hasVertexBuffer);
Robert Phillips901aff02019-10-08 12:32:56 -0400572
Chris Dalton2a26c502021-08-26 10:05:11 -0600573 GrProgramInfo programInfo(fState->caps(), fState->writeView(), fState->usesMSAASurface(),
574 pipeline, &GrUserStencilSettings::kUnused, mtp, primitiveType, 0,
575 fState->renderPassBarriers(), fState->colorLoadOp());
Robert Phillips901aff02019-10-08 12:32:56 -0400576
Chris Dalton4386ad12020-02-19 16:42:06 -0700577 fState->opsRenderPass()->bindPipeline(programInfo, SkRect::MakeIWH(kImageWidth, kImageHeight));
Chris Daltonbb768422020-03-12 12:13:29 -0600578 return fState->opsRenderPass();
Chris Dalton114a3c02017-05-26 15:17:19 -0600579}
580
Robert Phillips4dca8312021-07-28 15:13:20 -0400581static void run_test(GrDirectContext* dContext,
582 const char* testName,
Robert Phillipseffd13f2020-07-20 15:00:36 -0400583 skiatest::Reporter* reporter,
Robert Phillips4dca8312021-07-28 15:13:20 -0400584 const std::unique_ptr<skgpu::v1::SurfaceDrawContext>& sdc,
585 const SkBitmap& gold,
Greg Danielf793de12019-09-05 13:23:23 -0400586 std::function<void(DrawMeshHelper*)> prepareFn,
587 std::function<void(DrawMeshHelper*)> executeFn) {
Brian Salomondd4087d2020-12-23 20:36:44 -0500588 const int w = gold.width(), h = gold.height();
Chris Dalton114a3c02017-05-26 15:17:19 -0600589 const uint32_t* goldPx = reinterpret_cast<const uint32_t*>(gold.getPixels());
Robert Phillips4dca8312021-07-28 15:13:20 -0400590 if (h != sdc->height() || w != sdc->width()) {
Chris Dalton114a3c02017-05-26 15:17:19 -0600591 ERRORF(reporter, "[%s] expectation and rtc not compatible (?).", testName);
592 return;
593 }
594 if (sizeof(uint32_t) * kImageWidth != gold.rowBytes()) {
Adlai Holler684838f2020-05-12 10:41:04 -0400595 ERRORF(reporter, "[%s] unexpected row bytes in gold image", testName);
Chris Dalton114a3c02017-05-26 15:17:19 -0600596 return;
597 }
598
Brian Salomonbe1084b2021-01-26 13:29:30 -0500599 GrPixmap resultPM = GrPixmap::Allocate(gold.info());
Robert Phillips4dca8312021-07-28 15:13:20 -0400600 sdc->clear(SkPMColor4f::FromBytes_RGBA(0xbaaaaaad));
Brian Salomonbab2d112021-08-11 09:59:56 -0400601 sdc->addDrawOp(MeshTestOp::Make(dContext, prepareFn, executeFn));
Chris Daltond4dec972020-04-03 11:01:30 -0600602
Robert Phillips4dca8312021-07-28 15:13:20 -0400603 sdc->readPixels(dContext, resultPM, {0, 0});
Chris Daltond4dec972020-04-03 11:01:30 -0600604
605#ifdef WRITE_PNG_CONTEXT_TYPE
606#define STRINGIFY(X) #X
607#define TOSTRING(X) STRINGIFY(X)
608 SkString filename;
609 filename.printf("GrMeshTest_%s_%s.png", TOSTRING(WRITE_PNG_CONTEXT_TYPE), testName);
610 SkDebugf("writing %s...\n", filename.c_str());
Brian Salomondd4087d2020-12-23 20:36:44 -0500611 ToolUtils::EncodeImageToFile(filename.c_str(), resultPM, SkEncodedImageFormat::kPNG, 100);
Chris Daltond4dec972020-04-03 11:01:30 -0600612#endif
613
Chris Dalton114a3c02017-05-26 15:17:19 -0600614 for (int y = 0; y < h; ++y) {
615 for (int x = 0; x < w; ++x) {
616 uint32_t expected = goldPx[y * kImageWidth + x];
Brian Salomondd4087d2020-12-23 20:36:44 -0500617 uint32_t actual = static_cast<uint32_t*>(resultPM.addr())[y * kImageWidth + x];
Chris Dalton114a3c02017-05-26 15:17:19 -0600618 if (expected != actual) {
619 ERRORF(reporter, "[%s] pixel (%i,%i): got 0x%x expected 0x%x",
620 testName, x, y, actual, expected);
621 return;
622 }
623 }
624 }
625}