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 12#include <TargetConditionals.h> 13 14#include "libANGLE/renderer/metal/BufferMtl.h" 15#include "libANGLE/renderer/metal/ContextMtl.h" 16#include "libANGLE/renderer/metal/DisplayMtl.h" 17#include "libANGLE/renderer/metal/mtl_format_utils.h" 18 19#include "common/debug.h" 20#include "common/utilities.h" 21 22namespace rx 23{ 24namespace 25{ 26constexpr size_t kDynamicIndexDataSize = 1024 * 8; 27 28angle::Result StreamVertexData(ContextMtl *contextMtl, 29 mtl::BufferPool *dynamicBuffer, 30 const uint8_t *sourceData, 31 size_t bytesToAllocate, 32 size_t destOffset, 33 size_t vertexCount, 34 size_t stride, 35 VertexCopyFunction vertexLoadFunction, 36 SimpleWeakBufferHolderMtl *bufferHolder, 37 size_t *bufferOffsetOut) 38{ 39 ANGLE_CHECK(contextMtl, vertexLoadFunction, "Unsupported format conversion", GL_INVALID_ENUM); 40 uint8_t *dst = nullptr; 41 mtl::BufferRef newBuffer; 42 ANGLE_TRY(dynamicBuffer->allocate(contextMtl, bytesToAllocate, &dst, &newBuffer, 43 bufferOffsetOut, nullptr)); 44 bufferHolder->set(newBuffer); 45 dst += destOffset; 46 vertexLoadFunction(sourceData, stride, vertexCount, dst); 47 48 ANGLE_TRY(dynamicBuffer->commit(contextMtl)); 49 return angle::Result::Continue; 50} 51 52template <typename SizeT> 53const mtl::VertexFormat &GetVertexConversionFormat(ContextMtl *contextMtl, 54 angle::FormatID originalFormat, 55 SizeT *strideOut) 56{ 57 // Convert to tightly packed format 58 const mtl::VertexFormat &packedFormat = contextMtl->getVertexFormat(originalFormat, true); 59 *strideOut = packedFormat.actualAngleFormat().pixelBytes; 60 return packedFormat; 61} 62 63size_t GetIndexConvertedBufferSize(gl::DrawElementsType indexType, size_t indexCount) 64{ 65 size_t elementSize = gl::GetDrawElementsTypeSize(indexType); 66 if (indexType == gl::DrawElementsType::UnsignedByte) 67 { 68 // 8-bit indices are not supported by Metal, so they are promoted to 69 // 16-bit indices below 70 elementSize = sizeof(GLushort); 71 } 72 73 const size_t amount = elementSize * indexCount; 74 75 return amount; 76} 77 78angle::Result StreamIndexData(ContextMtl *contextMtl, 79 mtl::BufferPool *dynamicBuffer, 80 const uint8_t *sourcePointer, 81 gl::DrawElementsType indexType, 82 size_t indexCount, 83 bool primitiveRestartEnabled, 84 mtl::BufferRef *bufferOut, 85 size_t *bufferOffsetOut) 86{ 87 dynamicBuffer->releaseInFlightBuffers(contextMtl); 88 const size_t amount = GetIndexConvertedBufferSize(indexType, indexCount); 89 GLubyte *dst = nullptr; 90 ANGLE_TRY( 91 dynamicBuffer->allocate(contextMtl, amount, &dst, bufferOut, bufferOffsetOut, nullptr)); 92 93 if (indexType == gl::DrawElementsType::UnsignedByte) 94 { 95 // Unsigned bytes don't have direct support in Metal so we have to expand the 96 // memory to a GLushort. 97 const GLubyte *in = static_cast<const GLubyte *>(sourcePointer); 98 GLushort *expandedDst = reinterpret_cast<GLushort *>(dst); 99 100 if (primitiveRestartEnabled) 101 { 102 for (size_t index = 0; index < indexCount; index++) 103 { 104 if (in[index] == 0xFF) 105 { 106 expandedDst[index] = 0xFFFF; 107 } 108 else 109 { 110 expandedDst[index] = static_cast<GLushort>(in[index]); 111 } 112 } 113 } // if (primitiveRestartEnabled) 114 else 115 { 116 for (size_t index = 0; index < indexCount; index++) 117 { 118 expandedDst[index] = static_cast<GLushort>(in[index]); 119 } 120 } // if (primitiveRestartEnabled) 121 } 122 else 123 { 124 memcpy(dst, sourcePointer, amount); 125 } 126 ANGLE_TRY(dynamicBuffer->commit(contextMtl)); 127 128 return angle::Result::Continue; 129} 130 131size_t GetVertexCount(BufferMtl *srcBuffer, 132 const gl::VertexBinding &binding, 133 uint32_t srcFormatSize) 134{ 135 // Bytes usable for vertex data. 136 GLint64 bytes = srcBuffer->size() - binding.getOffset(); 137 if (bytes < srcFormatSize) 138 return 0; 139 140 // Count the last vertex. It may occupy less than a full stride. 141 size_t numVertices = 1; 142 bytes -= srcFormatSize; 143 144 // Count how many strides fit remaining space. 145 if (bytes > 0) 146 numVertices += static_cast<size_t>(bytes) / binding.getStride(); 147 148 return numVertices; 149} 150 151size_t GetVertexCountWithConversion(BufferMtl *srcBuffer, 152 VertexConversionBufferMtl *conversionBuffer, 153 const gl::VertexBinding &binding, 154 uint32_t srcFormatSize) 155{ 156 // Bytes usable for vertex data. 157 GLint64 bytes = srcBuffer->size() - 158 MIN(static_cast<GLintptr>(conversionBuffer->offset), binding.getOffset()); 159 if (bytes < srcFormatSize) 160 return 0; 161 162 // Count the last vertex. It may occupy less than a full stride. 163 size_t numVertices = 1; 164 bytes -= srcFormatSize; 165 166 // Count how many strides fit remaining space. 167 if (bytes > 0) 168 numVertices += static_cast<size_t>(bytes) / binding.getStride(); 169 170 return numVertices; 171} 172inline size_t GetIndexCount(BufferMtl *srcBuffer, size_t offset, gl::DrawElementsType indexType) 173{ 174 size_t elementSize = gl::GetDrawElementsTypeSize(indexType); 175 return (srcBuffer->size() - offset) / elementSize; 176} 177 178inline void SetDefaultVertexBufferLayout(mtl::VertexBufferLayoutDesc *layout) 179{ 180 layout->stepFunction = mtl::kVertexStepFunctionInvalid; 181 layout->stepRate = 0; 182 layout->stride = 0; 183} 184 185} // namespace 186 187// VertexArrayMtl implementation 188VertexArrayMtl::VertexArrayMtl(const gl::VertexArrayState &state, ContextMtl *context) 189 : VertexArrayImpl(state), 190 mDefaultFloatVertexFormat( 191 context->getVertexFormat(angle::FormatID::R32G32B32A32_FLOAT, false)), 192 mDefaultIntVertexFormat(context->getVertexFormat(angle::FormatID::R32G32B32A32_SINT, false)), 193 mDefaultUIntVertexFormat(context->getVertexFormat(angle::FormatID::R32G32B32A32_UINT, false)) 194{ 195 reset(context); 196 197 mDynamicVertexData.initialize(context, 0, mtl::kVertexAttribBufferStrideAlignment, 198 /** maxBuffers */ 10 * mtl::kMaxVertexAttribs); 199 200 mDynamicIndexData.initialize(context, kDynamicIndexDataSize, mtl::kIndexBufferOffsetAlignment, 201 0); 202} 203VertexArrayMtl::~VertexArrayMtl() {} 204 205void VertexArrayMtl::destroy(const gl::Context *context) 206{ 207 ContextMtl *contextMtl = mtl::GetImpl(context); 208 209 reset(contextMtl); 210 211 mDynamicVertexData.destroy(contextMtl); 212 mDynamicIndexData.destroy(contextMtl); 213} 214 215void VertexArrayMtl::reset(ContextMtl *context) 216{ 217 for (BufferHolderMtl *&buffer : mCurrentArrayBuffers) 218 { 219 buffer = nullptr; 220 } 221 for (size_t &offset : mCurrentArrayBufferOffsets) 222 { 223 offset = 0; 224 } 225 for (GLuint &stride : mCurrentArrayBufferStrides) 226 { 227 stride = 0; 228 } 229 for (const mtl::VertexFormat *&format : mCurrentArrayBufferFormats) 230 { 231 format = &mDefaultFloatVertexFormat; 232 } 233 234 for (size_t &inlineDataSize : mCurrentArrayInlineDataSizes) 235 { 236 inlineDataSize = 0; 237 } 238 239 for (angle::MemoryBuffer &convertedClientArray : mConvertedClientSmallArrays) 240 { 241 convertedClientArray.clear(); 242 } 243 244 for (const uint8_t *&clientPointer : mCurrentArrayInlineDataPointers) 245 { 246 clientPointer = nullptr; 247 } 248 249 if (context->getDisplay()->getFeatures().allowInlineConstVertexData.enabled) 250 { 251 mInlineDataMaxSize = mtl::kInlineConstDataMaxSize; 252 } 253 else 254 { 255 mInlineDataMaxSize = 0; 256 } 257 258 mVertexArrayDirty = true; 259} 260 261angle::Result VertexArrayMtl::syncState(const gl::Context *context, 262 const gl::VertexArray::DirtyBits &dirtyBits, 263 gl::VertexArray::DirtyAttribBitsArray *attribBits, 264 gl::VertexArray::DirtyBindingBitsArray *bindingBits) 265{ 266 const std::vector<gl::VertexAttribute> &attribs = mState.getVertexAttributes(); 267 const std::vector<gl::VertexBinding> &bindings = mState.getVertexBindings(); 268 269 for (auto iter = dirtyBits.begin(), endIter = dirtyBits.end(); iter != endIter; ++iter) 270 { 271 size_t dirtyBit = *iter; 272 switch (dirtyBit) 273 { 274 case gl::VertexArray::DIRTY_BIT_LOST_OBSERVATION: 275 { 276 // If vertex array was not observing while unbound, we need to check buffer's 277 // internal storage and take action if buffer has changed while not observing. 278 // For now we just simply assume buffer storage has changed and always dirty all 279 // binding points. 280 iter.setLaterBits( 281 gl::VertexArray::DirtyBits(mState.getBufferBindingMask().to_ulong() 282 << gl::VertexArray::DIRTY_BIT_BINDING_0)); 283 break; 284 } 285 286 case gl::VertexArray::DIRTY_BIT_ELEMENT_ARRAY_BUFFER: 287 case gl::VertexArray::DIRTY_BIT_ELEMENT_ARRAY_BUFFER_DATA: 288 { 289 mVertexDataDirty = true; 290 break; 291 } 292 293#define ANGLE_VERTEX_DIRTY_ATTRIB_FUNC(INDEX) \ 294 case gl::VertexArray::DIRTY_BIT_ATTRIB_0 + INDEX: \ 295 ANGLE_TRY(syncDirtyAttrib(context, attribs[INDEX], bindings[attribs[INDEX].bindingIndex], \ 296 INDEX)); \ 297 mVertexArrayDirty = true; \ 298 (*attribBits)[INDEX].reset(); \ 299 break; 300 301 ANGLE_VERTEX_INDEX_CASES(ANGLE_VERTEX_DIRTY_ATTRIB_FUNC) 302 303#define ANGLE_VERTEX_DIRTY_BINDING_FUNC(INDEX) \ 304 case gl::VertexArray::DIRTY_BIT_BINDING_0 + INDEX: \ 305 ANGLE_TRY(syncDirtyAttrib(context, attribs[INDEX], bindings[attribs[INDEX].bindingIndex], \ 306 INDEX)); \ 307 mVertexArrayDirty = true; \ 308 (*bindingBits)[INDEX].reset(); \ 309 break; 310 311 ANGLE_VERTEX_INDEX_CASES(ANGLE_VERTEX_DIRTY_BINDING_FUNC) 312 313#define ANGLE_VERTEX_DIRTY_BUFFER_DATA_FUNC(INDEX) \ 314 case gl::VertexArray::DIRTY_BIT_BUFFER_DATA_0 + INDEX: \ 315 ANGLE_TRY(syncDirtyAttrib(context, attribs[INDEX], bindings[attribs[INDEX].bindingIndex], \ 316 INDEX)); \ 317 mVertexDataDirty = true; \ 318 break; 319 320 ANGLE_VERTEX_INDEX_CASES(ANGLE_VERTEX_DIRTY_BUFFER_DATA_FUNC) 321 322 default: 323 UNREACHABLE(); 324 break; 325 } 326 } 327 328 return angle::Result::Continue; 329} 330 331ANGLE_INLINE void VertexArrayMtl::getVertexAttribFormatAndArraySize(const sh::ShaderVariable &var, 332 MTLVertexFormat *formatOut, 333 uint32_t *arraySizeOut) 334{ 335 uint32_t arraySize = var.getArraySizeProduct(); 336 337 MTLVertexFormat format; 338 switch (var.type) 339 { 340 case GL_INT: 341 case GL_INT_VEC2: 342 case GL_INT_VEC3: 343 case GL_INT_VEC4: 344 format = mDefaultIntVertexFormat.metalFormat; 345 break; 346 case GL_UNSIGNED_INT: 347 case GL_UNSIGNED_INT_VEC2: 348 case GL_UNSIGNED_INT_VEC3: 349 case GL_UNSIGNED_INT_VEC4: 350 format = mDefaultUIntVertexFormat.metalFormat; 351 break; 352 case GL_FLOAT_MAT2: 353 case GL_FLOAT_MAT2x3: 354 case GL_FLOAT_MAT2x4: 355 arraySize *= 2; 356 format = mDefaultFloatVertexFormat.metalFormat; 357 break; 358 case GL_FLOAT_MAT3: 359 case GL_FLOAT_MAT3x2: 360 case GL_FLOAT_MAT3x4: 361 arraySize *= 3; 362 format = mDefaultFloatVertexFormat.metalFormat; 363 break; 364 case GL_FLOAT_MAT4: 365 case GL_FLOAT_MAT4x2: 366 case GL_FLOAT_MAT4x3: 367 arraySize *= 4; 368 format = mDefaultFloatVertexFormat.metalFormat; 369 break; 370 default: 371 format = mDefaultFloatVertexFormat.metalFormat; 372 } 373 374 *arraySizeOut = arraySize; 375 *formatOut = format; 376} 377 378// vertexDescChanged is both input and output, the input value if is true, will force new 379// mtl::VertexDesc to be returned via vertexDescOut. This typically happens when active shader 380// program is changed. 381// Otherwise, it is only returned when the vertex array is dirty. 382angle::Result VertexArrayMtl::setupDraw(const gl::Context *glContext, 383 mtl::RenderCommandEncoder *cmdEncoder, 384 bool *vertexDescChanged, 385 mtl::VertexDesc *vertexDescOut) 386{ 387 // NOTE(hqle): consider only updating dirty attributes 388 bool dirty = mVertexArrayDirty || *vertexDescChanged; 389 390 if (dirty) 391 { 392 393 mVertexArrayDirty = false; 394 mEmulatedInstanceAttribs.clear(); 395 396 const gl::ProgramExecutable *executable = glContext->getState().getProgramExecutable(); 397 const gl::AttributesMask &programActiveAttribsMask = 398 executable->getActiveAttribLocationsMask(); 399 const gl::ProgramState &programState = glContext->getState().getProgram()->getState(); 400 401 const std::vector<gl::VertexAttribute> &attribs = mState.getVertexAttributes(); 402 const std::vector<gl::VertexBinding> &bindings = mState.getVertexBindings(); 403 404 mtl::VertexDesc &desc = *vertexDescOut; 405 406 desc.numAttribs = mtl::kMaxVertexAttribs; 407 desc.numBufferLayouts = mtl::kMaxVertexAttribs; 408 409 // Initialize the buffer layouts with constant step rate 410 for (uint32_t b = 0; b < mtl::kMaxVertexAttribs; ++b) 411 { 412 SetDefaultVertexBufferLayout(&desc.layouts[b]); 413 } 414 415 for (uint32_t v = 0; v < mtl::kMaxVertexAttribs; ++v) 416 { 417 if (!programActiveAttribsMask.test(v)) 418 { 419 desc.attributes[v].format = MTLVertexFormatInvalid; 420 desc.attributes[v].bufferIndex = 0; 421 desc.attributes[v].offset = 0; 422 continue; 423 } 424 425 const auto &attrib = attribs[v]; 426 const gl::VertexBinding &binding = bindings[attrib.bindingIndex]; 427 428 bool attribEnabled = attrib.enabled; 429 if (attribEnabled && !mCurrentArrayBuffers[v] && !mCurrentArrayInlineDataPointers[v]) 430 { 431 // Disable it to avoid crash. 432 attribEnabled = false; 433 } 434 435 if (!attribEnabled) 436 { 437 // Use default attribute 438 // Need to find the attribute having the exact binding location = v in the program 439 // inputs list to retrieve its coresponding data type: 440 const std::vector<sh::ShaderVariable> &programInputs = 441 programState.getProgramInputs(); 442 std::vector<sh::ShaderVariable>::const_iterator attribInfoIte = std::find_if( 443 begin(programInputs), end(programInputs), [v](const sh::ShaderVariable &sv) { 444 return static_cast<uint32_t>(sv.location) == v; 445 }); 446 447 if (attribInfoIte == end(programInputs)) 448 { 449 // Most likely this is array element with index > 0. 450 // Already handled when encounter first element. 451 continue; 452 } 453 454 uint32_t arraySize; 455 MTLVertexFormat format; 456 457 getVertexAttribFormatAndArraySize(*attribInfoIte, &format, &arraySize); 458 459 for (uint32_t vaIdx = v; vaIdx < v + arraySize; ++vaIdx) 460 { 461 desc.attributes[vaIdx].bufferIndex = mtl::kDefaultAttribsBindingIndex; 462 desc.attributes[vaIdx].offset = vaIdx * mtl::kDefaultAttributeSize; 463 desc.attributes[vaIdx].format = format; 464 } 465 } 466 else 467 { 468 uint32_t bufferIdx = mtl::kVboBindingIndexStart + v; 469 uint32_t bufferOffset = static_cast<uint32_t>(mCurrentArrayBufferOffsets[v]); 470 471 const angle::Format &angleFormat = 472 mCurrentArrayBufferFormats[v]->actualAngleFormat(); 473 desc.attributes[v].format = mCurrentArrayBufferFormats[v]->metalFormat; 474 475 desc.attributes[v].bufferIndex = bufferIdx; 476 desc.attributes[v].offset = 0; 477 ASSERT((bufferOffset % angleFormat.pixelBytes) == 0); 478 479 ASSERT(bufferIdx < mtl::kMaxVertexAttribs); 480 if (binding.getDivisor() == 0) 481 { 482 desc.layouts[bufferIdx].stepFunction = MTLVertexStepFunctionPerVertex; 483 desc.layouts[bufferIdx].stepRate = 1; 484 } 485 else 486 { 487 desc.layouts[bufferIdx].stepFunction = MTLVertexStepFunctionPerInstance; 488 desc.layouts[bufferIdx].stepRate = binding.getDivisor(); 489 } 490 491 desc.layouts[bufferIdx].stride = mCurrentArrayBufferStrides[v]; 492 } 493 } // for (v) 494 } 495 496 if (dirty || mVertexDataDirty) 497 { 498 mVertexDataDirty = false; 499 const gl::ProgramExecutable *executable = glContext->getState().getProgramExecutable(); 500 const gl::AttributesMask &programActiveAttribsMask = 501 executable->getActiveAttribLocationsMask(); 502 503 for (uint32_t v = 0; v < mtl::kMaxVertexAttribs; ++v) 504 { 505 if (!programActiveAttribsMask.test(v)) 506 { 507 continue; 508 } 509 uint32_t bufferIdx = mtl::kVboBindingIndexStart + v; 510 uint32_t bufferOffset = static_cast<uint32_t>(mCurrentArrayBufferOffsets[v]); 511 if (mCurrentArrayBuffers[v]) 512 { 513 cmdEncoder->setVertexBuffer(mCurrentArrayBuffers[v]->getCurrentBuffer(), 514 bufferOffset, bufferIdx); 515 } 516 else 517 { 518 // No buffer specified, use the client memory directly as inline constant data 519 ASSERT(mCurrentArrayInlineDataSizes[v] <= mInlineDataMaxSize); 520 cmdEncoder->setVertexBytes(mCurrentArrayInlineDataPointers[v], 521 mCurrentArrayInlineDataSizes[v], bufferIdx); 522 } 523 } 524 } 525 526 *vertexDescChanged = dirty; 527 528 return angle::Result::Continue; 529} 530 531angle::Result VertexArrayMtl::updateClientAttribs(const gl::Context *context, 532 GLint firstVertex, 533 GLsizei vertexOrIndexCount, 534 GLsizei instanceCount, 535 gl::DrawElementsType indexTypeOrInvalid, 536 const void *indices) 537{ 538 ContextMtl *contextMtl = mtl::GetImpl(context); 539 const gl::AttributesMask &clientAttribs = context->getStateCache().getActiveClientAttribsMask(); 540 541 ASSERT(clientAttribs.any()); 542 543 GLint startVertex; 544 size_t vertexCount; 545 ANGLE_TRY(GetVertexRangeInfo(context, firstVertex, vertexOrIndexCount, indexTypeOrInvalid, 546 indices, 0, &startVertex, &vertexCount)); 547 548 mDynamicVertexData.releaseInFlightBuffers(contextMtl); 549 550 const std::vector<gl::VertexAttribute> &attribs = mState.getVertexAttributes(); 551 const std::vector<gl::VertexBinding> &bindings = mState.getVertexBindings(); 552 553 for (size_t attribIndex : clientAttribs) 554 { 555 const gl::VertexAttribute &attrib = attribs[attribIndex]; 556 const gl::VertexBinding &binding = bindings[attrib.bindingIndex]; 557 ASSERT(attrib.enabled && binding.getBuffer().get() == nullptr); 558 559 // Source client memory pointer 560 const uint8_t *src = static_cast<const uint8_t *>(attrib.pointer); 561 ASSERT(src); 562 563 GLint startElement; 564 size_t elementCount; 565 if (binding.getDivisor() == 0) 566 { 567 // Per vertex attribute 568 startElement = startVertex; 569 elementCount = vertexCount; 570 } 571 else 572 { 573 // Per instance attribute 574 startElement = 0; 575 elementCount = UnsignedCeilDivide(instanceCount, binding.getDivisor()); 576 } 577 size_t bytesIntendedToUse = (startElement + elementCount) * binding.getStride(); 578 579 const mtl::VertexFormat &format = contextMtl->getVertexFormat(attrib.format->id, false); 580 bool needStreaming = format.actualFormatId != format.intendedFormatId || 581 (binding.getStride() % mtl::kVertexAttribBufferStrideAlignment) != 0 || 582 (binding.getStride() < format.actualAngleFormat().pixelBytes) || 583 bytesIntendedToUse > mInlineDataMaxSize; 584 585 if (!needStreaming) 586 { 587 // Data will be uploaded directly as inline constant data 588 mCurrentArrayBuffers[attribIndex] = nullptr; 589 mCurrentArrayInlineDataPointers[attribIndex] = src; 590 mCurrentArrayInlineDataSizes[attribIndex] = bytesIntendedToUse; 591 mCurrentArrayBufferOffsets[attribIndex] = 0; 592 mCurrentArrayBufferFormats[attribIndex] = &format; 593 mCurrentArrayBufferStrides[attribIndex] = binding.getStride(); 594 } 595 else 596 { 597 GLuint convertedStride; 598 // Need to stream the client vertex data to a buffer. 599 const mtl::VertexFormat &streamFormat = 600 GetVertexConversionFormat(contextMtl, attrib.format->id, &convertedStride); 601 602 // Allocate space for startElement + elementCount so indexing will work. If we don't 603 // start at zero all the indices will be off. 604 // Only elementCount vertices will be used by the upcoming draw so that is all we copy. 605 size_t bytesToAllocate = (startElement + elementCount) * convertedStride; 606 src += startElement * binding.getStride(); 607 size_t destOffset = startElement * convertedStride; 608 609 mCurrentArrayBufferFormats[attribIndex] = &streamFormat; 610 mCurrentArrayBufferStrides[attribIndex] = convertedStride; 611 612 if (bytesToAllocate <= mInlineDataMaxSize) 613 { 614 // If the data is small enough, use host memory instead of creating GPU buffer. To 615 // avoid synchronizing access to GPU buffer that is still in use. 616 angle::MemoryBuffer &convertedClientArray = 617 mConvertedClientSmallArrays[attribIndex]; 618 if (bytesToAllocate > convertedClientArray.size()) 619 { 620 ANGLE_CHECK_GL_ALLOC(contextMtl, convertedClientArray.resize(bytesToAllocate)); 621 } 622 623 ASSERT(streamFormat.vertexLoadFunction); 624 streamFormat.vertexLoadFunction(src, binding.getStride(), elementCount, 625 convertedClientArray.data() + destOffset); 626 627 mCurrentArrayBuffers[attribIndex] = nullptr; 628 mCurrentArrayInlineDataPointers[attribIndex] = convertedClientArray.data(); 629 mCurrentArrayInlineDataSizes[attribIndex] = bytesToAllocate; 630 mCurrentArrayBufferOffsets[attribIndex] = 0; 631 } 632 else 633 { 634 // Stream the client data to a GPU buffer. Synchronization might happen if buffer is 635 // in use. 636 mDynamicVertexData.updateAlignment(contextMtl, 637 streamFormat.actualAngleFormat().pixelBytes); 638 ANGLE_TRY(StreamVertexData(contextMtl, &mDynamicVertexData, src, bytesToAllocate, 639 destOffset, elementCount, binding.getStride(), 640 streamFormat.vertexLoadFunction, 641 &mConvertedArrayBufferHolders[attribIndex], 642 &mCurrentArrayBufferOffsets[attribIndex])); 643 if (contextMtl->getDisplay()->getFeatures().flushAfterStreamVertexData.enabled) 644 { 645 // WaitUntilScheduled is needed for this workaround. NoWait does not have the 646 // needed effect. 647 contextMtl->flushCommandBuffer(mtl::WaitUntilScheduled); 648 } 649 650 mCurrentArrayBuffers[attribIndex] = &mConvertedArrayBufferHolders[attribIndex]; 651 } 652 } // if (needStreaming) 653 } 654 655 mVertexArrayDirty = true; 656 657 return angle::Result::Continue; 658} 659 660angle::Result VertexArrayMtl::syncDirtyAttrib(const gl::Context *glContext, 661 const gl::VertexAttribute &attrib, 662 const gl::VertexBinding &binding, 663 size_t attribIndex) 664{ 665 ContextMtl *contextMtl = mtl::GetImpl(glContext); 666 ASSERT(mtl::kMaxVertexAttribs > attribIndex); 667 668 if (attrib.enabled) 669 { 670 gl::Buffer *bufferGL = binding.getBuffer().get(); 671 const mtl::VertexFormat &format = contextMtl->getVertexFormat(attrib.format->id, false); 672 673 if (bufferGL) 674 { 675 BufferMtl *bufferMtl = mtl::GetImpl(bufferGL); 676 // https://bugs.webkit.org/show_bug.cgi?id=236733 677 // even non-converted buffers need to be observed for potential 678 // data rebinds. 679 mContentsObservers->enableForBuffer(bufferGL, static_cast<uint32_t>(attribIndex)); 680 bool needConversion = 681 format.actualFormatId != format.intendedFormatId || 682 (binding.getOffset() % format.actualAngleFormat().pixelBytes) != 0 || 683 (binding.getOffset() % mtl::kVertexAttribBufferStrideAlignment) != 0 || 684 (binding.getStride() < format.actualAngleFormat().pixelBytes) || 685 (binding.getStride() % mtl::kVertexAttribBufferStrideAlignment) != 0; 686 687 if (needConversion) 688 { 689 ANGLE_TRY(convertVertexBuffer(glContext, bufferMtl, binding, attribIndex, format)); 690 } 691 else 692 { 693 mCurrentArrayBuffers[attribIndex] = bufferMtl; 694 mCurrentArrayBufferOffsets[attribIndex] = binding.getOffset(); 695 mCurrentArrayBufferStrides[attribIndex] = binding.getStride(); 696 697 mCurrentArrayBufferFormats[attribIndex] = &format; 698 } 699 } 700 else 701 { 702 // ContextMtl must feed the client data using updateClientAttribs() 703 } 704 } 705 else 706 { 707 // Use default attribute value. Handled in setupDraw(). 708 mCurrentArrayBuffers[attribIndex] = nullptr; 709 mCurrentArrayBufferOffsets[attribIndex] = 0; 710 mCurrentArrayBufferStrides[attribIndex] = 0; 711 mCurrentArrayBufferFormats[attribIndex] = 712 &contextMtl->getVertexFormat(angle::FormatID::NONE, false); 713 } 714 715 return angle::Result::Continue; 716} 717 718angle::Result VertexArrayMtl::getIndexBuffer(const gl::Context *context, 719 gl::DrawElementsType type, 720 size_t count, 721 const void *indices, 722 mtl::BufferRef *idxBufferOut, 723 size_t *idxBufferOffsetOut, 724 gl::DrawElementsType *indexTypeOut) 725{ 726 const gl::Buffer *glElementArrayBuffer = getState().getElementArrayBuffer(); 727 728 size_t convertedOffset = reinterpret_cast<size_t>(indices); 729 if (!glElementArrayBuffer) 730 { 731 ANGLE_TRY(streamIndexBufferFromClient(context, type, count, indices, idxBufferOut, 732 idxBufferOffsetOut)); 733 } 734 else 735 { 736 bool needConversion = type == gl::DrawElementsType::UnsignedByte; 737 if (needConversion) 738 { 739 ANGLE_TRY(convertIndexBuffer(context, type, convertedOffset, idxBufferOut, 740 idxBufferOffsetOut)); 741 } 742 else 743 { 744 // No conversion needed: 745 BufferMtl *bufferMtl = mtl::GetImpl(glElementArrayBuffer); 746 *idxBufferOut = bufferMtl->getCurrentBuffer(); 747 *idxBufferOffsetOut = convertedOffset; 748 } 749 } 750 751 *indexTypeOut = type; 752 if (type == gl::DrawElementsType::UnsignedByte) 753 { 754 // This buffer is already converted to ushort indices above 755 *indexTypeOut = gl::DrawElementsType::UnsignedShort; 756 } 757 758 return angle::Result::Continue; 759} 760 761std::vector<DrawCommandRange> VertexArrayMtl::getDrawIndices(const gl::Context *glContext, 762 gl::DrawElementsType originalIndexType, 763 gl::DrawElementsType indexType, 764 gl::PrimitiveMode primitiveMode, 765 mtl::BufferRef clientBuffer, 766 uint32_t indexCount, 767 size_t offset) 768{ 769 ContextMtl *contextMtl = mtl::GetImpl(glContext); 770 std::vector<DrawCommandRange> drawCommands; 771 // The indexed draw needs to be split to separate draw commands in case primitive restart is 772 // enabled and the drawn primitive supports primitive restart. Otherwise the whole indexed draw 773 // can be sent as one draw command. 774 bool isSimpleType = primitiveMode == gl::PrimitiveMode::Points || 775 primitiveMode == gl::PrimitiveMode::Lines || 776 primitiveMode == gl::PrimitiveMode::Triangles; 777 if (!isSimpleType || !glContext->getState().isPrimitiveRestartEnabled()) 778 { 779 drawCommands.push_back({indexCount, offset}); 780 return drawCommands; 781 } 782 const std::vector<IndexRange> *restartIndices; 783 std::vector<IndexRange> clientIndexRange; 784 const gl::Buffer *glElementArrayBuffer = getState().getElementArrayBuffer(); 785 if (glElementArrayBuffer) 786 { 787 BufferMtl *idxBuffer = mtl::GetImpl(glElementArrayBuffer); 788 restartIndices = &idxBuffer->getRestartIndices(contextMtl, originalIndexType); 789 } 790 else 791 { 792 clientIndexRange = 793 BufferMtl::getRestartIndicesFromClientData(contextMtl, indexType, clientBuffer); 794 restartIndices = &clientIndexRange; 795 } 796 // Reminder, offset is in bytes, not elements. 797 // Slice draw commands based off of indices. 798 uint32_t nIndicesPerPrimitive; 799 switch (primitiveMode) 800 { 801 case gl::PrimitiveMode::Points: 802 nIndicesPerPrimitive = 1; 803 break; 804 case gl::PrimitiveMode::Lines: 805 nIndicesPerPrimitive = 2; 806 break; 807 case gl::PrimitiveMode::Triangles: 808 nIndicesPerPrimitive = 3; 809 break; 810 default: 811 UNREACHABLE(); 812 return drawCommands; 813 } 814 const GLuint indexTypeBytes = gl::GetDrawElementsTypeSize(indexType); 815 uint32_t indicesLeft = indexCount; 816 size_t currentIndexOffset = offset / indexTypeBytes; 817 818 for (auto &range : *restartIndices) 819 { 820 if (range.restartBegin > currentIndexOffset) 821 { 822 int64_t nIndicesInSlice = 823 MIN(((int64_t)range.restartBegin - currentIndexOffset) - 824 ((int64_t)range.restartBegin - currentIndexOffset) % nIndicesPerPrimitive, 825 indicesLeft); 826 size_t restartSize = (range.restartEnd - range.restartBegin) + 1; 827 if (nIndicesInSlice >= nIndicesPerPrimitive) 828 { 829 drawCommands.push_back( 830 {(uint32_t)nIndicesInSlice, currentIndexOffset * indexTypeBytes}); 831 } 832 // Account for dropped indices due to incomplete primitives. 833 size_t indicesUsed = ((range.restartBegin + restartSize) - currentIndexOffset); 834 if (indicesLeft <= indicesUsed) 835 { 836 indicesLeft = 0; 837 } 838 else 839 { 840 indicesLeft -= indicesUsed; 841 } 842 currentIndexOffset = (size_t)(range.restartBegin + restartSize); 843 } 844 // If the initial offset into the index buffer is within a restart zone, move to the end of 845 // the restart zone. 846 else if (range.restartEnd >= currentIndexOffset) 847 { 848 size_t restartSize = (range.restartEnd - currentIndexOffset) + 1; 849 if (indicesLeft <= restartSize) 850 { 851 indicesLeft = 0; 852 } 853 else 854 { 855 indicesLeft -= restartSize; 856 } 857 currentIndexOffset = (size_t)(currentIndexOffset + restartSize); 858 } 859 } 860 if (indicesLeft >= nIndicesPerPrimitive) 861 drawCommands.push_back({indicesLeft, currentIndexOffset * indexTypeBytes}); 862 return drawCommands; 863} 864 865angle::Result VertexArrayMtl::convertIndexBuffer(const gl::Context *glContext, 866 gl::DrawElementsType indexType, 867 size_t offset, 868 mtl::BufferRef *idxBufferOut, 869 size_t *idxBufferOffsetOut) 870{ 871 size_t offsetModulo = offset % mtl::kIndexBufferOffsetAlignment; 872 ASSERT(offsetModulo != 0 || indexType == gl::DrawElementsType::UnsignedByte); 873 874 size_t alignedOffset = offset - offsetModulo; 875 if (indexType == gl::DrawElementsType::UnsignedByte) 876 { 877 // Unsigned byte index will be promoted to unsigned short, thus double its offset. 878 alignedOffset = alignedOffset << 1; 879 } 880 881 ContextMtl *contextMtl = mtl::GetImpl(glContext); 882 const gl::State &glState = glContext->getState(); 883 BufferMtl *idxBuffer = mtl::GetImpl(getState().getElementArrayBuffer()); 884 885 IndexConversionBufferMtl *conversion = idxBuffer->getIndexConversionBuffer( 886 contextMtl, indexType, glState.isPrimitiveRestartEnabled(), offsetModulo); 887 888 // Has the content of the buffer has changed since last conversion? 889 if (!conversion->dirty) 890 { 891 // reuse the converted buffer 892 *idxBufferOut = conversion->convertedBuffer; 893 *idxBufferOffsetOut = conversion->convertedOffset + alignedOffset; 894 return angle::Result::Continue; 895 } 896 897 size_t indexCount = GetIndexCount(idxBuffer, offsetModulo, indexType); 898 if ((!contextMtl->getDisplay()->getFeatures().hasCheapRenderPass.enabled && 899 contextMtl->getRenderCommandEncoder())) 900 { 901 // We shouldn't use GPU to convert when we are in a middle of a render pass. 902 ANGLE_TRY(StreamIndexData(contextMtl, &conversion->data, 903 idxBuffer->getBufferDataReadOnly(contextMtl) + offsetModulo, 904 indexType, indexCount, glState.isPrimitiveRestartEnabled(), 905 &conversion->convertedBuffer, &conversion->convertedOffset)); 906 } 907 else 908 { 909 ANGLE_TRY(convertIndexBufferGPU(glContext, indexType, idxBuffer, offsetModulo, indexCount, 910 conversion)); 911 } 912 // Calculate ranges for prim restart simple types. 913 *idxBufferOut = conversion->convertedBuffer; 914 *idxBufferOffsetOut = conversion->convertedOffset + alignedOffset; 915 916 return angle::Result::Continue; 917} 918 919angle::Result VertexArrayMtl::convertIndexBufferGPU(const gl::Context *glContext, 920 gl::DrawElementsType indexType, 921 BufferMtl *idxBuffer, 922 size_t offset, 923 size_t indexCount, 924 IndexConversionBufferMtl *conversion) 925{ 926 ContextMtl *contextMtl = mtl::GetImpl(glContext); 927 DisplayMtl *display = contextMtl->getDisplay(); 928 929 const size_t amount = GetIndexConvertedBufferSize(indexType, indexCount); 930 931 // Allocate new buffer, save it in conversion struct so that we can reuse it when the content 932 // of the original buffer is not dirty. 933 conversion->data.releaseInFlightBuffers(contextMtl); 934 ANGLE_TRY(conversion->data.allocate(contextMtl, amount, nullptr, &conversion->convertedBuffer, 935 &conversion->convertedOffset)); 936 937 // Do the conversion on GPU. 938 ANGLE_TRY(display->getUtils().convertIndexBufferGPU( 939 contextMtl, {indexType, static_cast<uint32_t>(indexCount), idxBuffer->getCurrentBuffer(), 940 static_cast<uint32_t>(offset), conversion->convertedBuffer, 941 static_cast<uint32_t>(conversion->convertedOffset), 942 glContext->getState().isPrimitiveRestartEnabled()})); 943 944 ANGLE_TRY(conversion->data.commit(contextMtl)); 945 946 ASSERT(conversion->dirty); 947 conversion->dirty = false; 948 949 return angle::Result::Continue; 950} 951 952angle::Result VertexArrayMtl::streamIndexBufferFromClient(const gl::Context *context, 953 gl::DrawElementsType indexType, 954 size_t indexCount, 955 const void *sourcePointer, 956 mtl::BufferRef *idxBufferOut, 957 size_t *idxBufferOffsetOut) 958{ 959 ASSERT(getState().getElementArrayBuffer() == nullptr); 960 ContextMtl *contextMtl = mtl::GetImpl(context); 961 962 auto srcData = static_cast<const uint8_t *>(sourcePointer); 963 ANGLE_TRY(StreamIndexData(contextMtl, &mDynamicIndexData, srcData, indexType, indexCount, 964 context->getState().isPrimitiveRestartEnabled(), idxBufferOut, 965 idxBufferOffsetOut)); 966 967 return angle::Result::Continue; 968} 969 970angle::Result VertexArrayMtl::convertVertexBuffer(const gl::Context *glContext, 971 BufferMtl *srcBuffer, 972 const gl::VertexBinding &binding, 973 size_t attribIndex, 974 const mtl::VertexFormat &srcVertexFormat) 975{ 976 unsigned srcFormatSize = srcVertexFormat.intendedAngleFormat().pixelBytes; 977 978 size_t numVertices = GetVertexCount(srcBuffer, binding, srcFormatSize); 979 if (numVertices == 0) 980 { 981 // Out of bound buffer access, can return any values. 982 // See KHR_robust_buffer_access_behavior 983 mCurrentArrayBuffers[attribIndex] = srcBuffer; 984 mCurrentArrayBufferFormats[attribIndex] = &srcVertexFormat; 985 mCurrentArrayBufferOffsets[attribIndex] = 0; 986 mCurrentArrayBufferStrides[attribIndex] = 16; 987 return angle::Result::Continue; 988 } 989 990 ContextMtl *contextMtl = mtl::GetImpl(glContext); 991 992 // Convert to tightly packed format 993 GLuint stride; 994 const mtl::VertexFormat &convertedFormat = 995 GetVertexConversionFormat(contextMtl, srcVertexFormat.intendedFormatId, &stride); 996 997 ConversionBufferMtl *conversion = srcBuffer->getVertexConversionBuffer( 998 contextMtl, srcVertexFormat.intendedFormatId, binding.getStride(), binding.getOffset()); 999 1000 // Has the content of the buffer has changed since last conversion? 1001 if (!conversion->dirty) 1002 { 1003 VertexConversionBufferMtl *vertexConversionMtl = 1004 static_cast<VertexConversionBufferMtl *>(conversion); 1005 ASSERT((binding.getOffset() - vertexConversionMtl->offset) % binding.getStride() == 0); 1006 mConvertedArrayBufferHolders[attribIndex].set(conversion->convertedBuffer); 1007 mCurrentArrayBufferOffsets[attribIndex] = 1008 conversion->convertedOffset + 1009 stride * ((binding.getOffset() - vertexConversionMtl->offset) / binding.getStride()); 1010 1011 mCurrentArrayBuffers[attribIndex] = &mConvertedArrayBufferHolders[attribIndex]; 1012 mCurrentArrayBufferFormats[attribIndex] = &convertedFormat; 1013 mCurrentArrayBufferStrides[attribIndex] = stride; 1014 return angle::Result::Continue; 1015 } 1016 numVertices = GetVertexCountWithConversion( 1017 srcBuffer, static_cast<VertexConversionBufferMtl *>(conversion), binding, srcFormatSize); 1018 1019 const angle::Format &convertedAngleFormat = convertedFormat.actualAngleFormat(); 1020 bool canConvertToFloatOnGPU = 1021 convertedAngleFormat.isFloat() && !convertedAngleFormat.isVertexTypeHalfFloat(); 1022 1023 bool canExpandComponentsOnGPU = convertedFormat.actualSameGLType; 1024 1025 if (contextMtl->getRenderCommandEncoder() && 1026 !contextMtl->getDisplay()->getFeatures().hasCheapRenderPass.enabled && 1027 !contextMtl->getDisplay()->getFeatures().hasExplicitMemBarrier.enabled) 1028 { 1029 // Cannot use GPU to convert when we are in a middle of a render pass. 1030 canConvertToFloatOnGPU = canExpandComponentsOnGPU = false; 1031 } 1032 1033 conversion->data.releaseInFlightBuffers(contextMtl); 1034 conversion->data.updateAlignment(contextMtl, convertedAngleFormat.pixelBytes); 1035 1036 if (canConvertToFloatOnGPU || canExpandComponentsOnGPU) 1037 { 1038 ANGLE_TRY(convertVertexBufferGPU(glContext, srcBuffer, binding, attribIndex, 1039 convertedFormat, stride, numVertices, 1040 canExpandComponentsOnGPU, conversion)); 1041 } 1042 else 1043 { 1044 ANGLE_TRY(convertVertexBufferCPU(contextMtl, srcBuffer, binding, attribIndex, 1045 convertedFormat, stride, numVertices, conversion)); 1046 } 1047 1048 mConvertedArrayBufferHolders[attribIndex].set(conversion->convertedBuffer); 1049 mCurrentArrayBufferOffsets[attribIndex] = 1050 conversion->convertedOffset + 1051 stride * 1052 ((binding.getOffset() - static_cast<VertexConversionBufferMtl *>(conversion)->offset) / 1053 binding.getStride()); 1054 mCurrentArrayBuffers[attribIndex] = &mConvertedArrayBufferHolders[attribIndex]; 1055 mCurrentArrayBufferFormats[attribIndex] = &convertedFormat; 1056 mCurrentArrayBufferStrides[attribIndex] = stride; 1057 1058 ASSERT(conversion->dirty); 1059 conversion->dirty = false; 1060 1061#ifndef NDEBUG 1062 ANGLE_MTL_OBJC_SCOPE 1063 { 1064 mConvertedArrayBufferHolders[attribIndex].getCurrentBuffer()->get().label = 1065 [NSString stringWithFormat:@"Converted from %p offset=%zu stride=%u", srcBuffer, 1066 binding.getOffset(), binding.getStride()]; 1067 } 1068#endif 1069 1070 return angle::Result::Continue; 1071} 1072 1073angle::Result VertexArrayMtl::convertVertexBufferCPU(ContextMtl *contextMtl, 1074 BufferMtl *srcBuffer, 1075 const gl::VertexBinding &binding, 1076 size_t attribIndex, 1077 const mtl::VertexFormat &convertedFormat, 1078 GLuint targetStride, 1079 size_t numVertices, 1080 ConversionBufferMtl *conversion) 1081{ 1082 1083 const uint8_t *srcBytes = srcBuffer->getBufferDataReadOnly(contextMtl); 1084 ANGLE_CHECK_GL_ALLOC(contextMtl, srcBytes); 1085 VertexConversionBufferMtl *vertexConverison = 1086 static_cast<VertexConversionBufferMtl *>(conversion); 1087 srcBytes += MIN(binding.getOffset(), static_cast<GLintptr>(vertexConverison->offset)); 1088 SimpleWeakBufferHolderMtl conversionBufferHolder; 1089 ANGLE_TRY(StreamVertexData(contextMtl, &conversion->data, srcBytes, numVertices * targetStride, 1090 0, numVertices, binding.getStride(), 1091 convertedFormat.vertexLoadFunction, &conversionBufferHolder, 1092 &conversion->convertedOffset)); 1093 conversion->convertedBuffer = conversionBufferHolder.getCurrentBuffer(); 1094 return angle::Result::Continue; 1095} 1096 1097angle::Result VertexArrayMtl::convertVertexBufferGPU(const gl::Context *glContext, 1098 BufferMtl *srcBuffer, 1099 const gl::VertexBinding &binding, 1100 size_t attribIndex, 1101 const mtl::VertexFormat &convertedFormat, 1102 GLuint targetStride, 1103 size_t numVertices, 1104 bool isExpandingComponents, 1105 ConversionBufferMtl *conversion) 1106{ 1107 ContextMtl *contextMtl = mtl::GetImpl(glContext); 1108 1109 mtl::BufferRef newBuffer; 1110 size_t newBufferOffset; 1111 ANGLE_TRY(conversion->data.allocate(contextMtl, numVertices * targetStride, nullptr, &newBuffer, 1112 &newBufferOffset)); 1113 1114 ANGLE_CHECK_GL_MATH(contextMtl, binding.getOffset() <= std::numeric_limits<uint32_t>::max()); 1115 ANGLE_CHECK_GL_MATH(contextMtl, newBufferOffset <= std::numeric_limits<uint32_t>::max()); 1116 ANGLE_CHECK_GL_MATH(contextMtl, numVertices <= std::numeric_limits<uint32_t>::max()); 1117 1118 mtl::VertexFormatConvertParams params; 1119 VertexConversionBufferMtl *vertexConversion = 1120 static_cast<VertexConversionBufferMtl *>(conversion); 1121 params.srcBuffer = srcBuffer->getCurrentBuffer(); 1122 params.srcBufferStartOffset = static_cast<uint32_t>( 1123 MIN(static_cast<GLintptr>(vertexConversion->offset), binding.getOffset())); 1124 params.srcStride = binding.getStride(); 1125 params.srcDefaultAlphaData = convertedFormat.defaultAlpha; 1126 1127 params.dstBuffer = newBuffer; 1128 params.dstBufferStartOffset = static_cast<uint32_t>(newBufferOffset); 1129 params.dstStride = targetStride; 1130 params.dstComponents = convertedFormat.actualAngleFormat().channelCount; 1131 1132 params.vertexCount = static_cast<uint32_t>(numVertices); 1133 1134 mtl::RenderUtils &utils = contextMtl->getDisplay()->getUtils(); 1135 mtl::RenderCommandEncoder *renderEncoder = contextMtl->getRenderCommandEncoder(); 1136 if (renderEncoder && contextMtl->getDisplay()->getFeatures().hasExplicitMemBarrier.enabled) 1137 { 1138 // If we are in the middle of a render pass, use vertex shader based buffer conversion to 1139 // avoid breaking the render pass. 1140 if (!isExpandingComponents) 1141 { 1142 ANGLE_TRY(utils.convertVertexFormatToFloatVS( 1143 glContext, renderEncoder, convertedFormat.intendedAngleFormat(), params)); 1144 } 1145 else 1146 { 1147 ANGLE_TRY(utils.expandVertexFormatComponentsVS( 1148 glContext, renderEncoder, convertedFormat.intendedAngleFormat(), params)); 1149 } 1150 } 1151 else 1152 { 1153 // Compute based buffer conversion. 1154 if (!isExpandingComponents) 1155 { 1156 ANGLE_TRY(utils.convertVertexFormatToFloatCS( 1157 contextMtl, convertedFormat.intendedAngleFormat(), params)); 1158 } 1159 else 1160 { 1161 ANGLE_TRY(utils.expandVertexFormatComponentsCS( 1162 contextMtl, convertedFormat.intendedAngleFormat(), params)); 1163 } 1164 } 1165 1166 ANGLE_TRY(conversion->data.commit(contextMtl)); 1167 1168 conversion->convertedBuffer = newBuffer; 1169 conversion->convertedOffset = newBufferOffset; 1170 1171 return angle::Result::Continue; 1172} 1173} // namespace rx 1174