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