• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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> &currentLodClampRange =
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