[dawn][vk] Set up the metadata buffer for Tint's resource_binding.
Tint's support for `resource_binding` is done with a transform that
needs to know where the metadata buffer will be. Add that information in
the options set up in vulkan::ShaderModule.
Add tests that the metadata buffer is correctly set up by checking that
WGSL's arrayLength on `resource_binding` works correctly.
Bug: 435317394, 442483669
Change-Id: Ie90d857047f621bac7fc0c58c21b39e4683fc7a3
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/259874
Reviewed-by: dan sinclair <dsinclair@chromium.org>
Reviewed-by: Geoff Lang <geofflang@chromium.org>
Commit-Queue: Corentin Wallez <cwallez@chromium.org>
diff --git a/src/dawn/native/BindGroup.cpp b/src/dawn/native/BindGroup.cpp
index 3626965..860dbbd 100644
--- a/src/dawn/native/BindGroup.cpp
+++ b/src/dawn/native/BindGroup.cpp
@@ -984,6 +984,12 @@
return {};
}
+DynamicArrayState* BindGroupBase::GetDynamicArray() const {
+ DAWN_ASSERT(!IsError());
+ DAWN_ASSERT(HasDynamicArray());
+ return mDynamicArray.get();
+}
+
MaybeError BindGroupBase::ValidateDestroy() const {
DAWN_TRY(GetDevice()->ValidateObject(this));
diff --git a/src/dawn/native/BindGroup.h b/src/dawn/native/BindGroup.h
index 77bcafc..9144ffc 100644
--- a/src/dawn/native/BindGroup.h
+++ b/src/dawn/native/BindGroup.h
@@ -91,6 +91,7 @@
bool HasDynamicArray() const;
ityp::span<BindingIndex, const Ref<TextureViewBase>> GetDynamicArrayBindings() const;
MaybeError ValidateCanUseOnQueueNow() const;
+ DynamicArrayState* GetDynamicArray() const;
protected:
// To save memory, the size of a bind group is dynamically determined and the bind group is
diff --git a/src/dawn/native/vulkan/ShaderModuleVk.cpp b/src/dawn/native/vulkan/ShaderModuleVk.cpp
index 61cb081..8a03626 100644
--- a/src/dawn/native/vulkan/ShaderModuleVk.cpp
+++ b/src/dawn/native/vulkan/ShaderModuleVk.cpp
@@ -222,6 +222,40 @@
}
}
+ // Add options for dynamic binding arrays. They need remapping like all regular bindings but
+ // also need to give information about additional bindings for the metadata buffer and the
+ // default bindings.
+ tint::ResourceBindingConfig resourceBindingConfig;
+ for (BindGroupIndex group : layout->GetBindGroupLayoutsMask()) {
+ const BindGroupLayout* bgl = ToBackend(layout->GetBindGroupLayout(group));
+ if (!bgl->HasDynamicArray()) {
+ continue;
+ }
+
+ tint::BindingPoint wgslDynamicArrayBindPoint = {
+ .group = uint32_t(group), .binding = uint32_t(bgl->GetAPIDynamicArrayStart())};
+ tint::BindingPoint remappedDynamicArrayBindPoint = {
+ .group = uint32_t(group),
+ .binding = uint32_t(bgl->GetDynamicArrayStart()),
+ };
+ tint::BindingPoint metadataBindPoint = {
+ .group = uint32_t(group),
+ .binding = uint32_t(bgl->GetDynamicArrayMetadataBinding()),
+ };
+
+ // TODO(https://crbug.com/442483669): This uses the texture binding remapper support to
+ // remap a `resource_binding`. It is a hack until Tint adds support for `resource_binding`
+ // to the binding remapper.
+ bindings.texture.emplace(wgslDynamicArrayBindPoint, remappedDynamicArrayBindPoint);
+
+ // The resourceBindingConfig only uses remapped bind points.
+ resourceBindingConfig.bindings[remappedDynamicArrayBindPoint] = {
+ .storage_buffer_binding = metadataBindPoint,
+ // TODO(https://crbug.com/435317394): Support for all the resource types.
+ .default_binding_type_order = {},
+ };
+ }
+
const bool hasInputAttachment = !bindings.input_attachment.empty();
SpirvCompilationRequest req = {};
@@ -253,6 +287,7 @@
req.tintOptions.use_storage_input_output_16 =
GetDevice()->IsToggleEnabled(Toggle::VulkanUseStorageInputOutput16);
req.tintOptions.bindings = std::move(bindings);
+ req.tintOptions.resource_binding = std::move(resourceBindingConfig);
req.tintOptions.disable_image_robustness =
GetDevice()->IsToggleEnabled(Toggle::VulkanUseImageRobustAccess2);
// Currently we can disable index clamping on all runtime-sized arrays in Tint robustness
diff --git a/src/dawn/tests/end2end/BindingArrayTests.cpp b/src/dawn/tests/end2end/BindingArrayTests.cpp
index 2ac4811..156cb00 100644
--- a/src/dawn/tests/end2end/BindingArrayTests.cpp
+++ b/src/dawn/tests/end2end/BindingArrayTests.cpp
@@ -810,6 +810,72 @@
// - Check that a newly created resource that's pinned samples as zeroes.
// - Likewise for a texture written to, then discarded with a render pass.
+// Test that the WGSL `arrayLength` builtin on dynamic binding arrays returns the correct length.
+TEST_P(DynamicBindingArrayTests, ArrayLengthBuiltin) {
+ // Create a compute pipeline that returns the array length of the dynamic binding arrays.
+ // One of them has a static binding as well so as to check that it doesn't mess up the
+ // computation of the array length.
+ wgpu::ShaderModule module = utils::CreateShaderModule(device, R"(
+ enable chromium_experimental_dynamic_binding;
+
+ @group(0) @binding(0) var<storage, read_write> result : array<u32, 2>;
+ @group(0) @binding(1) var firstBindings : resource_binding;
+ @group(1) @binding(0) var secondBindings : resource_binding;
+
+ @compute @workgroup_size(1) fn getArrayLengths() {
+ // Force the defaulted layout to wgpu::DynamicBindingKind::SampledTexture
+ _ = hasBinding<texture_2d<f32>>(firstBindings, 0);
+ _ = hasBinding<texture_2d<f32>>(secondBindings, 0);
+
+ result[0] = arrayLength(firstBindings);
+ result[1] = arrayLength(secondBindings);
+ }
+ )");
+ wgpu::ComputePipelineDescriptor csDesc = {.compute = {
+ .module = module,
+ }};
+ wgpu::ComputePipeline pipeline = device.CreateComputePipeline(&csDesc);
+
+ // Create the dynamic binding arrays and fetch their array length in a buffer.
+ wgpu::BufferDescriptor bDesc = {
+ .usage = wgpu::BufferUsage::Storage | wgpu::BufferUsage::CopySrc,
+ .size = 2 * sizeof(uint32_t),
+ };
+ wgpu::Buffer resultBuffer = device.CreateBuffer(&bDesc);
+
+ wgpu::BindGroup bg0 = MakeBindGroup(pipeline.GetBindGroupLayout(0), 17, {{0, resultBuffer}});
+ wgpu::BindGroup bg1 = MakeBindGroup(pipeline.GetBindGroupLayout(1), 3, {});
+
+ wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
+ wgpu::ComputePassEncoder pass = encoder.BeginComputePass();
+ pass.SetBindGroup(0, bg0);
+ pass.SetBindGroup(1, bg1);
+ pass.SetPipeline(pipeline);
+ pass.DispatchWorkgroups(1);
+ pass.End();
+
+ wgpu::CommandBuffer commands = encoder.Finish();
+ device.GetQueue().Submit(1, &commands);
+
+ // The result buffer should contain the lengths of the dynamic binding arrays.
+ EXPECT_BUFFER_U32_EQ(17, resultBuffer, 0);
+ EXPECT_BUFFER_U32_EQ(3, resultBuffer, 4);
+}
+
+// TODO(https://crbug.com/435317394): Add tests that texture pinning / unpinning is reflected in the
+// availability of the binding in the shader. This can be done with a compute shader that loops over
+// [0, arrayLength(&resource_binding)) and returns hasBinding<T>(i) in a result storage buffer.
+// - Test adding one texture binding in the middle of a dynamic binding array, unpinned the pinned
+// - Test adding one texture binding multiple times, pinning it then unpinning it.
+// - Test adding multiple textures and the same texture twice (pinned). then unpinning one of them
+// - Test adding a texture, pinning it, then destroying it.
+// - Test adding the same texture to multiple dynamic binding arrays, pinning it, then destroying
+// one of the arrays.
+// - Add TODO to test with binding array updates in the future.
+// - Test for each possible sampled type, a shader that check hasBinding on every possible sampled
+// type as well (to know that we correctly pass in the type ID).
+// - Start at other dynamicArrayStart
+
DAWN_INSTANTIATE_TEST(DynamicBindingArrayTests, D3D12Backend(), MetalBackend(), VulkanBackend());
} // anonymous namespace