From 5c7343beaff187e12a6bf217eaf5de60a0848598 Mon Sep 17 00:00:00 2001 From: Connor Fitzgerald Date: Sun, 19 Jan 2025 17:18:30 -0500 Subject: [PATCH] New Binding Array Limit --- examples/src/framework.rs | 6 +- examples/src/texture_arrays/mod.rs | 7 ++ tests/tests/binding_array/buffers.rs | 8 +- tests/tests/binding_array/sampled_textures.rs | 4 +- tests/tests/binding_array/samplers.rs | 6 +- tests/tests/binding_array/storage_textures.rs | 4 +- tests/tests/binding_array/validation.rs | 11 +-- wgpu-core/src/binding_model.rs | 98 ++++++++++++------- wgpu-core/src/device/resource.rs | 6 +- wgpu-hal/src/dx12/adapter.rs | 15 ++- wgpu-hal/src/gles/adapter.rs | 2 + wgpu-hal/src/metal/adapter.rs | 29 +++++- wgpu-hal/src/metal/mod.rs | 2 + wgpu-hal/src/vulkan/adapter.rs | 71 +++++++------- wgpu-hal/src/vulkan/device.rs | 49 +++++----- wgpu-info/src/human.rs | 78 ++++++++------- wgpu-types/src/lib.rs | 16 +++ wgpu/src/backend/webgpu.rs | 2 + 18 files changed, 257 insertions(+), 157 deletions(-) diff --git a/examples/src/framework.rs b/examples/src/framework.rs index db863526d7..31762a2c67 100644 --- a/examples/src/framework.rs +++ b/examples/src/framework.rs @@ -511,7 +511,11 @@ impl From> 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 { diff --git a/examples/src/texture_arrays/mod.rs b/examples/src/texture_arrays/mod.rs index d07f3a6df5..92b481806f 100644 --- a/examples/src/texture_arrays/mod.rs +++ b/examples/src/texture_arrays/mod.rs @@ -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, diff --git a/tests/tests/binding_array/buffers.rs b/tests/tests/binding_array/buffers.rs index 6a2ba4bab5..0215bb41c8 100644 --- a/tests/tests/binding_array/buffers.rs +++ b/tests/tests/binding_array/buffers.rs @@ -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 @@ -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 @@ -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. @@ -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. diff --git a/tests/tests/binding_array/sampled_textures.rs b/tests/tests/binding_array/sampled_textures.rs index e95476e876..fee57b0a1c 100644 --- a/tests/tests/binding_array/sampled_textures.rs +++ b/tests/tests/binding_array/sampled_textures.rs @@ -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() }), ) @@ -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() }), ) diff --git a/tests/tests/binding_array/samplers.rs b/tests/tests/binding_array/samplers.rs index d4ff2a24b5..fe31d75a11 100644 --- a/tests/tests/binding_array/samplers.rs +++ b/tests/tests/binding_array/samplers.rs @@ -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() }), ) @@ -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() }), ) diff --git a/tests/tests/binding_array/storage_textures.rs b/tests/tests/binding_array/storage_textures.rs index bee8db9b77..1118247551 100644 --- a/tests/tests/binding_array/storage_textures.rs +++ b/tests/tests/binding_array/storage_textures.rs @@ -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)), @@ -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)), diff --git a/tests/tests/binding_array/validation.rs b/tests/tests/binding_array/validation.rs index 46aa56a7e5..bc3b3de18b 100644 --- a/tests/tests/binding_array/validation.rs +++ b/tests/tests/binding_array/validation.rs @@ -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() @@ -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); diff --git a/wgpu-core/src/binding_model.rs b/wgpu-core/src/binding_model.rs index 3ab652abce..96db2235d4 100644 --- a/wgpu-core/src/binding_model.rs +++ b/wgpu-core/src/binding_model.rs @@ -229,6 +229,8 @@ pub enum BindingTypeMaxCountErrorKind { StorageBuffers, StorageTextures, UniformBuffers, + BindingArrayElements, + BindingArraySamplerElements, } impl BindingTypeMaxCountErrorKind { @@ -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" + } } } } @@ -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) { @@ -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> { @@ -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(()) } diff --git a/wgpu-core/src/device/resource.rs b/wgpu-core/src/device/resource.rs index 7642c7baf2..7735d32380 100644 --- a/wgpu-core/src/device/resource.rs +++ b/wgpu-core/src/device/resource.rs @@ -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); @@ -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(), diff --git a/wgpu-hal/src/dx12/adapter.rs b/wgpu-hal/src/dx12/adapter.rs index 65d52a630a..ee76b65590 100644 --- a/wgpu-hal/src/dx12/adapter.rs +++ b/wgpu-hal/src/dx12/adapter.rs @@ -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 = { @@ -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, diff --git a/wgpu-hal/src/gles/adapter.rs b/wgpu-hal/src/gles/adapter.rs index 67ff20ff19..398473ed8c 100644 --- a/wgpu-hal/src/gles/adapter.rs +++ b/wgpu-hal/src/gles/adapter.rs @@ -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, diff --git a/wgpu-hal/src/metal/adapter.rs b/wgpu-hal/src/metal/adapter.rs index bf45ccdcfc..35d17e665b 100644 --- a/wgpu-hal/src/metal/adapter.rs +++ b/wgpu-hal/src/metal/adapter.rs @@ -592,6 +592,8 @@ impl super::PrivateCapabilities { // `TimestampQuerySupport::INSIDE_WGPU_PASSES` emerges from the other flags. } + let argument_buffers = device.argument_buffers_support(); + Self { family_check, msl_version: if os_is_xr || version.at_least((14, 0), (17, 0), os_is_mac) { @@ -626,7 +628,7 @@ impl super::PrivateCapabilities { }, msaa_apple7: family_check && device.supports_family(MTLGPUFamily::Apple7), resource_heaps: Self::supports_any(device, RESOURCE_HEAP_SUPPORT), - argument_buffers: device.argument_buffers_support(), + argument_buffers, shared_textures: !os_is_mac, mutable_comparison_samplers: Self::supports_any( device, @@ -725,6 +727,28 @@ impl super::PrivateCapabilities { 31 }, max_samplers_per_stage: 16, + max_binding_array_elements: if argument_buffers == metal::MTLArgumentBuffersTier::Tier2 + { + 1_000_000 + } else if family_check && device.supports_family(MTLGPUFamily::Apple4) { + 96 + } else { + 31 + }, + max_sampler_binding_array_elements: if family_check + && device.supports_family(MTLGPUFamily::Apple9) + { + 500_000 + } else if family_check + && (device.supports_family(MTLGPUFamily::Apple7) + || device.supports_family(MTLGPUFamily::Mac2)) + { + 1000 + } else if family_check && device.supports_family(MTLGPUFamily::Apple6) { + 128 + } else { + 16 + }, buffer_alignment: if os_is_mac || os_is_xr { 256 } else { 64 }, max_buffer_size: if version.at_least((10, 14), (12, 0), os_is_mac) { // maxBufferLength available on macOS 10.14+ and iOS 12.0+ @@ -1013,6 +1037,9 @@ impl super::PrivateCapabilities { max_storage_buffers_per_shader_stage: self.max_buffers_per_stage, max_storage_textures_per_shader_stage: self.max_textures_per_stage, max_uniform_buffers_per_shader_stage: self.max_buffers_per_stage, + max_binding_array_elements_per_shader_stage: self.max_binding_array_elements, + max_binding_array_sampler_elements_per_shader_stage: self + .max_sampler_binding_array_elements, max_uniform_buffer_binding_size: self.max_buffer_size.min(!0u32 as u64) as u32, max_storage_buffer_binding_size: self.max_buffer_size.min(!0u32 as u64) as u32, max_vertex_buffers: self.max_vertex_buffers, diff --git a/wgpu-hal/src/metal/mod.rs b/wgpu-hal/src/metal/mod.rs index cd1136a3b4..87d32c84d5 100644 --- a/wgpu-hal/src/metal/mod.rs +++ b/wgpu-hal/src/metal/mod.rs @@ -261,6 +261,8 @@ struct PrivateCapabilities { max_vertex_buffers: ResourceIndex, max_textures_per_stage: ResourceIndex, max_samplers_per_stage: ResourceIndex, + max_binding_array_elements: ResourceIndex, + max_sampler_binding_array_elements: ResourceIndex, buffer_alignment: u64, max_buffer_size: u64, max_texture_size: u64, diff --git a/wgpu-hal/src/vulkan/adapter.rs b/wgpu-hal/src/vulkan/adapter.rs index 91b8b71f46..0005069b90 100644 --- a/wgpu-hal/src/vulkan/adapter.rs +++ b/wgpu-hal/src/vulkan/adapter.rs @@ -9,13 +9,13 @@ fn depth_stencil_required_flags() -> vk::FormatFeatureFlags { vk::FormatFeatureFlags::SAMPLED_IMAGE | vk::FormatFeatureFlags::DEPTH_STENCIL_ATTACHMENT } -//TODO: const fn? -fn indexing_features() -> wgt::Features { - wgt::Features::SAMPLED_TEXTURE_AND_STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING - | wgt::Features::STORAGE_TEXTURE_ARRAY_NON_UNIFORM_INDEXING - | wgt::Features::UNIFORM_BUFFER_INDEXING - | wgt::Features::PARTIALLY_BOUND_BINDING_ARRAY -} +const INDEXING_FEATURES: wgt::Features = wgt::Features::TEXTURE_BINDING_ARRAY + .union(wgt::Features::BUFFER_BINDING_ARRAY) + .union(wgt::Features::STORAGE_RESOURCE_BINDING_ARRAY) + .union(wgt::Features::SAMPLED_TEXTURE_AND_STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING) + .union(wgt::Features::STORAGE_TEXTURE_ARRAY_NON_UNIFORM_INDEXING) + .union(wgt::Features::UNIFORM_BUFFER_INDEXING) + .union(wgt::Features::PARTIALLY_BOUND_BINDING_ARRAY); /// Features supported by a [`vk::PhysicalDevice`] and its extensions. /// @@ -209,22 +209,13 @@ impl PhysicalDeviceFeatures { downlevel_flags: wgt::DownlevelFlags, private_caps: &super::PrivateCapabilities, ) -> Self { - let needs_sampled_image_non_uniform = requested_features.contains( + let needs_bindless = requested_features.intersects( wgt::Features::TEXTURE_BINDING_ARRAY - | wgt::Features::SAMPLED_TEXTURE_AND_STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING, - ); - let needs_storage_buffer_non_uniform = requested_features.contains( - wgt::Features::BUFFER_BINDING_ARRAY + | wgt::Features::BUFFER_BINDING_ARRAY | wgt::Features::STORAGE_RESOURCE_BINDING_ARRAY + | wgt::Features::STORAGE_TEXTURE_ARRAY_NON_UNIFORM_INDEXING | wgt::Features::SAMPLED_TEXTURE_AND_STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING, ); - let needs_uniform_buffer_non_uniform = - requested_features.contains(wgt::Features::UNIFORM_BUFFER_INDEXING); - let needs_storage_image_non_uniform = requested_features.contains( - wgt::Features::TEXTURE_BINDING_ARRAY - | wgt::Features::STORAGE_RESOURCE_BINDING_ARRAY - | wgt::Features::STORAGE_TEXTURE_ARRAY_NON_UNIFORM_INDEXING, - ); let needs_partially_bound = requested_features.intersects(wgt::Features::PARTIALLY_BOUND_BINDING_ARRAY); @@ -302,21 +293,15 @@ impl PhysicalDeviceFeatures { .geometry_shader(requested_features.contains(wgt::Features::SHADER_PRIMITIVE_INDEX)) .depth_clamp(requested_features.contains(wgt::Features::DEPTH_CLIP_CONTROL)) .dual_src_blend(requested_features.contains(wgt::Features::DUAL_SOURCE_BLENDING)), - descriptor_indexing: if requested_features.intersects(indexing_features()) { + descriptor_indexing: if requested_features.intersects(INDEXING_FEATURES) { Some( vk::PhysicalDeviceDescriptorIndexingFeaturesEXT::default() - .shader_sampled_image_array_non_uniform_indexing( - needs_sampled_image_non_uniform, - ) - .shader_storage_image_array_non_uniform_indexing( - needs_storage_image_non_uniform, - ) - .shader_uniform_buffer_array_non_uniform_indexing( - needs_uniform_buffer_non_uniform, - ) - .shader_storage_buffer_array_non_uniform_indexing( - needs_storage_buffer_non_uniform, - ) + .shader_sampled_image_array_non_uniform_indexing(needs_bindless) + .shader_storage_image_array_non_uniform_indexing(needs_bindless) + .shader_storage_buffer_array_non_uniform_indexing(needs_bindless) + .descriptor_binding_sampled_image_update_after_bind(needs_bindless) + .descriptor_binding_storage_image_update_after_bind(needs_bindless) + .descriptor_binding_storage_buffer_update_after_bind(needs_bindless) .descriptor_binding_partially_bound(needs_partially_bound), ) } else { @@ -947,7 +932,7 @@ impl PhysicalDeviceProperties { } // Require `VK_EXT_descriptor_indexing` if one of the associated features was requested - if requested_features.intersects(indexing_features()) { + if requested_features.intersects(INDEXING_FEATURES) { extensions.push(ext::descriptor_indexing::NAME); } @@ -1068,6 +1053,24 @@ impl PhysicalDeviceProperties { u64::MAX }; + let mut max_binding_array_elements = 0; + let mut max_sampler_binding_array_elements = 0; + if let Some(ref descriptor_indexing) = self.descriptor_indexing { + max_binding_array_elements = descriptor_indexing + .max_descriptor_set_update_after_bind_sampled_images + .min(descriptor_indexing.max_descriptor_set_update_after_bind_storage_images) + .min(descriptor_indexing.max_descriptor_set_update_after_bind_storage_buffers) + .min(descriptor_indexing.max_per_stage_descriptor_update_after_bind_sampled_images) + .min(descriptor_indexing.max_per_stage_descriptor_update_after_bind_storage_images) + .min( + descriptor_indexing.max_per_stage_descriptor_update_after_bind_storage_buffers, + ); + + max_sampler_binding_array_elements = descriptor_indexing + .max_descriptor_set_update_after_bind_samplers + .min(descriptor_indexing.max_per_stage_descriptor_update_after_bind_samplers); + } + // TODO: programmatically determine this, if possible. It's unclear whether we can // as of https://github.com/gpuweb/gpuweb/issues/2965#issuecomment-1361315447. // @@ -1096,6 +1099,8 @@ impl PhysicalDeviceProperties { max_storage_buffers_per_shader_stage: limits.max_per_stage_descriptor_storage_buffers, max_storage_textures_per_shader_stage: limits.max_per_stage_descriptor_storage_images, max_uniform_buffers_per_shader_stage: limits.max_per_stage_descriptor_uniform_buffers, + max_binding_array_elements_per_shader_stage: max_binding_array_elements, + max_binding_array_sampler_elements_per_shader_stage: max_sampler_binding_array_elements, max_uniform_buffer_binding_size: limits .max_uniform_buffer_range .min(crate::auxil::MAX_I32_BINDING_SIZE), diff --git a/wgpu-hal/src/vulkan/device.rs b/wgpu-hal/src/vulkan/device.rs index 45df3c8fdb..bd60ba1fdf 100644 --- a/wgpu-hal/src/vulkan/device.rs +++ b/wgpu-hal/src/vulkan/device.rs @@ -1461,44 +1461,47 @@ impl crate::Device for super::Device { }) .collect::>(); - let vk_info = vk::DescriptorSetLayoutCreateInfo::default().bindings(&vk_bindings); - - let binding_arrays = desc + let binding_arrays: Vec<_> = desc .entries .iter() .enumerate() .filter_map(|(idx, entry)| entry.count.map(|count| (idx as u32, count))) .collect(); - let mut binding_flag_info; - let binding_flag_vec; + let vk_info = vk::DescriptorSetLayoutCreateInfo::default() + .bindings(&vk_bindings) + .flags(if !binding_arrays.is_empty() { + vk::DescriptorSetLayoutCreateFlags::UPDATE_AFTER_BIND_POOL + } else { + vk::DescriptorSetLayoutCreateFlags::empty() + }); let partially_bound = desc .flags .contains(crate::BindGroupLayoutFlags::PARTIALLY_BOUND); - let vk_info = if partially_bound { - binding_flag_vec = desc - .entries - .iter() - .map(|entry| { - let mut flags = vk::DescriptorBindingFlags::empty(); + let binding_flag_vec = desc + .entries + .iter() + .map(|entry| { + let mut flags = vk::DescriptorBindingFlags::empty(); - if partially_bound && entry.count.is_some() { - flags |= vk::DescriptorBindingFlags::PARTIALLY_BOUND; - } + if partially_bound && entry.count.is_some() { + flags |= vk::DescriptorBindingFlags::PARTIALLY_BOUND; + } - flags - }) - .collect::>(); + if entry.count.is_some() { + flags |= vk::DescriptorBindingFlags::UPDATE_AFTER_BIND; + } - binding_flag_info = vk::DescriptorSetLayoutBindingFlagsCreateInfo::default() - .binding_flags(&binding_flag_vec); + flags + }) + .collect::>(); - vk_info.push_next(&mut binding_flag_info) - } else { - vk_info - }; + let mut binding_flag_info = vk::DescriptorSetLayoutBindingFlagsCreateInfo::default() + .binding_flags(&binding_flag_vec); + + let vk_info = vk_info.push_next(&mut binding_flag_info); let raw = unsafe { self.shared diff --git a/wgpu-info/src/human.rs b/wgpu-info/src/human.rs index 8bbd4c006e..1c2f7a841d 100644 --- a/wgpu-info/src/human.rs +++ b/wgpu-info/src/human.rs @@ -137,15 +137,14 @@ fn print_adapter(output: &mut impl io::Write, report: &AdapterReport, idx: usize 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, + max_binding_array_sampler_elements_per_shader_stage, max_uniform_buffer_binding_size, max_storage_buffer_binding_size, - max_buffer_size, max_vertex_buffers, + max_buffer_size, max_vertex_attributes, max_vertex_buffer_array_stride, - min_subgroup_size, - max_subgroup_size, - max_push_constant_size, min_uniform_buffer_offset_alignment, min_storage_buffer_offset_alignment, max_inter_stage_shader_components, @@ -157,41 +156,46 @@ fn print_adapter(output: &mut impl io::Write, report: &AdapterReport, idx: usize max_compute_workgroup_size_y, max_compute_workgroup_size_z, max_compute_workgroups_per_dimension, + min_subgroup_size, + max_subgroup_size, + max_push_constant_size, max_non_sampler_bindings, } = limits; - writeln!(output, "\t\t Max Texture Dimension 1d: {max_texture_dimension_1d}")?; - writeln!(output, "\t\t Max Texture Dimension 2d: {max_texture_dimension_2d}")?; - writeln!(output, "\t\t Max Texture Dimension 3d: {max_texture_dimension_3d}")?; - writeln!(output, "\t\t Max Texture Array Layers: {max_texture_array_layers}")?; - writeln!(output, "\t\t Max Bind Groups: {max_bind_groups}")?; - writeln!(output, "\t\t Max Bindings Per Bind Group: {max_bindings_per_bind_group}")?; - writeln!(output, "\t\t Max Dynamic Uniform Buffers Per Pipeline Layout: {max_dynamic_uniform_buffers_per_pipeline_layout}")?; - writeln!(output, "\t\t Max Dynamic Storage Buffers Per Pipeline Layout: {max_dynamic_storage_buffers_per_pipeline_layout}")?; - writeln!(output, "\t\t Max Sampled Textures Per Shader Stage: {max_sampled_textures_per_shader_stage}")?; - writeln!(output, "\t\t Max Samplers Per Shader Stage: {max_samplers_per_shader_stage}")?; - writeln!(output, "\t\t Max Storage Buffers Per Shader Stage: {max_storage_buffers_per_shader_stage}")?; - writeln!(output, "\t\t Max Storage Textures Per Shader Stage: {max_storage_textures_per_shader_stage}")?; - writeln!(output, "\t\t Max Uniform Buffers Per Shader Stage: {max_uniform_buffers_per_shader_stage}")?; - writeln!(output, "\t\t Max Uniform Buffer Binding Size: {max_uniform_buffer_binding_size}")?; - writeln!(output, "\t\t Max Storage Buffer Binding Size: {max_storage_buffer_binding_size}")?; - writeln!(output, "\t\t Max Buffer Size: {max_buffer_size}")?; - writeln!(output, "\t\t Max Vertex Buffers: {max_vertex_buffers}")?; - writeln!(output, "\t\t Max Vertex Attributes: {max_vertex_attributes}")?; - writeln!(output, "\t\t Max Vertex Buffer Array Stride: {max_vertex_buffer_array_stride}")?; - writeln!(output, "\t\t Min Subgroup Size: {min_subgroup_size}")?; - writeln!(output, "\t\t Max Subgroup Size: {max_subgroup_size}")?; - writeln!(output, "\t\t Max Push Constant Size: {max_push_constant_size}")?; - writeln!(output, "\t\t Min Uniform Buffer Offset Alignment: {min_uniform_buffer_offset_alignment}")?; - writeln!(output, "\t\t Min Storage Buffer Offset Alignment: {min_storage_buffer_offset_alignment}")?; - writeln!(output, "\t\t Max Inter-Stage Shader Component: {max_inter_stage_shader_components}")?; - writeln!(output, "\t\t Max Color Attachments: {max_color_attachments}")?; - writeln!(output, "\t\t Max Color Attachment Bytes per sample: {max_color_attachment_bytes_per_sample}")?; - writeln!(output, "\t\t Max Compute Workgroup Storage Size: {max_compute_workgroup_storage_size}")?; - writeln!(output, "\t\t Max Compute Invocations Per Workgroup: {max_compute_invocations_per_workgroup}")?; - writeln!(output, "\t\t Max Compute Workgroup Size X: {max_compute_workgroup_size_x}")?; - writeln!(output, "\t\t Max Compute Workgroup Size Y: {max_compute_workgroup_size_y}")?; - writeln!(output, "\t\t Max Compute Workgroup Size Z: {max_compute_workgroup_size_z}")?; - writeln!(output, "\t\t Max Compute Workgroups Per Dimension: {max_compute_workgroups_per_dimension}")?; + writeln!(output, "\t\t Max Texture Dimension 1d: {max_texture_dimension_1d}")?; + writeln!(output, "\t\t Max Texture Dimension 2d: {max_texture_dimension_2d}")?; + writeln!(output, "\t\t Max Texture Dimension 3d: {max_texture_dimension_3d}")?; + writeln!(output, "\t\t Max Texture Array Layers: {max_texture_array_layers}")?; + writeln!(output, "\t\t Max Bind Groups: {max_bind_groups}")?; + writeln!(output, "\t\t Max Bindings Per Bind Group: {max_bindings_per_bind_group}")?; + writeln!(output, "\t\t Max Dynamic Uniform Buffers Per Pipeline Layout: {max_dynamic_uniform_buffers_per_pipeline_layout}")?; + writeln!(output, "\t\t Max Dynamic Storage Buffers Per Pipeline Layout: {max_dynamic_storage_buffers_per_pipeline_layout}")?; + writeln!(output, "\t\t Max Sampled Textures Per Shader Stage: {max_sampled_textures_per_shader_stage}")?; + writeln!(output, "\t\t Max Samplers Per Shader Stage: {max_samplers_per_shader_stage}")?; + writeln!(output, "\t\t Max Storage Buffers Per Shader Stage: {max_storage_buffers_per_shader_stage}")?; + writeln!(output, "\t\t Max Storage Textures Per Shader Stage: {max_storage_textures_per_shader_stage}")?; + writeln!(output, "\t\t Max Uniform Buffers Per Shader Stage: {max_uniform_buffers_per_shader_stage}")?; + writeln!(output, "\t\t Max Binding Array Elements Per Shader Stage: {max_binding_array_elements_per_shader_stage}")?; + writeln!(output, "\t\tMax Binding Array Sampler Elements Per Shader Stage: {max_binding_array_sampler_elements_per_shader_stage}")?; + writeln!(output, "\t\t Max Uniform Buffer Binding Size: {max_uniform_buffer_binding_size}")?; + writeln!(output, "\t\t Max Storage Buffer Binding Size: {max_storage_buffer_binding_size}")?; + writeln!(output, "\t\t Max Buffer Size: {max_buffer_size}")?; + writeln!(output, "\t\t Max Vertex Buffers: {max_vertex_buffers}")?; + writeln!(output, "\t\t Max Vertex Attributes: {max_vertex_attributes}")?; + writeln!(output, "\t\t Max Vertex Buffer Array Stride: {max_vertex_buffer_array_stride}")?; + writeln!(output, "\t\t Min Subgroup Size: {min_subgroup_size}")?; + writeln!(output, "\t\t Max Subgroup Size: {max_subgroup_size}")?; + writeln!(output, "\t\t Max Push Constant Size: {max_push_constant_size}")?; + writeln!(output, "\t\t Min Uniform Buffer Offset Alignment: {min_uniform_buffer_offset_alignment}")?; + writeln!(output, "\t\t Min Storage Buffer Offset Alignment: {min_storage_buffer_offset_alignment}")?; + writeln!(output, "\t\t Max Inter-Stage Shader Component: {max_inter_stage_shader_components}")?; + writeln!(output, "\t\t Max Color Attachments: {max_color_attachments}")?; + writeln!(output, "\t\t Max Color Attachment Bytes per sample: {max_color_attachment_bytes_per_sample}")?; + writeln!(output, "\t\t Max Compute Workgroup Storage Size: {max_compute_workgroup_storage_size}")?; + writeln!(output, "\t\t Max Compute Invocations Per Workgroup: {max_compute_invocations_per_workgroup}")?; + writeln!(output, "\t\t Max Compute Workgroup Size X: {max_compute_workgroup_size_x}")?; + writeln!(output, "\t\t Max Compute Workgroup Size Y: {max_compute_workgroup_size_y}")?; + writeln!(output, "\t\t Max Compute Workgroup Size Z: {max_compute_workgroup_size_z}")?; + writeln!(output, "\t\t Max Compute Workgroups Per Dimension: {max_compute_workgroups_per_dimension}")?; // This one reflects more of a wgpu implementation limitations than a hardware limit // so don't show it here. diff --git a/wgpu-types/src/lib.rs b/wgpu-types/src/lib.rs index d168d76839..49d90421dc 100644 --- a/wgpu-types/src/lib.rs +++ b/wgpu-types/src/lib.rs @@ -1191,6 +1191,15 @@ pub struct Limits { pub max_storage_textures_per_shader_stage: u32, /// Amount of uniform buffers visible in a single shader stage. Defaults to 12. Higher is "better". pub max_uniform_buffers_per_shader_stage: u32, + /// Amount of individual resources within binding arrays that can be accessed in a single shader stage. Applies + /// to all types of bindings except samplers. + /// + /// This "defaults" to 0. However if binding arrays are supported, all devices can support 500,000. Higher is "better". + pub max_binding_array_elements_per_shader_stage: u32, + /// Amount of individual samplers within binding arrays that can be accessed in a single shader stage. + /// + /// This "defaults" to 0. However if binding arrays are supported, all devices can support 1,000. Higher is "better". + pub max_binding_array_sampler_elements_per_shader_stage: u32, /// Maximum size in bytes of a binding to a uniform buffer. Defaults to 64 KiB. Higher is "better". pub max_uniform_buffer_binding_size: u32, /// Maximum size in bytes of a binding to a storage buffer. Defaults to 128 MiB. Higher is "better". @@ -1295,6 +1304,8 @@ impl Limits { max_storage_buffers_per_shader_stage: 8, max_storage_textures_per_shader_stage: 4, max_uniform_buffers_per_shader_stage: 12, + max_binding_array_elements_per_shader_stage: 0, + max_binding_array_sampler_elements_per_shader_stage: 0, max_uniform_buffer_binding_size: 64 << 10, // (64 KiB) max_storage_buffer_binding_size: 128 << 20, // (128 MiB) max_vertex_buffers: 8, @@ -1338,6 +1349,8 @@ impl Limits { /// max_storage_buffers_per_shader_stage: 4, // * /// max_storage_textures_per_shader_stage: 4, /// max_uniform_buffers_per_shader_stage: 12, + /// max_binding_array_elements_per_shader_stage: 0, + /// max_binding_array_sampler_elements_per_shader_stage: 0, /// max_uniform_buffer_binding_size: 16 << 10, // * (16 KiB) /// max_storage_buffer_binding_size: 128 << 20, // (128 MiB) /// max_vertex_buffers: 8, @@ -1395,6 +1408,8 @@ impl Limits { /// max_storage_buffers_per_shader_stage: 0, // * + /// max_storage_textures_per_shader_stage: 0, // + /// max_uniform_buffers_per_shader_stage: 11, // + + /// max_binding_array_elements_per_shader_stage: 0, + /// max_binding_array_sampler_elements_per_shader_stage: 0, /// max_uniform_buffer_binding_size: 16 << 10, // * (16 KiB) /// max_storage_buffer_binding_size: 0, // * + /// max_vertex_buffers: 8, @@ -1526,6 +1541,7 @@ impl Limits { compare!(max_storage_buffers_per_shader_stage, Less); compare!(max_storage_textures_per_shader_stage, Less); compare!(max_uniform_buffers_per_shader_stage, Less); + compare!(max_binding_array_elements_per_shader_stage, Less); compare!(max_uniform_buffer_binding_size, Less); compare!(max_storage_buffer_binding_size, Less); compare!(max_vertex_buffers, Less); diff --git a/wgpu/src/backend/webgpu.rs b/wgpu/src/backend/webgpu.rs index 9c11a129c2..81bb0af9ae 100644 --- a/wgpu/src/backend/webgpu.rs +++ b/wgpu/src/backend/webgpu.rs @@ -812,6 +812,8 @@ fn map_wgt_limits(limits: webgpu_sys::GpuSupportedLimits) -> wgt::Limits { max_storage_buffers_per_shader_stage: limits.max_storage_buffers_per_shader_stage(), max_storage_textures_per_shader_stage: limits.max_storage_textures_per_shader_stage(), max_uniform_buffers_per_shader_stage: limits.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: limits.max_uniform_buffer_binding_size() as u32, max_storage_buffer_binding_size: limits.max_storage_buffer_binding_size() as u32, max_vertex_buffers: limits.max_vertex_buffers(),