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