• 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
14#include "common/debug.h"
15#include "libANGLE/renderer/metal/BufferMtl.h"
16#include "libANGLE/renderer/metal/CompilerMtl.h"
17#include "libANGLE/renderer/metal/DisplayMtl.h"
18#include "libANGLE/renderer/metal/FrameBufferMtl.h"
19#include "libANGLE/renderer/metal/ProgramMtl.h"
20#include "libANGLE/renderer/metal/RenderBufferMtl.h"
21#include "libANGLE/renderer/metal/ShaderMtl.h"
22#include "libANGLE/renderer/metal/TextureMtl.h"
23#include "libANGLE/renderer/metal/VertexArrayMtl.h"
24#include "libANGLE/renderer/metal/mtl_command_buffer.h"
25#include "libANGLE/renderer/metal/mtl_format_utils.h"
26#include "libANGLE/renderer/metal/mtl_utils.h"
27
28namespace rx
29{
30
31namespace
32{
33#if TARGET_OS_OSX
34// Unlimited triangle fan buffers
35constexpr uint32_t kMaxTriFanLineLoopBuffersPerFrame = 0;
36#else
37// Allow up to 10 buffers for trifan/line loop generation without stalling the GPU.
38constexpr uint32_t kMaxTriFanLineLoopBuffersPerFrame = 10;
39#endif
40
41angle::Result TriangleFanBoundCheck(ContextMtl *context, size_t numTris)
42{
43    bool indexCheck =
44        (numTris > std::numeric_limits<unsigned int>::max() / (sizeof(unsigned int) * 3));
45    ANGLE_CHECK(context, !indexCheck,
46                "Failed to create a scratch index buffer for GL_TRIANGLE_FAN, "
47                "too many indices required.",
48                GL_OUT_OF_MEMORY);
49    return angle::Result::Continue;
50}
51
52angle::Result GetTriangleFanIndicesCount(ContextMtl *context,
53                                         GLsizei vetexCount,
54                                         uint32_t *numElemsOut)
55{
56    size_t numTris = vetexCount - 2;
57    ANGLE_TRY(TriangleFanBoundCheck(context, numTris));
58    size_t numIndices = numTris * 3;
59    ANGLE_CHECK(context, numIndices <= std::numeric_limits<uint32_t>::max(),
60                "Failed to create a scratch index buffer for GL_TRIANGLE_FAN, "
61                "too many indices required.",
62                GL_OUT_OF_MEMORY);
63
64    *numElemsOut = static_cast<uint32_t>(numIndices);
65    return angle::Result::Continue;
66}
67
68angle::Result AllocateTriangleFanBufferFromPool(ContextMtl *context,
69                                                GLsizei vertexCount,
70                                                mtl::BufferPool *pool,
71                                                mtl::BufferRef *bufferOut,
72                                                uint32_t *offsetOut,
73                                                uint32_t *numElemsOut)
74{
75    uint32_t numIndices;
76    ANGLE_TRY(GetTriangleFanIndicesCount(context, vertexCount, &numIndices));
77
78    size_t offset;
79    pool->releaseInFlightBuffers(context);
80    ANGLE_TRY(pool->allocate(context, numIndices * sizeof(uint32_t), nullptr, bufferOut, &offset,
81                             nullptr));
82
83    *offsetOut   = static_cast<uint32_t>(offset);
84    *numElemsOut = numIndices;
85
86    return angle::Result::Continue;
87}
88
89bool NeedToInvertDepthRange(float near, float far)
90{
91    return near > far;
92}
93}  // namespace
94
95ContextMtl::ContextMtl(const gl::State &state, gl::ErrorSet *errorSet, DisplayMtl *display)
96    : ContextImpl(state, errorSet),
97      mtl::Context(display),
98      mCmdBuffer(&display->cmdQueue()),
99      mRenderEncoder(&mCmdBuffer),
100      mBlitEncoder(&mCmdBuffer),
101      mComputeEncoder(&mCmdBuffer)
102{}
103
104ContextMtl::~ContextMtl() {}
105
106angle::Result ContextMtl::initialize()
107{
108    mBlendDesc.reset();
109    mDepthStencilDesc.reset();
110
111    mTriFanIndexBuffer.initialize(this, 0, mtl::kIndexBufferOffsetAlignment,
112                                  kMaxTriFanLineLoopBuffersPerFrame);
113    mLineLoopIndexBuffer.initialize(this, 0, 2 * sizeof(uint32_t),
114                                    kMaxTriFanLineLoopBuffersPerFrame);
115    mLineLoopIndexBuffer.setAlwaysAllocateNewBuffer(true);
116
117    return angle::Result::Continue;
118}
119
120void ContextMtl::onDestroy(const gl::Context *context)
121{
122    mTriFanIndexBuffer.destroy(this);
123    mLineLoopIndexBuffer.destroy(this);
124
125    mIncompleteTextures.onDestroy(context);
126    mIncompleteTexturesInitialized = false;
127}
128
129angle::Result ContextMtl::ensureIncompleteTexturesCreated(const gl::Context *context)
130{
131    if (ANGLE_LIKELY(mIncompleteTexturesInitialized))
132    {
133        return angle::Result::Continue;
134    }
135    constexpr gl::TextureType supportedTextureTypes[] = {gl::TextureType::_2D,
136                                                         gl::TextureType::CubeMap};
137    for (gl::TextureType texType : supportedTextureTypes)
138    {
139        gl::Texture *texture;
140        ANGLE_UNUSED_VARIABLE(texture);
141        ANGLE_TRY(mIncompleteTextures.getIncompleteTexture(context, texType, nullptr, &texture));
142    }
143    mIncompleteTexturesInitialized = true;
144
145    return angle::Result::Continue;
146}
147
148// Flush and finish.
149angle::Result ContextMtl::flush(const gl::Context *context)
150{
151    flushCommandBufer();
152    return angle::Result::Continue;
153}
154angle::Result ContextMtl::finish(const gl::Context *context)
155{
156    ANGLE_TRY(finishCommandBuffer());
157    return angle::Result::Continue;
158}
159
160// Drawing methods.
161angle::Result ContextMtl::drawTriFanArraysWithBaseVertex(const gl::Context *context,
162                                                         GLint first,
163                                                         GLsizei count,
164                                                         GLsizei instances)
165{
166    uint32_t genIndicesCount;
167    ANGLE_TRY(GetTriangleFanIndicesCount(this, count, &genIndicesCount));
168
169    size_t indexBufferSize = genIndicesCount * sizeof(uint32_t);
170    // We can reuse the previously generated index buffer if it has more than enough indices
171    // data already.
172    if (mTriFanArraysIndexBuffer == nullptr || mTriFanArraysIndexBuffer->size() < indexBufferSize)
173    {
174        // Re-generate a new index buffer, which the first index will be zero.
175        ANGLE_TRY(
176            mtl::Buffer::MakeBuffer(this, indexBufferSize, nullptr, &mTriFanArraysIndexBuffer));
177        ANGLE_TRY(getDisplay()->getUtils().generateTriFanBufferFromArrays(
178            this, {0, static_cast<uint32_t>(count), mTriFanArraysIndexBuffer, 0}));
179    }
180
181    ANGLE_TRY(setupDraw(context, gl::PrimitiveMode::TriangleFan, first, count, instances,
182                        gl::DrawElementsType::InvalidEnum, reinterpret_cast<const void *>(0)));
183
184    // Draw with the zero starting index buffer, shift the vertex index using baseVertex instanced
185    // draw:
186    mRenderEncoder.drawIndexedInstancedBaseVertex(MTLPrimitiveTypeTriangle, genIndicesCount,
187                                                  MTLIndexTypeUInt32, mTriFanArraysIndexBuffer, 0,
188                                                  instances, first);
189
190    return angle::Result::Continue;
191}
192angle::Result ContextMtl::drawTriFanArraysLegacy(const gl::Context *context,
193                                                 GLint first,
194                                                 GLsizei count,
195                                                 GLsizei instances)
196{
197    // Legacy method is only used for GPU lacking instanced draw capabilities.
198    ASSERT(instances == 1);
199
200    mtl::BufferRef genIdxBuffer;
201    uint32_t genIdxBufferOffset;
202    uint32_t genIndicesCount;
203    ANGLE_TRY(AllocateTriangleFanBufferFromPool(this, count, &mTriFanIndexBuffer, &genIdxBuffer,
204                                                &genIdxBufferOffset, &genIndicesCount));
205    ANGLE_TRY(getDisplay()->getUtils().generateTriFanBufferFromArrays(
206        this, {static_cast<uint32_t>(first), static_cast<uint32_t>(count), genIdxBuffer,
207               genIdxBufferOffset}));
208
209    ANGLE_TRY(setupDraw(context, gl::PrimitiveMode::TriangleFan, first, count, instances,
210                        gl::DrawElementsType::InvalidEnum, reinterpret_cast<const void *>(0)));
211
212    mRenderEncoder.drawIndexed(MTLPrimitiveTypeTriangle, genIndicesCount, MTLIndexTypeUInt32,
213                               genIdxBuffer, genIdxBufferOffset);
214
215    return angle::Result::Continue;
216}
217angle::Result ContextMtl::drawTriFanArrays(const gl::Context *context,
218                                           GLint first,
219                                           GLsizei count,
220                                           GLsizei instances)
221{
222    if (count <= 3)
223    {
224        return drawArraysInstanced(context, gl::PrimitiveMode::Triangles, first, count, instances);
225    }
226    if (getDisplay()->getFeatures().hasBaseVertexInstancedDraw.enabled)
227    {
228        return drawTriFanArraysWithBaseVertex(context, first, count, instances);
229    }
230    return drawTriFanArraysLegacy(context, first, count, instances);
231}
232
233angle::Result ContextMtl::drawArraysImpl(const gl::Context *context,
234                                         gl::PrimitiveMode mode,
235                                         GLint first,
236                                         GLsizei count,
237                                         GLsizei instances)
238{
239    // Real instances count. Zero means this is not instanced draw.
240    GLsizei instanceCount = instances ? instances : 1;
241
242    if (mCullAllPolygons && gl::IsPolygonMode(mode))
243    {
244        return angle::Result::Continue;
245    }
246
247    if (mode == gl::PrimitiveMode::TriangleFan)
248    {
249        return drawTriFanArrays(context, first, count, instanceCount);
250    }
251
252    MTLPrimitiveType mtlType = mtl::GetPrimitiveType(mode);
253
254    ANGLE_TRY(setupDraw(context, mode, first, count, instances, gl::DrawElementsType::InvalidEnum,
255                        nullptr));
256
257    if (instances == 0)
258    {
259        // This method is called from normal drawArrays()
260        mRenderEncoder.draw(mtlType, first, count);
261    }
262    else
263    {
264        mRenderEncoder.drawInstanced(mtlType, first, count, instanceCount);
265    }
266
267    return angle::Result::Continue;
268}
269
270angle::Result ContextMtl::drawArrays(const gl::Context *context,
271                                     gl::PrimitiveMode mode,
272                                     GLint first,
273                                     GLsizei count)
274{
275    return drawArraysImpl(context, mode, first, count, 0);
276}
277
278angle::Result ContextMtl::drawArraysInstanced(const gl::Context *context,
279                                              gl::PrimitiveMode mode,
280                                              GLint first,
281                                              GLsizei count,
282                                              GLsizei instances)
283{
284    if (instances == 0)
285    {
286        return angle::Result::Continue;
287    }
288    return drawArraysImpl(context, mode, first, count, instances);
289}
290
291angle::Result ContextMtl::drawArraysInstancedBaseInstance(const gl::Context *context,
292                                                          gl::PrimitiveMode mode,
293                                                          GLint first,
294                                                          GLsizei count,
295                                                          GLsizei instanceCount,
296                                                          GLuint baseInstance)
297{
298    UNIMPLEMENTED();
299    return angle::Result::Stop;
300}
301
302angle::Result ContextMtl::drawTriFanElements(const gl::Context *context,
303                                             GLsizei count,
304                                             gl::DrawElementsType type,
305                                             const void *indices,
306                                             GLsizei instances)
307{
308    if (count > 3)
309    {
310        mtl::BufferRef genIdxBuffer;
311        uint32_t genIdxBufferOffset;
312        uint32_t genIndicesCount;
313        ANGLE_TRY(AllocateTriangleFanBufferFromPool(this, count, &mTriFanIndexBuffer, &genIdxBuffer,
314                                                    &genIdxBufferOffset, &genIndicesCount));
315
316        ANGLE_TRY(getDisplay()->getUtils().generateTriFanBufferFromElementsArray(
317            this, {type, count, indices, genIdxBuffer, genIdxBufferOffset}));
318
319        ANGLE_TRY(mTriFanIndexBuffer.commit(this));
320
321        ANGLE_TRY(
322            setupDraw(context, gl::PrimitiveMode::TriangleFan, 0, count, instances, type, indices));
323
324        mRenderEncoder.drawIndexedInstanced(MTLPrimitiveTypeTriangle, genIndicesCount,
325                                            MTLIndexTypeUInt32, genIdxBuffer, genIdxBufferOffset,
326                                            instances);
327
328        return angle::Result::Continue;
329    }  // if (count > 3)
330    return drawElementsInstanced(context, gl::PrimitiveMode::Triangles, count, type, indices,
331                                 instances);
332}
333
334angle::Result ContextMtl::drawElementsImpl(const gl::Context *context,
335                                           gl::PrimitiveMode mode,
336                                           GLsizei count,
337                                           gl::DrawElementsType type,
338                                           const void *indices,
339                                           GLsizei instances)
340{
341    // Real instances count. Zero means this is not instanced draw.
342    GLsizei instanceCount = instances ? instances : 1;
343
344    if (mCullAllPolygons && gl::IsPolygonMode(mode))
345    {
346        return angle::Result::Continue;
347    }
348
349    if (mode == gl::PrimitiveMode::TriangleFan)
350    {
351        return drawTriFanElements(context, count, type, indices, instanceCount);
352    }
353
354    mtl::BufferRef idxBuffer;
355    size_t convertedOffset             = 0;
356    gl::DrawElementsType convertedType = type;
357
358    ANGLE_TRY(mVertexArray->getIndexBuffer(context, type, count, indices, &idxBuffer,
359                                           &convertedOffset, &convertedType));
360
361    ASSERT(idxBuffer);
362    ASSERT((convertedOffset % mtl::kIndexBufferOffsetAlignment) == 0);
363
364    ANGLE_TRY(setupDraw(context, mode, 0, count, instances, type, indices));
365
366    MTLPrimitiveType mtlType = mtl::GetPrimitiveType(mode);
367
368    MTLIndexType mtlIdxType = mtl::GetIndexType(convertedType);
369
370    if (instances == 0)
371    {
372        // Normal draw
373        mRenderEncoder.drawIndexed(mtlType, count, mtlIdxType, idxBuffer, convertedOffset);
374    }
375    else
376    {
377        // Instanced draw
378        mRenderEncoder.drawIndexedInstanced(mtlType, count, mtlIdxType, idxBuffer, convertedOffset,
379                                            instanceCount);
380    }
381
382    return angle::Result::Continue;
383}
384
385angle::Result ContextMtl::drawElements(const gl::Context *context,
386                                       gl::PrimitiveMode mode,
387                                       GLsizei count,
388                                       gl::DrawElementsType type,
389                                       const void *indices)
390{
391    return drawElementsImpl(context, mode, count, type, indices, 0);
392}
393
394angle::Result ContextMtl::drawElementsBaseVertex(const gl::Context *context,
395                                                 gl::PrimitiveMode mode,
396                                                 GLsizei count,
397                                                 gl::DrawElementsType type,
398                                                 const void *indices,
399                                                 GLint baseVertex)
400{
401    // NOTE(hqle): ES 3.2
402    UNIMPLEMENTED();
403    return angle::Result::Stop;
404}
405
406angle::Result ContextMtl::drawElementsInstanced(const gl::Context *context,
407                                                gl::PrimitiveMode mode,
408                                                GLsizei count,
409                                                gl::DrawElementsType type,
410                                                const void *indices,
411                                                GLsizei instanceCount)
412{
413    if (instanceCount == 0)
414    {
415        return angle::Result::Continue;
416    }
417    return drawElementsImpl(context, mode, count, type, indices, instanceCount);
418}
419
420angle::Result ContextMtl::drawElementsInstancedBaseVertex(const gl::Context *context,
421                                                          gl::PrimitiveMode mode,
422                                                          GLsizei count,
423                                                          gl::DrawElementsType type,
424                                                          const void *indices,
425                                                          GLsizei instanceCount,
426                                                          GLint baseVertex)
427{
428    // NOTE(hqle): ES 3.2
429    UNIMPLEMENTED();
430    return angle::Result::Stop;
431}
432
433angle::Result ContextMtl::drawElementsInstancedBaseVertexBaseInstance(const gl::Context *context,
434                                                                      gl::PrimitiveMode mode,
435                                                                      GLsizei count,
436                                                                      gl::DrawElementsType type,
437                                                                      const void *indices,
438                                                                      GLsizei instances,
439                                                                      GLint baseVertex,
440                                                                      GLuint baseInstance)
441{
442    UNIMPLEMENTED();
443    return angle::Result::Stop;
444}
445
446angle::Result ContextMtl::drawRangeElements(const gl::Context *context,
447                                            gl::PrimitiveMode mode,
448                                            GLuint start,
449                                            GLuint end,
450                                            GLsizei count,
451                                            gl::DrawElementsType type,
452                                            const void *indices)
453{
454    // NOTE(hqle): ES 3.0
455    UNIMPLEMENTED();
456    return angle::Result::Stop;
457}
458
459angle::Result ContextMtl::drawRangeElementsBaseVertex(const gl::Context *context,
460                                                      gl::PrimitiveMode mode,
461                                                      GLuint start,
462                                                      GLuint end,
463                                                      GLsizei count,
464                                                      gl::DrawElementsType type,
465                                                      const void *indices,
466                                                      GLint baseVertex)
467{
468    // NOTE(hqle): ES 3.2
469    UNIMPLEMENTED();
470    return angle::Result::Stop;
471}
472
473angle::Result ContextMtl::drawArraysIndirect(const gl::Context *context,
474                                             gl::PrimitiveMode mode,
475                                             const void *indirect)
476{
477    // NOTE(hqle): ES 3.0
478    UNIMPLEMENTED();
479    return angle::Result::Stop;
480}
481angle::Result ContextMtl::drawElementsIndirect(const gl::Context *context,
482                                               gl::PrimitiveMode mode,
483                                               gl::DrawElementsType type,
484                                               const void *indirect)
485{
486    // NOTE(hqle): ES 3.0
487    UNIMPLEMENTED();
488    return angle::Result::Stop;
489}
490
491// Device loss
492gl::GraphicsResetStatus ContextMtl::getResetStatus()
493{
494    return gl::GraphicsResetStatus::NoError;
495}
496
497// Vendor and description strings.
498std::string ContextMtl::getVendorString() const
499{
500    return getDisplay()->getVendorString();
501}
502std::string ContextMtl::getRendererDescription() const
503{
504    return getDisplay()->getRendererDescription();
505}
506
507// EXT_debug_marker
508angle::Result ContextMtl::insertEventMarker(GLsizei length, const char *marker)
509{
510    return angle::Result::Continue;
511}
512
513angle::Result ContextMtl::pushGroupMarker(GLsizei length, const char *marker)
514{
515    return angle::Result::Continue;
516}
517
518angle::Result ContextMtl::popGroupMarker()
519{
520    return angle::Result::Continue;
521}
522
523// KHR_debug
524angle::Result ContextMtl::pushDebugGroup(const gl::Context *context,
525                                         GLenum source,
526                                         GLuint id,
527                                         const std::string &message)
528{
529    return angle::Result::Continue;
530}
531
532angle::Result ContextMtl::popDebugGroup(const gl::Context *context)
533{
534    return angle::Result::Continue;
535}
536
537// State sync with dirty bits.
538angle::Result ContextMtl::syncState(const gl::Context *context,
539                                    const gl::State::DirtyBits &dirtyBits,
540                                    const gl::State::DirtyBits &bitMask)
541{
542    const gl::State &glState = context->getState();
543
544    // Initialize incomplete texture set.
545    ANGLE_TRY(ensureIncompleteTexturesCreated(context));
546
547    for (size_t dirtyBit : dirtyBits)
548    {
549        switch (dirtyBit)
550        {
551            case gl::State::DIRTY_BIT_SCISSOR_TEST_ENABLED:
552            case gl::State::DIRTY_BIT_SCISSOR:
553                updateScissor(glState);
554                break;
555            case gl::State::DIRTY_BIT_VIEWPORT:
556            {
557                FramebufferMtl *framebufferMtl = mtl::GetImpl(glState.getDrawFramebuffer());
558                updateViewport(framebufferMtl, glState.getViewport(), glState.getNearPlane(),
559                               glState.getFarPlane());
560                // Update the scissor, which will be constrained to the viewport
561                updateScissor(glState);
562                break;
563            }
564            case gl::State::DIRTY_BIT_DEPTH_RANGE:
565                updateDepthRange(glState.getNearPlane(), glState.getFarPlane());
566                break;
567            case gl::State::DIRTY_BIT_BLEND_COLOR:
568                mDirtyBits.set(DIRTY_BIT_BLEND_COLOR);
569                break;
570            case gl::State::DIRTY_BIT_BLEND_ENABLED:
571                mBlendDesc.updateBlendEnabled(glState.getBlendState());
572                invalidateRenderPipeline();
573                break;
574            case gl::State::DIRTY_BIT_BLEND_FUNCS:
575                mBlendDesc.updateBlendFactors(glState.getBlendState());
576                invalidateRenderPipeline();
577                break;
578            case gl::State::DIRTY_BIT_BLEND_EQUATIONS:
579                mBlendDesc.updateBlendOps(glState.getBlendState());
580                invalidateRenderPipeline();
581                break;
582            case gl::State::DIRTY_BIT_COLOR_MASK:
583                mBlendDesc.updateWriteMask(glState.getBlendState());
584                invalidateRenderPipeline();
585                break;
586            case gl::State::DIRTY_BIT_SAMPLE_ALPHA_TO_COVERAGE_ENABLED:
587                // NOTE(hqle): MSAA support
588                break;
589            case gl::State::DIRTY_BIT_SAMPLE_COVERAGE_ENABLED:
590                // NOTE(hqle): MSAA support
591                break;
592            case gl::State::DIRTY_BIT_SAMPLE_COVERAGE:
593                // NOTE(hqle): MSAA support
594                break;
595            case gl::State::DIRTY_BIT_SAMPLE_MASK_ENABLED:
596                // NOTE(hqle): MSAA support
597                break;
598            case gl::State::DIRTY_BIT_SAMPLE_MASK:
599                // NOTE(hqle): MSAA support
600                break;
601            case gl::State::DIRTY_BIT_DEPTH_TEST_ENABLED:
602                mDepthStencilDesc.updateDepthTestEnabled(glState.getDepthStencilState());
603                mDirtyBits.set(DIRTY_BIT_DEPTH_STENCIL_DESC);
604                break;
605            case gl::State::DIRTY_BIT_DEPTH_FUNC:
606                mDepthStencilDesc.updateDepthCompareFunc(glState.getDepthStencilState());
607                mDirtyBits.set(DIRTY_BIT_DEPTH_STENCIL_DESC);
608                break;
609            case gl::State::DIRTY_BIT_DEPTH_MASK:
610                mDepthStencilDesc.updateDepthWriteEnabled(glState.getDepthStencilState());
611                mDirtyBits.set(DIRTY_BIT_DEPTH_STENCIL_DESC);
612                break;
613            case gl::State::DIRTY_BIT_STENCIL_TEST_ENABLED:
614                mDepthStencilDesc.updateStencilTestEnabled(glState.getDepthStencilState());
615                mDirtyBits.set(DIRTY_BIT_DEPTH_STENCIL_DESC);
616                break;
617            case gl::State::DIRTY_BIT_STENCIL_FUNCS_FRONT:
618                mDepthStencilDesc.updateStencilFrontFuncs(glState.getDepthStencilState());
619                mStencilRefFront =
620                    gl::clamp<int, int, int>(glState.getStencilRef(), 0, mtl::kStencilMaskAll);
621                mDirtyBits.set(DIRTY_BIT_DEPTH_STENCIL_DESC);
622                mDirtyBits.set(DIRTY_BIT_STENCIL_REF);
623                break;
624            case gl::State::DIRTY_BIT_STENCIL_FUNCS_BACK:
625                mDepthStencilDesc.updateStencilBackFuncs(glState.getDepthStencilState());
626                mStencilRefBack =
627                    gl::clamp<int, int, int>(glState.getStencilBackRef(), 0, mtl::kStencilMaskAll);
628                mDirtyBits.set(DIRTY_BIT_DEPTH_STENCIL_DESC);
629                mDirtyBits.set(DIRTY_BIT_STENCIL_REF);
630                break;
631            case gl::State::DIRTY_BIT_STENCIL_OPS_FRONT:
632                mDepthStencilDesc.updateStencilFrontOps(glState.getDepthStencilState());
633                mDirtyBits.set(DIRTY_BIT_DEPTH_STENCIL_DESC);
634                break;
635            case gl::State::DIRTY_BIT_STENCIL_OPS_BACK:
636                mDepthStencilDesc.updateStencilBackOps(glState.getDepthStencilState());
637                mDirtyBits.set(DIRTY_BIT_DEPTH_STENCIL_DESC);
638                break;
639            case gl::State::DIRTY_BIT_STENCIL_WRITEMASK_FRONT:
640                mDepthStencilDesc.updateStencilFrontWriteMask(glState.getDepthStencilState());
641                mDirtyBits.set(DIRTY_BIT_DEPTH_STENCIL_DESC);
642                break;
643            case gl::State::DIRTY_BIT_STENCIL_WRITEMASK_BACK:
644                mDepthStencilDesc.updateStencilBackWriteMask(glState.getDepthStencilState());
645                mDirtyBits.set(DIRTY_BIT_DEPTH_STENCIL_DESC);
646                break;
647            case gl::State::DIRTY_BIT_CULL_FACE_ENABLED:
648            case gl::State::DIRTY_BIT_CULL_FACE:
649                updateCullMode(glState);
650                break;
651            case gl::State::DIRTY_BIT_FRONT_FACE:
652                updateFrontFace(glState);
653                break;
654            case gl::State::DIRTY_BIT_POLYGON_OFFSET_FILL_ENABLED:
655            case gl::State::DIRTY_BIT_POLYGON_OFFSET:
656                updateDepthBias(glState);
657                break;
658            case gl::State::DIRTY_BIT_RASTERIZER_DISCARD_ENABLED:
659                // NOTE(hqle): ES 3.0 feature.
660                break;
661            case gl::State::DIRTY_BIT_LINE_WIDTH:
662                // Do nothing
663                break;
664            case gl::State::DIRTY_BIT_PRIMITIVE_RESTART_ENABLED:
665                // NOTE(hqle): ES 3.0 feature.
666                break;
667            case gl::State::DIRTY_BIT_CLEAR_COLOR:
668                mClearColor.red   = glState.getColorClearValue().red;
669                mClearColor.green = glState.getColorClearValue().green;
670                mClearColor.blue  = glState.getColorClearValue().blue;
671                mClearColor.alpha = glState.getColorClearValue().alpha;
672                break;
673            case gl::State::DIRTY_BIT_CLEAR_DEPTH:
674                break;
675            case gl::State::DIRTY_BIT_CLEAR_STENCIL:
676                mClearStencil = glState.getStencilClearValue() & mtl::kStencilMaskAll;
677                break;
678            case gl::State::DIRTY_BIT_UNPACK_STATE:
679                // This is a no-op, its only important to use the right unpack state when we do
680                // setImage or setSubImage in TextureMtl, which is plumbed through the frontend call
681                break;
682            case gl::State::DIRTY_BIT_UNPACK_BUFFER_BINDING:
683                break;
684            case gl::State::DIRTY_BIT_PACK_STATE:
685                // This is a no-op, its only important to use the right pack state when we do
686                // call readPixels later on.
687                break;
688            case gl::State::DIRTY_BIT_PACK_BUFFER_BINDING:
689                break;
690            case gl::State::DIRTY_BIT_DITHER_ENABLED:
691                break;
692            case gl::State::DIRTY_BIT_READ_FRAMEBUFFER_BINDING:
693                break;
694            case gl::State::DIRTY_BIT_DRAW_FRAMEBUFFER_BINDING:
695                updateDrawFrameBufferBinding(context);
696                break;
697            case gl::State::DIRTY_BIT_RENDERBUFFER_BINDING:
698                break;
699            case gl::State::DIRTY_BIT_VERTEX_ARRAY_BINDING:
700                updateVertexArray(context);
701                break;
702            case gl::State::DIRTY_BIT_DRAW_INDIRECT_BUFFER_BINDING:
703                break;
704            case gl::State::DIRTY_BIT_DISPATCH_INDIRECT_BUFFER_BINDING:
705                break;
706            case gl::State::DIRTY_BIT_PROGRAM_BINDING:
707                mProgram = mtl::GetImpl(glState.getProgram());
708                break;
709            case gl::State::DIRTY_BIT_PROGRAM_EXECUTABLE:
710                updateProgramExecutable(context);
711                break;
712            case gl::State::DIRTY_BIT_TEXTURE_BINDINGS:
713                invalidateCurrentTextures();
714                break;
715            case gl::State::DIRTY_BIT_SAMPLER_BINDINGS:
716                invalidateCurrentTextures();
717                break;
718            case gl::State::DIRTY_BIT_TRANSFORM_FEEDBACK_BINDING:
719                // Nothing to do.
720                break;
721            case gl::State::DIRTY_BIT_SHADER_STORAGE_BUFFER_BINDING:
722                // NOTE(hqle): ES 3.0 feature.
723                break;
724            case gl::State::DIRTY_BIT_UNIFORM_BUFFER_BINDINGS:
725                // NOTE(hqle): ES 3.0 feature.
726                break;
727            case gl::State::DIRTY_BIT_ATOMIC_COUNTER_BUFFER_BINDING:
728                break;
729            case gl::State::DIRTY_BIT_IMAGE_BINDINGS:
730                // NOTE(hqle): properly handle GLSL images.
731                invalidateCurrentTextures();
732                break;
733            case gl::State::DIRTY_BIT_MULTISAMPLING:
734                // NOTE(hqle): MSAA feature.
735                break;
736            case gl::State::DIRTY_BIT_SAMPLE_ALPHA_TO_ONE:
737                // NOTE(hqle): this is part of EXT_multisample_compatibility.
738                // NOTE(hqle): MSAA feature.
739                break;
740            case gl::State::DIRTY_BIT_COVERAGE_MODULATION:
741                break;
742            case gl::State::DIRTY_BIT_FRAMEBUFFER_SRGB:
743                break;
744            case gl::State::DIRTY_BIT_CURRENT_VALUES:
745            {
746                invalidateDefaultAttributes(glState.getAndResetDirtyCurrentValues());
747                break;
748            }
749            case gl::State::DIRTY_BIT_PROVOKING_VERTEX:
750                break;
751            case gl::State::DIRTY_BIT_EXTENDED:
752                updateExtendedState(glState);
753                break;
754            default:
755                UNREACHABLE();
756                break;
757        }
758    }
759
760    return angle::Result::Continue;
761}
762
763void ContextMtl::updateExtendedState(const gl::State &glState)
764{
765    // Handling clip distance enabled flags, mipmap generation hint & shader derivative
766    // hint.
767    invalidateDriverUniforms();
768}
769
770// Disjoint timer queries
771GLint ContextMtl::getGPUDisjoint()
772{
773    UNIMPLEMENTED();
774    return 0;
775}
776GLint64 ContextMtl::getTimestamp()
777{
778    UNIMPLEMENTED();
779    return 0;
780}
781
782// Context switching
783angle::Result ContextMtl::onMakeCurrent(const gl::Context *context)
784{
785    invalidateState(context);
786    return angle::Result::Continue;
787}
788angle::Result ContextMtl::onUnMakeCurrent(const gl::Context *context)
789{
790    flushCommandBufer();
791    return angle::Result::Continue;
792}
793
794// Native capabilities, unmodified by gl::Context.
795gl::Caps ContextMtl::getNativeCaps() const
796{
797    return getDisplay()->getNativeCaps();
798}
799const gl::TextureCapsMap &ContextMtl::getNativeTextureCaps() const
800{
801    return getDisplay()->getNativeTextureCaps();
802}
803const gl::Extensions &ContextMtl::getNativeExtensions() const
804{
805    return getDisplay()->getNativeExtensions();
806}
807const gl::Limitations &ContextMtl::getNativeLimitations() const
808{
809    return getDisplay()->getNativeLimitations();
810}
811
812// Shader creation
813CompilerImpl *ContextMtl::createCompiler()
814{
815    return new CompilerMtl();
816}
817ShaderImpl *ContextMtl::createShader(const gl::ShaderState &state)
818{
819    return new ShaderMtl(state);
820}
821ProgramImpl *ContextMtl::createProgram(const gl::ProgramState &state)
822{
823    return new ProgramMtl(state);
824}
825
826// Framebuffer creation
827FramebufferImpl *ContextMtl::createFramebuffer(const gl::FramebufferState &state)
828{
829    return new FramebufferMtl(state, false);
830}
831
832// Texture creation
833TextureImpl *ContextMtl::createTexture(const gl::TextureState &state)
834{
835    return new TextureMtl(state);
836}
837
838// Renderbuffer creation
839RenderbufferImpl *ContextMtl::createRenderbuffer(const gl::RenderbufferState &state)
840{
841    return new RenderbufferMtl(state);
842}
843
844// Buffer creation
845BufferImpl *ContextMtl::createBuffer(const gl::BufferState &state)
846{
847    return new BufferMtl(state);
848}
849
850// Vertex Array creation
851VertexArrayImpl *ContextMtl::createVertexArray(const gl::VertexArrayState &state)
852{
853    return new VertexArrayMtl(state, this);
854}
855
856// Query and Fence creation
857QueryImpl *ContextMtl::createQuery(gl::QueryType type)
858{
859    // NOTE(hqle): ES 3.0
860    UNIMPLEMENTED();
861    return nullptr;
862}
863FenceNVImpl *ContextMtl::createFenceNV()
864{
865    UNIMPLEMENTED();
866    return nullptr;
867}
868SyncImpl *ContextMtl::createSync()
869{
870    UNIMPLEMENTED();
871    return nullptr;
872}
873
874// Transform Feedback creation
875TransformFeedbackImpl *ContextMtl::createTransformFeedback(const gl::TransformFeedbackState &state)
876{
877    // NOTE(hqle): ES 3.0
878    UNIMPLEMENTED();
879    return nullptr;
880}
881
882// Sampler object creation
883SamplerImpl *ContextMtl::createSampler(const gl::SamplerState &state)
884{
885    // NOTE(hqle): ES 3.0
886    UNIMPLEMENTED();
887    return nullptr;
888}
889
890// Program Pipeline object creation
891ProgramPipelineImpl *ContextMtl::createProgramPipeline(const gl::ProgramPipelineState &data)
892{
893    // NOTE(hqle): ES 3.0
894    UNIMPLEMENTED();
895    return nullptr;
896}
897
898// Memory object creation.
899MemoryObjectImpl *ContextMtl::createMemoryObject()
900{
901    UNIMPLEMENTED();
902    return nullptr;
903}
904
905// Semaphore creation.
906SemaphoreImpl *ContextMtl::createSemaphore()
907{
908    UNIMPLEMENTED();
909    return nullptr;
910}
911
912OverlayImpl *ContextMtl::createOverlay(const gl::OverlayState &state)
913{
914    UNIMPLEMENTED();
915    return nullptr;
916}
917
918angle::Result ContextMtl::dispatchCompute(const gl::Context *context,
919                                          GLuint numGroupsX,
920                                          GLuint numGroupsY,
921                                          GLuint numGroupsZ)
922{
923    // NOTE(hqle): ES 3.0
924    UNIMPLEMENTED();
925    return angle::Result::Stop;
926}
927angle::Result ContextMtl::dispatchComputeIndirect(const gl::Context *context, GLintptr indirect)
928{
929    // NOTE(hqle): ES 3.0
930    UNIMPLEMENTED();
931    return angle::Result::Stop;
932}
933
934angle::Result ContextMtl::memoryBarrier(const gl::Context *context, GLbitfield barriers)
935{
936    // NOTE(hqle): ES 3.0
937    UNIMPLEMENTED();
938    return angle::Result::Stop;
939}
940angle::Result ContextMtl::memoryBarrierByRegion(const gl::Context *context, GLbitfield barriers)
941{
942    // NOTE(hqle): ES 3.0
943    UNIMPLEMENTED();
944    return angle::Result::Stop;
945}
946
947// override mtl::ErrorHandler
948void ContextMtl::handleError(GLenum glErrorCode,
949                             const char *file,
950                             const char *function,
951                             unsigned int line)
952{
953    std::stringstream errorStream;
954    errorStream << "Metal backend encountered an error. Code=" << glErrorCode << ".";
955
956    mErrors->handleError(glErrorCode, errorStream.str().c_str(), file, function, line);
957}
958
959void ContextMtl::handleError(NSError *nserror,
960                             const char *file,
961                             const char *function,
962                             unsigned int line)
963{
964    if (!nserror)
965    {
966        return;
967    }
968
969    std::stringstream errorStream;
970    errorStream << "Metal backend encountered an error: \n"
971                << nserror.localizedDescription.UTF8String;
972
973    mErrors->handleError(GL_INVALID_OPERATION, errorStream.str().c_str(), file, function, line);
974}
975
976void ContextMtl::invalidateState(const gl::Context *context)
977{
978    mDirtyBits.set();
979
980    invalidateDefaultAttributes(context->getStateCache().getActiveDefaultAttribsMask());
981}
982
983void ContextMtl::invalidateDefaultAttribute(size_t attribIndex)
984{
985    mDirtyDefaultAttribsMask.set(attribIndex);
986    mDirtyBits.set(DIRTY_BIT_DEFAULT_ATTRIBS);
987}
988
989void ContextMtl::invalidateDefaultAttributes(const gl::AttributesMask &dirtyMask)
990{
991    if (dirtyMask.any())
992    {
993        mDirtyDefaultAttribsMask |= dirtyMask;
994        mDirtyBits.set(DIRTY_BIT_DEFAULT_ATTRIBS);
995    }
996}
997
998void ContextMtl::invalidateCurrentTextures()
999{
1000    mDirtyBits.set(DIRTY_BIT_TEXTURES);
1001}
1002
1003void ContextMtl::invalidateDriverUniforms()
1004{
1005    mDirtyBits.set(DIRTY_BIT_DRIVER_UNIFORMS);
1006}
1007
1008void ContextMtl::invalidateRenderPipeline()
1009{
1010    mDirtyBits.set(DIRTY_BIT_RENDER_PIPELINE);
1011}
1012
1013const MTLClearColor &ContextMtl::getClearColorValue() const
1014{
1015    return mClearColor;
1016}
1017MTLColorWriteMask ContextMtl::getColorMask() const
1018{
1019    return mBlendDesc.writeMask;
1020}
1021float ContextMtl::getClearDepthValue() const
1022{
1023    return getState().getDepthClearValue();
1024}
1025uint32_t ContextMtl::getClearStencilValue() const
1026{
1027    return mClearStencil;
1028}
1029uint32_t ContextMtl::getStencilMask() const
1030{
1031    return getState().getDepthStencilState().stencilWritemask & mtl::kStencilMaskAll;
1032}
1033
1034bool ContextMtl::getDepthMask() const
1035{
1036    return getState().getDepthStencilState().depthMask;
1037}
1038
1039const mtl::Format &ContextMtl::getPixelFormat(angle::FormatID angleFormatId) const
1040{
1041    return getDisplay()->getPixelFormat(angleFormatId);
1042}
1043
1044// See mtl::FormatTable::getVertexFormat()
1045const mtl::VertexFormat &ContextMtl::getVertexFormat(angle::FormatID angleFormatId,
1046                                                     bool tightlyPacked) const
1047{
1048    return getDisplay()->getVertexFormat(angleFormatId, tightlyPacked);
1049}
1050
1051angle::Result ContextMtl::getIncompleteTexture(const gl::Context *context,
1052                                               gl::TextureType type,
1053                                               gl::Texture **textureOut)
1054{
1055    return mIncompleteTextures.getIncompleteTexture(context, type, nullptr, textureOut);
1056}
1057
1058void ContextMtl::endEncoding(mtl::RenderCommandEncoder *encoder)
1059{
1060    encoder->endEncoding();
1061}
1062
1063void ContextMtl::endEncoding(bool forceSaveRenderPassContent)
1064{
1065    if (mRenderEncoder.valid())
1066    {
1067        if (forceSaveRenderPassContent)
1068        {
1069            // Save the work in progress.
1070            mRenderEncoder.setColorStoreAction(MTLStoreActionStore);
1071            mRenderEncoder.setDepthStencilStoreAction(MTLStoreActionStore, MTLStoreActionStore);
1072        }
1073
1074        mRenderEncoder.endEncoding();
1075    }
1076
1077    if (mBlitEncoder.valid())
1078    {
1079        mBlitEncoder.endEncoding();
1080    }
1081
1082    if (mComputeEncoder.valid())
1083    {
1084        mComputeEncoder.endEncoding();
1085    }
1086}
1087
1088void ContextMtl::flushCommandBufer()
1089{
1090    if (!mCmdBuffer.valid())
1091    {
1092        return;
1093    }
1094
1095    endEncoding(true);
1096    mCmdBuffer.commit();
1097}
1098
1099void ContextMtl::present(const gl::Context *context, id<CAMetalDrawable> presentationDrawable)
1100{
1101    ensureCommandBufferValid();
1102
1103    // Always discard default FBO's depth stencil buffers at the end of the frame:
1104    if (mDrawFramebufferIsDefault && hasStartedRenderPass(mDrawFramebuffer))
1105    {
1106        constexpr GLenum dsAttachments[] = {GL_DEPTH, GL_STENCIL};
1107        (void)mDrawFramebuffer->invalidate(context, 2, dsAttachments);
1108
1109        endEncoding(false);
1110
1111        // Reset discard flag by notify framebuffer that a new render pass has started.
1112        mDrawFramebuffer->onStartedDrawingToFrameBuffer(context);
1113    }
1114
1115    endEncoding(false);
1116    mCmdBuffer.present(presentationDrawable);
1117    mCmdBuffer.commit();
1118}
1119
1120angle::Result ContextMtl::finishCommandBuffer()
1121{
1122    flushCommandBufer();
1123
1124    if (mCmdBuffer.valid())
1125    {
1126        mCmdBuffer.finish();
1127    }
1128
1129    return angle::Result::Continue;
1130}
1131
1132bool ContextMtl::hasStartedRenderPass(const mtl::RenderPassDesc &desc)
1133{
1134    return mRenderEncoder.valid() &&
1135           mRenderEncoder.renderPassDesc().equalIgnoreLoadStoreOptions(desc);
1136}
1137
1138bool ContextMtl::hasStartedRenderPass(FramebufferMtl *framebuffer)
1139{
1140    return framebuffer && hasStartedRenderPass(framebuffer->getRenderPassDesc(this));
1141}
1142
1143// Get current render encoder
1144mtl::RenderCommandEncoder *ContextMtl::getRenderCommandEncoder()
1145{
1146    if (!mRenderEncoder.valid())
1147    {
1148        return nullptr;
1149    }
1150
1151    return &mRenderEncoder;
1152}
1153
1154mtl::RenderCommandEncoder *ContextMtl::getCurrentFramebufferRenderCommandEncoder()
1155{
1156    if (!mDrawFramebuffer)
1157    {
1158        return nullptr;
1159    }
1160
1161    return getRenderCommandEncoder(mDrawFramebuffer->getRenderPassDesc(this));
1162}
1163
1164mtl::RenderCommandEncoder *ContextMtl::getRenderCommandEncoder(const mtl::RenderPassDesc &desc)
1165{
1166    if (hasStartedRenderPass(desc))
1167    {
1168        return &mRenderEncoder;
1169    }
1170
1171    endEncoding(false);
1172
1173    ensureCommandBufferValid();
1174
1175    // Need to re-apply everything on next draw call.
1176    mDirtyBits.set();
1177
1178    return &mRenderEncoder.restart(desc);
1179}
1180
1181// Utilities to quickly create render command enconder to a specific texture:
1182// The previous content of texture will be loaded if clearColor is not provided
1183mtl::RenderCommandEncoder *ContextMtl::getRenderCommandEncoder(
1184    const mtl::TextureRef &textureTarget,
1185    const gl::ImageIndex &index,
1186    const Optional<MTLClearColor> &clearColor)
1187{
1188    ASSERT(textureTarget && textureTarget->valid());
1189
1190    mtl::RenderPassDesc rpDesc;
1191
1192    rpDesc.colorAttachments[0].texture = textureTarget;
1193    rpDesc.colorAttachments[0].level   = index.getLevelIndex();
1194    rpDesc.colorAttachments[0].slice   = index.hasLayer() ? index.getLayerIndex() : 0;
1195    rpDesc.numColorAttachments         = 1;
1196
1197    if (clearColor.valid())
1198    {
1199        rpDesc.colorAttachments[0].loadAction = MTLLoadActionClear;
1200        rpDesc.colorAttachments[0].clearColor =
1201            mtl::EmulatedAlphaClearColor(clearColor.value(), textureTarget->getColorWritableMask());
1202    }
1203
1204    return getRenderCommandEncoder(rpDesc);
1205}
1206// The previous content of texture will be loaded
1207mtl::RenderCommandEncoder *ContextMtl::getRenderCommandEncoder(const mtl::TextureRef &textureTarget,
1208                                                               const gl::ImageIndex &index)
1209{
1210    return getRenderCommandEncoder(textureTarget, index, Optional<MTLClearColor>());
1211}
1212
1213mtl::BlitCommandEncoder *ContextMtl::getBlitCommandEncoder()
1214{
1215    if (mBlitEncoder.valid())
1216    {
1217        return &mBlitEncoder;
1218    }
1219
1220    endEncoding(true);
1221
1222    ensureCommandBufferValid();
1223
1224    return &mBlitEncoder.restart();
1225}
1226
1227mtl::ComputeCommandEncoder *ContextMtl::getComputeCommandEncoder()
1228{
1229    if (mComputeEncoder.valid())
1230    {
1231        return &mComputeEncoder;
1232    }
1233
1234    endEncoding(true);
1235
1236    ensureCommandBufferValid();
1237
1238    return &mComputeEncoder.restart();
1239}
1240
1241void ContextMtl::ensureCommandBufferValid()
1242{
1243    if (!mCmdBuffer.valid())
1244    {
1245        mCmdBuffer.restart();
1246    }
1247
1248    ASSERT(mCmdBuffer.valid());
1249}
1250
1251void ContextMtl::updateViewport(FramebufferMtl *framebufferMtl,
1252                                const gl::Rectangle &viewport,
1253                                float nearPlane,
1254                                float farPlane)
1255{
1256    mViewport = mtl::GetViewport(viewport, framebufferMtl->getState().getDimensions().height,
1257                                 framebufferMtl->flipY(), nearPlane, farPlane);
1258    mDirtyBits.set(DIRTY_BIT_VIEWPORT);
1259
1260    invalidateDriverUniforms();
1261}
1262
1263void ContextMtl::updateDepthRange(float nearPlane, float farPlane)
1264{
1265    if (NeedToInvertDepthRange(nearPlane, farPlane))
1266    {
1267        // We also need to invert the depth in shader later by using scale value stored in driver
1268        // uniform depthRange.reserved
1269        std::swap(nearPlane, farPlane);
1270    }
1271    mViewport.znear = nearPlane;
1272    mViewport.zfar  = farPlane;
1273    mDirtyBits.set(DIRTY_BIT_VIEWPORT);
1274
1275    invalidateDriverUniforms();
1276}
1277
1278void ContextMtl::updateScissor(const gl::State &glState)
1279{
1280    FramebufferMtl *framebufferMtl = mtl::GetImpl(glState.getDrawFramebuffer());
1281    gl::Rectangle renderArea       = framebufferMtl->getCompleteRenderArea();
1282
1283    ANGLE_MTL_LOG("renderArea = %d,%d,%d,%d", renderArea.x, renderArea.y, renderArea.width,
1284                  renderArea.height);
1285
1286    // Clip the render area to the viewport.
1287    gl::Rectangle viewportClippedRenderArea;
1288    gl::ClipRectangle(renderArea, glState.getViewport(), &viewportClippedRenderArea);
1289
1290    gl::Rectangle scissoredArea = ClipRectToScissor(getState(), viewportClippedRenderArea, false);
1291    if (framebufferMtl->flipY())
1292    {
1293        scissoredArea.y = renderArea.height - scissoredArea.y - scissoredArea.height;
1294    }
1295
1296    ANGLE_MTL_LOG("scissoredArea = %d,%d,%d,%d", scissoredArea.x, scissoredArea.y,
1297                  scissoredArea.width, scissoredArea.height);
1298
1299    mScissorRect = mtl::GetScissorRect(scissoredArea);
1300    mDirtyBits.set(DIRTY_BIT_SCISSOR);
1301}
1302
1303void ContextMtl::updateCullMode(const gl::State &glState)
1304{
1305    const gl::RasterizerState &rasterState = glState.getRasterizerState();
1306
1307    mCullAllPolygons = false;
1308    if (!rasterState.cullFace)
1309    {
1310        mCullMode = MTLCullModeNone;
1311    }
1312    else
1313    {
1314        switch (rasterState.cullMode)
1315        {
1316            case gl::CullFaceMode::Back:
1317                mCullMode = MTLCullModeBack;
1318                break;
1319            case gl::CullFaceMode::Front:
1320                mCullMode = MTLCullModeFront;
1321                break;
1322            case gl::CullFaceMode::FrontAndBack:
1323                mCullAllPolygons = true;
1324                break;
1325            default:
1326                UNREACHABLE();
1327                break;
1328        }
1329    }
1330
1331    mDirtyBits.set(DIRTY_BIT_CULL_MODE);
1332}
1333
1334void ContextMtl::updateFrontFace(const gl::State &glState)
1335{
1336    FramebufferMtl *framebufferMtl = mtl::GetImpl(glState.getDrawFramebuffer());
1337    mWinding =
1338        mtl::GetFontfaceWinding(glState.getRasterizerState().frontFace, !framebufferMtl->flipY());
1339    mDirtyBits.set(DIRTY_BIT_WINDING);
1340}
1341
1342void ContextMtl::updateDepthBias(const gl::State &glState)
1343{
1344    mDirtyBits.set(DIRTY_BIT_DEPTH_BIAS);
1345}
1346
1347void ContextMtl::updateDrawFrameBufferBinding(const gl::Context *context)
1348{
1349    const gl::State &glState = getState();
1350
1351    mDrawFramebuffer          = mtl::GetImpl(glState.getDrawFramebuffer());
1352    mDrawFramebufferIsDefault = mDrawFramebuffer->getState().isDefault();
1353
1354    mDrawFramebuffer->onStartedDrawingToFrameBuffer(context);
1355
1356    onDrawFrameBufferChange(context, mDrawFramebuffer);
1357}
1358
1359void ContextMtl::onDrawFrameBufferChange(const gl::Context *context, FramebufferMtl *framebuffer)
1360{
1361    const gl::State &glState = getState();
1362    ASSERT(framebuffer == mtl::GetImpl(glState.getDrawFramebuffer()));
1363
1364    mDirtyBits.set(DIRTY_BIT_DRAW_FRAMEBUFFER);
1365
1366    updateViewport(framebuffer, glState.getViewport(), glState.getNearPlane(),
1367                   glState.getFarPlane());
1368    updateFrontFace(glState);
1369    updateScissor(glState);
1370
1371    // Need to re-apply state to RenderCommandEncoder
1372    invalidateState(context);
1373}
1374
1375void ContextMtl::updateProgramExecutable(const gl::Context *context)
1376{
1377    // Need to rebind textures
1378    invalidateCurrentTextures();
1379    // Need to re-upload default attributes
1380    invalidateDefaultAttributes(context->getStateCache().getActiveDefaultAttribsMask());
1381    // Render pipeline need to be re-applied
1382    invalidateRenderPipeline();
1383}
1384
1385void ContextMtl::updateVertexArray(const gl::Context *context)
1386{
1387    const gl::State &glState = getState();
1388    mVertexArray             = mtl::GetImpl(glState.getVertexArray());
1389    invalidateDefaultAttributes(context->getStateCache().getActiveDefaultAttribsMask());
1390    invalidateRenderPipeline();
1391}
1392
1393angle::Result ContextMtl::updateDefaultAttribute(size_t attribIndex)
1394{
1395    const gl::State &glState = mState;
1396    const gl::VertexAttribCurrentValueData &defaultValue =
1397        glState.getVertexAttribCurrentValues()[attribIndex];
1398
1399    constexpr size_t kDefaultGLAttributeValueSize =
1400        sizeof(gl::VertexAttribCurrentValueData::Values);
1401
1402    static_assert(kDefaultGLAttributeValueSize == mtl::kDefaultAttributeSize,
1403                  "Unexpected default attribute size");
1404    memcpy(mDefaultAttributes[attribIndex].values, &defaultValue.Values,
1405           mtl::kDefaultAttributeSize);
1406
1407    return angle::Result::Continue;
1408}
1409
1410angle::Result ContextMtl::setupDraw(const gl::Context *context,
1411                                    gl::PrimitiveMode mode,
1412                                    GLint firstVertex,
1413                                    GLsizei vertexOrIndexCount,
1414                                    GLsizei instances,
1415                                    gl::DrawElementsType indexTypeOrNone,
1416                                    const void *indices)
1417{
1418    ASSERT(mProgram);
1419
1420    // instances=0 means no instanced draw.
1421    GLsizei instanceCount = instances ? instances : 1;
1422
1423    mtl::BufferRef lineLoopLastSegmentIndexBuffer;
1424    if (mode == gl::PrimitiveMode::LineLoop)
1425    {
1426        // Generate line loop last segment before render command encoder is created
1427        ANGLE_TRY(genLineLoopLastSegment(context, firstVertex, vertexOrIndexCount, instanceCount,
1428                                         indexTypeOrNone, indices,
1429                                         &lineLoopLastSegmentIndexBuffer));
1430    }
1431
1432    // Must be called before the render command encoder is started.
1433    if (context->getStateCache().hasAnyActiveClientAttrib())
1434    {
1435        ANGLE_TRY(mVertexArray->updateClientAttribs(context, firstVertex, vertexOrIndexCount,
1436                                                    instanceCount, indexTypeOrNone, indices));
1437    }
1438    // This must be called before render command encoder is started.
1439    bool textureChanged = false;
1440    if (mDirtyBits.test(DIRTY_BIT_TEXTURES))
1441    {
1442        textureChanged = true;
1443        ANGLE_TRY(handleDirtyActiveTextures(context));
1444    }
1445
1446    if (!mRenderEncoder.valid())
1447    {
1448        // re-apply everything
1449        invalidateState(context);
1450    }
1451
1452    if (mDirtyBits.test(DIRTY_BIT_DRAW_FRAMEBUFFER))
1453    {
1454        // Start new render command encoder
1455        const mtl::RenderPassDesc &rpDesc = mDrawFramebuffer->getRenderPassDesc(this);
1456        ANGLE_MTL_TRY(this, getRenderCommandEncoder(rpDesc));
1457
1458        // re-apply everything
1459        invalidateState(context);
1460    }
1461
1462    Optional<mtl::RenderPipelineDesc> changedPipelineDesc;
1463    ANGLE_TRY(checkIfPipelineChanged(context, mode, &changedPipelineDesc));
1464
1465    for (size_t bit : mDirtyBits)
1466    {
1467        switch (bit)
1468        {
1469            case DIRTY_BIT_TEXTURES:
1470                // Already handled.
1471                break;
1472            case DIRTY_BIT_DEFAULT_ATTRIBS:
1473                ANGLE_TRY(handleDirtyDefaultAttribs(context));
1474                break;
1475            case DIRTY_BIT_DRIVER_UNIFORMS:
1476                ANGLE_TRY(handleDirtyDriverUniforms(context));
1477                break;
1478            case DIRTY_BIT_DEPTH_STENCIL_DESC:
1479                ANGLE_TRY(handleDirtyDepthStencilState(context));
1480                break;
1481            case DIRTY_BIT_DEPTH_BIAS:
1482                ANGLE_TRY(handleDirtyDepthBias(context));
1483                break;
1484            case DIRTY_BIT_STENCIL_REF:
1485                mRenderEncoder.setStencilRefVals(mStencilRefFront, mStencilRefBack);
1486                break;
1487            case DIRTY_BIT_BLEND_COLOR:
1488                mRenderEncoder.setBlendColor(
1489                    mState.getBlendColor().red, mState.getBlendColor().green,
1490                    mState.getBlendColor().blue, mState.getBlendColor().alpha);
1491                break;
1492            case DIRTY_BIT_VIEWPORT:
1493                mRenderEncoder.setViewport(mViewport);
1494                break;
1495            case DIRTY_BIT_SCISSOR:
1496                mRenderEncoder.setScissorRect(mScissorRect);
1497                break;
1498            case DIRTY_BIT_DRAW_FRAMEBUFFER:
1499                // Already handled.
1500                break;
1501            case DIRTY_BIT_CULL_MODE:
1502                mRenderEncoder.setCullMode(mCullMode);
1503                break;
1504            case DIRTY_BIT_WINDING:
1505                mRenderEncoder.setFrontFacingWinding(mWinding);
1506                break;
1507            case DIRTY_BIT_RENDER_PIPELINE:
1508                // Already handled. See checkIfPipelineChanged().
1509                break;
1510            default:
1511                UNREACHABLE();
1512                break;
1513        }
1514    }
1515
1516    mDirtyBits.reset();
1517
1518    ANGLE_TRY(mProgram->setupDraw(context, &mRenderEncoder, changedPipelineDesc, textureChanged));
1519
1520    if (mode == gl::PrimitiveMode::LineLoop)
1521    {
1522        // Draw last segment of line loop here
1523        if (instances == 0)
1524        {
1525            mRenderEncoder.drawIndexed(MTLPrimitiveTypeLine, 2, MTLIndexTypeUInt32,
1526                                       lineLoopLastSegmentIndexBuffer, 0);
1527        }
1528        else
1529        {
1530            mRenderEncoder.drawIndexedInstanced(MTLPrimitiveTypeLine, 2, MTLIndexTypeUInt32,
1531                                                lineLoopLastSegmentIndexBuffer, 0, instanceCount);
1532        }
1533    }
1534
1535    return angle::Result::Continue;
1536}
1537
1538angle::Result ContextMtl::genLineLoopLastSegment(const gl::Context *context,
1539                                                 GLint firstVertex,
1540                                                 GLsizei vertexOrIndexCount,
1541                                                 GLsizei instanceCount,
1542                                                 gl::DrawElementsType indexTypeOrNone,
1543                                                 const void *indices,
1544                                                 mtl::BufferRef *lastSegmentIndexBufferOut)
1545{
1546    mLineLoopIndexBuffer.releaseInFlightBuffers(this);
1547
1548    mtl::BufferRef newBuffer;
1549    ANGLE_TRY(mLineLoopIndexBuffer.allocate(this, 2 * sizeof(uint32_t), nullptr, &newBuffer,
1550                                            nullptr, nullptr));
1551
1552    if (indexTypeOrNone == gl::DrawElementsType::InvalidEnum)
1553    {
1554        ANGLE_TRY(getDisplay()->getUtils().generateLineLoopLastSegment(
1555            this, firstVertex, firstVertex + vertexOrIndexCount - 1, newBuffer, 0));
1556    }
1557    else
1558    {
1559        // NOTE(hqle): Support drawRangeElements & instanced draw, which means firstVertex has to be
1560        // taken into account
1561        ASSERT(firstVertex == 0);
1562        ANGLE_TRY(getDisplay()->getUtils().generateLineLoopLastSegmentFromElementsArray(
1563            this, {indexTypeOrNone, vertexOrIndexCount, indices, newBuffer, 0}));
1564    }
1565
1566    ANGLE_TRY(mLineLoopIndexBuffer.commit(this));
1567
1568    *lastSegmentIndexBufferOut = newBuffer;
1569
1570    return angle::Result::Continue;
1571}
1572
1573angle::Result ContextMtl::handleDirtyActiveTextures(const gl::Context *context)
1574{
1575    const gl::State &glState   = mState;
1576    const gl::Program *program = glState.getProgram();
1577
1578    const gl::ActiveTexturesCache &textures     = glState.getActiveTexturesCache();
1579    const gl::ActiveTextureMask &activeTextures = program->getExecutable().getActiveSamplersMask();
1580
1581    for (size_t textureUnit : activeTextures)
1582    {
1583        gl::Texture *texture = textures[textureUnit];
1584
1585        if (texture == nullptr)
1586        {
1587            continue;
1588        }
1589
1590        TextureMtl *textureMtl = mtl::GetImpl(texture);
1591
1592        // Make sure texture's images update will be transferred to GPU.
1593        ANGLE_TRY(textureMtl->ensureTextureCreated(context));
1594
1595        // The binding of this texture will be done by ProgramMtl.
1596    }
1597
1598    return angle::Result::Continue;
1599}
1600
1601angle::Result ContextMtl::handleDirtyDefaultAttribs(const gl::Context *context)
1602{
1603    for (size_t attribIndex : mDirtyDefaultAttribsMask)
1604    {
1605        ANGLE_TRY(updateDefaultAttribute(attribIndex));
1606    }
1607
1608    ASSERT(mRenderEncoder.valid());
1609    mRenderEncoder.setFragmentData(mDefaultAttributes, mtl::kDefaultAttribsBindingIndex);
1610    mRenderEncoder.setVertexData(mDefaultAttributes, mtl::kDefaultAttribsBindingIndex);
1611
1612    mDirtyDefaultAttribsMask.reset();
1613    return angle::Result::Continue;
1614}
1615
1616angle::Result ContextMtl::handleDirtyDriverUniforms(const gl::Context *context)
1617{
1618    const gl::Rectangle &glViewport = mState.getViewport();
1619
1620    float depthRangeNear = mState.getNearPlane();
1621    float depthRangeFar  = mState.getFarPlane();
1622    float depthRangeDiff = depthRangeFar - depthRangeNear;
1623
1624    mDriverUniforms.viewport[0] = glViewport.x;
1625    mDriverUniforms.viewport[1] = glViewport.y;
1626    mDriverUniforms.viewport[2] = glViewport.width;
1627    mDriverUniforms.viewport[3] = glViewport.height;
1628
1629    mDriverUniforms.halfRenderArea[0] =
1630        static_cast<float>(mDrawFramebuffer->getState().getDimensions().width) * 0.5f;
1631    mDriverUniforms.halfRenderArea[1] =
1632        static_cast<float>(mDrawFramebuffer->getState().getDimensions().height) * 0.5f;
1633    mDriverUniforms.flipXY[0]    = 1.0f;
1634    mDriverUniforms.flipXY[1]    = mDrawFramebuffer->flipY() ? -1.0f : 1.0f;
1635    mDriverUniforms.negFlipXY[0] = mDriverUniforms.flipXY[0];
1636    mDriverUniforms.negFlipXY[1] = -mDriverUniforms.flipXY[1];
1637
1638    mDriverUniforms.enabledClipDistances = mState.getEnabledClipDistances().bits();
1639
1640    mDriverUniforms.depthRange[0] = depthRangeNear;
1641    mDriverUniforms.depthRange[1] = depthRangeFar;
1642    mDriverUniforms.depthRange[2] = depthRangeDiff;
1643    mDriverUniforms.depthRange[3] = NeedToInvertDepthRange(depthRangeNear, depthRangeFar) ? -1 : 1;
1644
1645    // Fill in a mat2 identity matrix, plus padding
1646    mDriverUniforms.preRotation[0] = 1.0f;
1647    mDriverUniforms.preRotation[1] = 0.0f;
1648    mDriverUniforms.preRotation[2] = 0.0f;
1649    mDriverUniforms.preRotation[3] = 0.0f;
1650    mDriverUniforms.preRotation[4] = 0.0f;
1651    mDriverUniforms.preRotation[5] = 1.0f;
1652    mDriverUniforms.preRotation[6] = 0.0f;
1653    mDriverUniforms.preRotation[7] = 0.0f;
1654
1655    // Fill in a mat2 identity matrix, plus padding
1656    mDriverUniforms.fragRotation[0] = 1.0f;
1657    mDriverUniforms.fragRotation[1] = 0.0f;
1658    mDriverUniforms.fragRotation[2] = 0.0f;
1659    mDriverUniforms.fragRotation[3] = 0.0f;
1660    mDriverUniforms.fragRotation[4] = 0.0f;
1661    mDriverUniforms.fragRotation[5] = 1.0f;
1662    mDriverUniforms.fragRotation[6] = 0.0f;
1663    mDriverUniforms.fragRotation[7] = 0.0f;
1664
1665    ASSERT(mRenderEncoder.valid());
1666    mRenderEncoder.setFragmentData(mDriverUniforms, mtl::kDriverUniformsBindingIndex);
1667    mRenderEncoder.setVertexData(mDriverUniforms, mtl::kDriverUniformsBindingIndex);
1668
1669    return angle::Result::Continue;
1670}
1671
1672angle::Result ContextMtl::handleDirtyDepthStencilState(const gl::Context *context)
1673{
1674    ASSERT(mRenderEncoder.valid());
1675
1676    // Need to handle the case when render pass doesn't have depth/stencil attachment.
1677    mtl::DepthStencilDesc dsDesc              = mDepthStencilDesc;
1678    const mtl::RenderPassDesc &renderPassDesc = mDrawFramebuffer->getRenderPassDesc(this);
1679
1680    if (!renderPassDesc.depthAttachment.texture)
1681    {
1682        dsDesc.depthWriteEnabled    = false;
1683        dsDesc.depthCompareFunction = MTLCompareFunctionAlways;
1684    }
1685
1686    if (!renderPassDesc.stencilAttachment.texture)
1687    {
1688        dsDesc.frontFaceStencil.reset();
1689        dsDesc.backFaceStencil.reset();
1690    }
1691
1692    // Apply depth stencil state
1693    mRenderEncoder.setDepthStencilState(getDisplay()->getDepthStencilState(dsDesc));
1694
1695    return angle::Result::Continue;
1696}
1697
1698angle::Result ContextMtl::handleDirtyDepthBias(const gl::Context *context)
1699{
1700    const gl::RasterizerState &raserState = mState.getRasterizerState();
1701    ASSERT(mRenderEncoder.valid());
1702    if (!mState.isPolygonOffsetFillEnabled())
1703    {
1704        mRenderEncoder.setDepthBias(0, 0, 0);
1705    }
1706    else
1707    {
1708        mRenderEncoder.setDepthBias(raserState.polygonOffsetUnits, raserState.polygonOffsetFactor,
1709                                    0);
1710    }
1711
1712    return angle::Result::Continue;
1713}
1714
1715angle::Result ContextMtl::checkIfPipelineChanged(
1716    const gl::Context *context,
1717    gl::PrimitiveMode primitiveMode,
1718    Optional<mtl::RenderPipelineDesc> *changedPipelineDesc)
1719{
1720    ASSERT(mRenderEncoder.valid());
1721    mtl::PrimitiveTopologyClass topologyClass = mtl::GetPrimitiveTopologyClass(primitiveMode);
1722
1723    bool rppChange = mDirtyBits.test(DIRTY_BIT_RENDER_PIPELINE) ||
1724                     topologyClass != mRenderPipelineDesc.inputPrimitiveTopology;
1725
1726    // Obtain RenderPipelineDesc's vertex array descriptor.
1727    ANGLE_TRY(mVertexArray->setupDraw(context, &mRenderEncoder, &rppChange,
1728                                      &mRenderPipelineDesc.vertexDescriptor));
1729
1730    if (rppChange)
1731    {
1732        const mtl::RenderPassDesc &renderPassDesc = mDrawFramebuffer->getRenderPassDesc(this);
1733        // Obtain RenderPipelineDesc's output descriptor.
1734        renderPassDesc.populateRenderPipelineOutputDesc(mBlendDesc,
1735                                                        &mRenderPipelineDesc.outputDescriptor);
1736
1737        mRenderPipelineDesc.inputPrimitiveTopology = topologyClass;
1738
1739        *changedPipelineDesc = mRenderPipelineDesc;
1740    }
1741
1742    return angle::Result::Continue;
1743}
1744}
1745