Skip to content

Commit

Permalink
New Binding Array Limit
Browse files Browse the repository at this point in the history
  • Loading branch information
cwfitzgerald committed Jan 20, 2025
1 parent 78549c4 commit 5c7343b
Show file tree
Hide file tree
Showing 18 changed files with 257 additions and 157 deletions.
6 changes: 5 additions & 1 deletion examples/src/framework.rs
Original file line number Diff line number Diff line change
Expand Up @@ -511,7 +511,11 @@ impl<E: Example + wgpu::WasmNotSendSync> From<ExampleTestParams<E>>

let features = E::required_features() | params.optional_features;

params.base_test_parameters.clone().features(features)
params
.base_test_parameters
.clone()
.features(features)
.limits(E::required_limits())
})
.run_async(move |ctx| async move {
let format = if E::SRGB {
Expand Down
7 changes: 7 additions & 0 deletions examples/src/texture_arrays/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,13 @@ impl crate::framework::Example for Example {
fn required_features() -> wgpu::Features {
wgpu::Features::TEXTURE_BINDING_ARRAY
}
fn required_limits() -> wgpu::Limits {
wgpu::Limits {
max_binding_array_elements_per_shader_stage: 6,
max_binding_array_sampler_elements_per_shader_stage: 2,
..wgpu::Limits::downlevel_defaults()
}
}
fn init(
config: &wgpu::SurfaceConfiguration,
_adapter: &wgpu::Adapter,
Expand Down
8 changes: 4 additions & 4 deletions tests/tests/binding_array/buffers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ static BINDING_ARRAY_UNIFORM_BUFFERS: GpuTestConfiguration = GpuTestConfiguratio
TestParameters::default()
.features(Features::BUFFER_BINDING_ARRAY | Features::UNIFORM_BUFFER_INDEXING)
.limits(Limits {
max_uniform_buffers_per_shader_stage: 16,
max_binding_array_elements_per_shader_stage: 16,
..Limits::default()
})
// Naga bug on vulkan: https://github.com/gfx-rs/wgpu/issues/6733
Expand All @@ -31,7 +31,7 @@ static PARTIAL_BINDING_ARRAY_UNIFORM_BUFFERS: GpuTestConfiguration = GpuTestConf
| Features::UNIFORM_BUFFER_INDEXING,
)
.limits(Limits {
max_uniform_buffers_per_shader_stage: 32,
max_binding_array_elements_per_shader_stage: 32,
..Limits::default()
})
// Naga bug on vulkan: https://github.com/gfx-rs/wgpu/issues/6733
Expand All @@ -53,7 +53,7 @@ static BINDING_ARRAY_STORAGE_BUFFERS: GpuTestConfiguration = GpuTestConfiguratio
| Features::SAMPLED_TEXTURE_AND_STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING,
)
.limits(Limits {
max_storage_buffers_per_shader_stage: 17,
max_binding_array_elements_per_shader_stage: 17,
..Limits::default()
})
// See https://github.com/gfx-rs/wgpu/issues/6745.
Expand All @@ -72,7 +72,7 @@ static PARTIAL_BINDING_ARRAY_STORAGE_BUFFERS: GpuTestConfiguration = GpuTestConf
| Features::SAMPLED_TEXTURE_AND_STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING,
)
.limits(Limits {
max_storage_buffers_per_shader_stage: 33,
max_binding_array_elements_per_shader_stage: 33,
..Limits::default()
})
// See https://github.com/gfx-rs/wgpu/issues/6745.
Expand Down
4 changes: 2 additions & 2 deletions tests/tests/binding_array/sampled_textures.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ static BINDING_ARRAY_SAMPLED_TEXTURES: GpuTestConfiguration = GpuTestConfigurati
| Features::SAMPLED_TEXTURE_AND_STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING,
)
.limits(Limits {
max_sampled_textures_per_shader_stage: 16,
max_binding_array_elements_per_shader_stage: 16,
..Limits::default()
}),
)
Expand All @@ -30,7 +30,7 @@ static PARTIAL_BINDING_ARRAY_SAMPLED_TEXTURES: GpuTestConfiguration = GpuTestCon
| Features::PARTIALLY_BOUND_BINDING_ARRAY,
)
.limits(Limits {
max_sampled_textures_per_shader_stage: 32,
max_binding_array_elements_per_shader_stage: 32,
..Limits::default()
}),
)
Expand Down
6 changes: 4 additions & 2 deletions tests/tests/binding_array/samplers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ static BINDING_ARRAY_SAMPLERS: GpuTestConfiguration = GpuTestConfiguration::new(
| Features::SAMPLED_TEXTURE_AND_STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING,
)
.limits(Limits {
max_samplers_per_shader_stage: 2,
max_binding_array_elements_per_shader_stage: 2,
max_binding_array_sampler_elements_per_shader_stage: 2,
..Limits::default()
}),
)
Expand All @@ -28,7 +29,8 @@ static PARTIAL_BINDING_ARRAY_SAMPLERS: GpuTestConfiguration = GpuTestConfigurati
| Features::PARTIALLY_BOUND_BINDING_ARRAY,
)
.limits(Limits {
max_samplers_per_shader_stage: 4,
max_binding_array_elements_per_shader_stage: 4,
max_binding_array_sampler_elements_per_shader_stage: 4,
..Limits::default()
}),
)
Expand Down
4 changes: 2 additions & 2 deletions tests/tests/binding_array/storage_textures.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ static BINDING_ARRAY_STORAGE_TEXTURES: GpuTestConfiguration = GpuTestConfigurati
| Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES,
)
.limits(Limits {
max_storage_textures_per_shader_stage: 17,
max_binding_array_elements_per_shader_stage: 17,
..Limits::default()
})
.expect_fail(FailureCase::backend(Backends::METAL)),
Expand All @@ -36,7 +36,7 @@ static PARTIAL_BINDING_ARRAY_STORAGE_TEXTURES: GpuTestConfiguration = GpuTestCon
| Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES,
)
.limits(Limits {
max_storage_textures_per_shader_stage: 33,
max_binding_array_elements_per_shader_stage: 33,
..Limits::default()
})
.expect_fail(FailureCase::backend(Backends::METAL)),
Expand Down
11 changes: 3 additions & 8 deletions tests/tests/binding_array/validation.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
use std::num::NonZeroU32;

use wgpu::*;
use wgpu_test::{
fail, gpu_test, FailureCase, GpuTestConfiguration, TestParameters, TestingContext,
};
use wgpu_test::{fail, gpu_test, GpuTestConfiguration, TestParameters, TestingContext};

#[gpu_test]
static VALIDATION: GpuTestConfiguration = GpuTestConfiguration::new()
Expand All @@ -12,12 +10,9 @@ static VALIDATION: GpuTestConfiguration = GpuTestConfiguration::new()
.features(Features::TEXTURE_BINDING_ARRAY)
.limits(Limits {
max_dynamic_storage_buffers_per_pipeline_layout: 1,
max_binding_array_elements_per_shader_stage: 4,
..Limits::downlevel_defaults()
})
.expect_fail(
// https://github.com/gfx-rs/wgpu/issues/6950
FailureCase::backend(Backends::VULKAN).validation_error("has not been destroyed"),
),
}),
)
.run_async(validation);

Expand Down
98 changes: 65 additions & 33 deletions wgpu-core/src/binding_model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,8 @@ pub enum BindingTypeMaxCountErrorKind {
StorageBuffers,
StorageTextures,
UniformBuffers,
BindingArrayElements,
BindingArraySamplerElements,
}

impl BindingTypeMaxCountErrorKind {
Expand All @@ -249,6 +251,12 @@ impl BindingTypeMaxCountErrorKind {
"max_storage_textures_per_shader_stage"
}
BindingTypeMaxCountErrorKind::UniformBuffers => "max_uniform_buffers_per_shader_stage",
BindingTypeMaxCountErrorKind::BindingArrayElements => {
"max_binding_array_elements_per_shader_stage"
}
BindingTypeMaxCountErrorKind::BindingArraySamplerElements => {
"max_binding_array_elements_per_shader_stage"
}
}
}
}
Expand Down Expand Up @@ -323,49 +331,59 @@ pub(crate) struct BindingTypeMaxCountValidator {
storage_textures: PerStageBindingTypeCounter,
uniform_buffers: PerStageBindingTypeCounter,
acceleration_structures: PerStageBindingTypeCounter,
binding_array_elements: PerStageBindingTypeCounter,
binding_array_sampler_elements: PerStageBindingTypeCounter,
has_bindless_array: bool,
}

impl BindingTypeMaxCountValidator {
pub(crate) fn add_binding(&mut self, binding: &wgt::BindGroupLayoutEntry) {
let count = binding.count.map_or(1, |count| count.get());
match binding.ty {
wgt::BindingType::Buffer {
ty: wgt::BufferBindingType::Uniform,
has_dynamic_offset,
..
} => {
self.uniform_buffers.add(binding.visibility, count);
if has_dynamic_offset {
self.dynamic_uniform_buffers += count;
}

if binding.count.is_some() {
self.binding_array_elements.add(binding.visibility, count);
self.has_bindless_array = true;

if let wgt::BindingType::Sampler(_) = binding.ty {
self.binding_array_sampler_elements
.add(binding.visibility, count);
}
wgt::BindingType::Buffer {
ty: wgt::BufferBindingType::Storage { .. },
has_dynamic_offset,
..
} => {
self.storage_buffers.add(binding.visibility, count);
if has_dynamic_offset {
self.dynamic_storage_buffers += count;
} else {
match binding.ty {
wgt::BindingType::Buffer {
ty: wgt::BufferBindingType::Uniform,
has_dynamic_offset,
..
} => {
self.uniform_buffers.add(binding.visibility, count);
if has_dynamic_offset {
self.dynamic_uniform_buffers += count;
}
}
wgt::BindingType::Buffer {
ty: wgt::BufferBindingType::Storage { .. },
has_dynamic_offset,
..
} => {
self.storage_buffers.add(binding.visibility, count);
if has_dynamic_offset {
self.dynamic_storage_buffers += count;
}
}
wgt::BindingType::Sampler { .. } => {
self.samplers.add(binding.visibility, count);
}
wgt::BindingType::Texture { .. } => {
self.sampled_textures.add(binding.visibility, count);
}
wgt::BindingType::StorageTexture { .. } => {
self.storage_textures.add(binding.visibility, count);
}
wgt::BindingType::AccelerationStructure => {
self.acceleration_structures.add(binding.visibility, count);
}
}
wgt::BindingType::Sampler { .. } => {
self.samplers.add(binding.visibility, count);
}
wgt::BindingType::Texture { .. } => {
self.sampled_textures.add(binding.visibility, count);
}
wgt::BindingType::StorageTexture { .. } => {
self.storage_textures.add(binding.visibility, count);
}
wgt::BindingType::AccelerationStructure => {
self.acceleration_structures.add(binding.visibility, count);
}
}
if binding.count.is_some() {
self.has_bindless_array = true;
}
}

pub(crate) fn merge(&mut self, other: &Self) {
Expand All @@ -376,6 +394,12 @@ impl BindingTypeMaxCountValidator {
self.storage_buffers.merge(&other.storage_buffers);
self.storage_textures.merge(&other.storage_textures);
self.uniform_buffers.merge(&other.uniform_buffers);
self.acceleration_structures
.merge(&other.acceleration_structures);
self.binding_array_elements
.merge(&other.binding_array_elements);
self.binding_array_sampler_elements
.merge(&other.binding_array_sampler_elements);
}

pub(crate) fn validate(&self, limits: &wgt::Limits) -> Result<(), BindingTypeMaxCountError> {
Expand Down Expand Up @@ -415,6 +439,14 @@ impl BindingTypeMaxCountValidator {
limits.max_uniform_buffers_per_shader_stage,
BindingTypeMaxCountErrorKind::UniformBuffers,
)?;
self.binding_array_elements.validate(
limits.max_binding_array_elements_per_shader_stage,
BindingTypeMaxCountErrorKind::BindingArrayElements,
)?;
self.binding_array_sampler_elements.validate(
limits.max_binding_array_sampler_elements_per_shader_stage,
BindingTypeMaxCountErrorKind::BindingArraySamplerElements,
)?;
Ok(())
}

Expand Down
6 changes: 3 additions & 3 deletions wgpu-core/src/device/resource.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1895,9 +1895,6 @@ impl Device {
entries: &hal_bindings,
};

let raw = unsafe { self.raw().create_bind_group_layout(&hal_desc) }
.map_err(|e| self.handle_hal_error(e))?;

let mut count_validator = binding_model::BindingTypeMaxCountValidator::default();
for entry in entry_map.values() {
count_validator.add_binding(entry);
Expand All @@ -1911,6 +1908,9 @@ impl Device {
// Validate that binding arrays don't conflict with dynamic offsets.
count_validator.validate_binding_arrays()?;

let raw = unsafe { self.raw().create_bind_group_layout(&hal_desc) }
.map_err(|e| self.handle_hal_error(e))?;

let bgl = BindGroupLayout {
raw: ManuallyDrop::new(raw),
device: self.clone(),
Expand Down
15 changes: 7 additions & 8 deletions wgpu-hal/src/dx12/adapter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -341,14 +341,11 @@ impl super::Adapter {
wgt::Features::TEXTURE_BINDING_ARRAY
| wgt::Features::STORAGE_TEXTURE_ARRAY_NON_UNIFORM_INDEXING
| wgt::Features::UNIFORM_BUFFER_INDEXING
| wgt::Features::SAMPLED_TEXTURE_AND_STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING,
shader_model >= naga::back::hlsl::ShaderModel::V5_1,
);

// See note below the table https://learn.microsoft.com/en-us/windows/win32/direct3d12/hardware-support
features.set(
wgt::Features::PARTIALLY_BOUND_BINDING_ARRAY,
options.ResourceBindingTier.0 >= Direct3D12::D3D12_RESOURCE_BINDING_TIER_3.0,
| wgt::Features::SAMPLED_TEXTURE_AND_STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING
// See note below the table https://learn.microsoft.com/en-us/windows/win32/direct3d12/hardware-support
| wgt::Features::PARTIALLY_BOUND_BINDING_ARRAY,
shader_model >= naga::back::hlsl::ShaderModel::V5_1
&& options.ResourceBindingTier.0 >= Direct3D12::D3D12_RESOURCE_BINDING_TIER_3.0,
);

let bgra8unorm_storage_supported = {
Expand Down Expand Up @@ -497,6 +494,8 @@ impl super::Adapter {
max_storage_buffers_per_shader_stage: uav_count / 4,
max_storage_textures_per_shader_stage: uav_count / 4,
max_uniform_buffers_per_shader_stage: full_heap_count,
max_binding_array_elements_per_shader_stage: full_heap_count,
max_binding_array_sampler_elements_per_shader_stage: full_heap_count,
max_uniform_buffer_binding_size:
Direct3D12::D3D12_REQ_CONSTANT_BUFFER_ELEMENT_COUNT * 16,
max_storage_buffer_binding_size: auxil::MAX_I32_BINDING_SIZE,
Expand Down
2 changes: 2 additions & 0 deletions wgpu-hal/src/gles/adapter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -684,6 +684,8 @@ impl super::Adapter {
max_storage_buffers_per_shader_stage,
max_storage_textures_per_shader_stage,
max_uniform_buffers_per_shader_stage,
max_binding_array_elements_per_shader_stage: 0,
max_binding_array_sampler_elements_per_shader_stage: 0,
max_uniform_buffer_binding_size: unsafe {
gl.get_parameter_i32(glow::MAX_UNIFORM_BLOCK_SIZE)
} as u32,
Expand Down
Loading

0 comments on commit 5c7343b

Please sign in to comment.