Dawn: vertex buffer never OOB with zero stride count draw/Indexed
According to the spec, when call draw or drawIndexed, vertex step mode vertex buffer never OOB if (vertexCount + firstVertex) = 0, and instance step mode vertex buffer never OOB if (instanceCount + firstInstance) = 0. Modify the validation implementation to be aligned with the spec, and add corresponding unit tests. This patch also add unit test case for (strideCount - 1) * arrayStride + lastStride <= bound buffer size < strideCount * arrayStride. Bug: dawn:1287 Change-Id: If444e400f5ac24f86ca12ff59fb886d8ef70e8c7 Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/90584 Kokoro: Kokoro <noreply+kokoro@google.com> Reviewed-by: Corentin Wallez <cwallez@chromium.org> Reviewed-by: Austin Eng <enga@chromium.org> Commit-Queue: Zhaoming Jiang <zhaoming.jiang@intel.com>
This commit is contained in:
parent
edbd5a467b
commit
4bdded68d5
|
@ -98,6 +98,13 @@ MaybeError CommandBufferStateTracker::ValidateCanDrawIndexed() {
|
||||||
|
|
||||||
MaybeError CommandBufferStateTracker::ValidateBufferInRangeForVertexBuffer(uint32_t vertexCount,
|
MaybeError CommandBufferStateTracker::ValidateBufferInRangeForVertexBuffer(uint32_t vertexCount,
|
||||||
uint32_t firstVertex) {
|
uint32_t firstVertex) {
|
||||||
|
uint64_t strideCount = static_cast<uint64_t>(firstVertex) + vertexCount;
|
||||||
|
|
||||||
|
if (strideCount == 0) {
|
||||||
|
// All vertex step mode buffers are always in range if stride count is zero
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
RenderPipelineBase* lastRenderPipeline = GetRenderPipeline();
|
RenderPipelineBase* lastRenderPipeline = GetRenderPipeline();
|
||||||
|
|
||||||
const ityp::bitset<VertexBufferSlot, kMaxVertexBuffers>& vertexBufferSlotsUsedAsVertexBuffer =
|
const ityp::bitset<VertexBufferSlot, kMaxVertexBuffers>& vertexBufferSlotsUsedAsVertexBuffer =
|
||||||
|
@ -115,23 +122,21 @@ MaybeError CommandBufferStateTracker::ValidateBufferInRangeForVertexBuffer(uint3
|
||||||
bufferSize, static_cast<uint8_t>(usedSlotVertex),
|
bufferSize, static_cast<uint8_t>(usedSlotVertex),
|
||||||
vertexBuffer.usedBytesInStride);
|
vertexBuffer.usedBytesInStride);
|
||||||
} else {
|
} else {
|
||||||
uint64_t strideCount = static_cast<uint64_t>(firstVertex) + vertexCount;
|
DAWN_ASSERT(strideCount != 0u);
|
||||||
if (strideCount != 0u) {
|
uint64_t requiredSize = (strideCount - 1u) * arrayStride + vertexBuffer.lastStride;
|
||||||
uint64_t requiredSize = (strideCount - 1u) * arrayStride + vertexBuffer.lastStride;
|
// firstVertex and vertexCount are in uint32_t,
|
||||||
// firstVertex and vertexCount are in uint32_t,
|
// arrayStride must not be larger than kMaxVertexBufferArrayStride, which is
|
||||||
// arrayStride must not be larger than kMaxVertexBufferArrayStride, which is
|
// currently 2048, and vertexBuffer.lastStride = max(attribute.offset +
|
||||||
// currently 2048, and vertexBuffer.lastStride = max(attribute.offset +
|
// sizeof(attribute.format)) with attribute.offset being no larger than
|
||||||
// sizeof(attribute.format)) with attribute.offset being no larger than
|
// kMaxVertexBufferArrayStride, so by doing checks in uint64_t we avoid
|
||||||
// kMaxVertexBufferArrayStride, so by doing checks in uint64_t we avoid
|
// overflows.
|
||||||
// overflows.
|
DAWN_INVALID_IF(
|
||||||
DAWN_INVALID_IF(
|
requiredSize > bufferSize,
|
||||||
requiredSize > bufferSize,
|
"Vertex range (first: %u, count: %u) requires a larger buffer (%u) than "
|
||||||
"Vertex range (first: %u, count: %u) requires a larger buffer (%u) than "
|
"the "
|
||||||
"the "
|
"bound buffer size (%u) of the vertex buffer at slot %u with stride %u.",
|
||||||
"bound buffer size (%u) of the vertex buffer at slot %u with stride %u.",
|
firstVertex, vertexCount, requiredSize, bufferSize,
|
||||||
firstVertex, vertexCount, requiredSize, bufferSize,
|
static_cast<uint8_t>(usedSlotVertex), arrayStride);
|
||||||
static_cast<uint8_t>(usedSlotVertex), arrayStride);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -141,6 +146,13 @@ MaybeError CommandBufferStateTracker::ValidateBufferInRangeForVertexBuffer(uint3
|
||||||
MaybeError CommandBufferStateTracker::ValidateBufferInRangeForInstanceBuffer(
|
MaybeError CommandBufferStateTracker::ValidateBufferInRangeForInstanceBuffer(
|
||||||
uint32_t instanceCount,
|
uint32_t instanceCount,
|
||||||
uint32_t firstInstance) {
|
uint32_t firstInstance) {
|
||||||
|
uint64_t strideCount = static_cast<uint64_t>(firstInstance) + instanceCount;
|
||||||
|
|
||||||
|
if (strideCount == 0) {
|
||||||
|
// All instance step mode buffers are always in range if stride count is zero
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
RenderPipelineBase* lastRenderPipeline = GetRenderPipeline();
|
RenderPipelineBase* lastRenderPipeline = GetRenderPipeline();
|
||||||
|
|
||||||
const ityp::bitset<VertexBufferSlot, kMaxVertexBuffers>& vertexBufferSlotsUsedAsInstanceBuffer =
|
const ityp::bitset<VertexBufferSlot, kMaxVertexBuffers>& vertexBufferSlotsUsedAsInstanceBuffer =
|
||||||
|
@ -158,23 +170,21 @@ MaybeError CommandBufferStateTracker::ValidateBufferInRangeForInstanceBuffer(
|
||||||
bufferSize, static_cast<uint8_t>(usedSlotInstance),
|
bufferSize, static_cast<uint8_t>(usedSlotInstance),
|
||||||
vertexBuffer.usedBytesInStride);
|
vertexBuffer.usedBytesInStride);
|
||||||
} else {
|
} else {
|
||||||
uint64_t strideCount = static_cast<uint64_t>(firstInstance) + instanceCount;
|
DAWN_ASSERT(strideCount != 0u);
|
||||||
if (strideCount != 0u) {
|
uint64_t requiredSize = (strideCount - 1u) * arrayStride + vertexBuffer.lastStride;
|
||||||
uint64_t requiredSize = (strideCount - 1u) * arrayStride + vertexBuffer.lastStride;
|
// firstInstance and instanceCount are in uint32_t,
|
||||||
// firstInstance and instanceCount are in uint32_t,
|
// arrayStride must not be larger than kMaxVertexBufferArrayStride, which is
|
||||||
// arrayStride must not be larger than kMaxVertexBufferArrayStride, which is
|
// currently 2048, and vertexBuffer.lastStride = max(attribute.offset +
|
||||||
// currently 2048, and vertexBuffer.lastStride = max(attribute.offset +
|
// sizeof(attribute.format)) with attribute.offset being no larger than
|
||||||
// sizeof(attribute.format)) with attribute.offset being no larger than
|
// kMaxVertexBufferArrayStride, so by doing checks in uint64_t we avoid
|
||||||
// kMaxVertexBufferArrayStride, so by doing checks in uint64_t we avoid
|
// overflows.
|
||||||
// overflows.
|
DAWN_INVALID_IF(
|
||||||
DAWN_INVALID_IF(
|
requiredSize > bufferSize,
|
||||||
requiredSize > bufferSize,
|
"Instance range (first: %u, count: %u) requires a larger buffer (%u) than "
|
||||||
"Instance range (first: %u, count: %u) requires a larger buffer (%u) than "
|
"the "
|
||||||
"the "
|
"bound buffer size (%u) of the vertex buffer at slot %u with stride %u.",
|
||||||
"bound buffer size (%u) of the vertex buffer at slot %u with stride %u.",
|
firstInstance, instanceCount, requiredSize, bufferSize,
|
||||||
firstInstance, instanceCount, requiredSize, bufferSize,
|
static_cast<uint8_t>(usedSlotInstance), arrayStride);
|
||||||
static_cast<uint8_t>(usedSlotInstance), arrayStride);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -129,10 +129,7 @@ void RenderEncoderBase::APIDrawIndexed(uint32_t indexCount,
|
||||||
|
|
||||||
DAWN_TRY(mCommandBufferState.ValidateIndexBufferInRange(indexCount, firstIndex));
|
DAWN_TRY(mCommandBufferState.ValidateIndexBufferInRange(indexCount, firstIndex));
|
||||||
|
|
||||||
// Although we don't know actual vertex access range in CPU, we still call the
|
// DrawIndexed only validate instance step mode vertex buffer
|
||||||
// ValidateBufferInRangeForVertexBuffer in order to deal with those vertex step
|
|
||||||
// mode vertex buffer with an array stride of zero.
|
|
||||||
DAWN_TRY(mCommandBufferState.ValidateBufferInRangeForVertexBuffer(0, 0));
|
|
||||||
DAWN_TRY(mCommandBufferState.ValidateBufferInRangeForInstanceBuffer(instanceCount,
|
DAWN_TRY(mCommandBufferState.ValidateBufferInRangeForInstanceBuffer(instanceCount,
|
||||||
firstInstance));
|
firstInstance));
|
||||||
}
|
}
|
||||||
|
|
|
@ -304,6 +304,12 @@ class DrawVertexAndIndexBufferOOBValidationTests : public ValidationTest {
|
||||||
// Non-zero offset and size
|
// Non-zero offset and size
|
||||||
{(2 * kFloat32x4Stride), 5 * (2 * kFloat32x4Stride), (2 * kFloat32x4Stride),
|
{(2 * kFloat32x4Stride), 5 * (2 * kFloat32x4Stride), (2 * kFloat32x4Stride),
|
||||||
3 * (2 * kFloat32x4Stride), 3},
|
3 * (2 * kFloat32x4Stride), 3},
|
||||||
|
// For (strideCount - 1) * arrayStride + lastStride <= bound buffer size < strideCount *
|
||||||
|
// arrayStride
|
||||||
|
{(kFloat32x4Stride + 4), 2 * (kFloat32x4Stride + 4) + kFloat32x4Stride, 0, wgpu::kWholeSize,
|
||||||
|
3},
|
||||||
|
{(kFloat32x4Stride + 4), 2 * (kFloat32x4Stride + 4) + kFloat32x4Stride - 1, 0,
|
||||||
|
wgpu::kWholeSize, 2},
|
||||||
};
|
};
|
||||||
// Parameters list for instance-step-mode buffer.
|
// Parameters list for instance-step-mode buffer.
|
||||||
const std::vector<VertexBufferParams> kInstanceParamsList = {
|
const std::vector<VertexBufferParams> kInstanceParamsList = {
|
||||||
|
@ -325,6 +331,12 @@ class DrawVertexAndIndexBufferOOBValidationTests : public ValidationTest {
|
||||||
// Non-zero offset and size
|
// Non-zero offset and size
|
||||||
{(3 * kFloat32x2Stride), 7 * (3 * kFloat32x2Stride), (3 * kFloat32x2Stride),
|
{(3 * kFloat32x2Stride), 7 * (3 * kFloat32x2Stride), (3 * kFloat32x2Stride),
|
||||||
5 * (3 * kFloat32x2Stride), 5},
|
5 * (3 * kFloat32x2Stride), 5},
|
||||||
|
// For (strideCount - 1) * arrayStride + lastStride <= bound buffer size < strideCount *
|
||||||
|
// arrayStride
|
||||||
|
{(kFloat32x2Stride + 4), 2 * (kFloat32x2Stride + 4) + kFloat32x2Stride, 0, wgpu::kWholeSize,
|
||||||
|
3},
|
||||||
|
{(kFloat32x2Stride + 4), 2 * (kFloat32x2Stride + 4) + kFloat32x2Stride - 1, 0,
|
||||||
|
wgpu::kWholeSize, 2},
|
||||||
};
|
};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -341,7 +353,9 @@ TEST_F(DrawVertexAndIndexBufferOOBValidationTests, DrawBasic) {
|
||||||
{
|
{
|
||||||
// Implicit size
|
// Implicit size
|
||||||
VertexBufferList vertexBufferList = {{0, vertexBuffer, 0, wgpu::kWholeSize}};
|
VertexBufferList vertexBufferList = {{0, vertexBuffer, 0, wgpu::kWholeSize}};
|
||||||
TestRenderPassDraw(pipeline, vertexBufferList, 3, 1, 0, 0, true);
|
TestRenderPassDraw(pipeline, vertexBufferList, /* vertexCount */ 3,
|
||||||
|
/* instanceCount */ 1, /* firstVertex */ 0,
|
||||||
|
/* firstInstance */ 0, /* isSuccess */ true);
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
|
@ -362,9 +376,12 @@ TEST_F(DrawVertexAndIndexBufferOOBValidationTests, DrawVertexBufferOutOfBoundWit
|
||||||
VertexBufferList vertexBufferList = {
|
VertexBufferList vertexBufferList = {
|
||||||
{0, vertexBuffer, params.bufferOffsetForEncoder, params.bufferSizeForEncoder}};
|
{0, vertexBuffer, params.bufferOffsetForEncoder, params.bufferSizeForEncoder}};
|
||||||
|
|
||||||
|
DAWN_ASSERT(params.maxValidAccessNumber > 0);
|
||||||
uint32_t n = params.maxValidAccessNumber;
|
uint32_t n = params.maxValidAccessNumber;
|
||||||
// It is ok to draw n vertices with vertex buffer
|
// It is ok to draw n vertices with vertex buffer
|
||||||
TestRenderPassDraw(pipeline, vertexBufferList, n, 1, 0, 0, true);
|
TestRenderPassDraw(pipeline, vertexBufferList, /* vertexCount */ n,
|
||||||
|
/* instanceCount */ 1, /* firstVertex */ 0,
|
||||||
|
/* firstInstance */ 0, /* isSuccess */ true);
|
||||||
// It is ok to draw n-1 vertices with offset 1
|
// It is ok to draw n-1 vertices with offset 1
|
||||||
TestRenderPassDraw(pipeline, vertexBufferList, n - 1, 1, 1, 0, true);
|
TestRenderPassDraw(pipeline, vertexBufferList, n - 1, 1, 1, 0, true);
|
||||||
// Drawing more vertices will cause OOB, even if not enough for another primitive
|
// Drawing more vertices will cause OOB, even if not enough for another primitive
|
||||||
|
@ -396,10 +413,14 @@ TEST_F(DrawVertexAndIndexBufferOOBValidationTests, DrawVertexBufferOutOfBoundWit
|
||||||
instanceParams.bufferSizeForEncoder},
|
instanceParams.bufferSizeForEncoder},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
DAWN_ASSERT(vertexParams.maxValidAccessNumber > 0);
|
||||||
|
DAWN_ASSERT(instanceParams.maxValidAccessNumber > 0);
|
||||||
uint32_t vert = vertexParams.maxValidAccessNumber;
|
uint32_t vert = vertexParams.maxValidAccessNumber;
|
||||||
uint32_t inst = instanceParams.maxValidAccessNumber;
|
uint32_t inst = instanceParams.maxValidAccessNumber;
|
||||||
// It is ok to draw vert vertices
|
// It is ok to draw vert vertices
|
||||||
TestRenderPassDraw(pipeline, vertexBufferList, vert, 1, 0, 0, true);
|
TestRenderPassDraw(pipeline, vertexBufferList, /* vertexCount */ vert,
|
||||||
|
/* instanceCount */ 1, /* firstVertex */ 0,
|
||||||
|
/* firstInstance */ 0, /* isSuccess */ true);
|
||||||
TestRenderPassDraw(pipeline, vertexBufferList, vert - 1, 1, 1, 0, true);
|
TestRenderPassDraw(pipeline, vertexBufferList, vert - 1, 1, 1, 0, true);
|
||||||
// It is ok to draw vert vertices and inst instences
|
// It is ok to draw vert vertices and inst instences
|
||||||
TestRenderPassDraw(pipeline, vertexBufferList, vert, inst, 0, 0, true);
|
TestRenderPassDraw(pipeline, vertexBufferList, vert, inst, 0, 0, true);
|
||||||
|
@ -432,7 +453,9 @@ TEST_F(DrawVertexAndIndexBufferOOBValidationTests, DrawIndexedBasic) {
|
||||||
|
|
||||||
IndexBufferDesc indexBufferDesc = {indexBuffer, wgpu::IndexFormat::Uint32};
|
IndexBufferDesc indexBufferDesc = {indexBuffer, wgpu::IndexFormat::Uint32};
|
||||||
|
|
||||||
TestRenderPassDrawIndexed(pipeline, indexBufferDesc, vertexBufferList, 12, 1, 0, 0, 0, true);
|
TestRenderPassDrawIndexed(pipeline, indexBufferDesc, vertexBufferList, /* indexCount */ 12,
|
||||||
|
/* instanceCount */ 1, /* firstIndex */ 0, /* baseVertex */ 0,
|
||||||
|
/* firstInstance */ 0, /* isSuccess */ true);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify index buffer OOB for DrawIndexed are caught in command encoder
|
// Verify index buffer OOB for DrawIndexed are caught in command encoder
|
||||||
|
@ -454,10 +477,13 @@ TEST_F(DrawVertexAndIndexBufferOOBValidationTests, DrawIndexedIndexBufferOOB) {
|
||||||
params.indexBufferOffsetForEncoder,
|
params.indexBufferOffsetForEncoder,
|
||||||
params.indexBufferSizeForEncoder};
|
params.indexBufferSizeForEncoder};
|
||||||
|
|
||||||
|
DAWN_ASSERT(params.maxValidIndexNumber > 0);
|
||||||
uint32_t n = params.maxValidIndexNumber;
|
uint32_t n = params.maxValidIndexNumber;
|
||||||
|
|
||||||
// Control case
|
// Control case
|
||||||
TestRenderPassDrawIndexed(pipeline, indexBufferDesc, vertexBufferList, n, 5, 0, 0, 0, true);
|
TestRenderPassDrawIndexed(pipeline, indexBufferDesc, vertexBufferList, /* indexCount */ n,
|
||||||
|
/* instanceCount */ 5, /* firstIndex */ 0, /* baseVertex */ 0,
|
||||||
|
/* firstInstance */ 0, /* isSuccess */ true);
|
||||||
TestRenderPassDrawIndexed(pipeline, indexBufferDesc, vertexBufferList, n - 1, 5, 1, 0, 0,
|
TestRenderPassDrawIndexed(pipeline, indexBufferDesc, vertexBufferList, n - 1, 5, 1, 0, 0,
|
||||||
true);
|
true);
|
||||||
// Index buffer OOB, indexCount too large
|
// Index buffer OOB, indexCount too large
|
||||||
|
@ -487,8 +513,8 @@ TEST_F(DrawVertexAndIndexBufferOOBValidationTests, DrawIndexedVertexBufferOOB) {
|
||||||
wgpu::RenderPipeline pipeline = CreateBasicRenderPipelineWithInstance(
|
wgpu::RenderPipeline pipeline = CreateBasicRenderPipelineWithInstance(
|
||||||
vertexParams.bufferStride, instanceParams.bufferStride);
|
vertexParams.bufferStride, instanceParams.bufferStride);
|
||||||
|
|
||||||
auto indexFormat = wgpu::IndexFormat::Uint32;
|
constexpr wgpu::IndexFormat indexFormat = wgpu::IndexFormat::Uint32;
|
||||||
auto indexStride = sizeof(uint32_t);
|
constexpr uint64_t indexStride = sizeof(uint32_t);
|
||||||
|
|
||||||
// Build index buffer for 12 indexes
|
// Build index buffer for 12 indexes
|
||||||
wgpu::Buffer indexBuffer = CreateBuffer(12 * indexStride, wgpu::BufferUsage::Index);
|
wgpu::Buffer indexBuffer = CreateBuffer(12 * indexStride, wgpu::BufferUsage::Index);
|
||||||
|
@ -505,10 +531,13 @@ TEST_F(DrawVertexAndIndexBufferOOBValidationTests, DrawIndexedVertexBufferOOB) {
|
||||||
|
|
||||||
IndexBufferDesc indexBufferDesc = {indexBuffer, indexFormat};
|
IndexBufferDesc indexBufferDesc = {indexBuffer, indexFormat};
|
||||||
|
|
||||||
|
DAWN_ASSERT(instanceParams.maxValidAccessNumber > 0);
|
||||||
uint32_t inst = instanceParams.maxValidAccessNumber;
|
uint32_t inst = instanceParams.maxValidAccessNumber;
|
||||||
// Control case
|
// Control case
|
||||||
TestRenderPassDrawIndexed(pipeline, indexBufferDesc, vertexBufferList, 12, inst, 0, 0,
|
TestRenderPassDrawIndexed(
|
||||||
0, true);
|
pipeline, indexBufferDesc, vertexBufferList, /* indexCount */ 12,
|
||||||
|
/* instanceCount */ inst, /* firstIndex */ 0, /* baseVertex */ 0,
|
||||||
|
/* firstInstance */ 0, /* isSuccess */ true);
|
||||||
// Vertex buffer (stepMode = instance) OOB, instanceCount too large
|
// Vertex buffer (stepMode = instance) OOB, instanceCount too large
|
||||||
TestRenderPassDrawIndexed(pipeline, indexBufferDesc, vertexBufferList, 12, inst + 1, 0,
|
TestRenderPassDrawIndexed(pipeline, indexBufferDesc, vertexBufferList, 12, inst + 1, 0,
|
||||||
0, 0, false);
|
0, 0, false);
|
||||||
|
@ -527,8 +556,10 @@ TEST_F(DrawVertexAndIndexBufferOOBValidationTests, DrawIndexedVertexBufferOOB) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify instance mode vertex buffer OOB for DrawIndexed are caught in command encoder
|
// Verify zero array stride vertex buffer OOB for Draw and DrawIndexed are caught in command encoder
|
||||||
TEST_F(DrawVertexAndIndexBufferOOBValidationTests, ZeroArrayStrideVertexBufferOOB) {
|
// This test only test cases that strideCount > 0. Cases of strideCount == 0 are tested in
|
||||||
|
// ZeroStrideCountVertexBufferNeverOOB.
|
||||||
|
TEST_F(DrawVertexAndIndexBufferOOBValidationTests, ZeroArrayStrideVertexBuffer) {
|
||||||
// In this test, we use VertexBufferParams.maxValidAccessNumber > 0 to indicate that such
|
// In this test, we use VertexBufferParams.maxValidAccessNumber > 0 to indicate that such
|
||||||
// buffer parameter meet the requirement of pipeline, and maxValidAccessNumber == 0 to
|
// buffer parameter meet the requirement of pipeline, and maxValidAccessNumber == 0 to
|
||||||
// indicate that such buffer parameter will cause OOB.
|
// indicate that such buffer parameter will cause OOB.
|
||||||
|
@ -569,8 +600,8 @@ TEST_F(DrawVertexAndIndexBufferOOBValidationTests, ZeroArrayStrideVertexBufferOO
|
||||||
|
|
||||||
for (VertexBufferParams vertexParams : kVertexParamsListForZeroStride) {
|
for (VertexBufferParams vertexParams : kVertexParamsListForZeroStride) {
|
||||||
for (VertexBufferParams instanceParams : kInstanceParamsListForZeroStride) {
|
for (VertexBufferParams instanceParams : kInstanceParamsListForZeroStride) {
|
||||||
auto indexFormat = wgpu::IndexFormat::Uint32;
|
constexpr wgpu::IndexFormat indexFormat = wgpu::IndexFormat::Uint32;
|
||||||
auto indexStride = sizeof(uint32_t);
|
constexpr uint64_t indexStride = sizeof(uint32_t);
|
||||||
|
|
||||||
// Build index buffer for 12 indexes
|
// Build index buffer for 12 indexes
|
||||||
wgpu::Buffer indexBuffer = CreateBuffer(12 * indexStride, wgpu::BufferUsage::Index);
|
wgpu::Buffer indexBuffer = CreateBuffer(12 * indexStride, wgpu::BufferUsage::Index);
|
||||||
|
@ -587,14 +618,137 @@ TEST_F(DrawVertexAndIndexBufferOOBValidationTests, ZeroArrayStrideVertexBufferOO
|
||||||
|
|
||||||
IndexBufferDesc indexBufferDesc = {indexBuffer, indexFormat};
|
IndexBufferDesc indexBufferDesc = {indexBuffer, indexFormat};
|
||||||
|
|
||||||
const bool isSuccess = (vertexParams.maxValidAccessNumber > 0) &&
|
const bool vertexModeBufferOOB = vertexParams.maxValidAccessNumber == 0;
|
||||||
(instanceParams.maxValidAccessNumber > 0);
|
const bool instanceModeBufferOOB = instanceParams.maxValidAccessNumber == 0;
|
||||||
|
|
||||||
|
// Draw validate both vertex and instance step mode buffer OOB.
|
||||||
// vertexCount and instanceCount doesn't matter, as array stride is zero and all
|
// vertexCount and instanceCount doesn't matter, as array stride is zero and all
|
||||||
// vertex/instance access the same space of buffer
|
// vertex/instance access the same space of buffer, as long as both (vertexCount +
|
||||||
TestRenderPassDraw(pipeline, vertexBufferList, 100, 100, 0, 0, isSuccess);
|
// firstVertex) and (instanceCount + firstInstance) are larger than zero.
|
||||||
// indexCount doesn't matter as long as no index buffer OOB happened
|
TestRenderPassDraw(pipeline, vertexBufferList, /* vertexCount */ 100,
|
||||||
TestRenderPassDrawIndexed(pipeline, indexBufferDesc, vertexBufferList, 12, 100, 0, 0, 0,
|
/* instanceCount */ 100, /* firstVertex */ 0,
|
||||||
isSuccess);
|
/* firstInstance */ 0,
|
||||||
|
/* isSuccess */ !vertexModeBufferOOB && !instanceModeBufferOOB);
|
||||||
|
TestRenderPassDraw(pipeline, vertexBufferList, 100, 100, 100, 100,
|
||||||
|
!vertexModeBufferOOB && !instanceModeBufferOOB);
|
||||||
|
TestRenderPassDraw(pipeline, vertexBufferList, 0, 0, 100, 100,
|
||||||
|
!vertexModeBufferOOB && !instanceModeBufferOOB);
|
||||||
|
|
||||||
|
// DrawIndexed only validate instance step mode buffer OOB.
|
||||||
|
// indexCount doesn't matter as long as no index buffer OOB happened, and instanceCount
|
||||||
|
// doesn't matter as long as (instanceCount + firstInstance) are larger than zero.
|
||||||
|
TestRenderPassDrawIndexed(
|
||||||
|
pipeline, indexBufferDesc, vertexBufferList, /* indexCount */ 12,
|
||||||
|
/* instanceCount */ 100, /* firstIndex */ 0, /* baseVertex */ 0,
|
||||||
|
/* firstInstance */ 0, /* isSuccess */ !instanceModeBufferOOB);
|
||||||
|
TestRenderPassDrawIndexed(pipeline, indexBufferDesc, vertexBufferList, 12, 0, 0, 0, 100,
|
||||||
|
!instanceModeBufferOOB);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify all vertex buffer never OOB for Draw and DrawIndexed with zero stride count
|
||||||
|
TEST_F(DrawVertexAndIndexBufferOOBValidationTests, ZeroStrideCountVertexBufferNeverOOB) {
|
||||||
|
// In this test we use a render pipeline with non-zero array stride and a render pipeline with
|
||||||
|
// zero array stride, both use a vertex step mode vertex buffer with lastStride = 16 and a
|
||||||
|
// instance step mode vertex buffer with lastStride = 8. Binding a buffer with size less than
|
||||||
|
// lastStride to corresponding buffer slot will result in a OOB validation error is strideCount
|
||||||
|
// is larger than 0, but shall not result in validation error if strideCount == 0.
|
||||||
|
|
||||||
|
// Create the render pipeline with non-zero array stride (larger than lastStride)
|
||||||
|
std::vector<PipelineVertexBufferDesc> bufferDescListNonZeroArrayStride = {
|
||||||
|
{20u, wgpu::VertexStepMode::Vertex, {{0, wgpu::VertexFormat::Float32x4}}},
|
||||||
|
{12u, wgpu::VertexStepMode::Instance, {{3, wgpu::VertexFormat::Float32x2}}},
|
||||||
|
};
|
||||||
|
wgpu::RenderPipeline pipelineWithNonZeroArrayStride =
|
||||||
|
CreateRenderPipelineWithBufferDesc(bufferDescListNonZeroArrayStride);
|
||||||
|
|
||||||
|
// Create the render pipeline with zero array stride
|
||||||
|
std::vector<PipelineVertexBufferDesc> bufferDescListZeroArrayStride = {
|
||||||
|
{0u, wgpu::VertexStepMode::Vertex, {{0, wgpu::VertexFormat::Float32x4}}},
|
||||||
|
{0u, wgpu::VertexStepMode::Instance, {{3, wgpu::VertexFormat::Float32x2}}},
|
||||||
|
};
|
||||||
|
wgpu::RenderPipeline pipelineWithZeroArrayStride =
|
||||||
|
CreateRenderPipelineWithBufferDesc(bufferDescListZeroArrayStride);
|
||||||
|
|
||||||
|
const std::vector<VertexBufferParams> kVertexParamsListForZeroStride = {
|
||||||
|
// Size enough for 1 vertex
|
||||||
|
{kFloat32x4Stride, 16, 0, wgpu::kWholeSize, 1},
|
||||||
|
// No enough size for 1 vertex
|
||||||
|
{kFloat32x4Stride, 19, 4, wgpu::kWholeSize, 0},
|
||||||
|
{kFloat32x4Stride, 16, 16, wgpu::kWholeSize, 0},
|
||||||
|
};
|
||||||
|
|
||||||
|
const std::vector<VertexBufferParams> kInstanceParamsListForZeroStride = {
|
||||||
|
// Size enough for 1 instance
|
||||||
|
{kFloat32x2Stride, 8, 0, wgpu::kWholeSize, 1},
|
||||||
|
// No enough size for 1 instance
|
||||||
|
{kFloat32x2Stride, 11, 4, wgpu::kWholeSize, 0},
|
||||||
|
{kFloat32x2Stride, 8, 8, wgpu::kWholeSize, 0},
|
||||||
|
};
|
||||||
|
|
||||||
|
for (VertexBufferParams vertexParams : kVertexParamsListForZeroStride) {
|
||||||
|
for (VertexBufferParams instanceParams : kInstanceParamsListForZeroStride) {
|
||||||
|
for (wgpu::RenderPipeline pipeline :
|
||||||
|
{pipelineWithNonZeroArrayStride, pipelineWithZeroArrayStride}) {
|
||||||
|
constexpr wgpu::IndexFormat indexFormat = wgpu::IndexFormat::Uint32;
|
||||||
|
constexpr uint64_t indexStride = sizeof(uint32_t);
|
||||||
|
|
||||||
|
// Build index buffer for 1 index
|
||||||
|
wgpu::Buffer indexBuffer = CreateBuffer(indexStride, wgpu::BufferUsage::Index);
|
||||||
|
// Build vertex buffer for vertices
|
||||||
|
wgpu::Buffer vertexBuffer = CreateBuffer(vertexParams.bufferSize);
|
||||||
|
// Build vertex buffer for instances
|
||||||
|
wgpu::Buffer instanceBuffer = CreateBuffer(instanceParams.bufferSize);
|
||||||
|
|
||||||
|
VertexBufferList vertexBufferList = {
|
||||||
|
{0, vertexBuffer, vertexParams.bufferOffsetForEncoder,
|
||||||
|
vertexParams.bufferSizeForEncoder},
|
||||||
|
{1, instanceBuffer, instanceParams.bufferOffsetForEncoder,
|
||||||
|
instanceParams.bufferSizeForEncoder}};
|
||||||
|
|
||||||
|
IndexBufferDesc indexBufferDesc = {indexBuffer, indexFormat};
|
||||||
|
|
||||||
|
const bool vertexModeBufferOOB = vertexParams.maxValidAccessNumber == 0;
|
||||||
|
const bool instanceModeBufferOOB = instanceParams.maxValidAccessNumber == 0;
|
||||||
|
|
||||||
|
// Draw validate both vertex and instance step mode buffer OOB.
|
||||||
|
// Control case, non-zero stride for both step mode buffer
|
||||||
|
TestRenderPassDraw(pipeline, vertexBufferList, /* vertexCount */ 1,
|
||||||
|
/* instanceCount */ 1, /* firstVertex */ 0,
|
||||||
|
/* firstInstance */ 0,
|
||||||
|
/* isSuccess */ !vertexModeBufferOOB && !instanceModeBufferOOB);
|
||||||
|
TestRenderPassDraw(pipeline, vertexBufferList, 1, 0, 0, 1,
|
||||||
|
!vertexModeBufferOOB && !instanceModeBufferOOB);
|
||||||
|
TestRenderPassDraw(pipeline, vertexBufferList, 0, 1, 1, 0,
|
||||||
|
!vertexModeBufferOOB && !instanceModeBufferOOB);
|
||||||
|
TestRenderPassDraw(pipeline, vertexBufferList, 0, 0, 1, 1,
|
||||||
|
!vertexModeBufferOOB && !instanceModeBufferOOB);
|
||||||
|
// Vertex step mode buffer will never OOB if (vertexCount + firstVertex) is zero,
|
||||||
|
// and only instance step mode buffer OOB will fail validation
|
||||||
|
TestRenderPassDraw(pipeline, vertexBufferList, 0, 1, 0, 0, !instanceModeBufferOOB);
|
||||||
|
TestRenderPassDraw(pipeline, vertexBufferList, 0, 0, 0, 1, !instanceModeBufferOOB);
|
||||||
|
// Instance step mode buffer will never OOB if (instanceCount + firstInstance) is
|
||||||
|
// zero, and only vertex step mode buffer OOB will fail validation
|
||||||
|
TestRenderPassDraw(pipeline, vertexBufferList, 1, 0, 0, 0, !vertexModeBufferOOB);
|
||||||
|
TestRenderPassDraw(pipeline, vertexBufferList, 0, 0, 1, 0, !vertexModeBufferOOB);
|
||||||
|
// If both (vertexCount + firstVertex) and (instanceCount + firstInstance) are zero,
|
||||||
|
// all vertex buffer will never OOB
|
||||||
|
TestRenderPassDraw(pipeline, vertexBufferList, 0, 0, 0, 0, true);
|
||||||
|
|
||||||
|
// DrawIndexed only validate instance step mode buffer OOB.
|
||||||
|
// Control case, non-zero stride for instance step mode buffer
|
||||||
|
TestRenderPassDrawIndexed(
|
||||||
|
pipeline, indexBufferDesc, vertexBufferList, /* indexCount */ 1,
|
||||||
|
/* instanceCount */ 1, /* firstIndex */ 0, /* baseVertex */ 0,
|
||||||
|
/* firstInstance */ 0, /* isSuccess */ !instanceModeBufferOOB);
|
||||||
|
TestRenderPassDrawIndexed(pipeline, indexBufferDesc, vertexBufferList, 1, 0, 0, 0,
|
||||||
|
1, !instanceModeBufferOOB);
|
||||||
|
// Instance step mode buffer will never OOB if (instanceCount + firstInstance) is
|
||||||
|
// zero, validation shall always succeed
|
||||||
|
TestRenderPassDrawIndexed(pipeline, indexBufferDesc, vertexBufferList, 1, 0, 0, 0,
|
||||||
|
0, true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue