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