• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "experimental/graphite/src/mtl/MtlCommandBuffer.h"
9
10#include "experimental/graphite/src/TextureProxy.h"
11#include "experimental/graphite/src/mtl/MtlBlitCommandEncoder.h"
12#include "experimental/graphite/src/mtl/MtlBuffer.h"
13#include "experimental/graphite/src/mtl/MtlCaps.h"
14#include "experimental/graphite/src/mtl/MtlGpu.h"
15#include "experimental/graphite/src/mtl/MtlGraphicsPipeline.h"
16#include "experimental/graphite/src/mtl/MtlRenderCommandEncoder.h"
17#include "experimental/graphite/src/mtl/MtlTexture.h"
18
19namespace skgpu::mtl {
20
21sk_sp<CommandBuffer> CommandBuffer::Make(const Gpu* gpu) {
22    sk_cfp<id<MTLCommandBuffer>> cmdBuffer;
23    id<MTLCommandQueue> queue = gpu->queue();
24    if (@available(macOS 11.0, iOS 14.0, tvOS 14.0, *)) {
25        sk_cfp<MTLCommandBufferDescriptor*> desc([[MTLCommandBufferDescriptor alloc] init]);
26        (*desc).retainedReferences = NO;
27#ifdef SK_ENABLE_MTL_DEBUG_INFO
28        (*desc).errorOptions = MTLCommandBufferErrorOptionEncoderExecutionStatus;
29#endif
30        // We add a retain here because the command buffer is set to autorelease (not alloc or copy)
31        cmdBuffer.reset([[queue commandBufferWithDescriptor:desc.get()] retain]);
32    } else {
33        // We add a retain here because the command buffer is set to autorelease (not alloc or copy)
34        cmdBuffer.reset([[queue commandBufferWithUnretainedReferences] retain]);
35    }
36    if (cmdBuffer == nil) {
37        return nullptr;
38    }
39
40#ifdef SK_ENABLE_MTL_DEBUG_INFO
41     (*cmdBuffer).label = @"CommandBuffer::Make";
42#endif
43
44    return sk_sp<CommandBuffer>(new CommandBuffer(std::move(cmdBuffer), gpu));
45}
46
47CommandBuffer::CommandBuffer(sk_cfp<id<MTLCommandBuffer>> cmdBuffer, const Gpu* gpu)
48    : fCommandBuffer(std::move(cmdBuffer)), fGpu(gpu) {}
49
50CommandBuffer::~CommandBuffer() {}
51
52bool CommandBuffer::commit() {
53    SkASSERT(!fActiveRenderCommandEncoder);
54    this->endBlitCommandEncoder();
55    [(*fCommandBuffer) commit];
56
57    // TODO: better error reporting
58    if ((*fCommandBuffer).status == MTLCommandBufferStatusError) {
59        NSString* description = (*fCommandBuffer).error.localizedDescription;
60        const char* errorString = [description UTF8String];
61        SkDebugf("Error submitting command buffer: %s\n", errorString);
62    }
63
64    return ((*fCommandBuffer).status != MTLCommandBufferStatusError);
65}
66
67void CommandBuffer::onBeginRenderPass(const RenderPassDesc& renderPassDesc) {
68    SkASSERT(!fActiveRenderCommandEncoder);
69    this->endBlitCommandEncoder();
70
71    const static MTLLoadAction mtlLoadAction[] {
72        MTLLoadActionLoad,
73        MTLLoadActionClear,
74        MTLLoadActionDontCare
75    };
76    static_assert((int)LoadOp::kLoad == 0);
77    static_assert((int)LoadOp::kClear == 1);
78    static_assert((int)LoadOp::kDiscard == 2);
79    static_assert(SK_ARRAY_COUNT(mtlLoadAction) == kLoadOpCount);
80
81    const static MTLStoreAction mtlStoreAction[] {
82        MTLStoreActionStore,
83        MTLStoreActionDontCare
84    };
85    static_assert((int)StoreOp::kStore == 0);
86    static_assert((int)StoreOp::kDiscard == 1);
87    static_assert(SK_ARRAY_COUNT(mtlStoreAction) == kStoreOpCount);
88
89    sk_cfp<MTLRenderPassDescriptor*> descriptor([[MTLRenderPassDescriptor alloc] init]);
90    // Set up color attachment.
91    auto& colorInfo = renderPassDesc.fColorAttachment;
92    if (colorInfo.fTextureProxy) {
93        auto colorAttachment = (*descriptor).colorAttachments[0];
94        const Texture* colorTexture = (const Texture*)colorInfo.fTextureProxy->texture();
95        colorAttachment.texture = colorTexture->mtlTexture();
96        const std::array<float, 4>& clearColor = renderPassDesc.fClearColor;
97        colorAttachment.clearColor =
98                MTLClearColorMake(clearColor[0], clearColor[1], clearColor[2], clearColor[3]);
99        colorAttachment.loadAction = mtlLoadAction[static_cast<int>(colorInfo.fLoadOp)];
100        colorAttachment.storeAction = mtlStoreAction[static_cast<int>(colorInfo.fStoreOp)];
101    }
102
103    // TODO:
104    // * setup resolve
105    // * set up stencil and depth
106
107    fActiveRenderCommandEncoder = RenderCommandEncoder::Make(fCommandBuffer.get(),
108                                                             descriptor.get());
109
110    this->trackResource(fActiveRenderCommandEncoder);
111}
112
113void CommandBuffer::endRenderPass() {
114    SkASSERT(fActiveRenderCommandEncoder);
115    fActiveRenderCommandEncoder->endEncoding();
116    fActiveRenderCommandEncoder.reset();
117}
118
119BlitCommandEncoder* CommandBuffer::getBlitCommandEncoder() {
120    if (fActiveBlitCommandEncoder) {
121        return fActiveBlitCommandEncoder.get();
122    }
123
124    fActiveBlitCommandEncoder = BlitCommandEncoder::Make(fCommandBuffer.get());
125
126    if (!fActiveBlitCommandEncoder) {
127        return nullptr;
128    }
129
130    // We add the ref on the command buffer for the BlitCommandEncoder now so that we don't need
131    // to add a ref for every copy we do.
132    this->trackResource(fActiveBlitCommandEncoder);
133    return fActiveBlitCommandEncoder.get();
134}
135
136void CommandBuffer::endBlitCommandEncoder() {
137    if (fActiveBlitCommandEncoder) {
138        fActiveBlitCommandEncoder->endEncoding();
139        fActiveBlitCommandEncoder.reset();
140    }
141}
142
143void CommandBuffer::onBindGraphicsPipeline(const skgpu::GraphicsPipeline* graphicsPipeline) {
144    SkASSERT(fActiveRenderCommandEncoder);
145
146    auto mtlPipeline = static_cast<const GraphicsPipeline*>(graphicsPipeline);
147    auto pipelineState = mtlPipeline->mtlPipelineState();
148    fActiveRenderCommandEncoder->setRenderPipelineState(pipelineState);
149    fCurrentVertexStride = mtlPipeline->vertexStride();
150    fCurrentInstanceStride = mtlPipeline->instanceStride();
151}
152
153void CommandBuffer::onBindUniformBuffer(const skgpu::Buffer* uniformBuffer,
154                                        size_t uniformOffset) {
155    SkASSERT(fActiveRenderCommandEncoder);
156
157    id<MTLBuffer> mtlBuffer = static_cast<const Buffer*>(uniformBuffer)->mtlBuffer();
158
159    if (fGpu->mtlCaps().isMac()) {
160        SkASSERT((uniformOffset & 0xFF) == 0);
161    } else {
162        SkASSERT((uniformOffset & 0xF) == 0);
163    }
164    fActiveRenderCommandEncoder->setVertexBuffer(mtlBuffer, uniformOffset,
165                                                 GraphicsPipeline::kUniformBufferIndex);
166    fActiveRenderCommandEncoder->setFragmentBuffer(mtlBuffer, uniformOffset,
167                                                   GraphicsPipeline::kUniformBufferIndex);
168}
169
170void CommandBuffer::onBindVertexBuffers(const skgpu::Buffer* vertexBuffer,
171                                        size_t vertexOffset,
172                                        const skgpu::Buffer* instanceBuffer,
173                                        size_t instanceOffset) {
174    SkASSERT(fActiveRenderCommandEncoder);
175
176    if (vertexBuffer) {
177        id<MTLBuffer> mtlBuffer = static_cast<const Buffer*>(vertexBuffer)->mtlBuffer();
178        SkASSERT((vertexOffset & 0xF) == 0);
179        fActiveRenderCommandEncoder->setVertexBuffer(mtlBuffer, vertexOffset,
180                                                     GraphicsPipeline::kVertexBufferIndex);
181    }
182    if (instanceBuffer) {
183        id<MTLBuffer> mtlBuffer = static_cast<const Buffer*>(instanceBuffer)->mtlBuffer();
184        SkASSERT((instanceOffset & 0xF) == 0);
185        fActiveRenderCommandEncoder->setVertexBuffer(mtlBuffer, instanceOffset,
186                                                     GraphicsPipeline::kInstanceBufferIndex);
187    }
188}
189
190void CommandBuffer::onBindIndexBuffer(const skgpu::Buffer* indexBuffer, size_t offset) {
191    if (indexBuffer) {
192        fCurrentIndexBuffer = static_cast<const Buffer*>(indexBuffer)->mtlBuffer();
193        fCurrentIndexBufferOffset = offset;
194    } else {
195        fCurrentIndexBuffer = nil;
196        fCurrentIndexBufferOffset = 0;
197    }
198}
199
200static MTLPrimitiveType graphite_to_mtl_primitive(PrimitiveType primitiveType) {
201    const static MTLPrimitiveType mtlPrimitiveType[] {
202        MTLPrimitiveTypeTriangle,
203        MTLPrimitiveTypeTriangleStrip,
204        MTLPrimitiveTypePoint,
205    };
206    static_assert((int)PrimitiveType::kTriangles == 0);
207    static_assert((int)PrimitiveType::kTriangleStrip == 1);
208    static_assert((int)PrimitiveType::kPoints == 2);
209
210    SkASSERT(primitiveType <= PrimitiveType::kPoints);
211    return mtlPrimitiveType[static_cast<int>(primitiveType)];
212}
213
214void CommandBuffer::onDraw(PrimitiveType type, unsigned int baseVertex, unsigned int vertexCount) {
215    SkASSERT(fActiveRenderCommandEncoder);
216
217    auto mtlPrimitiveType = graphite_to_mtl_primitive(type);
218
219    fActiveRenderCommandEncoder->drawPrimitives(mtlPrimitiveType, baseVertex, vertexCount);
220}
221
222void CommandBuffer::onDrawIndexed(PrimitiveType type, unsigned int baseIndex,
223                                  unsigned int indexCount, unsigned int baseVertex) {
224    SkASSERT(fActiveRenderCommandEncoder);
225
226    auto mtlPrimitiveType = graphite_to_mtl_primitive(type);
227
228    fActiveRenderCommandEncoder->setVertexBufferOffset(baseVertex * fCurrentVertexStride,
229                                                       GraphicsPipeline::kVertexBufferIndex);
230    size_t indexOffset =  fCurrentIndexBufferOffset + sizeof(uint16_t )* baseIndex;
231    fActiveRenderCommandEncoder->drawIndexedPrimitives(mtlPrimitiveType, indexCount,
232                                                       MTLIndexTypeUInt16, fCurrentIndexBuffer,
233                                                       indexOffset);
234}
235
236void CommandBuffer::onDrawInstanced(PrimitiveType type, unsigned int baseVertex,
237                                    unsigned int vertexCount, unsigned int baseInstance,
238                                    unsigned int instanceCount) {
239    SkASSERT(fActiveRenderCommandEncoder);
240
241    auto mtlPrimitiveType = graphite_to_mtl_primitive(type);
242
243    // This ordering is correct
244    fActiveRenderCommandEncoder->drawPrimitives(mtlPrimitiveType, baseVertex, vertexCount,
245                                                instanceCount, baseInstance);
246}
247
248void CommandBuffer::onDrawIndexedInstanced(PrimitiveType type, unsigned int baseIndex,
249                                           unsigned int indexCount, unsigned int baseVertex,
250                                           unsigned int baseInstance, unsigned int instanceCount) {
251    SkASSERT(fActiveRenderCommandEncoder);
252
253    auto mtlPrimitiveType = graphite_to_mtl_primitive(type);
254
255    fActiveRenderCommandEncoder->setVertexBufferOffset(baseVertex * fCurrentVertexStride,
256                                                       GraphicsPipeline::kVertexBufferIndex);
257    fActiveRenderCommandEncoder->setVertexBufferOffset(baseInstance * fCurrentInstanceStride,
258                                                       GraphicsPipeline::kInstanceBufferIndex);
259    size_t indexOffset =  fCurrentIndexBufferOffset + sizeof(uint16_t) * baseIndex;
260    fActiveRenderCommandEncoder->drawIndexedPrimitives(mtlPrimitiveType, indexCount,
261                                                       MTLIndexTypeUInt16, fCurrentIndexBuffer,
262                                                       indexOffset, instanceCount,
263                                                       baseVertex, baseInstance);
264}
265
266void CommandBuffer::onCopyTextureToBuffer(const skgpu::Texture* texture,
267                                          SkIRect srcRect,
268                                          const skgpu::Buffer* buffer,
269                                          size_t bufferOffset,
270                                          size_t bufferRowBytes) {
271    SkASSERT(!fActiveRenderCommandEncoder);
272
273    id<MTLTexture> mtlTexture = static_cast<const Texture*>(texture)->mtlTexture();
274    id<MTLBuffer> mtlBuffer = static_cast<const Buffer*>(buffer)->mtlBuffer();
275
276    BlitCommandEncoder* blitCmdEncoder = this->getBlitCommandEncoder();
277
278#ifdef SK_ENABLE_MTL_DEBUG_INFO
279    blitCmdEncoder->pushDebugGroup(@"readOrTransferPixels");
280#endif
281    blitCmdEncoder->copyFromTexture(mtlTexture, srcRect, mtlBuffer, bufferOffset, bufferRowBytes);
282
283    if (fGpu->mtlCaps().isMac()) {
284#ifdef SK_BUILD_FOR_MAC
285        // Sync GPU data back to the CPU
286        blitCmdEncoder->synchronizeResource(mtlBuffer);
287#endif
288    }
289#ifdef SK_ENABLE_MTL_DEBUG_INFO
290    blitCmdEncoder->popDebugGroup();
291#endif
292}
293
294
295} // namespace skgpu::mtl
296