• 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// VertexArrayMtl.mm:
7//    Implements the class methods for VertexArrayMtl.
8//
9
10#include "libANGLE/renderer/metal/VertexArrayMtl.h"
11
12#include <TargetConditionals.h>
13
14#include "libANGLE/renderer/metal/BufferMtl.h"
15#include "libANGLE/renderer/metal/ContextMtl.h"
16#include "libANGLE/renderer/metal/DisplayMtl.h"
17#include "libANGLE/renderer/metal/mtl_format_utils.h"
18
19#include "common/debug.h"
20#include "common/utilities.h"
21
22namespace rx
23{
24namespace
25{
26constexpr size_t kDynamicIndexDataSize = 1024 * 8;
27
28angle::Result StreamVertexData(ContextMtl *contextMtl,
29                               mtl::BufferPool *dynamicBuffer,
30                               const uint8_t *sourceData,
31                               size_t bytesToAllocate,
32                               size_t destOffset,
33                               size_t vertexCount,
34                               size_t stride,
35                               VertexCopyFunction vertexLoadFunction,
36                               SimpleWeakBufferHolderMtl *bufferHolder,
37                               size_t *bufferOffsetOut)
38{
39    ANGLE_CHECK(contextMtl, vertexLoadFunction, "Unsupported format conversion", GL_INVALID_ENUM);
40    uint8_t *dst = nullptr;
41    mtl::BufferRef newBuffer;
42    ANGLE_TRY(dynamicBuffer->allocate(contextMtl, bytesToAllocate, &dst, &newBuffer,
43                                      bufferOffsetOut, nullptr));
44    bufferHolder->set(newBuffer);
45    dst += destOffset;
46    vertexLoadFunction(sourceData, stride, vertexCount, dst);
47
48    ANGLE_TRY(dynamicBuffer->commit(contextMtl));
49    return angle::Result::Continue;
50}
51
52template <typename SizeT>
53const mtl::VertexFormat &GetVertexConversionFormat(ContextMtl *contextMtl,
54                                                   angle::FormatID originalFormat,
55                                                   SizeT *strideOut)
56{
57    // Convert to tightly packed format
58    const mtl::VertexFormat &packedFormat = contextMtl->getVertexFormat(originalFormat, true);
59    *strideOut                            = packedFormat.actualAngleFormat().pixelBytes;
60    return packedFormat;
61}
62
63size_t GetIndexConvertedBufferSize(gl::DrawElementsType indexType, size_t indexCount)
64{
65    size_t elementSize = gl::GetDrawElementsTypeSize(indexType);
66    if (indexType == gl::DrawElementsType::UnsignedByte)
67    {
68        // 8-bit indices are not supported by Metal, so they are promoted to
69        // 16-bit indices below
70        elementSize = sizeof(GLushort);
71    }
72
73    const size_t amount = elementSize * indexCount;
74
75    return amount;
76}
77
78angle::Result StreamIndexData(ContextMtl *contextMtl,
79                              mtl::BufferPool *dynamicBuffer,
80                              const uint8_t *sourcePointer,
81                              gl::DrawElementsType indexType,
82                              size_t indexCount,
83                              bool primitiveRestartEnabled,
84                              mtl::BufferRef *bufferOut,
85                              size_t *bufferOffsetOut)
86{
87    dynamicBuffer->releaseInFlightBuffers(contextMtl);
88    const size_t amount = GetIndexConvertedBufferSize(indexType, indexCount);
89    GLubyte *dst        = nullptr;
90    ANGLE_TRY(
91        dynamicBuffer->allocate(contextMtl, amount, &dst, bufferOut, bufferOffsetOut, nullptr));
92
93    if (indexType == gl::DrawElementsType::UnsignedByte)
94    {
95        // Unsigned bytes don't have direct support in Metal so we have to expand the
96        // memory to a GLushort.
97        const GLubyte *in     = static_cast<const GLubyte *>(sourcePointer);
98        GLushort *expandedDst = reinterpret_cast<GLushort *>(dst);
99
100        if (primitiveRestartEnabled)
101        {
102            for (size_t index = 0; index < indexCount; index++)
103            {
104                if (in[index] == 0xFF)
105                {
106                    expandedDst[index] = 0xFFFF;
107                }
108                else
109                {
110                    expandedDst[index] = static_cast<GLushort>(in[index]);
111                }
112            }
113        }  // if (primitiveRestartEnabled)
114        else
115        {
116            for (size_t index = 0; index < indexCount; index++)
117            {
118                expandedDst[index] = static_cast<GLushort>(in[index]);
119            }
120        }  // if (primitiveRestartEnabled)
121    }
122    else
123    {
124        memcpy(dst, sourcePointer, amount);
125    }
126    ANGLE_TRY(dynamicBuffer->commit(contextMtl));
127
128    return angle::Result::Continue;
129}
130
131size_t GetVertexCount(BufferMtl *srcBuffer,
132                      const gl::VertexBinding &binding,
133                      uint32_t srcFormatSize)
134{
135    // Bytes usable for vertex data.
136    GLint64 bytes = srcBuffer->size() - binding.getOffset();
137    if (bytes < srcFormatSize)
138        return 0;
139
140    // Count the last vertex.  It may occupy less than a full stride.
141    size_t numVertices = 1;
142    bytes -= srcFormatSize;
143
144    // Count how many strides fit remaining space.
145    if (bytes > 0)
146        numVertices += static_cast<size_t>(bytes) / binding.getStride();
147
148    return numVertices;
149}
150
151size_t GetVertexCountWithConversion(BufferMtl *srcBuffer,
152                                    VertexConversionBufferMtl *conversionBuffer,
153                                    const gl::VertexBinding &binding,
154                                    uint32_t srcFormatSize)
155{
156    // Bytes usable for vertex data.
157    GLint64 bytes = srcBuffer->size() -
158                    MIN(static_cast<GLintptr>(conversionBuffer->offset), binding.getOffset());
159    if (bytes < srcFormatSize)
160        return 0;
161
162    // Count the last vertex.  It may occupy less than a full stride.
163    size_t numVertices = 1;
164    bytes -= srcFormatSize;
165
166    // Count how many strides fit remaining space.
167    if (bytes > 0)
168        numVertices += static_cast<size_t>(bytes) / binding.getStride();
169
170    return numVertices;
171}
172inline size_t GetIndexCount(BufferMtl *srcBuffer, size_t offset, gl::DrawElementsType indexType)
173{
174    size_t elementSize = gl::GetDrawElementsTypeSize(indexType);
175    return (srcBuffer->size() - offset) / elementSize;
176}
177
178inline void SetDefaultVertexBufferLayout(mtl::VertexBufferLayoutDesc *layout)
179{
180    layout->stepFunction = mtl::kVertexStepFunctionInvalid;
181    layout->stepRate     = 0;
182    layout->stride       = 0;
183}
184
185}  // namespace
186
187// VertexArrayMtl implementation
188VertexArrayMtl::VertexArrayMtl(const gl::VertexArrayState &state, ContextMtl *context)
189    : VertexArrayImpl(state),
190      mDefaultFloatVertexFormat(
191          context->getVertexFormat(angle::FormatID::R32G32B32A32_FLOAT, false)),
192      mDefaultIntVertexFormat(context->getVertexFormat(angle::FormatID::R32G32B32A32_SINT, false)),
193      mDefaultUIntVertexFormat(context->getVertexFormat(angle::FormatID::R32G32B32A32_UINT, false))
194{
195    reset(context);
196
197    mDynamicVertexData.initialize(context, 0, mtl::kVertexAttribBufferStrideAlignment,
198                                  /** maxBuffers */ 10 * mtl::kMaxVertexAttribs);
199
200    mDynamicIndexData.initialize(context, kDynamicIndexDataSize, mtl::kIndexBufferOffsetAlignment,
201                                 0);
202}
203VertexArrayMtl::~VertexArrayMtl() {}
204
205void VertexArrayMtl::destroy(const gl::Context *context)
206{
207    ContextMtl *contextMtl = mtl::GetImpl(context);
208
209    reset(contextMtl);
210
211    mDynamicVertexData.destroy(contextMtl);
212    mDynamicIndexData.destroy(contextMtl);
213}
214
215void VertexArrayMtl::reset(ContextMtl *context)
216{
217    for (BufferHolderMtl *&buffer : mCurrentArrayBuffers)
218    {
219        buffer = nullptr;
220    }
221    for (size_t &offset : mCurrentArrayBufferOffsets)
222    {
223        offset = 0;
224    }
225    for (GLuint &stride : mCurrentArrayBufferStrides)
226    {
227        stride = 0;
228    }
229    for (const mtl::VertexFormat *&format : mCurrentArrayBufferFormats)
230    {
231        format = &mDefaultFloatVertexFormat;
232    }
233
234    for (size_t &inlineDataSize : mCurrentArrayInlineDataSizes)
235    {
236        inlineDataSize = 0;
237    }
238
239    for (angle::MemoryBuffer &convertedClientArray : mConvertedClientSmallArrays)
240    {
241        convertedClientArray.clear();
242    }
243
244    for (const uint8_t *&clientPointer : mCurrentArrayInlineDataPointers)
245    {
246        clientPointer = nullptr;
247    }
248
249    if (context->getDisplay()->getFeatures().allowInlineConstVertexData.enabled)
250    {
251        mInlineDataMaxSize = mtl::kInlineConstDataMaxSize;
252    }
253    else
254    {
255        mInlineDataMaxSize = 0;
256    }
257
258    mVertexArrayDirty = true;
259}
260
261angle::Result VertexArrayMtl::syncState(const gl::Context *context,
262                                        const gl::VertexArray::DirtyBits &dirtyBits,
263                                        gl::VertexArray::DirtyAttribBitsArray *attribBits,
264                                        gl::VertexArray::DirtyBindingBitsArray *bindingBits)
265{
266    const std::vector<gl::VertexAttribute> &attribs = mState.getVertexAttributes();
267    const std::vector<gl::VertexBinding> &bindings  = mState.getVertexBindings();
268
269    for (auto iter = dirtyBits.begin(), endIter = dirtyBits.end(); iter != endIter; ++iter)
270    {
271        size_t dirtyBit = *iter;
272        switch (dirtyBit)
273        {
274            case gl::VertexArray::DIRTY_BIT_LOST_OBSERVATION:
275            {
276                // If vertex array was not observing while unbound, we need to check buffer's
277                // internal storage and take action if buffer has changed while not observing.
278                // For now we just simply assume buffer storage has changed and always dirty all
279                // binding points.
280                iter.setLaterBits(
281                    gl::VertexArray::DirtyBits(mState.getBufferBindingMask().to_ulong()
282                                               << gl::VertexArray::DIRTY_BIT_BINDING_0));
283                break;
284            }
285
286            case gl::VertexArray::DIRTY_BIT_ELEMENT_ARRAY_BUFFER:
287            case gl::VertexArray::DIRTY_BIT_ELEMENT_ARRAY_BUFFER_DATA:
288            {
289                mVertexDataDirty = true;
290                break;
291            }
292
293#define ANGLE_VERTEX_DIRTY_ATTRIB_FUNC(INDEX)                                                     \
294    case gl::VertexArray::DIRTY_BIT_ATTRIB_0 + INDEX:                                             \
295        ANGLE_TRY(syncDirtyAttrib(context, attribs[INDEX], bindings[attribs[INDEX].bindingIndex], \
296                                  INDEX));                                                        \
297        mVertexArrayDirty = true;                                                                 \
298        (*attribBits)[INDEX].reset();                                                             \
299        break;
300
301                ANGLE_VERTEX_INDEX_CASES(ANGLE_VERTEX_DIRTY_ATTRIB_FUNC)
302
303#define ANGLE_VERTEX_DIRTY_BINDING_FUNC(INDEX)                                                    \
304    case gl::VertexArray::DIRTY_BIT_BINDING_0 + INDEX:                                            \
305        ANGLE_TRY(syncDirtyAttrib(context, attribs[INDEX], bindings[attribs[INDEX].bindingIndex], \
306                                  INDEX));                                                        \
307        mVertexArrayDirty = true;                                                                 \
308        (*bindingBits)[INDEX].reset();                                                            \
309        break;
310
311                ANGLE_VERTEX_INDEX_CASES(ANGLE_VERTEX_DIRTY_BINDING_FUNC)
312
313#define ANGLE_VERTEX_DIRTY_BUFFER_DATA_FUNC(INDEX)                                                \
314    case gl::VertexArray::DIRTY_BIT_BUFFER_DATA_0 + INDEX:                                        \
315        ANGLE_TRY(syncDirtyAttrib(context, attribs[INDEX], bindings[attribs[INDEX].bindingIndex], \
316                                  INDEX));                                                        \
317        mVertexDataDirty = true;                                                                  \
318        break;
319
320                ANGLE_VERTEX_INDEX_CASES(ANGLE_VERTEX_DIRTY_BUFFER_DATA_FUNC)
321
322            default:
323                UNREACHABLE();
324                break;
325        }
326    }
327
328    return angle::Result::Continue;
329}
330
331ANGLE_INLINE void VertexArrayMtl::getVertexAttribFormatAndArraySize(const sh::ShaderVariable &var,
332                                                                    MTLVertexFormat *formatOut,
333                                                                    uint32_t *arraySizeOut)
334{
335    uint32_t arraySize = var.getArraySizeProduct();
336
337    MTLVertexFormat format;
338    switch (var.type)
339    {
340        case GL_INT:
341        case GL_INT_VEC2:
342        case GL_INT_VEC3:
343        case GL_INT_VEC4:
344            format = mDefaultIntVertexFormat.metalFormat;
345            break;
346        case GL_UNSIGNED_INT:
347        case GL_UNSIGNED_INT_VEC2:
348        case GL_UNSIGNED_INT_VEC3:
349        case GL_UNSIGNED_INT_VEC4:
350            format = mDefaultUIntVertexFormat.metalFormat;
351            break;
352        case GL_FLOAT_MAT2:
353        case GL_FLOAT_MAT2x3:
354        case GL_FLOAT_MAT2x4:
355            arraySize *= 2;
356            format = mDefaultFloatVertexFormat.metalFormat;
357            break;
358        case GL_FLOAT_MAT3:
359        case GL_FLOAT_MAT3x2:
360        case GL_FLOAT_MAT3x4:
361            arraySize *= 3;
362            format = mDefaultFloatVertexFormat.metalFormat;
363            break;
364        case GL_FLOAT_MAT4:
365        case GL_FLOAT_MAT4x2:
366        case GL_FLOAT_MAT4x3:
367            arraySize *= 4;
368            format = mDefaultFloatVertexFormat.metalFormat;
369            break;
370        default:
371            format = mDefaultFloatVertexFormat.metalFormat;
372    }
373
374    *arraySizeOut = arraySize;
375    *formatOut    = format;
376}
377
378// vertexDescChanged is both input and output, the input value if is true, will force new
379// mtl::VertexDesc to be returned via vertexDescOut. This typically happens when active shader
380// program is changed.
381// Otherwise, it is only returned when the vertex array is dirty.
382angle::Result VertexArrayMtl::setupDraw(const gl::Context *glContext,
383                                        mtl::RenderCommandEncoder *cmdEncoder,
384                                        bool *vertexDescChanged,
385                                        mtl::VertexDesc *vertexDescOut)
386{
387    // NOTE(hqle): consider only updating dirty attributes
388    bool dirty = mVertexArrayDirty || *vertexDescChanged;
389
390    if (dirty)
391    {
392
393        mVertexArrayDirty = false;
394        mEmulatedInstanceAttribs.clear();
395
396        const gl::ProgramExecutable *executable = glContext->getState().getProgramExecutable();
397        const gl::AttributesMask &programActiveAttribsMask =
398            executable->getActiveAttribLocationsMask();
399        const gl::ProgramState &programState = glContext->getState().getProgram()->getState();
400
401        const std::vector<gl::VertexAttribute> &attribs = mState.getVertexAttributes();
402        const std::vector<gl::VertexBinding> &bindings  = mState.getVertexBindings();
403
404        mtl::VertexDesc &desc = *vertexDescOut;
405
406        desc.numAttribs       = mtl::kMaxVertexAttribs;
407        desc.numBufferLayouts = mtl::kMaxVertexAttribs;
408
409        // Initialize the buffer layouts with constant step rate
410        for (uint32_t b = 0; b < mtl::kMaxVertexAttribs; ++b)
411        {
412            SetDefaultVertexBufferLayout(&desc.layouts[b]);
413        }
414
415        for (uint32_t v = 0; v < mtl::kMaxVertexAttribs; ++v)
416        {
417            if (!programActiveAttribsMask.test(v))
418            {
419                desc.attributes[v].format      = MTLVertexFormatInvalid;
420                desc.attributes[v].bufferIndex = 0;
421                desc.attributes[v].offset      = 0;
422                continue;
423            }
424
425            const auto &attrib               = attribs[v];
426            const gl::VertexBinding &binding = bindings[attrib.bindingIndex];
427
428            bool attribEnabled = attrib.enabled;
429            if (attribEnabled && !mCurrentArrayBuffers[v] && !mCurrentArrayInlineDataPointers[v])
430            {
431                // Disable it to avoid crash.
432                attribEnabled = false;
433            }
434
435            if (!attribEnabled)
436            {
437                // Use default attribute
438                // Need to find the attribute having the exact binding location = v in the program
439                // inputs list to retrieve its coresponding data type:
440                const std::vector<sh::ShaderVariable> &programInputs =
441                    programState.getProgramInputs();
442                std::vector<sh::ShaderVariable>::const_iterator attribInfoIte = std::find_if(
443                    begin(programInputs), end(programInputs), [v](const sh::ShaderVariable &sv) {
444                        return static_cast<uint32_t>(sv.location) == v;
445                    });
446
447                if (attribInfoIte == end(programInputs))
448                {
449                    // Most likely this is array element with index > 0.
450                    // Already handled when encounter first element.
451                    continue;
452                }
453
454                uint32_t arraySize;
455                MTLVertexFormat format;
456
457                getVertexAttribFormatAndArraySize(*attribInfoIte, &format, &arraySize);
458
459                for (uint32_t vaIdx = v; vaIdx < v + arraySize; ++vaIdx)
460                {
461                    desc.attributes[vaIdx].bufferIndex = mtl::kDefaultAttribsBindingIndex;
462                    desc.attributes[vaIdx].offset      = vaIdx * mtl::kDefaultAttributeSize;
463                    desc.attributes[vaIdx].format      = format;
464                }
465            }
466            else
467            {
468                uint32_t bufferIdx    = mtl::kVboBindingIndexStart + v;
469                uint32_t bufferOffset = static_cast<uint32_t>(mCurrentArrayBufferOffsets[v]);
470
471                const angle::Format &angleFormat =
472                    mCurrentArrayBufferFormats[v]->actualAngleFormat();
473                desc.attributes[v].format = mCurrentArrayBufferFormats[v]->metalFormat;
474
475                desc.attributes[v].bufferIndex = bufferIdx;
476                desc.attributes[v].offset      = 0;
477                ASSERT((bufferOffset % angleFormat.pixelBytes) == 0);
478
479                ASSERT(bufferIdx < mtl::kMaxVertexAttribs);
480                if (binding.getDivisor() == 0)
481                {
482                    desc.layouts[bufferIdx].stepFunction = MTLVertexStepFunctionPerVertex;
483                    desc.layouts[bufferIdx].stepRate     = 1;
484                }
485                else
486                {
487                    desc.layouts[bufferIdx].stepFunction = MTLVertexStepFunctionPerInstance;
488                    desc.layouts[bufferIdx].stepRate     = binding.getDivisor();
489                }
490
491                desc.layouts[bufferIdx].stride = mCurrentArrayBufferStrides[v];
492            }
493        }  // for (v)
494    }
495
496    if (dirty || mVertexDataDirty)
497    {
498        mVertexDataDirty                        = false;
499        const gl::ProgramExecutable *executable = glContext->getState().getProgramExecutable();
500        const gl::AttributesMask &programActiveAttribsMask =
501            executable->getActiveAttribLocationsMask();
502
503        for (uint32_t v = 0; v < mtl::kMaxVertexAttribs; ++v)
504        {
505            if (!programActiveAttribsMask.test(v))
506            {
507                continue;
508            }
509            uint32_t bufferIdx    = mtl::kVboBindingIndexStart + v;
510            uint32_t bufferOffset = static_cast<uint32_t>(mCurrentArrayBufferOffsets[v]);
511            if (mCurrentArrayBuffers[v])
512            {
513                cmdEncoder->setVertexBuffer(mCurrentArrayBuffers[v]->getCurrentBuffer(),
514                                            bufferOffset, bufferIdx);
515            }
516            else
517            {
518                // No buffer specified, use the client memory directly as inline constant data
519                ASSERT(mCurrentArrayInlineDataSizes[v] <= mInlineDataMaxSize);
520                cmdEncoder->setVertexBytes(mCurrentArrayInlineDataPointers[v],
521                                           mCurrentArrayInlineDataSizes[v], bufferIdx);
522            }
523        }
524    }
525
526    *vertexDescChanged = dirty;
527
528    return angle::Result::Continue;
529}
530
531angle::Result VertexArrayMtl::updateClientAttribs(const gl::Context *context,
532                                                  GLint firstVertex,
533                                                  GLsizei vertexOrIndexCount,
534                                                  GLsizei instanceCount,
535                                                  gl::DrawElementsType indexTypeOrInvalid,
536                                                  const void *indices)
537{
538    ContextMtl *contextMtl                  = mtl::GetImpl(context);
539    const gl::AttributesMask &clientAttribs = context->getStateCache().getActiveClientAttribsMask();
540
541    ASSERT(clientAttribs.any());
542
543    GLint startVertex;
544    size_t vertexCount;
545    ANGLE_TRY(GetVertexRangeInfo(context, firstVertex, vertexOrIndexCount, indexTypeOrInvalid,
546                                 indices, 0, &startVertex, &vertexCount));
547
548    mDynamicVertexData.releaseInFlightBuffers(contextMtl);
549
550    const std::vector<gl::VertexAttribute> &attribs = mState.getVertexAttributes();
551    const std::vector<gl::VertexBinding> &bindings  = mState.getVertexBindings();
552
553    for (size_t attribIndex : clientAttribs)
554    {
555        const gl::VertexAttribute &attrib = attribs[attribIndex];
556        const gl::VertexBinding &binding  = bindings[attrib.bindingIndex];
557        ASSERT(attrib.enabled && binding.getBuffer().get() == nullptr);
558
559        // Source client memory pointer
560        const uint8_t *src = static_cast<const uint8_t *>(attrib.pointer);
561        ASSERT(src);
562
563        GLint startElement;
564        size_t elementCount;
565        if (binding.getDivisor() == 0)
566        {
567            // Per vertex attribute
568            startElement = startVertex;
569            elementCount = vertexCount;
570        }
571        else
572        {
573            // Per instance attribute
574            startElement = 0;
575            elementCount = UnsignedCeilDivide(instanceCount, binding.getDivisor());
576        }
577        size_t bytesIntendedToUse = (startElement + elementCount) * binding.getStride();
578
579        const mtl::VertexFormat &format = contextMtl->getVertexFormat(attrib.format->id, false);
580        bool needStreaming              = format.actualFormatId != format.intendedFormatId ||
581                             (binding.getStride() % mtl::kVertexAttribBufferStrideAlignment) != 0 ||
582                             (binding.getStride() < format.actualAngleFormat().pixelBytes) ||
583                             bytesIntendedToUse > mInlineDataMaxSize;
584
585        if (!needStreaming)
586        {
587            // Data will be uploaded directly as inline constant data
588            mCurrentArrayBuffers[attribIndex]            = nullptr;
589            mCurrentArrayInlineDataPointers[attribIndex] = src;
590            mCurrentArrayInlineDataSizes[attribIndex]    = bytesIntendedToUse;
591            mCurrentArrayBufferOffsets[attribIndex]      = 0;
592            mCurrentArrayBufferFormats[attribIndex]      = &format;
593            mCurrentArrayBufferStrides[attribIndex]      = binding.getStride();
594        }
595        else
596        {
597            GLuint convertedStride;
598            // Need to stream the client vertex data to a buffer.
599            const mtl::VertexFormat &streamFormat =
600                GetVertexConversionFormat(contextMtl, attrib.format->id, &convertedStride);
601
602            // Allocate space for startElement + elementCount so indexing will work.  If we don't
603            // start at zero all the indices will be off.
604            // Only elementCount vertices will be used by the upcoming draw so that is all we copy.
605            size_t bytesToAllocate = (startElement + elementCount) * convertedStride;
606            src += startElement * binding.getStride();
607            size_t destOffset = startElement * convertedStride;
608
609            mCurrentArrayBufferFormats[attribIndex] = &streamFormat;
610            mCurrentArrayBufferStrides[attribIndex] = convertedStride;
611
612            if (bytesToAllocate <= mInlineDataMaxSize)
613            {
614                // If the data is small enough, use host memory instead of creating GPU buffer. To
615                // avoid synchronizing access to GPU buffer that is still in use.
616                angle::MemoryBuffer &convertedClientArray =
617                    mConvertedClientSmallArrays[attribIndex];
618                if (bytesToAllocate > convertedClientArray.size())
619                {
620                    ANGLE_CHECK_GL_ALLOC(contextMtl, convertedClientArray.resize(bytesToAllocate));
621                }
622
623                ASSERT(streamFormat.vertexLoadFunction);
624                streamFormat.vertexLoadFunction(src, binding.getStride(), elementCount,
625                                                convertedClientArray.data() + destOffset);
626
627                mCurrentArrayBuffers[attribIndex]            = nullptr;
628                mCurrentArrayInlineDataPointers[attribIndex] = convertedClientArray.data();
629                mCurrentArrayInlineDataSizes[attribIndex]    = bytesToAllocate;
630                mCurrentArrayBufferOffsets[attribIndex]      = 0;
631            }
632            else
633            {
634                // Stream the client data to a GPU buffer. Synchronization might happen if buffer is
635                // in use.
636                mDynamicVertexData.updateAlignment(contextMtl,
637                                                   streamFormat.actualAngleFormat().pixelBytes);
638                ANGLE_TRY(StreamVertexData(contextMtl, &mDynamicVertexData, src, bytesToAllocate,
639                                           destOffset, elementCount, binding.getStride(),
640                                           streamFormat.vertexLoadFunction,
641                                           &mConvertedArrayBufferHolders[attribIndex],
642                                           &mCurrentArrayBufferOffsets[attribIndex]));
643                if (contextMtl->getDisplay()->getFeatures().flushAfterStreamVertexData.enabled)
644                {
645                    // WaitUntilScheduled is needed for this workaround. NoWait does not have the
646                    // needed effect.
647                    contextMtl->flushCommandBuffer(mtl::WaitUntilScheduled);
648                }
649
650                mCurrentArrayBuffers[attribIndex] = &mConvertedArrayBufferHolders[attribIndex];
651            }
652        }  // if (needStreaming)
653    }
654
655    mVertexArrayDirty = true;
656
657    return angle::Result::Continue;
658}
659
660angle::Result VertexArrayMtl::syncDirtyAttrib(const gl::Context *glContext,
661                                              const gl::VertexAttribute &attrib,
662                                              const gl::VertexBinding &binding,
663                                              size_t attribIndex)
664{
665    ContextMtl *contextMtl = mtl::GetImpl(glContext);
666    ASSERT(mtl::kMaxVertexAttribs > attribIndex);
667
668    if (attrib.enabled)
669    {
670        gl::Buffer *bufferGL            = binding.getBuffer().get();
671        const mtl::VertexFormat &format = contextMtl->getVertexFormat(attrib.format->id, false);
672
673        if (bufferGL)
674        {
675            BufferMtl *bufferMtl = mtl::GetImpl(bufferGL);
676            // https://bugs.webkit.org/show_bug.cgi?id=236733
677            // even non-converted buffers need to be observed for potential
678            // data rebinds.
679            mContentsObservers->enableForBuffer(bufferGL, static_cast<uint32_t>(attribIndex));
680            bool needConversion =
681                format.actualFormatId != format.intendedFormatId ||
682                (binding.getOffset() % format.actualAngleFormat().pixelBytes) != 0 ||
683                (binding.getOffset() % mtl::kVertexAttribBufferStrideAlignment) != 0 ||
684                (binding.getStride() < format.actualAngleFormat().pixelBytes) ||
685                (binding.getStride() % mtl::kVertexAttribBufferStrideAlignment) != 0;
686
687            if (needConversion)
688            {
689                ANGLE_TRY(convertVertexBuffer(glContext, bufferMtl, binding, attribIndex, format));
690            }
691            else
692            {
693                mCurrentArrayBuffers[attribIndex]       = bufferMtl;
694                mCurrentArrayBufferOffsets[attribIndex] = binding.getOffset();
695                mCurrentArrayBufferStrides[attribIndex] = binding.getStride();
696
697                mCurrentArrayBufferFormats[attribIndex] = &format;
698            }
699        }
700        else
701        {
702            // ContextMtl must feed the client data using updateClientAttribs()
703        }
704    }
705    else
706    {
707        // Use default attribute value. Handled in setupDraw().
708        mCurrentArrayBuffers[attribIndex]       = nullptr;
709        mCurrentArrayBufferOffsets[attribIndex] = 0;
710        mCurrentArrayBufferStrides[attribIndex] = 0;
711        mCurrentArrayBufferFormats[attribIndex] =
712            &contextMtl->getVertexFormat(angle::FormatID::NONE, false);
713    }
714
715    return angle::Result::Continue;
716}
717
718angle::Result VertexArrayMtl::getIndexBuffer(const gl::Context *context,
719                                             gl::DrawElementsType type,
720                                             size_t count,
721                                             const void *indices,
722                                             mtl::BufferRef *idxBufferOut,
723                                             size_t *idxBufferOffsetOut,
724                                             gl::DrawElementsType *indexTypeOut)
725{
726    const gl::Buffer *glElementArrayBuffer = getState().getElementArrayBuffer();
727
728    size_t convertedOffset = reinterpret_cast<size_t>(indices);
729    if (!glElementArrayBuffer)
730    {
731        ANGLE_TRY(streamIndexBufferFromClient(context, type, count, indices, idxBufferOut,
732                                              idxBufferOffsetOut));
733    }
734    else
735    {
736        bool needConversion = type == gl::DrawElementsType::UnsignedByte;
737        if (needConversion)
738        {
739            ANGLE_TRY(convertIndexBuffer(context, type, convertedOffset, idxBufferOut,
740                                         idxBufferOffsetOut));
741        }
742        else
743        {
744            // No conversion needed:
745            BufferMtl *bufferMtl = mtl::GetImpl(glElementArrayBuffer);
746            *idxBufferOut        = bufferMtl->getCurrentBuffer();
747            *idxBufferOffsetOut  = convertedOffset;
748        }
749    }
750
751    *indexTypeOut = type;
752    if (type == gl::DrawElementsType::UnsignedByte)
753    {
754        // This buffer is already converted to ushort indices above
755        *indexTypeOut = gl::DrawElementsType::UnsignedShort;
756    }
757
758    return angle::Result::Continue;
759}
760
761std::vector<DrawCommandRange> VertexArrayMtl::getDrawIndices(const gl::Context *glContext,
762                                                             gl::DrawElementsType originalIndexType,
763                                                             gl::DrawElementsType indexType,
764                                                             gl::PrimitiveMode primitiveMode,
765                                                             mtl::BufferRef clientBuffer,
766                                                             uint32_t indexCount,
767                                                             size_t offset)
768{
769    ContextMtl *contextMtl = mtl::GetImpl(glContext);
770    std::vector<DrawCommandRange> drawCommands;
771    // The indexed draw needs to be split to separate draw commands in case primitive restart is
772    // enabled and the drawn primitive supports primitive restart. Otherwise the whole indexed draw
773    // can be sent as one draw command.
774    bool isSimpleType = primitiveMode == gl::PrimitiveMode::Points ||
775                        primitiveMode == gl::PrimitiveMode::Lines ||
776                        primitiveMode == gl::PrimitiveMode::Triangles;
777    if (!isSimpleType || !glContext->getState().isPrimitiveRestartEnabled())
778    {
779        drawCommands.push_back({indexCount, offset});
780        return drawCommands;
781    }
782    const std::vector<IndexRange> *restartIndices;
783    std::vector<IndexRange> clientIndexRange;
784    const gl::Buffer *glElementArrayBuffer = getState().getElementArrayBuffer();
785    if (glElementArrayBuffer)
786    {
787        BufferMtl *idxBuffer = mtl::GetImpl(glElementArrayBuffer);
788        restartIndices       = &idxBuffer->getRestartIndices(contextMtl, originalIndexType);
789    }
790    else
791    {
792        clientIndexRange =
793            BufferMtl::getRestartIndicesFromClientData(contextMtl, indexType, clientBuffer);
794        restartIndices = &clientIndexRange;
795    }
796    // Reminder, offset is in bytes, not elements.
797    // Slice draw commands based off of indices.
798    uint32_t nIndicesPerPrimitive;
799    switch (primitiveMode)
800    {
801        case gl::PrimitiveMode::Points:
802            nIndicesPerPrimitive = 1;
803            break;
804        case gl::PrimitiveMode::Lines:
805            nIndicesPerPrimitive = 2;
806            break;
807        case gl::PrimitiveMode::Triangles:
808            nIndicesPerPrimitive = 3;
809            break;
810        default:
811            UNREACHABLE();
812            return drawCommands;
813    }
814    const GLuint indexTypeBytes = gl::GetDrawElementsTypeSize(indexType);
815    uint32_t indicesLeft        = indexCount;
816    size_t currentIndexOffset   = offset / indexTypeBytes;
817
818    for (auto &range : *restartIndices)
819    {
820        if (range.restartBegin > currentIndexOffset)
821        {
822            int64_t nIndicesInSlice =
823                MIN(((int64_t)range.restartBegin - currentIndexOffset) -
824                        ((int64_t)range.restartBegin - currentIndexOffset) % nIndicesPerPrimitive,
825                    indicesLeft);
826            size_t restartSize = (range.restartEnd - range.restartBegin) + 1;
827            if (nIndicesInSlice >= nIndicesPerPrimitive)
828            {
829                drawCommands.push_back(
830                    {(uint32_t)nIndicesInSlice, currentIndexOffset * indexTypeBytes});
831            }
832            // Account for dropped indices due to incomplete primitives.
833            size_t indicesUsed = ((range.restartBegin + restartSize) - currentIndexOffset);
834            if (indicesLeft <= indicesUsed)
835            {
836                indicesLeft = 0;
837            }
838            else
839            {
840                indicesLeft -= indicesUsed;
841            }
842            currentIndexOffset = (size_t)(range.restartBegin + restartSize);
843        }
844        // If the initial offset into the index buffer is within a restart zone, move to the end of
845        // the restart zone.
846        else if (range.restartEnd >= currentIndexOffset)
847        {
848            size_t restartSize = (range.restartEnd - currentIndexOffset) + 1;
849            if (indicesLeft <= restartSize)
850            {
851                indicesLeft = 0;
852            }
853            else
854            {
855                indicesLeft -= restartSize;
856            }
857            currentIndexOffset = (size_t)(currentIndexOffset + restartSize);
858        }
859    }
860    if (indicesLeft >= nIndicesPerPrimitive)
861        drawCommands.push_back({indicesLeft, currentIndexOffset * indexTypeBytes});
862    return drawCommands;
863}
864
865angle::Result VertexArrayMtl::convertIndexBuffer(const gl::Context *glContext,
866                                                 gl::DrawElementsType indexType,
867                                                 size_t offset,
868                                                 mtl::BufferRef *idxBufferOut,
869                                                 size_t *idxBufferOffsetOut)
870{
871    size_t offsetModulo = offset % mtl::kIndexBufferOffsetAlignment;
872    ASSERT(offsetModulo != 0 || indexType == gl::DrawElementsType::UnsignedByte);
873
874    size_t alignedOffset = offset - offsetModulo;
875    if (indexType == gl::DrawElementsType::UnsignedByte)
876    {
877        // Unsigned byte index will be promoted to unsigned short, thus double its offset.
878        alignedOffset = alignedOffset << 1;
879    }
880
881    ContextMtl *contextMtl   = mtl::GetImpl(glContext);
882    const gl::State &glState = glContext->getState();
883    BufferMtl *idxBuffer     = mtl::GetImpl(getState().getElementArrayBuffer());
884
885    IndexConversionBufferMtl *conversion = idxBuffer->getIndexConversionBuffer(
886        contextMtl, indexType, glState.isPrimitiveRestartEnabled(), offsetModulo);
887
888    // Has the content of the buffer has changed since last conversion?
889    if (!conversion->dirty)
890    {
891        // reuse the converted buffer
892        *idxBufferOut       = conversion->convertedBuffer;
893        *idxBufferOffsetOut = conversion->convertedOffset + alignedOffset;
894        return angle::Result::Continue;
895    }
896
897    size_t indexCount = GetIndexCount(idxBuffer, offsetModulo, indexType);
898    if ((!contextMtl->getDisplay()->getFeatures().hasCheapRenderPass.enabled &&
899         contextMtl->getRenderCommandEncoder()))
900    {
901        // We shouldn't use GPU to convert when we are in a middle of a render pass.
902        ANGLE_TRY(StreamIndexData(contextMtl, &conversion->data,
903                                  idxBuffer->getBufferDataReadOnly(contextMtl) + offsetModulo,
904                                  indexType, indexCount, glState.isPrimitiveRestartEnabled(),
905                                  &conversion->convertedBuffer, &conversion->convertedOffset));
906    }
907    else
908    {
909        ANGLE_TRY(convertIndexBufferGPU(glContext, indexType, idxBuffer, offsetModulo, indexCount,
910                                        conversion));
911    }
912    // Calculate ranges for prim restart simple types.
913    *idxBufferOut       = conversion->convertedBuffer;
914    *idxBufferOffsetOut = conversion->convertedOffset + alignedOffset;
915
916    return angle::Result::Continue;
917}
918
919angle::Result VertexArrayMtl::convertIndexBufferGPU(const gl::Context *glContext,
920                                                    gl::DrawElementsType indexType,
921                                                    BufferMtl *idxBuffer,
922                                                    size_t offset,
923                                                    size_t indexCount,
924                                                    IndexConversionBufferMtl *conversion)
925{
926    ContextMtl *contextMtl = mtl::GetImpl(glContext);
927    DisplayMtl *display    = contextMtl->getDisplay();
928
929    const size_t amount = GetIndexConvertedBufferSize(indexType, indexCount);
930
931    // Allocate new buffer, save it in conversion struct so that we can reuse it when the content
932    // of the original buffer is not dirty.
933    conversion->data.releaseInFlightBuffers(contextMtl);
934    ANGLE_TRY(conversion->data.allocate(contextMtl, amount, nullptr, &conversion->convertedBuffer,
935                                        &conversion->convertedOffset));
936
937    // Do the conversion on GPU.
938    ANGLE_TRY(display->getUtils().convertIndexBufferGPU(
939        contextMtl, {indexType, static_cast<uint32_t>(indexCount), idxBuffer->getCurrentBuffer(),
940                     static_cast<uint32_t>(offset), conversion->convertedBuffer,
941                     static_cast<uint32_t>(conversion->convertedOffset),
942                     glContext->getState().isPrimitiveRestartEnabled()}));
943
944    ANGLE_TRY(conversion->data.commit(contextMtl));
945
946    ASSERT(conversion->dirty);
947    conversion->dirty = false;
948
949    return angle::Result::Continue;
950}
951
952angle::Result VertexArrayMtl::streamIndexBufferFromClient(const gl::Context *context,
953                                                          gl::DrawElementsType indexType,
954                                                          size_t indexCount,
955                                                          const void *sourcePointer,
956                                                          mtl::BufferRef *idxBufferOut,
957                                                          size_t *idxBufferOffsetOut)
958{
959    ASSERT(getState().getElementArrayBuffer() == nullptr);
960    ContextMtl *contextMtl = mtl::GetImpl(context);
961
962    auto srcData = static_cast<const uint8_t *>(sourcePointer);
963    ANGLE_TRY(StreamIndexData(contextMtl, &mDynamicIndexData, srcData, indexType, indexCount,
964                              context->getState().isPrimitiveRestartEnabled(), idxBufferOut,
965                              idxBufferOffsetOut));
966
967    return angle::Result::Continue;
968}
969
970angle::Result VertexArrayMtl::convertVertexBuffer(const gl::Context *glContext,
971                                                  BufferMtl *srcBuffer,
972                                                  const gl::VertexBinding &binding,
973                                                  size_t attribIndex,
974                                                  const mtl::VertexFormat &srcVertexFormat)
975{
976    unsigned srcFormatSize = srcVertexFormat.intendedAngleFormat().pixelBytes;
977
978    size_t numVertices = GetVertexCount(srcBuffer, binding, srcFormatSize);
979    if (numVertices == 0)
980    {
981        // Out of bound buffer access, can return any values.
982        // See KHR_robust_buffer_access_behavior
983        mCurrentArrayBuffers[attribIndex]       = srcBuffer;
984        mCurrentArrayBufferFormats[attribIndex] = &srcVertexFormat;
985        mCurrentArrayBufferOffsets[attribIndex] = 0;
986        mCurrentArrayBufferStrides[attribIndex] = 16;
987        return angle::Result::Continue;
988    }
989
990    ContextMtl *contextMtl = mtl::GetImpl(glContext);
991
992    // Convert to tightly packed format
993    GLuint stride;
994    const mtl::VertexFormat &convertedFormat =
995        GetVertexConversionFormat(contextMtl, srcVertexFormat.intendedFormatId, &stride);
996
997    ConversionBufferMtl *conversion = srcBuffer->getVertexConversionBuffer(
998        contextMtl, srcVertexFormat.intendedFormatId, binding.getStride(), binding.getOffset());
999
1000    // Has the content of the buffer has changed since last conversion?
1001    if (!conversion->dirty)
1002    {
1003        VertexConversionBufferMtl *vertexConversionMtl =
1004            static_cast<VertexConversionBufferMtl *>(conversion);
1005        ASSERT((binding.getOffset() - vertexConversionMtl->offset) % binding.getStride() == 0);
1006        mConvertedArrayBufferHolders[attribIndex].set(conversion->convertedBuffer);
1007        mCurrentArrayBufferOffsets[attribIndex] =
1008            conversion->convertedOffset +
1009            stride * ((binding.getOffset() - vertexConversionMtl->offset) / binding.getStride());
1010
1011        mCurrentArrayBuffers[attribIndex]       = &mConvertedArrayBufferHolders[attribIndex];
1012        mCurrentArrayBufferFormats[attribIndex] = &convertedFormat;
1013        mCurrentArrayBufferStrides[attribIndex] = stride;
1014        return angle::Result::Continue;
1015    }
1016    numVertices = GetVertexCountWithConversion(
1017        srcBuffer, static_cast<VertexConversionBufferMtl *>(conversion), binding, srcFormatSize);
1018
1019    const angle::Format &convertedAngleFormat = convertedFormat.actualAngleFormat();
1020    bool canConvertToFloatOnGPU =
1021        convertedAngleFormat.isFloat() && !convertedAngleFormat.isVertexTypeHalfFloat();
1022
1023    bool canExpandComponentsOnGPU = convertedFormat.actualSameGLType;
1024
1025    if (contextMtl->getRenderCommandEncoder() &&
1026        !contextMtl->getDisplay()->getFeatures().hasCheapRenderPass.enabled &&
1027        !contextMtl->getDisplay()->getFeatures().hasExplicitMemBarrier.enabled)
1028    {
1029        // Cannot use GPU to convert when we are in a middle of a render pass.
1030        canConvertToFloatOnGPU = canExpandComponentsOnGPU = false;
1031    }
1032
1033    conversion->data.releaseInFlightBuffers(contextMtl);
1034    conversion->data.updateAlignment(contextMtl, convertedAngleFormat.pixelBytes);
1035
1036    if (canConvertToFloatOnGPU || canExpandComponentsOnGPU)
1037    {
1038        ANGLE_TRY(convertVertexBufferGPU(glContext, srcBuffer, binding, attribIndex,
1039                                         convertedFormat, stride, numVertices,
1040                                         canExpandComponentsOnGPU, conversion));
1041    }
1042    else
1043    {
1044        ANGLE_TRY(convertVertexBufferCPU(contextMtl, srcBuffer, binding, attribIndex,
1045                                         convertedFormat, stride, numVertices, conversion));
1046    }
1047
1048    mConvertedArrayBufferHolders[attribIndex].set(conversion->convertedBuffer);
1049    mCurrentArrayBufferOffsets[attribIndex] =
1050        conversion->convertedOffset +
1051        stride *
1052            ((binding.getOffset() - static_cast<VertexConversionBufferMtl *>(conversion)->offset) /
1053             binding.getStride());
1054    mCurrentArrayBuffers[attribIndex]       = &mConvertedArrayBufferHolders[attribIndex];
1055    mCurrentArrayBufferFormats[attribIndex] = &convertedFormat;
1056    mCurrentArrayBufferStrides[attribIndex] = stride;
1057
1058    ASSERT(conversion->dirty);
1059    conversion->dirty = false;
1060
1061#ifndef NDEBUG
1062    ANGLE_MTL_OBJC_SCOPE
1063    {
1064        mConvertedArrayBufferHolders[attribIndex].getCurrentBuffer()->get().label =
1065            [NSString stringWithFormat:@"Converted from %p offset=%zu stride=%u", srcBuffer,
1066                                       binding.getOffset(), binding.getStride()];
1067    }
1068#endif
1069
1070    return angle::Result::Continue;
1071}
1072
1073angle::Result VertexArrayMtl::convertVertexBufferCPU(ContextMtl *contextMtl,
1074                                                     BufferMtl *srcBuffer,
1075                                                     const gl::VertexBinding &binding,
1076                                                     size_t attribIndex,
1077                                                     const mtl::VertexFormat &convertedFormat,
1078                                                     GLuint targetStride,
1079                                                     size_t numVertices,
1080                                                     ConversionBufferMtl *conversion)
1081{
1082
1083    const uint8_t *srcBytes = srcBuffer->getBufferDataReadOnly(contextMtl);
1084    ANGLE_CHECK_GL_ALLOC(contextMtl, srcBytes);
1085    VertexConversionBufferMtl *vertexConverison =
1086        static_cast<VertexConversionBufferMtl *>(conversion);
1087    srcBytes += MIN(binding.getOffset(), static_cast<GLintptr>(vertexConverison->offset));
1088    SimpleWeakBufferHolderMtl conversionBufferHolder;
1089    ANGLE_TRY(StreamVertexData(contextMtl, &conversion->data, srcBytes, numVertices * targetStride,
1090                               0, numVertices, binding.getStride(),
1091                               convertedFormat.vertexLoadFunction, &conversionBufferHolder,
1092                               &conversion->convertedOffset));
1093    conversion->convertedBuffer = conversionBufferHolder.getCurrentBuffer();
1094    return angle::Result::Continue;
1095}
1096
1097angle::Result VertexArrayMtl::convertVertexBufferGPU(const gl::Context *glContext,
1098                                                     BufferMtl *srcBuffer,
1099                                                     const gl::VertexBinding &binding,
1100                                                     size_t attribIndex,
1101                                                     const mtl::VertexFormat &convertedFormat,
1102                                                     GLuint targetStride,
1103                                                     size_t numVertices,
1104                                                     bool isExpandingComponents,
1105                                                     ConversionBufferMtl *conversion)
1106{
1107    ContextMtl *contextMtl = mtl::GetImpl(glContext);
1108
1109    mtl::BufferRef newBuffer;
1110    size_t newBufferOffset;
1111    ANGLE_TRY(conversion->data.allocate(contextMtl, numVertices * targetStride, nullptr, &newBuffer,
1112                                        &newBufferOffset));
1113
1114    ANGLE_CHECK_GL_MATH(contextMtl, binding.getOffset() <= std::numeric_limits<uint32_t>::max());
1115    ANGLE_CHECK_GL_MATH(contextMtl, newBufferOffset <= std::numeric_limits<uint32_t>::max());
1116    ANGLE_CHECK_GL_MATH(contextMtl, numVertices <= std::numeric_limits<uint32_t>::max());
1117
1118    mtl::VertexFormatConvertParams params;
1119    VertexConversionBufferMtl *vertexConversion =
1120        static_cast<VertexConversionBufferMtl *>(conversion);
1121    params.srcBuffer            = srcBuffer->getCurrentBuffer();
1122    params.srcBufferStartOffset = static_cast<uint32_t>(
1123        MIN(static_cast<GLintptr>(vertexConversion->offset), binding.getOffset()));
1124    params.srcStride           = binding.getStride();
1125    params.srcDefaultAlphaData = convertedFormat.defaultAlpha;
1126
1127    params.dstBuffer            = newBuffer;
1128    params.dstBufferStartOffset = static_cast<uint32_t>(newBufferOffset);
1129    params.dstStride            = targetStride;
1130    params.dstComponents        = convertedFormat.actualAngleFormat().channelCount;
1131
1132    params.vertexCount = static_cast<uint32_t>(numVertices);
1133
1134    mtl::RenderUtils &utils                  = contextMtl->getDisplay()->getUtils();
1135    mtl::RenderCommandEncoder *renderEncoder = contextMtl->getRenderCommandEncoder();
1136    if (renderEncoder && contextMtl->getDisplay()->getFeatures().hasExplicitMemBarrier.enabled)
1137    {
1138        // If we are in the middle of a render pass, use vertex shader based buffer conversion to
1139        // avoid breaking the render pass.
1140        if (!isExpandingComponents)
1141        {
1142            ANGLE_TRY(utils.convertVertexFormatToFloatVS(
1143                glContext, renderEncoder, convertedFormat.intendedAngleFormat(), params));
1144        }
1145        else
1146        {
1147            ANGLE_TRY(utils.expandVertexFormatComponentsVS(
1148                glContext, renderEncoder, convertedFormat.intendedAngleFormat(), params));
1149        }
1150    }
1151    else
1152    {
1153        // Compute based buffer conversion.
1154        if (!isExpandingComponents)
1155        {
1156            ANGLE_TRY(utils.convertVertexFormatToFloatCS(
1157                contextMtl, convertedFormat.intendedAngleFormat(), params));
1158        }
1159        else
1160        {
1161            ANGLE_TRY(utils.expandVertexFormatComponentsCS(
1162                contextMtl, convertedFormat.intendedAngleFormat(), params));
1163        }
1164    }
1165
1166    ANGLE_TRY(conversion->data.commit(contextMtl));
1167
1168    conversion->convertedBuffer = newBuffer;
1169    conversion->convertedOffset = newBufferOffset;
1170
1171    return angle::Result::Continue;
1172}
1173}  // namespace rx
1174