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