• 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/ErrorStrings.h"
15#include "libANGLE/renderer/metal/ContextMtl.h"
16#include "libANGLE/renderer/metal/DisplayMtl.h"
17#include "libANGLE/renderer/metal/mtl_buffer_manager.h"
18
19namespace rx
20{
21
22namespace
23{
24
25// Start with a fairly small buffer size. We can increase this dynamically as we convert more data.
26constexpr size_t kConvertedElementArrayBufferInitialSize = 1024 * 8;
27
28template <typename IndexType>
29angle::Result GetFirstLastIndices(const IndexType *indices,
30                                  size_t count,
31                                  std::pair<uint32_t, uint32_t> *outIndices)
32{
33    IndexType first, last;
34    // Use memcpy to avoid unaligned memory access crash:
35    memcpy(&first, &indices[0], sizeof(first));
36    memcpy(&last, &indices[count - 1], sizeof(last));
37
38    outIndices->first  = first;
39    outIndices->second = last;
40
41    return angle::Result::Continue;
42}
43
44bool isOffsetAndSizeMetalBlitCompatible(size_t offset, size_t size)
45{
46    // Metal requires offset and size to be multiples of 4
47    return offset % 4 == 0 && size % 4 == 0;
48}
49
50}  // namespace
51
52// ConversionBufferMtl implementation.
53ConversionBufferMtl::ConversionBufferMtl(ContextMtl *contextMtl,
54                                         size_t initialSize,
55                                         size_t alignment)
56    : dirty(true), convertedBuffer(nullptr), convertedOffset(0)
57{
58    data.initialize(contextMtl, initialSize, alignment, 0);
59}
60
61ConversionBufferMtl::~ConversionBufferMtl() = default;
62
63// IndexConversionBufferMtl implementation.
64IndexConversionBufferMtl::IndexConversionBufferMtl(ContextMtl *context,
65                                                   gl::DrawElementsType elemTypeIn,
66                                                   bool primitiveRestartEnabledIn,
67                                                   size_t offsetIn)
68    : ConversionBufferMtl(context,
69                          kConvertedElementArrayBufferInitialSize,
70                          mtl::kIndexBufferOffsetAlignment),
71      elemType(elemTypeIn),
72      offset(offsetIn),
73      primitiveRestartEnabled(primitiveRestartEnabledIn)
74{}
75
76IndexRange IndexConversionBufferMtl::getRangeForConvertedBuffer(size_t count)
77{
78    return IndexRange{0, count};
79}
80
81// UniformConversionBufferMtl implementation
82UniformConversionBufferMtl::UniformConversionBufferMtl(ContextMtl *context,
83                                                       std::pair<size_t, size_t> offsetIn,
84                                                       size_t uniformBufferBlockSize)
85    : ConversionBufferMtl(context, 0, mtl::kUniformBufferSettingOffsetMinAlignment),
86      uniformBufferBlockSize(uniformBufferBlockSize),
87      offset(offsetIn)
88{}
89
90// VertexConversionBufferMtl implementation.
91VertexConversionBufferMtl::VertexConversionBufferMtl(ContextMtl *context,
92                                                     angle::FormatID formatIDIn,
93                                                     GLuint strideIn,
94                                                     size_t offsetIn)
95    : ConversionBufferMtl(context, 0, mtl::kVertexAttribBufferStrideAlignment),
96      formatID(formatIDIn),
97      stride(strideIn),
98      offset(offsetIn)
99{}
100
101// BufferMtl implementation
102BufferMtl::BufferMtl(const gl::BufferState &state) : BufferImpl(state) {}
103
104BufferMtl::~BufferMtl() {}
105
106void BufferMtl::destroy(const gl::Context *context)
107{
108    ContextMtl *contextMtl = mtl::GetImpl(context);
109    mShadowCopy.clear();
110
111    // if there's a buffer, give it back to the buffer manager
112    if (mBuffer)
113    {
114        contextMtl->getBufferManager().returnBuffer(contextMtl, mBuffer);
115        mBuffer = nullptr;
116    }
117
118    clearConversionBuffers();
119}
120
121angle::Result BufferMtl::setData(const gl::Context *context,
122                                 gl::BufferBinding target,
123                                 const void *data,
124                                 size_t intendedSize,
125                                 gl::BufferUsage usage)
126{
127    return setDataImpl(context, target, data, intendedSize, usage);
128}
129
130angle::Result BufferMtl::setSubData(const gl::Context *context,
131                                    gl::BufferBinding target,
132                                    const void *data,
133                                    size_t size,
134                                    size_t offset)
135{
136    return setSubDataImpl(context, data, size, offset);
137}
138
139angle::Result BufferMtl::copySubData(const gl::Context *context,
140                                     BufferImpl *source,
141                                     GLintptr sourceOffset,
142                                     GLintptr destOffset,
143                                     GLsizeiptr size)
144{
145    if (!source)
146    {
147        return angle::Result::Continue;
148    }
149
150    ContextMtl *contextMtl = mtl::GetImpl(context);
151    auto srcMtl            = GetAs<BufferMtl>(source);
152
153    markConversionBuffersDirty();
154
155    if (mShadowCopy.size() > 0)
156    {
157        if (srcMtl->clientShadowCopyDataNeedSync(contextMtl) ||
158            mBuffer->isBeingUsedByGPU(contextMtl))
159        {
160            // If shadow copy requires a synchronization then use blit command instead.
161            // It might break a pending render pass, but still faster than synchronization with
162            // GPU.
163            mtl::BlitCommandEncoder *blitEncoder = contextMtl->getBlitCommandEncoder();
164            blitEncoder->copyBuffer(srcMtl->getCurrentBuffer(), sourceOffset, mBuffer, destOffset,
165                                    size);
166
167            return angle::Result::Continue;
168        }
169        return setSubDataImpl(context, srcMtl->getBufferDataReadOnly(contextMtl) + sourceOffset,
170                              size, destOffset);
171    }
172
173    mtl::BlitCommandEncoder *blitEncoder = contextMtl->getBlitCommandEncoder();
174    blitEncoder->copyBuffer(srcMtl->getCurrentBuffer(), sourceOffset, mBuffer, destOffset, size);
175
176    return angle::Result::Continue;
177}
178
179angle::Result BufferMtl::map(const gl::Context *context, GLenum access, void **mapPtr)
180{
181    GLbitfield mapRangeAccess = 0;
182    if ((access & GL_WRITE_ONLY_OES) != 0 || (access & GL_READ_WRITE) != 0)
183    {
184        mapRangeAccess |= GL_MAP_WRITE_BIT;
185    }
186    return mapRange(context, 0, size(), mapRangeAccess, mapPtr);
187}
188
189angle::Result BufferMtl::mapRange(const gl::Context *context,
190                                  size_t offset,
191                                  size_t length,
192                                  GLbitfield access,
193                                  void **mapPtr)
194{
195    if (access & GL_MAP_INVALIDATE_BUFFER_BIT)
196    {
197        ANGLE_TRY(setDataImpl(context, gl::BufferBinding::InvalidEnum, nullptr, size(),
198                              mState.getUsage()));
199    }
200
201    if (mapPtr)
202    {
203        ContextMtl *contextMtl = mtl::GetImpl(context);
204        if (mShadowCopy.size() == 0)
205        {
206            *mapPtr = mBuffer->mapWithOpt(contextMtl, (access & GL_MAP_WRITE_BIT) == 0,
207                                          access & GL_MAP_UNSYNCHRONIZED_BIT) +
208                      offset;
209        }
210        else
211        {
212            *mapPtr = syncAndObtainShadowCopy(contextMtl) + offset;
213        }
214    }
215
216    return angle::Result::Continue;
217}
218
219angle::Result BufferMtl::unmap(const gl::Context *context, GLboolean *result)
220{
221    ContextMtl *contextMtl = mtl::GetImpl(context);
222    size_t offset          = static_cast<size_t>(mState.getMapOffset());
223    size_t len             = static_cast<size_t>(mState.getMapLength());
224
225    markConversionBuffersDirty();
226
227    if (mShadowCopy.size() == 0)
228    {
229        ASSERT(mBuffer);
230        if (mState.getAccessFlags() & GL_MAP_WRITE_BIT)
231        {
232            mBuffer->unmapAndFlushSubset(contextMtl, offset, len);
233        }
234        else
235        {
236            // Buffer is already mapped with readonly flag, so just unmap it, no flushing will
237            // occur.
238            mBuffer->unmap(contextMtl);
239        }
240    }
241    else
242    {
243        if (mState.getAccessFlags() & GL_MAP_UNSYNCHRONIZED_BIT)
244        {
245            // Copy the mapped region without synchronization with GPU
246            uint8_t *ptr =
247                mBuffer->mapWithOpt(contextMtl, /* readonly */ false, /* noSync */ true) + offset;
248            std::copy(mShadowCopy.data() + offset, mShadowCopy.data() + offset + len, ptr);
249            mBuffer->unmapAndFlushSubset(contextMtl, offset, len);
250        }
251        else
252        {
253            // commit shadow copy data to GPU synchronously
254            ANGLE_TRY(commitShadowCopy(contextMtl));
255        }
256    }
257
258    if (result)
259    {
260        *result = true;
261    }
262
263    return angle::Result::Continue;
264}
265
266angle::Result BufferMtl::getIndexRange(const gl::Context *context,
267                                       gl::DrawElementsType type,
268                                       size_t offset,
269                                       size_t count,
270                                       bool primitiveRestartEnabled,
271                                       gl::IndexRange *outRange)
272{
273    const uint8_t *indices = getBufferDataReadOnly(mtl::GetImpl(context)) + offset;
274
275    *outRange = gl::ComputeIndexRange(type, indices, count, primitiveRestartEnabled);
276
277    return angle::Result::Continue;
278}
279
280angle::Result BufferMtl::getFirstLastIndices(ContextMtl *contextMtl,
281                                             gl::DrawElementsType type,
282                                             size_t offset,
283                                             size_t count,
284                                             std::pair<uint32_t, uint32_t> *outIndices)
285{
286    const uint8_t *indices = getBufferDataReadOnly(contextMtl) + offset;
287
288    switch (type)
289    {
290        case gl::DrawElementsType::UnsignedByte:
291            return GetFirstLastIndices(static_cast<const GLubyte *>(indices), count, outIndices);
292        case gl::DrawElementsType::UnsignedShort:
293            return GetFirstLastIndices(reinterpret_cast<const GLushort *>(indices), count,
294                                       outIndices);
295        case gl::DrawElementsType::UnsignedInt:
296            return GetFirstLastIndices(reinterpret_cast<const GLuint *>(indices), count,
297                                       outIndices);
298        default:
299            UNREACHABLE();
300            return angle::Result::Stop;
301    }
302}
303
304void BufferMtl::onDataChanged()
305{
306    markConversionBuffersDirty();
307}
308
309const uint8_t *BufferMtl::getBufferDataReadOnly(ContextMtl *contextMtl)
310{
311    if (mShadowCopy.size() == 0)
312    {
313        // Don't need shadow copy in this case, use the buffer directly
314        return mBuffer->mapReadOnly(contextMtl);
315    }
316    return syncAndObtainShadowCopy(contextMtl);
317}
318
319bool BufferMtl::clientShadowCopyDataNeedSync(ContextMtl *contextMtl)
320{
321    return mBuffer->isCPUReadMemDirty();
322}
323
324void BufferMtl::ensureShadowCopySyncedFromGPU(ContextMtl *contextMtl)
325{
326    if (mBuffer->isCPUReadMemDirty())
327    {
328        const uint8_t *ptr = mBuffer->mapReadOnly(contextMtl);
329        memcpy(mShadowCopy.data(), ptr, size());
330        mBuffer->unmap(contextMtl);
331
332        mBuffer->resetCPUReadMemDirty();
333    }
334}
335uint8_t *BufferMtl::syncAndObtainShadowCopy(ContextMtl *contextMtl)
336{
337    ASSERT(mShadowCopy.size());
338
339    ensureShadowCopySyncedFromGPU(contextMtl);
340
341    return mShadowCopy.data();
342}
343
344ConversionBufferMtl *BufferMtl::getVertexConversionBuffer(ContextMtl *context,
345                                                          angle::FormatID formatID,
346                                                          GLuint stride,
347                                                          size_t offset)
348{
349    for (VertexConversionBufferMtl &buffer : mVertexConversionBuffers)
350    {
351        if (buffer.formatID == formatID && buffer.stride == stride && buffer.offset <= offset &&
352            buffer.offset % buffer.stride == offset % stride)
353        {
354            return &buffer;
355        }
356    }
357
358    mVertexConversionBuffers.emplace_back(context, formatID, stride, offset);
359    ConversionBufferMtl *conv        = &mVertexConversionBuffers.back();
360    const angle::Format &angleFormat = angle::Format::Get(formatID);
361    conv->data.updateAlignment(context, angleFormat.pixelBytes);
362
363    return conv;
364}
365
366IndexConversionBufferMtl *BufferMtl::getIndexConversionBuffer(ContextMtl *context,
367                                                              gl::DrawElementsType elemType,
368                                                              bool primitiveRestartEnabled,
369                                                              size_t offset)
370{
371    for (auto &buffer : mIndexConversionBuffers)
372    {
373        if (buffer.elemType == elemType && buffer.offset == offset &&
374            buffer.primitiveRestartEnabled == primitiveRestartEnabled)
375        {
376            return &buffer;
377        }
378    }
379
380    mIndexConversionBuffers.emplace_back(context, elemType, primitiveRestartEnabled, offset);
381    return &mIndexConversionBuffers.back();
382}
383
384ConversionBufferMtl *BufferMtl::getUniformConversionBuffer(ContextMtl *context,
385                                                           std::pair<size_t, size_t> offset,
386                                                           size_t stdSize)
387{
388    for (UniformConversionBufferMtl &buffer : mUniformConversionBuffers)
389    {
390        if (buffer.offset.first == offset.first && buffer.uniformBufferBlockSize == stdSize)
391        {
392            if (buffer.offset.second <= offset.second &&
393                (offset.second - buffer.offset.second) % buffer.uniformBufferBlockSize == 0)
394                return &buffer;
395        }
396    }
397
398    mUniformConversionBuffers.emplace_back(context, offset, stdSize);
399    return &mUniformConversionBuffers.back();
400}
401
402void BufferMtl::markConversionBuffersDirty()
403{
404    for (VertexConversionBufferMtl &buffer : mVertexConversionBuffers)
405    {
406        buffer.dirty           = true;
407        buffer.convertedBuffer = nullptr;
408        buffer.convertedOffset = 0;
409    }
410
411    for (IndexConversionBufferMtl &buffer : mIndexConversionBuffers)
412    {
413        buffer.dirty           = true;
414        buffer.convertedBuffer = nullptr;
415        buffer.convertedOffset = 0;
416    }
417
418    for (UniformConversionBufferMtl &buffer : mUniformConversionBuffers)
419    {
420        buffer.dirty           = true;
421        buffer.convertedBuffer = nullptr;
422        buffer.convertedOffset = 0;
423    }
424    mRestartRangeCache.reset();
425}
426
427void BufferMtl::clearConversionBuffers()
428{
429    mVertexConversionBuffers.clear();
430    mIndexConversionBuffers.clear();
431    mUniformConversionBuffers.clear();
432    mRestartRangeCache.reset();
433}
434
435template <typename T>
436static std::vector<IndexRange> calculateRestartRanges(ContextMtl *ctx, mtl::BufferRef idxBuffer)
437{
438    std::vector<IndexRange> result;
439    const T *bufferData       = reinterpret_cast<const T *>(idxBuffer->mapReadOnly(ctx));
440    const size_t numIndices   = idxBuffer->size() / sizeof(T);
441    constexpr T restartMarker = std::numeric_limits<T>::max();
442    for (size_t i = 0; i < numIndices; ++i)
443    {
444        // Find the start of the restart range, i.e. first index with value of restart marker.
445        if (bufferData[i] != restartMarker)
446            continue;
447        size_t restartBegin = i;
448        // Find the end of the restart range, i.e. last index with value of restart marker.
449        do
450        {
451            ++i;
452        } while (i < numIndices && bufferData[i] == restartMarker);
453        result.emplace_back(restartBegin, i - 1);
454    }
455    idxBuffer->unmap(ctx);
456    return result;
457}
458
459const std::vector<IndexRange> &BufferMtl::getRestartIndices(ContextMtl *ctx,
460                                                            gl::DrawElementsType indexType)
461{
462    if (!mRestartRangeCache || mRestartRangeCache->indexType != indexType)
463    {
464        mRestartRangeCache.reset();
465        std::vector<IndexRange> ranges;
466        switch (indexType)
467        {
468            case gl::DrawElementsType::UnsignedByte:
469                ranges = calculateRestartRanges<uint8_t>(ctx, getCurrentBuffer());
470                break;
471            case gl::DrawElementsType::UnsignedShort:
472                ranges = calculateRestartRanges<uint16_t>(ctx, getCurrentBuffer());
473                break;
474            case gl::DrawElementsType::UnsignedInt:
475                ranges = calculateRestartRanges<uint32_t>(ctx, getCurrentBuffer());
476                break;
477            default:
478                ASSERT(false);
479        }
480        mRestartRangeCache.emplace(std::move(ranges), indexType);
481    }
482    return mRestartRangeCache->ranges;
483}
484
485const std::vector<IndexRange> BufferMtl::getRestartIndicesFromClientData(
486    ContextMtl *ctx,
487    gl::DrawElementsType indexType,
488    mtl::BufferRef idxBuffer)
489{
490    std::vector<IndexRange> restartIndices;
491    switch (indexType)
492    {
493        case gl::DrawElementsType::UnsignedByte:
494            restartIndices = calculateRestartRanges<uint8_t>(ctx, idxBuffer);
495            break;
496        case gl::DrawElementsType::UnsignedShort:
497            restartIndices = calculateRestartRanges<uint16_t>(ctx, idxBuffer);
498            break;
499        case gl::DrawElementsType::UnsignedInt:
500            restartIndices = calculateRestartRanges<uint32_t>(ctx, idxBuffer);
501            break;
502        default:
503            ASSERT(false);
504    }
505    return restartIndices;
506}
507
508angle::Result BufferMtl::allocateNewMetalBuffer(ContextMtl *contextMtl,
509                                                MTLStorageMode storageMode,
510                                                size_t size,
511                                                bool returnOldBufferImmediately)
512{
513    mtl::BufferManager &bufferManager = contextMtl->getBufferManager();
514    if (returnOldBufferImmediately && mBuffer)
515    {
516        // Return the current buffer to the buffer manager
517        // It will not be re-used until it's no longer in use.
518        bufferManager.returnBuffer(contextMtl, mBuffer);
519        mBuffer = nullptr;
520    }
521    ANGLE_TRY(bufferManager.getBuffer(contextMtl, storageMode, size, mBuffer));
522
523    onStateChange(angle::SubjectMessage::InternalMemoryAllocationChanged);
524
525    return angle::Result::Continue;
526}
527
528angle::Result BufferMtl::setDataImpl(const gl::Context *context,
529                                     gl::BufferBinding target,
530                                     const void *data,
531                                     size_t intendedSize,
532                                     gl::BufferUsage usage)
533{
534    ContextMtl *contextMtl             = mtl::GetImpl(context);
535    const angle::FeaturesMtl &features = contextMtl->getDisplay()->getFeatures();
536
537    // Invalidate conversion buffers
538    if (mState.getSize() != static_cast<GLint64>(intendedSize))
539    {
540        clearConversionBuffers();
541    }
542    else
543    {
544        markConversionBuffersDirty();
545    }
546
547    mUsage              = usage;
548    mGLSize             = intendedSize;
549    size_t adjustedSize = std::max<size_t>(1, intendedSize);
550
551    // Ensures no validation layer issues in std140 with data types like vec3 being 12 bytes vs 16
552    // in MSL.
553    if (target == gl::BufferBinding::Uniform)
554    {
555        // This doesn't work! A buffer can be allocated on ARRAY_BUFFER and used in UNIFORM_BUFFER
556        // TODO(anglebug.com/42266052)
557        adjustedSize = roundUpPow2(adjustedSize, (size_t)16);
558    }
559
560    // Re-create the buffer
561    auto storageMode = mtl::Buffer::getStorageModeForUsage(contextMtl, usage);
562    ANGLE_TRY(allocateNewMetalBuffer(contextMtl, storageMode, adjustedSize,
563                                     /*returnOldBufferImmediately=*/true));
564
565#ifndef NDEBUG
566    ANGLE_MTL_OBJC_SCOPE
567    {
568        mBuffer->get().label = [NSString stringWithFormat:@"BufferMtl=%p", this];
569    }
570#endif
571
572    // We may use shadow copy to maintain consistent data between buffers in pool
573    size_t shadowSize = (!features.preferCpuForBuffersubdata.enabled &&
574                         features.useShadowBuffersWhenAppropriate.enabled &&
575                         adjustedSize <= mtl::kSharedMemBufferMaxBufSizeHint)
576                            ? adjustedSize
577                            : 0;
578    ANGLE_CHECK_GL_ALLOC(contextMtl, mShadowCopy.resize(shadowSize));
579
580    if (data)
581    {
582        ANGLE_TRY(setSubDataImpl(context, data, intendedSize, 0));
583    }
584
585    return angle::Result::Continue;
586}
587
588// states:
589//  * The buffer is not use
590//
591//    safe = true
592//
593//  * The buffer has a pending blit
594//
595//    In this case, as long as we are only reading from it
596//    via blit to a new buffer our blits will happen after existing
597//    blits
598//
599//    safe = true
600//
601//  * The buffer has pending writes in a commited render encoder
602//
603//    In this case we're encoding commands that will happen after
604//    that encoder
605//
606//    safe = true
607//
608//  * The buffer has pending writes in the current render encoder
609//
610//    in this case we have to split/end the render encoder
611//    before we can use the buffer.
612//
613//    safe = false
614bool BufferMtl::isSafeToReadFromBufferViaBlit(ContextMtl *contextMtl)
615{
616    uint64_t serial   = mBuffer->getLastWritingRenderEncoderSerial();
617    bool isSameSerial = contextMtl->isCurrentRenderEncoderSerial(serial);
618    return !isSameSerial;
619}
620
621angle::Result BufferMtl::updateExistingBufferViaBlitFromStagingBuffer(ContextMtl *contextMtl,
622                                                                      const uint8_t *srcPtr,
623                                                                      size_t sizeToCopy,
624                                                                      size_t offset)
625{
626    ASSERT(isOffsetAndSizeMetalBlitCompatible(offset, sizeToCopy));
627
628    mtl::BufferManager &bufferManager = contextMtl->getBufferManager();
629    return bufferManager.queueBlitCopyDataToBuffer(contextMtl, srcPtr, sizeToCopy, offset, mBuffer);
630}
631
632// * get a new or unused buffer
633// * copy the new data to it
634// * copy any old data not overwriten by the new data to the new buffer
635// * start using the new buffer
636angle::Result BufferMtl::putDataInNewBufferAndStartUsingNewBuffer(ContextMtl *contextMtl,
637                                                                  const uint8_t *srcPtr,
638                                                                  size_t sizeToCopy,
639                                                                  size_t offset)
640{
641    ASSERT(isOffsetAndSizeMetalBlitCompatible(offset, sizeToCopy));
642
643    mtl::BufferRef oldBuffer = mBuffer;
644    auto storageMode         = mtl::Buffer::getStorageModeForUsage(contextMtl, mUsage);
645
646    ANGLE_TRY(allocateNewMetalBuffer(contextMtl, storageMode, mGLSize,
647                                     /*returnOldBufferImmediately=*/false));
648    mBuffer->get().label = [NSString stringWithFormat:@"BufferMtl=%p(%lu)", this, ++mRevisionCount];
649
650    uint8_t *ptr = mBuffer->mapWithOpt(contextMtl, false, true);
651    std::copy(srcPtr, srcPtr + sizeToCopy, ptr + offset);
652    mBuffer->unmapAndFlushSubset(contextMtl, offset, sizeToCopy);
653
654    if (offset > 0 || offset + sizeToCopy < mGLSize)
655    {
656        mtl::BlitCommandEncoder *blitEncoder =
657            contextMtl->getBlitCommandEncoderWithoutEndingRenderEncoder();
658        if (offset > 0)
659        {
660            // copy old data before updated region
661            blitEncoder->copyBuffer(oldBuffer, 0, mBuffer, 0, offset);
662        }
663        if (offset + sizeToCopy < mGLSize)
664        {
665            // copy old data after updated region
666            const size_t endOffset     = offset + sizeToCopy;
667            const size_t endSizeToCopy = mGLSize - endOffset;
668            blitEncoder->copyBuffer(oldBuffer, endOffset, mBuffer, endOffset, endSizeToCopy);
669        }
670    }
671
672    mtl::BufferManager &bufferManager = contextMtl->getBufferManager();
673    bufferManager.returnBuffer(contextMtl, oldBuffer);
674    return angle::Result::Continue;
675}
676
677angle::Result BufferMtl::copyDataToExistingBufferViaCPU(ContextMtl *contextMtl,
678                                                        const uint8_t *srcPtr,
679                                                        size_t sizeToCopy,
680                                                        size_t offset)
681{
682    uint8_t *ptr = mBuffer->map(contextMtl);
683    std::copy(srcPtr, srcPtr + sizeToCopy, ptr + offset);
684    mBuffer->unmapAndFlushSubset(contextMtl, offset, sizeToCopy);
685    return angle::Result::Continue;
686}
687
688angle::Result BufferMtl::updateShadowCopyThenCopyShadowToNewBuffer(ContextMtl *contextMtl,
689                                                                   const uint8_t *srcPtr,
690                                                                   size_t sizeToCopy,
691                                                                   size_t offset)
692{
693    // 1. Before copying data from client, we need to synchronize modified data from GPU to
694    // shadow copy first.
695    ensureShadowCopySyncedFromGPU(contextMtl);
696
697    // 2. Copy data from client to shadow copy.
698    std::copy(srcPtr, srcPtr + sizeToCopy, mShadowCopy.data() + offset);
699
700    // 3. Copy data from shadow copy to GPU.
701    return commitShadowCopy(contextMtl);
702}
703
704angle::Result BufferMtl::setSubDataImpl(const gl::Context *context,
705                                        const void *data,
706                                        size_t size,
707                                        size_t offset)
708{
709    if (!data)
710    {
711        return angle::Result::Continue;
712    }
713
714    ASSERT(mBuffer);
715
716    ContextMtl *contextMtl             = mtl::GetImpl(context);
717    const angle::FeaturesMtl &features = contextMtl->getDisplay()->getFeatures();
718
719    ANGLE_CHECK(contextMtl, offset <= mGLSize, gl::err::kInternalError, GL_INVALID_OPERATION);
720
721    auto srcPtr     = static_cast<const uint8_t *>(data);
722    auto sizeToCopy = std::min<size_t>(size, mGLSize - offset);
723
724    markConversionBuffersDirty();
725
726    if (features.preferCpuForBuffersubdata.enabled)
727    {
728        return copyDataToExistingBufferViaCPU(contextMtl, srcPtr, sizeToCopy, offset);
729    }
730
731    if (mShadowCopy.size() > 0)
732    {
733        return updateShadowCopyThenCopyShadowToNewBuffer(contextMtl, srcPtr, sizeToCopy, offset);
734    }
735    else
736    {
737        bool alwaysUseStagedBufferUpdates = features.alwaysUseStagedBufferUpdates.enabled;
738
739        if (isOffsetAndSizeMetalBlitCompatible(offset, size) &&
740            (alwaysUseStagedBufferUpdates || mBuffer->isBeingUsedByGPU(contextMtl)))
741        {
742            if (alwaysUseStagedBufferUpdates || !isSafeToReadFromBufferViaBlit(contextMtl))
743            {
744                // We can't use the buffer now so copy the data
745                // to a staging buffer and blit it in
746                return updateExistingBufferViaBlitFromStagingBuffer(contextMtl, srcPtr, sizeToCopy,
747                                                                    offset);
748            }
749            else
750            {
751                return putDataInNewBufferAndStartUsingNewBuffer(contextMtl, srcPtr, sizeToCopy,
752                                                                offset);
753            }
754        }
755        else
756        {
757            return copyDataToExistingBufferViaCPU(contextMtl, srcPtr, sizeToCopy, offset);
758        }
759    }
760}
761
762angle::Result BufferMtl::commitShadowCopy(ContextMtl *contextMtl)
763{
764    return commitShadowCopy(contextMtl, mGLSize);
765}
766
767angle::Result BufferMtl::commitShadowCopy(ContextMtl *contextMtl, size_t size)
768{
769    auto storageMode = mtl::Buffer::getStorageModeForUsage(contextMtl, mUsage);
770
771    size_t bufferSize = (mGLSize == 0 ? mShadowCopy.size() : mGLSize);
772    ANGLE_TRY(allocateNewMetalBuffer(contextMtl, storageMode, bufferSize,
773                                     /*returnOldBufferImmediately=*/true));
774
775    if (size)
776    {
777        uint8_t *ptr = mBuffer->mapWithOpt(contextMtl, false, true);
778        std::copy(mShadowCopy.data(), mShadowCopy.data() + size, ptr);
779        mBuffer->unmapAndFlushSubset(contextMtl, 0, size);
780    }
781
782    return angle::Result::Continue;
783}
784
785// SimpleWeakBufferHolderMtl implementation
786SimpleWeakBufferHolderMtl::SimpleWeakBufferHolderMtl()
787{
788    mIsWeak = true;
789}
790
791}  // namespace rx
792