1// 2// Copyright 2019 The ANGLE Project Authors. All rights reserved. 3// Use of this source code is governed by a BSD-style license that can be 4// found in the LICENSE file. 5// 6// mtl_command_buffer.mm: 7// Implementations of Metal framework's MTLCommandBuffer, MTLCommandQueue, 8// MTLCommandEncoder's wrappers. 9// 10 11#include "libANGLE/renderer/metal/mtl_command_buffer.h" 12 13#include <cassert> 14#include <cstdint> 15#include "mtl_command_buffer.h" 16#if ANGLE_MTL_SIMULATE_DISCARD_FRAMEBUFFER 17# include <random> 18#endif 19 20#include "common/debug.h" 21#include "libANGLE/renderer/metal/mtl_occlusion_query_pool.h" 22#include "libANGLE/renderer/metal/mtl_resources.h" 23#include "libANGLE/renderer/metal/mtl_utils.h" 24 25// Use to compare the new values with the values already set in the command encoder: 26static inline bool operator==(const MTLViewport &lhs, const MTLViewport &rhs) 27{ 28 return memcmp(&lhs, &rhs, sizeof(lhs)) == 0; 29} 30 31static inline bool operator==(const MTLScissorRect &lhs, const MTLScissorRect &rhs) 32{ 33 return memcmp(&lhs, &rhs, sizeof(lhs)) == 0; 34} 35 36namespace rx 37{ 38namespace mtl 39{ 40 41namespace 42{ 43 44#define ANGLE_MTL_CMD_X(PROC) \ 45 PROC(Invalid) \ 46 PROC(SetRenderPipelineState) \ 47 PROC(SetTriangleFillMode) \ 48 PROC(SetFrontFacingWinding) \ 49 PROC(SetCullMode) \ 50 PROC(SetDepthStencilState) \ 51 PROC(SetDepthBias) \ 52 PROC(SetDepthClipMode) \ 53 PROC(SetStencilRefVals) \ 54 PROC(SetViewport) \ 55 PROC(SetScissorRect) \ 56 PROC(SetBlendColor) \ 57 PROC(SetVertexBuffer) \ 58 PROC(SetVertexBufferOffset) \ 59 PROC(SetVertexBytes) \ 60 PROC(SetVertexSamplerState) \ 61 PROC(SetVertexTexture) \ 62 PROC(SetFragmentBuffer) \ 63 PROC(SetFragmentBufferOffset) \ 64 PROC(SetFragmentBytes) \ 65 PROC(SetFragmentSamplerState) \ 66 PROC(SetFragmentTexture) \ 67 PROC(Draw) \ 68 PROC(DrawInstanced) \ 69 PROC(DrawInstancedBaseInstance) \ 70 PROC(DrawIndexed) \ 71 PROC(DrawIndexedInstanced) \ 72 PROC(DrawIndexedInstancedBaseVertexBaseInstance) \ 73 PROC(SetVisibilityResultMode) \ 74 PROC(UseResource) \ 75 PROC(MemoryBarrier) \ 76 PROC(MemoryBarrierWithResource) \ 77 PROC(InsertDebugsign) \ 78 PROC(PushDebugGroup) \ 79 PROC(PopDebugGroup) 80 81#define ANGLE_MTL_TYPE_DECL(CMD) CMD, 82 83// Command types 84enum class CmdType : uint8_t 85{ 86 ANGLE_MTL_CMD_X(ANGLE_MTL_TYPE_DECL) 87}; 88 89// Commands decoder 90inline void InvalidCmd(id<MTLRenderCommandEncoder> encoder, IntermediateCommandStream *stream) 91{ 92 UNREACHABLE(); 93} 94 95inline void SetRenderPipelineStateCmd(id<MTLRenderCommandEncoder> encoder, 96 IntermediateCommandStream *stream) 97{ 98 id<MTLRenderPipelineState> state = stream->fetch<id<MTLRenderPipelineState>>(); 99 [encoder setRenderPipelineState:state]; 100 [state ANGLE_MTL_RELEASE]; 101} 102 103inline void SetTriangleFillModeCmd(id<MTLRenderCommandEncoder> encoder, 104 IntermediateCommandStream *stream) 105{ 106 MTLTriangleFillMode mode = stream->fetch<MTLTriangleFillMode>(); 107 [encoder setTriangleFillMode:mode]; 108} 109 110inline void SetFrontFacingWindingCmd(id<MTLRenderCommandEncoder> encoder, 111 IntermediateCommandStream *stream) 112{ 113 MTLWinding winding = stream->fetch<MTLWinding>(); 114 [encoder setFrontFacingWinding:winding]; 115} 116 117inline void SetCullModeCmd(id<MTLRenderCommandEncoder> encoder, IntermediateCommandStream *stream) 118{ 119 MTLCullMode mode = stream->fetch<MTLCullMode>(); 120 [encoder setCullMode:mode]; 121} 122 123inline void SetDepthStencilStateCmd(id<MTLRenderCommandEncoder> encoder, 124 IntermediateCommandStream *stream) 125{ 126 id<MTLDepthStencilState> state = stream->fetch<id<MTLDepthStencilState>>(); 127 [encoder setDepthStencilState:state]; 128 [state ANGLE_MTL_RELEASE]; 129} 130 131inline void SetDepthBiasCmd(id<MTLRenderCommandEncoder> encoder, IntermediateCommandStream *stream) 132{ 133 float depthBias = stream->fetch<float>(); 134 float slopeScale = stream->fetch<float>(); 135 float clamp = stream->fetch<float>(); 136 [encoder setDepthBias:depthBias slopeScale:slopeScale clamp:clamp]; 137} 138 139inline void SetDepthClipModeCmd(id<MTLRenderCommandEncoder> encoder, 140 IntermediateCommandStream *stream) 141{ 142 MTLDepthClipMode depthClipMode = stream->fetch<MTLDepthClipMode>(); 143 [encoder setDepthClipMode:depthClipMode]; 144} 145 146inline void SetStencilRefValsCmd(id<MTLRenderCommandEncoder> encoder, 147 IntermediateCommandStream *stream) 148{ 149 // Metal has some bugs when reference values are larger than 0xff 150 uint32_t frontRef = stream->fetch<uint32_t>(); 151 uint32_t backRef = stream->fetch<uint32_t>(); 152 [encoder setStencilFrontReferenceValue:frontRef backReferenceValue:backRef]; 153} 154 155inline void SetViewportCmd(id<MTLRenderCommandEncoder> encoder, IntermediateCommandStream *stream) 156{ 157 MTLViewport viewport = stream->fetch<MTLViewport>(); 158 [encoder setViewport:viewport]; 159} 160 161inline void SetScissorRectCmd(id<MTLRenderCommandEncoder> encoder, 162 IntermediateCommandStream *stream) 163{ 164 MTLScissorRect rect = stream->fetch<MTLScissorRect>(); 165 [encoder setScissorRect:rect]; 166} 167 168inline void SetBlendColorCmd(id<MTLRenderCommandEncoder> encoder, IntermediateCommandStream *stream) 169{ 170 float r = stream->fetch<float>(); 171 float g = stream->fetch<float>(); 172 float b = stream->fetch<float>(); 173 float a = stream->fetch<float>(); 174 [encoder setBlendColorRed:r green:g blue:b alpha:a]; 175} 176 177inline void SetVertexBufferCmd(id<MTLRenderCommandEncoder> encoder, 178 IntermediateCommandStream *stream) 179{ 180 id<MTLBuffer> buffer = stream->fetch<id<MTLBuffer>>(); 181 uint32_t offset = stream->fetch<uint32_t>(); 182 uint32_t index = stream->fetch<uint32_t>(); 183 [encoder setVertexBuffer:buffer offset:offset atIndex:index]; 184 [buffer ANGLE_MTL_RELEASE]; 185} 186 187inline void SetVertexBufferOffsetCmd(id<MTLRenderCommandEncoder> encoder, 188 IntermediateCommandStream *stream) 189{ 190 uint32_t offset = stream->fetch<uint32_t>(); 191 uint32_t index = stream->fetch<uint32_t>(); 192 [encoder setVertexBufferOffset:offset atIndex:index]; 193} 194 195inline void SetVertexBytesCmd(id<MTLRenderCommandEncoder> encoder, 196 IntermediateCommandStream *stream) 197{ 198 size_t size = stream->fetch<size_t>(); 199 const uint8_t *bytes = stream->fetch(size); 200 uint32_t index = stream->fetch<uint32_t>(); 201 [encoder setVertexBytes:bytes length:size atIndex:index]; 202} 203 204inline void SetVertexSamplerStateCmd(id<MTLRenderCommandEncoder> encoder, 205 IntermediateCommandStream *stream) 206{ 207 id<MTLSamplerState> state = stream->fetch<id<MTLSamplerState>>(); 208 float lodMinClamp = stream->fetch<float>(); 209 float lodMaxClamp = stream->fetch<float>(); 210 uint32_t index = stream->fetch<uint32_t>(); 211 [encoder setVertexSamplerState:state 212 lodMinClamp:lodMinClamp 213 lodMaxClamp:lodMaxClamp 214 atIndex:index]; 215 216 [state ANGLE_MTL_RELEASE]; 217} 218 219inline void SetVertexTextureCmd(id<MTLRenderCommandEncoder> encoder, 220 IntermediateCommandStream *stream) 221{ 222 id<MTLTexture> texture = stream->fetch<id<MTLTexture>>(); 223 uint32_t index = stream->fetch<uint32_t>(); 224 [encoder setVertexTexture:texture atIndex:index]; 225 [texture ANGLE_MTL_RELEASE]; 226} 227 228inline void SetFragmentBufferCmd(id<MTLRenderCommandEncoder> encoder, 229 IntermediateCommandStream *stream) 230{ 231 id<MTLBuffer> buffer = stream->fetch<id<MTLBuffer>>(); 232 uint32_t offset = stream->fetch<uint32_t>(); 233 uint32_t index = stream->fetch<uint32_t>(); 234 [encoder setFragmentBuffer:buffer offset:offset atIndex:index]; 235 [buffer ANGLE_MTL_RELEASE]; 236} 237 238inline void SetFragmentBufferOffsetCmd(id<MTLRenderCommandEncoder> encoder, 239 IntermediateCommandStream *stream) 240{ 241 uint32_t offset = stream->fetch<uint32_t>(); 242 uint32_t index = stream->fetch<uint32_t>(); 243 [encoder setFragmentBufferOffset:offset atIndex:index]; 244} 245 246inline void SetFragmentBytesCmd(id<MTLRenderCommandEncoder> encoder, 247 IntermediateCommandStream *stream) 248{ 249 size_t size = stream->fetch<size_t>(); 250 const uint8_t *bytes = stream->fetch(size); 251 uint32_t index = stream->fetch<uint32_t>(); 252 [encoder setFragmentBytes:bytes length:size atIndex:index]; 253} 254 255inline void SetFragmentSamplerStateCmd(id<MTLRenderCommandEncoder> encoder, 256 IntermediateCommandStream *stream) 257{ 258 id<MTLSamplerState> state = stream->fetch<id<MTLSamplerState>>(); 259 float lodMinClamp = stream->fetch<float>(); 260 float lodMaxClamp = stream->fetch<float>(); 261 uint32_t index = stream->fetch<uint32_t>(); 262 [encoder setFragmentSamplerState:state 263 lodMinClamp:lodMinClamp 264 lodMaxClamp:lodMaxClamp 265 atIndex:index]; 266 [state ANGLE_MTL_RELEASE]; 267} 268 269inline void SetFragmentTextureCmd(id<MTLRenderCommandEncoder> encoder, 270 IntermediateCommandStream *stream) 271{ 272 id<MTLTexture> texture = stream->fetch<id<MTLTexture>>(); 273 uint32_t index = stream->fetch<uint32_t>(); 274 [encoder setFragmentTexture:texture atIndex:index]; 275 [texture ANGLE_MTL_RELEASE]; 276} 277 278inline void DrawCmd(id<MTLRenderCommandEncoder> encoder, IntermediateCommandStream *stream) 279{ 280 MTLPrimitiveType primitiveType = stream->fetch<MTLPrimitiveType>(); 281 uint32_t vertexStart = stream->fetch<uint32_t>(); 282 uint32_t vertexCount = stream->fetch<uint32_t>(); 283 [encoder drawPrimitives:primitiveType vertexStart:vertexStart vertexCount:vertexCount]; 284} 285 286inline void DrawInstancedCmd(id<MTLRenderCommandEncoder> encoder, IntermediateCommandStream *stream) 287{ 288 MTLPrimitiveType primitiveType = stream->fetch<MTLPrimitiveType>(); 289 uint32_t vertexStart = stream->fetch<uint32_t>(); 290 uint32_t vertexCount = stream->fetch<uint32_t>(); 291 uint32_t instances = stream->fetch<uint32_t>(); 292 [encoder drawPrimitives:primitiveType 293 vertexStart:vertexStart 294 vertexCount:vertexCount 295 instanceCount:instances]; 296} 297 298inline void DrawInstancedBaseInstanceCmd(id<MTLRenderCommandEncoder> encoder, 299 IntermediateCommandStream *stream) 300{ 301 MTLPrimitiveType primitiveType = stream->fetch<MTLPrimitiveType>(); 302 uint32_t vertexStart = stream->fetch<uint32_t>(); 303 uint32_t vertexCount = stream->fetch<uint32_t>(); 304 uint32_t instances = stream->fetch<uint32_t>(); 305 uint32_t baseInstance = stream->fetch<uint32_t>(); 306 [encoder drawPrimitives:primitiveType 307 vertexStart:vertexStart 308 vertexCount:vertexCount 309 instanceCount:instances 310 baseInstance:baseInstance]; 311} 312 313inline void DrawIndexedCmd(id<MTLRenderCommandEncoder> encoder, IntermediateCommandStream *stream) 314{ 315 MTLPrimitiveType primitiveType = stream->fetch<MTLPrimitiveType>(); 316 uint32_t indexCount = stream->fetch<uint32_t>(); 317 MTLIndexType indexType = stream->fetch<MTLIndexType>(); 318 id<MTLBuffer> indexBuffer = stream->fetch<id<MTLBuffer>>(); 319 size_t bufferOffset = stream->fetch<size_t>(); 320 [encoder drawIndexedPrimitives:primitiveType 321 indexCount:indexCount 322 indexType:indexType 323 indexBuffer:indexBuffer 324 indexBufferOffset:bufferOffset]; 325 [indexBuffer ANGLE_MTL_RELEASE]; 326} 327 328inline void DrawIndexedInstancedCmd(id<MTLRenderCommandEncoder> encoder, 329 IntermediateCommandStream *stream) 330{ 331 MTLPrimitiveType primitiveType = stream->fetch<MTLPrimitiveType>(); 332 uint32_t indexCount = stream->fetch<uint32_t>(); 333 MTLIndexType indexType = stream->fetch<MTLIndexType>(); 334 id<MTLBuffer> indexBuffer = stream->fetch<id<MTLBuffer>>(); 335 size_t bufferOffset = stream->fetch<size_t>(); 336 uint32_t instances = stream->fetch<uint32_t>(); 337 [encoder drawIndexedPrimitives:primitiveType 338 indexCount:indexCount 339 indexType:indexType 340 indexBuffer:indexBuffer 341 indexBufferOffset:bufferOffset 342 instanceCount:instances]; 343 [indexBuffer ANGLE_MTL_RELEASE]; 344} 345 346inline void DrawIndexedInstancedBaseVertexBaseInstanceCmd(id<MTLRenderCommandEncoder> encoder, 347 IntermediateCommandStream *stream) 348{ 349 MTLPrimitiveType primitiveType = stream->fetch<MTLPrimitiveType>(); 350 uint32_t indexCount = stream->fetch<uint32_t>(); 351 MTLIndexType indexType = stream->fetch<MTLIndexType>(); 352 id<MTLBuffer> indexBuffer = stream->fetch<id<MTLBuffer>>(); 353 size_t bufferOffset = stream->fetch<size_t>(); 354 uint32_t instances = stream->fetch<uint32_t>(); 355 uint32_t baseVertex = stream->fetch<uint32_t>(); 356 uint32_t baseInstance = stream->fetch<uint32_t>(); 357 [encoder drawIndexedPrimitives:primitiveType 358 indexCount:indexCount 359 indexType:indexType 360 indexBuffer:indexBuffer 361 indexBufferOffset:bufferOffset 362 instanceCount:instances 363 baseVertex:baseVertex 364 baseInstance:baseInstance]; 365 [indexBuffer ANGLE_MTL_RELEASE]; 366} 367 368inline void SetVisibilityResultModeCmd(id<MTLRenderCommandEncoder> encoder, 369 IntermediateCommandStream *stream) 370{ 371 MTLVisibilityResultMode mode = stream->fetch<MTLVisibilityResultMode>(); 372 size_t offset = stream->fetch<size_t>(); 373 [encoder setVisibilityResultMode:mode offset:offset]; 374} 375 376inline void UseResourceCmd(id<MTLRenderCommandEncoder> encoder, IntermediateCommandStream *stream) 377{ 378 id<MTLResource> resource = stream->fetch<id<MTLResource>>(); 379 MTLResourceUsage usage = stream->fetch<MTLResourceUsage>(); 380 mtl::RenderStages stages = stream->fetch<mtl::RenderStages>(); 381 ANGLE_UNUSED_VARIABLE(stages); 382#if defined(__IPHONE_13_0) || defined(__MAC_10_15) 383 if (ANGLE_APPLE_AVAILABLE_XCI(10.15, 13.1, 13.0)) 384 { 385 [encoder useResource:resource usage:usage stages:stages]; 386 } 387 else 388#endif 389 { 390 ANGLE_APPLE_ALLOW_DEPRECATED_BEGIN 391 [encoder useResource:resource usage:usage]; 392 ANGLE_APPLE_ALLOW_DEPRECATED_END 393 } 394 [resource ANGLE_MTL_RELEASE]; 395} 396 397inline void MemoryBarrierCmd(id<MTLRenderCommandEncoder> encoder, IntermediateCommandStream *stream) 398{ 399 mtl::RenderStages scope = stream->fetch<mtl::BarrierScope>(); 400 mtl::RenderStages after = stream->fetch<mtl::RenderStages>(); 401 mtl::RenderStages before = stream->fetch<mtl::RenderStages>(); 402 ANGLE_UNUSED_VARIABLE(scope); 403 ANGLE_UNUSED_VARIABLE(after); 404 ANGLE_UNUSED_VARIABLE(before); 405#if defined(__MAC_10_14) && (TARGET_OS_OSX || TARGET_OS_MACCATALYST) 406 if (ANGLE_APPLE_AVAILABLE_XC(10.14, 13.1)) 407 { 408 [encoder memoryBarrierWithScope:scope afterStages:after beforeStages:before]; 409 } 410#endif 411} 412 413inline void MemoryBarrierWithResourceCmd(id<MTLRenderCommandEncoder> encoder, 414 IntermediateCommandStream *stream) 415{ 416 id<MTLResource> resource = stream->fetch<id<MTLResource>>(); 417 mtl::RenderStages after = stream->fetch<mtl::RenderStages>(); 418 mtl::RenderStages before = stream->fetch<mtl::RenderStages>(); 419 ANGLE_UNUSED_VARIABLE(after); 420 ANGLE_UNUSED_VARIABLE(before); 421#if defined(__MAC_10_14) && (TARGET_OS_OSX || TARGET_OS_MACCATALYST) 422 if (ANGLE_APPLE_AVAILABLE_XC(10.14, 13.1)) 423 { 424 [encoder memoryBarrierWithResources:&resource 425 count:1 426 afterStages:after 427 beforeStages:before]; 428 } 429#endif 430 [resource ANGLE_MTL_RELEASE]; 431} 432 433inline void InsertDebugsignCmd(id<MTLRenderCommandEncoder> encoder, 434 IntermediateCommandStream *stream) 435{ 436 NSString *label = stream->fetch<NSString *>(); 437 [encoder insertDebugSignpost:label]; 438 [label ANGLE_MTL_RELEASE]; 439} 440 441inline void PushDebugGroupCmd(id<MTLRenderCommandEncoder> encoder, 442 IntermediateCommandStream *stream) 443{ 444 NSString *label = stream->fetch<NSString *>(); 445 [encoder pushDebugGroup:label]; 446 [label ANGLE_MTL_RELEASE]; 447} 448 449inline void PopDebugGroupCmd(id<MTLRenderCommandEncoder> encoder, IntermediateCommandStream *stream) 450{ 451 [encoder popDebugGroup]; 452} 453 454NSString *cppLabelToObjC(const std::string &marker) 455{ 456 NSString *label = [NSString stringWithUTF8String:marker.c_str()]; 457 if (!label) 458 { 459 // This can happen if the string is not a valid ascii string. 460 label = @"Invalid ASCII string"; 461 } 462 return label; 463} 464 465inline void CheckPrimitiveType(MTLPrimitiveType primitiveType) 466{ 467 if (ANGLE_UNLIKELY(primitiveType == MTLPrimitiveTypeInvalid)) 468 { 469 // Should have been caught by validation higher up. 470 FATAL() << "invalid primitive type was uncaught by validation"; 471 } 472} 473 474} // namespace 475 476// AtomicSerial implementation 477void AtomicSerial::storeMaxValue(uint64_t value) 478{ 479 uint64_t prevValue = load(); 480 while (prevValue < value && 481 !mValue.compare_exchange_weak(prevValue, value, std::memory_order_release, 482 std::memory_order_consume)) 483 { 484 } 485} 486 487// CommandQueue implementation 488void CommandQueue::reset() 489{ 490 finishAllCommands(); 491 ParentClass::reset(); 492} 493 494void CommandQueue::set(id<MTLCommandQueue> metalQueue) 495{ 496 finishAllCommands(); 497 498 ParentClass::set(metalQueue); 499} 500 501void CommandQueue::finishAllCommands() 502{ 503 std::deque<CmdBufferQueueEntry> commandBuffers; 504 { 505 std::lock_guard<std::mutex> lg(mLock); 506 mMetalCmdBuffers.swap(commandBuffers); 507 } 508 for (CmdBufferQueueEntry &entry : commandBuffers) 509 { 510 [entry.buffer waitUntilCompleted]; 511 } 512} 513 514void CommandQueue::ensureResourceReadyForCPU(const ResourceRef &resource) 515{ 516 if (!resource) 517 { 518 return; 519 } 520 521 ensureResourceReadyForCPU(resource.get()); 522} 523 524void CommandQueue::ensureResourceReadyForCPU(Resource *resource) 525{ 526 mLock.lock(); 527 while (isResourceBeingUsedByGPU(resource) && !mMetalCmdBuffers.empty()) 528 { 529 CmdBufferQueueEntry metalBufferEntry = mMetalCmdBuffers.front(); 530 mMetalCmdBuffers.pop_front(); 531 mLock.unlock(); 532 533 ANGLE_MTL_LOG("Waiting for MTLCommandBuffer %llu:%p", metalBufferEntry.serial, 534 metalBufferEntry.buffer.get()); 535 [metalBufferEntry.buffer waitUntilCompleted]; 536 537 mLock.lock(); 538 } 539 mLock.unlock(); 540 541 // This can happen if the resource is read then write in the same command buffer. 542 // So it is the responsitibily of outer code to ensure the command buffer is commit before 543 // the resource can be read or written again 544 ASSERT(!isResourceBeingUsedByGPU(resource)); 545} 546 547bool CommandQueue::isResourceBeingUsedByGPU(const Resource *resource) const 548{ 549 if (!resource) 550 { 551 return false; 552 } 553 554 return !isSerialCompleted(resource->getCommandBufferQueueSerial()); 555} 556 557bool CommandQueue::resourceHasPendingWorks(const Resource *resource) const 558{ 559 if (!resource) 560 { 561 return false; 562 } 563 564 return mCommittedBufferSerial.load() < resource->getCommandBufferQueueSerial(); 565} 566 567bool CommandQueue::resourceHasPendingRenderWorks(const Resource *resource) const 568{ 569 if (!resource) 570 { 571 return false; 572 } 573 574 return mCommittedBufferSerial.load() < resource->getLastRenderEncoderSerial(); 575} 576 577bool CommandQueue::isSerialCompleted(uint64_t serial) const 578{ 579 return mCompletedBufferSerial.load() >= serial; 580} 581 582bool CommandQueue::waitUntilSerialCompleted(uint64_t serial, uint64_t timeoutNs) const 583{ 584 std::unique_lock<std::mutex> lk(mLock); 585 if (isSerialCompleted(serial)) 586 { 587 return true; 588 } 589 590 if (timeoutNs == 0 || 591 !mCompletedBufferSerialCv.wait_for(lk, std::chrono::nanoseconds(timeoutNs), 592 [&]() { return isSerialCompleted(serial); })) 593 { 594 return false; 595 } 596 597 return true; 598} 599 600AutoObjCPtr<id<MTLCommandBuffer>> CommandQueue::makeMetalCommandBuffer(uint64_t *queueSerialOut) 601{ 602 ANGLE_MTL_OBJC_SCOPE 603 { 604 AutoObjCPtr<id<MTLCommandBuffer>> metalCmdBuffer = [get() commandBuffer]; 605 606 std::lock_guard<std::mutex> lg(mLock); 607 608 uint64_t serial = mQueueSerialCounter++; 609 uint64_t timeElapsedEntry = mActiveTimeElapsedId; 610 611 mMetalCmdBuffers.push_back({metalCmdBuffer, serial}); 612 613 ANGLE_MTL_LOG("Created MTLCommandBuffer %llu:%p", serial, metalCmdBuffer.get()); 614 615 if (timeElapsedEntry) 616 { 617 addCommandBufferToTimeElapsedEntry(lg, timeElapsedEntry); 618 } 619 620 [metalCmdBuffer addCompletedHandler:^(id<MTLCommandBuffer> buf) { 621 onCommandBufferCompleted(buf, serial, timeElapsedEntry); 622 }]; 623 624 ASSERT(metalCmdBuffer); 625 626 *queueSerialOut = serial; 627 628 return metalCmdBuffer; 629 } 630} 631 632void CommandQueue::onCommandBufferCommitted(id<MTLCommandBuffer> buf, uint64_t serial) 633{ 634 ANGLE_MTL_LOG("Committed MTLCommandBuffer %llu:%p", serial, buf); 635 636 mCommittedBufferSerial.storeMaxValue(serial); 637} 638 639void CommandQueue::onCommandBufferCompleted(id<MTLCommandBuffer> buf, 640 uint64_t serial, 641 uint64_t timeElapsedEntry) 642{ 643 std::lock_guard<std::mutex> lg(mLock); 644 645 ANGLE_MTL_LOG("Completed MTLCommandBuffer %llu:%p", serial, buf); 646 647 if (timeElapsedEntry != 0) 648 { 649 // Record this command buffer's elapsed time. 650 recordCommandBufferTimeElapsed(lg, timeElapsedEntry, [buf GPUEndTime] - [buf GPUStartTime]); 651 } 652 653 if (mCompletedBufferSerial.load() >= serial) 654 { 655 // Already handled. 656 return; 657 } 658 659 while (!mMetalCmdBuffers.empty() && mMetalCmdBuffers.front().serial <= serial) 660 { 661 CmdBufferQueueEntry metalBufferEntry = mMetalCmdBuffers.front(); 662 ANGLE_UNUSED_VARIABLE(metalBufferEntry); 663 ANGLE_MTL_LOG("Popped MTLCommandBuffer %llu:%p", metalBufferEntry.serial, 664 metalBufferEntry.buffer.get()); 665 666 mMetalCmdBuffers.pop_front(); 667 } 668 669 mCompletedBufferSerial.storeMaxValue(serial); 670 mCompletedBufferSerialCv.notify_all(); 671} 672 673uint64_t CommandQueue::getNextRenderEncoderSerial() 674{ 675 return ++mRenderEncoderCounter; 676} 677 678uint64_t CommandQueue::allocateTimeElapsedEntry() 679{ 680 std::lock_guard<std::mutex> lg(mLock); 681 682 uint64_t id = mTimeElapsedNextId++; 683 if (mTimeElapsedNextId == 0) 684 { 685 mTimeElapsedNextId = 1; 686 } 687 TimeElapsedEntry entry; 688 entry.id = id; 689 mTimeElapsedEntries.insert({id, entry}); 690 return id; 691} 692 693bool CommandQueue::deleteTimeElapsedEntry(uint64_t id) 694{ 695 std::lock_guard<std::mutex> lg(mLock); 696 697 auto result = mTimeElapsedEntries.find(id); 698 if (result == mTimeElapsedEntries.end()) 699 { 700 return false; 701 } 702 mTimeElapsedEntries.erase(result); 703 return true; 704} 705 706void CommandQueue::setActiveTimeElapsedEntry(uint64_t id) 707{ 708 std::lock_guard<std::mutex> lg(mLock); 709 710 // If multithreading support is added to the Metal backend and 711 // involves accessing the same CommandQueue from multiple threads, 712 // the time elapsed query implementation will need to be rethought. 713 mActiveTimeElapsedId = id; 714} 715 716bool CommandQueue::isTimeElapsedEntryComplete(uint64_t id) 717{ 718 std::lock_guard<std::mutex> lg(mLock); 719 720 auto result = mTimeElapsedEntries.find(id); 721 if (result == mTimeElapsedEntries.end()) 722 { 723 return false; 724 } 725 726 TimeElapsedEntry &entry = result->second; 727 ASSERT(entry.pending_command_buffers >= 0); 728 if (entry.pending_command_buffers > 0) 729 { 730 return false; 731 } 732 733 return true; 734} 735 736double CommandQueue::getTimeElapsedEntryInSeconds(uint64_t id) 737{ 738 std::lock_guard<std::mutex> lg(mLock); 739 740 auto result = mTimeElapsedEntries.find(id); 741 if (result == mTimeElapsedEntries.end()) 742 { 743 return 0.0; 744 } 745 746 return result->second.elapsed_seconds; 747} 748 749// Private. 750void CommandQueue::addCommandBufferToTimeElapsedEntry(std::lock_guard<std::mutex> &lg, uint64_t id) 751{ 752 // This must be called under the cover of mLock. 753 auto result = mTimeElapsedEntries.find(id); 754 if (result == mTimeElapsedEntries.end()) 755 { 756 return; 757 } 758 759 TimeElapsedEntry &entry = result->second; 760 ++entry.pending_command_buffers; 761} 762 763void CommandQueue::recordCommandBufferTimeElapsed(std::lock_guard<std::mutex> &lg, 764 uint64_t id, 765 double seconds) 766{ 767 // This must be called under the cover of mLock. 768 auto result = mTimeElapsedEntries.find(id); 769 if (result == mTimeElapsedEntries.end()) 770 { 771 return; 772 } 773 774 TimeElapsedEntry &entry = result->second; 775 ASSERT(entry.pending_command_buffers > 0); 776 --entry.pending_command_buffers; 777 entry.elapsed_seconds += seconds; 778} 779 780// CommandBuffer implementation 781CommandBuffer::CommandBuffer(CommandQueue *cmdQueue) : mCmdQueue(*cmdQueue) {} 782 783CommandBuffer::~CommandBuffer() 784{ 785 commit(WaitUntilFinished); 786 cleanup(); 787} 788 789bool CommandBuffer::ready() const 790{ 791 std::lock_guard<std::mutex> lg(mLock); 792 793 return readyImpl(); 794} 795 796void CommandBuffer::commit(CommandBufferFinishOperation operation) 797{ 798 std::lock_guard<std::mutex> lg(mLock); 799 if (commitImpl()) 800 { 801 wait(operation); 802 } 803} 804 805void CommandBuffer::wait(CommandBufferFinishOperation operation) 806{ 807 // NOTE: A CommandBuffer is valid forever under current conditions (2022-12-22) 808 // except before the first call to restart. 809 if (!valid()) 810 { 811 return; 812 } 813 814 // You can't wait on an uncommitted command buffer. 815 ASSERT(mCommitted); 816 817 switch (operation) 818 { 819 case NoWait: 820 break; 821 822 case WaitUntilScheduled: 823 // Only wait if we haven't already waited 824 if (mLastWaitOp == NoWait) 825 { 826 [get() waitUntilScheduled]; 827 mLastWaitOp = WaitUntilScheduled; 828 } 829 break; 830 831 case WaitUntilFinished: 832 // Only wait if we haven't already waited until finished. 833 if (mLastWaitOp != WaitUntilFinished) 834 { 835 [get() waitUntilCompleted]; 836 mLastWaitOp = WaitUntilFinished; 837 } 838 break; 839 } 840} 841 842void CommandBuffer::present(id<CAMetalDrawable> presentationDrawable) 843{ 844 [get() presentDrawable:presentationDrawable]; 845} 846 847void CommandBuffer::setResourceUsedByCommandBuffer(const ResourceRef &resource) 848{ 849 if (resource) 850 { 851 auto result = mResourceList.insert(resource->getID()); 852 // If we were able to add a unique Metal resource ID to the list, count it. 853 // 854 // Note that we store Metal IDs here, properly retained in non-ARC environments, rather than 855 // the ResourceRefs. There are some assumptions in TextureMtl in particular about weak refs 856 // to temporary textures being cleared out eagerly. Holding on to additional references here 857 // implies that that texture is still being used, and would require additional code to clear 858 // out temporary render targets upon texture redefinition. 859 if (result.second) 860 { 861 [resource->getID() ANGLE_MTL_RETAIN]; 862 mWorkingResourceSize += resource->estimatedByteSize(); 863 } 864 } 865} 866 867void CommandBuffer::clearResourceListAndSize() 868{ 869 for (const id &metalID : mResourceList) 870 { 871 [metalID ANGLE_MTL_RELEASE]; 872 } 873 mResourceList.clear(); 874 mWorkingResourceSize = 0; 875} 876 877void CommandBuffer::setWriteDependency(const ResourceRef &resource, bool isRenderCommand) 878{ 879 if (!resource) 880 { 881 return; 882 } 883 884 std::lock_guard<std::mutex> lg(mLock); 885 886 if (!readyImpl()) 887 { 888 return; 889 } 890 891 resource->setUsedByCommandBufferWithQueueSerial(mQueueSerial, true, isRenderCommand); 892 setResourceUsedByCommandBuffer(resource); 893} 894 895void CommandBuffer::setReadDependency(const ResourceRef &resource, bool isRenderCommand) 896{ 897 setReadDependency(resource.get(), isRenderCommand); 898 setResourceUsedByCommandBuffer(resource); 899} 900 901void CommandBuffer::setReadDependency(Resource *resource, bool isRenderCommand) 902{ 903 if (!resource) 904 { 905 return; 906 } 907 908 std::lock_guard<std::mutex> lg(mLock); 909 910 if (!readyImpl()) 911 { 912 return; 913 } 914 915 resource->setUsedByCommandBufferWithQueueSerial(mQueueSerial, false, isRenderCommand); 916} 917 918bool CommandBuffer::needsFlushForDrawCallLimits() const 919{ 920 return mWorkingResourceSize > kMaximumResidentMemorySizeInBytes; 921} 922 923uint64_t CommandBuffer::getQueueSerial() const 924{ 925 std::lock_guard<std::mutex> lg(mLock); 926 return mQueueSerial; 927} 928 929void CommandBuffer::restart() 930{ 931 uint64_t serial = 0; 932 AutoObjCPtr<id<MTLCommandBuffer>> metalCmdBuffer = mCmdQueue.makeMetalCommandBuffer(&serial); 933 934 std::lock_guard<std::mutex> lg(mLock); 935 936 set(metalCmdBuffer); 937 mQueueSerial = serial; 938 mCommitted = false; 939 mLastWaitOp = mtl::NoWait; 940 941 for (std::string &marker : mDebugGroups) 942 { 943 pushDebugGroupImpl(marker); 944 } 945 clearResourceListAndSize(); 946 ASSERT(metalCmdBuffer); 947} 948 949void CommandBuffer::insertDebugSign(const std::string &marker) 950{ 951 mtl::CommandEncoder *currentEncoder = getPendingCommandEncoder(); 952 if (currentEncoder) 953 { 954 ANGLE_MTL_OBJC_SCOPE 955 { 956 NSString *label = cppLabelToObjC(marker); 957 currentEncoder->insertDebugSign(label); 958 } 959 } 960 else 961 { 962 mPendingDebugSigns.push_back(marker); 963 } 964} 965 966void CommandBuffer::pushDebugGroup(const std::string &marker) 967{ 968 mDebugGroups.push_back(marker); 969 970 std::lock_guard<std::mutex> lg(mLock); 971 972 if (readyImpl()) 973 { 974 pushDebugGroupImpl(marker); 975 } 976} 977 978void CommandBuffer::popDebugGroup() 979{ 980 if (!mDebugGroups.empty()) 981 { 982 mDebugGroups.pop_back(); 983 } 984 985 std::lock_guard<std::mutex> lg(mLock); 986 987 if (readyImpl()) 988 { 989 return; 990 } 991} 992 993#if ANGLE_MTL_EVENT_AVAILABLE 994uint64_t CommandBuffer::queueEventSignal(id<MTLEvent> event, uint64_t value) 995{ 996 std::lock_guard<std::mutex> lg(mLock); 997 998 ASSERT(readyImpl()); 999 1000 CommandEncoder *currentEncoder = getPendingCommandEncoder(); 1001 1002 if (currentEncoder) 1003 { 1004 // We cannot set event when there is an active render pass, defer the setting until the pass 1005 // end. 1006 PendingEvent pending; 1007 pending.event.retainAssign(event); 1008 pending.signalValue = value; 1009 mPendingSignalEvents.push_back(std::move(pending)); 1010 } 1011 else 1012 { 1013 setEventImpl(event, value); 1014 } 1015 return mQueueSerial; 1016} 1017 1018void CommandBuffer::serverWaitEvent(id<MTLEvent> event, uint64_t value) 1019{ 1020 std::lock_guard<std::mutex> lg(mLock); 1021 ASSERT(readyImpl()); 1022 ASSERT(!getPendingCommandEncoder()); 1023 setPendingEvents(); 1024 [get() encodeWaitForEvent:event value:value]; 1025} 1026#endif // ANGLE_MTL_EVENT_AVAILABLE 1027 1028/** private use only */ 1029void CommandBuffer::set(id<MTLCommandBuffer> metalBuffer) 1030{ 1031 ParentClass::set(metalBuffer); 1032} 1033 1034void CommandBuffer::setActiveCommandEncoder(CommandEncoder *encoder) 1035{ 1036 if (encoder->getType() == CommandEncoder::Type::RENDER) 1037 { 1038 mActiveRenderEncoder = encoder; 1039 } 1040 else 1041 { 1042 mActiveBlitOrComputeEncoder = encoder; 1043 } 1044 1045 for (std::string &marker : mPendingDebugSigns) 1046 { 1047 ANGLE_MTL_OBJC_SCOPE 1048 { 1049 NSString *label = cppLabelToObjC(marker); 1050 encoder->insertDebugSign(label); 1051 } 1052 } 1053 mPendingDebugSigns.clear(); 1054} 1055 1056void CommandBuffer::invalidateActiveCommandEncoder(CommandEncoder *encoder) 1057{ 1058 if (mActiveRenderEncoder == encoder) 1059 { 1060 mActiveRenderEncoder = nullptr; 1061 } 1062 else if (mActiveBlitOrComputeEncoder == encoder) 1063 { 1064 mActiveBlitOrComputeEncoder = nullptr; 1065 } 1066 1067 if (getPendingCommandEncoder() == nullptr) 1068 { 1069 // No active command encoder, we can safely encode event signalling now. 1070 setPendingEvents(); 1071 } 1072} 1073 1074CommandEncoder *CommandBuffer::getPendingCommandEncoder() 1075{ 1076 // blit/compute encoder takes precedence over render encoder because the former is immediate 1077 // encoder i.e its native MTLCommandEncoder is already created. 1078 return mActiveBlitOrComputeEncoder ? mActiveBlitOrComputeEncoder : mActiveRenderEncoder; 1079} 1080 1081void CommandBuffer::cleanup() 1082{ 1083 mActiveBlitOrComputeEncoder = mActiveRenderEncoder = nullptr; 1084 1085 ParentClass::set(nil); 1086} 1087 1088bool CommandBuffer::readyImpl() const 1089{ 1090 if (!ParentClass::valid()) 1091 { 1092 return false; 1093 } 1094 1095 return !mCommitted; 1096} 1097 1098bool CommandBuffer::commitImpl() 1099{ 1100 if (!readyImpl()) 1101 { 1102 return false; 1103 } 1104 1105 // End the current encoders 1106 forceEndingAllEncoders(); 1107 1108 // Encoding any pending event's signalling. 1109 setPendingEvents(); 1110 1111 // Notify command queue 1112 mCmdQueue.onCommandBufferCommitted(get(), mQueueSerial); 1113 1114 // Do the actual commit 1115 [get() enqueue]; 1116 [get() commit]; 1117 // Reset the working resource set. 1118 clearResourceListAndSize(); 1119 mCommitted = true; 1120 return true; 1121} 1122 1123void CommandBuffer::forceEndingAllEncoders() 1124{ 1125 // End active blit/compute encoder first since it's immediate encoder. 1126 if (mActiveBlitOrComputeEncoder) 1127 { 1128 mActiveBlitOrComputeEncoder->endEncoding(); 1129 mActiveBlitOrComputeEncoder = nullptr; 1130 } 1131 1132 // End render encoder last. This is possible because it is deferred encoder. 1133 if (mActiveRenderEncoder) 1134 { 1135 mActiveRenderEncoder->endEncoding(); 1136 mActiveRenderEncoder = nullptr; 1137 } 1138} 1139 1140void CommandBuffer::setPendingEvents() 1141{ 1142#if ANGLE_MTL_EVENT_AVAILABLE 1143 for (const PendingEvent &eventEntry : mPendingSignalEvents) 1144 { 1145 setEventImpl(eventEntry.event, eventEntry.signalValue); 1146 } 1147 mPendingSignalEvents.clear(); 1148#endif 1149} 1150 1151#if ANGLE_MTL_EVENT_AVAILABLE 1152void CommandBuffer::setEventImpl(id<MTLEvent> event, uint64_t value) 1153{ 1154 ASSERT(!getPendingCommandEncoder()); 1155 [get() encodeSignalEvent:event value:value]; 1156} 1157#endif // #if ANGLE_MTL_EVENT_AVAILABLE 1158 1159void CommandBuffer::pushDebugGroupImpl(const std::string &marker) 1160{ 1161 ANGLE_MTL_OBJC_SCOPE 1162 { 1163 NSString *label = cppLabelToObjC(marker); 1164 [get() pushDebugGroup:label]; 1165 1166 CommandEncoder *currentEncoder = getPendingCommandEncoder(); 1167 if (currentEncoder) 1168 { 1169 currentEncoder->pushDebugGroup(label); 1170 } 1171 } 1172} 1173 1174void CommandBuffer::popDebugGroupImpl() 1175{ 1176 CommandEncoder *currentEncoder = getPendingCommandEncoder(); 1177 if (currentEncoder) 1178 { 1179 currentEncoder->popDebugGroup(); 1180 } 1181 [get() popDebugGroup]; 1182} 1183 1184// CommandEncoder implementation 1185CommandEncoder::CommandEncoder(CommandBuffer *cmdBuffer, Type type) 1186 : mType(type), mCmdBuffer(*cmdBuffer) 1187{} 1188 1189CommandEncoder::~CommandEncoder() 1190{ 1191 reset(); 1192} 1193 1194void CommandEncoder::endEncoding() 1195{ 1196 [get() endEncoding]; 1197 reset(); 1198} 1199 1200void CommandEncoder::reset() 1201{ 1202 ParentClass::reset(); 1203 1204 mCmdBuffer.invalidateActiveCommandEncoder(this); 1205} 1206 1207void CommandEncoder::set(id<MTLCommandEncoder> metalCmdEncoder) 1208{ 1209 ParentClass::set(metalCmdEncoder); 1210 1211 // Set this as active encoder 1212 cmdBuffer().setActiveCommandEncoder(this); 1213} 1214 1215CommandEncoder &CommandEncoder::markResourceBeingWrittenByGPU(const BufferRef &buffer) 1216{ 1217 cmdBuffer().setWriteDependency(buffer, isRenderEncoder()); 1218 return *this; 1219} 1220 1221CommandEncoder &CommandEncoder::markResourceBeingWrittenByGPU(const TextureRef &texture) 1222{ 1223 cmdBuffer().setWriteDependency(texture, isRenderEncoder()); 1224 return *this; 1225} 1226 1227void CommandEncoder::pushDebugGroup(NSString *label) 1228{ 1229 // Default implementation 1230 [get() pushDebugGroup:label]; 1231} 1232 1233void CommandEncoder::popDebugGroup() 1234{ 1235 // Default implementation 1236 [get() popDebugGroup]; 1237} 1238 1239void CommandEncoder::insertDebugSign(NSString *label) 1240{ 1241 insertDebugSignImpl(label); 1242} 1243 1244void CommandEncoder::insertDebugSignImpl(NSString *label) 1245{ 1246 // Default implementation 1247 [get() insertDebugSignpost:label]; 1248} 1249 1250// RenderCommandEncoderShaderStates implementation 1251RenderCommandEncoderShaderStates::RenderCommandEncoderShaderStates() 1252{ 1253 reset(); 1254} 1255 1256void RenderCommandEncoderShaderStates::reset() 1257{ 1258 for (id<MTLBuffer> &buffer : buffers) 1259 { 1260 buffer = nil; 1261 } 1262 1263 for (uint32_t &offset : bufferOffsets) 1264 { 1265 offset = 0; 1266 } 1267 1268 for (id<MTLSamplerState> &sampler : samplers) 1269 { 1270 sampler = nil; 1271 } 1272 1273 for (Optional<std::pair<float, float>> &lodClampRange : samplerLodClamps) 1274 { 1275 lodClampRange.reset(); 1276 } 1277 1278 for (id<MTLTexture> &texture : textures) 1279 { 1280 texture = nil; 1281 } 1282} 1283 1284// RenderCommandEncoderStates implementation 1285RenderCommandEncoderStates::RenderCommandEncoderStates() 1286{ 1287 reset(); 1288} 1289 1290void RenderCommandEncoderStates::reset() 1291{ 1292 renderPipeline = nil; 1293 1294 triangleFillMode = MTLTriangleFillModeFill; 1295 winding = MTLWindingClockwise; 1296 cullMode = MTLCullModeNone; 1297 1298 depthStencilState = nil; 1299 depthBias = depthSlopeScale = depthClamp = 0; 1300 1301 depthClipMode = MTLDepthClipModeClip; 1302 1303 stencilFrontRef = stencilBackRef = 0; 1304 1305 viewport.reset(); 1306 scissorRect.reset(); 1307 1308 blendColor = {0, 0, 0, 0}; 1309 1310 for (RenderCommandEncoderShaderStates &shaderStates : perShaderStates) 1311 { 1312 shaderStates.reset(); 1313 } 1314 1315 visibilityResultMode = MTLVisibilityResultModeDisabled; 1316 visibilityResultBufferOffset = 0; 1317} 1318 1319// RenderCommandEncoder implemtation 1320RenderCommandEncoder::RenderCommandEncoder(CommandBuffer *cmdBuffer, 1321 const OcclusionQueryPool &queryPool) 1322 : CommandEncoder(cmdBuffer, RENDER), 1323 mOcclusionQueryPool(queryPool), 1324 mSerial(cmdBuffer->cmdQueue().getNextRenderEncoderSerial()) 1325{ 1326 ANGLE_MTL_OBJC_SCOPE 1327 { 1328 mCachedRenderPassDescObjC = [MTLRenderPassDescriptor renderPassDescriptor]; 1329 } 1330 1331 static_assert(sizeof(uint8_t) == sizeof(CmdType), "CmdType was expected to be 8 bit"); 1332 for (gl::ShaderType shaderType : gl::AllShaderTypes()) 1333 { 1334 mSetBufferCmds[shaderType] = static_cast<uint8_t>(CmdType::Invalid); 1335 mSetBytesCmds[shaderType] = static_cast<uint8_t>(CmdType::Invalid); 1336 mSetTextureCmds[shaderType] = static_cast<uint8_t>(CmdType::Invalid); 1337 mSetSamplerCmds[shaderType] = static_cast<uint8_t>(CmdType::Invalid); 1338 } 1339 1340 mSetBufferCmds[gl::ShaderType::Vertex] = static_cast<uint8_t>(CmdType::SetVertexBuffer); 1341 mSetBufferCmds[gl::ShaderType::Fragment] = static_cast<uint8_t>(CmdType::SetFragmentBuffer); 1342 1343 mSetBufferOffsetCmds[gl::ShaderType::Vertex] = 1344 static_cast<uint8_t>(CmdType::SetVertexBufferOffset); 1345 mSetBufferOffsetCmds[gl::ShaderType::Fragment] = 1346 static_cast<uint8_t>(CmdType::SetFragmentBufferOffset); 1347 1348 mSetBytesCmds[gl::ShaderType::Vertex] = static_cast<uint8_t>(CmdType::SetVertexBytes); 1349 mSetBytesCmds[gl::ShaderType::Fragment] = static_cast<uint8_t>(CmdType::SetFragmentBytes); 1350 1351 mSetTextureCmds[gl::ShaderType::Vertex] = static_cast<uint8_t>(CmdType::SetVertexTexture); 1352 mSetTextureCmds[gl::ShaderType::Fragment] = static_cast<uint8_t>(CmdType::SetFragmentTexture); 1353 1354 mSetSamplerCmds[gl::ShaderType::Vertex] = static_cast<uint8_t>(CmdType::SetVertexSamplerState); 1355 mSetSamplerCmds[gl::ShaderType::Fragment] = 1356 static_cast<uint8_t>(CmdType::SetFragmentSamplerState); 1357} 1358RenderCommandEncoder::~RenderCommandEncoder() {} 1359 1360void RenderCommandEncoder::reset() 1361{ 1362 CommandEncoder::reset(); 1363 mRecording = false; 1364 mPipelineStateSet = false; 1365 mCommands.clear(); 1366} 1367 1368bool RenderCommandEncoder::finalizeLoadStoreAction( 1369 MTLRenderPassAttachmentDescriptor *objCRenderPassAttachment) 1370{ 1371 if (!objCRenderPassAttachment.texture) 1372 { 1373 objCRenderPassAttachment.loadAction = MTLLoadActionDontCare; 1374 objCRenderPassAttachment.storeAction = MTLStoreActionDontCare; 1375 objCRenderPassAttachment.resolveTexture = nil; 1376 return false; 1377 } 1378 1379 if (objCRenderPassAttachment.resolveTexture) 1380 { 1381 if (objCRenderPassAttachment.storeAction == MTLStoreActionStore) 1382 { 1383 // NOTE(hqle): Currently if the store action with implicit MS texture is MTLStoreAction, 1384 // it is automatically convert to store and resolve action. It might introduce 1385 // unnecessary overhead. 1386 // Consider an improvement such as only store the MS texture, and resolve only at 1387 // the end of real render pass (not render pass the was interrupted by compute pass) 1388 // or before glBlitFramebuffer operation starts. 1389 objCRenderPassAttachment.storeAction = MTLStoreActionStoreAndMultisampleResolve; 1390 } 1391 else if (objCRenderPassAttachment.storeAction == MTLStoreActionDontCare) 1392 { 1393 // Ignore resolve texture if the store action is not a resolve action. 1394 objCRenderPassAttachment.resolveTexture = nil; 1395 } 1396 } 1397 1398 if (objCRenderPassAttachment.storeAction == MTLStoreActionUnknown) 1399 { 1400 // If storeAction hasn't been set for this attachment, we set to dontcare. 1401 objCRenderPassAttachment.storeAction = MTLStoreActionDontCare; 1402 } 1403 1404 return true; 1405} 1406 1407void RenderCommandEncoder::endEncoding() 1408{ 1409 endEncodingImpl(true); 1410} 1411 1412void RenderCommandEncoder::endEncodingImpl(bool considerDiscardSimulation) 1413{ 1414 if (!valid()) 1415 return; 1416 1417 bool hasAttachment = false; 1418 1419 // Last minute correcting the store options. 1420 MTLRenderPassDescriptor *objCRenderPassDesc = mCachedRenderPassDescObjC.get(); 1421 for (uint32_t i = 0; i < mRenderPassDesc.numColorAttachments; ++i) 1422 { 1423 // Update store action set between restart() and endEncoding() 1424 objCRenderPassDesc.colorAttachments[i].storeAction = 1425 mRenderPassDesc.colorAttachments[i].storeAction; 1426 if (finalizeLoadStoreAction(objCRenderPassDesc.colorAttachments[i])) 1427 hasAttachment = true; 1428 } 1429 1430 // Update store action set between restart() and endEncoding() 1431 objCRenderPassDesc.depthAttachment.storeAction = mRenderPassDesc.depthAttachment.storeAction; 1432 if (finalizeLoadStoreAction(objCRenderPassDesc.depthAttachment)) 1433 hasAttachment = true; 1434 1435 // Update store action set between restart() and endEncoding() 1436 objCRenderPassDesc.stencilAttachment.storeAction = 1437 mRenderPassDesc.stencilAttachment.storeAction; 1438 if (finalizeLoadStoreAction(objCRenderPassDesc.stencilAttachment)) 1439 hasAttachment = true; 1440 1441 // Set visibility result buffer 1442 if (mOcclusionQueryPool.getNumRenderPassAllocatedQueries()) 1443 { 1444 objCRenderPassDesc.visibilityResultBuffer = 1445 mOcclusionQueryPool.getRenderPassVisibilityPoolBuffer()->get(); 1446 } 1447 else 1448 { 1449 objCRenderPassDesc.visibilityResultBuffer = nil; 1450 } 1451 1452 // If a render pass has intended side effects but no attachments, the app must set a default 1453 // width/height. 1454 bool hasSideEffects = 1455 hasAttachment || (mRenderPassDesc.defaultWidth != 0 && mRenderPassDesc.defaultHeight != 0); 1456 1457 // Encode the actual encoder. It will not be created when there are no side effects. 1458 if (hasSideEffects) 1459 { 1460 // Metal validation messages say: Either set rendertargets in RenderPassDescriptor or set 1461 // defaultRasterSampleCount. 1462 ASSERT(hasAttachment || objCRenderPassDesc.defaultRasterSampleCount != 0); 1463 encodeMetalEncoder(); 1464 } 1465 else if (!hasSideEffects && hasDrawCalls()) 1466 { 1467 // Command encoder should not have been created if no side effects occur, but draw calls do. 1468 UNREACHABLE(); 1469 // Fallback to clearing commands if on release. 1470 mCommands.clear(); 1471 } 1472 // If no side effects, and no drawing is encoded, there's no point in encoding. Skip the 1473 // commands. 1474 else 1475 { 1476 mCommands.clear(); 1477 } 1478 1479 CommandEncoder::endEncoding(); 1480 1481#if ANGLE_MTL_SIMULATE_DISCARD_FRAMEBUFFER 1482 if (considerDiscardSimulation) 1483 { 1484 simulateDiscardFramebuffer(); 1485 } 1486#endif 1487 1488 // reset state 1489 mRenderPassDesc = RenderPassDesc(); 1490 mStateCache.reset(); 1491} 1492 1493inline void RenderCommandEncoder::initAttachmentWriteDependencyAndScissorRect( 1494 const RenderPassAttachmentDesc &attachment) 1495{ 1496 TextureRef texture = attachment.texture; 1497 if (texture) 1498 { 1499 cmdBuffer().setWriteDependency(texture, /*isRenderCommand=*/true); 1500 1501 const MipmapNativeLevel &mipLevel = attachment.level; 1502 1503 mRenderPassMaxScissorRect.width = 1504 std::min<NSUInteger>(mRenderPassMaxScissorRect.width, texture->width(mipLevel)); 1505 mRenderPassMaxScissorRect.height = 1506 std::min<NSUInteger>(mRenderPassMaxScissorRect.height, texture->height(mipLevel)); 1507 } 1508} 1509 1510inline void RenderCommandEncoder::initWriteDependency(const TextureRef &texture) 1511{ 1512 if (texture) 1513 { 1514 cmdBuffer().setWriteDependency(texture, /*isRenderCommand=*/true); 1515 } 1516} 1517 1518void RenderCommandEncoder::simulateDiscardFramebuffer() 1519{ 1520 // Simulate true framebuffer discard operation by clearing the framebuffer 1521#if ANGLE_MTL_SIMULATE_DISCARD_FRAMEBUFFER 1522 std::random_device rd; // Will be used to obtain a seed for the random number engine 1523 std::mt19937 gen(rd()); // Standard mersenne_twister_engine seeded with rd() 1524 std::uniform_real_distribution<float> dis(0.0, 1.0f); 1525 bool hasDiscard = false; 1526 for (uint32_t i = 0; i < mRenderPassDesc.numColorAttachments; ++i) 1527 { 1528 if (mRenderPassDesc.colorAttachments[i].storeAction == MTLStoreActionDontCare) 1529 { 1530 hasDiscard = true; 1531 mRenderPassDesc.colorAttachments[i].loadAction = MTLLoadActionClear; 1532 mRenderPassDesc.colorAttachments[i].clearColor = 1533 MTLClearColorMake(dis(gen), dis(gen), dis(gen), dis(gen)); 1534 } 1535 else 1536 { 1537 mRenderPassDesc.colorAttachments[i].loadAction = MTLLoadActionLoad; 1538 } 1539 } 1540 1541 if (mRenderPassDesc.depthAttachment.storeAction == MTLStoreActionDontCare) 1542 { 1543 hasDiscard = true; 1544 mRenderPassDesc.depthAttachment.loadAction = MTLLoadActionClear; 1545 mRenderPassDesc.depthAttachment.clearDepth = dis(gen); 1546 } 1547 else 1548 { 1549 mRenderPassDesc.depthAttachment.loadAction = MTLLoadActionLoad; 1550 } 1551 1552 if (mRenderPassDesc.stencilAttachment.storeAction == MTLStoreActionDontCare) 1553 { 1554 hasDiscard = true; 1555 mRenderPassDesc.stencilAttachment.loadAction = MTLLoadActionClear; 1556 mRenderPassDesc.stencilAttachment.clearStencil = rand(); 1557 } 1558 else 1559 { 1560 mRenderPassDesc.stencilAttachment.loadAction = MTLLoadActionLoad; 1561 } 1562 1563 if (hasDiscard) 1564 { 1565 MTLRenderPassDescriptor tmpDesc = mRenderPassDesc; 1566 restart(tmpDesc); 1567 endEncodingImpl(false); 1568 } 1569#endif // ANGLE_MTL_SIMULATE_DISCARD_FRAMEBUFFER 1570} 1571 1572void RenderCommandEncoder::encodeMetalEncoder() 1573{ 1574 ANGLE_MTL_OBJC_SCOPE 1575 { 1576 ANGLE_MTL_LOG("Creating new render command encoder with desc: %@", 1577 [mCachedRenderPassDescObjC description]); 1578 1579 id<MTLRenderCommandEncoder> metalCmdEncoder = 1580 [cmdBuffer().get() renderCommandEncoderWithDescriptor:mCachedRenderPassDescObjC]; 1581 1582 set(metalCmdEncoder); 1583 1584 // Verify that it was created successfully 1585 ASSERT(get()); 1586 1587 // Work-around driver bug on iOS devices: stencil must be explicitly set to zero 1588 // even if the doc says the default value is already zero. 1589 [metalCmdEncoder setStencilReferenceValue:0]; 1590 1591 if (mLabel) 1592 { 1593 metalCmdEncoder.label = mLabel; 1594 } 1595 1596 while (mCommands.good()) 1597 { 1598 CmdType cmdType = mCommands.fetch<CmdType>(); 1599 switch (cmdType) 1600 { 1601#define ANGLE_MTL_CMD_MAP(CMD) \ 1602 case CmdType::CMD: \ 1603 CMD##Cmd(metalCmdEncoder, &mCommands); \ 1604 break; 1605 ANGLE_MTL_CMD_X(ANGLE_MTL_CMD_MAP) 1606#undef ANGLE_MTL_CMD_MAP 1607 } 1608 } 1609 1610 mCommands.clear(); 1611 } 1612} 1613 1614RenderCommandEncoder &RenderCommandEncoder::restart(const RenderPassDesc &desc, 1615 uint32_t deviceMaxRenderTargets) 1616{ 1617 if (valid()) 1618 { 1619 if (mRenderPassDesc == desc) 1620 { 1621 // no change, skip 1622 return *this; 1623 } 1624 1625 // finish current encoder 1626 endEncoding(); 1627 } 1628 1629 if (!cmdBuffer().ready()) 1630 { 1631 reset(); 1632 return *this; 1633 } 1634 1635 mRenderPassDesc = desc; 1636 mRecording = true; 1637 mHasDrawCalls = false; 1638 mRenderPassMaxScissorRect = {.x = 0, 1639 .y = 0, 1640 .width = std::numeric_limits<NSUInteger>::max(), 1641 .height = std::numeric_limits<NSUInteger>::max()}; 1642 // mask writing dependency & set appropriate store options 1643 for (uint32_t i = 0; i < mRenderPassDesc.numColorAttachments; ++i) 1644 { 1645 initAttachmentWriteDependencyAndScissorRect(mRenderPassDesc.colorAttachments[i]); 1646 } 1647 1648 initAttachmentWriteDependencyAndScissorRect(mRenderPassDesc.depthAttachment); 1649 1650 initAttachmentWriteDependencyAndScissorRect(mRenderPassDesc.stencilAttachment); 1651 1652 // Convert to Objective-C descriptor 1653 mRenderPassDesc.convertToMetalDesc(mCachedRenderPassDescObjC, deviceMaxRenderTargets); 1654 1655 // The actual Objective-C encoder will be created later in endEncoding(), we do so in order 1656 // to be able to sort the commands or do the preprocessing before the actual encoding. 1657 1658 // Since we defer the native encoder creation, we need to explicitly tell command buffer that 1659 // this object is the active encoder: 1660 cmdBuffer().setActiveCommandEncoder(this); 1661 1662 return *this; 1663} 1664 1665RenderCommandEncoder &RenderCommandEncoder::setRenderPipelineState(id<MTLRenderPipelineState> state) 1666{ 1667 mPipelineStateSet = true; 1668 if (mStateCache.renderPipeline == state) 1669 { 1670 return *this; 1671 } 1672 mStateCache.renderPipeline = state; 1673 1674 mCommands.push(CmdType::SetRenderPipelineState).push([state ANGLE_MTL_RETAIN]); 1675 1676 return *this; 1677} 1678RenderCommandEncoder &RenderCommandEncoder::setTriangleFillMode(MTLTriangleFillMode mode) 1679{ 1680 if (mStateCache.triangleFillMode == mode) 1681 { 1682 return *this; 1683 } 1684 mStateCache.triangleFillMode = mode; 1685 1686 mCommands.push(CmdType::SetTriangleFillMode).push(mode); 1687 1688 return *this; 1689} 1690RenderCommandEncoder &RenderCommandEncoder::setFrontFacingWinding(MTLWinding winding) 1691{ 1692 if (mStateCache.winding == winding) 1693 { 1694 return *this; 1695 } 1696 mStateCache.winding = winding; 1697 1698 mCommands.push(CmdType::SetFrontFacingWinding).push(winding); 1699 1700 return *this; 1701} 1702RenderCommandEncoder &RenderCommandEncoder::setCullMode(MTLCullMode mode) 1703{ 1704 if (mStateCache.cullMode == mode) 1705 { 1706 return *this; 1707 } 1708 mStateCache.cullMode = mode; 1709 1710 mCommands.push(CmdType::SetCullMode).push(mode); 1711 1712 return *this; 1713} 1714 1715RenderCommandEncoder &RenderCommandEncoder::setDepthStencilState(id<MTLDepthStencilState> state) 1716{ 1717 if (mStateCache.depthStencilState == state) 1718 { 1719 return *this; 1720 } 1721 mStateCache.depthStencilState = state; 1722 1723 mCommands.push(CmdType::SetDepthStencilState).push([state ANGLE_MTL_RETAIN]); 1724 1725 return *this; 1726} 1727RenderCommandEncoder &RenderCommandEncoder::setDepthBias(float depthBias, 1728 float slopeScale, 1729 float clamp) 1730{ 1731 if (mStateCache.depthBias == depthBias && mStateCache.depthSlopeScale == slopeScale && 1732 mStateCache.depthClamp == clamp) 1733 { 1734 return *this; 1735 } 1736 mStateCache.depthBias = depthBias; 1737 mStateCache.depthSlopeScale = slopeScale; 1738 mStateCache.depthClamp = clamp; 1739 1740 mCommands.push(CmdType::SetDepthBias).push(depthBias).push(slopeScale).push(clamp); 1741 1742 return *this; 1743} 1744RenderCommandEncoder &RenderCommandEncoder::setDepthClipMode(MTLDepthClipMode depthClipMode) 1745{ 1746 if (mStateCache.depthClipMode == depthClipMode) 1747 { 1748 return *this; 1749 } 1750 mStateCache.depthClipMode = depthClipMode; 1751 1752 mCommands.push(CmdType::SetDepthClipMode).push(depthClipMode); 1753 1754 return *this; 1755} 1756RenderCommandEncoder &RenderCommandEncoder::setStencilRefVals(uint32_t frontRef, uint32_t backRef) 1757{ 1758 // Metal has some bugs when reference values are larger than 0xff 1759 ASSERT(frontRef == (frontRef & kStencilMaskAll)); 1760 ASSERT(backRef == (backRef & kStencilMaskAll)); 1761 1762 if (mStateCache.stencilFrontRef == frontRef && mStateCache.stencilBackRef == backRef) 1763 { 1764 return *this; 1765 } 1766 mStateCache.stencilFrontRef = frontRef; 1767 mStateCache.stencilBackRef = backRef; 1768 1769 mCommands.push(CmdType::SetStencilRefVals).push(frontRef).push(backRef); 1770 1771 return *this; 1772} 1773 1774RenderCommandEncoder &RenderCommandEncoder::setStencilRefVal(uint32_t ref) 1775{ 1776 return setStencilRefVals(ref, ref); 1777} 1778 1779RenderCommandEncoder &RenderCommandEncoder::setViewport(const MTLViewport &viewport) 1780{ 1781 if (mStateCache.viewport.valid() && mStateCache.viewport.value() == viewport) 1782 { 1783 return *this; 1784 } 1785 mStateCache.viewport = viewport; 1786 1787 mCommands.push(CmdType::SetViewport).push(viewport); 1788 1789 return *this; 1790} 1791 1792RenderCommandEncoder &RenderCommandEncoder::setScissorRect(const MTLScissorRect &rect) 1793{ 1794 NSUInteger clampedWidth = 1795 rect.x > mRenderPassMaxScissorRect.width ? 0 : mRenderPassMaxScissorRect.width - rect.x; 1796 NSUInteger clampedHeight = 1797 rect.y > mRenderPassMaxScissorRect.height ? 0 : mRenderPassMaxScissorRect.height - rect.y; 1798 1799 MTLScissorRect clampedRect = {rect.x, rect.y, std::min(rect.width, clampedWidth), 1800 std::min(rect.height, clampedHeight)}; 1801 1802 if (mStateCache.scissorRect.valid() && mStateCache.scissorRect.value() == clampedRect) 1803 { 1804 return *this; 1805 } 1806 1807 mStateCache.scissorRect = clampedRect; 1808 1809 mCommands.push(CmdType::SetScissorRect).push(clampedRect); 1810 1811 return *this; 1812} 1813 1814RenderCommandEncoder &RenderCommandEncoder::setBlendColor(float r, float g, float b, float a) 1815{ 1816 if (mStateCache.blendColor[0] == r && mStateCache.blendColor[1] == g && 1817 mStateCache.blendColor[2] == b && mStateCache.blendColor[3] == a) 1818 { 1819 return *this; 1820 } 1821 mStateCache.blendColor[0] = r; 1822 mStateCache.blendColor[1] = g; 1823 mStateCache.blendColor[2] = b; 1824 mStateCache.blendColor[3] = a; 1825 1826 mCommands.push(CmdType::SetBlendColor).push(r).push(g).push(b).push(a); 1827 1828 return *this; 1829} 1830 1831RenderCommandEncoder &RenderCommandEncoder::setBuffer(gl::ShaderType shaderType, 1832 const BufferRef &buffer, 1833 uint32_t offset, 1834 uint32_t index) 1835{ 1836 if (index >= kMaxShaderBuffers) 1837 { 1838 return *this; 1839 } 1840 1841 cmdBuffer().setReadDependency(buffer, /*isRenderCommand=*/true); 1842 1843 id<MTLBuffer> mtlBuffer = (buffer ? buffer->get() : nil); 1844 1845 return commonSetBuffer(shaderType, mtlBuffer, offset, index); 1846} 1847 1848RenderCommandEncoder &RenderCommandEncoder::setBufferForWrite(gl::ShaderType shaderType, 1849 const BufferRef &buffer, 1850 uint32_t offset, 1851 uint32_t index) 1852{ 1853 if (index >= kMaxShaderBuffers) 1854 { 1855 return *this; 1856 } 1857 1858 cmdBuffer().setWriteDependency(buffer, /*isRenderCommand=*/true); 1859 1860 id<MTLBuffer> mtlBuffer = (buffer ? buffer->get() : nil); 1861 1862 return commonSetBuffer(shaderType, mtlBuffer, offset, index); 1863} 1864 1865RenderCommandEncoder &RenderCommandEncoder::commonSetBuffer(gl::ShaderType shaderType, 1866 id<MTLBuffer> mtlBuffer, 1867 uint32_t offset, 1868 uint32_t index) 1869{ 1870 RenderCommandEncoderShaderStates &shaderStates = mStateCache.perShaderStates[shaderType]; 1871 if (shaderStates.buffers[index] == mtlBuffer) 1872 { 1873 if (shaderStates.bufferOffsets[index] == offset) 1874 { 1875 return *this; 1876 } 1877 1878 // If buffer already bound but with different offset, then update the offer only. 1879 shaderStates.bufferOffsets[index] = offset; 1880 1881 mCommands.push(static_cast<CmdType>(mSetBufferOffsetCmds[shaderType])) 1882 .push(offset) 1883 .push(index); 1884 1885 return *this; 1886 } 1887 1888 shaderStates.buffers[index] = mtlBuffer; 1889 shaderStates.bufferOffsets[index] = offset; 1890 1891 mCommands.push(static_cast<CmdType>(mSetBufferCmds[shaderType])) 1892 .push([mtlBuffer ANGLE_MTL_RETAIN]) 1893 .push(offset) 1894 .push(index); 1895 1896 return *this; 1897} 1898 1899RenderCommandEncoder &RenderCommandEncoder::setBytes(gl::ShaderType shaderType, 1900 const uint8_t *bytes, 1901 size_t size, 1902 uint32_t index) 1903{ 1904 if (index >= kMaxShaderBuffers) 1905 { 1906 return *this; 1907 } 1908 1909 RenderCommandEncoderShaderStates &shaderStates = mStateCache.perShaderStates[shaderType]; 1910 shaderStates.buffers[index] = nil; 1911 shaderStates.bufferOffsets[index] = 0; 1912 1913 mCommands.push(static_cast<CmdType>(mSetBytesCmds[shaderType])) 1914 .push(size) 1915 .push(bytes, size) 1916 .push(index); 1917 1918 return *this; 1919} 1920 1921RenderCommandEncoder &RenderCommandEncoder::setSamplerState(gl::ShaderType shaderType, 1922 id<MTLSamplerState> state, 1923 float lodMinClamp, 1924 float lodMaxClamp, 1925 uint32_t index) 1926{ 1927 if (index >= kMaxShaderSamplers) 1928 { 1929 return *this; 1930 } 1931 1932 RenderCommandEncoderShaderStates &shaderStates = mStateCache.perShaderStates[shaderType]; 1933 if (shaderStates.samplers[index] == state && shaderStates.samplerLodClamps[index].valid()) 1934 { 1935 const std::pair<float, float> ¤tLodClampRange = 1936 shaderStates.samplerLodClamps[index].value(); 1937 if (currentLodClampRange.first == lodMinClamp && currentLodClampRange.second == lodMaxClamp) 1938 { 1939 return *this; 1940 } 1941 } 1942 1943 shaderStates.samplers[index] = state; 1944 shaderStates.samplerLodClamps[index] = {lodMinClamp, lodMaxClamp}; 1945 1946 mCommands.push(static_cast<CmdType>(mSetSamplerCmds[shaderType])) 1947 .push([state ANGLE_MTL_RETAIN]) 1948 .push(lodMinClamp) 1949 .push(lodMaxClamp) 1950 .push(index); 1951 1952 return *this; 1953} 1954 1955RenderCommandEncoder &RenderCommandEncoder::setTexture(gl::ShaderType shaderType, 1956 const TextureRef &texture, 1957 uint32_t index) 1958{ 1959 if (index >= kMaxShaderSamplers) 1960 { 1961 return *this; 1962 } 1963 1964 cmdBuffer().setReadDependency(texture, /*isRenderCommand=*/true); 1965 1966 id<MTLTexture> mtlTexture = (texture ? texture->get() : nil); 1967 1968 RenderCommandEncoderShaderStates &shaderStates = mStateCache.perShaderStates[shaderType]; 1969 if (shaderStates.textures[index] == mtlTexture) 1970 { 1971 return *this; 1972 } 1973 shaderStates.textures[index] = mtlTexture; 1974 1975 mCommands.push(static_cast<CmdType>(mSetTextureCmds[shaderType])) 1976 .push([mtlTexture ANGLE_MTL_RETAIN]) 1977 .push(index); 1978 1979 return *this; 1980} 1981 1982RenderCommandEncoder &RenderCommandEncoder::setRWTexture(gl::ShaderType shaderType, 1983 const TextureRef &texture, 1984 uint32_t index) 1985{ 1986 if (index >= kMaxShaderSamplers) 1987 { 1988 return *this; 1989 } 1990 1991 cmdBuffer().setWriteDependency(texture, /*isRenderCommand=*/true); 1992 return setTexture(shaderType, texture, index); 1993} 1994 1995RenderCommandEncoder &RenderCommandEncoder::draw(MTLPrimitiveType primitiveType, 1996 uint32_t vertexStart, 1997 uint32_t vertexCount) 1998{ 1999 ASSERT(mPipelineStateSet && 2000 "Render Pipeline State was never set and we've issued a draw command."); 2001 CheckPrimitiveType(primitiveType); 2002 mHasDrawCalls = true; 2003 mCommands.push(CmdType::Draw).push(primitiveType).push(vertexStart).push(vertexCount); 2004 2005 return *this; 2006} 2007 2008RenderCommandEncoder &RenderCommandEncoder::drawInstanced(MTLPrimitiveType primitiveType, 2009 uint32_t vertexStart, 2010 uint32_t vertexCount, 2011 uint32_t instances) 2012{ 2013 ASSERT(mPipelineStateSet && 2014 "Render Pipeline State was never set and we've issued a draw command."); 2015 CheckPrimitiveType(primitiveType); 2016 mHasDrawCalls = true; 2017 mCommands.push(CmdType::DrawInstanced) 2018 .push(primitiveType) 2019 .push(vertexStart) 2020 .push(vertexCount) 2021 .push(instances); 2022 2023 return *this; 2024} 2025 2026RenderCommandEncoder &RenderCommandEncoder::drawInstancedBaseInstance( 2027 MTLPrimitiveType primitiveType, 2028 uint32_t vertexStart, 2029 uint32_t vertexCount, 2030 uint32_t instances, 2031 uint32_t baseInstance) 2032{ 2033 ASSERT(mPipelineStateSet && 2034 "Render Pipeline State was never set and we've issued a draw command."); 2035 CheckPrimitiveType(primitiveType); 2036 mHasDrawCalls = true; 2037 mCommands.push(CmdType::DrawInstancedBaseInstance) 2038 .push(primitiveType) 2039 .push(vertexStart) 2040 .push(vertexCount) 2041 .push(instances) 2042 .push(baseInstance); 2043 2044 return *this; 2045} 2046 2047RenderCommandEncoder &RenderCommandEncoder::drawIndexed(MTLPrimitiveType primitiveType, 2048 uint32_t indexCount, 2049 MTLIndexType indexType, 2050 const BufferRef &indexBuffer, 2051 size_t bufferOffset) 2052{ 2053 ASSERT(mPipelineStateSet && 2054 "Render Pipeline State was never set and we've issued a draw command."); 2055 CheckPrimitiveType(primitiveType); 2056 if (!indexBuffer) 2057 { 2058 return *this; 2059 } 2060 2061 mHasDrawCalls = true; 2062 cmdBuffer().setReadDependency(indexBuffer, /*isRenderCommand=*/true); 2063 2064 mCommands.push(CmdType::DrawIndexed) 2065 .push(primitiveType) 2066 .push(indexCount) 2067 .push(indexType) 2068 .push([indexBuffer->get() ANGLE_MTL_RETAIN]) 2069 .push(bufferOffset); 2070 2071 return *this; 2072} 2073 2074RenderCommandEncoder &RenderCommandEncoder::drawIndexedInstanced(MTLPrimitiveType primitiveType, 2075 uint32_t indexCount, 2076 MTLIndexType indexType, 2077 const BufferRef &indexBuffer, 2078 size_t bufferOffset, 2079 uint32_t instances) 2080{ 2081 ASSERT(mPipelineStateSet && 2082 "Render Pipeline State was never set and we've issued a draw command."); 2083 CheckPrimitiveType(primitiveType); 2084 if (!indexBuffer) 2085 { 2086 return *this; 2087 } 2088 2089 mHasDrawCalls = true; 2090 cmdBuffer().setReadDependency(indexBuffer, /*isRenderCommand=*/true); 2091 2092 mCommands.push(CmdType::DrawIndexedInstanced) 2093 .push(primitiveType) 2094 .push(indexCount) 2095 .push(indexType) 2096 .push([indexBuffer->get() ANGLE_MTL_RETAIN]) 2097 .push(bufferOffset) 2098 .push(instances); 2099 2100 return *this; 2101} 2102 2103RenderCommandEncoder &RenderCommandEncoder::drawIndexedInstancedBaseVertexBaseInstance( 2104 MTLPrimitiveType primitiveType, 2105 uint32_t indexCount, 2106 MTLIndexType indexType, 2107 const BufferRef &indexBuffer, 2108 size_t bufferOffset, 2109 uint32_t instances, 2110 uint32_t baseVertex, 2111 uint32_t baseInstance) 2112{ 2113 ASSERT(mPipelineStateSet && 2114 "Render Pipeline State was never set and we've issued a draw command."); 2115 CheckPrimitiveType(primitiveType); 2116 if (!indexBuffer) 2117 { 2118 return *this; 2119 } 2120 2121 mHasDrawCalls = true; 2122 cmdBuffer().setReadDependency(indexBuffer, /*isRenderCommand=*/true); 2123 2124 mCommands.push(CmdType::DrawIndexedInstancedBaseVertexBaseInstance) 2125 .push(primitiveType) 2126 .push(indexCount) 2127 .push(indexType) 2128 .push([indexBuffer->get() ANGLE_MTL_RETAIN]) 2129 .push(bufferOffset) 2130 .push(instances) 2131 .push(baseVertex) 2132 .push(baseInstance); 2133 2134 return *this; 2135} 2136 2137RenderCommandEncoder &RenderCommandEncoder::setVisibilityResultMode(MTLVisibilityResultMode mode, 2138 size_t offset) 2139{ 2140 if (mStateCache.visibilityResultMode == mode && 2141 mStateCache.visibilityResultBufferOffset == offset) 2142 { 2143 return *this; 2144 } 2145 mStateCache.visibilityResultMode = mode; 2146 mStateCache.visibilityResultBufferOffset = offset; 2147 2148 mCommands.push(CmdType::SetVisibilityResultMode).push(mode).push(offset); 2149 return *this; 2150} 2151 2152RenderCommandEncoder &RenderCommandEncoder::useResource(const BufferRef &resource, 2153 MTLResourceUsage usage, 2154 mtl::RenderStages states) 2155{ 2156 if (!resource) 2157 { 2158 return *this; 2159 } 2160 2161 cmdBuffer().setReadDependency(resource, /*isRenderCommand=*/true); 2162 2163 mCommands.push(CmdType::UseResource) 2164 .push([resource->get() ANGLE_MTL_RETAIN]) 2165 .push(usage) 2166 .push(states); 2167 2168 return *this; 2169} 2170 2171RenderCommandEncoder &RenderCommandEncoder::memoryBarrier(mtl::BarrierScope scope, 2172 mtl::RenderStages after, 2173 mtl::RenderStages before) 2174{ 2175 mCommands.push(CmdType::MemoryBarrier).push(scope).push(after).push(before); 2176 return *this; 2177} 2178 2179RenderCommandEncoder &RenderCommandEncoder::memoryBarrierWithResource(const BufferRef &resource, 2180 mtl::RenderStages after, 2181 mtl::RenderStages before) 2182{ 2183 if (!resource) 2184 { 2185 return *this; 2186 } 2187 2188 cmdBuffer().setWriteDependency(resource, /*isRenderCommand=*/true); 2189 2190 mCommands.push(CmdType::MemoryBarrierWithResource) 2191 .push([resource->get() ANGLE_MTL_RETAIN]) 2192 .push(after) 2193 .push(before); 2194 2195 return *this; 2196} 2197 2198void RenderCommandEncoder::insertDebugSignImpl(NSString *label) 2199{ 2200 // Defer the insertion until endEncoding() 2201 mCommands.push(CmdType::InsertDebugsign).push([label ANGLE_MTL_RETAIN]); 2202} 2203 2204void RenderCommandEncoder::pushDebugGroup(NSString *label) 2205{ 2206 // Defer the insertion until endEncoding() 2207 mCommands.push(CmdType::PushDebugGroup).push([label ANGLE_MTL_RETAIN]); 2208} 2209void RenderCommandEncoder::popDebugGroup() 2210{ 2211 mCommands.push(CmdType::PopDebugGroup); 2212} 2213 2214RenderCommandEncoder &RenderCommandEncoder::setColorStoreAction(MTLStoreAction action, 2215 uint32_t colorAttachmentIndex) 2216{ 2217 if (colorAttachmentIndex >= mRenderPassDesc.numColorAttachments) 2218 { 2219 return *this; 2220 } 2221 2222 // We only store the options, will defer the actual setting until the encoder finishes 2223 mRenderPassDesc.colorAttachments[colorAttachmentIndex].storeAction = action; 2224 2225 return *this; 2226} 2227 2228RenderCommandEncoder &RenderCommandEncoder::setColorStoreAction(MTLStoreAction action) 2229{ 2230 for (uint32_t i = 0; i < mRenderPassDesc.numColorAttachments; ++i) 2231 { 2232 setColorStoreAction(action, i); 2233 } 2234 return *this; 2235} 2236 2237RenderCommandEncoder &RenderCommandEncoder::setDepthStencilStoreAction( 2238 MTLStoreAction depthStoreAction, 2239 MTLStoreAction stencilStoreAction) 2240{ 2241 // We only store the options, will defer the actual setting until the encoder finishes 2242 mRenderPassDesc.depthAttachment.storeAction = depthStoreAction; 2243 mRenderPassDesc.stencilAttachment.storeAction = stencilStoreAction; 2244 2245 return *this; 2246} 2247 2248RenderCommandEncoder &RenderCommandEncoder::setDepthStoreAction(MTLStoreAction action) 2249{ 2250 // We only store the options, will defer the actual setting until the encoder finishes 2251 mRenderPassDesc.depthAttachment.storeAction = action; 2252 2253 return *this; 2254} 2255 2256RenderCommandEncoder &RenderCommandEncoder::setStencilStoreAction(MTLStoreAction action) 2257{ 2258 // We only store the options, will defer the actual setting until the encoder finishes 2259 mRenderPassDesc.stencilAttachment.storeAction = action; 2260 2261 return *this; 2262} 2263 2264RenderCommandEncoder &RenderCommandEncoder::setStoreAction(MTLStoreAction action) 2265{ 2266 setColorStoreAction(action); 2267 setDepthStencilStoreAction(action, action); 2268 return *this; 2269} 2270 2271RenderCommandEncoder &RenderCommandEncoder::setColorLoadAction(MTLLoadAction action, 2272 const MTLClearColor &clearValue, 2273 uint32_t colorAttachmentIndex) 2274{ 2275 ASSERT(!hasDrawCalls()); 2276 if (mCachedRenderPassDescObjC.get().colorAttachments[colorAttachmentIndex].texture) 2277 { 2278 mCachedRenderPassDescObjC.get().colorAttachments[colorAttachmentIndex].loadAction = action; 2279 mCachedRenderPassDescObjC.get().colorAttachments[colorAttachmentIndex].clearColor = 2280 clearValue; 2281 } 2282 return *this; 2283} 2284 2285RenderCommandEncoder &RenderCommandEncoder::setDepthLoadAction(MTLLoadAction action, 2286 double clearVal) 2287{ 2288 ASSERT(!hasDrawCalls()); 2289 if (mCachedRenderPassDescObjC.get().depthAttachment.texture) 2290 { 2291 mCachedRenderPassDescObjC.get().depthAttachment.loadAction = action; 2292 mCachedRenderPassDescObjC.get().depthAttachment.clearDepth = clearVal; 2293 } 2294 return *this; 2295} 2296 2297RenderCommandEncoder &RenderCommandEncoder::setStencilLoadAction(MTLLoadAction action, 2298 uint32_t clearVal) 2299{ 2300 ASSERT(!hasDrawCalls()); 2301 if (mCachedRenderPassDescObjC.get().stencilAttachment.texture) 2302 { 2303 mCachedRenderPassDescObjC.get().stencilAttachment.loadAction = action; 2304 mCachedRenderPassDescObjC.get().stencilAttachment.clearStencil = clearVal; 2305 } 2306 return *this; 2307} 2308 2309void RenderCommandEncoder::setLabel(NSString *label) 2310{ 2311 mLabel.retainAssign(label); 2312} 2313 2314// BlitCommandEncoder 2315BlitCommandEncoder::BlitCommandEncoder(CommandBuffer *cmdBuffer) : CommandEncoder(cmdBuffer, BLIT) 2316{} 2317 2318BlitCommandEncoder::~BlitCommandEncoder() {} 2319 2320BlitCommandEncoder &BlitCommandEncoder::restart() 2321{ 2322 ANGLE_MTL_OBJC_SCOPE 2323 { 2324 if (valid()) 2325 { 2326 // no change, skip 2327 return *this; 2328 } 2329 2330 if (!cmdBuffer().ready()) 2331 { 2332 reset(); 2333 return *this; 2334 } 2335 2336 // Create objective C object 2337 set([cmdBuffer().get() blitCommandEncoder]); 2338 2339 // Verify that it was created successfully 2340 ASSERT(get()); 2341 2342 return *this; 2343 } 2344} 2345 2346BlitCommandEncoder &BlitCommandEncoder::copyBuffer(const BufferRef &src, 2347 size_t srcOffset, 2348 const BufferRef &dst, 2349 size_t dstOffset, 2350 size_t size) 2351{ 2352 if (!src || !dst) 2353 { 2354 return *this; 2355 } 2356 2357 cmdBuffer().setReadDependency(src, /*isRenderCommand=*/false); 2358 cmdBuffer().setWriteDependency(dst, /*isRenderCommand=*/false); 2359 2360 [get() copyFromBuffer:src->get() 2361 sourceOffset:srcOffset 2362 toBuffer:dst->get() 2363 destinationOffset:dstOffset 2364 size:size]; 2365 2366 return *this; 2367} 2368 2369BlitCommandEncoder &BlitCommandEncoder::copyBufferToTexture(const BufferRef &src, 2370 size_t srcOffset, 2371 size_t srcBytesPerRow, 2372 size_t srcBytesPerImage, 2373 MTLSize srcSize, 2374 const TextureRef &dst, 2375 uint32_t dstSlice, 2376 MipmapNativeLevel dstLevel, 2377 MTLOrigin dstOrigin, 2378 MTLBlitOption blitOption) 2379{ 2380 if (!src || !dst) 2381 { 2382 return *this; 2383 } 2384 2385 cmdBuffer().setReadDependency(src, /*isRenderCommand=*/false); 2386 cmdBuffer().setWriteDependency(dst, /*isRenderCommand=*/false); 2387 2388 [get() copyFromBuffer:src->get() 2389 sourceOffset:srcOffset 2390 sourceBytesPerRow:srcBytesPerRow 2391 sourceBytesPerImage:srcBytesPerImage 2392 sourceSize:srcSize 2393 toTexture:dst->get() 2394 destinationSlice:dstSlice 2395 destinationLevel:dstLevel.get() 2396 destinationOrigin:dstOrigin 2397 options:blitOption]; 2398 2399 return *this; 2400} 2401 2402BlitCommandEncoder &BlitCommandEncoder::copyTextureToBuffer(const TextureRef &src, 2403 uint32_t srcSlice, 2404 MipmapNativeLevel srcLevel, 2405 MTLOrigin srcOrigin, 2406 MTLSize srcSize, 2407 const BufferRef &dst, 2408 size_t dstOffset, 2409 size_t dstBytesPerRow, 2410 size_t dstBytesPerImage, 2411 MTLBlitOption blitOption) 2412{ 2413 2414 if (!src || !dst) 2415 { 2416 return *this; 2417 } 2418 2419 cmdBuffer().setReadDependency(src, /*isRenderCommand=*/false); 2420 cmdBuffer().setWriteDependency(dst, /*isRenderCommand=*/false); 2421 2422 [get() copyFromTexture:src->get() 2423 sourceSlice:srcSlice 2424 sourceLevel:srcLevel.get() 2425 sourceOrigin:srcOrigin 2426 sourceSize:srcSize 2427 toBuffer:dst->get() 2428 destinationOffset:dstOffset 2429 destinationBytesPerRow:dstBytesPerRow 2430 destinationBytesPerImage:dstBytesPerImage 2431 options:blitOption]; 2432 2433 return *this; 2434} 2435 2436BlitCommandEncoder &BlitCommandEncoder::copyTexture(const TextureRef &src, 2437 uint32_t srcStartSlice, 2438 MipmapNativeLevel srcStartLevel, 2439 const TextureRef &dst, 2440 uint32_t dstStartSlice, 2441 MipmapNativeLevel dstStartLevel, 2442 uint32_t sliceCount, 2443 uint32_t levelCount) 2444{ 2445 if (!src || !dst) 2446 { 2447 return *this; 2448 } 2449 2450 cmdBuffer().setReadDependency(src, /*isRenderCommand=*/false); 2451 cmdBuffer().setWriteDependency(dst, /*isRenderCommand=*/false); 2452 2453 MTLOrigin origin = MTLOriginMake(0, 0, 0); 2454 for (uint32_t slice = 0; slice < sliceCount; ++slice) 2455 { 2456 uint32_t srcSlice = srcStartSlice + slice; 2457 uint32_t dstSlice = dstStartSlice + slice; 2458 for (uint32_t level = 0; level < levelCount; ++level) 2459 { 2460 MipmapNativeLevel srcLevel = srcStartLevel + level; 2461 MipmapNativeLevel dstLevel = dstStartLevel + level; 2462 MTLSize srcSize = 2463 MTLSizeMake(src->width(srcLevel), src->height(srcLevel), src->depth(srcLevel)); 2464 2465 [get() copyFromTexture:src->get() 2466 sourceSlice:srcSlice 2467 sourceLevel:srcLevel.get() 2468 sourceOrigin:origin 2469 sourceSize:srcSize 2470 toTexture:dst->get() 2471 destinationSlice:dstSlice 2472 destinationLevel:dstLevel.get() 2473 destinationOrigin:origin]; 2474 } 2475 } 2476 2477 return *this; 2478} 2479 2480BlitCommandEncoder &BlitCommandEncoder::fillBuffer(const BufferRef &buffer, 2481 NSRange range, 2482 uint8_t value) 2483{ 2484 if (!buffer) 2485 { 2486 return *this; 2487 } 2488 2489 [get() fillBuffer:buffer->get() range:range value:value]; 2490 return *this; 2491} 2492 2493BlitCommandEncoder &BlitCommandEncoder::generateMipmapsForTexture(const TextureRef &texture) 2494{ 2495 if (!texture) 2496 { 2497 return *this; 2498 } 2499 2500 cmdBuffer().setWriteDependency(texture, /*isRenderCommand=*/false); 2501 [get() generateMipmapsForTexture:texture->get()]; 2502 2503 return *this; 2504} 2505BlitCommandEncoder &BlitCommandEncoder::synchronizeResource(Buffer *buffer) 2506{ 2507 if (!buffer) 2508 { 2509 return *this; 2510 } 2511 2512#if TARGET_OS_OSX || TARGET_OS_MACCATALYST 2513 if (buffer->get().storageMode == MTLStorageModeManaged) 2514 { 2515 // Only MacOS has separated storage for resource on CPU and GPU and needs explicit 2516 // synchronization 2517 cmdBuffer().setReadDependency(buffer, /*isRenderCommand=*/false); 2518 2519 [get() synchronizeResource:buffer->get()]; 2520 } 2521#endif 2522 return *this; 2523} 2524BlitCommandEncoder &BlitCommandEncoder::synchronizeResource(Texture *texture) 2525{ 2526 if (!texture) 2527 { 2528 return *this; 2529 } 2530 2531#if TARGET_OS_OSX || TARGET_OS_MACCATALYST 2532 // Only MacOS has separated storage for resource on CPU and GPU and needs explicit 2533 // synchronization 2534 cmdBuffer().setReadDependency(texture, /*isRenderCommand=*/false); 2535 if (texture->get().parentTexture) 2536 { 2537 [get() synchronizeResource:texture->get().parentTexture]; 2538 } 2539 else 2540 { 2541 [get() synchronizeResource:texture->get()]; 2542 } 2543#endif 2544 return *this; 2545} 2546 2547// ComputeCommandEncoder implementation 2548ComputeCommandEncoder::ComputeCommandEncoder(CommandBuffer *cmdBuffer) 2549 : CommandEncoder(cmdBuffer, COMPUTE) 2550{} 2551ComputeCommandEncoder::~ComputeCommandEncoder() {} 2552 2553ComputeCommandEncoder &ComputeCommandEncoder::restart() 2554{ 2555 ANGLE_MTL_OBJC_SCOPE 2556 { 2557 if (valid()) 2558 { 2559 // no change, skip 2560 return *this; 2561 } 2562 2563 if (!cmdBuffer().ready()) 2564 { 2565 reset(); 2566 return *this; 2567 } 2568 2569 // Create objective C object 2570 set([cmdBuffer().get() computeCommandEncoder]); 2571 2572 // Verify that it was created successfully 2573 ASSERT(get()); 2574 2575 return *this; 2576 } 2577} 2578 2579ComputeCommandEncoder &ComputeCommandEncoder::setComputePipelineState( 2580 id<MTLComputePipelineState> state) 2581{ 2582 [get() setComputePipelineState:state]; 2583 return *this; 2584} 2585 2586ComputeCommandEncoder &ComputeCommandEncoder::setBuffer(const BufferRef &buffer, 2587 uint32_t offset, 2588 uint32_t index) 2589{ 2590 if (index >= kMaxShaderBuffers) 2591 { 2592 return *this; 2593 } 2594 2595 cmdBuffer().setReadDependency(buffer, /*isRenderCommand=*/false); 2596 2597 [get() setBuffer:(buffer ? buffer->get() : nil) offset:offset atIndex:index]; 2598 2599 return *this; 2600} 2601 2602ComputeCommandEncoder &ComputeCommandEncoder::setBufferForWrite(const BufferRef &buffer, 2603 uint32_t offset, 2604 uint32_t index) 2605{ 2606 if (index >= kMaxShaderBuffers) 2607 { 2608 return *this; 2609 } 2610 2611 cmdBuffer().setWriteDependency(buffer, /*isRenderCommand=*/false); 2612 return setBuffer(buffer, offset, index); 2613} 2614 2615ComputeCommandEncoder &ComputeCommandEncoder::setBytes(const uint8_t *bytes, 2616 size_t size, 2617 uint32_t index) 2618{ 2619 if (index >= kMaxShaderBuffers) 2620 { 2621 return *this; 2622 } 2623 2624 [get() setBytes:bytes length:size atIndex:index]; 2625 2626 return *this; 2627} 2628 2629ComputeCommandEncoder &ComputeCommandEncoder::setSamplerState(id<MTLSamplerState> state, 2630 float lodMinClamp, 2631 float lodMaxClamp, 2632 uint32_t index) 2633{ 2634 if (index >= kMaxShaderSamplers) 2635 { 2636 return *this; 2637 } 2638 2639 [get() setSamplerState:state lodMinClamp:lodMinClamp lodMaxClamp:lodMaxClamp atIndex:index]; 2640 2641 return *this; 2642} 2643ComputeCommandEncoder &ComputeCommandEncoder::setTexture(const TextureRef &texture, uint32_t index) 2644{ 2645 if (index >= kMaxShaderSamplers) 2646 { 2647 return *this; 2648 } 2649 2650 cmdBuffer().setReadDependency(texture, /*isRenderCommand=*/false); 2651 [get() setTexture:(texture ? texture->get() : nil) atIndex:index]; 2652 2653 return *this; 2654} 2655ComputeCommandEncoder &ComputeCommandEncoder::setTextureForWrite(const TextureRef &texture, 2656 uint32_t index) 2657{ 2658 if (index >= kMaxShaderSamplers) 2659 { 2660 return *this; 2661 } 2662 2663 cmdBuffer().setWriteDependency(texture, /*isRenderCommand=*/false); 2664 return setTexture(texture, index); 2665} 2666 2667ComputeCommandEncoder &ComputeCommandEncoder::dispatch(const MTLSize &threadGroupsPerGrid, 2668 const MTLSize &threadsPerGroup) 2669{ 2670 [get() dispatchThreadgroups:threadGroupsPerGrid threadsPerThreadgroup:threadsPerGroup]; 2671 return *this; 2672} 2673 2674ComputeCommandEncoder &ComputeCommandEncoder::dispatchNonUniform(const MTLSize &threadsPerGrid, 2675 const MTLSize &threadsPerGroup) 2676{ 2677#if TARGET_OS_TV 2678 UNREACHABLE(); 2679#else 2680 [get() dispatchThreads:threadsPerGrid threadsPerThreadgroup:threadsPerGroup]; 2681#endif 2682 return *this; 2683} 2684} // namespace mtl 2685} // namespace rx 2686