xref: /aosp_15_r20/external/angle/src/libANGLE/renderer/metal/ContextMtl.mm (revision 8975f5c5ed3d1c378011245431ada316dfb6f244)
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// ContextMtl.mm:
7//    Implements the class methods for ContextMtl.
8//
9
10#include "libANGLE/renderer/metal/ContextMtl.h"
11
12#include <TargetConditionals.h>
13#include <cstdint>
14
15#include "GLSLANG/ShaderLang.h"
16#include "common/debug.h"
17#include "image_util/loadimage.h"
18#include "libANGLE/Display.h"
19#include "libANGLE/Query.h"
20#include "libANGLE/TransformFeedback.h"
21#include "libANGLE/renderer/OverlayImpl.h"
22#include "libANGLE/renderer/metal/BufferMtl.h"
23#include "libANGLE/renderer/metal/CompilerMtl.h"
24#include "libANGLE/renderer/metal/DisplayMtl.h"
25#include "libANGLE/renderer/metal/FrameBufferMtl.h"
26#include "libANGLE/renderer/metal/ProgramExecutableMtl.h"
27#include "libANGLE/renderer/metal/ProgramMtl.h"
28#include "libANGLE/renderer/metal/QueryMtl.h"
29#include "libANGLE/renderer/metal/RenderBufferMtl.h"
30#include "libANGLE/renderer/metal/RenderTargetMtl.h"
31#include "libANGLE/renderer/metal/SamplerMtl.h"
32#include "libANGLE/renderer/metal/ShaderMtl.h"
33#include "libANGLE/renderer/metal/SyncMtl.h"
34#include "libANGLE/renderer/metal/TextureMtl.h"
35#include "libANGLE/renderer/metal/TransformFeedbackMtl.h"
36#include "libANGLE/renderer/metal/VertexArrayMtl.h"
37#include "libANGLE/renderer/metal/mtl_command_buffer.h"
38#include "libANGLE/renderer/metal/mtl_common.h"
39#include "libANGLE/renderer/metal/mtl_context_device.h"
40#include "libANGLE/renderer/metal/mtl_format_utils.h"
41#include "libANGLE/renderer/metal/mtl_utils.h"
42
43namespace rx
44{
45
46namespace
47{
48#if TARGET_OS_OSX
49// Unlimited triangle fan buffers
50constexpr uint32_t kMaxTriFanLineLoopBuffersPerFrame = 0;
51#else
52// Allow up to 10 buffers for trifan/line loop generation without stalling the GPU.
53constexpr uint32_t kMaxTriFanLineLoopBuffersPerFrame = 10;
54#endif
55
56#define ANGLE_MTL_XFB_DRAW(DRAW_PROC)                                                            \
57    if (!mState.isTransformFeedbackActiveUnpaused())                                             \
58    {                                                                                            \
59        /* Normal draw call */                                                                   \
60        DRAW_PROC(false);                                                                        \
61    }                                                                                            \
62    else                                                                                         \
63    {                                                                                            \
64        /* First pass: write to XFB buffers in vertex shader, fragment shader inactive */        \
65        bool rasterizationNotDisabled =                                                          \
66            mRenderPipelineDesc.rasterizationType != mtl::RenderPipelineRasterization::Disabled; \
67        if (rasterizationNotDisabled)                                                            \
68        {                                                                                        \
69            invalidateRenderPipeline();                                                          \
70        }                                                                                        \
71        DRAW_PROC(true);                                                                         \
72        if (rasterizationNotDisabled)                                                            \
73        {                                                                                        \
74            /* Second pass: full rasterization: vertex shader + fragment shader are active.      \
75               Vertex shader writes to stage output but won't write to XFB buffers */            \
76            invalidateRenderPipeline();                                                          \
77            DRAW_PROC(false);                                                                    \
78        }                                                                                        \
79    }
80
81angle::Result AllocateTriangleFanBufferFromPool(ContextMtl *context,
82                                                GLsizei vertexCount,
83                                                mtl::BufferPool *pool,
84                                                mtl::BufferRef *bufferOut,
85                                                uint32_t *offsetOut,
86                                                uint32_t *numElemsOut)
87{
88    uint32_t numIndices;
89    ANGLE_TRY(mtl::GetTriangleFanIndicesCount(context, vertexCount, &numIndices));
90
91    size_t offset;
92    pool->releaseInFlightBuffers(context);
93    ANGLE_TRY(pool->allocate(context, numIndices * sizeof(uint32_t), nullptr, bufferOut, &offset,
94                             nullptr));
95
96    *offsetOut   = static_cast<uint32_t>(offset);
97    *numElemsOut = numIndices;
98
99    return angle::Result::Continue;
100}
101
102angle::Result AllocateBufferFromPool(ContextMtl *context,
103                                     GLsizei indicesToReserve,
104                                     mtl::BufferPool *pool,
105                                     mtl::BufferRef *bufferOut,
106                                     uint32_t *offsetOut)
107{
108    size_t offset;
109    pool->releaseInFlightBuffers(context);
110    ANGLE_TRY(pool->allocate(context, indicesToReserve * sizeof(uint32_t), nullptr, bufferOut,
111                             &offset, nullptr));
112
113    *offsetOut = static_cast<uint32_t>(offset);
114
115    return angle::Result::Continue;
116}
117
118bool NeedToInvertDepthRange(float near, float far)
119{
120    return near > far;
121}
122
123bool IsTransformFeedbackOnly(const gl::State &glState)
124{
125    return glState.isTransformFeedbackActiveUnpaused() && glState.isRasterizerDiscardEnabled();
126}
127
128std::string ConvertMarkerToString(GLsizei length, const char *marker)
129{
130    std::string cppString;
131    if (length == 0)
132    {
133        cppString = marker;
134    }
135    else
136    {
137        cppString.assign(marker, length);
138    }
139    return cppString;
140}
141
142// This class constructs line loop's last segment buffer inside begin() method
143// and perform the draw of the line loop's last segment inside destructor
144class LineLoopLastSegmentHelper
145{
146  public:
147    LineLoopLastSegmentHelper() {}
148
149    ~LineLoopLastSegmentHelper()
150    {
151        if (!mLineLoopIndexBuffer)
152        {
153            return;
154        }
155
156        // Draw last segment of line loop here
157        mtl::RenderCommandEncoder *encoder = mContextMtl->getRenderCommandEncoder();
158        ASSERT(encoder);
159        encoder->drawIndexed(MTLPrimitiveTypeLine, 2, MTLIndexTypeUInt32, mLineLoopIndexBuffer, 0);
160    }
161
162    angle::Result begin(const gl::Context *context,
163                        mtl::BufferPool *indexBufferPool,
164                        GLint firstVertex,
165                        GLsizei vertexOrIndexCount,
166                        gl::DrawElementsType indexTypeOrNone,
167                        const void *indices)
168    {
169        mContextMtl = mtl::GetImpl(context);
170
171        indexBufferPool->releaseInFlightBuffers(mContextMtl);
172
173        ANGLE_TRY(indexBufferPool->allocate(mContextMtl, 2 * sizeof(uint32_t), nullptr,
174                                            &mLineLoopIndexBuffer, nullptr, nullptr));
175
176        if (indexTypeOrNone == gl::DrawElementsType::InvalidEnum)
177        {
178            ANGLE_TRY(mContextMtl->getDisplay()->getUtils().generateLineLoopLastSegment(
179                mContextMtl, firstVertex, firstVertex + vertexOrIndexCount - 1,
180                mLineLoopIndexBuffer, 0));
181        }
182        else
183        {
184            ASSERT(firstVertex == 0);
185            ANGLE_TRY(
186                mContextMtl->getDisplay()->getUtils().generateLineLoopLastSegmentFromElementsArray(
187                    mContextMtl,
188                    {indexTypeOrNone, vertexOrIndexCount, indices, mLineLoopIndexBuffer, 0}));
189        }
190
191        ANGLE_TRY(indexBufferPool->commit(mContextMtl));
192
193        return angle::Result::Continue;
194    }
195
196  private:
197    ContextMtl *mContextMtl = nullptr;
198    mtl::BufferRef mLineLoopIndexBuffer;
199};
200
201GLint GetOwnershipIdentity(const egl::AttributeMap &attribs)
202{
203    return attribs.getAsInt(EGL_CONTEXT_METAL_OWNERSHIP_IDENTITY_ANGLE, 0);
204}
205
206}  // namespace
207
208ContextMtl::ContextMtl(const gl::State &state,
209                       gl::ErrorSet *errorSet,
210                       const egl::AttributeMap &attribs,
211                       DisplayMtl *display)
212    : ContextImpl(state, errorSet),
213      mtl::Context(display),
214      mCmdBuffer(&display->cmdQueue()),
215      mRenderEncoder(&mCmdBuffer,
216                     mOcclusionQueryPool,
217                     display->getFeatures().emulateDontCareLoadWithRandomClear.enabled),
218      mBlitEncoder(&mCmdBuffer),
219      mComputeEncoder(&mCmdBuffer),
220      mDriverUniforms{},
221      mProvokingVertexHelper(this),
222      mContextDevice(GetOwnershipIdentity(attribs))
223{}
224
225ContextMtl::~ContextMtl() {}
226
227angle::Result ContextMtl::initialize(const angle::ImageLoadContext &imageLoadContext)
228{
229    for (mtl::BlendDesc &blendDesc : mBlendDescArray)
230    {
231        blendDesc.reset();
232    }
233
234    mWriteMaskArray.fill(MTLColorWriteMaskAll);
235
236    mDepthStencilDesc.reset();
237
238    mTriFanIndexBuffer.initialize(this, 0, mtl::kIndexBufferOffsetAlignment,
239                                  kMaxTriFanLineLoopBuffersPerFrame);
240    mLineLoopIndexBuffer.initialize(this, 0, mtl::kIndexBufferOffsetAlignment,
241                                    kMaxTriFanLineLoopBuffersPerFrame);
242    mLineLoopLastSegmentIndexBuffer.initialize(this, 2 * sizeof(uint32_t),
243                                               mtl::kIndexBufferOffsetAlignment,
244                                               kMaxTriFanLineLoopBuffersPerFrame);
245
246    mContextDevice.set(mDisplay->getMetalDevice());
247
248    mImageLoadContext = imageLoadContext;
249
250    return angle::Result::Continue;
251}
252
253void ContextMtl::onDestroy(const gl::Context *context)
254{
255    mTriFanIndexBuffer.destroy(this);
256    mLineLoopIndexBuffer.destroy(this);
257    mLineLoopLastSegmentIndexBuffer.destroy(this);
258    mOcclusionQueryPool.destroy(this);
259
260    mIncompleteTextures.onDestroy(context);
261    mProvokingVertexHelper.onDestroy(this);
262    mDummyXFBRenderTexture = nullptr;
263
264    mContextDevice.reset();
265}
266
267// Flush and finish.
268angle::Result ContextMtl::flush(const gl::Context *context)
269{
270    // MTLSharedEvent is available on these platforms, and callers
271    // are expected to use the EGL_ANGLE_metal_shared_event_sync
272    // extension to synchronize with ANGLE's Metal backend, if
273    // needed. This is typically required if two MTLDevices are
274    // operating on the same IOSurface.
275    flushCommandBuffer(mtl::NoWait);
276    return angle::Result::Continue;
277}
278angle::Result ContextMtl::finish(const gl::Context *context)
279{
280    ANGLE_TRY(finishCommandBuffer());
281    return angle::Result::Continue;
282}
283
284ANGLE_INLINE angle::Result ContextMtl::resyncDrawFramebufferIfNeeded(const gl::Context *context)
285{
286    // Resync the draw framebuffer if
287    // - it has incompatible attachments; OR
288    // - it had incompatible attachments during the previous operation.
289    if (ANGLE_UNLIKELY(mIncompatibleAttachments.any() || mForceResyncDrawFramebuffer))
290    {
291        if (mIncompatibleAttachments.any())
292        {
293            ANGLE_PERF_WARNING(context->getState().getDebug(), GL_DEBUG_SEVERITY_LOW,
294                               "Resyncing the draw framebuffer because it has active attachments "
295                               "incompatible with the current program outputs.");
296        }
297
298        // Ensure sync on the next operation if the current state has incompatible attachments.
299        mForceResyncDrawFramebuffer = mIncompatibleAttachments.any();
300
301        FramebufferMtl *fbo = mtl::GetImpl(getState().getDrawFramebuffer());
302        ASSERT(fbo != nullptr);
303        return fbo->syncState(context, GL_DRAW_FRAMEBUFFER, gl::Framebuffer::DirtyBits(),
304                              gl::Command::Draw);
305    }
306    return angle::Result::Continue;
307}
308
309// Drawing methods.
310angle::Result ContextMtl::drawTriFanArraysWithBaseVertex(const gl::Context *context,
311                                                         GLint first,
312                                                         GLsizei count,
313                                                         GLsizei instances,
314                                                         GLuint baseInstance)
315{
316    ASSERT((getDisplay()->getFeatures().hasBaseVertexInstancedDraw.enabled));
317
318    uint32_t genIndicesCount;
319    ANGLE_TRY(mtl::GetTriangleFanIndicesCount(this, count, &genIndicesCount));
320
321    size_t indexBufferSize = genIndicesCount * sizeof(uint32_t);
322    // We can reuse the previously generated index buffer if it has more than enough indices
323    // data already.
324    if (mTriFanArraysIndexBuffer == nullptr || mTriFanArraysIndexBuffer->size() < indexBufferSize)
325    {
326        // Re-generate a new index buffer, which the first index will be zero.
327        ANGLE_TRY(
328            mtl::Buffer::MakeBuffer(this, indexBufferSize, nullptr, &mTriFanArraysIndexBuffer));
329        ANGLE_TRY(getDisplay()->getUtils().generateTriFanBufferFromArrays(
330            this, {0, static_cast<uint32_t>(count), mTriFanArraysIndexBuffer, 0}));
331    }
332
333    ASSERT(!getState().isTransformFeedbackActiveUnpaused());
334    bool isNoOp = false;
335    ANGLE_TRY(setupDraw(context, gl::PrimitiveMode::TriangleFan, first, count, instances,
336                        gl::DrawElementsType::InvalidEnum, reinterpret_cast<const void *>(0), false,
337                        &isNoOp));
338    if (!isNoOp)
339    {
340        // Draw with the zero starting index buffer, shift the vertex index using baseVertex
341        // instanced draw:
342        mRenderEncoder.drawIndexedInstancedBaseVertexBaseInstance(
343            MTLPrimitiveTypeTriangle, genIndicesCount, MTLIndexTypeUInt32, mTriFanArraysIndexBuffer,
344            0, instances, first, baseInstance);
345    }
346
347    return angle::Result::Continue;
348}
349angle::Result ContextMtl::drawTriFanArraysLegacy(const gl::Context *context,
350                                                 GLint first,
351                                                 GLsizei count,
352                                                 GLsizei instances)
353{
354    // Legacy method is only used for GPU lacking instanced base vertex draw capabilities.
355    mtl::BufferRef genIdxBuffer;
356    uint32_t genIdxBufferOffset;
357    uint32_t genIndicesCount;
358    ANGLE_TRY(AllocateTriangleFanBufferFromPool(this, count, &mTriFanIndexBuffer, &genIdxBuffer,
359                                                &genIdxBufferOffset, &genIndicesCount));
360    ANGLE_TRY(getDisplay()->getUtils().generateTriFanBufferFromArrays(
361        this, {static_cast<uint32_t>(first), static_cast<uint32_t>(count), genIdxBuffer,
362               genIdxBufferOffset}));
363
364    ANGLE_TRY(mTriFanIndexBuffer.commit(this));
365
366    ASSERT(!getState().isTransformFeedbackActiveUnpaused());
367    bool isNoOp = false;
368    ANGLE_TRY(setupDraw(context, gl::PrimitiveMode::TriangleFan, first, count, instances,
369                        gl::DrawElementsType::InvalidEnum, reinterpret_cast<const void *>(0), false,
370                        &isNoOp));
371    if (!isNoOp)
372    {
373        mRenderEncoder.drawIndexedInstanced(MTLPrimitiveTypeTriangle, genIndicesCount,
374                                            MTLIndexTypeUInt32, genIdxBuffer, genIdxBufferOffset,
375                                            instances);
376    }
377    return angle::Result::Continue;
378}
379angle::Result ContextMtl::drawTriFanArrays(const gl::Context *context,
380                                           GLint first,
381                                           GLsizei count,
382                                           GLsizei instances,
383                                           GLuint baseInstance)
384{
385    if (count <= 3 && baseInstance == 0)
386    {
387        return drawArraysImpl(context, gl::PrimitiveMode::Triangles, first, count, instances, 0);
388    }
389    if (getDisplay()->getFeatures().hasBaseVertexInstancedDraw.enabled)
390    {
391        return drawTriFanArraysWithBaseVertex(context, first, count, instances, baseInstance);
392    }
393    return drawTriFanArraysLegacy(context, first, count, instances);
394}
395
396angle::Result ContextMtl::drawLineLoopArraysNonInstanced(const gl::Context *context,
397                                                         GLint first,
398                                                         GLsizei count)
399{
400    // Generate line loop's last segment. It will be rendered when this function exits.
401    LineLoopLastSegmentHelper lineloopHelper;
402    // Line loop helper needs to generate last segment indices before rendering command encoder
403    // starts.
404    ANGLE_TRY(lineloopHelper.begin(context, &mLineLoopLastSegmentIndexBuffer, first, count,
405                                   gl::DrawElementsType::InvalidEnum, nullptr));
406
407    return drawArraysImpl(context, gl::PrimitiveMode::LineStrip, first, count, 0, 0);
408}
409
410angle::Result ContextMtl::drawLineLoopArrays(const gl::Context *context,
411                                             GLint first,
412                                             GLsizei count,
413                                             GLsizei instances,
414                                             GLuint baseInstance)
415{
416    if (instances <= 1 && baseInstance == 0)
417    {
418        return drawLineLoopArraysNonInstanced(context, first, count);
419    }
420
421    mtl::BufferRef genIdxBuffer;
422    uint32_t genIdxBufferOffset;
423    uint32_t genIndicesCount = count + 1;
424
425    ANGLE_TRY(AllocateBufferFromPool(this, genIndicesCount, &mLineLoopIndexBuffer, &genIdxBuffer,
426                                     &genIdxBufferOffset));
427    ANGLE_TRY(getDisplay()->getUtils().generateLineLoopBufferFromArrays(
428        this, {static_cast<uint32_t>(first), static_cast<uint32_t>(count), genIdxBuffer,
429               genIdxBufferOffset}));
430
431    ANGLE_TRY(mLineLoopIndexBuffer.commit(this));
432
433    ASSERT(!getState().isTransformFeedbackActiveUnpaused());
434    bool isNoOp = false;
435    ANGLE_TRY(setupDraw(context, gl::PrimitiveMode::LineLoop, first, count, instances,
436                        gl::DrawElementsType::InvalidEnum, nullptr, false, &isNoOp));
437    if (!isNoOp)
438    {
439        if (baseInstance == 0)
440        {
441            mRenderEncoder.drawIndexedInstanced(MTLPrimitiveTypeLineStrip, genIndicesCount,
442                                                MTLIndexTypeUInt32, genIdxBuffer,
443                                                genIdxBufferOffset, instances);
444        }
445        else
446        {
447            mRenderEncoder.drawIndexedInstancedBaseVertexBaseInstance(
448                MTLPrimitiveTypeLineStrip, genIndicesCount, MTLIndexTypeUInt32, genIdxBuffer,
449                genIdxBufferOffset, instances, 0, baseInstance);
450        }
451    }
452
453    return angle::Result::Continue;
454}
455
456angle::Result ContextMtl::drawArraysImpl(const gl::Context *context,
457                                         gl::PrimitiveMode mode,
458                                         GLint first,
459                                         GLsizei count,
460                                         GLsizei instances,
461                                         GLuint baseInstance)
462{
463    // Real instances count. Zero means this is not instanced draw.
464    GLsizei instanceCount = instances ? instances : 1;
465
466    if (mCullAllPolygons && gl::IsPolygonMode(mode))
467    {
468        return angle::Result::Continue;
469    }
470    if (requiresIndexRewrite(context->getState(), mode))
471    {
472        return drawArraysProvokingVertexImpl(context, mode, first, count, instances, baseInstance);
473    }
474    if (mode == gl::PrimitiveMode::TriangleFan)
475    {
476        return drawTriFanArrays(context, first, count, instanceCount, baseInstance);
477    }
478    else if (mode == gl::PrimitiveMode::LineLoop)
479    {
480        return drawLineLoopArrays(context, first, count, instanceCount, baseInstance);
481    }
482
483    MTLPrimitiveType mtlType = mtl::GetPrimitiveType(mode);
484
485#define DRAW_GENERIC_ARRAY(xfbPass)                                                                \
486    {                                                                                              \
487        bool isNoOp = false;                                                                       \
488        ANGLE_TRY(setupDraw(context, mode, first, count, instances,                                \
489                            gl::DrawElementsType::InvalidEnum, nullptr, xfbPass, &isNoOp));        \
490        if (!isNoOp)                                                                               \
491        {                                                                                          \
492                                                                                                   \
493            if (instances == 0)                                                                    \
494            {                                                                                      \
495                /* This method is called from normal drawArrays() */                               \
496                mRenderEncoder.draw(mtlType, first, count);                                        \
497            }                                                                                      \
498            else                                                                                   \
499            {                                                                                      \
500                if (baseInstance == 0)                                                             \
501                {                                                                                  \
502                    mRenderEncoder.drawInstanced(mtlType, first, count, instanceCount);            \
503                }                                                                                  \
504                else                                                                               \
505                {                                                                                  \
506                    mRenderEncoder.drawInstancedBaseInstance(mtlType, first, count, instanceCount, \
507                                                             baseInstance);                        \
508                }                                                                                  \
509            }                                                                                      \
510        }                                                                                          \
511    }
512
513    ANGLE_MTL_XFB_DRAW(DRAW_GENERIC_ARRAY)
514
515    return angle::Result::Continue;
516}
517
518angle::Result ContextMtl::drawArrays(const gl::Context *context,
519                                     gl::PrimitiveMode mode,
520                                     GLint first,
521                                     GLsizei count)
522{
523    ANGLE_TRY(resyncDrawFramebufferIfNeeded(context));
524    return drawArraysImpl(context, mode, first, count, 0, 0);
525}
526
527angle::Result ContextMtl::drawArraysInstanced(const gl::Context *context,
528                                              gl::PrimitiveMode mode,
529                                              GLint first,
530                                              GLsizei count,
531                                              GLsizei instances)
532{
533    // Instanced draw calls with zero instances are skipped in the frontend.
534    // The drawArraysImpl function would treat them as non-instanced.
535    ASSERT(instances > 0);
536    ANGLE_TRY(resyncDrawFramebufferIfNeeded(context));
537    return drawArraysImpl(context, mode, first, count, instances, 0);
538}
539
540angle::Result ContextMtl::drawArraysInstancedBaseInstance(const gl::Context *context,
541                                                          gl::PrimitiveMode mode,
542                                                          GLint first,
543                                                          GLsizei count,
544                                                          GLsizei instanceCount,
545                                                          GLuint baseInstance)
546{
547    // Instanced draw calls with zero instances are skipped in the frontend.
548    // The drawArraysImpl function would treat them as non-instanced.
549    ASSERT(instanceCount > 0);
550    ANGLE_TRY(resyncDrawFramebufferIfNeeded(context));
551    return drawArraysImpl(context, mode, first, count, instanceCount, baseInstance);
552}
553
554angle::Result ContextMtl::drawTriFanElements(const gl::Context *context,
555                                             GLsizei count,
556                                             gl::DrawElementsType type,
557                                             const void *indices,
558                                             GLsizei instances,
559                                             GLint baseVertex,
560                                             GLuint baseInstance)
561{
562    if (count > 3)
563    {
564        mtl::BufferRef genIdxBuffer;
565        uint32_t genIdxBufferOffset;
566        uint32_t genIndicesCount;
567        bool primitiveRestart = getState().isPrimitiveRestartEnabled();
568        ANGLE_TRY(AllocateTriangleFanBufferFromPool(this, count, &mTriFanIndexBuffer, &genIdxBuffer,
569                                                    &genIdxBufferOffset, &genIndicesCount));
570
571        ANGLE_TRY(getDisplay()->getUtils().generateTriFanBufferFromElementsArray(
572            this, {type, count, indices, genIdxBuffer, genIdxBufferOffset, primitiveRestart},
573            &genIndicesCount));
574
575        ANGLE_TRY(mTriFanIndexBuffer.commit(this));
576        bool isNoOp = false;
577        ANGLE_TRY(setupDraw(context, gl::PrimitiveMode::TriangleFan, 0, count, instances, type,
578                            indices, false, &isNoOp));
579        if (!isNoOp && genIndicesCount > 0)
580        {
581            if (baseVertex == 0 && baseInstance == 0)
582            {
583                mRenderEncoder.drawIndexedInstanced(MTLPrimitiveTypeTriangle, genIndicesCount,
584                                                    MTLIndexTypeUInt32, genIdxBuffer,
585                                                    genIdxBufferOffset, instances);
586            }
587            else
588            {
589                mRenderEncoder.drawIndexedInstancedBaseVertexBaseInstance(
590                    MTLPrimitiveTypeTriangle, genIndicesCount, MTLIndexTypeUInt32, genIdxBuffer,
591                    genIdxBufferOffset, instances, baseVertex, baseInstance);
592            }
593        }
594
595        return angle::Result::Continue;
596    }  // if (count > 3)
597    return drawElementsImpl(context, gl::PrimitiveMode::Triangles, count, type, indices, instances,
598                            baseVertex, baseInstance);
599}
600
601angle::Result ContextMtl::drawLineLoopElementsNonInstancedNoPrimitiveRestart(
602    const gl::Context *context,
603    GLsizei count,
604    gl::DrawElementsType type,
605    const void *indices)
606{
607    // Generate line loop's last segment. It will be rendered when this function exits.
608    LineLoopLastSegmentHelper lineloopHelper;
609    // Line loop helper needs to generate index before rendering command encoder starts.
610    ANGLE_TRY(
611        lineloopHelper.begin(context, &mLineLoopLastSegmentIndexBuffer, 0, count, type, indices));
612
613    return drawElementsImpl(context, gl::PrimitiveMode::LineStrip, count, type, indices, 0, 0, 0);
614}
615
616angle::Result ContextMtl::drawLineLoopElements(const gl::Context *context,
617                                               GLsizei count,
618                                               gl::DrawElementsType type,
619                                               const void *indices,
620                                               GLsizei instances,
621                                               GLint baseVertex,
622                                               GLuint baseInstance)
623{
624    if (count >= 2)
625    {
626        bool primitiveRestart = getState().isPrimitiveRestartEnabled();
627        if (instances <= 1 && !primitiveRestart && baseVertex == 0 && baseInstance == 0)
628        {
629            // Non instanced draw and no primitive restart, just use faster version.
630            return drawLineLoopElementsNonInstancedNoPrimitiveRestart(context, count, type,
631                                                                      indices);
632        }
633
634        mtl::BufferRef genIdxBuffer;
635        uint32_t genIdxBufferOffset;
636        uint32_t reservedIndices = count * 2;
637        uint32_t genIndicesCount;
638        ANGLE_TRY(AllocateBufferFromPool(this, reservedIndices, &mLineLoopIndexBuffer,
639                                         &genIdxBuffer, &genIdxBufferOffset));
640
641        ANGLE_TRY(getDisplay()->getUtils().generateLineLoopBufferFromElementsArray(
642            this, {type, count, indices, genIdxBuffer, genIdxBufferOffset, primitiveRestart},
643            &genIndicesCount));
644
645        ANGLE_TRY(mLineLoopIndexBuffer.commit(this));
646        bool isNoOp = false;
647        ANGLE_TRY(setupDraw(context, gl::PrimitiveMode::LineLoop, 0, count, instances, type,
648                            indices, false, &isNoOp));
649        if (!isNoOp && genIndicesCount > 0)
650        {
651            if (baseVertex == 0 && baseInstance == 0)
652            {
653                mRenderEncoder.drawIndexedInstanced(MTLPrimitiveTypeLineStrip, genIndicesCount,
654                                                    MTLIndexTypeUInt32, genIdxBuffer,
655                                                    genIdxBufferOffset, instances);
656            }
657            else
658            {
659                mRenderEncoder.drawIndexedInstancedBaseVertexBaseInstance(
660                    MTLPrimitiveTypeLineStrip, genIndicesCount, MTLIndexTypeUInt32, genIdxBuffer,
661                    genIdxBufferOffset, instances, baseVertex, baseInstance);
662            }
663        }
664
665        return angle::Result::Continue;
666    }  // if (count >= 2)
667    return drawElementsImpl(context, gl::PrimitiveMode::Lines, count, type, indices, instances,
668                            baseVertex, baseInstance);
669}
670
671angle::Result ContextMtl::drawArraysProvokingVertexImpl(const gl::Context *context,
672                                                        gl::PrimitiveMode mode,
673                                                        GLsizei first,
674                                                        GLsizei count,
675                                                        GLsizei instances,
676                                                        GLuint baseInstance)
677{
678
679    size_t outIndexCount               = 0;
680    size_t outIndexOffset              = 0;
681    gl::DrawElementsType convertedType = gl::DrawElementsType::UnsignedInt;
682    gl::PrimitiveMode outIndexMode     = gl::PrimitiveMode::InvalidEnum;
683
684    mtl::BufferRef drawIdxBuffer;
685    ANGLE_TRY(mProvokingVertexHelper.generateIndexBuffer(
686        mtl::GetImpl(context), first, count, mode, convertedType, outIndexCount, outIndexOffset,
687        outIndexMode, drawIdxBuffer));
688    GLsizei outIndexCounti32 = static_cast<GLsizei>(outIndexCount);
689
690    // Note: we don't need to pass the generated index buffer to ContextMtl::setupDraw.
691    // Because setupDraw only needs to operate on the original vertex buffers & PrimitiveMode.
692    // setupDraw might convert vertex attributes if the offset & alignment are not natively
693    // supported by Metal. However, the converted attributes have the same order as the original
694    // vertices. Hence the conversion doesn't need to know about the newly generated index buffer.
695#define DRAW_PROVOKING_VERTEX_ARRAY(xfbPass)                                                       \
696    if (xfbPass)                                                                                   \
697    {                                                                                              \
698        bool isNoOp = false;                                                                       \
699        ANGLE_TRY(setupDraw(context, mode, first, count, instances,                                \
700                            gl::DrawElementsType::InvalidEnum, nullptr, xfbPass, &isNoOp));        \
701        if (!isNoOp)                                                                               \
702        {                                                                                          \
703            MTLPrimitiveType mtlType = mtl::GetPrimitiveType(mode);                                \
704            if (instances == 0)                                                                    \
705            {                                                                                      \
706                /* This method is called from normal drawArrays() */                               \
707                mRenderEncoder.draw(mtlType, first, count);                                        \
708            }                                                                                      \
709            else                                                                                   \
710            {                                                                                      \
711                if (baseInstance == 0)                                                             \
712                {                                                                                  \
713                    mRenderEncoder.drawInstanced(mtlType, first, count, instances);                \
714                }                                                                                  \
715                else                                                                               \
716                {                                                                                  \
717                    mRenderEncoder.drawInstancedBaseInstance(mtlType, first, count, instances,     \
718                                                             baseInstance);                        \
719                }                                                                                  \
720            }                                                                                      \
721        }                                                                                          \
722    }                                                                                              \
723    else                                                                                           \
724    {                                                                                              \
725        bool isNoOp = false;                                                                       \
726        ANGLE_TRY(setupDraw(context, mode, first, count, instances,                                \
727                            gl::DrawElementsType::InvalidEnum, nullptr, xfbPass, &isNoOp));        \
728                                                                                                   \
729        if (!isNoOp)                                                                               \
730        {                                                                                          \
731            MTLPrimitiveType mtlType = mtl::GetPrimitiveType(outIndexMode);                        \
732            MTLIndexType mtlIdxType  = mtl::GetIndexType(convertedType);                           \
733            if (instances == 0)                                                                    \
734            {                                                                                      \
735                mRenderEncoder.drawIndexed(mtlType, outIndexCounti32, mtlIdxType, drawIdxBuffer,   \
736                                           outIndexOffset);                                        \
737            }                                                                                      \
738            else                                                                                   \
739            {                                                                                      \
740                if (baseInstance == 0)                                                             \
741                {                                                                                  \
742                    mRenderEncoder.drawIndexedInstanced(mtlType, outIndexCounti32, mtlIdxType,     \
743                                                        drawIdxBuffer, outIndexOffset, instances); \
744                }                                                                                  \
745                else                                                                               \
746                {                                                                                  \
747                    mRenderEncoder.drawIndexedInstancedBaseVertexBaseInstance(                     \
748                        mtlType, outIndexCounti32, mtlIdxType, drawIdxBuffer, outIndexOffset,      \
749                        instances, 0, baseInstance);                                               \
750                }                                                                                  \
751            }                                                                                      \
752        }                                                                                          \
753    }
754
755    ANGLE_MTL_XFB_DRAW(DRAW_PROVOKING_VERTEX_ARRAY)
756    return angle::Result::Continue;
757}
758
759angle::Result ContextMtl::drawElementsImpl(const gl::Context *context,
760                                           gl::PrimitiveMode mode,
761                                           GLsizei count,
762                                           gl::DrawElementsType type,
763                                           const void *indices,
764                                           GLsizei instances,
765                                           GLint baseVertex,
766                                           GLuint baseInstance)
767{
768    // Real instances count. Zero means this is not instanced draw.
769    GLsizei instanceCount = instances ? instances : 1;
770
771    if (mCullAllPolygons && gl::IsPolygonMode(mode))
772    {
773        return angle::Result::Continue;
774    }
775
776    if (mode == gl::PrimitiveMode::TriangleFan)
777    {
778        return drawTriFanElements(context, count, type, indices, instanceCount, baseVertex,
779                                  baseInstance);
780    }
781    else if (mode == gl::PrimitiveMode::LineLoop)
782    {
783        return drawLineLoopElements(context, count, type, indices, instanceCount, baseVertex,
784                                    baseInstance);
785    }
786
787    mtl::BufferRef idxBuffer;
788    mtl::BufferRef drawIdxBuffer;
789    size_t convertedOffset             = 0;
790    gl::DrawElementsType convertedType = type;
791
792    ANGLE_TRY(mVertexArray->getIndexBuffer(context, type, count, indices, &idxBuffer,
793                                           &convertedOffset, &convertedType));
794
795    ASSERT(idxBuffer);
796    ASSERT((convertedType == gl::DrawElementsType::UnsignedShort && (convertedOffset % 2) == 0) ||
797           (convertedType == gl::DrawElementsType::UnsignedInt && (convertedOffset % 4) == 0));
798
799    uint32_t convertedCounti32 = (uint32_t)count;
800
801    size_t provokingVertexAdditionalOffset = 0;
802
803    if (requiresIndexRewrite(context->getState(), mode))
804    {
805        size_t outIndexCount      = 0;
806        gl::PrimitiveMode newMode = gl::PrimitiveMode::InvalidEnum;
807        ANGLE_TRY(mProvokingVertexHelper.preconditionIndexBuffer(
808            mtl::GetImpl(context), idxBuffer, count, convertedOffset,
809            mState.isPrimitiveRestartEnabled(), mode, convertedType, outIndexCount,
810            provokingVertexAdditionalOffset, newMode, drawIdxBuffer));
811        // Line strips and triangle strips are rewritten to flat line arrays and tri arrays.
812        convertedCounti32 = (uint32_t)outIndexCount;
813        mode              = newMode;
814    }
815    else
816    {
817        drawIdxBuffer = idxBuffer;
818    }
819    // Draw commands will only be broken up if transform feedback is enabled,
820    // if the mode is a simple type, and if the buffer contained any restart
821    // indices.
822    // It's safe to use idxBuffer in this case, as it will contain the same count and restart ranges
823    // as drawIdxBuffer.
824    const std::vector<DrawCommandRange> drawCommands = mVertexArray->getDrawIndices(
825        context, type, convertedType, mode, idxBuffer, convertedCounti32, convertedOffset);
826    bool isNoOp = false;
827    ANGLE_TRY(setupDraw(context, mode, 0, count, instances, type, indices, false, &isNoOp));
828    if (!isNoOp)
829    {
830        MTLPrimitiveType mtlType = mtl::GetPrimitiveType(mode);
831
832        MTLIndexType mtlIdxType = mtl::GetIndexType(convertedType);
833
834        if (instances == 0 && baseVertex == 0 && baseInstance == 0)
835        {
836            // Normal draw
837            for (auto &command : drawCommands)
838            {
839                mRenderEncoder.drawIndexed(mtlType, command.count, mtlIdxType, drawIdxBuffer,
840                                           command.offset + provokingVertexAdditionalOffset);
841            }
842        }
843        else
844        {
845            // Instanced draw
846            if (baseVertex == 0 && baseInstance == 0)
847            {
848                for (auto &command : drawCommands)
849                {
850                    mRenderEncoder.drawIndexedInstanced(
851                        mtlType, command.count, mtlIdxType, drawIdxBuffer,
852                        command.offset + provokingVertexAdditionalOffset, instanceCount);
853                }
854            }
855            else
856            {
857                for (auto &command : drawCommands)
858                {
859                    mRenderEncoder.drawIndexedInstancedBaseVertexBaseInstance(
860                        mtlType, command.count, mtlIdxType, drawIdxBuffer,
861                        command.offset + provokingVertexAdditionalOffset, instanceCount, baseVertex,
862                        baseInstance);
863                }
864            }
865        }
866    }
867    return angle::Result::Continue;
868}
869
870angle::Result ContextMtl::drawElements(const gl::Context *context,
871                                       gl::PrimitiveMode mode,
872                                       GLsizei count,
873                                       gl::DrawElementsType type,
874                                       const void *indices)
875{
876    ANGLE_TRY(resyncDrawFramebufferIfNeeded(context));
877    return drawElementsImpl(context, mode, count, type, indices, 0, 0, 0);
878}
879
880angle::Result ContextMtl::drawElementsBaseVertex(const gl::Context *context,
881                                                 gl::PrimitiveMode mode,
882                                                 GLsizei count,
883                                                 gl::DrawElementsType type,
884                                                 const void *indices,
885                                                 GLint baseVertex)
886{
887    UNIMPLEMENTED();
888    return angle::Result::Stop;
889}
890
891angle::Result ContextMtl::drawElementsInstanced(const gl::Context *context,
892                                                gl::PrimitiveMode mode,
893                                                GLsizei count,
894                                                gl::DrawElementsType type,
895                                                const void *indices,
896                                                GLsizei instanceCount)
897{
898    ANGLE_TRY(resyncDrawFramebufferIfNeeded(context));
899    // Instanced draw calls with zero instances are skipped in the frontend.
900    // The drawElementsImpl function would treat them as non-instanced.
901    ASSERT(instanceCount > 0);
902    return drawElementsImpl(context, mode, count, type, indices, instanceCount, 0, 0);
903}
904
905angle::Result ContextMtl::drawElementsInstancedBaseVertex(const gl::Context *context,
906                                                          gl::PrimitiveMode mode,
907                                                          GLsizei count,
908                                                          gl::DrawElementsType type,
909                                                          const void *indices,
910                                                          GLsizei instanceCount,
911                                                          GLint baseVertex)
912{
913    ANGLE_TRY(resyncDrawFramebufferIfNeeded(context));
914    // Instanced draw calls with zero instances are skipped in the frontend.
915    // The drawElementsImpl function would treat them as non-instanced.
916    ASSERT(instanceCount > 0);
917    return drawElementsImpl(context, mode, count, type, indices, instanceCount, baseVertex, 0);
918}
919
920angle::Result ContextMtl::drawElementsInstancedBaseVertexBaseInstance(const gl::Context *context,
921                                                                      gl::PrimitiveMode mode,
922                                                                      GLsizei count,
923                                                                      gl::DrawElementsType type,
924                                                                      const void *indices,
925                                                                      GLsizei instances,
926                                                                      GLint baseVertex,
927                                                                      GLuint baseInstance)
928{
929    ANGLE_TRY(resyncDrawFramebufferIfNeeded(context));
930    // Instanced draw calls with zero instances are skipped in the frontend.
931    // The drawElementsImpl function would treat them as non-instanced.
932    ASSERT(instances > 0);
933    return drawElementsImpl(context, mode, count, type, indices, instances, baseVertex,
934                            baseInstance);
935}
936
937angle::Result ContextMtl::drawRangeElements(const gl::Context *context,
938                                            gl::PrimitiveMode mode,
939                                            GLuint start,
940                                            GLuint end,
941                                            GLsizei count,
942                                            gl::DrawElementsType type,
943                                            const void *indices)
944{
945    ANGLE_TRY(resyncDrawFramebufferIfNeeded(context));
946    return drawElementsImpl(context, mode, count, type, indices, 0, 0, 0);
947}
948
949angle::Result ContextMtl::drawRangeElementsBaseVertex(const gl::Context *context,
950                                                      gl::PrimitiveMode mode,
951                                                      GLuint start,
952                                                      GLuint end,
953                                                      GLsizei count,
954                                                      gl::DrawElementsType type,
955                                                      const void *indices,
956                                                      GLint baseVertex)
957{
958    // NOTE(hqle): ES 3.2
959    UNIMPLEMENTED();
960    return angle::Result::Stop;
961}
962
963angle::Result ContextMtl::drawArraysIndirect(const gl::Context *context,
964                                             gl::PrimitiveMode mode,
965                                             const void *indirect)
966{
967    // NOTE(hqle): ES 3.0
968    UNIMPLEMENTED();
969    return angle::Result::Stop;
970}
971angle::Result ContextMtl::drawElementsIndirect(const gl::Context *context,
972                                               gl::PrimitiveMode mode,
973                                               gl::DrawElementsType type,
974                                               const void *indirect)
975{
976    // NOTE(hqle): ES 3.0
977    UNIMPLEMENTED();
978    return angle::Result::Stop;
979}
980
981angle::Result ContextMtl::multiDrawArrays(const gl::Context *context,
982                                          gl::PrimitiveMode mode,
983                                          const GLint *firsts,
984                                          const GLsizei *counts,
985                                          GLsizei drawcount)
986{
987    return rx::MultiDrawArraysGeneral(this, context, mode, firsts, counts, drawcount);
988}
989
990angle::Result ContextMtl::multiDrawArraysInstanced(const gl::Context *context,
991                                                   gl::PrimitiveMode mode,
992                                                   const GLint *firsts,
993                                                   const GLsizei *counts,
994                                                   const GLsizei *instanceCounts,
995                                                   GLsizei drawcount)
996{
997    return rx::MultiDrawArraysInstancedGeneral(this, context, mode, firsts, counts, instanceCounts,
998                                               drawcount);
999}
1000
1001angle::Result ContextMtl::multiDrawArraysIndirect(const gl::Context *context,
1002                                                  gl::PrimitiveMode mode,
1003                                                  const void *indirect,
1004                                                  GLsizei drawcount,
1005                                                  GLsizei stride)
1006{
1007    return rx::MultiDrawArraysIndirectGeneral(this, context, mode, indirect, drawcount, stride);
1008}
1009
1010angle::Result ContextMtl::multiDrawElements(const gl::Context *context,
1011                                            gl::PrimitiveMode mode,
1012                                            const GLsizei *counts,
1013                                            gl::DrawElementsType type,
1014                                            const GLvoid *const *indices,
1015                                            GLsizei drawcount)
1016{
1017    return rx::MultiDrawElementsGeneral(this, context, mode, counts, type, indices, drawcount);
1018}
1019
1020angle::Result ContextMtl::multiDrawElementsInstanced(const gl::Context *context,
1021                                                     gl::PrimitiveMode mode,
1022                                                     const GLsizei *counts,
1023                                                     gl::DrawElementsType type,
1024                                                     const GLvoid *const *indices,
1025                                                     const GLsizei *instanceCounts,
1026                                                     GLsizei drawcount)
1027{
1028    return rx::MultiDrawElementsInstancedGeneral(this, context, mode, counts, type, indices,
1029                                                 instanceCounts, drawcount);
1030}
1031
1032angle::Result ContextMtl::multiDrawElementsIndirect(const gl::Context *context,
1033                                                    gl::PrimitiveMode mode,
1034                                                    gl::DrawElementsType type,
1035                                                    const void *indirect,
1036                                                    GLsizei drawcount,
1037                                                    GLsizei stride)
1038{
1039    return rx::MultiDrawElementsIndirectGeneral(this, context, mode, type, indirect, drawcount,
1040                                                stride);
1041}
1042
1043angle::Result ContextMtl::multiDrawArraysInstancedBaseInstance(const gl::Context *context,
1044                                                               gl::PrimitiveMode mode,
1045                                                               const GLint *firsts,
1046                                                               const GLsizei *counts,
1047                                                               const GLsizei *instanceCounts,
1048                                                               const GLuint *baseInstances,
1049                                                               GLsizei drawcount)
1050{
1051    return rx::MultiDrawArraysInstancedBaseInstanceGeneral(
1052        this, context, mode, firsts, counts, instanceCounts, baseInstances, drawcount);
1053}
1054
1055angle::Result ContextMtl::multiDrawElementsInstancedBaseVertexBaseInstance(
1056    const gl::Context *context,
1057    gl::PrimitiveMode mode,
1058    const GLsizei *counts,
1059    gl::DrawElementsType type,
1060    const GLvoid *const *indices,
1061    const GLsizei *instanceCounts,
1062    const GLint *baseVertices,
1063    const GLuint *baseInstances,
1064    GLsizei drawcount)
1065{
1066    return rx::MultiDrawElementsInstancedBaseVertexBaseInstanceGeneral(
1067        this, context, mode, counts, type, indices, instanceCounts, baseVertices, baseInstances,
1068        drawcount);
1069}
1070
1071// Device loss
1072gl::GraphicsResetStatus ContextMtl::getResetStatus()
1073{
1074    return gl::GraphicsResetStatus::NoError;
1075}
1076
1077// EXT_debug_marker
1078angle::Result ContextMtl::insertEventMarker(GLsizei length, const char *marker)
1079{
1080    return angle::Result::Continue;
1081}
1082
1083angle::Result ContextMtl::pushGroupMarker(GLsizei length, const char *marker)
1084{
1085    mCmdBuffer.pushDebugGroup(ConvertMarkerToString(length, marker));
1086    return angle::Result::Continue;
1087}
1088
1089angle::Result ContextMtl::popGroupMarker()
1090{
1091    mCmdBuffer.popDebugGroup();
1092    return angle::Result::Continue;
1093}
1094
1095// KHR_debug
1096angle::Result ContextMtl::pushDebugGroup(const gl::Context *context,
1097                                         GLenum source,
1098                                         GLuint id,
1099                                         const std::string &message)
1100{
1101    return angle::Result::Continue;
1102}
1103
1104angle::Result ContextMtl::popDebugGroup(const gl::Context *context)
1105{
1106    return angle::Result::Continue;
1107}
1108
1109void ContextMtl::updateIncompatibleAttachments(const gl::State &glState)
1110{
1111    const gl::ProgramExecutable *programExecutable = glState.getProgramExecutable();
1112    gl::Framebuffer *drawFramebuffer               = glState.getDrawFramebuffer();
1113    if (programExecutable == nullptr || drawFramebuffer == nullptr)
1114    {
1115        mIncompatibleAttachments.reset();
1116        return;
1117    }
1118
1119    // Cache a mask of incompatible attachments ignoring unused outputs and disabled draw buffers.
1120    mIncompatibleAttachments =
1121        gl::GetComponentTypeMaskDiff(drawFramebuffer->getDrawBufferTypeMask(),
1122                                     programExecutable->getFragmentOutputsTypeMask()) &
1123        drawFramebuffer->getDrawBufferMask() & programExecutable->getActiveOutputVariablesMask();
1124}
1125
1126// State sync with dirty bits.
1127angle::Result ContextMtl::syncState(const gl::Context *context,
1128                                    const gl::state::DirtyBits dirtyBits,
1129                                    const gl::state::DirtyBits bitMask,
1130                                    const gl::state::ExtendedDirtyBits extendedDirtyBits,
1131                                    const gl::state::ExtendedDirtyBits extendedBitMask,
1132                                    gl::Command command)
1133{
1134    const gl::State &glState = context->getState();
1135
1136    // Metal's blend state is set at once, while ANGLE tracks separate dirty
1137    // bits: ENABLED, FUNCS, and EQUATIONS. Merge all three of them to the first one.
1138    // PS: these can not be statically initialized on some architectures as there is
1139    // no constuctor for DirtyBits that takes an int (which becomes BitSetArray<64>).
1140    gl::state::DirtyBits checkBlendBitsMask;
1141    checkBlendBitsMask.set(gl::state::DIRTY_BIT_BLEND_ENABLED);
1142    checkBlendBitsMask.set(gl::state::DIRTY_BIT_BLEND_FUNCS);
1143    checkBlendBitsMask.set(gl::state::DIRTY_BIT_BLEND_EQUATIONS);
1144    gl::state::DirtyBits resetBlendBitsMask;
1145    resetBlendBitsMask.set(gl::state::DIRTY_BIT_BLEND_FUNCS);
1146    resetBlendBitsMask.set(gl::state::DIRTY_BIT_BLEND_EQUATIONS);
1147
1148    gl::state::DirtyBits mergedDirtyBits = gl::state::DirtyBits(dirtyBits) & ~resetBlendBitsMask;
1149    mergedDirtyBits.set(gl::state::DIRTY_BIT_BLEND_ENABLED, (dirtyBits & checkBlendBitsMask).any());
1150
1151    for (auto iter = mergedDirtyBits.begin(), endIter = mergedDirtyBits.end(); iter != endIter;
1152         ++iter)
1153    {
1154        size_t dirtyBit = *iter;
1155        switch (dirtyBit)
1156        {
1157            case gl::state::DIRTY_BIT_SCISSOR_TEST_ENABLED:
1158            case gl::state::DIRTY_BIT_SCISSOR:
1159                updateScissor(glState);
1160                break;
1161            case gl::state::DIRTY_BIT_VIEWPORT:
1162            {
1163                FramebufferMtl *framebufferMtl = mtl::GetImpl(glState.getDrawFramebuffer());
1164                updateViewport(framebufferMtl, glState.getViewport(), glState.getNearPlane(),
1165                               glState.getFarPlane());
1166                // Update the scissor, which will be constrained to the viewport
1167                updateScissor(glState);
1168                break;
1169            }
1170            case gl::state::DIRTY_BIT_DEPTH_RANGE:
1171                updateDepthRange(glState.getNearPlane(), glState.getFarPlane());
1172                break;
1173            case gl::state::DIRTY_BIT_BLEND_COLOR:
1174                mDirtyBits.set(DIRTY_BIT_BLEND_COLOR);
1175                break;
1176            case gl::state::DIRTY_BIT_BLEND_ENABLED:
1177                updateBlendDescArray(glState.getBlendStateExt());
1178                break;
1179            case gl::state::DIRTY_BIT_COLOR_MASK:
1180            {
1181                const gl::BlendStateExt &blendStateExt = glState.getBlendStateExt();
1182                size_t i                               = 0;
1183                for (; i < blendStateExt.getDrawBufferCount(); i++)
1184                {
1185                    mBlendDescArray[i].updateWriteMask(blendStateExt.getColorMaskIndexed(i));
1186                    mWriteMaskArray[i] = mBlendDescArray[i].writeMask;
1187                }
1188                for (; i < mBlendDescArray.size(); i++)
1189                {
1190                    mBlendDescArray[i].updateWriteMask(0);
1191                    mWriteMaskArray[i] = mBlendDescArray[i].writeMask;
1192                }
1193                invalidateRenderPipeline();
1194                break;
1195            }
1196            case gl::state::DIRTY_BIT_SAMPLE_ALPHA_TO_COVERAGE_ENABLED:
1197                if (getDisplay()->getFeatures().emulateAlphaToCoverage.enabled)
1198                {
1199                    invalidateDriverUniforms();
1200                }
1201                else
1202                {
1203                    invalidateRenderPipeline();
1204                }
1205                break;
1206            case gl::state::DIRTY_BIT_SAMPLE_COVERAGE_ENABLED:
1207            case gl::state::DIRTY_BIT_SAMPLE_COVERAGE:
1208            case gl::state::DIRTY_BIT_SAMPLE_MASK_ENABLED:
1209            case gl::state::DIRTY_BIT_SAMPLE_MASK:
1210                invalidateDriverUniforms();
1211                break;
1212            case gl::state::DIRTY_BIT_DEPTH_TEST_ENABLED:
1213                mDepthStencilDesc.updateDepthTestEnabled(glState.getDepthStencilState());
1214                mDirtyBits.set(DIRTY_BIT_DEPTH_STENCIL_DESC);
1215                break;
1216            case gl::state::DIRTY_BIT_DEPTH_FUNC:
1217                mDepthStencilDesc.updateDepthCompareFunc(glState.getDepthStencilState());
1218                mDirtyBits.set(DIRTY_BIT_DEPTH_STENCIL_DESC);
1219                break;
1220            case gl::state::DIRTY_BIT_DEPTH_MASK:
1221                mDepthStencilDesc.updateDepthWriteEnabled(glState.getDepthStencilState());
1222                mDirtyBits.set(DIRTY_BIT_DEPTH_STENCIL_DESC);
1223                break;
1224            case gl::state::DIRTY_BIT_STENCIL_TEST_ENABLED:
1225                mDepthStencilDesc.updateStencilTestEnabled(glState.getDepthStencilState());
1226                mDirtyBits.set(DIRTY_BIT_DEPTH_STENCIL_DESC);
1227                break;
1228            case gl::state::DIRTY_BIT_STENCIL_FUNCS_FRONT:
1229                mDepthStencilDesc.updateStencilFrontFuncs(glState.getDepthStencilState());
1230                mStencilRefFront = glState.getStencilRef();  // clamped on the frontend
1231                mDirtyBits.set(DIRTY_BIT_DEPTH_STENCIL_DESC);
1232                mDirtyBits.set(DIRTY_BIT_STENCIL_REF);
1233                break;
1234            case gl::state::DIRTY_BIT_STENCIL_FUNCS_BACK:
1235                mDepthStencilDesc.updateStencilBackFuncs(glState.getDepthStencilState());
1236                mStencilRefBack = glState.getStencilBackRef();  // clamped on the frontend
1237                mDirtyBits.set(DIRTY_BIT_DEPTH_STENCIL_DESC);
1238                mDirtyBits.set(DIRTY_BIT_STENCIL_REF);
1239                break;
1240            case gl::state::DIRTY_BIT_STENCIL_OPS_FRONT:
1241                mDepthStencilDesc.updateStencilFrontOps(glState.getDepthStencilState());
1242                mDirtyBits.set(DIRTY_BIT_DEPTH_STENCIL_DESC);
1243                break;
1244            case gl::state::DIRTY_BIT_STENCIL_OPS_BACK:
1245                mDepthStencilDesc.updateStencilBackOps(glState.getDepthStencilState());
1246                mDirtyBits.set(DIRTY_BIT_DEPTH_STENCIL_DESC);
1247                break;
1248            case gl::state::DIRTY_BIT_STENCIL_WRITEMASK_FRONT:
1249                mDepthStencilDesc.updateStencilFrontWriteMask(glState.getDepthStencilState());
1250                mDirtyBits.set(DIRTY_BIT_DEPTH_STENCIL_DESC);
1251                break;
1252            case gl::state::DIRTY_BIT_STENCIL_WRITEMASK_BACK:
1253                mDepthStencilDesc.updateStencilBackWriteMask(glState.getDepthStencilState());
1254                mDirtyBits.set(DIRTY_BIT_DEPTH_STENCIL_DESC);
1255                break;
1256            case gl::state::DIRTY_BIT_CULL_FACE_ENABLED:
1257            case gl::state::DIRTY_BIT_CULL_FACE:
1258                updateCullMode(glState);
1259                break;
1260            case gl::state::DIRTY_BIT_FRONT_FACE:
1261                updateFrontFace(glState);
1262                break;
1263            case gl::state::DIRTY_BIT_POLYGON_OFFSET_FILL_ENABLED:
1264            case gl::state::DIRTY_BIT_POLYGON_OFFSET:
1265                mDirtyBits.set(DIRTY_BIT_DEPTH_BIAS);
1266                break;
1267            case gl::state::DIRTY_BIT_RASTERIZER_DISCARD_ENABLED:
1268                mDirtyBits.set(DIRTY_BIT_RASTERIZER_DISCARD);
1269                break;
1270            case gl::state::DIRTY_BIT_LINE_WIDTH:
1271                // Do nothing
1272                break;
1273            case gl::state::DIRTY_BIT_PRIMITIVE_RESTART_ENABLED:
1274                // NOTE(hqle): ES 3.0 feature.
1275                break;
1276            case gl::state::DIRTY_BIT_CLEAR_COLOR:
1277                mClearColor = mtl::ClearColorValue(
1278                    glState.getColorClearValue().red, glState.getColorClearValue().green,
1279                    glState.getColorClearValue().blue, glState.getColorClearValue().alpha);
1280                break;
1281            case gl::state::DIRTY_BIT_CLEAR_DEPTH:
1282                break;
1283            case gl::state::DIRTY_BIT_CLEAR_STENCIL:
1284                mClearStencil = glState.getStencilClearValue() & mtl::kStencilMaskAll;
1285                break;
1286            case gl::state::DIRTY_BIT_UNPACK_STATE:
1287                // This is a no-op, its only important to use the right unpack state when we do
1288                // setImage or setSubImage in TextureMtl, which is plumbed through the frontend call
1289                break;
1290            case gl::state::DIRTY_BIT_UNPACK_BUFFER_BINDING:
1291                break;
1292            case gl::state::DIRTY_BIT_PACK_STATE:
1293                // This is a no-op, its only important to use the right pack state when we do
1294                // call readPixels later on.
1295                break;
1296            case gl::state::DIRTY_BIT_PACK_BUFFER_BINDING:
1297                break;
1298            case gl::state::DIRTY_BIT_DITHER_ENABLED:
1299                break;
1300            case gl::state::DIRTY_BIT_READ_FRAMEBUFFER_BINDING:
1301                break;
1302            case gl::state::DIRTY_BIT_DRAW_FRAMEBUFFER_BINDING:
1303                updateIncompatibleAttachments(glState);
1304                updateDrawFrameBufferBinding(context);
1305                break;
1306            case gl::state::DIRTY_BIT_RENDERBUFFER_BINDING:
1307                break;
1308            case gl::state::DIRTY_BIT_VERTEX_ARRAY_BINDING:
1309                updateVertexArray(context);
1310                break;
1311            case gl::state::DIRTY_BIT_DRAW_INDIRECT_BUFFER_BINDING:
1312                break;
1313            case gl::state::DIRTY_BIT_DISPATCH_INDIRECT_BUFFER_BINDING:
1314                break;
1315            case gl::state::DIRTY_BIT_PROGRAM_BINDING:
1316                static_assert(
1317                    gl::state::DIRTY_BIT_PROGRAM_EXECUTABLE > gl::state::DIRTY_BIT_PROGRAM_BINDING,
1318                    "Dirty bit order");
1319                iter.setLaterBit(gl::state::DIRTY_BIT_PROGRAM_EXECUTABLE);
1320                break;
1321            case gl::state::DIRTY_BIT_PROGRAM_EXECUTABLE:
1322            {
1323                updateIncompatibleAttachments(glState);
1324                const gl::ProgramExecutable *executable = mState.getProgramExecutable();
1325                ASSERT(executable);
1326                mExecutable = mtl::GetImpl(executable);
1327                updateProgramExecutable(context);
1328                break;
1329            }
1330            case gl::state::DIRTY_BIT_TEXTURE_BINDINGS:
1331                invalidateCurrentTextures();
1332                break;
1333            case gl::state::DIRTY_BIT_SAMPLER_BINDINGS:
1334                invalidateCurrentTextures();
1335                break;
1336            case gl::state::DIRTY_BIT_TRANSFORM_FEEDBACK_BINDING:
1337                // Nothing to do.
1338                break;
1339            case gl::state::DIRTY_BIT_SHADER_STORAGE_BUFFER_BINDING:
1340                // NOTE(hqle): ES 3.0 feature.
1341                break;
1342            case gl::state::DIRTY_BIT_UNIFORM_BUFFER_BINDINGS:
1343                mDirtyBits.set(DIRTY_BIT_UNIFORM_BUFFERS_BINDING);
1344                break;
1345            case gl::state::DIRTY_BIT_ATOMIC_COUNTER_BUFFER_BINDING:
1346                break;
1347            case gl::state::DIRTY_BIT_IMAGE_BINDINGS:
1348                // NOTE(hqle): properly handle GLSL images.
1349                invalidateCurrentTextures();
1350                break;
1351            case gl::state::DIRTY_BIT_MULTISAMPLING:
1352                // NOTE(hqle): MSAA on/off.
1353                break;
1354            case gl::state::DIRTY_BIT_SAMPLE_ALPHA_TO_ONE:
1355                // NOTE(hqle): this is part of EXT_multisample_compatibility.
1356                // NOTE(hqle): MSAA feature.
1357                break;
1358            case gl::state::DIRTY_BIT_COVERAGE_MODULATION:
1359                break;
1360            case gl::state::DIRTY_BIT_FRAMEBUFFER_SRGB_WRITE_CONTROL_MODE:
1361                break;
1362            case gl::state::DIRTY_BIT_CURRENT_VALUES:
1363            {
1364                invalidateDefaultAttributes(glState.getAndResetDirtyCurrentValues());
1365                break;
1366            }
1367            case gl::state::DIRTY_BIT_PROVOKING_VERTEX:
1368                break;
1369            case gl::state::DIRTY_BIT_EXTENDED:
1370                updateExtendedState(glState, extendedDirtyBits);
1371                break;
1372            case gl::state::DIRTY_BIT_SAMPLE_SHADING:
1373                // Nothing to do until OES_sample_shading is implemented.
1374                break;
1375            case gl::state::DIRTY_BIT_PATCH_VERTICES:
1376                // Nothing to do until EXT_tessellation_shader is implemented.
1377                break;
1378            default:
1379                UNREACHABLE();
1380                break;
1381        }
1382    }
1383
1384    return angle::Result::Continue;
1385}
1386
1387void ContextMtl::updateExtendedState(const gl::State &glState,
1388                                     const gl::state::ExtendedDirtyBits extendedDirtyBits)
1389{
1390    for (size_t extendedDirtyBit : extendedDirtyBits)
1391    {
1392        switch (extendedDirtyBit)
1393        {
1394            case gl::state::EXTENDED_DIRTY_BIT_CLIP_CONTROL:
1395                updateFrontFace(glState);
1396                invalidateDriverUniforms();
1397                break;
1398            case gl::state::EXTENDED_DIRTY_BIT_CLIP_DISTANCES:
1399                invalidateDriverUniforms();
1400                break;
1401            case gl::state::EXTENDED_DIRTY_BIT_DEPTH_CLAMP_ENABLED:
1402                mDirtyBits.set(DIRTY_BIT_DEPTH_CLIP_MODE);
1403                break;
1404            case gl::state::EXTENDED_DIRTY_BIT_POLYGON_MODE:
1405                mDirtyBits.set(DIRTY_BIT_FILL_MODE);
1406                mDirtyBits.set(DIRTY_BIT_DEPTH_BIAS);
1407                break;
1408            case gl::state::EXTENDED_DIRTY_BIT_POLYGON_OFFSET_LINE_ENABLED:
1409                mDirtyBits.set(DIRTY_BIT_DEPTH_BIAS);
1410                break;
1411            default:
1412                break;
1413        }
1414    }
1415}
1416
1417// Disjoint timer queries
1418GLint ContextMtl::getGPUDisjoint()
1419{
1420    // Implementation currently is not affected by this.
1421    return 0;
1422}
1423
1424GLint64 ContextMtl::getTimestamp()
1425{
1426    // Timestamps are currently unsupported. An implementation
1427    // strategy is written up in anglebug.com/42266300 if they're needed
1428    // in the future.
1429    return 0;
1430}
1431
1432// Context switching
1433angle::Result ContextMtl::onMakeCurrent(const gl::Context *context)
1434{
1435    invalidateState(context);
1436    gl::Query *query = mState.getActiveQuery(gl::QueryType::TimeElapsed);
1437    if (query)
1438    {
1439        GetImplAs<QueryMtl>(query)->onContextMakeCurrent(context);
1440    }
1441    mBufferManager.incrementNumContextSwitches();
1442    return angle::Result::Continue;
1443}
1444angle::Result ContextMtl::onUnMakeCurrent(const gl::Context *context)
1445{
1446    flushCommandBuffer(mtl::WaitUntilScheduled);
1447    // Note: this 2nd flush is needed because if there is a query in progress
1448    // then during flush, new command buffers are allocated that also need
1449    // to be flushed. This is a temporary fix and we should probably refactor
1450    // this later. See TODO(anglebug.com/42265611)
1451    flushCommandBuffer(mtl::WaitUntilScheduled);
1452    gl::Query *query = mState.getActiveQuery(gl::QueryType::TimeElapsed);
1453    if (query)
1454    {
1455        GetImplAs<QueryMtl>(query)->onContextUnMakeCurrent(context);
1456    }
1457    return angle::Result::Continue;
1458}
1459
1460// Native capabilities, unmodified by gl::Context.
1461gl::Caps ContextMtl::getNativeCaps() const
1462{
1463    return getDisplay()->getNativeCaps();
1464}
1465const gl::TextureCapsMap &ContextMtl::getNativeTextureCaps() const
1466{
1467    return getDisplay()->getNativeTextureCaps();
1468}
1469const gl::Extensions &ContextMtl::getNativeExtensions() const
1470{
1471    return getDisplay()->getNativeExtensions();
1472}
1473const gl::Limitations &ContextMtl::getNativeLimitations() const
1474{
1475    return getDisplay()->getNativeLimitations();
1476}
1477const ShPixelLocalStorageOptions &ContextMtl::getNativePixelLocalStorageOptions() const
1478{
1479    return getDisplay()->getNativePixelLocalStorageOptions();
1480}
1481
1482// Shader creation
1483CompilerImpl *ContextMtl::createCompiler()
1484{
1485    return new CompilerMtl();
1486}
1487ShaderImpl *ContextMtl::createShader(const gl::ShaderState &state)
1488{
1489    return new ShaderMtl(state);
1490}
1491ProgramImpl *ContextMtl::createProgram(const gl::ProgramState &state)
1492{
1493    return new ProgramMtl(state);
1494}
1495
1496ProgramExecutableImpl *ContextMtl::createProgramExecutable(const gl::ProgramExecutable *executable)
1497{
1498    return new ProgramExecutableMtl(executable);
1499}
1500
1501// Framebuffer creation
1502FramebufferImpl *ContextMtl::createFramebuffer(const gl::FramebufferState &state)
1503{
1504    return new FramebufferMtl(state, this, /* flipY */ false);
1505}
1506
1507// Texture creation
1508TextureImpl *ContextMtl::createTexture(const gl::TextureState &state)
1509{
1510    return new TextureMtl(state);
1511}
1512
1513// Renderbuffer creation
1514RenderbufferImpl *ContextMtl::createRenderbuffer(const gl::RenderbufferState &state)
1515{
1516    return new RenderbufferMtl(state);
1517}
1518
1519// Buffer creation
1520BufferImpl *ContextMtl::createBuffer(const gl::BufferState &state)
1521{
1522    return new BufferMtl(state);
1523}
1524
1525// Vertex Array creation
1526VertexArrayImpl *ContextMtl::createVertexArray(const gl::VertexArrayState &state)
1527{
1528    return new VertexArrayMtl(state, this);
1529}
1530
1531// Query and Fence creation
1532QueryImpl *ContextMtl::createQuery(gl::QueryType type)
1533{
1534    return new QueryMtl(type);
1535}
1536FenceNVImpl *ContextMtl::createFenceNV()
1537{
1538    return new FenceNVMtl();
1539}
1540SyncImpl *ContextMtl::createSync()
1541{
1542    return new SyncMtl();
1543}
1544
1545// Transform Feedback creation
1546TransformFeedbackImpl *ContextMtl::createTransformFeedback(const gl::TransformFeedbackState &state)
1547{
1548    // NOTE(hqle): ES 3.0
1549    return new TransformFeedbackMtl(state);
1550}
1551
1552// Sampler object creation
1553SamplerImpl *ContextMtl::createSampler(const gl::SamplerState &state)
1554{
1555    return new SamplerMtl(state);
1556}
1557
1558// Program Pipeline object creation
1559ProgramPipelineImpl *ContextMtl::createProgramPipeline(const gl::ProgramPipelineState &data)
1560{
1561    // NOTE(hqle): ES 3.0
1562    UNIMPLEMENTED();
1563    return nullptr;
1564}
1565
1566// Memory object creation.
1567MemoryObjectImpl *ContextMtl::createMemoryObject()
1568{
1569    UNIMPLEMENTED();
1570    return nullptr;
1571}
1572
1573// Semaphore creation.
1574SemaphoreImpl *ContextMtl::createSemaphore()
1575{
1576    UNIMPLEMENTED();
1577    return nullptr;
1578}
1579
1580OverlayImpl *ContextMtl::createOverlay(const gl::OverlayState &state)
1581{
1582    // Not implemented.
1583    return new OverlayImpl(state);
1584}
1585
1586angle::Result ContextMtl::dispatchCompute(const gl::Context *context,
1587                                          GLuint numGroupsX,
1588                                          GLuint numGroupsY,
1589                                          GLuint numGroupsZ)
1590{
1591    // NOTE(hqle): ES 3.0
1592    UNIMPLEMENTED();
1593    return angle::Result::Stop;
1594}
1595angle::Result ContextMtl::dispatchComputeIndirect(const gl::Context *context, GLintptr indirect)
1596{
1597    // NOTE(hqle): ES 3.0
1598    UNIMPLEMENTED();
1599    return angle::Result::Stop;
1600}
1601
1602angle::Result ContextMtl::memoryBarrier(const gl::Context *context, GLbitfield barriers)
1603{
1604    if (barriers == 0)
1605    {
1606        return angle::Result::Continue;
1607    }
1608    if (context->getClientVersion() >= gl::Version{3, 1})
1609    {
1610        // We expect ES 3.0, and as such we don't consider ES 3.1+ objects in this function yet.
1611        UNIMPLEMENTED();
1612        return angle::Result::Stop;
1613    }
1614    MTLBarrierScope scope;
1615    switch (barriers)
1616    {
1617        case GL_ALL_BARRIER_BITS:
1618            scope = MTLBarrierScopeTextures | MTLBarrierScopeBuffers;
1619#if TARGET_OS_OSX || TARGET_OS_MACCATALYST
1620            if (getDisplay()->hasFragmentMemoryBarriers())
1621            {
1622                scope |= MTLBarrierScopeRenderTargets;
1623            }
1624#endif
1625            break;
1626        case GL_SHADER_IMAGE_ACCESS_BARRIER_BIT:
1627            scope = MTLBarrierScopeTextures;
1628#if TARGET_OS_OSX || TARGET_OS_MACCATALYST
1629            if (getDisplay()->hasFragmentMemoryBarriers())
1630            {
1631                // SHADER_IMAGE_ACCESS_BARRIER_BIT (and SHADER_STORAGE_BARRIER_BIT) require that all
1632                // prior types of accesses are finished before writes to the resource. Since this is
1633                // the case, we also have to include render targets in our barrier to ensure any
1634                // rendering completes before an imageLoad().
1635                //
1636                // NOTE: Apple Silicon doesn't support MTLBarrierScopeRenderTargets. This seems to
1637                // work anyway though, and on that hardware we use programmable blending for pixel
1638                // local storage instead of read_write textures anyway.
1639                scope |= MTLBarrierScopeRenderTargets;
1640            }
1641#endif
1642            break;
1643        default:
1644            UNIMPLEMENTED();
1645            return angle::Result::Stop;
1646    }
1647    // The GL API doesn't provide a distinction between different shader stages.
1648    // ES 3.0 doesn't have compute.
1649    MTLRenderStages stages = MTLRenderStageVertex;
1650    if (getDisplay()->hasFragmentMemoryBarriers())
1651    {
1652        stages |= MTLRenderStageFragment;
1653    }
1654    mRenderEncoder.memoryBarrier(scope, stages, stages);
1655    return angle::Result::Continue;
1656}
1657
1658angle::Result ContextMtl::memoryBarrierByRegion(const gl::Context *context, GLbitfield barriers)
1659{
1660    // NOTE(hqle): ES 3.0
1661    UNIMPLEMENTED();
1662    return angle::Result::Stop;
1663}
1664
1665// override mtl::ErrorHandler
1666void ContextMtl::handleError(GLenum glErrorCode,
1667                             const char *message,
1668                             const char *file,
1669                             const char *function,
1670                             unsigned int line)
1671{
1672    mErrors->handleError(glErrorCode, message, file, function, line);
1673}
1674
1675void ContextMtl::handleError(NSError *nserror,
1676                             const char *message,
1677                             const char *file,
1678                             const char *function,
1679                             unsigned int line)
1680{
1681    if (!nserror)
1682    {
1683        return;
1684    }
1685
1686    mErrors->handleError(GL_INVALID_OPERATION, message, file, function, line);
1687}
1688
1689void ContextMtl::invalidateState(const gl::Context *context)
1690{
1691    mDirtyBits.set();
1692
1693    invalidateDefaultAttributes(context->getStateCache().getActiveDefaultAttribsMask());
1694}
1695
1696void ContextMtl::invalidateDefaultAttribute(size_t attribIndex)
1697{
1698    mDirtyDefaultAttribsMask.set(attribIndex);
1699    mDirtyBits.set(DIRTY_BIT_DEFAULT_ATTRIBS);
1700}
1701
1702void ContextMtl::invalidateDefaultAttributes(const gl::AttributesMask &dirtyMask)
1703{
1704    if (dirtyMask.any())
1705    {
1706        mDirtyDefaultAttribsMask |= dirtyMask;
1707        mDirtyBits.set(DIRTY_BIT_DEFAULT_ATTRIBS);
1708    }
1709
1710    // TODO(anglebug.com/40096755): determine how to merge this.
1711#if 0
1712    if (getDisplay()->getFeatures().hasExplicitMemBarrier.enabled)
1713    {
1714        const gl::ProgramExecutable *executable = mState.getProgramExecutable();
1715        ASSERT(executable);
1716        ASSERT(executable->hasTransformFeedbackOutput() || mState.isTransformFeedbackActive());
1717        TransformFeedbackMtl *transformFeedbackMtl = mtl::GetImpl(mState.getCurrentTransformFeedback());
1718        size_t bufferCount                         = executable->getTransformFeedbackBufferCount();
1719        const gl::TransformFeedbackBuffersArray<BufferMtl *> &bufferHandles =
1720            transformFeedbackMtl->getBufferHandles();
1721        for (size_t i = 0; i < bufferCount; i++)
1722        {
1723            const mtl::BufferRef & constBufferRef = bufferHandles[i]->getCurrentBuffer();
1724            mRenderEncoder.memoryBarrierWithResource(constBufferRef, mtl::kRenderStageVertex, mtl::kRenderStageVertex);
1725        }
1726    }
1727    else
1728    {
1729        //End the command encoder, so any Transform Feedback changes are available to subsequent draw calls.
1730        endEncoding(false);
1731    }
1732#endif
1733}
1734
1735void ContextMtl::invalidateCurrentTextures()
1736{
1737    mDirtyBits.set(DIRTY_BIT_TEXTURES);
1738}
1739
1740void ContextMtl::invalidateDriverUniforms()
1741{
1742    mDirtyBits.set(DIRTY_BIT_DRIVER_UNIFORMS);
1743}
1744
1745void ContextMtl::invalidateRenderPipeline()
1746{
1747    mDirtyBits.set(DIRTY_BIT_RENDER_PIPELINE);
1748}
1749
1750const mtl::ClearColorValue &ContextMtl::getClearColorValue() const
1751{
1752    return mClearColor;
1753}
1754const mtl::WriteMaskArray &ContextMtl::getWriteMaskArray() const
1755{
1756    return mWriteMaskArray;
1757}
1758float ContextMtl::getClearDepthValue() const
1759{
1760    return getState().getDepthClearValue();
1761}
1762uint32_t ContextMtl::getClearStencilValue() const
1763{
1764    return mClearStencil;
1765}
1766uint32_t ContextMtl::getStencilMask() const
1767{
1768    return getState().getDepthStencilState().stencilWritemask & mtl::kStencilMaskAll;
1769}
1770
1771bool ContextMtl::getDepthMask() const
1772{
1773    return getState().getDepthStencilState().depthMask;
1774}
1775
1776const mtl::Format &ContextMtl::getPixelFormat(angle::FormatID angleFormatId) const
1777{
1778    return getDisplay()->getPixelFormat(angleFormatId);
1779}
1780
1781// See mtl::FormatTable::getVertexFormat()
1782const mtl::VertexFormat &ContextMtl::getVertexFormat(angle::FormatID angleFormatId,
1783                                                     bool tightlyPacked) const
1784{
1785    return getDisplay()->getVertexFormat(angleFormatId, tightlyPacked);
1786}
1787
1788const mtl::FormatCaps &ContextMtl::getNativeFormatCaps(MTLPixelFormat mtlFormat) const
1789{
1790    return getDisplay()->getNativeFormatCaps(mtlFormat);
1791}
1792
1793angle::Result ContextMtl::getIncompleteTexture(const gl::Context *context,
1794                                               gl::TextureType type,
1795                                               gl::SamplerFormat format,
1796                                               gl::Texture **textureOut)
1797{
1798    return mIncompleteTextures.getIncompleteTexture(context, type, format, nullptr, textureOut);
1799}
1800
1801void ContextMtl::endRenderEncoding(mtl::RenderCommandEncoder *encoder)
1802{
1803    // End any pending visibility query in the render pass
1804    if (mOcclusionQuery)
1805    {
1806        disableActiveOcclusionQueryInRenderPass();
1807    }
1808
1809    if (mBlitEncoder.valid())
1810    {
1811        mBlitEncoder.endEncoding();
1812    }
1813
1814    mOcclusionQueryPool.prepareRenderPassVisibilityPoolBuffer(this);
1815
1816    encoder->endEncoding();
1817
1818    // Resolve visibility results
1819    mOcclusionQueryPool.resolveVisibilityResults(this);
1820}
1821
1822void ContextMtl::endBlitAndComputeEncoding()
1823{
1824    if (mBlitEncoder.valid())
1825    {
1826        mBlitEncoder.endEncoding();
1827    }
1828
1829    if (mComputeEncoder.valid())
1830    {
1831        mComputeEncoder.endEncoding();
1832        mProvokingVertexHelper.releaseInFlightBuffers(this);
1833    }
1834}
1835
1836void ContextMtl::endEncoding(bool forceSaveRenderPassContent)
1837{
1838    endBlitAndComputeEncoding();
1839
1840    if (mRenderEncoder.valid())
1841    {
1842        if (forceSaveRenderPassContent)
1843        {
1844            // Save the work in progress.
1845            mRenderEncoder.setStoreAction(MTLStoreActionStore);
1846        }
1847
1848        endRenderEncoding(&mRenderEncoder);
1849    }
1850    // End blit encoder after render encoder, as endRenderEncoding() might create a
1851    // blit encoder to resolve the visibility results.
1852    if (mBlitEncoder.valid())
1853    {
1854        mBlitEncoder.endEncoding();
1855    }
1856}
1857
1858void ContextMtl::flushCommandBuffer(mtl::CommandBufferFinishOperation operation)
1859{
1860    mRenderPassesSinceFlush = 0;
1861    if (mCmdBuffer.ready())
1862    {
1863        endEncoding(true);
1864        mCmdBuffer.commit(operation);
1865        mBufferManager.incrementNumCommandBufferCommits();
1866    }
1867    else
1868    {
1869        mCmdBuffer.wait(operation);
1870    }
1871}
1872
1873void ContextMtl::flushCommandBufferIfNeeded()
1874{
1875    if (mRenderPassesSinceFlush >= mtl::kMaxRenderPassesPerCommandBuffer ||
1876        mCmdBuffer.needsFlushForDrawCallLimits())
1877    {
1878        // Ensure that we don't accumulate too many unflushed render passes. Don't wait until they
1879        // are submitted, other components handle backpressure so don't create uneccessary CPU/GPU
1880        // synchronization.
1881        flushCommandBuffer(mtl::NoWait);
1882    }
1883}
1884
1885void ContextMtl::present(const gl::Context *context, id<CAMetalDrawable> presentationDrawable)
1886{
1887    ensureCommandBufferReady();
1888
1889    FramebufferMtl *currentframebuffer = mtl::GetImpl(getState().getDrawFramebuffer());
1890    if (currentframebuffer)
1891    {
1892        currentframebuffer->onFrameEnd(context);
1893    }
1894
1895    endEncoding(false);
1896    mCmdBuffer.present(presentationDrawable);
1897    mCmdBuffer.commit(mtl::NoWait);
1898    mRenderPassesSinceFlush = 0;
1899}
1900
1901angle::Result ContextMtl::finishCommandBuffer()
1902{
1903    flushCommandBuffer(mtl::WaitUntilFinished);
1904    return angle::Result::Continue;
1905}
1906
1907bool ContextMtl::hasStartedRenderPass(const mtl::RenderPassDesc &desc)
1908{
1909    return mRenderEncoder.valid() &&
1910           mRenderEncoder.renderPassDesc().equalIgnoreLoadStoreOptions(desc);
1911}
1912
1913bool ContextMtl::isCurrentRenderEncoderSerial(uint64_t serial)
1914{
1915    if (!mRenderEncoder.valid())
1916    {
1917        return false;
1918    }
1919
1920    return serial == mRenderEncoder.getSerial();
1921}
1922
1923// Get current render encoder
1924mtl::RenderCommandEncoder *ContextMtl::getRenderCommandEncoder()
1925{
1926    if (!mRenderEncoder.valid())
1927    {
1928        return nullptr;
1929    }
1930
1931    return &mRenderEncoder;
1932}
1933
1934mtl::RenderCommandEncoder *ContextMtl::getRenderPassCommandEncoder(const mtl::RenderPassDesc &desc)
1935{
1936    if (hasStartedRenderPass(desc))
1937    {
1938        return &mRenderEncoder;
1939    }
1940
1941    endEncoding(false);
1942
1943    ensureCommandBufferReady();
1944    ++mRenderPassesSinceFlush;
1945
1946    // Need to re-apply everything on next draw call.
1947    mDirtyBits.set();
1948
1949    const mtl::ContextDevice &metalDevice = getMetalDevice();
1950    if (mtl::DeviceHasMaximumRenderTargetSize(metalDevice))
1951    {
1952        NSUInteger maxSize = mtl::GetMaxRenderTargetSizeForDeviceInBytes(metalDevice);
1953        NSUInteger renderTargetSize =
1954            ComputeTotalSizeUsedForMTLRenderPassDescriptor(desc, this, metalDevice);
1955        if (renderTargetSize > maxSize)
1956        {
1957            std::stringstream errorStream;
1958            errorStream << "This set of render targets requires " << renderTargetSize
1959                        << " bytes of pixel storage. This device supports " << maxSize << " bytes.";
1960            ANGLE_MTL_HANDLE_ERROR(this, errorStream.str().c_str(), GL_INVALID_OPERATION);
1961            return nullptr;
1962        }
1963    }
1964    return &mRenderEncoder.restart(desc, getNativeCaps().maxColorAttachments);
1965}
1966
1967// Utilities to quickly create render command encoder to a specific texture:
1968// The previous content of texture will be loaded
1969mtl::RenderCommandEncoder *ContextMtl::getTextureRenderCommandEncoder(
1970    const mtl::TextureRef &textureTarget,
1971    const mtl::ImageNativeIndex &index)
1972{
1973    ASSERT(textureTarget && textureTarget->valid());
1974
1975    mtl::RenderPassDesc rpDesc;
1976
1977    rpDesc.colorAttachments[0].texture      = textureTarget;
1978    rpDesc.colorAttachments[0].level        = index.getNativeLevel();
1979    rpDesc.colorAttachments[0].sliceOrDepth = index.hasLayer() ? index.getLayerIndex() : 0;
1980    rpDesc.numColorAttachments              = 1;
1981    rpDesc.rasterSampleCount                = textureTarget->samples();
1982
1983    return getRenderPassCommandEncoder(rpDesc);
1984}
1985
1986// The previous content of texture will be loaded if clearColor is not provided
1987mtl::RenderCommandEncoder *ContextMtl::getRenderTargetCommandEncoderWithClear(
1988    const RenderTargetMtl &renderTarget,
1989    const Optional<MTLClearColor> &clearColor)
1990{
1991    ASSERT(renderTarget.getTexture());
1992
1993    mtl::RenderPassDesc rpDesc;
1994    renderTarget.toRenderPassAttachmentDesc(&rpDesc.colorAttachments[0]);
1995    rpDesc.numColorAttachments = 1;
1996    rpDesc.rasterSampleCount   = renderTarget.getRenderSamples();
1997
1998    if (clearColor.valid())
1999    {
2000        rpDesc.colorAttachments[0].loadAction = MTLLoadActionClear;
2001        rpDesc.colorAttachments[0].clearColor = mtl::EmulatedAlphaClearColor(
2002            clearColor.value(), renderTarget.getTexture()->getColorWritableMask());
2003
2004        endEncoding(true);
2005    }
2006
2007    return getRenderPassCommandEncoder(rpDesc);
2008}
2009// The previous content of texture will be loaded
2010mtl::RenderCommandEncoder *ContextMtl::getRenderTargetCommandEncoder(
2011    const RenderTargetMtl &renderTarget)
2012{
2013    return getRenderTargetCommandEncoderWithClear(renderTarget, Optional<MTLClearColor>());
2014}
2015
2016mtl::BlitCommandEncoder *ContextMtl::getBlitCommandEncoder()
2017{
2018    if (mRenderEncoder.valid() || mComputeEncoder.valid())
2019    {
2020        endEncoding(true);
2021    }
2022
2023    if (mBlitEncoder.valid())
2024    {
2025        return &mBlitEncoder;
2026    }
2027
2028    endEncoding(true);
2029    ensureCommandBufferReady();
2030
2031    return &mBlitEncoder.restart();
2032}
2033
2034mtl::BlitCommandEncoder *ContextMtl::getBlitCommandEncoderWithoutEndingRenderEncoder()
2035{
2036    if (mBlitEncoder.valid())
2037    {
2038        return &mBlitEncoder;
2039    }
2040
2041    endBlitAndComputeEncoding();
2042    ensureCommandBufferReady();
2043
2044    return &mBlitEncoder.restart();
2045}
2046
2047mtl::ComputeCommandEncoder *ContextMtl::getComputeCommandEncoder()
2048{
2049    if (mRenderEncoder.valid() || mBlitEncoder.valid())
2050    {
2051        endEncoding(true);
2052    }
2053
2054    if (mComputeEncoder.valid())
2055    {
2056        return &mComputeEncoder;
2057    }
2058
2059    endEncoding(true);
2060    ensureCommandBufferReady();
2061
2062    return &mComputeEncoder.restart();
2063}
2064
2065mtl::ComputeCommandEncoder *ContextMtl::getComputeCommandEncoderWithoutEndingRenderEncoder()
2066{
2067    if (mComputeEncoder.valid())
2068    {
2069        return &mComputeEncoder;
2070    }
2071
2072    endBlitAndComputeEncoding();
2073    ensureCommandBufferReady();
2074
2075    return &mComputeEncoder.restart();
2076}
2077
2078mtl::ComputeCommandEncoder *ContextMtl::getIndexPreprocessingCommandEncoder()
2079{
2080    return getComputeCommandEncoder();
2081}
2082
2083void ContextMtl::ensureCommandBufferReady()
2084{
2085    flushCommandBufferIfNeeded();
2086
2087    if (!mCmdBuffer.ready())
2088    {
2089        mCmdBuffer.restart();
2090    }
2091
2092    ASSERT(mCmdBuffer.ready());
2093}
2094
2095void ContextMtl::updateViewport(FramebufferMtl *framebufferMtl,
2096                                const gl::Rectangle &viewport,
2097                                float nearPlane,
2098                                float farPlane)
2099{
2100    mViewport = mtl::GetViewport(viewport, framebufferMtl->getState().getDimensions().height,
2101                                 framebufferMtl->flipY(), nearPlane, farPlane);
2102    mDirtyBits.set(DIRTY_BIT_VIEWPORT);
2103
2104    invalidateDriverUniforms();
2105}
2106
2107void ContextMtl::updateDepthRange(float nearPlane, float farPlane)
2108{
2109    if (NeedToInvertDepthRange(nearPlane, farPlane))
2110    {
2111        // We also need to invert the depth in shader later by using scale value stored in driver
2112        // uniform depthRange.reserved
2113        std::swap(nearPlane, farPlane);
2114    }
2115    mViewport.znear = nearPlane;
2116    mViewport.zfar  = farPlane;
2117    mDirtyBits.set(DIRTY_BIT_VIEWPORT);
2118
2119    invalidateDriverUniforms();
2120}
2121
2122void ContextMtl::updateBlendDescArray(const gl::BlendStateExt &blendStateExt)
2123{
2124    for (size_t i = 0; i < mBlendDescArray.size(); i++)
2125    {
2126        mtl::BlendDesc &blendDesc = mBlendDescArray[i];
2127        if (blendStateExt.getEnabledMask().test(i))
2128        {
2129            blendDesc.blendingEnabled = true;
2130
2131            blendDesc.sourceRGBBlendFactor =
2132                mtl::GetBlendFactor(blendStateExt.getSrcColorIndexed(i));
2133            blendDesc.sourceAlphaBlendFactor =
2134                mtl::GetBlendFactor(blendStateExt.getSrcAlphaIndexed(i));
2135            blendDesc.destinationRGBBlendFactor =
2136                mtl::GetBlendFactor(blendStateExt.getDstColorIndexed(i));
2137            blendDesc.destinationAlphaBlendFactor =
2138                mtl::GetBlendFactor(blendStateExt.getDstAlphaIndexed(i));
2139
2140            blendDesc.rgbBlendOperation = mtl::GetBlendOp(blendStateExt.getEquationColorIndexed(i));
2141            blendDesc.alphaBlendOperation =
2142                mtl::GetBlendOp(blendStateExt.getEquationAlphaIndexed(i));
2143        }
2144        else
2145        {
2146            // Enforce default state when blending is disabled,
2147            blendDesc.reset(blendDesc.writeMask);
2148        }
2149    }
2150    invalidateRenderPipeline();
2151}
2152
2153void ContextMtl::updateScissor(const gl::State &glState)
2154{
2155    FramebufferMtl *framebufferMtl = mtl::GetImpl(glState.getDrawFramebuffer());
2156    gl::Rectangle renderArea       = framebufferMtl->getCompleteRenderArea();
2157
2158    ANGLE_MTL_LOG("renderArea = %d,%d,%d,%d", renderArea.x, renderArea.y, renderArea.width,
2159                  renderArea.height);
2160
2161    // Clip the render area to the viewport.
2162    gl::Rectangle viewportClippedRenderArea;
2163    if (!gl::ClipRectangle(renderArea, glState.getViewport(), &viewportClippedRenderArea))
2164    {
2165        viewportClippedRenderArea = gl::Rectangle();
2166    }
2167
2168    gl::Rectangle scissoredArea = ClipRectToScissor(getState(), viewportClippedRenderArea, false);
2169    if (framebufferMtl->flipY())
2170    {
2171        scissoredArea.y = renderArea.height - scissoredArea.y - scissoredArea.height;
2172    }
2173
2174    ANGLE_MTL_LOG("scissoredArea = %d,%d,%d,%d", scissoredArea.x, scissoredArea.y,
2175                  scissoredArea.width, scissoredArea.height);
2176
2177    mScissorRect = mtl::GetScissorRect(scissoredArea);
2178    mDirtyBits.set(DIRTY_BIT_SCISSOR);
2179}
2180
2181void ContextMtl::updateCullMode(const gl::State &glState)
2182{
2183    const gl::RasterizerState &rasterState = glState.getRasterizerState();
2184
2185    mCullAllPolygons = false;
2186    if (!rasterState.cullFace)
2187    {
2188        mCullMode = MTLCullModeNone;
2189    }
2190    else
2191    {
2192        switch (rasterState.cullMode)
2193        {
2194            case gl::CullFaceMode::Back:
2195                mCullMode = MTLCullModeBack;
2196                break;
2197            case gl::CullFaceMode::Front:
2198                mCullMode = MTLCullModeFront;
2199                break;
2200            case gl::CullFaceMode::FrontAndBack:
2201                mCullAllPolygons = true;
2202                break;
2203            default:
2204                UNREACHABLE();
2205                break;
2206        }
2207    }
2208
2209    mDirtyBits.set(DIRTY_BIT_CULL_MODE);
2210}
2211
2212void ContextMtl::updateFrontFace(const gl::State &glState)
2213{
2214    FramebufferMtl *framebufferMtl = mtl::GetImpl(glState.getDrawFramebuffer());
2215    const bool upperLeftOrigin     = mState.getClipOrigin() == gl::ClipOrigin::UpperLeft;
2216    mWinding = mtl::GetFrontfaceWinding(glState.getRasterizerState().frontFace,
2217                                        framebufferMtl->flipY() == upperLeftOrigin);
2218    mDirtyBits.set(DIRTY_BIT_WINDING);
2219}
2220
2221// Index rewrite is required if:
2222// Provkoing vertex mode is 'last'
2223// Program has at least one 'flat' attribute
2224// PrimitiveMode is not POINTS.
2225bool ContextMtl::requiresIndexRewrite(const gl::State &state, gl::PrimitiveMode mode)
2226{
2227    return mode != gl::PrimitiveMode::Points && mExecutable->hasFlatAttribute() &&
2228           (state.getProvokingVertex() == gl::ProvokingVertexConvention::LastVertexConvention);
2229}
2230
2231void ContextMtl::updateDrawFrameBufferBinding(const gl::Context *context)
2232{
2233    const gl::State &glState = getState();
2234
2235    FramebufferMtl *newDrawFramebuffer = mtl::GetImpl(glState.getDrawFramebuffer());
2236    if (newDrawFramebuffer != mDrawFramebuffer)
2237    {
2238        // Reset this flag if the framebuffer has changed to not sync it twice
2239        mForceResyncDrawFramebuffer = false;
2240    }
2241
2242    mDrawFramebuffer = newDrawFramebuffer;
2243
2244    mDrawFramebuffer->onStartedDrawingToFrameBuffer(context);
2245
2246    onDrawFrameBufferChangedState(context, mDrawFramebuffer, true);
2247}
2248
2249void ContextMtl::onDrawFrameBufferChangedState(const gl::Context *context,
2250                                               FramebufferMtl *framebuffer,
2251                                               bool renderPassChanged)
2252{
2253    const gl::State &glState = getState();
2254    ASSERT(framebuffer == mtl::GetImpl(glState.getDrawFramebuffer()));
2255
2256    updateViewport(framebuffer, glState.getViewport(), glState.getNearPlane(),
2257                   glState.getFarPlane());
2258    updateFrontFace(glState);
2259    updateScissor(glState);
2260
2261    if (renderPassChanged)
2262    {
2263        // End any render encoding using the old render pass.
2264        endEncoding(false);
2265        // Need to re-apply state to RenderCommandEncoder
2266        invalidateState(context);
2267    }
2268    else
2269    {
2270        // Invalidate current pipeline only.
2271        invalidateRenderPipeline();
2272    }
2273}
2274
2275void ContextMtl::onBackbufferResized(const gl::Context *context, WindowSurfaceMtl *backbuffer)
2276{
2277    const gl::State &glState    = getState();
2278    FramebufferMtl *framebuffer = mtl::GetImpl(glState.getDrawFramebuffer());
2279    if (framebuffer->getAttachedBackbuffer() != backbuffer)
2280    {
2281        return;
2282    }
2283
2284    onDrawFrameBufferChangedState(context, framebuffer, true);
2285}
2286
2287angle::Result ContextMtl::onOcclusionQueryBegin(const gl::Context *context, QueryMtl *query)
2288{
2289    ASSERT(mOcclusionQuery == nullptr);
2290    mOcclusionQuery = query;
2291
2292    if (mRenderEncoder.valid())
2293    {
2294        // if render pass has started, start the query in the encoder
2295        return startOcclusionQueryInRenderPass(query, true);
2296    }
2297    else
2298    {
2299        query->resetVisibilityResult(this);
2300    }
2301
2302    return angle::Result::Continue;
2303}
2304void ContextMtl::onOcclusionQueryEnd(const gl::Context *context, QueryMtl *query)
2305{
2306    ASSERT(mOcclusionQuery == query);
2307
2308    if (mRenderEncoder.valid())
2309    {
2310        // if render pass has started, end the query in the encoder
2311        disableActiveOcclusionQueryInRenderPass();
2312    }
2313
2314    mOcclusionQuery = nullptr;
2315}
2316void ContextMtl::onOcclusionQueryDestroy(const gl::Context *context, QueryMtl *query)
2317{
2318    if (query->getAllocatedVisibilityOffsets().empty())
2319    {
2320        return;
2321    }
2322    if (mOcclusionQuery == query)
2323    {
2324        onOcclusionQueryEnd(context, query);
2325    }
2326    mOcclusionQueryPool.deallocateQueryOffset(this, query);
2327}
2328
2329void ContextMtl::disableActiveOcclusionQueryInRenderPass()
2330{
2331    if (!mOcclusionQuery || mOcclusionQuery->getAllocatedVisibilityOffsets().empty())
2332    {
2333        return;
2334    }
2335
2336    ASSERT(mRenderEncoder.valid());
2337    mRenderEncoder.setVisibilityResultMode(MTLVisibilityResultModeDisabled,
2338                                           mOcclusionQuery->getAllocatedVisibilityOffsets().back());
2339}
2340
2341angle::Result ContextMtl::restartActiveOcclusionQueryInRenderPass()
2342{
2343    if (!mOcclusionQuery || mOcclusionQuery->getAllocatedVisibilityOffsets().empty())
2344    {
2345        return angle::Result::Continue;
2346    }
2347
2348    return startOcclusionQueryInRenderPass(mOcclusionQuery, false);
2349}
2350
2351angle::Result ContextMtl::startOcclusionQueryInRenderPass(QueryMtl *query, bool clearOldValue)
2352{
2353    ASSERT(mRenderEncoder.valid());
2354
2355    ANGLE_TRY(mOcclusionQueryPool.allocateQueryOffset(this, query, clearOldValue));
2356
2357    mRenderEncoder.setVisibilityResultMode(MTLVisibilityResultModeBoolean,
2358                                           query->getAllocatedVisibilityOffsets().back());
2359
2360    // We need to mark the query's buffer as being written in this command buffer now. Since the
2361    // actual writing is deferred until the render pass ends and user could try to read the query
2362    // result before the render pass ends.
2363    mCmdBuffer.setWriteDependency(query->getVisibilityResultBuffer(), /*isRenderCommand=*/true);
2364
2365    return angle::Result::Continue;
2366}
2367
2368void ContextMtl::onTransformFeedbackActive(const gl::Context *context, TransformFeedbackMtl *xfb)
2369{
2370    // NOTE(hqle): We have to end current render pass to enable synchronization before XFB
2371    // buffers could be used as vertex input. Consider a better approach.
2372    endEncoding(true);
2373}
2374
2375void ContextMtl::onTransformFeedbackInactive(const gl::Context *context, TransformFeedbackMtl *xfb)
2376{
2377    // NOTE(hqle): We have to end current render pass to enable synchronization before XFB
2378    // buffers could be used as vertex input. Consider a better approach.
2379    endEncoding(true);
2380}
2381
2382uint64_t ContextMtl::queueEventSignal(id<MTLEvent> event, uint64_t value)
2383{
2384    ensureCommandBufferReady();
2385    // Event is queued to be signaled after current render pass. If we have helper blit or
2386    // compute encoders, avoid queueing by stopping them immediately so we get to insert the event
2387    // right away.
2388    endBlitAndComputeEncoding();
2389    return mCmdBuffer.queueEventSignal(event, value);
2390}
2391
2392void ContextMtl::serverWaitEvent(id<MTLEvent> event, uint64_t value)
2393{
2394    ensureCommandBufferReady();
2395
2396    // Event waiting cannot be encoded if there is active encoder.
2397    endEncoding(true);
2398
2399    mCmdBuffer.serverWaitEvent(event, value);
2400}
2401
2402void ContextMtl::updateProgramExecutable(const gl::Context *context)
2403{
2404    // Need to rebind textures
2405    invalidateCurrentTextures();
2406    // Need to re-upload default attributes
2407    invalidateDefaultAttributes(context->getStateCache().getActiveDefaultAttribsMask());
2408    // Render pipeline need to be re-applied
2409    invalidateRenderPipeline();
2410}
2411
2412void ContextMtl::updateVertexArray(const gl::Context *context)
2413{
2414    const gl::State &glState = getState();
2415    mVertexArray             = mtl::GetImpl(glState.getVertexArray());
2416    invalidateDefaultAttributes(context->getStateCache().getActiveDefaultAttribsMask());
2417    invalidateRenderPipeline();
2418}
2419
2420angle::Result ContextMtl::updateDefaultAttribute(size_t attribIndex)
2421{
2422    const gl::State &glState = mState;
2423    const gl::VertexAttribCurrentValueData &defaultValue =
2424        glState.getVertexAttribCurrentValues()[attribIndex];
2425
2426    constexpr size_t kDefaultGLAttributeValueSize =
2427        sizeof(gl::VertexAttribCurrentValueData::Values);
2428
2429    static_assert(kDefaultGLAttributeValueSize == mtl::kDefaultAttributeSize,
2430                  "Unexpected default attribute size");
2431    memcpy(mDefaultAttributes[attribIndex].values, &defaultValue.Values,
2432           mtl::kDefaultAttributeSize);
2433
2434    return angle::Result::Continue;
2435}
2436
2437static bool isDrawNoOp(const mtl::RenderPipelineDesc &descriptor,
2438                       ContextMtl *context,
2439                       const mtl::ContextDevice &device)
2440{
2441    // Ensure there is at least one valid render target.
2442    bool hasValidRenderTarget = false;
2443
2444    const NSUInteger maxColorRenderTargets = GetMaxNumberOfRenderTargetsForDevice(device);
2445    for (NSUInteger i = 0; i < maxColorRenderTargets; ++i)
2446    {
2447        const auto &colorAttachment = descriptor.outputDescriptor.colorAttachments[i];
2448        if (colorAttachment.pixelFormat != MTLPixelFormatInvalid)
2449        {
2450            hasValidRenderTarget = true;
2451            break;
2452        }
2453    }
2454
2455    if (!hasValidRenderTarget &&
2456        descriptor.outputDescriptor.depthAttachmentPixelFormat != MTLPixelFormatInvalid)
2457    {
2458        hasValidRenderTarget = true;
2459    }
2460
2461    if (!hasValidRenderTarget &&
2462        descriptor.outputDescriptor.stencilAttachmentPixelFormat != MTLPixelFormatInvalid)
2463    {
2464        hasValidRenderTarget = true;
2465    }
2466
2467    if (!hasValidRenderTarget)
2468    {
2469        FramebufferMtl *framebufferMtl = mtl::GetImpl(context->getState().getDrawFramebuffer());
2470        hasValidRenderTarget           = framebufferMtl->renderPassHasDefaultWidthOrHeight();
2471    }
2472
2473    // Draw is no op if there is no valid render target, and we're not in a
2474    // rasterization-disabled draw.
2475
2476    bool noRenderTarget        = !hasValidRenderTarget;
2477    bool rasterizationDisabled = !descriptor.rasterizationEnabled();
2478    return !rasterizationDisabled && noRenderTarget;
2479}
2480
2481angle::Result ContextMtl::setupDraw(const gl::Context *context,
2482                                    gl::PrimitiveMode mode,
2483                                    GLint firstVertex,
2484                                    GLsizei vertexOrIndexCount,
2485                                    GLsizei instances,
2486                                    gl::DrawElementsType indexTypeOrNone,
2487                                    const void *indices,
2488                                    bool xfbPass,
2489                                    bool *isNoOp)
2490{
2491    ANGLE_TRY(setupDrawImpl(context, mode, firstVertex, vertexOrIndexCount, instances,
2492                            indexTypeOrNone, indices, xfbPass, isNoOp));
2493    if (*isNoOp)
2494    {
2495        return angle::Result::Continue;
2496    }
2497    if (!mRenderEncoder.valid())
2498    {
2499        // Flush occurred during setup, due to running out of memory while setting up the render
2500        // pass state. This would happen for example when there is no more space in the uniform
2501        // buffers in the uniform buffer pool. The rendering would be flushed to free the uniform
2502        // buffer memory for new usage. In this case, re-run the setup.
2503        ANGLE_TRY(setupDrawImpl(context, mode, firstVertex, vertexOrIndexCount, instances,
2504                                indexTypeOrNone, indices, xfbPass, isNoOp));
2505
2506        if (*isNoOp)
2507        {
2508            return angle::Result::Continue;
2509        }
2510        // Setup with flushed state should either produce a working encoder or fail with an error
2511        // result.
2512        ASSERT(mRenderEncoder.valid());
2513    }
2514    return angle::Result::Continue;
2515}
2516
2517angle::Result ContextMtl::setupDrawImpl(const gl::Context *context,
2518                                        gl::PrimitiveMode mode,
2519                                        GLint firstVertex,
2520                                        GLsizei vertexOrIndexCount,
2521                                        GLsizei instances,
2522                                        gl::DrawElementsType indexTypeOrNone,
2523                                        const void *indices,
2524                                        bool xfbPass,
2525                                        bool *isNoOp)
2526{
2527    ASSERT(mExecutable);
2528    *isNoOp = false;
2529    // instances=0 means no instanced draw.
2530    GLsizei instanceCount = instances ? instances : 1;
2531
2532    if (context->getStateCache().hasAnyActiveClientAttrib())
2533    {
2534        ANGLE_TRY(mVertexArray->updateClientAttribs(context, firstVertex, vertexOrIndexCount,
2535                                                    instanceCount, indexTypeOrNone, indices));
2536    }
2537
2538    // This must be called before render command encoder is started.
2539    bool textureChanged = false;
2540    if (mDirtyBits.test(DIRTY_BIT_TEXTURES))
2541    {
2542        textureChanged = true;
2543        ANGLE_TRY(handleDirtyActiveTextures(context));
2544    }
2545
2546    if (mDirtyBits.test(DIRTY_BIT_RASTERIZER_DISCARD))
2547    {
2548        if (getState().isTransformFeedbackActiveUnpaused())
2549        {
2550            // If XFB is active we need to reset render pass since we could use a dummy render
2551            // target if only XFB is needed.
2552            invalidateState(context);
2553        }
2554        else
2555        {
2556            invalidateRenderPipeline();
2557        }
2558    }
2559
2560    if (!mRenderEncoder.valid())
2561    {
2562        // re-apply everything
2563        invalidateState(context);
2564    }
2565
2566    if (mDirtyBits.test(DIRTY_BIT_DRAW_FRAMEBUFFER))
2567    {
2568        ANGLE_TRY(handleDirtyRenderPass(context));
2569    }
2570
2571    if (mOcclusionQuery && mOcclusionQueryPool.getNumRenderPassAllocatedQueries() == 0)
2572    {
2573        // The occlusion query is still active, and a new render pass has started.
2574        // We need to continue the querying process in the new render encoder.
2575        ANGLE_TRY(startOcclusionQueryInRenderPass(mOcclusionQuery, false));
2576    }
2577
2578    bool isPipelineDescChanged;
2579    ANGLE_TRY(checkIfPipelineChanged(context, mode, xfbPass, &isPipelineDescChanged));
2580
2581    bool uniformBuffersDirty = false;
2582
2583    if (IsTransformFeedbackOnly(getState()))
2584    {
2585        // Filter out unneeded dirty bits
2586        filterOutXFBOnlyDirtyBits(context);
2587    }
2588
2589    for (size_t bit : mDirtyBits)
2590    {
2591        switch (bit)
2592        {
2593            case DIRTY_BIT_TEXTURES:
2594                // Already handled.
2595                break;
2596            case DIRTY_BIT_DEFAULT_ATTRIBS:
2597                ANGLE_TRY(handleDirtyDefaultAttribs(context));
2598                break;
2599            case DIRTY_BIT_DRIVER_UNIFORMS:
2600                ANGLE_TRY(handleDirtyDriverUniforms(context, firstVertex, vertexOrIndexCount));
2601                break;
2602            case DIRTY_BIT_DEPTH_STENCIL_DESC:
2603                ANGLE_TRY(handleDirtyDepthStencilState(context));
2604                break;
2605            case DIRTY_BIT_DEPTH_BIAS:
2606                ANGLE_TRY(handleDirtyDepthBias(context));
2607                break;
2608            case DIRTY_BIT_DEPTH_CLIP_MODE:
2609                mRenderEncoder.setDepthClipMode(
2610                    mState.isDepthClampEnabled() ? MTLDepthClipModeClamp : MTLDepthClipModeClip);
2611                break;
2612            case DIRTY_BIT_STENCIL_REF:
2613                mRenderEncoder.setStencilRefVals(mStencilRefFront, mStencilRefBack);
2614                break;
2615            case DIRTY_BIT_BLEND_COLOR:
2616                mRenderEncoder.setBlendColor(
2617                    mState.getBlendColor().red, mState.getBlendColor().green,
2618                    mState.getBlendColor().blue, mState.getBlendColor().alpha);
2619                break;
2620            case DIRTY_BIT_VIEWPORT:
2621                mRenderEncoder.setViewport(mViewport);
2622                break;
2623            case DIRTY_BIT_SCISSOR:
2624                mRenderEncoder.setScissorRect(mScissorRect);
2625                break;
2626            case DIRTY_BIT_DRAW_FRAMEBUFFER:
2627                // Already handled.
2628                break;
2629            case DIRTY_BIT_CULL_MODE:
2630                mRenderEncoder.setCullMode(mCullMode);
2631                break;
2632            case DIRTY_BIT_FILL_MODE:
2633                mRenderEncoder.setTriangleFillMode(mState.getPolygonMode() == gl::PolygonMode::Fill
2634                                                       ? MTLTriangleFillModeFill
2635                                                       : MTLTriangleFillModeLines);
2636                break;
2637            case DIRTY_BIT_WINDING:
2638                mRenderEncoder.setFrontFacingWinding(mWinding);
2639                break;
2640            case DIRTY_BIT_RENDER_PIPELINE:
2641                // Already handled. See checkIfPipelineChanged().
2642                break;
2643            case DIRTY_BIT_UNIFORM_BUFFERS_BINDING:
2644                uniformBuffersDirty = true;
2645                break;
2646            case DIRTY_BIT_RASTERIZER_DISCARD:
2647                // Already handled.
2648                break;
2649            default:
2650                UNREACHABLE();
2651                break;
2652        }
2653    }
2654
2655    if (xfbPass && !mDirtyBits.test(DIRTY_BIT_DRIVER_UNIFORMS))
2656    {
2657        // If handleDirtyDriverUniforms() was not called and this is XFB pass, we still need to
2658        // update XFB related uniforms
2659        ANGLE_TRY(
2660            fillDriverXFBUniforms(firstVertex, vertexOrIndexCount, /** skippedInstances */ 0));
2661        mRenderEncoder.setVertexData(mDriverUniforms, mtl::kDriverUniformsBindingIndex);
2662    }
2663
2664    mDirtyBits.reset();
2665    // Check to see if our state would lead to a no-op draw.
2666    // If so, skip program setup until we end up with a state that requires a program.
2667    if (isDrawNoOp(mRenderPipelineDesc, this, mContextDevice))
2668    {
2669        *isNoOp = true;
2670    }
2671    else
2672    {
2673        ANGLE_TRY(mExecutable->setupDraw(context, &mRenderEncoder, mRenderPipelineDesc,
2674                                         isPipelineDescChanged, textureChanged,
2675                                         uniformBuffersDirty));
2676    }
2677
2678    return angle::Result::Continue;
2679}
2680
2681void ContextMtl::filterOutXFBOnlyDirtyBits(const gl::Context *context)
2682{
2683    ASSERT(IsTransformFeedbackOnly(getState()));
2684
2685    ASSERT(mRenderEncoder.renderPassDesc().colorAttachments[0].texture == mDummyXFBRenderTexture);
2686
2687    // In transform feedback only pass, only vertex shader's related states are needed.
2688    constexpr size_t kUnneededBits =
2689        angle::Bit<size_t>(DIRTY_BIT_DEPTH_STENCIL_DESC) |
2690        angle::Bit<size_t>(DIRTY_BIT_DEPTH_BIAS) | angle::Bit<size_t>(DIRTY_BIT_STENCIL_REF) |
2691        angle::Bit<size_t>(DIRTY_BIT_BLEND_COLOR) | angle::Bit<size_t>(DIRTY_BIT_VIEWPORT) |
2692        angle::Bit<size_t>(DIRTY_BIT_SCISSOR) | angle::Bit<size_t>(DIRTY_BIT_CULL_MODE) |
2693        angle::Bit<size_t>(DIRTY_BIT_FILL_MODE) | angle::Bit<size_t>(DIRTY_BIT_WINDING);
2694
2695    mDirtyBits &= ~kUnneededBits;
2696}
2697
2698angle::Result ContextMtl::handleDirtyRenderPass(const gl::Context *context)
2699{
2700    if (!IsTransformFeedbackOnly(mState))
2701    {
2702        // Start new render command encoder
2703        mtl::RenderCommandEncoder *encoder;
2704        ANGLE_TRY(mDrawFramebuffer->ensureRenderPassStarted(context, &encoder));
2705    }
2706    else
2707    {
2708        // XFB is active and rasterization is disabled. Use dummy render target.
2709        // We currently need to end the render pass when XFB is activated/deactivated so using
2710        // a small dummy render target would make the render pass ending very cheap.
2711        if (!mDummyXFBRenderTexture)
2712        {
2713            ANGLE_TRY(mtl::Texture::Make2DTexture(this,
2714                                                  getPixelFormat(angle::FormatID::R8G8B8A8_UNORM),
2715                                                  1, 1, 1, true, false, &mDummyXFBRenderTexture));
2716        }
2717        mtl::RenderCommandEncoder *encoder = getTextureRenderCommandEncoder(
2718            mDummyXFBRenderTexture,
2719            mtl::ImageNativeIndex::FromBaseZeroGLIndex(gl::ImageIndex::Make2D(0)));
2720        encoder->setColorLoadAction(MTLLoadActionDontCare, MTLClearColor(), 0);
2721        encoder->setColorStoreAction(MTLStoreActionDontCare);
2722
2723#ifndef NDEBUG
2724        encoder->setLabel(@"TransformFeedbackOnlyPass");
2725#endif
2726    }
2727
2728    // re-apply everything
2729    invalidateState(context);
2730
2731    return angle::Result::Continue;
2732}
2733
2734angle::Result ContextMtl::handleDirtyActiveTextures(const gl::Context *context)
2735{
2736    const gl::State &glState                = mState;
2737    const gl::ProgramExecutable *executable = glState.getProgramExecutable();
2738
2739    constexpr auto ensureTextureStorageCreated = [](const gl::Context *context,
2740                                                    gl::Texture *texture) -> angle::Result {
2741        if (texture == nullptr)
2742        {
2743            return angle::Result::Continue;
2744        }
2745
2746        TextureMtl *textureMtl = mtl::GetImpl(texture);
2747
2748        // Make sure texture's image definitions will be transferred to GPU.
2749        ANGLE_TRY(textureMtl->ensureNativeStorageCreated(context));
2750
2751        // The binding of this texture will be done by ProgramMtl.
2752        return angle::Result::Continue;
2753    };
2754
2755    const gl::ActiveTexturesCache &textures     = glState.getActiveTexturesCache();
2756    const gl::ActiveTextureMask &activeTextures = executable->getActiveSamplersMask();
2757
2758    for (size_t textureUnit : activeTextures)
2759    {
2760        ANGLE_TRY(ensureTextureStorageCreated(context, textures[textureUnit]));
2761    }
2762
2763    for (size_t imageUnit : executable->getActiveImagesMask())
2764    {
2765        ANGLE_TRY(
2766            ensureTextureStorageCreated(context, glState.getImageUnit(imageUnit).texture.get()));
2767    }
2768
2769    return angle::Result::Continue;
2770}
2771
2772angle::Result ContextMtl::handleDirtyDefaultAttribs(const gl::Context *context)
2773{
2774    for (size_t attribIndex : mDirtyDefaultAttribsMask)
2775    {
2776        ANGLE_TRY(updateDefaultAttribute(attribIndex));
2777    }
2778
2779    ASSERT(mRenderEncoder.valid());
2780    mRenderEncoder.setVertexData(mDefaultAttributes, mtl::kDefaultAttribsBindingIndex);
2781
2782    mDirtyDefaultAttribsMask.reset();
2783    return angle::Result::Continue;
2784}
2785
2786angle::Result ContextMtl::handleDirtyDriverUniforms(const gl::Context *context,
2787                                                    GLint drawCallFirstVertex,
2788                                                    uint32_t verticesPerInstance)
2789{
2790    mDriverUniforms.depthRange[0] = mState.getNearPlane();
2791    mDriverUniforms.depthRange[1] = mState.getFarPlane();
2792
2793    mDriverUniforms.renderArea = mDrawFramebuffer->getState().getDimensions().height << 16 |
2794                                 mDrawFramebuffer->getState().getDimensions().width;
2795
2796    const float flipX      = 1.0;
2797    const float flipY      = mDrawFramebuffer->flipY() ? -1.0f : 1.0f;
2798    mDriverUniforms.flipXY = gl::PackSnorm4x8(
2799        flipX, flipY, flipX, mState.getClipOrigin() == gl::ClipOrigin::LowerLeft ? -flipY : flipY);
2800
2801    // gl_ClipDistance
2802    const uint32_t enabledClipDistances = mState.getEnabledClipDistances().bits();
2803    ASSERT((enabledClipDistances & ~sh::vk::kDriverUniformsMiscEnabledClipPlanesMask) == 0);
2804
2805    // GL_CLIP_DEPTH_MODE_EXT
2806    const uint32_t transformDepth = !mState.isClipDepthModeZeroToOne();
2807    ASSERT((transformDepth & ~sh::vk::kDriverUniformsMiscTransformDepthMask) == 0);
2808
2809    // GL_SAMPLE_ALPHA_TO_COVERAGE
2810    const uint32_t alphaToCoverage = mState.isSampleAlphaToCoverageEnabled();
2811    ASSERT((alphaToCoverage & ~sh::vk::kDriverUniformsMiscAlphaToCoverageMask) == 0);
2812
2813    mDriverUniforms.misc =
2814        (enabledClipDistances << sh::vk::kDriverUniformsMiscEnabledClipPlanesOffset) |
2815        (transformDepth << sh::vk::kDriverUniformsMiscTransformDepthOffset) |
2816        (alphaToCoverage << sh::vk::kDriverUniformsMiscAlphaToCoverageOffset);
2817
2818    // Sample coverage mask
2819    if (mState.isSampleCoverageEnabled())
2820    {
2821        const uint32_t sampleBitCount = mDrawFramebuffer->getSamples();
2822        ASSERT(sampleBitCount < 32);
2823        const uint32_t coverageSampleBitCount =
2824            static_cast<uint32_t>(std::round(mState.getSampleCoverageValue() * sampleBitCount));
2825        uint32_t coverageMask = (1u << coverageSampleBitCount) - 1;
2826        if (mState.getSampleCoverageInvert())
2827        {
2828            const uint32_t sampleMask = (1u << sampleBitCount) - 1;
2829            coverageMask              = sampleMask & (~coverageMask);
2830        }
2831        mDriverUniforms.coverageMask = coverageMask;
2832    }
2833    else
2834    {
2835        mDriverUniforms.coverageMask = 0xFFFFFFFFu;
2836    }
2837
2838    // Sample mask
2839    if (mState.isSampleMaskEnabled())
2840    {
2841        mDriverUniforms.coverageMask &= mState.getSampleMaskWord(0);
2842    }
2843
2844    ANGLE_TRY(
2845        fillDriverXFBUniforms(drawCallFirstVertex, verticesPerInstance, /** skippedInstances */ 0));
2846
2847    ASSERT(mRenderEncoder.valid());
2848    mRenderEncoder.setFragmentData(mDriverUniforms, mtl::kDriverUniformsBindingIndex);
2849    mRenderEncoder.setVertexData(mDriverUniforms, mtl::kDriverUniformsBindingIndex);
2850
2851    return angle::Result::Continue;
2852}
2853
2854angle::Result ContextMtl::fillDriverXFBUniforms(GLint drawCallFirstVertex,
2855                                                uint32_t verticesPerInstance,
2856                                                uint32_t skippedInstances)
2857{
2858    gl::TransformFeedback *transformFeedback = getState().getCurrentTransformFeedback();
2859
2860    bool xfbActiveUnpaused = getState().isTransformFeedbackActiveUnpaused();
2861    if (!transformFeedback || !xfbActiveUnpaused)
2862    {
2863        return angle::Result::Continue;
2864    }
2865
2866    mDriverUniforms.xfbVerticesPerInstance = verticesPerInstance;
2867
2868    TransformFeedbackMtl *transformFeedbackMtl = mtl::GetImpl(transformFeedback);
2869
2870    return transformFeedbackMtl->getBufferOffsets(this, drawCallFirstVertex,
2871                                                  verticesPerInstance * skippedInstances,
2872                                                  mDriverUniforms.xfbBufferOffsets);
2873}
2874
2875angle::Result ContextMtl::handleDirtyDepthStencilState(const gl::Context *context)
2876{
2877    ASSERT(mRenderEncoder.valid());
2878
2879    // Need to handle the case when render pass doesn't have depth/stencil attachment.
2880    mtl::DepthStencilDesc dsDesc              = mDepthStencilDesc;
2881    const mtl::RenderPassDesc &renderPassDesc = mRenderEncoder.renderPassDesc();
2882
2883    if (!renderPassDesc.depthAttachment.texture)
2884    {
2885        dsDesc.depthWriteEnabled    = false;
2886        dsDesc.depthCompareFunction = MTLCompareFunctionAlways;
2887    }
2888
2889    if (!renderPassDesc.stencilAttachment.texture)
2890    {
2891        dsDesc.frontFaceStencil.reset();
2892        dsDesc.backFaceStencil.reset();
2893    }
2894
2895    // Apply depth stencil state
2896    mRenderEncoder.setDepthStencilState(
2897        getDisplay()->getStateCache().getDepthStencilState(getMetalDevice(), dsDesc));
2898
2899    return angle::Result::Continue;
2900}
2901
2902angle::Result ContextMtl::handleDirtyDepthBias(const gl::Context *context)
2903{
2904    const gl::RasterizerState &rasterState = mState.getRasterizerState();
2905    ASSERT(mRenderEncoder.valid());
2906    if (!mState.isPolygonOffsetEnabled())
2907    {
2908        mRenderEncoder.setDepthBias(0, 0, 0);
2909    }
2910    else
2911    {
2912        mRenderEncoder.setDepthBias(rasterState.polygonOffsetUnits, rasterState.polygonOffsetFactor,
2913                                    rasterState.polygonOffsetClamp);
2914    }
2915
2916    return angle::Result::Continue;
2917}
2918
2919angle::Result ContextMtl::checkIfPipelineChanged(const gl::Context *context,
2920                                                 gl::PrimitiveMode primitiveMode,
2921                                                 bool xfbPass,
2922                                                 bool *isPipelineDescChanged)
2923{
2924    ASSERT(mRenderEncoder.valid());
2925    MTLPrimitiveTopologyClass topologyClass = mtl::GetPrimitiveTopologyClass(primitiveMode);
2926
2927    bool rppChange = mDirtyBits.test(DIRTY_BIT_RENDER_PIPELINE) ||
2928                     topologyClass != mRenderPipelineDesc.inputPrimitiveTopology;
2929
2930    // Obtain RenderPipelineDesc's vertex array descriptor.
2931    ANGLE_TRY(mVertexArray->setupDraw(context, &mRenderEncoder, &rppChange,
2932                                      &mRenderPipelineDesc.vertexDescriptor));
2933
2934    if (rppChange)
2935    {
2936        const mtl::RenderPassDesc &renderPassDesc = mRenderEncoder.renderPassDesc();
2937        // Obtain RenderPipelineDesc's output descriptor.
2938        renderPassDesc.populateRenderPipelineOutputDesc(mBlendDescArray,
2939                                                        &mRenderPipelineDesc.outputDescriptor);
2940
2941        if (xfbPass)
2942        {
2943            // In XFB pass, we disable fragment shader.
2944            mRenderPipelineDesc.rasterizationType = mtl::RenderPipelineRasterization::Disabled;
2945        }
2946        else if (mState.isRasterizerDiscardEnabled())
2947        {
2948            // If XFB is not active and rasterizer discard is enabled, we need to emulate the
2949            // discard. Because in this case, vertex shader might write to stage output values and
2950            // Metal doesn't allow rasterization to be disabled.
2951            mRenderPipelineDesc.rasterizationType =
2952                mtl::RenderPipelineRasterization::EmulatedDiscard;
2953        }
2954        else
2955        {
2956            mRenderPipelineDesc.rasterizationType = mtl::RenderPipelineRasterization::Enabled;
2957        }
2958        mRenderPipelineDesc.inputPrimitiveTopology = topologyClass;
2959        mRenderPipelineDesc.alphaToCoverageEnabled =
2960            mState.isSampleAlphaToCoverageEnabled() &&
2961            mRenderPipelineDesc.outputDescriptor.rasterSampleCount > 1 &&
2962            !getDisplay()->getFeatures().emulateAlphaToCoverage.enabled;
2963
2964        mRenderPipelineDesc.outputDescriptor.updateEnabledDrawBuffers(
2965            mDrawFramebuffer->getState().getEnabledDrawBuffers());
2966    }
2967
2968    *isPipelineDescChanged = rppChange;
2969
2970    return angle::Result::Continue;
2971}
2972
2973}  // namespace rx
2974