blob: e8e3aeb36fb67bc0e33dccb18089d9899cf0d6a4 [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
8#include "SkTypes.h"
9#include "Test.h"
10
11#if SK_SUPPORT_GPU
12
13#include "GrContext.h"
14#include "GrGeometryProcessor.h"
15#include "GrOpFlushState.h"
16#include "GrRenderTargetContext.h"
17#include "GrRenderTargetContextPriv.h"
18#include "GrResourceProvider.h"
19#include "GrResourceKey.h"
20#include "SkMakeUnique.h"
21#include "glsl/GrGLSLVertexShaderBuilder.h"
22#include "glsl/GrGLSLFragmentShaderBuilder.h"
23#include "glsl/GrGLSLGeometryProcessor.h"
24#include "glsl/GrGLSLVarying.h"
25#include <array>
26
27
28GR_DECLARE_STATIC_UNIQUE_KEY(gIndexBufferKey);
29
30static constexpr int kBoxSize = 2;
31static constexpr int kBoxCountY = 8;
32static constexpr int kBoxCountX = 8;
33static constexpr int kBoxCount = kBoxCountY * kBoxCountX;
34
35static constexpr int kImageWidth = kBoxCountY * kBoxSize;
36static constexpr int kImageHeight = kBoxCountX * kBoxSize;
37
38static constexpr int kIndexPatternRepeatCount = 3;
39constexpr uint16_t kIndexPattern[6] = {0, 1, 2, 1, 2, 3};
40
41
42class DrawMeshHelper {
43public:
44 DrawMeshHelper(GrOpFlushState* state) : fState(state) {}
45
46 sk_sp<const GrBuffer> getIndexBuffer();
47
48 template<typename T> sk_sp<const GrBuffer> makeVertexBuffer(const SkTArray<T>& data) {
49 return this->makeVertexBuffer(data.begin(), data.count());
50 }
51 template<typename T> sk_sp<const GrBuffer> makeVertexBuffer(const T* data, int count);
52
53 void drawMesh(const GrMesh& mesh);
54
55private:
56 GrOpFlushState* fState;
57};
58
59struct Box {
60 float fX, fY;
61 GrColor fColor;
62};
63
64////////////////////////////////////////////////////////////////////////////////////////////////////
65
66/**
67 * This is a GPU-backend specific test. It tries to test all possible usecases of GrMesh. The test
68 * works by drawing checkerboards of colored boxes, reading back the pixels, and comparing with
69 * expected results. The boxes are drawn on integer boundaries and the (opaque) colors are chosen
70 * from the set (r,g,b) = (0,255)^3, so the GPU renderings ought to produce exact matches.
71 */
72
73static void run_test(const char* testName, skiatest::Reporter*, const sk_sp<GrRenderTargetContext>&,
74 const SkBitmap& gold, std::function<void(DrawMeshHelper*)> testFn);
75
76DEF_GPUTEST_FOR_RENDERING_CONTEXTS(GrMeshTest, reporter, ctxInfo) {
77 GrContext* const context = ctxInfo.grContext();
78
79 sk_sp<GrRenderTargetContext> rtc(
80 context->makeDeferredRenderTargetContext(SkBackingFit::kExact, kImageWidth, kImageHeight,
81 kRGBA_8888_GrPixelConfig, nullptr));
82 if (!rtc) {
83 ERRORF(reporter, "could not create render target context.");
84 return;
85 }
86
87 SkTArray<Box> boxes;
88 SkTArray<std::array<Box, 4>> vertexData;
89 SkBitmap gold;
90
91 // ---- setup ----------
92
93 SkPaint paint;
94 paint.setBlendMode(SkBlendMode::kSrc);
95 gold.allocN32Pixels(kImageWidth, kImageHeight);
96
97 SkCanvas goldCanvas(gold);
98
99 for (int y = 0; y < kBoxCountY; ++y) {
100 for (int x = 0; x < kBoxCountX; ++x) {
101 int c = y + x;
102 int rgb[3] = {-(c & 1) & 0xff, -((c >> 1) & 1) & 0xff, -((c >> 2) & 1) & 0xff};
103
104 const Box box = boxes.push_back() = {
105 float(x * kBoxSize),
106 float(y * kBoxSize),
107 GrColorPackRGBA(rgb[0], rgb[1], rgb[2], 255)
108 };
109
110 std::array<Box, 4>& boxVertices = vertexData.push_back();
111 for (int i = 0; i < 4; ++i) {
112 boxVertices[i] = {
113 box.fX + (i/2) * kBoxSize,
114 box.fY + (i%2) * kBoxSize,
115 box.fColor
116 };
117 }
118
119 paint.setARGB(255, rgb[0], rgb[1], rgb[2]);
120 goldCanvas.drawRect(SkRect::MakeXYWH(box.fX, box.fY, kBoxSize, kBoxSize), paint);
121 }
122 }
123
124 goldCanvas.flush();
125
126 // ---- tests ----------
127
128#define VALIDATE(buff) \
129 if (!buff) { \
130 ERRORF(reporter, #buff " is null."); \
131 return; \
132 }
133
134 run_test("setNonIndexedNonInstanced", reporter, rtc, gold, [&](DrawMeshHelper* helper) {
135 SkTArray<Box> expandedVertexData;
136 for (int i = 0; i < kBoxCount; ++i) {
137 for (int j = 0; j < 6; ++j) {
138 expandedVertexData.push_back(vertexData[i][kIndexPattern[j]]);
139 }
140 }
141
142 // Draw boxes one line at a time to exercise base vertex.
143 auto vbuff = helper->makeVertexBuffer(expandedVertexData);
144 VALIDATE(vbuff);
145 for (int y = 0; y < kBoxCountY; ++y) {
146 GrMesh mesh(kTriangles_GrPrimitiveType);
147 mesh.setNonIndexed(kBoxCountX * 6);
148 mesh.setVertexData(vbuff.get(), y * kBoxCountX * 6);
149 helper->drawMesh(mesh);
150 }
151 });
152
153 run_test("setIndexed", reporter, rtc, gold, [&](DrawMeshHelper* helper) {
154 auto ibuff = helper->getIndexBuffer();
155 VALIDATE(ibuff);
156 auto vbuff = helper->makeVertexBuffer(vertexData);
157 VALIDATE(vbuff);
158 int baseRepetition = 0;
159 int i = 0;
160
161 // Start at various repetitions within the patterned index buffer to exercise base index.
162 while (i < kBoxCount) {
163 GR_STATIC_ASSERT(kIndexPatternRepeatCount >= 3);
164 int repetitionCount = SkTMin(3 - baseRepetition, kBoxCount - i);
165
166 GrMesh mesh(kTriangles_GrPrimitiveType);
167 mesh.setIndexed(ibuff.get(), repetitionCount * 6, baseRepetition * 6,
168 baseRepetition * 4, (baseRepetition + repetitionCount) * 4 - 1);
169 mesh.setVertexData(vbuff.get(), (i - baseRepetition) * 4);
170 helper->drawMesh(mesh);
171
172 baseRepetition = (baseRepetition + 1) % 3;
173 i += repetitionCount;
174 }
175 });
176
177 run_test("setIndexedPatterned", reporter, rtc, gold, [&](DrawMeshHelper* helper) {
178 auto ibuff = helper->getIndexBuffer();
179 VALIDATE(ibuff);
180 auto vbuff = helper->makeVertexBuffer(vertexData);
181 VALIDATE(vbuff);
182
183 // Draw boxes one line at a time to exercise base vertex. setIndexedPatterned does not
184 // support a base index.
185 for (int y = 0; y < kBoxCountY; ++y) {
186 GrMesh mesh(kTriangles_GrPrimitiveType);
187 mesh.setIndexedPatterned(ibuff.get(), 6, 4, kBoxCountX, kIndexPatternRepeatCount);
188 mesh.setVertexData(vbuff.get(), y * kBoxCountX * 4);
189 helper->drawMesh(mesh);
190 }
191 });
192}
193
194////////////////////////////////////////////////////////////////////////////////////////////////////
195
196class GrMeshTestOp : public GrDrawOp {
197public:
198 DEFINE_OP_CLASS_ID
199
200 GrMeshTestOp(std::function<void(DrawMeshHelper*)> testFn)
201 : INHERITED(ClassID())
202 , fTestFn(testFn) {
203 this->setBounds(SkRect::MakeIWH(kImageWidth, kImageHeight),
204 HasAABloat::kNo, IsZeroArea::kNo);
205 }
206
207private:
208 const char* name() const override { return "GrMeshTestOp"; }
209 FixedFunctionFlags fixedFunctionFlags() const override { return FixedFunctionFlags::kNone; }
210 bool xpRequiresDstTexture(const GrCaps&, const GrAppliedClip*) override { return false; }
211 bool onCombineIfPossible(GrOp* other, const GrCaps& caps) override { return false; }
212 void onPrepare(GrOpFlushState*) override {}
213 void onExecute(GrOpFlushState* state) override {
214 DrawMeshHelper helper(state);
215 fTestFn(&helper);
216 }
217
218 std::function<void(DrawMeshHelper*)> fTestFn;
219
220 typedef GrDrawOp INHERITED;
221};
222
223class GrMeshTestProcessor : public GrGeometryProcessor {
224public:
225 GrMeshTestProcessor()
226 : fVertex(this->addVertexAttrib("vertex", kVec2f_GrVertexAttribType))
227 , fColor(this->addVertexAttrib("color", kVec4ub_GrVertexAttribType)) {
228 this->initClassID<GrMeshTestProcessor>();
229 }
230
231 const char* name() const override { return "GrMeshTest Processor"; }
232
233 void getGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const final {}
234
235 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const final;
236
237protected:
238 const Attribute& fVertex;
239 const Attribute& fColor;
240
241 friend class GLSLMeshTestProcessor;
242 typedef GrGeometryProcessor INHERITED;
243};
244
245class GLSLMeshTestProcessor : public GrGLSLGeometryProcessor {
246 void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor&,
247 FPCoordTransformIter&& transformIter) final {}
248
249 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) final {
250 const GrMeshTestProcessor& mp = args.fGP.cast<GrMeshTestProcessor>();
251
252 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
253 varyingHandler->emitAttributes(mp);
254 varyingHandler->addPassThroughAttribute(&mp.fColor, args.fOutputColor);
255
256 GrGLSLVertexBuilder* v = args.fVertBuilder;
257 v->codeAppendf("vec2 vertex = %s;", mp.fVertex.fName);
258 gpArgs->fPositionVar.set(kVec2f_GrSLType, "vertex");
259
260 GrGLSLPPFragmentBuilder* f = args.fFragBuilder;
261 f->codeAppendf("%s = vec4(1);", args.fOutputCoverage);
262 }
263};
264
265GrGLSLPrimitiveProcessor* GrMeshTestProcessor::createGLSLInstance(const GrShaderCaps&) const {
266 return new GLSLMeshTestProcessor;
267}
268
269////////////////////////////////////////////////////////////////////////////////////////////////////
270
271template<typename T>
272sk_sp<const GrBuffer> DrawMeshHelper::makeVertexBuffer(const T* data, int count) {
273 return sk_sp<const GrBuffer>(
274 fState->resourceProvider()->createBuffer(
275 count * sizeof(T), kVertex_GrBufferType, kDynamic_GrAccessPattern,
276 GrResourceProvider::kNoPendingIO_Flag |
277 GrResourceProvider::kRequireGpuMemory_Flag, data));
278}
279
280sk_sp<const GrBuffer> DrawMeshHelper::getIndexBuffer() {
281 GR_DEFINE_STATIC_UNIQUE_KEY(gIndexBufferKey);
282 return sk_sp<const GrBuffer>(
283 fState->resourceProvider()->findOrCreatePatternedIndexBuffer(
284 kIndexPattern, 6, kIndexPatternRepeatCount, 4, gIndexBufferKey));
285}
286
287void DrawMeshHelper::drawMesh(const GrMesh& mesh) {
288 GrRenderTarget* rt = fState->drawOpArgs().fRenderTarget;
289 GrPipeline pipeline(rt, SkBlendMode::kSrc);
290 fState->commandBuffer()->draw(pipeline, GrMeshTestProcessor(), &mesh, 1,
291 SkRect::MakeIWH(kImageWidth, kImageHeight));
292}
293
294static void run_test(const char* testName, skiatest::Reporter* reporter,
295 const sk_sp<GrRenderTargetContext>& rtc, const SkBitmap& gold,
296 std::function<void(DrawMeshHelper*)> testFn) {
297 const int w = gold.width(), h = gold.height(), rowBytes = gold.rowBytes();
298 const uint32_t* goldPx = reinterpret_cast<const uint32_t*>(gold.getPixels());
299 if (h != rtc->height() || w != rtc->width()) {
300 ERRORF(reporter, "[%s] expectation and rtc not compatible (?).", testName);
301 return;
302 }
303 if (sizeof(uint32_t) * kImageWidth != gold.rowBytes()) {
304 ERRORF(reporter, "unexpected row bytes in gold image.", testName);
305 return;
306 }
307
308 SkAutoSTMalloc<kImageHeight * kImageWidth, uint32_t> resultPx(h * rowBytes);
309 rtc->clear(nullptr, 0xbaaaaaad, true);
310 rtc->priv().testingOnly_addDrawOp(skstd::make_unique<GrMeshTestOp>(testFn));
311 rtc->readPixels(gold.info(), resultPx, rowBytes, 0, 0, 0);
312 for (int y = 0; y < h; ++y) {
313 for (int x = 0; x < w; ++x) {
314 uint32_t expected = goldPx[y * kImageWidth + x];
315 uint32_t actual = resultPx[y * kImageWidth + x];
316 if (expected != actual) {
317 ERRORF(reporter, "[%s] pixel (%i,%i): got 0x%x expected 0x%x",
318 testName, x, y, actual, expected);
319 return;
320 }
321 }
322 }
323}
324
325#endif