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/src/ContextPriv.h"
12
13#include "experimental/graphite/include/mtl/MtlTypes.h"
14#include "experimental/graphite/src/Buffer.h"
15#include "experimental/graphite/src/CommandBuffer.h"
16#include "experimental/graphite/src/Gpu.h"
17#include "experimental/graphite/src/GraphicsPipeline.h"
18#include "experimental/graphite/src/ResourceProvider.h"
19#include "experimental/graphite/src/Texture.h"
20#include "experimental/graphite/src/TextureProxy.h"
21
22#if GRAPHITE_TEST_UTILS
23// set to 1 if you want to do GPU capture of the commandBuffer
24#define CAPTURE_COMMANDBUFFER 0
25#endif
26
27using namespace skgpu;
28
29/*
30 * This is to test the various pieces of the CommandBuffer interface.
31 */
32DEF_GRAPHITE_TEST_FOR_CONTEXTS(CommandBufferTest, reporter, context) {
33    constexpr int kTextureWidth = 1024;
34    constexpr int kTextureHeight = 768;
35
36    auto gpu = context->priv().gpu();
37    REPORTER_ASSERT(reporter, gpu);
38
39#if GRAPHITE_TEST_UTILS && CAPTURE_COMMANDBUFFER
40    gpu->testingOnly_startCapture();
41#endif
42    auto commandBuffer = gpu->resourceProvider()->createCommandBuffer();
43
44    SkISize textureSize = { kTextureWidth, kTextureHeight };
45#ifdef SK_METAL
46    skgpu::mtl::TextureInfo mtlTextureInfo = {
47        1,
48        1,
49        70,     // MTLPixelFormatRGBA8Unorm
50        0x0005, // MTLTextureUsageRenderTarget | MTLTextureUsageShaderRead
51        2,      // MTLStorageModePrivate
52        false,  // framebufferOnly
53    };
54    TextureInfo textureInfo(mtlTextureInfo);
55#else
56    TextureInfo textureInfo;
57#endif
58
59    auto target = sk_sp<TextureProxy>(new TextureProxy(textureSize, textureInfo));
60    REPORTER_ASSERT(reporter, target);
61
62    RenderPassDesc renderPassDesc = {};
63    renderPassDesc.fColorAttachment.fTextureProxy = target;
64    renderPassDesc.fColorAttachment.fLoadOp = LoadOp::kClear;
65    renderPassDesc.fColorAttachment.fStoreOp = StoreOp::kStore;
66    renderPassDesc.fClearColor = { 1, 0, 0, 1 }; // red
67
68    target->instantiate(gpu->resourceProvider());
69    commandBuffer->beginRenderPass(renderPassDesc);
70
71    // Shared uniform buffer
72    struct UniformData {
73        SkPoint fScale;
74        SkPoint fTranslate;
75        SkColor4f fColor;
76    };
77    sk_sp<Buffer> uniformBuffer = gpu->resourceProvider()->findOrCreateBuffer(
78            2*sizeof(UniformData), BufferType::kUniform, PrioritizeGpuReads::kNo);
79    size_t uniformOffset = 0;
80
81    // Draw blue rectangle over entire rendertarget (which was red)
82    GraphicsPipelineDesc pipelineDesc;
83    pipelineDesc.setTestingOnlyShaderIndex(0);
84    auto graphicsPipeline = gpu->resourceProvider()->findOrCreateGraphicsPipeline(pipelineDesc);
85    commandBuffer->bindGraphicsPipeline(std::move(graphicsPipeline));
86    commandBuffer->draw(PrimitiveType::kTriangleStrip, 0, 4);
87
88    // Draw inset yellow rectangle using uniforms
89    pipelineDesc.setTestingOnlyShaderIndex(1);
90    graphicsPipeline = gpu->resourceProvider()->findOrCreateGraphicsPipeline(pipelineDesc);
91    commandBuffer->bindGraphicsPipeline(std::move(graphicsPipeline));
92    UniformData* uniforms = (UniformData*)uniformBuffer->map();
93    uniforms->fScale = SkPoint({1.8, 1.8});
94    uniforms->fTranslate = SkPoint({-0.9, -0.9});
95    uniforms->fColor = SkColors::kYellow;
96    commandBuffer->bindUniformBuffer(uniformBuffer, uniformOffset);
97    commandBuffer->draw(PrimitiveType::kTriangleStrip, 0, 4);
98    uniformOffset += sizeof(UniformData);
99    ++uniforms;
100
101    // Draw inset magenta rectangle with triangles in vertex buffer
102    pipelineDesc.setTestingOnlyShaderIndex(2);
103    skgpu::GraphicsPipelineDesc::Attribute vertexAttributes[1] = {
104        { "position", VertexAttribType::kFloat2, SLType::kFloat2 }
105    };
106    pipelineDesc.setVertexAttributes(vertexAttributes, 1);
107    graphicsPipeline = gpu->resourceProvider()->findOrCreateGraphicsPipeline(pipelineDesc);
108    commandBuffer->bindGraphicsPipeline(std::move(graphicsPipeline));
109
110    struct VertexData {
111        SkPoint fPosition;
112    };
113    sk_sp<Buffer> vertexBuffer = gpu->resourceProvider()->findOrCreateBuffer(
114            4*sizeof(VertexData), BufferType::kVertex, PrioritizeGpuReads::kNo);
115    sk_sp<Buffer> indexBuffer = gpu->resourceProvider()->findOrCreateBuffer(
116            6*sizeof(uint16_t), BufferType::kIndex, PrioritizeGpuReads::kNo);
117    auto vertices = (VertexData*)vertexBuffer->map();
118    vertices[0].fPosition = SkPoint({0.25f, 0.25f});
119    vertices[1].fPosition = SkPoint({0.25f, 0.75f});
120    vertices[2].fPosition = SkPoint({0.75f, 0.25f});
121    vertices[3].fPosition = SkPoint({0.75f, 0.75f});
122    vertexBuffer->unmap();
123    auto indices = (uint16_t*)indexBuffer->map();
124    indices[0] = 0;
125    indices[1] = 1;
126    indices[2] = 2;
127    indices[3] = 2;
128    indices[4] = 1;
129    indices[5] = 3;
130    indexBuffer->unmap();
131    commandBuffer->bindVertexBuffers(vertexBuffer, 0, nullptr, 0);
132    commandBuffer->bindIndexBuffer(indexBuffer, 0);
133    uniforms->fScale = SkPoint({2, 2});
134    uniforms->fTranslate = SkPoint({-1, -1});
135    uniforms->fColor = SkColors::kMagenta;
136    commandBuffer->bindUniformBuffer(uniformBuffer, uniformOffset);
137    commandBuffer->drawIndexed(PrimitiveType::kTriangles, 0, 6, 0);
138
139    // draw rects using instance buffer
140    pipelineDesc.setTestingOnlyShaderIndex(3);
141    skgpu::GraphicsPipelineDesc::Attribute instanceAttributes[3] = {
142        { "position", VertexAttribType::kFloat2, SLType::kFloat2 },
143        { "dims", VertexAttribType::kFloat2, SLType::kFloat2 },
144        { "color", VertexAttribType::kFloat4, SLType::kFloat4 }
145    };
146    pipelineDesc.setVertexAttributes(nullptr, 0);
147    pipelineDesc.setInstanceAttributes(instanceAttributes, 3);
148    graphicsPipeline = gpu->resourceProvider()->findOrCreateGraphicsPipeline(pipelineDesc);
149    commandBuffer->bindGraphicsPipeline(std::move(graphicsPipeline));
150
151    struct InstanceData {
152        SkPoint fPosition;
153        SkPoint fDims;
154        SkColor4f fColor;
155    };
156    sk_sp<Buffer> instanceBuffer = gpu->resourceProvider()->findOrCreateBuffer(
157            2*sizeof(InstanceData), BufferType::kVertex, PrioritizeGpuReads::kNo);
158    auto instances = (InstanceData*)instanceBuffer->map();
159    instances[0].fPosition = SkPoint({-0.4, -0.4});
160    instances[0].fDims = SkPoint({0.4, 0.4});
161    instances[0].fColor = SkColors::kGreen;
162    instances[1].fPosition = SkPoint({0, 0});
163    instances[1].fDims = SkPoint({0.25, 0.25});
164    instances[1].fColor = SkColors::kCyan;
165    instanceBuffer->unmap();
166    commandBuffer->bindVertexBuffers(nullptr, 0, instanceBuffer, 0);
167//    commandBuffer->drawInstanced(PrimitiveType::kTriangleStrip, 0, 4, 0, 2);
168    commandBuffer->drawIndexedInstanced(PrimitiveType::kTriangles, 0, 6, 0, 0, 2);
169
170    commandBuffer->endRenderPass();
171
172    uniformBuffer->unmap();
173
174    // Do readback
175
176    // TODO: add 4-byte transfer buffer alignment for Mac to Caps
177    //       add bpp to Caps
178    size_t rowBytes = 4*kTextureWidth;
179    size_t bufferSize = rowBytes*kTextureHeight;
180    sk_sp<Buffer> copyBuffer = gpu->resourceProvider()->findOrCreateBuffer(
181            bufferSize, BufferType::kXferGpuToCpu, PrioritizeGpuReads::kNo);
182    REPORTER_ASSERT(reporter, copyBuffer);
183    SkIRect srcRect = { 0, 0, kTextureWidth, kTextureHeight };
184    commandBuffer->copyTextureToBuffer(target->refTexture(), srcRect, copyBuffer, 0, rowBytes);
185
186    bool result = gpu->submit(commandBuffer);
187    REPORTER_ASSERT(reporter, result);
188
189    gpu->checkForFinishedWork(skgpu::SyncToCpu::kYes);
190    uint32_t* pixels = (uint32_t*)(copyBuffer->map());
191    REPORTER_ASSERT(reporter, pixels[0] == 0xffff0000);
192    REPORTER_ASSERT(reporter, pixels[51 + 38*kTextureWidth] == 0xff00ffff);
193    REPORTER_ASSERT(reporter, pixels[256 + 192*kTextureWidth] == 0xffff00ff);
194    copyBuffer->unmap();
195
196#if GRAPHITE_TEST_UTILS && CAPTURE_COMMANDBUFFER
197    gpu->testingOnly_endCapture();
198#endif
199}
200