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