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