blob: b2441a23b975d3864c48bb332f783cab360d4555 [file] [log] [blame]
//
// Copyright 2024 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// VertexArrayWgpu.cpp:
// Implements the class methods for VertexArrayWgpu.
//
#include "libANGLE/renderer/wgpu/VertexArrayWgpu.h"
#include "common/PackedEnums.h"
#include "common/debug.h"
#include "libANGLE/Error.h"
#include "libANGLE/renderer/wgpu/ContextWgpu.h"
#include "libANGLE/renderer/wgpu/wgpu_utils.h"
namespace rx
{
namespace
{
bool AttributeNeedsStreaming(ContextWgpu *context,
const gl::VertexAttribute &attrib,
const gl::VertexBinding &binding)
{
const size_t stride = ComputeVertexAttributeStride(attrib, binding);
if (stride % 4 != 0)
{
return true;
}
const size_t typeSize = gl::ComputeVertexAttributeTypeSize(attrib);
if (stride % typeSize != 0)
{
return true;
}
const webgpu::Format &vertexFormat = context->getFormat(attrib.format->glInternalFormat);
if (vertexFormat.vertexLoadRequiresConversion())
{
return true;
}
gl::Buffer *bufferGl = binding.getBuffer().get();
if (!bufferGl || bufferGl->getSize() == 0)
{
return true;
}
return false;
}
template <typename SourceType, typename DestType>
void CopyIndexData(const uint8_t *sourceData, size_t count, uint8_t *destData)
{
if constexpr (std::is_same<SourceType, DestType>::value)
{
memcpy(destData, sourceData, sizeof(SourceType) * count);
}
else
{
for (size_t i = 0; i < count; i++)
{
DestType *dst = reinterpret_cast<DestType *>(destData) + i;
const SourceType *src = reinterpret_cast<const SourceType *>(sourceData) + i;
*dst = static_cast<DestType>(*src);
}
}
}
using CopyIndexFunction = void (*)(const uint8_t *sourceData, size_t count, uint8_t *destData);
CopyIndexFunction GetCopyIndexFunction(gl::DrawElementsType sourceType,
gl::DrawElementsType destType)
{
static_assert(static_cast<size_t>(gl::DrawElementsType::UnsignedByte) == 0);
static_assert(static_cast<size_t>(gl::DrawElementsType::UnsignedShort) == 1);
static_assert(static_cast<size_t>(gl::DrawElementsType::UnsignedInt) == 2);
ASSERT(static_cast<size_t>(sourceType) <= 2);
ASSERT(static_cast<size_t>(destType) <= 2);
ASSERT(static_cast<size_t>(destType) >=
static_cast<size_t>(sourceType)); // Can't copy to a smaller type
constexpr CopyIndexFunction copyFunctions[3][3] = {
{
CopyIndexData<GLubyte, GLubyte>,
CopyIndexData<GLubyte, GLushort>,
CopyIndexData<GLubyte, GLuint>,
},
{
nullptr,
CopyIndexData<GLushort, GLushort>,
CopyIndexData<GLushort, GLuint>,
},
{
nullptr,
nullptr,
CopyIndexData<GLuint, GLuint>,
},
};
CopyIndexFunction copyFunction =
copyFunctions[static_cast<size_t>(sourceType)][static_cast<size_t>(destType)];
ASSERT(copyFunction != nullptr);
return copyFunction;
}
} // namespace
VertexArrayWgpu::VertexArrayWgpu(const gl::VertexArrayState &data) : VertexArrayImpl(data)
{
// Pre-initialize mCurrentIndexBuffer to a streaming buffer because no index buffer dirty bit is
// triggered if our first draw call has no buffer bound.
mCurrentIndexBuffer = &mStreamingIndexBuffer;
}
angle::Result VertexArrayWgpu::syncState(const gl::Context *context,
const gl::VertexArray::DirtyBits &dirtyBits,
gl::VertexArray::DirtyAttribBitsArray *attribBits,
gl::VertexArray::DirtyBindingBitsArray *bindingBits)
{
ASSERT(dirtyBits.any());
ContextWgpu *contextWgpu = GetImplAs<ContextWgpu>(context);
const std::vector<gl::VertexAttribute> &attribs = mState.getVertexAttributes();
const std::vector<gl::VertexBinding> &bindings = mState.getVertexBindings();
gl::AttributesMask syncedAttributes;
for (auto iter = dirtyBits.begin(), endIter = dirtyBits.end(); iter != endIter; ++iter)
{
size_t dirtyBit = *iter;
switch (dirtyBit)
{
case gl::VertexArray::DIRTY_BIT_LOST_OBSERVATION:
break;
case gl::VertexArray::DIRTY_BIT_ELEMENT_ARRAY_BUFFER:
case gl::VertexArray::DIRTY_BIT_ELEMENT_ARRAY_BUFFER_DATA:
ANGLE_TRY(syncDirtyElementArrayBuffer(contextWgpu));
contextWgpu->invalidateIndexBuffer();
break;
#define ANGLE_VERTEX_DIRTY_ATTRIB_FUNC(INDEX) \
case gl::VertexArray::DIRTY_BIT_ATTRIB_0 + INDEX: \
ANGLE_TRY(syncDirtyAttrib(contextWgpu, attribs[INDEX], \
bindings[attribs[INDEX].bindingIndex], INDEX)); \
(*attribBits)[INDEX].reset(); \
syncedAttributes.set(INDEX); \
break;
ANGLE_VERTEX_INDEX_CASES(ANGLE_VERTEX_DIRTY_ATTRIB_FUNC)
#define ANGLE_VERTEX_DIRTY_BINDING_FUNC(INDEX) \
case gl::VertexArray::DIRTY_BIT_BINDING_0 + INDEX: \
ANGLE_TRY(syncDirtyAttrib(contextWgpu, attribs[INDEX], \
bindings[attribs[INDEX].bindingIndex], INDEX)); \
(*bindingBits)[INDEX].reset(); \
syncedAttributes.set(INDEX); \
break;
ANGLE_VERTEX_INDEX_CASES(ANGLE_VERTEX_DIRTY_BINDING_FUNC)
#define ANGLE_VERTEX_DIRTY_BUFFER_DATA_FUNC(INDEX) \
case gl::VertexArray::DIRTY_BIT_BUFFER_DATA_0 + INDEX: \
ANGLE_TRY(syncDirtyAttrib(contextWgpu, attribs[INDEX], \
bindings[attribs[INDEX].bindingIndex], INDEX)); \
syncedAttributes.set(INDEX); \
break;
ANGLE_VERTEX_INDEX_CASES(ANGLE_VERTEX_DIRTY_BUFFER_DATA_FUNC)
default:
break;
}
}
for (size_t syncedAttribIndex : syncedAttributes)
{
contextWgpu->setVertexAttribute(syncedAttribIndex, mCurrentAttribs[syncedAttribIndex]);
contextWgpu->invalidateVertexBuffer(syncedAttribIndex);
}
return angle::Result::Continue;
}
angle::Result VertexArrayWgpu::syncClientArrays(
const gl::Context *context,
const gl::AttributesMask &activeAttributesMask,
gl::PrimitiveMode mode,
GLint first,
GLsizei count,
GLsizei instanceCount,
gl::DrawElementsType sourceDrawElementsTypeOrInvalid,
const void *indices,
GLint baseVertex,
bool primitiveRestartEnabled,
const void **adjustedIndicesPtr,
uint32_t *indexCountOut)
{
*adjustedIndicesPtr = indices;
gl::AttributesMask clientAttributesToSync =
(mState.getClientMemoryAttribsMask() | mForcedStreamingAttributes) &
mState.getEnabledAttributesMask() & activeAttributesMask;
gl::DrawElementsType destDrawElementsTypeOrInvalid = sourceDrawElementsTypeOrInvalid;
IndexDataNeedsStreaming indexDataNeedsStreaming = IndexDataNeedsStreaming::No;
if (sourceDrawElementsTypeOrInvalid == gl::DrawElementsType::UnsignedByte)
{
// Promote 8-bit indices to 16-bit indices
indexDataNeedsStreaming = IndexDataNeedsStreaming::Yes;
destDrawElementsTypeOrInvalid = gl::DrawElementsType::UnsignedShort;
}
else if (mode == gl::PrimitiveMode::LineLoop)
{
// Index data will always need streaming for line loop mode regardless of what type of draw
// call it is.
if (sourceDrawElementsTypeOrInvalid == gl::DrawElementsType::InvalidEnum)
{
// Line loop draw array calls are emulated via indexed draw calls, so an index type must
// be set.
if (count >= std::numeric_limits<unsigned short>::max())
{
destDrawElementsTypeOrInvalid = gl::DrawElementsType::UnsignedInt;
}
else
{
destDrawElementsTypeOrInvalid = gl::DrawElementsType::UnsignedShort;
}
}
indexDataNeedsStreaming = IndexDataNeedsStreaming::Yes;
}
else if (sourceDrawElementsTypeOrInvalid != gl::DrawElementsType::InvalidEnum &&
!mState.getElementArrayBuffer())
{
// Index data needs to be uploaded to the GPU
indexDataNeedsStreaming = IndexDataNeedsStreaming::Yes;
}
if (!clientAttributesToSync.any() && indexDataNeedsStreaming == IndexDataNeedsStreaming::No)
{
return angle::Result::Continue;
}
GLsizei adjustedCount = count;
if (mode == gl::PrimitiveMode::LineLoop)
{
adjustedCount++;
}
if (indexCountOut)
{
*indexCountOut = adjustedCount;
}
ContextWgpu *contextWgpu = webgpu::GetImpl(context);
wgpu::Device device = webgpu::GetDevice(context);
// If any attributes need to be streamed, we need to know the index range. We also need to know
// the index range if there is a draw arrays call and we have to stream the index data for it.
std::optional<gl::IndexRange> indexRange;
if (clientAttributesToSync.any())
{
GLint startVertex = 0;
size_t vertexCount = 0;
ANGLE_TRY(GetVertexRangeInfo(context, first, count, sourceDrawElementsTypeOrInvalid,
indices, baseVertex, &startVertex, &vertexCount));
indexRange = gl::IndexRange(startVertex, startVertex + vertexCount - 1, 0);
}
else if (indexDataNeedsStreaming == IndexDataNeedsStreaming::Yes &&
sourceDrawElementsTypeOrInvalid == gl::DrawElementsType::InvalidEnum)
{
indexRange = gl::IndexRange(first, first + count - 1, 0);
}
// Pre-compute the total size of all streamed vertex and index data so a single staging buffer
// can be used
size_t stagingBufferSize = 0;
std::optional<size_t> destIndexDataSize;
std::optional<size_t> destIndexUnitSize;
gl::Buffer *elementArrayBuffer = mState.getElementArrayBuffer();
if (indexDataNeedsStreaming != IndexDataNeedsStreaming::No)
{
destIndexUnitSize =
static_cast<size_t>(gl::GetDrawElementsTypeSize(destDrawElementsTypeOrInvalid));
destIndexDataSize = destIndexUnitSize.value() * adjustedCount;
// Allocating staging buffer space for indices is only needed when there is no source index
// buffer or index data conversion is needed
if (!elementArrayBuffer || sourceDrawElementsTypeOrInvalid != destDrawElementsTypeOrInvalid)
{
stagingBufferSize +=
rx::roundUpPow2(destIndexDataSize.value(), webgpu::kBufferCopyToBufferAlignment);
}
}
const std::vector<gl::VertexAttribute> &attribs = mState.getVertexAttributes();
const std::vector<gl::VertexBinding> &bindings = mState.getVertexBindings();
if (clientAttributesToSync.any())
{
for (size_t attribIndex : clientAttributesToSync)
{
const gl::VertexAttribute &attrib = attribs[attribIndex];
const gl::VertexBinding &binding = bindings[attrib.bindingIndex];
size_t elementCount = gl::ComputeVertexBindingElementCount(
binding.getDivisor(), indexRange->vertexCount(), instanceCount);
const webgpu::Format &vertexFormat =
contextWgpu->getFormat(attrib.format->glInternalFormat);
size_t destTypeSize = vertexFormat.getActualBufferFormat().pixelBytes;
ASSERT(destTypeSize > 0);
size_t attribSize = destTypeSize * elementCount;
stagingBufferSize += rx::roundUpPow2(attribSize, webgpu::kBufferCopyToBufferAlignment);
}
}
if (stagingBufferSize > contextWgpu->getDisplay()->getLimitsWgpu().maxBufferSize)
{
ERR() << "Staging buffer size of " << stagingBufferSize
<< " in sync client arrays is larger than the max buffer size "
<< contextWgpu->getDisplay()->getLimitsWgpu().maxBufferSize;
return angle::Result::Stop;
}
ASSERT(stagingBufferSize % webgpu::kBufferSizeAlignment == 0);
webgpu::BufferHelper stagingBuffer;
uint8_t *stagingData = nullptr;
size_t currentStagingDataPosition = 0;
if (stagingBufferSize > 0)
{
ASSERT(stagingBufferSize > 0);
ASSERT(stagingBufferSize % webgpu::kBufferSizeAlignment == 0);
ANGLE_TRY(stagingBuffer.initBuffer(device, stagingBufferSize,
wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::MapWrite,
webgpu::MapAtCreation::Yes));
stagingData = stagingBuffer.getMapWritePointer(0, stagingBufferSize);
}
struct BufferCopy
{
uint64_t sourceOffset;
webgpu::BufferHelper *src;
webgpu::BufferHelper *dest;
uint64_t destOffset;
uint64_t size;
};
std::vector<BufferCopy> stagingUploads;
if (indexDataNeedsStreaming == IndexDataNeedsStreaming::Yes)
{
// Indices are streamed to the start of the buffer. Tell the draw call command to use 0 for
// firstIndex.
*adjustedIndicesPtr = 0;
ASSERT(destIndexDataSize.has_value());
ASSERT(destIndexUnitSize.has_value());
size_t destIndexBufferSize =
rx::roundUpPow2(destIndexDataSize.value(), webgpu::kBufferCopyToBufferAlignment);
ANGLE_TRY(ensureBufferCreated(context, mStreamingIndexBuffer, destIndexBufferSize, 0,
wgpu::BufferUsage::CopyDst | wgpu::BufferUsage::Index,
BufferType::IndexBuffer));
if (sourceDrawElementsTypeOrInvalid == destDrawElementsTypeOrInvalid && elementArrayBuffer)
{
// Use the element array buffer as the source for the new streaming index buffer. This
// condition is only hit when an indexed draw call has an element array buffer and is
// trying to draw line loops.
// When using an element array buffer, 'indices' is an offset to the first element.
size_t sourceOffset = reinterpret_cast<size_t>(indices);
BufferWgpu *elementArrayBufferWgpu = GetImplAs<BufferWgpu>(elementArrayBuffer);
webgpu::BufferHelper *sourceBuffer = &elementArrayBufferWgpu->getBuffer();
size_t copySize = rx::roundUpPow2(destIndexUnitSize.value() * count,
webgpu::kBufferCopyToBufferAlignment);
stagingUploads.push_back(
{sourceOffset, sourceBuffer, &mStreamingIndexBuffer, 0, copySize});
if (mode == gl::PrimitiveMode::LineLoop)
{
// Emulate line loops with an additional copy of the first index at the end of the
// buffer
size_t lastOffset = copySize;
stagingUploads.push_back({sourceOffset, sourceBuffer, &mStreamingIndexBuffer,
lastOffset,
rx::roundUpPow2(destIndexUnitSize.value(),
webgpu::kBufferCopyToBufferAlignment)});
}
}
// Handle emulating line loop for draw arrays calls.
else if (sourceDrawElementsTypeOrInvalid == gl::DrawElementsType::InvalidEnum)
{
ASSERT(destDrawElementsTypeOrInvalid != gl::DrawElementsType::InvalidEnum);
ASSERT(mode == gl::PrimitiveMode::LineLoop);
uint32_t clampedVertexCount = gl::clampCast<uint32_t>(indexRange->vertexCount());
uint32_t startVertex = static_cast<uint32_t>(indexRange->start);
size_t index = currentStagingDataPosition;
for (uint32_t i = 0; i < clampedVertexCount; i++)
{
uint32_t copyData = startVertex + i;
memcpy(stagingData + index, &copyData, destIndexUnitSize.value());
index += destIndexUnitSize.value();
}
memcpy(stagingData + currentStagingDataPosition + destIndexUnitSize.value() * count,
&startVertex, destIndexUnitSize.value());
size_t copySize = destIndexBufferSize;
stagingUploads.push_back(
{currentStagingDataPosition, &stagingBuffer, &mStreamingIndexBuffer, 0, copySize});
currentStagingDataPosition += copySize;
}
else
{
const uint8_t *srcIndexData = static_cast<const uint8_t *>(indices);
webgpu::BufferReadback readbackBuffer;
if (mState.getElementArrayBuffer())
{
webgpu::BufferHelper &srcBuffer =
webgpu::GetImpl(mState.getElementArrayBuffer())->getBuffer();
const GLuint srcIndexTypeSize =
gl::GetDrawElementsTypeSize(sourceDrawElementsTypeOrInvalid);
const size_t srcIndexOffset = reinterpret_cast<uintptr_t>(indices);
ANGLE_TRY(srcBuffer.readDataImmediate(
contextWgpu, srcIndexOffset, count * srcIndexTypeSize,
webgpu::RenderPassClosureReason::IndexRangeReadback, &readbackBuffer));
srcIndexData = readbackBuffer.data;
}
CopyIndexFunction indexCopyFunction = GetCopyIndexFunction(
sourceDrawElementsTypeOrInvalid, destDrawElementsTypeOrInvalid);
ASSERT(stagingData != nullptr);
indexCopyFunction(srcIndexData, count, stagingData + currentStagingDataPosition);
if (mode == gl::PrimitiveMode::LineLoop)
{
indexCopyFunction(
srcIndexData, count,
stagingData + currentStagingDataPosition + (destIndexUnitSize.value() * count));
}
size_t copySize = destIndexBufferSize;
stagingUploads.push_back(
{currentStagingDataPosition, &stagingBuffer, &mStreamingIndexBuffer, 0, copySize});
currentStagingDataPosition += copySize;
}
// TODO(anglebug.com/383356846): add support for primitive restarts
}
for (size_t attribIndex : clientAttributesToSync)
{
const gl::VertexAttribute &attrib = attribs[attribIndex];
const gl::VertexBinding &binding = bindings[attrib.bindingIndex];
size_t streamedVertexCount = gl::ComputeVertexBindingElementCount(
binding.getDivisor(), indexRange->vertexCount(), instanceCount);
const size_t sourceStride = ComputeVertexAttributeStride(attrib, binding);
const size_t sourceTypeSize = gl::ComputeVertexAttributeTypeSize(attrib);
// Vertices do not apply the 'start' offset when the divisor is non-zero even when doing
// a non-instanced draw call
const size_t firstIndex = (binding.getDivisor() == 0) ? indexRange->start : 0;
// Attributes using client memory ignore the VERTEX_ATTRIB_BINDING state.
// https://www.opengl.org/registry/specs/ARB/vertex_attrib_binding.txt
const uint8_t *inputPointer = static_cast<const uint8_t *>(attrib.pointer);
webgpu::BufferReadback readbackBuffer;
if (binding.getBuffer().get())
{
webgpu::BufferHelper &srcBuffer =
webgpu::GetImpl(binding.getBuffer().get())->getBuffer();
size_t sourceVertexDataSize =
sourceStride * (firstIndex + streamedVertexCount - 1) + sourceTypeSize;
ANGLE_TRY(srcBuffer.readDataImmediate(
contextWgpu, 0, reinterpret_cast<uintptr_t>(attrib.pointer) + sourceVertexDataSize,
webgpu::RenderPassClosureReason::IndexRangeReadback, &readbackBuffer));
inputPointer = readbackBuffer.data + reinterpret_cast<uintptr_t>(attrib.pointer);
}
const webgpu::Format &vertexFormat =
contextWgpu->getFormat(attrib.format->glInternalFormat);
size_t destTypeSize = vertexFormat.getActualBufferFormat().pixelBytes;
VertexCopyFunction copyFunction = vertexFormat.getVertexLoadFunction();
ASSERT(copyFunction != nullptr);
ASSERT(stagingData != nullptr);
copyFunction(inputPointer + (sourceStride * firstIndex), sourceStride, streamedVertexCount,
stagingData + currentStagingDataPosition);
size_t copySize = rx::roundUpPow2(streamedVertexCount * destTypeSize,
webgpu::kBufferCopyToBufferAlignment);
// Pad the streaming buffer with empty data at the beginning to put the vertex data at the
// same index location. The stride is tightly packed.
size_t destCopyOffset = firstIndex * destTypeSize;
ANGLE_TRY(ensureBufferCreated(
context, mStreamingArrayBuffers[attribIndex], destCopyOffset + copySize, attribIndex,
wgpu::BufferUsage::CopyDst | wgpu::BufferUsage::Vertex, BufferType::ArrayBuffer));
stagingUploads.push_back({currentStagingDataPosition, &stagingBuffer,
&mStreamingArrayBuffers[attribIndex], destCopyOffset, copySize});
currentStagingDataPosition += copySize;
}
if (stagingBuffer.valid())
{
ANGLE_TRY(stagingBuffer.unmap());
}
ANGLE_TRY(contextWgpu->flush(webgpu::RenderPassClosureReason::VertexArrayStreaming));
contextWgpu->ensureCommandEncoderCreated();
wgpu::CommandEncoder &commandEncoder = contextWgpu->getCurrentCommandEncoder();
for (const BufferCopy &copy : stagingUploads)
{
commandEncoder.CopyBufferToBuffer(copy.src->getBuffer(), copy.sourceOffset,
copy.dest->getBuffer(), copy.destOffset, copy.size);
}
return angle::Result::Continue;
}
angle::Result VertexArrayWgpu::syncDirtyAttrib(ContextWgpu *contextWgpu,
const gl::VertexAttribute &attrib,
const gl::VertexBinding &binding,
size_t attribIndex)
{
mForcedStreamingAttributes[attribIndex] = AttributeNeedsStreaming(contextWgpu, attrib, binding);
if (attrib.enabled)
{
SetBitField(mCurrentAttribs[attribIndex].enabled, true);
const webgpu::Format &webgpuFormat =
contextWgpu->getFormat(attrib.format->glInternalFormat);
SetBitField(mCurrentAttribs[attribIndex].format, webgpuFormat.getActualWgpuVertexFormat());
SetBitField(mCurrentAttribs[attribIndex].shaderLocation, attribIndex);
if (!mForcedStreamingAttributes[attribIndex])
{
// Data is sourced directly from the array buffer.
SetBitField(mCurrentAttribs[attribIndex].offset, 0);
SetBitField(mCurrentAttribs[attribIndex].stride, binding.getStride());
gl::Buffer *bufferGl = binding.getBuffer().get();
ASSERT(bufferGl);
BufferWgpu *bufferWgpu = webgpu::GetImpl(bufferGl);
mCurrentArrayBuffers[attribIndex].buffer = &(bufferWgpu->getBuffer());
mCurrentArrayBuffers[attribIndex].offset = reinterpret_cast<uintptr_t>(attrib.pointer);
}
else
{
SetBitField(mCurrentAttribs[attribIndex].offset, 0);
SetBitField(mCurrentAttribs[attribIndex].stride,
webgpuFormat.getActualBufferFormat().pixelBytes);
mCurrentArrayBuffers[attribIndex].buffer = &mStreamingArrayBuffers[attribIndex];
mCurrentArrayBuffers[attribIndex].offset = 0;
}
}
else
{
memset(&mCurrentAttribs[attribIndex], 0, sizeof(webgpu::PackedVertexAttribute));
mCurrentArrayBuffers[attribIndex].buffer = nullptr;
mCurrentArrayBuffers[attribIndex].offset = 0;
}
return angle::Result::Continue;
}
angle::Result VertexArrayWgpu::syncDirtyElementArrayBuffer(ContextWgpu *contextWgpu)
{
gl::Buffer *bufferGl = mState.getElementArrayBuffer();
if (bufferGl)
{
BufferWgpu *buffer = webgpu::GetImpl(bufferGl);
mCurrentIndexBuffer = &buffer->getBuffer();
}
else
{
mCurrentIndexBuffer = &mStreamingIndexBuffer;
}
return angle::Result::Continue;
}
angle::Result VertexArrayWgpu::ensureBufferCreated(const gl::Context *context,
webgpu::BufferHelper &buffer,
size_t size,
size_t attribIndex,
wgpu::BufferUsage usage,
BufferType bufferType)
{
ContextWgpu *contextWgpu = webgpu::GetImpl(context);
if (!buffer.valid() || buffer.requestedSize() < size || buffer.getBuffer().GetUsage() != usage)
{
wgpu::Device device = webgpu::GetDevice(context);
ANGLE_TRY(buffer.initBuffer(device, size, usage, webgpu::MapAtCreation::No));
if (bufferType == BufferType::IndexBuffer)
{
contextWgpu->invalidateIndexBuffer();
}
else
{
ASSERT(bufferType == BufferType::ArrayBuffer);
contextWgpu->invalidateVertexBuffer(attribIndex);
}
}
if (bufferType == BufferType::IndexBuffer)
{
mCurrentIndexBuffer = &buffer;
}
return angle::Result::Continue;
}
} // namespace rx