Relax vertex stride requirement

Relax validation of last stride of vertex buffer according to the
updated spec.

Bug: dawn:1287
Change-Id: I7a58401933b48c5cb121ba73c592575ada3e7151
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/83203
Auto-Submit: Shrek Shao <shrekshao@google.com>
Reviewed-by: Austin Eng <enga@chromium.org>
Commit-Queue: Austin Eng <enga@chromium.org>
This commit is contained in:
shrekshao 2022-03-11 01:09:02 +00:00 committed by Dawn LUCI CQ
parent 39098b1407
commit ed840583eb
5 changed files with 536 additions and 25 deletions

View File

@ -104,17 +104,24 @@ namespace dawn::native {
bufferSize, static_cast<uint8_t>(usedSlotVertex),
vertexBuffer.usedBytesInStride);
} else {
uint64_t requiredSize =
(static_cast<uint64_t>(firstVertex) + vertexCount) * arrayStride;
// firstVertex and vertexCount are in uint32_t, and arrayStride must not
// be larger than kMaxVertexBufferArrayStride, which is currently 2048. So by
// doing checks in uint64_t we avoid overflows.
DAWN_INVALID_IF(
requiredSize > bufferSize,
"Vertex range (first: %u, count: %u) requires a larger buffer (%u) than the "
"bound buffer size (%u) of the vertex buffer at slot %u with stride (%u).",
firstVertex, vertexCount, requiredSize, bufferSize,
static_cast<uint8_t>(usedSlotVertex), arrayStride);
uint64_t strideCount = static_cast<uint64_t>(firstVertex) + vertexCount;
if (strideCount != 0u) {
uint64_t requiredSize =
(strideCount - 1u) * arrayStride + vertexBuffer.lastStride;
// firstVertex and vertexCount are in uint32_t,
// arrayStride must not be larger than kMaxVertexBufferArrayStride, which is
// currently 2048, and vertexBuffer.lastStride = max(attribute.offset +
// sizeof(attribute.format)) with attribute.offset being no larger than
// kMaxVertexBufferArrayStride, so by doing checks in uint64_t we avoid
// overflows.
DAWN_INVALID_IF(
requiredSize > bufferSize,
"Vertex range (first: %u, count: %u) requires a larger buffer (%u) than "
"the "
"bound buffer size (%u) of the vertex buffer at slot %u with stride %u.",
firstVertex, vertexCount, requiredSize, bufferSize,
static_cast<uint8_t>(usedSlotVertex), arrayStride);
}
}
}
@ -142,17 +149,24 @@ namespace dawn::native {
bufferSize, static_cast<uint8_t>(usedSlotInstance),
vertexBuffer.usedBytesInStride);
} else {
uint64_t requiredSize =
(static_cast<uint64_t>(firstInstance) + instanceCount) * arrayStride;
// firstInstance and instanceCount are in uint32_t, and arrayStride must
// not be larger than kMaxVertexBufferArrayStride, which is currently 2048.
// So by doing checks in uint64_t we avoid overflows.
DAWN_INVALID_IF(
requiredSize > bufferSize,
"Instance range (first: %u, count: %u) requires a larger buffer (%u) than the "
"bound buffer size (%u) of the vertex buffer at slot %u with stride (%u).",
firstInstance, instanceCount, requiredSize, bufferSize,
static_cast<uint8_t>(usedSlotInstance), arrayStride);
uint64_t strideCount = static_cast<uint64_t>(firstInstance) + instanceCount;
if (strideCount != 0u) {
uint64_t requiredSize =
(strideCount - 1u) * arrayStride + vertexBuffer.lastStride;
// firstInstance and instanceCount are in uint32_t,
// arrayStride must not be larger than kMaxVertexBufferArrayStride, which is
// currently 2048, and vertexBuffer.lastStride = max(attribute.offset +
// sizeof(attribute.format)) with attribute.offset being no larger than
// kMaxVertexBufferArrayStride, so by doing checks in uint64_t we avoid
// overflows.
DAWN_INVALID_IF(
requiredSize > bufferSize,
"Instance range (first: %u, count: %u) requires a larger buffer (%u) than "
"the "
"bound buffer size (%u) of the vertex buffer at slot %u with stride %u.",
firstInstance, instanceCount, requiredSize, bufferSize,
static_cast<uint8_t>(usedSlotInstance), arrayStride);
}
}
}

View File

@ -610,6 +610,7 @@ namespace dawn::native {
mVertexBufferInfos[typedSlot].arrayStride = buffers[slot].arrayStride;
mVertexBufferInfos[typedSlot].stepMode = buffers[slot].stepMode;
mVertexBufferInfos[typedSlot].usedBytesInStride = 0;
mVertexBufferInfos[typedSlot].lastStride = 0;
switch (buffers[slot].stepMode) {
case wgpu::VertexStepMode::Vertex:
mVertexBufferSlotsUsedAsVertexBuffer.set(typedSlot);
@ -634,12 +635,16 @@ namespace dawn::native {
// maxVertexBufferArrayStride (2048), which is promised by the GPUVertexBufferLayout
// validation of creating render pipeline. Therefore, calculating in uint16_t will
// cause no overflow.
uint32_t formatByteSize =
GetVertexFormatInfo(buffers[slot].attributes[i].format).byteSize;
DAWN_ASSERT(buffers[slot].attributes[i].offset <= 2048);
uint16_t accessBoundary =
uint16_t(buffers[slot].attributes[i].offset) +
uint16_t(GetVertexFormatInfo(buffers[slot].attributes[i].format).byteSize);
uint16_t(buffers[slot].attributes[i].offset) + uint16_t(formatByteSize);
mVertexBufferInfos[typedSlot].usedBytesInStride =
std::max(mVertexBufferInfos[typedSlot].usedBytesInStride, accessBoundary);
mVertexBufferInfos[typedSlot].lastStride =
std::max(mVertexBufferInfos[typedSlot].lastStride,
mAttributeInfos[location].offset + formatByteSize);
}
}

View File

@ -54,6 +54,9 @@ namespace dawn::native {
uint64_t arrayStride;
wgpu::VertexStepMode stepMode;
uint16_t usedBytesInStride;
// As indicated in the spec, the lastStride is max(attribute.offset +
// sizeof(attribute.format)) for each attribute in the buffer[slot]
uint64_t lastStride;
};
class RenderPipelineBase : public PipelineBase {

View File

@ -25,6 +25,11 @@ class VertexBufferValidationTest : public ValidationTest {
void SetUp() override {
ValidationTest::SetUp();
// dummy vertex shader module
vsModule = utils::CreateShaderModule(device, R"(
@stage(vertex) fn main() -> @builtin(position) vec4<f32> {
return vec4<f32>(0.0, 0.0, 0.0, 0.0);
})");
fsModule = utils::CreateShaderModule(device, R"(
@stage(fragment) fn main() -> @location(0) vec4<f32> {
return vec4<f32>(0.0, 1.0, 0.0, 1.0);
@ -66,6 +71,18 @@ class VertexBufferValidationTest : public ValidationTest {
return utils::CreateShaderModule(device, vs.str().c_str());
}
wgpu::RenderPipeline MakeRenderPipeline(const wgpu::ShaderModule& vsModule,
const utils::ComboVertexState& state) {
utils::ComboRenderPipelineDescriptor descriptor;
descriptor.vertex.module = vsModule;
descriptor.cFragment.module = fsModule;
descriptor.vertex.bufferCount = state.vertexBufferCount;
descriptor.vertex.buffers = &state.cVertexBuffers[0];
return device.CreateRenderPipeline(&descriptor);
}
wgpu::RenderPipeline MakeRenderPipeline(const wgpu::ShaderModule& vsModule,
unsigned int bufferCount) {
utils::ComboRenderPipelineDescriptor descriptor;
@ -83,6 +100,7 @@ class VertexBufferValidationTest : public ValidationTest {
return device.CreateRenderPipeline(&descriptor);
}
wgpu::ShaderModule vsModule;
wgpu::ShaderModule fsModule;
};
@ -354,3 +372,475 @@ TEST_F(VertexBufferValidationTest, OffsetAlignment) {
ASSERT_DEVICE_ERROR(encoder.Finish());
}
}
// Check vertex buffer stride requirements for draw command.
TEST_F(VertexBufferValidationTest, DrawStrideLimitsVertex) {
DummyRenderPass renderPass(device);
// Create a buffer of size 28, containing 4 float32 elements, array stride size = 8
// The last element doesn't have the full stride size
wgpu::BufferDescriptor descriptor;
descriptor.size = 28;
descriptor.usage = wgpu::BufferUsage::Vertex;
wgpu::Buffer vertexBuffer = device.CreateBuffer(&descriptor);
// Vertex attribute offset is 0
wgpu::RenderPipeline pipeline1;
{
utils::ComboVertexState state;
state.vertexBufferCount = 1;
state.cVertexBuffers[0].arrayStride = 8;
state.cVertexBuffers[0].stepMode = wgpu::VertexStepMode::Vertex;
state.cVertexBuffers[0].attributeCount = 1;
state.cAttributes[0].offset = 0;
pipeline1 = MakeRenderPipeline(vsModule, state);
}
// Vertex attribute offset is 4
wgpu::RenderPipeline pipeline2;
{
utils::ComboVertexState state;
state.vertexBufferCount = 1;
state.cVertexBuffers[0].arrayStride = 8;
state.cVertexBuffers[0].stepMode = wgpu::VertexStepMode::Vertex;
state.cVertexBuffers[0].attributeCount = 1;
state.cAttributes[0].offset = 4;
pipeline2 = MakeRenderPipeline(vsModule, state);
}
// Control case: draw 3 elements, 3 * 8 = 24 <= 28, is valid anyway
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
{
wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass);
pass.SetPipeline(pipeline1);
pass.SetVertexBuffer(0, vertexBuffer);
pass.Draw(3);
pass.End();
}
encoder.Finish();
// Valid: draw 3 elements with firstVertex == 1, (2 + 1) * 8 + 4 = 28 <= 28
encoder = device.CreateCommandEncoder();
{
wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass);
pass.SetPipeline(pipeline1);
pass.SetVertexBuffer(0, vertexBuffer);
pass.Draw(3, 0, 1, 0);
pass.End();
}
encoder.Finish();
// Valid: draw 3 elements with offset == 4, 4 + 3 * 8 = 24 <= 28
encoder = device.CreateCommandEncoder();
{
wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass);
pass.SetPipeline(pipeline2);
pass.SetVertexBuffer(0, vertexBuffer);
pass.Draw(3);
pass.End();
}
encoder.Finish();
// Valid: draw 4 elements, 4 * 8 = 32 > 28
// But the last element does not require to have the full stride size
// So 3 * 8 + 4 = 28 <= 28
encoder = device.CreateCommandEncoder();
{
wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass);
pass.SetPipeline(pipeline1);
pass.SetVertexBuffer(0, vertexBuffer);
pass.Draw(4);
pass.End();
}
encoder.Finish();
// Invalid: draw 4 elements with firstVertex == 1
// It requires a buffer with size of (3 + 1) * 8 + 4 = 36 > 28
encoder = device.CreateCommandEncoder();
{
wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass);
pass.SetPipeline(pipeline1);
pass.SetVertexBuffer(0, vertexBuffer);
pass.Draw(4, 0, 1, 0);
pass.End();
}
ASSERT_DEVICE_ERROR(encoder.Finish());
// Invalid: draw 4 elements with offset == 4
// It requires a buffer with size of 4 + 3 * 8 + 4 = 32 > 28
encoder = device.CreateCommandEncoder();
{
wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass);
pass.SetPipeline(pipeline2);
pass.SetVertexBuffer(0, vertexBuffer);
pass.Draw(4);
pass.End();
}
ASSERT_DEVICE_ERROR(encoder.Finish());
// Valid: stride count == 0
encoder = device.CreateCommandEncoder();
{
wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass);
pass.SetPipeline(pipeline2);
pass.SetVertexBuffer(0, vertexBuffer);
pass.Draw(0);
pass.End();
}
encoder.Finish();
// Invalid: stride count == 4
// It requires a buffer with size of 4 + 3 * 8 + 4 = 32 > 28
encoder = device.CreateCommandEncoder();
{
wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass);
pass.SetPipeline(pipeline2);
pass.SetVertexBuffer(0, vertexBuffer);
pass.Draw(0, 0, 4);
pass.End();
}
ASSERT_DEVICE_ERROR(encoder.Finish());
}
// Check instance buffer stride requirements with instanced attributes for draw command.
TEST_F(VertexBufferValidationTest, DrawStrideLimitsInstance) {
DummyRenderPass renderPass(device);
// Create a buffer of size 28, containing 4 float32 elements, array stride size = 8
// The last element doesn't have the full stride size
wgpu::BufferDescriptor descriptor;
descriptor.size = 28;
descriptor.usage = wgpu::BufferUsage::Vertex;
wgpu::Buffer vertexBuffer = device.CreateBuffer(&descriptor);
// Vertex attribute offset is 0
wgpu::RenderPipeline pipeline1;
{
utils::ComboVertexState state;
state.vertexBufferCount = 1;
state.cVertexBuffers[0].arrayStride = 8;
state.cVertexBuffers[0].stepMode = wgpu::VertexStepMode::Instance;
state.cVertexBuffers[0].attributeCount = 1;
state.cAttributes[0].offset = 0;
pipeline1 = MakeRenderPipeline(vsModule, state);
}
// Vertex attribute offset is 4
wgpu::RenderPipeline pipeline2;
{
utils::ComboVertexState state;
state.vertexBufferCount = 1;
state.cVertexBuffers[0].arrayStride = 8;
state.cVertexBuffers[0].stepMode = wgpu::VertexStepMode::Instance;
state.cVertexBuffers[0].attributeCount = 1;
state.cAttributes[0].offset = 4;
pipeline2 = MakeRenderPipeline(vsModule, state);
}
// Control case: draw 3 instances, 3 * 8 = 24 <= 28, is valid anyway
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
{
wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass);
pass.SetPipeline(pipeline1);
pass.SetVertexBuffer(0, vertexBuffer);
pass.Draw(1, 3);
pass.End();
}
encoder.Finish();
// Valid: draw 3 instances with firstInstance == 1, (2 + 1) * 8 + 4 = 28 <= 28
encoder = device.CreateCommandEncoder();
{
wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass);
pass.SetPipeline(pipeline1);
pass.SetVertexBuffer(0, vertexBuffer);
pass.Draw(1, 3, 0, 1);
pass.End();
}
encoder.Finish();
// Valid: draw 3 instances with offset == 4, 4 + 3 * 8 = 24 <= 28
encoder = device.CreateCommandEncoder();
{
wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass);
pass.SetPipeline(pipeline2);
pass.SetVertexBuffer(0, vertexBuffer);
pass.Draw(1, 3);
pass.End();
}
encoder.Finish();
// Valid: draw 4 instances, 4 * 8 = 32 > 28
// But the last element does not require to have the full stride size
// So 3 * 8 + 4 = 28 <= 28
encoder = device.CreateCommandEncoder();
{
wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass);
pass.SetPipeline(pipeline1);
pass.SetVertexBuffer(0, vertexBuffer);
pass.Draw(1, 4);
pass.End();
}
encoder.Finish();
// Invalid: draw 4 instances with firstInstance == 1
// It requires a buffer with size of (3 + 1) * 8 + 4 = 36 > 28
encoder = device.CreateCommandEncoder();
{
wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass);
pass.SetPipeline(pipeline1);
pass.SetVertexBuffer(0, vertexBuffer);
pass.Draw(1, 4, 0, 1);
pass.End();
}
ASSERT_DEVICE_ERROR(encoder.Finish());
// Invalid: draw 4 instances with offset == 4
// It requires a buffer with size of 4 + 3 * 8 + 4 = 32 > 28
encoder = device.CreateCommandEncoder();
{
wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass);
pass.SetPipeline(pipeline2);
pass.SetVertexBuffer(0, vertexBuffer);
pass.Draw(1, 4);
pass.End();
}
ASSERT_DEVICE_ERROR(encoder.Finish());
// Valid: stride count == 0
encoder = device.CreateCommandEncoder();
{
wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass);
pass.SetPipeline(pipeline2);
pass.SetVertexBuffer(0, vertexBuffer);
pass.Draw(1, 0);
pass.End();
}
encoder.Finish();
// Invalid, stride count == 4
// It requires a buffer with size of 4 + 3 * 8 + 4 = 32 > 28
encoder = device.CreateCommandEncoder();
{
wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass);
pass.SetPipeline(pipeline2);
pass.SetVertexBuffer(0, vertexBuffer);
pass.Draw(1, 0, 0, 4);
pass.End();
}
ASSERT_DEVICE_ERROR(encoder.Finish());
}
// Check vertex buffer stride requirements with instanced attributes for draw indexed command.
TEST_F(VertexBufferValidationTest, DrawIndexedStrideLimitsInstance) {
DummyRenderPass renderPass(device);
// Create a buffer of size 28, containing 4 float32 elements, array stride size = 8
// The last element doesn't have the full stride size
wgpu::BufferDescriptor descriptor;
descriptor.size = 28;
descriptor.usage = wgpu::BufferUsage::Vertex;
wgpu::Buffer vertexBuffer = device.CreateBuffer(&descriptor);
wgpu::Buffer indexBuffer =
utils::CreateBufferFromData<uint32_t>(device, wgpu::BufferUsage::Index, {0, 1, 2});
// Vertex attribute offset is 0
wgpu::RenderPipeline pipeline1;
{
utils::ComboVertexState state;
state.vertexBufferCount = 1;
state.cVertexBuffers[0].arrayStride = 8;
state.cVertexBuffers[0].stepMode = wgpu::VertexStepMode::Instance;
state.cVertexBuffers[0].attributeCount = 1;
state.cAttributes[0].offset = 0;
pipeline1 = MakeRenderPipeline(vsModule, state);
}
// Vertex attribute offset is 4
wgpu::RenderPipeline pipeline2;
{
utils::ComboVertexState state;
state.vertexBufferCount = 1;
state.cVertexBuffers[0].arrayStride = 8;
state.cVertexBuffers[0].stepMode = wgpu::VertexStepMode::Instance;
state.cVertexBuffers[0].attributeCount = 1;
state.cAttributes[0].offset = 4;
pipeline2 = MakeRenderPipeline(vsModule, state);
}
// Control case: draw 3 instances, 3 * 8 = 24 <= 28, is valid anyway
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
{
wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass);
pass.SetPipeline(pipeline1);
pass.SetVertexBuffer(0, vertexBuffer);
pass.SetIndexBuffer(indexBuffer, wgpu::IndexFormat::Uint32);
pass.DrawIndexed(3, 3);
pass.End();
}
encoder.Finish();
// Valid: draw 3 instances with firstInstance == 1, (2 + 1) * 8 + 4 = 28 <= 28
encoder = device.CreateCommandEncoder();
{
wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass);
pass.SetPipeline(pipeline1);
pass.SetVertexBuffer(0, vertexBuffer);
pass.SetIndexBuffer(indexBuffer, wgpu::IndexFormat::Uint32);
pass.Draw(3, 3, 0, 1);
pass.End();
}
encoder.Finish();
// Valid: draw 3 instances with offset == 4, 4 + 3 * 8 = 24 <= 28
encoder = device.CreateCommandEncoder();
{
wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass);
pass.SetPipeline(pipeline2);
pass.SetVertexBuffer(0, vertexBuffer);
pass.SetIndexBuffer(indexBuffer, wgpu::IndexFormat::Uint32);
pass.Draw(3, 3);
pass.End();
}
encoder.Finish();
// Valid: draw 4 instances, 4 * 8 = 32 > 28
// But the last element does not require to have the full stride size
// So 3 * 8 + 4 = 28 <= 28
encoder = device.CreateCommandEncoder();
{
wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass);
pass.SetPipeline(pipeline1);
pass.SetVertexBuffer(0, vertexBuffer);
pass.SetIndexBuffer(indexBuffer, wgpu::IndexFormat::Uint32);
pass.Draw(3, 4);
pass.End();
}
encoder.Finish();
// Invalid: draw 4 instances with firstInstance == 1
// It requires a buffer with size of (3 + 1) * 8 + 4 = 36 > 28
encoder = device.CreateCommandEncoder();
{
wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass);
pass.SetPipeline(pipeline1);
pass.SetVertexBuffer(0, vertexBuffer);
pass.SetIndexBuffer(indexBuffer, wgpu::IndexFormat::Uint32);
pass.Draw(3, 4, 0, 1);
pass.End();
}
ASSERT_DEVICE_ERROR(encoder.Finish());
// Invalid: draw 4 instances with offset == 4
// It requires a buffer with size of 4 + 3 * 8 + 4 = 32 > 28
encoder = device.CreateCommandEncoder();
{
wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass);
pass.SetPipeline(pipeline2);
pass.SetVertexBuffer(0, vertexBuffer);
pass.SetIndexBuffer(indexBuffer, wgpu::IndexFormat::Uint32);
pass.Draw(3, 4);
pass.End();
}
ASSERT_DEVICE_ERROR(encoder.Finish());
// Valid: stride count == 0
encoder = device.CreateCommandEncoder();
{
wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass);
pass.SetPipeline(pipeline2);
pass.SetVertexBuffer(0, vertexBuffer);
pass.Draw(3, 0);
pass.End();
}
encoder.Finish();
// Invalid, stride count == 4
// It requires a buffer with size of 4 + 3 * 8 + 4 = 32 > 28
encoder = device.CreateCommandEncoder();
{
wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass);
pass.SetPipeline(pipeline2);
pass.SetVertexBuffer(0, vertexBuffer);
pass.SetIndexBuffer(indexBuffer, wgpu::IndexFormat::Uint32);
pass.Draw(3, 0, 0, 4);
pass.End();
}
ASSERT_DEVICE_ERROR(encoder.Finish());
}
// Check last stride is computed correctly for vertex buffer with multiple attributes.
TEST_F(VertexBufferValidationTest, DrawStrideLimitsVertexMultipleAttributes) {
DummyRenderPass renderPass(device);
// Create a buffer of size 44, array stride size = 12
wgpu::BufferDescriptor descriptor;
descriptor.size = 44;
descriptor.usage = wgpu::BufferUsage::Vertex;
wgpu::Buffer vertexBuffer = device.CreateBuffer(&descriptor);
// lastStride = attribute[1].offset + sizeof(attribute[1].format) = 8
wgpu::RenderPipeline pipeline1;
{
utils::ComboVertexState state;
state.vertexBufferCount = 1;
state.cVertexBuffers[0].arrayStride = 12;
state.cVertexBuffers[0].stepMode = wgpu::VertexStepMode::Vertex;
state.cVertexBuffers[0].attributeCount = 2;
state.cAttributes[0].format = wgpu::VertexFormat::Float32;
state.cAttributes[0].offset = 0;
state.cAttributes[0].shaderLocation = 0;
state.cAttributes[1].format = wgpu::VertexFormat::Float32;
state.cAttributes[1].offset = 4;
state.cAttributes[1].shaderLocation = 1;
pipeline1 = MakeRenderPipeline(vsModule, state);
}
// lastStride = attribute[1].offset + sizeof(attribute[1].format) = 12
wgpu::RenderPipeline pipeline2;
{
utils::ComboVertexState state;
state.vertexBufferCount = 1;
state.cVertexBuffers[0].arrayStride = 12;
state.cVertexBuffers[0].stepMode = wgpu::VertexStepMode::Vertex;
state.cVertexBuffers[0].attributeCount = 2;
state.cAttributes[0].format = wgpu::VertexFormat::Float32;
state.cAttributes[0].offset = 0;
state.cAttributes[0].shaderLocation = 0;
state.cAttributes[1].format = wgpu::VertexFormat::Float32x2;
state.cAttributes[1].offset = 4;
state.cAttributes[1].shaderLocation = 1;
pipeline2 = MakeRenderPipeline(vsModule, state);
}
// Valid: draw 4 elements, last stride is 8, 3 * 12 + 8 = 44 <= 44
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
{
wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass);
pass.SetPipeline(pipeline1);
pass.SetVertexBuffer(0, vertexBuffer);
pass.Draw(4);
pass.End();
}
encoder.Finish();
// Invalid: draw 4 elements, last stride is 12, 3 * 12 + 12 = 48 > 44
encoder = device.CreateCommandEncoder();
{
wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass);
pass.SetPipeline(pipeline2);
pass.SetVertexBuffer(0, vertexBuffer);
pass.Draw(4);
pass.End();
}
ASSERT_DEVICE_ERROR(encoder.Finish());
}

View File

@ -84,7 +84,6 @@ TEST_F(VertexStateTest, NullBufferIsOk) {
CreatePipeline(true, state, kDummyVertexShader);
}
// Check validation that pipeline vertex buffers are backed by attributes in the vertex input
// Check validation that pipeline vertex buffers are backed by attributes in the vertex input
TEST_F(VertexStateTest, PipelineCompatibility) {
utils::ComboVertexState state;