Ensure dynamic buffer offset bindings are sorted in increasing order

A previous CL sorted bindings by binding number, but bindings were first
sorted by type. This means a bind group layout with mixed dynamic
storage and uniform buffers would not always have all dynamic bindings
in increasing order. Instead, it would be strictly increasing within
each section of uniform/storage buffers. This CL corrects the issue
by first sorting dynamic buffers by binding number.

Bug: dawn:408
Change-Id: I3689eb64ad8aa8768cebe266eebcba75a21894ce
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/22303
Commit-Queue: Austin Eng <enga@chromium.org>
Reviewed-by: Stephen White <senorblanco@chromium.org>
This commit is contained in:
Austin Eng 2020-05-29 16:19:58 +00:00 committed by Commit Bot service account
parent 4c32bd0b7d
commit f7a98fa854
3 changed files with 61 additions and 37 deletions

View File

@ -233,22 +233,53 @@ namespace dawn_native {
a.storageTextureFormat != b.storageTextureFormat; a.storageTextureFormat != b.storageTextureFormat;
} }
bool IsBufferBinding(wgpu::BindingType bindingType) {
switch (bindingType) {
case wgpu::BindingType::UniformBuffer:
case wgpu::BindingType::StorageBuffer:
case wgpu::BindingType::ReadonlyStorageBuffer:
return true;
case wgpu::BindingType::SampledTexture:
case wgpu::BindingType::Sampler:
case wgpu::BindingType::ComparisonSampler:
case wgpu::BindingType::StorageTexture:
case wgpu::BindingType::ReadonlyStorageTexture:
case wgpu::BindingType::WriteonlyStorageTexture:
return false;
default:
UNREACHABLE();
return false;
}
}
bool SortBindingsCompare(const BindGroupLayoutEntry& a, const BindGroupLayoutEntry& b) { bool SortBindingsCompare(const BindGroupLayoutEntry& a, const BindGroupLayoutEntry& b) {
const bool aIsBuffer = IsBufferBinding(a.type);
const bool bIsBuffer = IsBufferBinding(b.type);
if (aIsBuffer != bIsBuffer) {
// Always place buffers first.
return aIsBuffer;
} else {
if (aIsBuffer) {
ASSERT(bIsBuffer);
if (a.hasDynamicOffset != b.hasDynamicOffset) { if (a.hasDynamicOffset != b.hasDynamicOffset) {
// Buffers with dynamic offsets should come before those without. // Buffers with dynamic offsets should come before those without.
// This makes it easy to iterate over the dynamic buffer bindings // This makes it easy to iterate over the dynamic buffer bindings
// [0, dynamicBufferCount) during validation. // [0, dynamicBufferCount) during validation.
return a.hasDynamicOffset > b.hasDynamicOffset; return a.hasDynamicOffset;
} }
if (a.hasDynamicOffset) {
ASSERT(b.hasDynamicOffset);
ASSERT(a.binding != b.binding);
// Above, we ensured that dynamic buffers are first. Now, ensure that
// dynamic buffer bindings are in increasing order. This is because dynamic
// buffer offsets are applied in increasing order of binding number.
return a.binding < b.binding;
}
}
// Otherwise, sort by type.
if (a.type != b.type) { if (a.type != b.type) {
// Buffers have smaller type enums. They should be placed first.
return a.type < b.type; return a.type < b.type;
} }
if (a.binding != b.binding) {
// Above we ensure that dynamic buffers are first. Now, ensure that bindings are in
// increasing order. This is because dynamic buffer offsets are applied in
// increasing order of binding number.
return a.binding < b.binding;
} }
if (a.visibility != b.visibility) { if (a.visibility != b.visibility) {
return a.visibility < b.visibility; return a.visibility < b.visibility;
@ -276,23 +307,10 @@ namespace dawn_native {
BindingIndex lastBufferIndex = 0; BindingIndex lastBufferIndex = 0;
BindingIndex firstNonBufferIndex = std::numeric_limits<BindingIndex>::max(); BindingIndex firstNonBufferIndex = std::numeric_limits<BindingIndex>::max();
for (BindingIndex i = 0; i < count; ++i) { for (BindingIndex i = 0; i < count; ++i) {
switch (bindings[i].type) { if (IsBufferBinding(bindings[i].type)) {
case wgpu::BindingType::UniformBuffer:
case wgpu::BindingType::StorageBuffer:
case wgpu::BindingType::ReadonlyStorageBuffer:
lastBufferIndex = std::max(i, lastBufferIndex); lastBufferIndex = std::max(i, lastBufferIndex);
break; } else {
case wgpu::BindingType::SampledTexture:
case wgpu::BindingType::Sampler:
case wgpu::BindingType::ComparisonSampler:
case wgpu::BindingType::StorageTexture:
case wgpu::BindingType::ReadonlyStorageTexture:
case wgpu::BindingType::WriteonlyStorageTexture:
firstNonBufferIndex = std::min(i, firstNonBufferIndex); firstNonBufferIndex = std::min(i, firstNonBufferIndex);
break;
default:
UNREACHABLE();
break;
} }
} }

View File

@ -762,9 +762,13 @@ TEST_P(BindGroupTests, DynamicOffsetOrder) {
bufferDescriptor.usage = wgpu::BufferUsage::Storage | wgpu::BufferUsage::CopyDst; bufferDescriptor.usage = wgpu::BufferUsage::Storage | wgpu::BufferUsage::CopyDst;
wgpu::Buffer buffer0 = device.CreateBuffer(&bufferDescriptor); wgpu::Buffer buffer0 = device.CreateBuffer(&bufferDescriptor);
wgpu::Buffer buffer2 = device.CreateBuffer(&bufferDescriptor);
wgpu::Buffer buffer3 = device.CreateBuffer(&bufferDescriptor); wgpu::Buffer buffer3 = device.CreateBuffer(&bufferDescriptor);
// This test uses both storage and uniform buffers to ensure buffer bindings are sorted first by
// binding number before type.
bufferDescriptor.usage = wgpu::BufferUsage::Uniform | wgpu::BufferUsage::CopyDst;
wgpu::Buffer buffer2 = device.CreateBuffer(&bufferDescriptor);
// Populate the values // Populate the values
buffer0.SetSubData(offsets[0], sizeof(uint32_t), &values[0]); buffer0.SetSubData(offsets[0], sizeof(uint32_t), &values[0]);
buffer2.SetSubData(offsets[1], sizeof(uint32_t), &values[1]); buffer2.SetSubData(offsets[1], sizeof(uint32_t), &values[1]);
@ -780,7 +784,7 @@ TEST_P(BindGroupTests, DynamicOffsetOrder) {
device, { device, {
{3, wgpu::ShaderStage::Compute, wgpu::BindingType::ReadonlyStorageBuffer, true}, {3, wgpu::ShaderStage::Compute, wgpu::BindingType::ReadonlyStorageBuffer, true},
{0, wgpu::ShaderStage::Compute, wgpu::BindingType::ReadonlyStorageBuffer, true}, {0, wgpu::ShaderStage::Compute, wgpu::BindingType::ReadonlyStorageBuffer, true},
{2, wgpu::ShaderStage::Compute, wgpu::BindingType::ReadonlyStorageBuffer, true}, {2, wgpu::ShaderStage::Compute, wgpu::BindingType::UniformBuffer, true},
{4, wgpu::ShaderStage::Compute, wgpu::BindingType::StorageBuffer}, {4, wgpu::ShaderStage::Compute, wgpu::BindingType::StorageBuffer},
}); });
wgpu::BindGroup bindGroup = utils::MakeBindGroup(device, bgl, wgpu::BindGroup bindGroup = utils::MakeBindGroup(device, bgl,
@ -795,7 +799,7 @@ TEST_P(BindGroupTests, DynamicOffsetOrder) {
pipelineDescriptor.computeStage.module = pipelineDescriptor.computeStage.module =
utils::CreateShaderModule(device, utils::SingleShaderStage::Compute, R"( utils::CreateShaderModule(device, utils::SingleShaderStage::Compute, R"(
#version 450 #version 450
layout(std430, set = 0, binding = 2) readonly buffer Buffer2 { layout(std140, set = 0, binding = 2) uniform Buffer2 {
uint value2; uint value2;
}; };
layout(std430, set = 0, binding = 3) readonly buffer Buffer3 { layout(std430, set = 0, binding = 3) readonly buffer Buffer3 {

View File

@ -1012,11 +1012,13 @@ TEST_F(SetBindGroupValidationTest, BindingSizeOutOfBoundDynamicStorageBuffer) {
TEST_F(SetBindGroupValidationTest, DynamicOffsetOrder) { TEST_F(SetBindGroupValidationTest, DynamicOffsetOrder) {
// Note: The order of the binding numbers of the bind group and bind group layout are // Note: The order of the binding numbers of the bind group and bind group layout are
// intentionally different and not in increasing order. // intentionally different and not in increasing order.
// This test uses both storage and uniform buffers to ensure buffer bindings are sorted first by
// binding number before type.
wgpu::BindGroupLayout bgl = utils::MakeBindGroupLayout( wgpu::BindGroupLayout bgl = utils::MakeBindGroupLayout(
device, { device, {
{3, wgpu::ShaderStage::Compute, wgpu::BindingType::ReadonlyStorageBuffer, true}, {3, wgpu::ShaderStage::Compute, wgpu::BindingType::ReadonlyStorageBuffer, true},
{0, wgpu::ShaderStage::Compute, wgpu::BindingType::ReadonlyStorageBuffer, true}, {0, wgpu::ShaderStage::Compute, wgpu::BindingType::ReadonlyStorageBuffer, true},
{2, wgpu::ShaderStage::Compute, wgpu::BindingType::ReadonlyStorageBuffer, true}, {2, wgpu::ShaderStage::Compute, wgpu::BindingType::UniformBuffer, true},
}); });
// Create buffers which are 3x, 2x, and 1x the size of the minimum buffer offset, plus 4 bytes // Create buffers which are 3x, 2x, and 1x the size of the minimum buffer offset, plus 4 bytes
@ -1028,7 +1030,7 @@ TEST_F(SetBindGroupValidationTest, DynamicOffsetOrder) {
wgpu::Buffer buffer2x = wgpu::Buffer buffer2x =
CreateBuffer(2 * kMinDynamicBufferOffsetAlignment + 4, wgpu::BufferUsage::Storage); CreateBuffer(2 * kMinDynamicBufferOffsetAlignment + 4, wgpu::BufferUsage::Storage);
wgpu::Buffer buffer1x = wgpu::Buffer buffer1x =
CreateBuffer(1 * kMinDynamicBufferOffsetAlignment + 4, wgpu::BufferUsage::Storage); CreateBuffer(1 * kMinDynamicBufferOffsetAlignment + 4, wgpu::BufferUsage::Uniform);
wgpu::BindGroup bindGroup = utils::MakeBindGroup(device, bgl, wgpu::BindGroup bindGroup = utils::MakeBindGroup(device, bgl,
{ {
{0, buffer3x, 0, 4}, {0, buffer3x, 0, 4},