#[cfg(dx12)] pub(super) mod dxgi; #[cfg(all(native, feature = "renderdoc"))] pub(super) mod renderdoc; pub mod db { pub mod amd { /// cbindgen:ignore pub const VENDOR: u32 = 0x1002; } pub mod apple { /// cbindgen:ignore pub const VENDOR: u32 = 0x106B; } pub mod arm { /// cbindgen:ignore pub const VENDOR: u32 = 0x13B5; } pub mod broadcom { /// cbindgen:ignore pub const VENDOR: u32 = 0x14E4; } pub mod imgtec { /// cbindgen:ignore pub const VENDOR: u32 = 0x1010; } pub mod intel { /// cbindgen:ignore pub const VENDOR: u32 = 0x8086; pub const DEVICE_KABY_LAKE_MASK: u32 = 0x5900; pub const DEVICE_SKY_LAKE_MASK: u32 = 0x1900; } pub mod mesa { // Mesa does not actually have a PCI vendor id. // // To match Vulkan, we use the VkVendorId for Mesa in the gles backend so that lavapipe (Vulkan) and // llvmpipe (OpenGL) have the same vendor id. /// cbindgen:ignore pub const VENDOR: u32 = 0x10005; } pub mod nvidia { /// cbindgen:ignore pub const VENDOR: u32 = 0x10DE; } pub mod qualcomm { /// cbindgen:ignore pub const VENDOR: u32 = 0x5143; } } /// Maximum binding size for the shaders that only support `i32` indexing. /// Interestingly, the index itself can't reach that high, because the minimum /// element size is 4 bytes, but the compiler toolchain still computes the /// offset at some intermediate point, internally, as i32. pub const MAX_I32_BINDING_SIZE: u32 = (1 << 31) - 1; pub use wgpu_naga_bridge::map_naga_stage; impl crate::CopyExtent { pub fn map_extent_to_copy_size(extent: &wgt::Extent3d, dim: wgt::TextureDimension) -> Self { Self { width: extent.width, height: extent.height, depth: match dim { wgt::TextureDimension::D1 | wgt::TextureDimension::D2 => 1, wgt::TextureDimension::D3 => extent.depth_or_array_layers, }, } } pub fn min(&self, other: &Self) -> Self { Self { width: self.width.min(other.width), height: self.height.min(other.height), depth: self.depth.min(other.depth), } } // Get the copy size at a specific mipmap level. This doesn't make most sense, // since the copy extents are provided *for* a mipmap level to start with. // But backends use `CopyExtent` more sparingly, and this piece is shared. pub fn at_mip_level(&self, level: u32) -> Self { Self { width: (self.width >> level).max(1), height: (self.height >> level).max(1), depth: (self.depth >> level).max(1), } } } impl crate::TextureCopyBase { pub fn max_copy_size(&self, full_size: &crate::CopyExtent) -> crate::CopyExtent { let mip = full_size.at_mip_level(self.mip_level); crate::CopyExtent { width: mip.width - self.origin.x, height: mip.height - self.origin.y, depth: mip.depth - self.origin.z, } } } impl crate::BufferTextureCopy { pub fn clamp_size_to_virtual(&mut self, full_size: &crate::CopyExtent) { let max_size = self.texture_base.max_copy_size(full_size); self.size = self.size.min(&max_size); } } impl crate::TextureCopy { pub fn clamp_size_to_virtual( &mut self, full_src_size: &crate::CopyExtent, full_dst_size: &crate::CopyExtent, ) { let max_src_size = self.src_base.max_copy_size(full_src_size); let max_dst_size = self.dst_base.max_copy_size(full_dst_size); self.size = self.size.min(&max_src_size).min(&max_dst_size); } } /// Adjust `limits` to honor HAL-imposed maximums and comply with WebGPU's /// adapter capability guarantees. #[cfg_attr(not(any_backend), allow(dead_code))] pub(crate) fn adjust_raw_limits(mut limits: wgt::Limits) -> wgt::Limits { // Apply hal limits. limits.max_bind_groups = limits.max_bind_groups.min(crate::MAX_BIND_GROUPS as u32); limits.max_vertex_buffers = limits .max_vertex_buffers .min(crate::MAX_VERTEX_BUFFERS as u32); limits.max_color_attachments = limits .max_color_attachments .min(crate::MAX_COLOR_ATTACHMENTS as u32); // Adjust limits according to WebGPU adapter capability guarantees. // See . // WebGPU requires maxBindingsPerBindGroup to be at least the sum of all // per-stage limits multiplied with the maximum shader stages per pipeline. // // Since backends already report their maximum maxBindingsPerBindGroup, // we need to lower all per-stage limits to satisfy this guarantee. const MAX_SHADER_STAGES_PER_PIPELINE: u32 = 2; let max_per_stage_resources = limits.max_bindings_per_bind_group / MAX_SHADER_STAGES_PER_PIPELINE; cap_limits_to_be_under_the_sum_limit( [ &mut limits.max_sampled_textures_per_shader_stage, &mut limits.max_uniform_buffers_per_shader_stage, &mut limits.max_storage_textures_per_shader_stage, &mut limits.max_storage_buffers_per_shader_stage, &mut limits.max_samplers_per_shader_stage, &mut limits.max_acceleration_structures_per_shader_stage, ], max_per_stage_resources, ); // Not required by the spec but dynamic buffers count // towards non-dynamic buffer limits as well. limits.max_dynamic_uniform_buffers_per_pipeline_layout = limits .max_dynamic_uniform_buffers_per_pipeline_layout .min(limits.max_uniform_buffers_per_shader_stage); limits.max_dynamic_storage_buffers_per_pipeline_layout = limits .max_dynamic_storage_buffers_per_pipeline_layout .min(limits.max_storage_buffers_per_shader_stage); limits.min_uniform_buffer_offset_alignment = limits.min_uniform_buffer_offset_alignment.max(32); limits.min_storage_buffer_offset_alignment = limits.min_storage_buffer_offset_alignment.max(32); limits.max_uniform_buffer_binding_size = limits .max_uniform_buffer_binding_size .min(limits.max_buffer_size); limits.max_storage_buffer_binding_size = limits .max_storage_buffer_binding_size .min(limits.max_buffer_size); limits.max_storage_buffer_binding_size &= !(u64::from(wgt::STORAGE_BINDING_SIZE_ALIGNMENT) - 1); limits.max_vertex_buffer_array_stride &= !(wgt::VERTEX_ALIGNMENT as u32 - 1); let x = limits.max_compute_workgroup_size_x; let y = limits.max_compute_workgroup_size_y; let z = limits.max_compute_workgroup_size_z; let m = limits.max_compute_invocations_per_workgroup; limits.max_compute_workgroup_size_x = x.min(m); limits.max_compute_workgroup_size_y = y.min(m); limits.max_compute_workgroup_size_z = z.min(m); limits.max_compute_invocations_per_workgroup = m.min(x.saturating_mul(y).saturating_mul(z)); limits } /// Evenly allocates space to each limit, /// capping them only if strictly necessary. pub fn cap_limits_to_be_under_the_sum_limit( mut limits: [&mut u32; N], sum_limit: u32, ) { limits.sort(); let mut rem_limit = sum_limit; let mut divisor = limits.len() as u32; for limit_to_adjust in limits { let limit = rem_limit / divisor; *limit_to_adjust = (*limit_to_adjust).min(limit); rem_limit -= *limit_to_adjust; divisor -= 1; } } #[cfg(test)] mod tests { use super::*; #[test] fn test_cap_limits_to_be_under_the_sum_limit() { test([3, 3, 3], 3, [1, 1, 1]); test([3, 2, 1], 3, [1, 1, 1]); test([1, 2, 3], 6, [1, 2, 3]); test([1, 2, 3], 3, [1, 1, 1]); test([1, 8, 100], 6, [1, 2, 3]); test([2, 80, 80], 6, [2, 2, 2]); test([2, 80, 80], 12, [2, 5, 5]); #[track_caller] fn test(mut input: [u32; N], limit: u32, output: [u32; N]) { cap_limits_to_be_under_the_sum_limit(input.each_mut(), limit); assert_eq!(input, output); } } }