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