diff --git a/wgpu-core/src/device/mod.rs b/wgpu-core/src/device/mod.rs index 68e0c1615..aa8b0991a 100644 --- a/wgpu-core/src/device/mod.rs +++ b/wgpu-core/src/device/mod.rs @@ -576,6 +576,14 @@ impl Device { transient: bool, ) -> Result, resource::CreateBufferError> { debug_assert_eq!(self_id.backend(), A::VARIANT); + + if desc.size > self.limits.max_buffer_size { + return Err(resource::CreateBufferError::MaxBufferSize { + requested: desc.size, + maximum: self.limits.max_buffer_size, + }); + } + let mut usage = conv::map_buffer_usage(desc.usage); if desc.usage.is_empty() { diff --git a/wgpu-core/src/instance.rs b/wgpu-core/src/instance.rs index 448c058c8..bcd6b4185 100644 --- a/wgpu-core/src/instance.rs +++ b/wgpu-core/src/instance.rs @@ -23,8 +23,8 @@ pub struct HalSurface { #[error("Limit '{name}' value {requested} is better than allowed {allowed}")] pub struct FailedLimit { name: &'static str, - requested: u32, - allowed: u32, + requested: u64, + allowed: u64, } fn check_limits(requested: &wgt::Limits, allowed: &wgt::Limits) -> Vec { diff --git a/wgpu-core/src/resource.rs b/wgpu-core/src/resource.rs index 9335e79f8..10812271d 100644 --- a/wgpu-core/src/resource.rs +++ b/wgpu-core/src/resource.rs @@ -174,6 +174,8 @@ pub enum CreateBufferError { EmptyUsage, #[error("`MAP` usage can only be combined with the opposite `COPY`, requested {0:?}")] UsageMismatch(wgt::BufferUsages), + #[error("Buffer size {requested} is greater than the maximum buffer size ({maximum})")] + MaxBufferSize { requested: u64, maximum: u64 }, } impl Resource for Buffer { diff --git a/wgpu-hal/src/dx11/adapter.rs b/wgpu-hal/src/dx11/adapter.rs index 3bc531eae..bfae7b203 100644 --- a/wgpu-hal/src/dx11/adapter.rs +++ b/wgpu-hal/src/dx11/adapter.rs @@ -220,6 +220,8 @@ impl super::Adapter { max_compute_workgroup_size_y: max_workgroup_size_xy, max_compute_workgroup_size_z: max_workgroup_size_z, max_compute_workgroups_per_dimension, + // D3D11_BUFFER_DESC represents the buffer size as a 32 bit int. + max_buffer_size: u32::MAX as u64, }; // diff --git a/wgpu-hal/src/dx12/adapter.rs b/wgpu-hal/src/dx12/adapter.rs index c8d390fcf..aedbd9c19 100644 --- a/wgpu-hal/src/dx12/adapter.rs +++ b/wgpu-hal/src/dx12/adapter.rs @@ -282,6 +282,7 @@ impl super::Adapter { max_compute_workgroup_size_z: d3d12::D3D12_CS_THREAD_GROUP_MAX_Z, max_compute_workgroups_per_dimension: d3d12::D3D12_CS_DISPATCH_MAX_THREAD_GROUPS_PER_DIMENSION, + max_buffer_size: u64::MAX, }, alignments: crate::Alignments { buffer_copy_offset: wgt::BufferSize::new( diff --git a/wgpu-hal/src/gles/adapter.rs b/wgpu-hal/src/gles/adapter.rs index 7a7b1b09a..3bd7bae5e 100644 --- a/wgpu-hal/src/gles/adapter.rs +++ b/wgpu-hal/src/gles/adapter.rs @@ -461,6 +461,7 @@ impl super::Adapter { 0 }, max_compute_workgroups_per_dimension, + max_buffer_size: i32::MAX as u64, }; let mut workarounds = super::Workarounds::empty(); diff --git a/wgpu-hal/src/metal/adapter.rs b/wgpu-hal/src/metal/adapter.rs index 04eb4091d..1325b901f 100644 --- a/wgpu-hal/src/metal/adapter.rs +++ b/wgpu-hal/src/metal/adapter.rs @@ -847,6 +847,7 @@ impl super::PrivateCapabilities { max_compute_workgroup_size_y: self.max_threads_per_group, max_compute_workgroup_size_z: self.max_threads_per_group, max_compute_workgroups_per_dimension: 0xFFFF, + max_buffer_size: self.max_buffer_size, }, alignments: crate::Alignments { buffer_copy_offset: wgt::BufferSize::new(self.buffer_alignment).unwrap(), diff --git a/wgpu-hal/src/vulkan/adapter.rs b/wgpu-hal/src/vulkan/adapter.rs index f55ccf6ff..e2d46eca3 100644 --- a/wgpu-hal/src/vulkan/adapter.rs +++ b/wgpu-hal/src/vulkan/adapter.rs @@ -768,6 +768,15 @@ impl PhysicalDeviceCapabilities { .min(limits.max_compute_work_group_count[1]) .min(limits.max_compute_work_group_count[2]); + // Prevent very large buffers on mesa and most android devices. + let is_nvidia = self.properties.vendor_id == crate::auxil::db::nvidia::VENDOR; + let max_buffer_size = + if (cfg!(target_os = "linux") || cfg!(target_os = "android")) && !is_nvidia { + i32::MAX as u64 + } else { + u64::MAX + }; + wgt::Limits { max_texture_dimension_1d: limits.max_image_dimension1_d, max_texture_dimension_2d: limits.max_image_dimension2_d, @@ -808,6 +817,7 @@ impl PhysicalDeviceCapabilities { max_compute_workgroup_size_y: max_compute_workgroup_sizes[1], max_compute_workgroup_size_z: max_compute_workgroup_sizes[2], max_compute_workgroups_per_dimension, + max_buffer_size, } } diff --git a/wgpu-info/src/main.rs b/wgpu-info/src/main.rs index 84f0c61f3..53eff5c7b 100644 --- a/wgpu-info/src/main.rs +++ b/wgpu-info/src/main.rs @@ -47,6 +47,7 @@ mod inner { max_uniform_buffers_per_shader_stage, max_uniform_buffer_binding_size, max_storage_buffer_binding_size, + max_buffer_size, max_vertex_buffers, max_vertex_attributes, max_vertex_buffer_array_stride, @@ -75,6 +76,7 @@ mod inner { println!("\t\tMax Uniform Buffers Per Shader Stage: {}", max_uniform_buffers_per_shader_stage); println!("\t\tMax Uniform Buffer Binding Size: {}", max_uniform_buffer_binding_size); println!("\t\tMax Storage Buffer Binding Size: {}", max_storage_buffer_binding_size); + println!("\t\tMax Buffer Size: {}", max_buffer_size); println!("\t\tMax Vertex Buffers: {}", max_vertex_buffers); println!("\t\tMax Vertex Attributes: {}", max_vertex_attributes); println!("\t\tMax Vertex Buffer Array Stride: {}", max_vertex_buffer_array_stride); diff --git a/wgpu-types/src/lib.rs b/wgpu-types/src/lib.rs index 70014447e..1737c9517 100644 --- a/wgpu-types/src/lib.rs +++ b/wgpu-types/src/lib.rs @@ -737,6 +737,11 @@ pub struct Limits { /// The maximum value for each dimension of a `ComputePass::dispatch(x, y, z)` operation. /// Defaults to 65535. pub max_compute_workgroups_per_dimension: u32, + /// A limit above which buffer allocations are guaranteed to fail. + /// + /// Buffer allocations below the maximum buffer size may not succed depending on available memory, + /// fragmentation and other factors. + pub max_buffer_size: u64, } impl Default for Limits { @@ -769,6 +774,7 @@ impl Default for Limits { max_compute_workgroup_size_y: 256, max_compute_workgroup_size_z: 64, max_compute_workgroups_per_dimension: 65535, + max_buffer_size: 1 << 30, } } } @@ -804,6 +810,7 @@ impl Limits { max_compute_workgroup_size_y: 256, max_compute_workgroup_size_z: 64, max_compute_workgroups_per_dimension: 65535, + max_buffer_size: 1 << 28, } } @@ -876,7 +883,7 @@ impl Limits { &self, allowed: &Self, fatal: bool, - mut fail_fn: impl FnMut(&'static str, u32, u32), + mut fail_fn: impl FnMut(&'static str, u64, u64), ) { use std::cmp::Ordering; @@ -885,7 +892,7 @@ impl Limits { match self.$name.cmp(&allowed.$name) { Ordering::$ordering | Ordering::Equal => (), _ => { - fail_fn(stringify!($name), self.$name, allowed.$name); + fail_fn(stringify!($name), self.$name as u64, allowed.$name as u64); if fatal { return; } @@ -921,6 +928,7 @@ impl Limits { compare!(max_compute_workgroup_size_y, Less); compare!(max_compute_workgroup_size_z, Less); compare!(max_compute_workgroups_per_dimension, Less); + compare!(max_buffer_size, Less); } }