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 "src/gpu/graphite/mtl/MtlCommandBuffer.h" 9 10#include "include/gpu/graphite/BackendSemaphore.h" 11#include "src/gpu/graphite/Log.h" 12#include "src/gpu/graphite/RenderPassDesc.h" 13#include "src/gpu/graphite/TextureProxy.h" 14#include "src/gpu/graphite/compute/DispatchGroup.h" 15#include "src/gpu/graphite/mtl/MtlBlitCommandEncoder.h" 16#include "src/gpu/graphite/mtl/MtlBuffer.h" 17#include "src/gpu/graphite/mtl/MtlCaps.h" 18#include "src/gpu/graphite/mtl/MtlComputeCommandEncoder.h" 19#include "src/gpu/graphite/mtl/MtlComputePipeline.h" 20#include "src/gpu/graphite/mtl/MtlGraphicsPipeline.h" 21#include "src/gpu/graphite/mtl/MtlRenderCommandEncoder.h" 22#include "src/gpu/graphite/mtl/MtlResourceProvider.h" 23#include "src/gpu/graphite/mtl/MtlSampler.h" 24#include "src/gpu/graphite/mtl/MtlSharedContext.h" 25#include "src/gpu/graphite/mtl/MtlTexture.h" 26#include "src/gpu/mtl/MtlUtilsPriv.h" 27 28namespace skgpu::graphite { 29 30std::unique_ptr<MtlCommandBuffer> MtlCommandBuffer::Make(id<MTLCommandQueue> queue, 31 const MtlSharedContext* sharedContext, 32 MtlResourceProvider* resourceProvider) { 33 auto commandBuffer = std::unique_ptr<MtlCommandBuffer>( 34 new MtlCommandBuffer(queue, sharedContext, resourceProvider)); 35 if (!commandBuffer) { 36 return nullptr; 37 } 38 if (!commandBuffer->createNewMTLCommandBuffer()) { 39 return nullptr; 40 } 41 return commandBuffer; 42} 43 44MtlCommandBuffer::MtlCommandBuffer(id<MTLCommandQueue> queue, 45 const MtlSharedContext* sharedContext, 46 MtlResourceProvider* resourceProvider) 47 : fQueue(queue) 48 , fSharedContext(sharedContext) 49 , fResourceProvider(resourceProvider) {} 50 51MtlCommandBuffer::~MtlCommandBuffer() { 52 SkASSERT(!fActiveRenderCommandEncoder); 53 SkASSERT(!fActiveComputeCommandEncoder); 54 SkASSERT(!fActiveBlitCommandEncoder); 55} 56 57bool MtlCommandBuffer::setNewCommandBufferResources() { 58 return this->createNewMTLCommandBuffer(); 59} 60 61bool MtlCommandBuffer::createNewMTLCommandBuffer() { 62 SkASSERT(fCommandBuffer == nil); 63 64 // Inserting a pool here so the autorelease occurs when we return and the 65 // only remaining ref is the retain below. 66 @autoreleasepool { 67 if (@available(macOS 11.0, iOS 14.0, tvOS 14.0, *)) { 68 sk_cfp<MTLCommandBufferDescriptor*> desc([[MTLCommandBufferDescriptor alloc] init]); 69 (*desc).retainedReferences = NO; 70#ifdef SK_ENABLE_MTL_DEBUG_INFO 71 (*desc).errorOptions = MTLCommandBufferErrorOptionEncoderExecutionStatus; 72#endif 73 // We add a retain here because the command buffer is set to autorelease (not alloc or copy) 74 fCommandBuffer.reset([[fQueue commandBufferWithDescriptor:desc.get()] retain]); 75 } else { 76 // We add a retain here because the command buffer is set to autorelease (not alloc or copy) 77 fCommandBuffer.reset([[fQueue commandBufferWithUnretainedReferences] retain]); 78 } 79 } 80 return fCommandBuffer != nil; 81} 82 83bool MtlCommandBuffer::commit() { 84 SkASSERT(!fActiveRenderCommandEncoder); 85 SkASSERT(!fActiveComputeCommandEncoder); 86 this->endBlitCommandEncoder(); 87 [(*fCommandBuffer) commit]; 88 89 if ((*fCommandBuffer).status == MTLCommandBufferStatusError) { 90 NSString* description = (*fCommandBuffer).error.localizedDescription; 91 const char* errorString = [description UTF8String]; 92 SKGPU_LOG_E("Failure submitting command buffer: %s", errorString); 93 } 94 95 return ((*fCommandBuffer).status != MTLCommandBufferStatusError); 96} 97 98void MtlCommandBuffer::onResetCommandBuffer() { 99 fCommandBuffer.reset(); 100 fActiveRenderCommandEncoder.reset(); 101 fActiveComputeCommandEncoder.reset(); 102 fActiveBlitCommandEncoder.reset(); 103 fCurrentIndexBuffer = nil; 104 fCurrentIndexBufferOffset = 0; 105} 106 107void MtlCommandBuffer::addWaitSemaphores(size_t numWaitSemaphores, 108 const BackendSemaphore* waitSemaphores) { 109 if (!waitSemaphores) { 110 SkASSERT(numWaitSemaphores == 0); 111 return; 112 } 113 114 // Can only insert events with no active encoder 115 SkASSERT(!fActiveRenderCommandEncoder); 116 SkASSERT(!fActiveComputeCommandEncoder); 117 this->endBlitCommandEncoder(); 118 if (@available(macOS 10.14, iOS 12.0, tvOS 12.0, *)) { 119 for (size_t i = 0; i < numWaitSemaphores; ++i) { 120 auto semaphore = waitSemaphores[i]; 121 if (semaphore.isValid() && semaphore.backend() == BackendApi::kMetal) { 122 id<MTLEvent> mtlEvent = (__bridge id<MTLEvent>)semaphore.getMtlEvent(); 123 [(*fCommandBuffer) encodeWaitForEvent: mtlEvent 124 value: semaphore.getMtlValue()]; 125 } 126 } 127 } 128} 129 130void MtlCommandBuffer::addSignalSemaphores(size_t numSignalSemaphores, 131 const BackendSemaphore* signalSemaphores) { 132 if (!signalSemaphores) { 133 SkASSERT(numSignalSemaphores == 0); 134 return; 135 } 136 137 // Can only insert events with no active encoder 138 SkASSERT(!fActiveRenderCommandEncoder); 139 SkASSERT(!fActiveComputeCommandEncoder); 140 this->endBlitCommandEncoder(); 141 142 if (@available(macOS 10.14, iOS 12.0, tvOS 12.0, *)) { 143 for (size_t i = 0; i < numSignalSemaphores; ++i) { 144 auto semaphore = signalSemaphores[i]; 145 if (semaphore.isValid() && semaphore.backend() == BackendApi::kMetal) { 146 id<MTLEvent> mtlEvent = (__bridge id<MTLEvent>)semaphore.getMtlEvent(); 147 [(*fCommandBuffer) encodeSignalEvent: mtlEvent 148 value: semaphore.getMtlValue()]; 149 } 150 } 151 } 152} 153 154bool MtlCommandBuffer::onAddRenderPass(const RenderPassDesc& renderPassDesc, 155 const Texture* colorTexture, 156 const Texture* resolveTexture, 157 const Texture* depthStencilTexture, 158 SkRect viewport, 159 const DrawPassList& drawPasses) { 160 if (!this->beginRenderPass(renderPassDesc, colorTexture, resolveTexture, depthStencilTexture)) { 161 return false; 162 } 163 164 this->setViewport(viewport.x(), viewport.y(), viewport.width(), viewport.height(), 0, 1); 165 166 for (const auto& drawPass : drawPasses) { 167 this->addDrawPass(drawPass.get()); 168 } 169 170 this->endRenderPass(); 171 return true; 172} 173 174bool MtlCommandBuffer::onAddComputePass(DispatchGroupSpan groups) { 175 this->beginComputePass(); 176 for (const auto& group : groups) { 177 group->addResourceRefs(this); 178 for (const auto& dispatch : group->dispatches()) { 179 this->bindComputePipeline(group->getPipeline(dispatch.fPipelineIndex)); 180 for (const ResourceBinding& binding : dispatch.fBindings) { 181 if (const BufferView* buffer = std::get_if<BufferView>(&binding.fResource)) { 182 this->bindBuffer(buffer->fInfo.fBuffer, buffer->fInfo.fOffset, binding.fIndex); 183 } else if (const TextureIndex* texIdx = 184 std::get_if<TextureIndex>(&binding.fResource)) { 185 SkASSERT(texIdx); 186 this->bindTexture(group->getTexture(texIdx->fValue), binding.fIndex); 187 } else { 188 const SamplerIndex* samplerIdx = std::get_if<SamplerIndex>(&binding.fResource); 189 SkASSERT(samplerIdx); 190 this->bindSampler(group->getSampler(samplerIdx->fValue), binding.fIndex); 191 } 192 } 193 SkASSERT(fActiveComputeCommandEncoder); 194 for (const ComputeStep::WorkgroupBufferDesc& wgBuf : dispatch.fWorkgroupBuffers) { 195 fActiveComputeCommandEncoder->setThreadgroupMemoryLength( 196 SkAlignTo(wgBuf.size, 16), 197 wgBuf.index); 198 } 199 if (const WorkgroupSize* globalSize = 200 std::get_if<WorkgroupSize>(&dispatch.fGlobalSizeOrIndirect)) { 201 this->dispatchThreadgroups(*globalSize, dispatch.fLocalSize); 202 } else { 203 SkASSERT(std::holds_alternative<BufferView>(dispatch.fGlobalSizeOrIndirect)); 204 const BufferView& indirect = 205 *std::get_if<BufferView>(&dispatch.fGlobalSizeOrIndirect); 206 this->dispatchThreadgroupsIndirect( 207 dispatch.fLocalSize, indirect.fInfo.fBuffer, indirect.fInfo.fOffset); 208 } 209 } 210 } 211 this->endComputePass(); 212 return true; 213} 214 215bool MtlCommandBuffer::beginRenderPass(const RenderPassDesc& renderPassDesc, 216 const Texture* colorTexture, 217 const Texture* resolveTexture, 218 const Texture* depthStencilTexture) { 219 SkASSERT(!fActiveRenderCommandEncoder); 220 SkASSERT(!fActiveComputeCommandEncoder); 221 this->endBlitCommandEncoder(); 222 223 const static MTLLoadAction mtlLoadAction[] { 224 MTLLoadActionLoad, 225 MTLLoadActionClear, 226 MTLLoadActionDontCare 227 }; 228 static_assert((int)LoadOp::kLoad == 0); 229 static_assert((int)LoadOp::kClear == 1); 230 static_assert((int)LoadOp::kDiscard == 2); 231 static_assert(std::size(mtlLoadAction) == kLoadOpCount); 232 233 const static MTLStoreAction mtlStoreAction[] { 234 MTLStoreActionStore, 235 MTLStoreActionDontCare 236 }; 237 static_assert((int)StoreOp::kStore == 0); 238 static_assert((int)StoreOp::kDiscard == 1); 239 static_assert(std::size(mtlStoreAction) == kStoreOpCount); 240 241 sk_cfp<MTLRenderPassDescriptor*> descriptor([[MTLRenderPassDescriptor alloc] init]); 242 // Set up color attachment. 243 auto& colorInfo = renderPassDesc.fColorAttachment; 244 bool loadMSAAFromResolve = false; 245 if (colorTexture) { 246 // TODO: check Texture matches RenderPassDesc 247 auto colorAttachment = (*descriptor).colorAttachments[0]; 248 colorAttachment.texture = ((const MtlTexture*)colorTexture)->mtlTexture(); 249 const std::array<float, 4>& clearColor = renderPassDesc.fClearColor; 250 colorAttachment.clearColor = 251 MTLClearColorMake(clearColor[0], clearColor[1], clearColor[2], clearColor[3]); 252 colorAttachment.loadAction = mtlLoadAction[static_cast<int>(colorInfo.fLoadOp)]; 253 colorAttachment.storeAction = mtlStoreAction[static_cast<int>(colorInfo.fStoreOp)]; 254 // Set up resolve attachment 255 if (resolveTexture) { 256 SkASSERT(renderPassDesc.fColorResolveAttachment.fStoreOp == StoreOp::kStore); 257 // TODO: check Texture matches RenderPassDesc 258 colorAttachment.resolveTexture = ((const MtlTexture*)resolveTexture)->mtlTexture(); 259 // Inclusion of a resolve texture implies the client wants to finish the 260 // renderpass with a resolve. 261 if (@available(macOS 10.12, iOS 10.0, tvOS 10.0, *)) { 262 SkASSERT(colorAttachment.storeAction == MTLStoreActionDontCare); 263 colorAttachment.storeAction = MTLStoreActionMultisampleResolve; 264 } else { 265 // We expect at least Metal 2 266 // TODO: Add error output 267 SkASSERT(false); 268 } 269 // But it also means we have to load the resolve texture into the MSAA color attachment 270 loadMSAAFromResolve = renderPassDesc.fColorResolveAttachment.fLoadOp == LoadOp::kLoad; 271 // TODO: If the color resolve texture is read-only we can use a private (vs. memoryless) 272 // msaa attachment that's coupled to the framebuffer and the StoreAndMultisampleResolve 273 // action instead of loading as a draw. 274 } 275 } 276 277 // Set up stencil/depth attachment 278 auto& depthStencilInfo = renderPassDesc.fDepthStencilAttachment; 279 if (depthStencilTexture) { 280 // TODO: check Texture matches RenderPassDesc 281 id<MTLTexture> mtlTexture = ((const MtlTexture*)depthStencilTexture)->mtlTexture(); 282 if (MtlFormatIsDepth(mtlTexture.pixelFormat)) { 283 auto depthAttachment = (*descriptor).depthAttachment; 284 depthAttachment.texture = mtlTexture; 285 depthAttachment.clearDepth = renderPassDesc.fClearDepth; 286 depthAttachment.loadAction = 287 mtlLoadAction[static_cast<int>(depthStencilInfo.fLoadOp)]; 288 depthAttachment.storeAction = 289 mtlStoreAction[static_cast<int>(depthStencilInfo.fStoreOp)]; 290 } 291 if (MtlFormatIsStencil(mtlTexture.pixelFormat)) { 292 auto stencilAttachment = (*descriptor).stencilAttachment; 293 stencilAttachment.texture = mtlTexture; 294 stencilAttachment.clearStencil = renderPassDesc.fClearStencil; 295 stencilAttachment.loadAction = 296 mtlLoadAction[static_cast<int>(depthStencilInfo.fLoadOp)]; 297 stencilAttachment.storeAction = 298 mtlStoreAction[static_cast<int>(depthStencilInfo.fStoreOp)]; 299 } 300 } else { 301 SkASSERT(!depthStencilInfo.fTextureInfo.isValid()); 302 } 303 304 fActiveRenderCommandEncoder = MtlRenderCommandEncoder::Make(fSharedContext, 305 fCommandBuffer.get(), 306 descriptor.get()); 307 this->trackResource(fActiveRenderCommandEncoder); 308 309 if (loadMSAAFromResolve) { 310 // Manually load the contents of the resolve texture into the MSAA attachment as a draw, 311 // so the actual load op for the MSAA attachment had better have been discard. 312 SkASSERT(colorInfo.fLoadOp == LoadOp::kDiscard); 313 auto loadPipeline = fResourceProvider->findOrCreateLoadMSAAPipeline(renderPassDesc); 314 if (!loadPipeline) { 315 SKGPU_LOG_E("Unable to create pipeline to load resolve texture into MSAA attachment"); 316 return false; 317 } 318 this->bindGraphicsPipeline(loadPipeline.get()); 319 // The load msaa pipeline takes no uniforms, no vertex/instance attributes and only uses 320 // one texture that does not require a sampler. 321 fActiveRenderCommandEncoder->setFragmentTexture( 322 ((const MtlTexture*) resolveTexture)->mtlTexture(), 0); 323 this->draw(PrimitiveType::kTriangleStrip, 0, 4); 324 } 325 326 return true; 327} 328 329void MtlCommandBuffer::endRenderPass() { 330 SkASSERT(fActiveRenderCommandEncoder); 331 fActiveRenderCommandEncoder->endEncoding(); 332 fActiveRenderCommandEncoder.reset(); 333 fDrawIsOffscreen = false; 334} 335 336void MtlCommandBuffer::addDrawPass(const DrawPass* drawPass) { 337 SkIRect replayPassBounds = drawPass->bounds().makeOffset(fReplayTranslation.x(), 338 fReplayTranslation.y()); 339 if (!SkIRect::Intersects(replayPassBounds, SkIRect::MakeSize(fRenderPassSize))) { 340 // The entire DrawPass is offscreen given the replay translation so skip adding any 341 // commands. When the DrawPass is partially offscreen individual draw commands will be 342 // culled while preserving state changing commands. 343 return; 344 } 345 346 drawPass->addResourceRefs(this); 347 348 for (auto[type, cmdPtr] : drawPass->commands()) { 349 // Skip draw commands if they'd be offscreen. 350 if (fDrawIsOffscreen) { 351 switch (type) { 352 case DrawPassCommands::Type::kDraw: 353 case DrawPassCommands::Type::kDrawIndexed: 354 case DrawPassCommands::Type::kDrawInstanced: 355 case DrawPassCommands::Type::kDrawIndexedInstanced: 356 continue; 357 default: 358 break; 359 } 360 } 361 362 switch (type) { 363 case DrawPassCommands::Type::kBindGraphicsPipeline: { 364 auto bgp = static_cast<DrawPassCommands::BindGraphicsPipeline*>(cmdPtr); 365 this->bindGraphicsPipeline(drawPass->getPipeline(bgp->fPipelineIndex)); 366 break; 367 } 368 case DrawPassCommands::Type::kSetBlendConstants: { 369 auto sbc = static_cast<DrawPassCommands::SetBlendConstants*>(cmdPtr); 370 this->setBlendConstants(sbc->fBlendConstants); 371 break; 372 } 373 case DrawPassCommands::Type::kBindUniformBuffer: { 374 auto bub = static_cast<DrawPassCommands::BindUniformBuffer*>(cmdPtr); 375 this->bindUniformBuffer(bub->fInfo, bub->fSlot); 376 break; 377 } 378 case DrawPassCommands::Type::kBindDrawBuffers: { 379 auto bdb = static_cast<DrawPassCommands::BindDrawBuffers*>(cmdPtr); 380 this->bindDrawBuffers( 381 bdb->fVertices, bdb->fInstances, bdb->fIndices, bdb->fIndirect); 382 break; 383 } 384 case DrawPassCommands::Type::kBindTexturesAndSamplers: { 385 auto bts = static_cast<DrawPassCommands::BindTexturesAndSamplers*>(cmdPtr); 386 for (int j = 0; j < bts->fNumTexSamplers; ++j) { 387 this->bindTextureAndSampler(drawPass->getTexture(bts->fTextureIndices[j]), 388 drawPass->getSampler(bts->fSamplerIndices[j]), 389 j); 390 } 391 break; 392 } 393 case DrawPassCommands::Type::kSetScissor: { 394 auto ss = static_cast<DrawPassCommands::SetScissor*>(cmdPtr); 395 const SkIRect& rect = ss->fScissor; 396 this->setScissor(rect.fLeft, rect.fTop, rect.width(), rect.height()); 397 break; 398 } 399 case DrawPassCommands::Type::kDraw: { 400 auto draw = static_cast<DrawPassCommands::Draw*>(cmdPtr); 401 this->draw(draw->fType, draw->fBaseVertex, draw->fVertexCount); 402 break; 403 } 404 case DrawPassCommands::Type::kDrawIndexed: { 405 auto draw = static_cast<DrawPassCommands::DrawIndexed*>(cmdPtr); 406 this->drawIndexed(draw->fType, 407 draw->fBaseIndex, 408 draw->fIndexCount, 409 draw->fBaseVertex); 410 break; 411 } 412 case DrawPassCommands::Type::kDrawInstanced: { 413 auto draw = static_cast<DrawPassCommands::DrawInstanced*>(cmdPtr); 414 this->drawInstanced(draw->fType, 415 draw->fBaseVertex, 416 draw->fVertexCount, 417 draw->fBaseInstance, 418 draw->fInstanceCount); 419 break; 420 } 421 case DrawPassCommands::Type::kDrawIndexedInstanced: { 422 auto draw = static_cast<DrawPassCommands::DrawIndexedInstanced*>(cmdPtr); 423 this->drawIndexedInstanced(draw->fType, 424 draw->fBaseIndex, 425 draw->fIndexCount, 426 draw->fBaseVertex, 427 draw->fBaseInstance, 428 draw->fInstanceCount); 429 break; 430 } 431 case DrawPassCommands::Type::kDrawIndirect: { 432 auto draw = static_cast<DrawPassCommands::DrawIndirect*>(cmdPtr); 433 this->drawIndirect(draw->fType); 434 break; 435 } 436 case DrawPassCommands::Type::kDrawIndexedIndirect: { 437 auto draw = static_cast<DrawPassCommands::DrawIndexedIndirect*>(cmdPtr); 438 this->drawIndexedIndirect(draw->fType); 439 break; 440 } 441 } 442 } 443} 444 445MtlBlitCommandEncoder* MtlCommandBuffer::getBlitCommandEncoder() { 446 if (fActiveBlitCommandEncoder) { 447 return fActiveBlitCommandEncoder.get(); 448 } 449 450 fActiveBlitCommandEncoder = MtlBlitCommandEncoder::Make(fSharedContext, fCommandBuffer.get()); 451 452 if (!fActiveBlitCommandEncoder) { 453 return nullptr; 454 } 455 456 // We add the ref on the command buffer for the BlitCommandEncoder now so that we don't need 457 // to add a ref for every copy we do. 458 this->trackResource(fActiveBlitCommandEncoder); 459 return fActiveBlitCommandEncoder.get(); 460} 461 462void MtlCommandBuffer::endBlitCommandEncoder() { 463 if (fActiveBlitCommandEncoder) { 464 fActiveBlitCommandEncoder->endEncoding(); 465 fActiveBlitCommandEncoder.reset(); 466 } 467} 468 469void MtlCommandBuffer::bindGraphicsPipeline(const GraphicsPipeline* graphicsPipeline) { 470 SkASSERT(fActiveRenderCommandEncoder); 471 472 auto mtlPipeline = static_cast<const MtlGraphicsPipeline*>(graphicsPipeline); 473 auto pipelineState = mtlPipeline->mtlPipelineState(); 474 fActiveRenderCommandEncoder->setRenderPipelineState(pipelineState); 475 auto depthStencilState = mtlPipeline->mtlDepthStencilState(); 476 fActiveRenderCommandEncoder->setDepthStencilState(depthStencilState); 477 uint32_t stencilRefValue = mtlPipeline->stencilReferenceValue(); 478 fActiveRenderCommandEncoder->setStencilReferenceValue(stencilRefValue); 479} 480 481void MtlCommandBuffer::bindUniformBuffer(const BindBufferInfo& info, UniformSlot slot) { 482 SkASSERT(fActiveRenderCommandEncoder); 483 484 id<MTLBuffer> mtlBuffer = info.fBuffer ? 485 static_cast<const MtlBuffer*>(info.fBuffer)->mtlBuffer() : nullptr; 486 487 unsigned int bufferIndex; 488 switch(slot) { 489 case UniformSlot::kRenderStep: 490 bufferIndex = MtlGraphicsPipeline::kRenderStepUniformBufferIndex; 491 break; 492 case UniformSlot::kPaint: 493 bufferIndex = MtlGraphicsPipeline::kPaintUniformBufferIndex; 494 break; 495 case UniformSlot::kGradient: 496 bufferIndex = MtlGraphicsPipeline::kGradientBufferIndex; 497 break; 498 } 499 500 fActiveRenderCommandEncoder->setVertexBuffer(mtlBuffer, info.fOffset, bufferIndex); 501 fActiveRenderCommandEncoder->setFragmentBuffer(mtlBuffer, info.fOffset, bufferIndex); 502} 503 504void MtlCommandBuffer::bindDrawBuffers(const BindBufferInfo& vertices, 505 const BindBufferInfo& instances, 506 const BindBufferInfo& indices, 507 const BindBufferInfo& indirect) { 508 this->bindVertexBuffers(vertices.fBuffer, 509 vertices.fOffset, 510 instances.fBuffer, 511 instances.fOffset); 512 this->bindIndexBuffer(indices.fBuffer, indices.fOffset); 513 this->bindIndirectBuffer(indirect.fBuffer, indirect.fOffset); 514} 515 516void MtlCommandBuffer::bindVertexBuffers(const Buffer* vertexBuffer, 517 size_t vertexOffset, 518 const Buffer* instanceBuffer, 519 size_t instanceOffset) { 520 SkASSERT(fActiveRenderCommandEncoder); 521 522 if (vertexBuffer) { 523 id<MTLBuffer> mtlBuffer = static_cast<const MtlBuffer*>(vertexBuffer)->mtlBuffer(); 524 // Metal requires buffer offsets to be aligned to the data type, which is at most 4 bytes 525 // since we use [[attribute]] to automatically unpack float components into SIMD arrays. 526 SkASSERT((vertexOffset & 0b11) == 0); 527 fActiveRenderCommandEncoder->setVertexBuffer(mtlBuffer, vertexOffset, 528 MtlGraphicsPipeline::kVertexBufferIndex); 529 } 530 if (instanceBuffer) { 531 id<MTLBuffer> mtlBuffer = static_cast<const MtlBuffer*>(instanceBuffer)->mtlBuffer(); 532 SkASSERT((instanceOffset & 0b11) == 0); 533 fActiveRenderCommandEncoder->setVertexBuffer(mtlBuffer, instanceOffset, 534 MtlGraphicsPipeline::kInstanceBufferIndex); 535 } 536} 537 538void MtlCommandBuffer::bindIndexBuffer(const Buffer* indexBuffer, size_t offset) { 539 if (indexBuffer) { 540 fCurrentIndexBuffer = static_cast<const MtlBuffer*>(indexBuffer)->mtlBuffer(); 541 fCurrentIndexBufferOffset = offset; 542 } else { 543 fCurrentIndexBuffer = nil; 544 fCurrentIndexBufferOffset = 0; 545 } 546} 547 548void MtlCommandBuffer::bindIndirectBuffer(const Buffer* indirectBuffer, size_t offset) { 549 if (indirectBuffer) { 550 fCurrentIndirectBuffer = static_cast<const MtlBuffer*>(indirectBuffer)->mtlBuffer(); 551 fCurrentIndirectBufferOffset = offset; 552 } else { 553 fCurrentIndirectBuffer = nil; 554 fCurrentIndirectBufferOffset = 0; 555 } 556} 557 558void MtlCommandBuffer::bindTextureAndSampler(const Texture* texture, 559 const Sampler* sampler, 560 unsigned int bindIndex) { 561 SkASSERT(texture && sampler); 562 SkASSERT(fActiveRenderCommandEncoder); 563 564 id<MTLTexture> mtlTexture = ((const MtlTexture*)texture)->mtlTexture(); 565 id<MTLSamplerState> mtlSamplerState = ((const MtlSampler*)sampler)->mtlSamplerState(); 566 fActiveRenderCommandEncoder->setFragmentTexture(mtlTexture, bindIndex); 567 fActiveRenderCommandEncoder->setFragmentSamplerState(mtlSamplerState, bindIndex); 568} 569 570void MtlCommandBuffer::setScissor(unsigned int left, unsigned int top, 571 unsigned int width, unsigned int height) { 572 SkASSERT(fActiveRenderCommandEncoder); 573 SkIRect scissor = SkIRect::MakeXYWH( 574 left + fReplayTranslation.x(), top + fReplayTranslation.y(), width, height); 575 fDrawIsOffscreen = !scissor.intersect(SkIRect::MakeSize(fRenderPassSize)); 576 if (fDrawIsOffscreen) { 577 scissor.setEmpty(); 578 } 579 580 fActiveRenderCommandEncoder->setScissorRect({ 581 static_cast<unsigned int>(scissor.x()), 582 static_cast<unsigned int>(scissor.y()), 583 static_cast<unsigned int>(scissor.width()), 584 static_cast<unsigned int>(scissor.height()), 585 }); 586} 587 588void MtlCommandBuffer::setViewport(float x, float y, float width, float height, 589 float minDepth, float maxDepth) { 590 SkASSERT(fActiveRenderCommandEncoder); 591 MTLViewport viewport = {x + fReplayTranslation.x(), 592 y + fReplayTranslation.y(), 593 width, 594 height, 595 minDepth, 596 maxDepth}; 597 fActiveRenderCommandEncoder->setViewport(viewport); 598 599 float invTwoW = 2.f / width; 600 float invTwoH = 2.f / height; 601 // Metal's framebuffer space has (0, 0) at the top left. This agrees with Skia's device coords. 602 // However, in NDC (-1, -1) is the bottom left. So we flip the origin here (assuming all 603 // surfaces we have are TopLeft origin). 604 float rtAdjust[4] = {invTwoW, -invTwoH, -1.f - x * invTwoW, 1.f + y * invTwoH}; 605 fActiveRenderCommandEncoder->setVertexBytes(rtAdjust, 4 * sizeof(float), 606 MtlGraphicsPipeline::kIntrinsicUniformBufferIndex); 607} 608 609void MtlCommandBuffer::setBlendConstants(float* blendConstants) { 610 SkASSERT(fActiveRenderCommandEncoder); 611 612 fActiveRenderCommandEncoder->setBlendColor(blendConstants); 613} 614 615static MTLPrimitiveType graphite_to_mtl_primitive(PrimitiveType primitiveType) { 616 const static MTLPrimitiveType mtlPrimitiveType[] { 617 MTLPrimitiveTypeTriangle, 618 MTLPrimitiveTypeTriangleStrip, 619 MTLPrimitiveTypePoint, 620 }; 621 static_assert((int)PrimitiveType::kTriangles == 0); 622 static_assert((int)PrimitiveType::kTriangleStrip == 1); 623 static_assert((int)PrimitiveType::kPoints == 2); 624 625 SkASSERT(primitiveType <= PrimitiveType::kPoints); 626 return mtlPrimitiveType[static_cast<int>(primitiveType)]; 627} 628 629void MtlCommandBuffer::draw(PrimitiveType type, 630 unsigned int baseVertex, 631 unsigned int vertexCount) { 632 SkASSERT(fActiveRenderCommandEncoder); 633 634 auto mtlPrimitiveType = graphite_to_mtl_primitive(type); 635 636 fActiveRenderCommandEncoder->drawPrimitives(mtlPrimitiveType, baseVertex, vertexCount); 637} 638 639void MtlCommandBuffer::drawIndexed(PrimitiveType type, unsigned int baseIndex, 640 unsigned int indexCount, unsigned int baseVertex) { 641 SkASSERT(fActiveRenderCommandEncoder); 642 643 if (@available(macOS 10.11, iOS 9.0, tvOS 9.0, *)) { 644 auto mtlPrimitiveType = graphite_to_mtl_primitive(type); 645 size_t indexOffset = fCurrentIndexBufferOffset + sizeof(uint16_t )* baseIndex; 646 // Use the "instance" variant witha count of 1 so that we can pass in a base vertex 647 // instead of rebinding a vertex buffer offset. 648 fActiveRenderCommandEncoder->drawIndexedPrimitives(mtlPrimitiveType, indexCount, 649 MTLIndexTypeUInt16, fCurrentIndexBuffer, 650 indexOffset, 1, baseVertex, 0); 651 652 } else { 653 SKGPU_LOG_E("Skipping unsupported draw call."); 654 } 655} 656 657void MtlCommandBuffer::drawInstanced(PrimitiveType type, unsigned int baseVertex, 658 unsigned int vertexCount, unsigned int baseInstance, 659 unsigned int instanceCount) { 660 SkASSERT(fActiveRenderCommandEncoder); 661 662 auto mtlPrimitiveType = graphite_to_mtl_primitive(type); 663 664 // This ordering is correct 665 fActiveRenderCommandEncoder->drawPrimitives(mtlPrimitiveType, baseVertex, vertexCount, 666 instanceCount, baseInstance); 667} 668 669void MtlCommandBuffer::drawIndexedInstanced(PrimitiveType type, 670 unsigned int baseIndex, 671 unsigned int indexCount, 672 unsigned int baseVertex, 673 unsigned int baseInstance, 674 unsigned int instanceCount) { 675 SkASSERT(fActiveRenderCommandEncoder); 676 677 if (@available(macOS 10.11, iOS 9.0, tvOS 9.0, *)) { 678 auto mtlPrimitiveType = graphite_to_mtl_primitive(type); 679 size_t indexOffset = fCurrentIndexBufferOffset + sizeof(uint16_t) * baseIndex; 680 fActiveRenderCommandEncoder->drawIndexedPrimitives(mtlPrimitiveType, indexCount, 681 MTLIndexTypeUInt16, fCurrentIndexBuffer, 682 indexOffset, instanceCount, 683 baseVertex, baseInstance); 684 } else { 685 SKGPU_LOG_E("Skipping unsupported draw call."); 686 } 687} 688 689void MtlCommandBuffer::drawIndirect(PrimitiveType type) { 690 SkASSERT(fActiveRenderCommandEncoder); 691 SkASSERT(fCurrentIndirectBuffer); 692 693 if (@available(macOS 10.11, iOS 9.0, tvOS 9.0, *)) { 694 auto mtlPrimitiveType = graphite_to_mtl_primitive(type); 695 fActiveRenderCommandEncoder->drawPrimitives( 696 mtlPrimitiveType, fCurrentIndirectBuffer, fCurrentIndirectBufferOffset); 697 } else { 698 SKGPU_LOG_E("Skipping unsupported draw call."); 699 } 700} 701 702void MtlCommandBuffer::drawIndexedIndirect(PrimitiveType type) { 703 SkASSERT(fActiveRenderCommandEncoder); 704 SkASSERT(fCurrentIndirectBuffer); 705 706 if (@available(macOS 10.11, iOS 9.0, tvOS 9.0, *)) { 707 auto mtlPrimitiveType = graphite_to_mtl_primitive(type); 708 fActiveRenderCommandEncoder->drawIndexedPrimitives(mtlPrimitiveType, 709 MTLIndexTypeUInt32, 710 fCurrentIndexBuffer, 711 fCurrentIndexBufferOffset, 712 fCurrentIndirectBuffer, 713 fCurrentIndirectBufferOffset); 714 } else { 715 SKGPU_LOG_E("Skipping unsupported draw call."); 716 } 717} 718 719void MtlCommandBuffer::beginComputePass() { 720 SkASSERT(!fActiveRenderCommandEncoder); 721 SkASSERT(!fActiveComputeCommandEncoder); 722 this->endBlitCommandEncoder(); 723 fActiveComputeCommandEncoder = MtlComputeCommandEncoder::Make(fSharedContext, 724 fCommandBuffer.get()); 725} 726 727void MtlCommandBuffer::bindComputePipeline(const ComputePipeline* computePipeline) { 728 SkASSERT(fActiveComputeCommandEncoder); 729 730 auto mtlPipeline = static_cast<const MtlComputePipeline*>(computePipeline); 731 fActiveComputeCommandEncoder->setComputePipelineState(mtlPipeline->mtlPipelineState()); 732} 733 734void MtlCommandBuffer::bindBuffer(const Buffer* buffer, unsigned int offset, unsigned int index) { 735 SkASSERT(fActiveComputeCommandEncoder); 736 737 id<MTLBuffer> mtlBuffer = buffer ? static_cast<const MtlBuffer*>(buffer)->mtlBuffer() : nil; 738 fActiveComputeCommandEncoder->setBuffer(mtlBuffer, offset, index); 739} 740 741void MtlCommandBuffer::bindTexture(const Texture* texture, unsigned int index) { 742 SkASSERT(fActiveComputeCommandEncoder); 743 744 id<MTLTexture> mtlTexture = 745 texture ? static_cast<const MtlTexture*>(texture)->mtlTexture() : nil; 746 fActiveComputeCommandEncoder->setTexture(mtlTexture, index); 747} 748 749void MtlCommandBuffer::bindSampler(const Sampler* sampler, unsigned int index) { 750 SkASSERT(fActiveComputeCommandEncoder); 751 752 id<MTLSamplerState> mtlSamplerState = 753 sampler ? static_cast<const MtlSampler*>(sampler)->mtlSamplerState() : nil; 754 fActiveComputeCommandEncoder->setSamplerState(mtlSamplerState, index); 755} 756 757void MtlCommandBuffer::dispatchThreadgroups(const WorkgroupSize& globalSize, 758 const WorkgroupSize& localSize) { 759 SkASSERT(fActiveComputeCommandEncoder); 760 fActiveComputeCommandEncoder->dispatchThreadgroups(globalSize, localSize); 761} 762 763void MtlCommandBuffer::dispatchThreadgroupsIndirect(const WorkgroupSize& localSize, 764 const Buffer* indirectBuffer, 765 size_t indirectBufferOffset) { 766 SkASSERT(fActiveComputeCommandEncoder); 767 768 id<MTLBuffer> mtlIndirectBuffer = static_cast<const MtlBuffer*>(indirectBuffer)->mtlBuffer(); 769 fActiveComputeCommandEncoder->dispatchThreadgroupsWithIndirectBuffer( 770 mtlIndirectBuffer, indirectBufferOffset, localSize); 771} 772 773void MtlCommandBuffer::endComputePass() { 774 SkASSERT(fActiveComputeCommandEncoder); 775 fActiveComputeCommandEncoder->endEncoding(); 776 fActiveComputeCommandEncoder.reset(); 777} 778 779static bool check_max_blit_width(int widthInPixels) { 780 if (widthInPixels > 32767) { 781 SkASSERT(false); // surfaces should not be this wide anyway 782 return false; 783 } 784 return true; 785} 786 787bool MtlCommandBuffer::onCopyBufferToBuffer(const Buffer* srcBuffer, 788 size_t srcOffset, 789 const Buffer* dstBuffer, 790 size_t dstOffset, 791 size_t size) { 792 SkASSERT(!fActiveRenderCommandEncoder); 793 SkASSERT(!fActiveComputeCommandEncoder); 794 795 id<MTLBuffer> mtlSrcBuffer = static_cast<const MtlBuffer*>(srcBuffer)->mtlBuffer(); 796 id<MTLBuffer> mtlDstBuffer = static_cast<const MtlBuffer*>(dstBuffer)->mtlBuffer(); 797 798 MtlBlitCommandEncoder* blitCmdEncoder = this->getBlitCommandEncoder(); 799 if (!blitCmdEncoder) { 800 return false; 801 } 802 803#ifdef SK_ENABLE_MTL_DEBUG_INFO 804 blitCmdEncoder->pushDebugGroup(@"copyBufferToBuffer"); 805#endif 806 blitCmdEncoder->copyBufferToBuffer(mtlSrcBuffer, srcOffset, mtlDstBuffer, dstOffset, size); 807#ifdef SK_ENABLE_MTL_DEBUG_INFO 808 blitCmdEncoder->popDebugGroup(); 809#endif 810 return true; 811} 812 813bool MtlCommandBuffer::onCopyTextureToBuffer(const Texture* texture, 814 SkIRect srcRect, 815 const Buffer* buffer, 816 size_t bufferOffset, 817 size_t bufferRowBytes) { 818 SkASSERT(!fActiveRenderCommandEncoder); 819 SkASSERT(!fActiveComputeCommandEncoder); 820 821 if (!check_max_blit_width(srcRect.width())) { 822 return false; 823 } 824 825 id<MTLTexture> mtlTexture = static_cast<const MtlTexture*>(texture)->mtlTexture(); 826 id<MTLBuffer> mtlBuffer = static_cast<const MtlBuffer*>(buffer)->mtlBuffer(); 827 828 MtlBlitCommandEncoder* blitCmdEncoder = this->getBlitCommandEncoder(); 829 if (!blitCmdEncoder) { 830 return false; 831 } 832 833#ifdef SK_ENABLE_MTL_DEBUG_INFO 834 blitCmdEncoder->pushDebugGroup(@"copyTextureToBuffer"); 835#endif 836 blitCmdEncoder->copyFromTexture(mtlTexture, srcRect, mtlBuffer, bufferOffset, bufferRowBytes); 837#ifdef SK_ENABLE_MTL_DEBUG_INFO 838 blitCmdEncoder->popDebugGroup(); 839#endif 840 return true; 841} 842 843bool MtlCommandBuffer::onCopyBufferToTexture(const Buffer* buffer, 844 const Texture* texture, 845 const BufferTextureCopyData* copyData, 846 int count) { 847 SkASSERT(!fActiveRenderCommandEncoder); 848 SkASSERT(!fActiveComputeCommandEncoder); 849 850 id<MTLBuffer> mtlBuffer = static_cast<const MtlBuffer*>(buffer)->mtlBuffer(); 851 id<MTLTexture> mtlTexture = static_cast<const MtlTexture*>(texture)->mtlTexture(); 852 853 MtlBlitCommandEncoder* blitCmdEncoder = this->getBlitCommandEncoder(); 854 if (!blitCmdEncoder) { 855 return false; 856 } 857 858#ifdef SK_ENABLE_MTL_DEBUG_INFO 859 blitCmdEncoder->pushDebugGroup(@"copyBufferToTexture"); 860#endif 861 for (int i = 0; i < count; ++i) { 862 if (!check_max_blit_width(copyData[i].fRect.width())) { 863 return false; 864 } 865 866 blitCmdEncoder->copyFromBuffer(mtlBuffer, 867 copyData[i].fBufferOffset, 868 copyData[i].fBufferRowBytes, 869 mtlTexture, 870 copyData[i].fRect, 871 copyData[i].fMipLevel); 872 } 873 874#ifdef SK_ENABLE_MTL_DEBUG_INFO 875 blitCmdEncoder->popDebugGroup(); 876#endif 877 return true; 878} 879 880bool MtlCommandBuffer::onCopyTextureToTexture(const Texture* src, 881 SkIRect srcRect, 882 const Texture* dst, 883 SkIPoint dstPoint, 884 int mipLevel) { 885 SkASSERT(!fActiveRenderCommandEncoder); 886 SkASSERT(!fActiveComputeCommandEncoder); 887 888 id<MTLTexture> srcMtlTexture = static_cast<const MtlTexture*>(src)->mtlTexture(); 889 id<MTLTexture> dstMtlTexture = static_cast<const MtlTexture*>(dst)->mtlTexture(); 890 891 MtlBlitCommandEncoder* blitCmdEncoder = this->getBlitCommandEncoder(); 892 if (!blitCmdEncoder) { 893 return false; 894 } 895 896#ifdef SK_ENABLE_MTL_DEBUG_INFO 897 blitCmdEncoder->pushDebugGroup(@"copyTextureToTexture"); 898#endif 899 900 blitCmdEncoder->copyTextureToTexture(srcMtlTexture, srcRect, dstMtlTexture, dstPoint, mipLevel); 901 902#ifdef SK_ENABLE_MTL_DEBUG_INFO 903 blitCmdEncoder->popDebugGroup(); 904#endif 905 return true; 906} 907 908bool MtlCommandBuffer::onSynchronizeBufferToCpu(const Buffer* buffer, bool* outDidResultInWork) { 909#ifdef SK_BUILD_FOR_MAC 910 SkASSERT(!fActiveRenderCommandEncoder); 911 SkASSERT(!fActiveComputeCommandEncoder); 912 913 id<MTLBuffer> mtlBuffer = static_cast<const MtlBuffer*>(buffer)->mtlBuffer(); 914 if ([mtlBuffer storageMode] != MTLStorageModeManaged) { 915 *outDidResultInWork = false; 916 return true; 917 } 918 919 MtlBlitCommandEncoder* blitCmdEncoder = this->getBlitCommandEncoder(); 920 if (!blitCmdEncoder) { 921 return false; 922 } 923 924#ifdef SK_ENABLE_MTL_DEBUG_INFO 925 blitCmdEncoder->pushDebugGroup(@"synchronizeToCpu"); 926#endif 927 blitCmdEncoder->synchronizeResource(mtlBuffer); 928#ifdef SK_ENABLE_MTL_DEBUG_INFO 929 blitCmdEncoder->popDebugGroup(); 930#endif 931 932 *outDidResultInWork = true; 933 return true; 934#else // SK_BUILD_FOR_MAC 935 // Explicit synchronization is never necessary on builds that are not macOS since we never use 936 // discrete GPUs with managed mode buffers outside of macOS. 937 *outDidResultInWork = false; 938 return true; 939#endif // SK_BUILD_FOR_MAC 940} 941 942bool MtlCommandBuffer::onClearBuffer(const Buffer* buffer, size_t offset, size_t size) { 943 SkASSERT(!fActiveRenderCommandEncoder); 944 SkASSERT(!fActiveComputeCommandEncoder); 945 946 MtlBlitCommandEncoder* blitCmdEncoder = this->getBlitCommandEncoder(); 947 if (!blitCmdEncoder) { 948 return false; 949 } 950 951 id<MTLBuffer> mtlBuffer = static_cast<const MtlBuffer*>(buffer)->mtlBuffer(); 952 blitCmdEncoder->fillBuffer(mtlBuffer, offset, size, 0); 953 954 return true; 955} 956 957} // namespace skgpu::graphite 958