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