Support and pack unbounded binding numbers in the BGL

Also fixes a bug where we weren't validating duplicating
bindings in the shader, and where dynamic offset validation
could be incorrectly fetching the wrong bindings.

Bug: dawn:354
Change-Id: I93178c34eb4d43119e8b9de5738ae4596e9277cd
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/17240
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Reviewed-by: Kai Ninomiya <kainino@chromium.org>
Commit-Queue: Austin Eng <enga@chromium.org>
This commit is contained in:
Austin Eng
2020-03-20 21:56:30 +00:00
committed by Commit Bot service account
parent 11652ff8f8
commit a80993da44
27 changed files with 561 additions and 280 deletions

View File

@@ -780,4 +780,101 @@ TEST_P(BindGroupTests, BindGroupLayoutVisibilityCanBeNone) {
queue.Submit(1, &commands);
}
// Test that bind group bindings may have unbounded and arbitrary binding numbers
TEST_P(BindGroupTests, ArbitraryBindingNumbers) {
utils::BasicRenderPass renderPass = utils::CreateBasicRenderPass(device, kRTSize, kRTSize);
wgpu::ShaderModule vsModule =
utils::CreateShaderModule(device, utils::SingleShaderStage::Vertex, R"(
#version 450
void main() {
const vec2 pos[3] = vec2[3](vec2(-1.f, 1.f), vec2(1.f, 1.f), vec2(-1.f, -1.f));
gl_Position = vec4(pos[gl_VertexIndex], 0.f, 1.f);
})");
wgpu::ShaderModule fsModule =
utils::CreateShaderModule(device, utils::SingleShaderStage::Fragment, R"(
#version 450
layout (set = 0, binding = 953) uniform ubo1 {
vec4 color1;
};
layout (set = 0, binding = 47) uniform ubo2 {
vec4 color2;
};
layout (set = 0, binding = 111) uniform ubo3 {
vec4 color3;
};
layout(location = 0) out vec4 fragColor;
void main() {
fragColor = color1 + 2 * color2 + 4 * color3;
})");
utils::ComboRenderPipelineDescriptor pipelineDescriptor(device);
pipelineDescriptor.vertexStage.module = vsModule;
pipelineDescriptor.cFragmentStage.module = fsModule;
pipelineDescriptor.cColorStates[0].format = renderPass.colorFormat;
wgpu::RenderPipeline pipeline = device.CreateRenderPipeline(&pipelineDescriptor);
wgpu::Buffer black =
utils::CreateBufferFromData(device, wgpu::BufferUsage::Uniform, {0.f, 0.f, 0.f, 0.f});
wgpu::Buffer red =
utils::CreateBufferFromData(device, wgpu::BufferUsage::Uniform, {0.251f, 0.0f, 0.0f, 0.0f});
wgpu::Buffer green =
utils::CreateBufferFromData(device, wgpu::BufferUsage::Uniform, {0.0f, 0.251f, 0.0f, 0.0f});
wgpu::Buffer blue =
utils::CreateBufferFromData(device, wgpu::BufferUsage::Uniform, {0.0f, 0.0f, 0.251f, 0.0f});
auto DoTest = [&](wgpu::Buffer color1, wgpu::Buffer color2, wgpu::Buffer color3, RGBA8 filled) {
auto DoTestInner = [&](wgpu::BindGroup bindGroup) {
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo);
pass.SetPipeline(pipeline);
pass.SetBindGroup(0, bindGroup);
pass.Draw(3, 1, 0, 0);
pass.EndPass();
wgpu::CommandBuffer commands = encoder.Finish();
queue.Submit(1, &commands);
EXPECT_PIXEL_RGBA8_EQ(filled, renderPass.color, 1, 1);
};
utils::BindingInitializationHelper bindings[] = {
{953, color1, 0, 4 * sizeof(float)}, //
{47, color2, 0, 4 * sizeof(float)}, //
{111, color3, 0, 4 * sizeof(float)}, //
};
// Should work regardless of what order the bindings are specified in.
DoTestInner(utils::MakeBindGroup(device, pipeline.GetBindGroupLayout(0),
{bindings[0], bindings[1], bindings[2]}));
DoTestInner(utils::MakeBindGroup(device, pipeline.GetBindGroupLayout(0),
{bindings[1], bindings[0], bindings[2]}));
DoTestInner(utils::MakeBindGroup(device, pipeline.GetBindGroupLayout(0),
{bindings[2], bindings[0], bindings[1]}));
};
// first color is normal, second is 2x, third is 3x.
DoTest(black, black, black, RGBA8(0, 0, 0, 0));
// Check the first binding maps to the first slot. We know this because the colors are
// multiplied 1x.
DoTest(red, black, black, RGBA8(64, 0, 0, 0));
DoTest(green, black, black, RGBA8(0, 64, 0, 0));
DoTest(blue, black, black, RGBA8(0, 0, 64, 0));
// Use multiple bindings and check the second color maps to the second slot.
// We know this because the second slot is multiplied 2x.
DoTest(green, blue, black, RGBA8(0, 64, 128, 0));
DoTest(blue, green, black, RGBA8(0, 128, 64, 0));
DoTest(red, green, black, RGBA8(64, 128, 0, 0));
// Use multiple bindings and check the third color maps to the third slot.
// We know this because the third slot is multiplied 4x.
DoTest(black, blue, red, RGBA8(255, 0, 128, 0));
DoTest(blue, black, green, RGBA8(0, 255, 64, 0));
DoTest(red, black, blue, RGBA8(64, 0, 255, 0));
}
DAWN_INSTANTIATE_TEST(BindGroupTests, D3D12Backend(), MetalBackend(), OpenGLBackend(), VulkanBackend());

View File

@@ -510,16 +510,15 @@ TEST_F(BindGroupLayoutValidationTest, BindGroupLayoutStorageBindingsInVertexShad
device, {{0, wgpu::ShaderStage::Fragment, wgpu::BindingType::ReadonlyStorageBuffer}});
}
// Tests setting OOB checks for kMaxBindingsPerGroup in bind group layouts.
TEST_F(BindGroupLayoutValidationTest, BindGroupLayoutBindingOOB) {
// Checks that kMaxBindingsPerGroup - 1 is valid.
utils::MakeBindGroupLayout(device, {{kMaxBindingsPerGroup - 1, wgpu::ShaderStage::Vertex,
// Tests setting that bind group layout bindings numbers may be >= kMaxBindingsPerGroup.
TEST_F(BindGroupLayoutValidationTest, BindGroupLayoutBindingUnbounded) {
// Checks that kMaxBindingsPerGroup is valid.
utils::MakeBindGroupLayout(device, {{kMaxBindingsPerGroup, wgpu::ShaderStage::Vertex,
wgpu::BindingType::UniformBuffer}});
// Checks that kMaxBindingsPerGroup is OOB
ASSERT_DEVICE_ERROR(utils::MakeBindGroupLayout(
device,
{{kMaxBindingsPerGroup, wgpu::ShaderStage::Vertex, wgpu::BindingType::UniformBuffer}}));
// Checks that kMaxBindingsPerGroup + 1 is valid.
utils::MakeBindGroupLayout(device, {{kMaxBindingsPerGroup + 1, wgpu::ShaderStage::Vertex,
wgpu::BindingType::UniformBuffer}});
}
// This test verifies that the BindGroupLayout bindings are correctly validated, even if the
@@ -698,8 +697,10 @@ class SetBindGroupValidationTest : public ValidationTest {
device, {{0, wgpu::ShaderStage::Compute | wgpu::ShaderStage::Fragment,
wgpu::BindingType::UniformBuffer, true},
{1, wgpu::ShaderStage::Compute | wgpu::ShaderStage::Fragment,
wgpu::BindingType::StorageBuffer, true},
wgpu::BindingType::UniformBuffer, false},
{2, wgpu::ShaderStage::Compute | wgpu::ShaderStage::Fragment,
wgpu::BindingType::StorageBuffer, true},
{3, wgpu::ShaderStage::Compute | wgpu::ShaderStage::Fragment,
wgpu::BindingType::ReadonlyStorageBuffer, true}});
}
@@ -723,13 +724,16 @@ class SetBindGroupValidationTest : public ValidationTest {
wgpu::ShaderModule fsModule =
utils::CreateShaderModule(device, utils::SingleShaderStage::Fragment, R"(
#version 450
layout(std140, set = 0, binding = 0) uniform uBuffer {
layout(std140, set = 0, binding = 0) uniform uBufferDynamic {
vec2 value0;
};
layout(std140, set = 0, binding = 1) uniform uBuffer {
vec2 value1;
};
layout(std140, set = 0, binding = 1) buffer SBuffer {
layout(std140, set = 0, binding = 2) buffer SBufferDynamic {
vec2 value2;
} sBuffer;
layout(std140, set = 0, binding = 2) readonly buffer RBuffer {
layout(std140, set = 0, binding = 3) readonly buffer RBufferDynamic {
vec2 value3;
} rBuffer;
layout(location = 0) out vec4 fragColor;
@@ -753,13 +757,16 @@ class SetBindGroupValidationTest : public ValidationTest {
const uint kInstances = 11;
layout(local_size_x = kTileSize, local_size_y = kTileSize, local_size_z = 1) in;
layout(std140, set = 0, binding = 0) uniform UniformBuffer {
layout(std140, set = 0, binding = 0) uniform UniformBufferDynamic {
float value0;
};
layout(std140, set = 0, binding = 1) uniform UniformBuffer {
float value1;
};
layout(std140, set = 0, binding = 1) buffer SBuffer {
layout(std140, set = 0, binding = 2) buffer SBufferDynamic {
float value2;
} dst;
layout(std140, set = 0, binding = 2) readonly buffer RBuffer {
layout(std140, set = 0, binding = 3) readonly buffer RBufferDynamic {
readonly float value3;
} rdst;
void main() {
@@ -824,8 +831,9 @@ TEST_F(SetBindGroupValidationTest, Basic) {
wgpu::Buffer readonlyStorageBuffer = CreateBuffer(kBufferSize, wgpu::BufferUsage::Storage);
wgpu::BindGroup bindGroup = utils::MakeBindGroup(device, mBindGroupLayout,
{{0, uniformBuffer, 0, kBindingSize},
{1, storageBuffer, 0, kBindingSize},
{2, readonlyStorageBuffer, 0, kBindingSize}});
{1, uniformBuffer, 0, kBindingSize},
{2, storageBuffer, 0, kBindingSize},
{3, readonlyStorageBuffer, 0, kBindingSize}});
std::array<uint32_t, 3> offsets = {512, 256, 0};
@@ -842,8 +850,9 @@ TEST_F(SetBindGroupValidationTest, DynamicOffsetsMismatch) {
wgpu::Buffer readonlyStorageBuffer = CreateBuffer(kBufferSize, wgpu::BufferUsage::Storage);
wgpu::BindGroup bindGroup = utils::MakeBindGroup(device, mBindGroupLayout,
{{0, uniformBuffer, 0, kBindingSize},
{1, storageBuffer, 0, kBindingSize},
{2, readonlyStorageBuffer, 0, kBindingSize}});
{1, uniformBuffer, 0, kBindingSize},
{2, storageBuffer, 0, kBindingSize},
{3, readonlyStorageBuffer, 0, kBindingSize}});
// Number of offsets mismatch.
std::array<uint32_t, 4> mismatchOffsets = {768, 512, 256, 0};
@@ -865,8 +874,9 @@ TEST_F(SetBindGroupValidationTest, DynamicOffsetsNotAligned) {
wgpu::Buffer readonlyStorageBuffer = CreateBuffer(kBufferSize, wgpu::BufferUsage::Storage);
wgpu::BindGroup bindGroup = utils::MakeBindGroup(device, mBindGroupLayout,
{{0, uniformBuffer, 0, kBindingSize},
{1, storageBuffer, 0, kBindingSize},
{2, readonlyStorageBuffer, 0, kBindingSize}});
{1, uniformBuffer, 0, kBindingSize},
{2, storageBuffer, 0, kBindingSize},
{3, readonlyStorageBuffer, 0, kBindingSize}});
// Dynamic offsets are not aligned.
std::array<uint32_t, 3> notAlignedOffsets = {512, 128, 0};
@@ -884,8 +894,9 @@ TEST_F(SetBindGroupValidationTest, OffsetOutOfBoundDynamicUniformBuffer) {
wgpu::Buffer readonlyStorageBuffer = CreateBuffer(kBufferSize, wgpu::BufferUsage::Storage);
wgpu::BindGroup bindGroup = utils::MakeBindGroup(device, mBindGroupLayout,
{{0, uniformBuffer, 0, kBindingSize},
{1, storageBuffer, 0, kBindingSize},
{2, readonlyStorageBuffer, 0, kBindingSize}});
{1, uniformBuffer, 0, kBindingSize},
{2, storageBuffer, 0, kBindingSize},
{3, readonlyStorageBuffer, 0, kBindingSize}});
// Dynamic offset + offset is larger than buffer size.
std::array<uint32_t, 3> overFlowOffsets = {1024, 256, 0};
@@ -903,8 +914,9 @@ TEST_F(SetBindGroupValidationTest, OffsetOutOfBoundDynamicStorageBuffer) {
wgpu::Buffer readonlyStorageBuffer = CreateBuffer(kBufferSize, wgpu::BufferUsage::Storage);
wgpu::BindGroup bindGroup = utils::MakeBindGroup(device, mBindGroupLayout,
{{0, uniformBuffer, 0, kBindingSize},
{1, storageBuffer, 0, kBindingSize},
{2, readonlyStorageBuffer, 0, kBindingSize}});
{1, uniformBuffer, 0, kBindingSize},
{2, storageBuffer, 0, kBindingSize},
{3, readonlyStorageBuffer, 0, kBindingSize}});
// Dynamic offset + offset is larger than buffer size.
std::array<uint32_t, 3> overFlowOffsets = {0, 256, 1024};
@@ -922,8 +934,9 @@ TEST_F(SetBindGroupValidationTest, BindingSizeOutOfBoundDynamicUniformBuffer) {
wgpu::Buffer readonlyStorageBuffer = CreateBuffer(kBufferSize, wgpu::BufferUsage::Storage);
wgpu::BindGroup bindGroup = utils::MakeBindGroup(device, mBindGroupLayout,
{{0, uniformBuffer, 0, kBindingSize},
{1, storageBuffer, 0, kBindingSize},
{2, readonlyStorageBuffer, 0, kBindingSize}});
{1, uniformBuffer, 0, kBindingSize},
{2, storageBuffer, 0, kBindingSize},
{3, readonlyStorageBuffer, 0, kBindingSize}});
// Dynamic offset + offset isn't larger than buffer size.
// But with binding size, it will trigger OOB error.
@@ -941,8 +954,9 @@ TEST_F(SetBindGroupValidationTest, BindingSizeOutOfBoundDynamicStorageBuffer) {
wgpu::Buffer readonlyStorageBuffer = CreateBuffer(kBufferSize, wgpu::BufferUsage::Storage);
wgpu::BindGroup bindGroup = utils::MakeBindGroup(device, mBindGroupLayout,
{{0, uniformBuffer, 0, kBindingSize},
{1, storageBuffer, 0, kBindingSize},
{2, readonlyStorageBuffer, 0, kBindingSize}});
{1, uniformBuffer, 0, kBindingSize},
{2, storageBuffer, 0, kBindingSize},
{3, readonlyStorageBuffer, 0, kBindingSize}});
// Dynamic offset + offset isn't larger than buffer size.
// But with binding size, it will trigger OOB error.
std::array<uint32_t, 3> offsets = {0, 256, 768};

View File

@@ -228,7 +228,7 @@ TEST_F(StorageTextureValidationTests, ComputePipeline) {
utils::CreateShaderModule(device, utils::SingleShaderStage::Compute, R"(
#version 450
layout(set = 0, binding = 0, rgba8) uniform readonly image2D image0;
layout(std430, set = 0, binding = 0) buffer Buf { uint buf; };
layout(std430, set = 0, binding = 1) buffer Buf { uint buf; };
void main() {
vec4 pixel = imageLoad(image0, ivec2(gl_LocalInvocationID.xy));
buf = uint(pixel.x);