• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1//
2// Copyright 2019 The ANGLE Project Authors. All rights reserved.
3// Use of this source code is governed by a BSD-style license that can be
4// found in the LICENSE file.
5//
6// BufferMtl.mm:
7//    Implements the class methods for BufferMtl.
8//
9
10#include "libANGLE/renderer/metal/BufferMtl.h"
11
12#include "common/debug.h"
13#include "common/utilities.h"
14#include "libANGLE/renderer/metal/ContextMtl.h"
15
16namespace rx
17{
18
19namespace
20{
21
22// Start with a fairly small buffer size. We can increase this dynamically as we convert more data.
23constexpr size_t kConvertedElementArrayBufferInitialSize = 1024 * 8;
24
25template <typename IndexType>
26angle::Result GetFirstLastIndices(const IndexType *indices,
27                                  size_t count,
28                                  std::pair<uint32_t, uint32_t> *outIndices)
29{
30    IndexType first, last;
31    // Use memcpy to avoid unaligned memory access crash:
32    memcpy(&first, &indices[0], sizeof(first));
33    memcpy(&last, &indices[count - 1], sizeof(last));
34
35    outIndices->first  = first;
36    outIndices->second = last;
37
38    return angle::Result::Continue;
39}
40
41}  // namespace
42
43// ConversionBufferMtl implementation.
44ConversionBufferMtl::ConversionBufferMtl(const gl::Context *context,
45                                         size_t initialSize,
46                                         size_t alignment)
47    : dirty(true), convertedBuffer(nullptr), convertedOffset(0)
48{
49    ContextMtl *contextMtl = mtl::GetImpl(context);
50    data.initialize(contextMtl, initialSize, alignment);
51}
52
53ConversionBufferMtl::~ConversionBufferMtl() = default;
54
55// IndexConversionBufferMtl implementation.
56IndexConversionBufferMtl::IndexConversionBufferMtl(const gl::Context *context,
57                                                   gl::DrawElementsType typeIn,
58                                                   size_t offsetIn)
59    : ConversionBufferMtl(context,
60                          kConvertedElementArrayBufferInitialSize,
61                          mtl::kIndexBufferOffsetAlignment),
62      type(typeIn),
63      offset(offsetIn)
64{}
65
66// BufferMtl::VertexConversionBuffer implementation.
67BufferMtl::VertexConversionBuffer::VertexConversionBuffer(const gl::Context *context,
68                                                          angle::FormatID formatIDIn,
69                                                          GLuint strideIn,
70                                                          size_t offsetIn)
71    : ConversionBufferMtl(context, 0, mtl::kVertexAttribBufferStrideAlignment),
72      formatID(formatIDIn),
73      stride(strideIn),
74      offset(offsetIn)
75{
76    // Due to Metal's strict requirement for offset and stride, we need to always allocate new
77    // buffer for every conversion.
78    data.setAlwaysAllocateNewBuffer(true);
79}
80
81// BufferMtl implementation
82BufferMtl::BufferMtl(const gl::BufferState &state)
83    : BufferImpl(state), mBufferPool(/** alwaysAllocNewBuffer */ true)
84{}
85
86BufferMtl::~BufferMtl() {}
87
88void BufferMtl::destroy(const gl::Context *context)
89{
90    ContextMtl *contextMtl = mtl::GetImpl(context);
91    mShadowCopy.clear();
92    mBufferPool.destroy(contextMtl);
93    mBuffer = nullptr;
94
95    clearConversionBuffers();
96}
97
98angle::Result BufferMtl::setData(const gl::Context *context,
99                                 gl::BufferBinding target,
100                                 const void *data,
101                                 size_t intendedSize,
102                                 gl::BufferUsage usage)
103{
104    ContextMtl *contextMtl = mtl::GetImpl(context);
105
106    // Invalidate conversion buffers
107    if (mState.getSize() != static_cast<GLint64>(intendedSize))
108    {
109        clearConversionBuffers();
110    }
111    else
112    {
113        markConversionBuffersDirty();
114    }
115
116    size_t adjustedSize = std::max<size_t>(1, intendedSize);
117
118    if (!mShadowCopy.size() || intendedSize > mShadowCopy.size() || usage != mState.getUsage())
119    {
120        // Re-create the buffer
121        ANGLE_MTL_CHECK(contextMtl, mShadowCopy.resize(adjustedSize), GL_OUT_OF_MEMORY);
122
123        size_t maxBuffers;
124        switch (usage)
125        {
126            case gl::BufferUsage::StaticCopy:
127            case gl::BufferUsage::StaticDraw:
128            case gl::BufferUsage::StaticRead:
129                maxBuffers = 1;  // static buffer doesn't need high speed data update
130                break;
131            default:
132                // dynamic buffer, allow up to 2 update per frame/encoding without
133                // waiting for GPU.
134                maxBuffers = 2;
135                break;
136        }
137
138        mBufferPool.initialize(contextMtl, adjustedSize, 1, maxBuffers);
139    }
140
141    // Transfer data to shadow copy buffer
142    if (data)
143    {
144        auto ptr = static_cast<const uint8_t *>(data);
145        std::copy(ptr, ptr + intendedSize, mShadowCopy.data());
146    }
147
148    // Transfer data from shadow copy buffer to GPU buffer.
149    return commitShadowCopy(context, adjustedSize);
150}
151
152angle::Result BufferMtl::setSubData(const gl::Context *context,
153                                    gl::BufferBinding target,
154                                    const void *data,
155                                    size_t size,
156                                    size_t offset)
157{
158    return setSubDataImpl(context, data, size, offset);
159}
160
161angle::Result BufferMtl::copySubData(const gl::Context *context,
162                                     BufferImpl *source,
163                                     GLintptr sourceOffset,
164                                     GLintptr destOffset,
165                                     GLsizeiptr size)
166{
167    if (!source)
168    {
169        return angle::Result::Continue;
170    }
171
172    ASSERT(mShadowCopy.size());
173
174    auto srcMtl = GetAs<BufferMtl>(source);
175
176    // NOTE(hqle): use blit command.
177    return setSubDataImpl(context, srcMtl->getClientShadowCopyData(context) + sourceOffset, size,
178                          destOffset);
179}
180
181angle::Result BufferMtl::map(const gl::Context *context, GLenum access, void **mapPtr)
182{
183    ASSERT(mShadowCopy.size());
184    return mapRange(context, 0, size(), 0, mapPtr);
185}
186
187angle::Result BufferMtl::mapRange(const gl::Context *context,
188                                  size_t offset,
189                                  size_t length,
190                                  GLbitfield access,
191                                  void **mapPtr)
192{
193    ASSERT(mShadowCopy.size());
194
195    // NOTE(hqle): use access flags
196    if (mapPtr)
197    {
198        *mapPtr = mShadowCopy.data() + offset;
199    }
200
201    return angle::Result::Continue;
202}
203
204angle::Result BufferMtl::unmap(const gl::Context *context, GLboolean *result)
205{
206    ASSERT(mShadowCopy.size());
207
208    markConversionBuffersDirty();
209
210    ANGLE_TRY(commitShadowCopy(context));
211
212    return angle::Result::Continue;
213}
214
215angle::Result BufferMtl::getIndexRange(const gl::Context *context,
216                                       gl::DrawElementsType type,
217                                       size_t offset,
218                                       size_t count,
219                                       bool primitiveRestartEnabled,
220                                       gl::IndexRange *outRange)
221{
222    ASSERT(mShadowCopy.size());
223
224    const uint8_t *indices = mShadowCopy.data() + offset;
225
226    *outRange = gl::ComputeIndexRange(type, indices, count, primitiveRestartEnabled);
227
228    return angle::Result::Continue;
229}
230
231angle::Result BufferMtl::getFirstLastIndices(const gl::Context *context,
232                                             gl::DrawElementsType type,
233                                             size_t offset,
234                                             size_t count,
235                                             std::pair<uint32_t, uint32_t> *outIndices) const
236{
237    ASSERT(mShadowCopy.size());
238
239    const uint8_t *indices = mShadowCopy.data() + offset;
240
241    switch (type)
242    {
243        case gl::DrawElementsType::UnsignedByte:
244            return GetFirstLastIndices(static_cast<const GLubyte *>(indices), count, outIndices);
245        case gl::DrawElementsType::UnsignedShort:
246            return GetFirstLastIndices(reinterpret_cast<const GLushort *>(indices), count,
247                                       outIndices);
248        case gl::DrawElementsType::UnsignedInt:
249            return GetFirstLastIndices(reinterpret_cast<const GLuint *>(indices), count,
250                                       outIndices);
251        default:
252            UNREACHABLE();
253            return angle::Result::Stop;
254    }
255
256    return angle::Result::Continue;
257}
258
259const uint8_t *BufferMtl::getClientShadowCopyData(const gl::Context *context)
260{
261    // NOTE(hqle): Support buffer update from GPU.
262    // Which mean we have to stall the GPU by calling finish and copy
263    // data back to shadow copy.
264    return mShadowCopy.data();
265}
266
267ConversionBufferMtl *BufferMtl::getVertexConversionBuffer(const gl::Context *context,
268                                                          angle::FormatID formatID,
269                                                          GLuint stride,
270                                                          size_t offset)
271{
272    for (VertexConversionBuffer &buffer : mVertexConversionBuffers)
273    {
274        if (buffer.formatID == formatID && buffer.stride == stride && buffer.offset == offset)
275        {
276            return &buffer;
277        }
278    }
279
280    mVertexConversionBuffers.emplace_back(context, formatID, stride, offset);
281    return &mVertexConversionBuffers.back();
282}
283
284IndexConversionBufferMtl *BufferMtl::getIndexConversionBuffer(const gl::Context *context,
285                                                              gl::DrawElementsType type,
286                                                              size_t offset)
287{
288    for (auto &buffer : mIndexConversionBuffers)
289    {
290        if (buffer.type == type && buffer.offset == offset)
291        {
292            return &buffer;
293        }
294    }
295
296    mIndexConversionBuffers.emplace_back(context, type, offset);
297    return &mIndexConversionBuffers.back();
298}
299
300void BufferMtl::markConversionBuffersDirty()
301{
302    for (VertexConversionBuffer &buffer : mVertexConversionBuffers)
303    {
304        buffer.dirty = true;
305    }
306
307    for (auto &buffer : mIndexConversionBuffers)
308    {
309        buffer.dirty           = true;
310        buffer.convertedBuffer = nullptr;
311        buffer.convertedOffset = 0;
312    }
313}
314
315void BufferMtl::clearConversionBuffers()
316{
317    mVertexConversionBuffers.clear();
318    mIndexConversionBuffers.clear();
319}
320
321angle::Result BufferMtl::setSubDataImpl(const gl::Context *context,
322                                        const void *data,
323                                        size_t size,
324                                        size_t offset)
325{
326    if (!data)
327    {
328        return angle::Result::Continue;
329    }
330    ContextMtl *contextMtl = mtl::GetImpl(context);
331
332    ASSERT(mShadowCopy.size());
333
334    ANGLE_MTL_TRY(contextMtl, offset <= this->size());
335
336    auto srcPtr     = static_cast<const uint8_t *>(data);
337    auto sizeToCopy = std::min<size_t>(size, this->size() - offset);
338    std::copy(srcPtr, srcPtr + sizeToCopy, mShadowCopy.data() + offset);
339
340    markConversionBuffersDirty();
341
342    ANGLE_TRY(commitShadowCopy(context));
343
344    return angle::Result::Continue;
345}
346
347angle::Result BufferMtl::commitShadowCopy(const gl::Context *context)
348{
349    return commitShadowCopy(context, size());
350}
351
352angle::Result BufferMtl::commitShadowCopy(const gl::Context *context, size_t size)
353{
354    ContextMtl *contextMtl = mtl::GetImpl(context);
355
356    uint8_t *ptr = nullptr;
357    ANGLE_TRY(mBufferPool.allocate(contextMtl, size, &ptr, &mBuffer, nullptr, nullptr));
358
359    std::copy(mShadowCopy.data(), mShadowCopy.data() + size, ptr);
360
361    ANGLE_TRY(mBufferPool.commit(contextMtl));
362
363#ifndef NDEBUG
364    ANGLE_MTL_OBJC_SCOPE { mBuffer->get().label = [NSString stringWithFormat:@"%p", this]; }
365#endif
366
367    return angle::Result::Continue;
368}
369
370// SimpleWeakBufferHolderMtl implementation
371SimpleWeakBufferHolderMtl::SimpleWeakBufferHolderMtl()
372{
373    mIsWeak = true;
374}
375
376}  // namespace rx
377