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