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// VertexArrayMtl.mm: 7// Implements the class methods for VertexArrayMtl. 8// 9 10#include "libANGLE/renderer/metal/VertexArrayMtl.h" 11#include "libANGLE/renderer/metal/BufferMtl.h" 12#include "libANGLE/renderer/metal/ContextMtl.h" 13#include "libANGLE/renderer/metal/DisplayMtl.h" 14#include "libANGLE/renderer/metal/mtl_format_utils.h" 15 16#include "common/debug.h" 17 18namespace rx 19{ 20namespace 21{ 22constexpr size_t kDynamicIndexDataSize = 1024 * 8; 23 24angle::Result StreamVertexData(ContextMtl *contextMtl, 25 mtl::BufferPool *dynamicBuffer, 26 const uint8_t *sourceData, 27 size_t bytesToAllocate, 28 size_t destOffset, 29 size_t vertexCount, 30 size_t stride, 31 VertexCopyFunction vertexLoadFunction, 32 SimpleWeakBufferHolderMtl *bufferHolder, 33 size_t *bufferOffsetOut) 34{ 35 ANGLE_CHECK(contextMtl, vertexLoadFunction, "Unsupported format conversion", GL_INVALID_ENUM); 36 uint8_t *dst = nullptr; 37 mtl::BufferRef newBuffer; 38 ANGLE_TRY(dynamicBuffer->allocate(contextMtl, bytesToAllocate, &dst, &newBuffer, 39 bufferOffsetOut, nullptr)); 40 bufferHolder->set(newBuffer); 41 dst += destOffset; 42 vertexLoadFunction(sourceData, stride, vertexCount, dst); 43 44 ANGLE_TRY(dynamicBuffer->commit(contextMtl)); 45 return angle::Result::Continue; 46} 47 48template <typename SizeT> 49const mtl::VertexFormat &GetVertexConversionFormat(ContextMtl *contextMtl, 50 angle::FormatID originalFormat, 51 SizeT *strideOut) 52{ 53 // Convert to tightly packed format 54 const mtl::VertexFormat &packedFormat = contextMtl->getVertexFormat(originalFormat, true); 55 *strideOut = packedFormat.actualAngleFormat().pixelBytes; 56 return packedFormat; 57} 58 59size_t GetIndexConvertedBufferSize(gl::DrawElementsType indexType, size_t indexCount) 60{ 61 size_t elementSize = gl::GetDrawElementsTypeSize(indexType); 62 if (indexType == gl::DrawElementsType::UnsignedByte) 63 { 64 // 8-bit indices are not supported by Metal, so they are promoted to 65 // 16-bit indices below 66 elementSize = sizeof(GLushort); 67 } 68 69 const size_t amount = elementSize * indexCount; 70 71 return amount; 72} 73 74angle::Result StreamIndexData(ContextMtl *contextMtl, 75 mtl::BufferPool *dynamicBuffer, 76 const uint8_t *sourcePointer, 77 gl::DrawElementsType indexType, 78 size_t indexCount, 79 mtl::BufferRef *bufferOut, 80 size_t *bufferOffsetOut) 81{ 82 dynamicBuffer->releaseInFlightBuffers(contextMtl); 83 84 const size_t amount = GetIndexConvertedBufferSize(indexType, indexCount); 85 GLubyte *dst = nullptr; 86 87 ANGLE_TRY( 88 dynamicBuffer->allocate(contextMtl, amount, &dst, bufferOut, bufferOffsetOut, nullptr)); 89 90 if (indexType == gl::DrawElementsType::UnsignedByte) 91 { 92 // Unsigned bytes don't have direct support in Metal so we have to expand the 93 // memory to a GLushort. 94 const GLubyte *in = static_cast<const GLubyte *>(sourcePointer); 95 GLushort *expandedDst = reinterpret_cast<GLushort *>(dst); 96 97 // NOTE(hqle): May need to handle primitive restart index in future when ES 3.0 98 // is supported. 99 // Fast path for common case. 100 for (size_t index = 0; index < indexCount; index++) 101 { 102 expandedDst[index] = static_cast<GLushort>(in[index]); 103 } 104 } 105 else 106 { 107 memcpy(dst, sourcePointer, amount); 108 } 109 ANGLE_TRY(dynamicBuffer->commit(contextMtl)); 110 111 return angle::Result::Continue; 112} 113 114size_t GetVertexCount(BufferMtl *srcBuffer, 115 const gl::VertexBinding &binding, 116 uint32_t srcFormatSize) 117{ 118 // Bytes usable for vertex data. 119 GLint64 bytes = srcBuffer->size() - binding.getOffset(); 120 if (bytes < srcFormatSize) 121 return 0; 122 123 // Count the last vertex. It may occupy less than a full stride. 124 size_t numVertices = 1; 125 bytes -= srcFormatSize; 126 127 // Count how many strides fit remaining space. 128 if (bytes > 0) 129 numVertices += static_cast<size_t>(bytes) / binding.getStride(); 130 131 return numVertices; 132} 133 134inline size_t GetIndexCount(BufferMtl *srcBuffer, size_t offset, gl::DrawElementsType indexType) 135{ 136 size_t elementSize = gl::GetDrawElementsTypeSize(indexType); 137 return (srcBuffer->size() - offset) / elementSize; 138} 139 140inline void SetDefaultVertexBufferLayout(mtl::VertexBufferLayoutDesc *layout) 141{ 142 layout->stepFunction = MTLVertexStepFunctionConstant; 143 layout->stepRate = 0; 144 layout->stride = 0; 145} 146 147} // namespace 148 149// VertexArrayMtl implementation 150VertexArrayMtl::VertexArrayMtl(const gl::VertexArrayState &state, ContextMtl *context) 151 : VertexArrayImpl(state), 152 // Due to Metal's strict requirement for offset and stride, we need to always allocate new 153 // buffer for every conversion. 154 mDynamicVertexData(true) 155{ 156 reset(context); 157 158 mDynamicVertexData.initialize(context, 0, mtl::kVertexAttribBufferStrideAlignment, 159 mtl::kMaxVertexAttribs); 160 161 mDynamicIndexData.initialize(context, kDynamicIndexDataSize, mtl::kIndexBufferOffsetAlignment); 162} 163VertexArrayMtl::~VertexArrayMtl() {} 164 165void VertexArrayMtl::destroy(const gl::Context *context) 166{ 167 ContextMtl *contextMtl = mtl::GetImpl(context); 168 169 reset(contextMtl); 170 171 mDynamicVertexData.destroy(contextMtl); 172 mDynamicIndexData.destroy(contextMtl); 173} 174 175void VertexArrayMtl::reset(ContextMtl *context) 176{ 177 for (BufferHolderMtl *&buffer : mCurrentArrayBuffers) 178 { 179 buffer = nullptr; 180 } 181 for (size_t &offset : mCurrentArrayBufferOffsets) 182 { 183 offset = 0; 184 } 185 for (GLuint &stride : mCurrentArrayBufferStrides) 186 { 187 stride = 0; 188 } 189 for (const mtl::VertexFormat *&format : mCurrentArrayBufferFormats) 190 { 191 format = &context->getVertexFormat(angle::FormatID::R32G32B32A32_FLOAT, false); 192 } 193 194 mVertexArrayDirty = true; 195} 196 197angle::Result VertexArrayMtl::syncState(const gl::Context *context, 198 const gl::VertexArray::DirtyBits &dirtyBits, 199 gl::VertexArray::DirtyAttribBitsArray *attribBits, 200 gl::VertexArray::DirtyBindingBitsArray *bindingBits) 201{ 202 const std::vector<gl::VertexAttribute> &attribs = mState.getVertexAttributes(); 203 const std::vector<gl::VertexBinding> &bindings = mState.getVertexBindings(); 204 205 for (size_t dirtyBit : dirtyBits) 206 { 207 switch (dirtyBit) 208 { 209 case gl::VertexArray::DIRTY_BIT_ELEMENT_ARRAY_BUFFER: 210 case gl::VertexArray::DIRTY_BIT_ELEMENT_ARRAY_BUFFER_DATA: 211 { 212 break; 213 } 214 215#define ANGLE_VERTEX_DIRTY_ATTRIB_FUNC(INDEX) \ 216 case gl::VertexArray::DIRTY_BIT_ATTRIB_0 + INDEX: \ 217 ANGLE_TRY(syncDirtyAttrib(context, attribs[INDEX], bindings[attribs[INDEX].bindingIndex], \ 218 INDEX)); \ 219 mVertexArrayDirty = true; \ 220 (*attribBits)[INDEX].reset(); \ 221 break; 222 223 ANGLE_VERTEX_INDEX_CASES(ANGLE_VERTEX_DIRTY_ATTRIB_FUNC) 224 225#define ANGLE_VERTEX_DIRTY_BINDING_FUNC(INDEX) \ 226 case gl::VertexArray::DIRTY_BIT_BINDING_0 + INDEX: \ 227 ANGLE_TRY(syncDirtyAttrib(context, attribs[INDEX], bindings[attribs[INDEX].bindingIndex], \ 228 INDEX)); \ 229 mVertexArrayDirty = true; \ 230 (*bindingBits)[INDEX].reset(); \ 231 break; 232 233 ANGLE_VERTEX_INDEX_CASES(ANGLE_VERTEX_DIRTY_BINDING_FUNC) 234 235#define ANGLE_VERTEX_DIRTY_BUFFER_DATA_FUNC(INDEX) \ 236 case gl::VertexArray::DIRTY_BIT_BUFFER_DATA_0 + INDEX: \ 237 ANGLE_TRY(syncDirtyAttrib(context, attribs[INDEX], bindings[attribs[INDEX].bindingIndex], \ 238 INDEX)); \ 239 mVertexArrayDirty = true; \ 240 break; 241 242 ANGLE_VERTEX_INDEX_CASES(ANGLE_VERTEX_DIRTY_BUFFER_DATA_FUNC) 243 244 default: 245 UNREACHABLE(); 246 break; 247 } 248 } 249 250 return angle::Result::Continue; 251} 252 253// vertexDescChanged is both input and output, the input value if is true, will force new 254// mtl::VertexDesc to be returned via vertexDescOut. Otherwise, it is only returned when the 255// vertex array is dirty 256angle::Result VertexArrayMtl::setupDraw(const gl::Context *glContext, 257 mtl::RenderCommandEncoder *cmdEncoder, 258 bool *vertexDescChanged, 259 mtl::VertexDesc *vertexDescOut) 260{ 261 bool dirty = mVertexArrayDirty || *vertexDescChanged; 262 263 if (dirty) 264 { 265 mVertexArrayDirty = false; 266 267 const std::vector<gl::VertexAttribute> &attribs = mState.getVertexAttributes(); 268 const std::vector<gl::VertexBinding> &bindings = mState.getVertexBindings(); 269 270 mtl::VertexDesc &desc = *vertexDescOut; 271 272 desc.numAttribs = mtl::kMaxVertexAttribs; 273 desc.numBufferLayouts = mtl::kMaxVertexAttribs; 274 275 // Initialize the buffer layouts with constant step rate 276 for (uint32_t b = 0; b < mtl::kMaxVertexAttribs; ++b) 277 { 278 SetDefaultVertexBufferLayout(&desc.layouts[b]); 279 } 280 281 for (uint32_t v = 0; v < mtl::kMaxVertexAttribs; ++v) 282 { 283 const auto &attrib = attribs[v]; 284 const gl::VertexBinding &binding = bindings[attrib.bindingIndex]; 285 286 const angle::Format &angleFormat = mCurrentArrayBufferFormats[v]->actualAngleFormat(); 287 desc.attributes[v].format = mCurrentArrayBufferFormats[v]->metalFormat; 288 289 bool attribEnabled = attrib.enabled; 290 if (attribEnabled && !mCurrentArrayBuffers[v]) 291 { 292 // Disable it to avoid crash. 293 attribEnabled = false; 294 } 295 296 if (attribEnabled) 297 { 298 uint32_t bufferIdx = mtl::kVboBindingIndexStart + v; 299 uint32_t bufferOffset = static_cast<uint32_t>(mCurrentArrayBufferOffsets[v]); 300 301 desc.attributes[v].bufferIndex = bufferIdx; 302 desc.attributes[v].offset = 0; 303 ASSERT((bufferOffset % angleFormat.pixelBytes) == 0); 304 305 ASSERT(bufferIdx < mtl::kMaxVertexAttribs); 306 if (binding.getDivisor() == 0) 307 { 308 desc.layouts[bufferIdx].stepFunction = MTLVertexStepFunctionPerVertex; 309 desc.layouts[bufferIdx].stepRate = 1; 310 } 311 else 312 { 313 desc.layouts[bufferIdx].stepFunction = MTLVertexStepFunctionPerInstance; 314 desc.layouts[bufferIdx].stepRate = binding.getDivisor(); 315 } 316 desc.layouts[bufferIdx].stride = mCurrentArrayBufferStrides[v]; 317 318 cmdEncoder->setVertexBuffer(mCurrentArrayBuffers[v]->getCurrentBuffer(), 319 bufferOffset, bufferIdx); 320 } 321 else 322 { 323 desc.attributes[v].bufferIndex = mtl::kDefaultAttribsBindingIndex; 324 desc.attributes[v].offset = v * mtl::kDefaultAttributeSize; 325 } 326 } 327 } 328 329 *vertexDescChanged = dirty; 330 331 return angle::Result::Continue; 332} 333 334angle::Result VertexArrayMtl::updateClientAttribs(const gl::Context *context, 335 GLint firstVertex, 336 GLsizei vertexOrIndexCount, 337 GLsizei instanceCount, 338 gl::DrawElementsType indexTypeOrInvalid, 339 const void *indices) 340{ 341 ContextMtl *contextMtl = mtl::GetImpl(context); 342 const gl::AttributesMask &clientAttribs = context->getStateCache().getActiveClientAttribsMask(); 343 344 ASSERT(clientAttribs.any()); 345 346 GLint startVertex; 347 size_t vertexCount; 348 ANGLE_TRY(GetVertexRangeInfo(context, firstVertex, vertexOrIndexCount, indexTypeOrInvalid, 349 indices, 0, &startVertex, &vertexCount)); 350 351 mDynamicVertexData.releaseInFlightBuffers(contextMtl); 352 353 const std::vector<gl::VertexAttribute> &attribs = mState.getVertexAttributes(); 354 const std::vector<gl::VertexBinding> &bindings = mState.getVertexBindings(); 355 356 for (size_t attribIndex : clientAttribs) 357 { 358 const gl::VertexAttribute &attrib = attribs[attribIndex]; 359 const gl::VertexBinding &binding = bindings[attrib.bindingIndex]; 360 ASSERT(attrib.enabled && binding.getBuffer().get() == nullptr); 361 362 GLuint stride; 363 const mtl::VertexFormat &vertexFormat = 364 GetVertexConversionFormat(contextMtl, attrib.format->id, &stride); 365 366 const uint8_t *src = static_cast<const uint8_t *>(attrib.pointer); 367 ASSERT(src); 368 369 GLint startElement; 370 size_t elementCount; 371 if (binding.getDivisor() == 0) 372 { 373 // Per vertex attribute 374 startElement = startVertex; 375 elementCount = vertexCount; 376 } 377 else 378 { 379 // Per instance attribute 380 startElement = 0; 381 elementCount = UnsignedCeilDivide(instanceCount, binding.getDivisor()); 382 } 383 // Allocate space for startElement + elementCount so indexing will work. If we don't 384 // start at zero all the indices will be off. 385 // Only elementCount vertices will be used by the upcoming draw so that is all we copy. 386 size_t bytesToAllocate = (startElement + elementCount) * stride; 387 src += startElement * binding.getStride(); 388 size_t destOffset = startElement * stride; 389 390 ANGLE_TRY(StreamVertexData( 391 contextMtl, &mDynamicVertexData, src, bytesToAllocate, destOffset, elementCount, 392 binding.getStride(), vertexFormat.vertexLoadFunction, 393 &mConvertedArrayBufferHolders[attribIndex], &mCurrentArrayBufferOffsets[attribIndex])); 394 395 mCurrentArrayBuffers[attribIndex] = &mConvertedArrayBufferHolders[attribIndex]; 396 mCurrentArrayBufferFormats[attribIndex] = &vertexFormat; 397 mCurrentArrayBufferStrides[attribIndex] = stride; 398 } 399 400 mVertexArrayDirty = true; 401 402 return angle::Result::Continue; 403} 404 405angle::Result VertexArrayMtl::syncDirtyAttrib(const gl::Context *glContext, 406 const gl::VertexAttribute &attrib, 407 const gl::VertexBinding &binding, 408 size_t attribIndex) 409{ 410 ContextMtl *contextMtl = mtl::GetImpl(glContext); 411 ASSERT(mtl::kMaxVertexAttribs > attribIndex); 412 413 if (attrib.enabled) 414 { 415 gl::Buffer *bufferGL = binding.getBuffer().get(); 416 const mtl::VertexFormat &format = contextMtl->getVertexFormat(attrib.format->id, false); 417 418 if (bufferGL) 419 { 420 BufferMtl *bufferMtl = mtl::GetImpl(bufferGL); 421 bool needConversion = 422 format.actualFormatId != format.intendedFormatId || 423 (binding.getOffset() % format.actualAngleFormat().pixelBytes) != 0 || 424 (binding.getStride() % mtl::kVertexAttribBufferStrideAlignment) != 0; 425 426 if (needConversion) 427 { 428 ANGLE_TRY(convertVertexBuffer(glContext, bufferMtl, binding, attribIndex, format)); 429 } 430 else 431 { 432 mCurrentArrayBuffers[attribIndex] = bufferMtl; 433 mCurrentArrayBufferOffsets[attribIndex] = binding.getOffset(); 434 mCurrentArrayBufferStrides[attribIndex] = binding.getStride(); 435 436 mCurrentArrayBufferFormats[attribIndex] = &format; 437 } 438 } 439 else 440 { 441 // ContextMtl must feed the client data using updateClientAttribs() 442 } 443 } 444 else 445 { 446 // Tell ContextMtl to update default attribute value 447 contextMtl->invalidateDefaultAttribute(attribIndex); 448 449 mCurrentArrayBuffers[attribIndex] = nullptr; 450 mCurrentArrayBufferOffsets[attribIndex] = 0; 451 mCurrentArrayBufferStrides[attribIndex] = 0; 452 // NOTE(hqle): We only support ES 2.0 atm. So default attribute type should always 453 // be float. 454 mCurrentArrayBufferFormats[attribIndex] = 455 &contextMtl->getVertexFormat(angle::FormatID::R32G32B32A32_FLOAT, false); 456 } 457 458 return angle::Result::Continue; 459} 460 461angle::Result VertexArrayMtl::getIndexBuffer(const gl::Context *context, 462 gl::DrawElementsType type, 463 size_t count, 464 const void *indices, 465 mtl::BufferRef *idxBufferOut, 466 size_t *idxBufferOffsetOut, 467 gl::DrawElementsType *indexTypeOut) 468{ 469 const gl::Buffer *glElementArrayBuffer = getState().getElementArrayBuffer(); 470 471 size_t convertedOffset = reinterpret_cast<size_t>(indices); 472 if (!glElementArrayBuffer) 473 { 474 ANGLE_TRY(streamIndexBufferFromClient(context, type, count, indices, idxBufferOut, 475 idxBufferOffsetOut)); 476 } 477 else 478 { 479 bool needConversion = type == gl::DrawElementsType::UnsignedByte || 480 (convertedOffset % mtl::kIndexBufferOffsetAlignment) != 0; 481 if (needConversion) 482 { 483 ANGLE_TRY(convertIndexBuffer(context, type, convertedOffset, idxBufferOut, 484 idxBufferOffsetOut)); 485 } 486 else 487 { 488 // No conversion needed: 489 BufferMtl *bufferMtl = mtl::GetImpl(glElementArrayBuffer); 490 *idxBufferOut = bufferMtl->getCurrentBuffer(); 491 *idxBufferOffsetOut = convertedOffset; 492 } 493 } 494 495 *indexTypeOut = type; 496 if (type == gl::DrawElementsType::UnsignedByte) 497 { 498 // This buffer is already converted to ushort indices above 499 *indexTypeOut = gl::DrawElementsType::UnsignedShort; 500 } 501 502 return angle::Result::Continue; 503} 504 505angle::Result VertexArrayMtl::convertIndexBuffer(const gl::Context *glContext, 506 gl::DrawElementsType indexType, 507 size_t offset, 508 mtl::BufferRef *idxBufferOut, 509 size_t *idxBufferOffsetOut) 510{ 511 ASSERT((offset % mtl::kIndexBufferOffsetAlignment) != 0 || 512 indexType == gl::DrawElementsType::UnsignedByte); 513 514 BufferMtl *idxBuffer = mtl::GetImpl(getState().getElementArrayBuffer()); 515 516 IndexConversionBufferMtl *conversion = 517 idxBuffer->getIndexConversionBuffer(glContext, indexType, offset); 518 519 // Has the content of the buffer has changed since last conversion? 520 if (!conversion->dirty) 521 { 522 // reuse the converted buffer 523 *idxBufferOut = conversion->convertedBuffer; 524 *idxBufferOffsetOut = conversion->convertedOffset; 525 return angle::Result::Continue; 526 } 527 528 size_t indexCount = GetIndexCount(idxBuffer, offset, indexType); 529 530 ANGLE_TRY( 531 convertIndexBufferGPU(glContext, indexType, idxBuffer, offset, indexCount, conversion)); 532 533 *idxBufferOut = conversion->convertedBuffer; 534 *idxBufferOffsetOut = conversion->convertedOffset; 535 536 return angle::Result::Continue; 537} 538 539angle::Result VertexArrayMtl::convertIndexBufferGPU(const gl::Context *glContext, 540 gl::DrawElementsType indexType, 541 BufferMtl *idxBuffer, 542 size_t offset, 543 size_t indexCount, 544 IndexConversionBufferMtl *conversion) 545{ 546 ContextMtl *contextMtl = mtl::GetImpl(glContext); 547 DisplayMtl *display = contextMtl->getDisplay(); 548 549 const size_t amount = GetIndexConvertedBufferSize(indexType, indexCount); 550 551 // Allocate new buffer, save it in conversion struct so that we can reuse it when the content 552 // of the original buffer is not dirty. 553 conversion->data.releaseInFlightBuffers(contextMtl); 554 ANGLE_TRY(conversion->data.allocate(contextMtl, amount, nullptr, &conversion->convertedBuffer, 555 &conversion->convertedOffset)); 556 557 // Do the conversion on GPU. 558 ANGLE_TRY(display->getUtils().convertIndexBufferGPU( 559 mtl::GetImpl(glContext), 560 {indexType, static_cast<uint32_t>(indexCount), idxBuffer->getCurrentBuffer(), 561 static_cast<uint32_t>(offset), conversion->convertedBuffer, 562 static_cast<uint32_t>(conversion->convertedOffset)})); 563 564 ANGLE_TRY(conversion->data.commit(contextMtl)); 565 566 ASSERT(conversion->dirty); 567 conversion->dirty = false; 568 569 return angle::Result::Continue; 570} 571 572angle::Result VertexArrayMtl::streamIndexBufferFromClient(const gl::Context *context, 573 gl::DrawElementsType indexType, 574 size_t indexCount, 575 const void *sourcePointer, 576 mtl::BufferRef *idxBufferOut, 577 size_t *idxBufferOffsetOut) 578{ 579 ASSERT(getState().getElementArrayBuffer() == nullptr); 580 ContextMtl *contextMtl = mtl::GetImpl(context); 581 582 auto srcData = static_cast<const uint8_t *>(sourcePointer); 583 ANGLE_TRY(StreamIndexData(contextMtl, &mDynamicIndexData, srcData, indexType, indexCount, 584 idxBufferOut, idxBufferOffsetOut)); 585 586 return angle::Result::Continue; 587} 588 589angle::Result VertexArrayMtl::convertVertexBuffer(const gl::Context *glContext, 590 BufferMtl *srcBuffer, 591 const gl::VertexBinding &binding, 592 size_t attribIndex, 593 const mtl::VertexFormat &srcVertexFormat) 594{ 595 const angle::Format &intendedAngleFormat = srcVertexFormat.intendedAngleFormat(); 596 597 ConversionBufferMtl *conversion = srcBuffer->getVertexConversionBuffer( 598 glContext, intendedAngleFormat.id, binding.getStride(), binding.getOffset()); 599 600 // Has the content of the buffer has changed since last conversion? 601 if (!conversion->dirty) 602 { 603 ContextMtl *contextMtl = mtl::GetImpl(glContext); 604 605 // Buffer's data hasn't been changed. Re-use last converted results 606 GLuint stride; 607 const mtl::VertexFormat &vertexFormat = 608 GetVertexConversionFormat(contextMtl, intendedAngleFormat.id, &stride); 609 610 mConvertedArrayBufferHolders[attribIndex].set(conversion->convertedBuffer); 611 mCurrentArrayBufferOffsets[attribIndex] = conversion->convertedOffset; 612 613 mCurrentArrayBuffers[attribIndex] = &mConvertedArrayBufferHolders[attribIndex]; 614 mCurrentArrayBufferFormats[attribIndex] = &vertexFormat; 615 mCurrentArrayBufferStrides[attribIndex] = stride; 616 return angle::Result::Continue; 617 } 618 619 // NOTE(hqle): Do the conversion on GPU. 620 return convertVertexBufferCPU(glContext, srcBuffer, binding, attribIndex, srcVertexFormat, 621 conversion); 622} 623 624angle::Result VertexArrayMtl::convertVertexBufferCPU(const gl::Context *glContext, 625 BufferMtl *srcBuffer, 626 const gl::VertexBinding &binding, 627 size_t attribIndex, 628 const mtl::VertexFormat &srcVertexFormat, 629 ConversionBufferMtl *conversion) 630{ 631 ContextMtl *contextMtl = mtl::GetImpl(glContext); 632 633 // Convert to tightly packed format 634 GLuint stride; 635 const mtl::VertexFormat &vertexFormat = 636 GetVertexConversionFormat(contextMtl, srcVertexFormat.intendedFormatId, &stride); 637 unsigned srcFormatSize = vertexFormat.intendedAngleFormat().pixelBytes; 638 639 conversion->data.releaseInFlightBuffers(contextMtl); 640 641 size_t numVertices = GetVertexCount(srcBuffer, binding, srcFormatSize); 642 if (numVertices == 0) 643 { 644 return angle::Result::Continue; 645 } 646 647 const uint8_t *srcBytes = srcBuffer->getClientShadowCopyData(glContext); 648 ANGLE_CHECK_GL_ALLOC(contextMtl, srcBytes); 649 650 srcBytes += binding.getOffset(); 651 652 ANGLE_TRY(StreamVertexData(contextMtl, &conversion->data, srcBytes, numVertices * stride, 0, 653 numVertices, binding.getStride(), vertexFormat.vertexLoadFunction, 654 &mConvertedArrayBufferHolders[attribIndex], 655 &mCurrentArrayBufferOffsets[attribIndex])); 656 657 mCurrentArrayBuffers[attribIndex] = &mConvertedArrayBufferHolders[attribIndex]; 658 mCurrentArrayBufferFormats[attribIndex] = &vertexFormat; 659 mCurrentArrayBufferStrides[attribIndex] = stride; 660 661 // Cache the last converted results to be re-used later if the buffer's content won't ever be 662 // changed. 663 conversion->convertedBuffer = mConvertedArrayBufferHolders[attribIndex].getCurrentBuffer(); 664 conversion->convertedOffset = mCurrentArrayBufferOffsets[attribIndex]; 665 666#ifndef NDEBUG 667 ANGLE_MTL_OBJC_SCOPE 668 { 669 mConvertedArrayBufferHolders[attribIndex].getCurrentBuffer()->get().label = 670 [NSString stringWithFormat:@"Converted from %p offset=%zu stride=%u", srcBuffer, 671 binding.getOffset(), binding.getStride()]; 672 } 673#endif 674 675 ASSERT(conversion->dirty); 676 conversion->dirty = false; 677 678 return angle::Result::Continue; 679} 680} 681