1 /*
2 * Copyright 2021 Google LLC
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 "tests/Test.h"
9
10 #include "experimental/graphite/include/Context.h"
11 #include "experimental/graphite/include/Recorder.h"
12 #include "experimental/graphite/include/mtl/MtlTypes.h"
13 #include "experimental/graphite/src/Buffer.h"
14 #include "experimental/graphite/src/Caps.h"
15 #include "experimental/graphite/src/CommandBuffer.h"
16 #include "experimental/graphite/src/ContextPriv.h"
17 #include "experimental/graphite/src/DrawBufferManager.h"
18 #include "experimental/graphite/src/DrawWriter.h"
19 #include "experimental/graphite/src/GlobalCache.h"
20 #include "experimental/graphite/src/Gpu.h"
21 #include "experimental/graphite/src/GraphicsPipeline.h"
22 #include "experimental/graphite/src/RecorderPriv.h"
23 #include "experimental/graphite/src/Renderer.h"
24 #include "experimental/graphite/src/ResourceProvider.h"
25 #include "experimental/graphite/src/Sampler.h"
26 #include "experimental/graphite/src/Texture.h"
27 #include "experimental/graphite/src/TextureProxy.h"
28 #include "experimental/graphite/src/UniformManager.h"
29 #include "experimental/graphite/src/geom/Shape.h"
30 #include "experimental/graphite/src/geom/Transform_graphite.h"
31 #include "src/core/SkKeyHelpers.h"
32 #include "src/core/SkShaderCodeDictionary.h"
33 #include "src/core/SkUniformData.h"
34
35 #if GRAPHITE_TEST_UTILS
36 // set to 1 if you want to do GPU capture of the commandBuffer
37 #define CAPTURE_COMMANDBUFFER 0
38 #endif
39
40 using namespace skgpu;
41
42 namespace {
43
44 const DepthStencilSettings kTestDepthStencilSettings = {
45 // stencil
46 {},
47 {},
48 0,
49 true,
50 // depth
51 CompareOp::kAlways,
52 true,
53 false,
54 };
55
56 class UniformRectDraw final : public RenderStep {
57 public:
~UniformRectDraw()58 ~UniformRectDraw() override {}
59
Singleton()60 static const RenderStep* Singleton() {
61 static const UniformRectDraw kSingleton;
62 return &kSingleton;
63 }
64
name() const65 const char* name() const override { return "uniform-rect"; }
66
vertexSkSL() const67 const char* vertexSkSL() const override {
68 return "float2 tmpPosition = float2(float(sk_VertexID >> 1), float(sk_VertexID & 1));\n"
69 "float4 devPosition = float4(tmpPosition * scale + translate, 0.0, 1.0);\n";
70 }
71
writeVertices(DrawWriter * writer,const SkIRect &,const Transform &,const Shape &) const72 void writeVertices(DrawWriter* writer,
73 const SkIRect&,
74 const Transform&,
75 const Shape&) const override {
76 // The shape is upload via uniforms, so this just needs to record 4 data-less vertices
77 writer->draw({}, 4);
78 }
79
writeUniforms(Layout layout,const SkIRect &,const Transform &,const Shape & shape) const80 sk_sp<SkUniformData> writeUniforms(Layout layout,
81 const SkIRect&,
82 const Transform&,
83 const Shape& shape) const override {
84 SkASSERT(shape.isRect());
85 // TODO: A << API for uniforms would be nice, particularly if it could take pre-computed
86 // offsets for each uniform.
87 auto uniforms = SkUniformData::Make(this->uniforms(), sizeof(float) * 4);
88 float2 scale = shape.rect().size();
89 float2 translate = shape.rect().topLeft();
90 memcpy(uniforms->data(), &scale, sizeof(float2));
91 memcpy(uniforms->data() + sizeof(float2), &translate, sizeof(float2));
92 return uniforms;
93 }
94
95 private:
UniformRectDraw()96 UniformRectDraw() : RenderStep(Flags::kPerformsShading,
97 /*uniforms=*/{{"scale", SkSLType::kFloat2},
98 {"translate", SkSLType::kFloat2}},
99 PrimitiveType::kTriangleStrip,
100 {{},
101 {},
102 0,
103 true,
104 CompareOp::kAlways,
105 false,
106 false},
107 /*vertexAttrs=*/{},
108 /*instanceAttrs=*/{}) {}
109 };
110
111 class TriangleRectDraw final : public RenderStep {
112 public:
~TriangleRectDraw()113 ~TriangleRectDraw() override {}
114
Singleton()115 static const RenderStep* Singleton() {
116 static const TriangleRectDraw kSingleton;
117 return &kSingleton;
118 }
119
name() const120 const char* name() const override { return "triangle-rect"; }
121
vertexSkSL() const122 const char* vertexSkSL() const override {
123 return "float4 devPosition = float4(position * scale + translate, 0.0, 1.0);\n";
124 }
125
writeVertices(DrawWriter * writer,const SkIRect &,const Transform &,const Shape & shape) const126 void writeVertices(DrawWriter* writer,
127 const SkIRect&,
128 const Transform&,
129 const Shape& shape) const override {
130 DrawBufferManager* bufferMgr = writer->bufferManager();
131 auto [vertexWriter, vertices] = bufferMgr->getVertexWriter(4 * this->vertexStride());
132 vertexWriter << 0.5f * (shape.rect().left() + 1.f) << 0.5f * (shape.rect().top() + 1.f)
133 << 0.5f * (shape.rect().left() + 1.f) << 0.5f * (shape.rect().bot() + 1.f)
134 << 0.5f * (shape.rect().right() + 1.f) << 0.5f * (shape.rect().top() + 1.f)
135 << 0.5f * (shape.rect().right() + 1.f) << 0.5f * (shape.rect().bot() + 1.f);
136
137 // TODO: Would be nice to re-use this
138 auto [indexWriter, indices] = bufferMgr->getIndexWriter(6 * sizeof(uint16_t));
139 indexWriter << 0 << 1 << 2
140 << 2 << 1 << 3;
141
142 writer->drawIndexed(vertices, indices, 6);
143 }
144
writeUniforms(Layout layout,const SkIRect &,const Transform &,const Shape &) const145 sk_sp<SkUniformData> writeUniforms(Layout layout,
146 const SkIRect&,
147 const Transform&,
148 const Shape&) const override {
149 auto uniforms = SkUniformData::Make(this->uniforms(), sizeof(float) * 4);
150 float data[4] = {2.f, 2.f, -1.f, -1.f};
151 memcpy(uniforms->data(), data, 4 * sizeof(float));
152 return uniforms;
153 }
154
155 private:
TriangleRectDraw()156 TriangleRectDraw()
157 : RenderStep(Flags::kPerformsShading,
158 /*uniforms=*/{{"scale", SkSLType::kFloat2},
159 {"translate", SkSLType::kFloat2}},
160 PrimitiveType::kTriangles,
161 kTestDepthStencilSettings,
162 /*vertexAttrs=*/{{"position",
163 VertexAttribType::kFloat2,
164 SkSLType::kFloat2}},
165 /*instanceAttrs=*/{}) {}
166 };
167
168 class InstanceRectDraw final : public RenderStep {
169 public:
~InstanceRectDraw()170 ~InstanceRectDraw() override {}
171
Singleton()172 static const RenderStep* Singleton() {
173 static const InstanceRectDraw kSingleton;
174 return &kSingleton;
175 }
176
name() const177 const char* name() const override { return "instance-rect"; }
178
vertexSkSL() const179 const char* vertexSkSL() const override {
180 return "float2 tmpPosition = float2(float(sk_VertexID >> 1), float(sk_VertexID & 1));\n"
181 "float4 devPosition = float4(tmpPosition * dims + position, 0.0, 1.0);\n";
182 }
183
writeVertices(DrawWriter * writer,const SkIRect &,const Transform &,const Shape & shape) const184 void writeVertices(DrawWriter* writer,
185 const SkIRect&,
186 const Transform&,
187 const Shape& shape) const override {
188 SkASSERT(shape.isRect());
189
190 DrawBufferManager* bufferMgr = writer->bufferManager();
191
192 // TODO: To truly test draw merging, this index buffer needs to remembered across
193 // writeVertices calls
194 auto [indexWriter, indices] = bufferMgr->getIndexWriter(6 * sizeof(uint16_t));
195 indexWriter << 0 << 1 << 2
196 << 2 << 1 << 3;
197
198 DrawWriter::Instances instances{*writer, {}, indices, 6};
199 instances.append(1) << shape.rect().topLeft() << shape.rect().size();
200 }
201
writeUniforms(Layout,const SkIRect &,const Transform &,const Shape &) const202 sk_sp<SkUniformData> writeUniforms(Layout,
203 const SkIRect&,
204 const Transform&,
205 const Shape&) const override {
206 return nullptr;
207 }
208
209 private:
InstanceRectDraw()210 InstanceRectDraw()
211 : RenderStep(Flags::kPerformsShading,
212 /*uniforms=*/{},
213 PrimitiveType::kTriangles,
214 kTestDepthStencilSettings,
215 /*vertexAttrs=*/{},
216 /*instanceAttrs=*/ {
217 { "position", VertexAttribType::kFloat2, SkSLType::kFloat2 },
218 { "dims", VertexAttribType::kFloat2, SkSLType::kFloat2 }
219 }) {}
220 };
221
222 } // anonymous namespace
223
224 /*
225 * This is to test the various pieces of the CommandBuffer interface.
226 */
DEF_GRAPHITE_TEST_FOR_CONTEXTS(CommandBufferTest,reporter,context)227 DEF_GRAPHITE_TEST_FOR_CONTEXTS(CommandBufferTest, reporter, context) {
228 constexpr int kTextureWidth = 1024;
229 constexpr int kTextureHeight = 768;
230
231 auto gpu = context->priv().gpu();
232 REPORTER_ASSERT(reporter, gpu);
233
234 #if GRAPHITE_TEST_UTILS && CAPTURE_COMMANDBUFFER
235 gpu->testingOnly_startCapture();
236 #endif
237 auto recorder = context->makeRecorder();
238 auto resourceProvider = recorder->priv().resourceProvider();
239 auto dict = resourceProvider->shaderCodeDictionary();
240 auto commandBuffer = resourceProvider->createCommandBuffer();
241
242 SkISize textureSize = { kTextureWidth, kTextureHeight };
243 #ifdef SK_METAL
244 skgpu::mtl::TextureInfo mtlTextureInfo = {
245 1,
246 1,
247 70, // MTLPixelFormatRGBA8Unorm
248 0x0005, // MTLTextureUsageRenderTarget | MTLTextureUsageShaderRead
249 2, // MTLStorageModePrivate
250 false, // framebufferOnly
251 };
252 TextureInfo textureInfo(mtlTextureInfo);
253 #else
254 TextureInfo textureInfo;
255 #endif
256
257 std::unique_ptr<SkPaintParamsKey> key = CreateKey(dict,
258 SkBackend::kGraphite,
259 ShaderCombo::ShaderType::kSolidColor,
260 SkTileMode::kClamp,
261 SkBlendMode::kSrc);
262
263 auto entry = dict->findOrCreate(std::move(key));
264
265 auto target = sk_sp<TextureProxy>(new TextureProxy(textureSize, textureInfo));
266 REPORTER_ASSERT(reporter, target);
267
268 RenderPassDesc renderPassDesc = {};
269 renderPassDesc.fColorAttachment.fTextureInfo = target->textureInfo();
270 renderPassDesc.fColorAttachment.fLoadOp = LoadOp::kClear;
271 renderPassDesc.fColorAttachment.fStoreOp = StoreOp::kStore;
272 renderPassDesc.fClearColor = { 1, 0, 0, 1 }; // red
273
274 target->instantiate(resourceProvider);
275 DrawBufferManager bufferMgr(resourceProvider, 4);
276
277 TextureInfo depthStencilInfo =
278 gpu->caps()->getDefaultDepthStencilTextureInfo(DepthStencilFlags::kDepthStencil,
279 1,
280 Protected::kNo);
281 renderPassDesc.fDepthStencilAttachment.fTextureInfo = depthStencilInfo;
282 renderPassDesc.fDepthStencilAttachment.fLoadOp = LoadOp::kDiscard;
283 renderPassDesc.fDepthStencilAttachment.fStoreOp = StoreOp::kDiscard;
284 sk_sp<Texture> depthStencilTexture =
285 resourceProvider->findOrCreateTexture(textureSize, depthStencilInfo);
286
287 // Create Sampler -- for now, just to test creation
288 sk_sp<Sampler> sampler = resourceProvider->findOrCreateCompatibleSampler(
289 SkSamplingOptions(SkFilterMode::kLinear), SkTileMode::kClamp, SkTileMode::kDecal);
290 REPORTER_ASSERT(reporter, sampler);
291
292 commandBuffer->beginRenderPass(renderPassDesc, target->refTexture(), nullptr,
293 depthStencilTexture);
294
295 commandBuffer->setViewport(0.f, 0.f, kTextureWidth, kTextureHeight);
296
297 DrawWriter drawWriter(commandBuffer->asDrawDispatcher(), &bufferMgr);
298
299 struct RectAndColor {
300 SkRect fRect;
301 SkColor4f fColor;
302 };
303
304 auto draw = [&](const RenderStep* step, std::vector<RectAndColor> draws) {
305 GraphicsPipelineDesc pipelineDesc;
306 pipelineDesc.setProgram(step, entry->uniqueID());
307 drawWriter.newPipelineState(step->primitiveType(),
308 step->vertexStride(),
309 step->instanceStride());
310 auto pipeline = resourceProvider->findOrCreateGraphicsPipeline(pipelineDesc,
311 renderPassDesc);
312 commandBuffer->bindGraphicsPipeline(std::move(pipeline));
313
314 // All of the test RenderSteps ignore the transform, so just use the identity
315 static const Transform kIdentity{SkM44()};
316 // No set scissor, so use entire render target dimensions
317 static const SkIRect kBounds = SkIRect::MakeWH(kTextureWidth, kTextureHeight);
318
319 for (auto d : draws) {
320 drawWriter.newDynamicState();
321 Shape shape(d.fRect);
322
323 auto renderStepUniforms =
324 step->writeUniforms(Layout::kMetal, kBounds, kIdentity, shape);
325 if (renderStepUniforms) {
326 auto [writer, bindInfo] =
327 bufferMgr.getUniformWriter(renderStepUniforms->dataSize());
328 writer.write(renderStepUniforms->data(), renderStepUniforms->dataSize());
329 commandBuffer->bindUniformBuffer(UniformSlot::kRenderStep,
330 sk_ref_sp(bindInfo.fBuffer),
331 bindInfo.fOffset);
332 }
333
334 // TODO: Rely on uniform writer and GetUniforms(kSolidColor).
335 auto [writer, bindInfo] = bufferMgr.getUniformWriter(sizeof(SkColor4f));
336 writer.write(&d.fColor, sizeof(SkColor4f));
337 commandBuffer->bindUniformBuffer(UniformSlot::kPaint,
338 sk_ref_sp(bindInfo.fBuffer),
339 bindInfo.fOffset);
340
341 step->writeVertices(&drawWriter, kBounds, kIdentity, shape);
342 }
343 };
344
345 SkRect fullRect = SkRect::MakeIWH(kTextureWidth, kTextureHeight);
346 // Draw blue rectangle over entire rendertarget (which was red)
347 draw(UniformRectDraw::Singleton(), {{fullRect, SkColors::kBlue}});
348
349 // Draw inset yellow rectangle using uniforms
350 draw(UniformRectDraw::Singleton(),
351 {{fullRect.makeInset(kTextureWidth/20.f, kTextureHeight/20.f), SkColors::kYellow}});
352
353 // Draw inset magenta rectangle with triangles in vertex buffer
354 draw(TriangleRectDraw::Singleton(),
355 {{fullRect.makeInset(kTextureWidth/4.f, kTextureHeight/4.f), SkColors::kMagenta}});
356
357 // Draw green and cyan rects using instance buffer
358 draw(InstanceRectDraw::Singleton(),
359 { {{kTextureWidth/3.f, kTextureHeight/3.f,
360 kTextureWidth/2.f, kTextureHeight/2.f}, SkColors::kGreen},
361 {{kTextureWidth/2.f, kTextureHeight/2.f,
362 5.f*kTextureWidth/8.f, 5.f*kTextureHeight/8.f}, SkColors::kCyan} });
363
364 drawWriter.flush();
365 bufferMgr.transferToCommandBuffer(commandBuffer.get());
366 commandBuffer->endRenderPass();
367
368 // Do readback
369
370 // TODO: add 4-byte transfer buffer alignment for Mac to Caps
371 // add bpp to Caps
372 size_t rowBytes = 4*kTextureWidth;
373 size_t bufferSize = rowBytes*kTextureHeight;
374 sk_sp<Buffer> copyBuffer = resourceProvider->findOrCreateBuffer(
375 bufferSize, BufferType::kXferGpuToCpu, PrioritizeGpuReads::kNo);
376 REPORTER_ASSERT(reporter, copyBuffer);
377 SkIRect srcRect = { 0, 0, kTextureWidth, kTextureHeight };
378 commandBuffer->copyTextureToBuffer(target->refTexture(), srcRect, copyBuffer, 0, rowBytes);
379
380 bool result = gpu->submit(commandBuffer);
381 REPORTER_ASSERT(reporter, result);
382
383 gpu->checkForFinishedWork(skgpu::SyncToCpu::kYes);
384 uint32_t* pixels = (uint32_t*)(copyBuffer->map());
385 REPORTER_ASSERT(reporter, pixels[0] == 0xffff0000);
386 REPORTER_ASSERT(reporter, pixels[51 + 38*kTextureWidth] == 0xff00ffff);
387 REPORTER_ASSERT(reporter, pixels[256 + 192*kTextureWidth] == 0xffff00ff);
388 copyBuffer->unmap();
389
390 #if GRAPHITE_TEST_UTILS && CAPTURE_COMMANDBUFFER
391 gpu->testingOnly_endCapture();
392 #endif
393 }
394