Skip to main content

wgpu_hal/auxil/
mod.rs

1// Mostly DX12-only, but also compiled for Vulkan-on-Windows, which reuses
2// `dxgi::hdr` to query display HDR info. The DX12-only submodules stay gated
3// behind `dx12` in `dxgi/mod.rs`.
4#[cfg(any(dx12, all(vulkan, windows)))]
5pub(super) mod dxgi;
6
7#[cfg(all(native, feature = "renderdoc"))]
8pub(super) mod renderdoc;
9
10pub mod prune;
11
12pub mod db {
13    pub mod amd {
14        /// cbindgen:ignore
15        pub const VENDOR: u32 = 0x1002;
16    }
17    pub mod apple {
18        /// cbindgen:ignore
19        pub const VENDOR: u32 = 0x106B;
20    }
21    pub mod arm {
22        /// cbindgen:ignore
23        pub const VENDOR: u32 = 0x13B5;
24    }
25    pub mod broadcom {
26        /// cbindgen:ignore
27        pub const VENDOR: u32 = 0x14E4;
28    }
29    pub mod imgtec {
30        /// cbindgen:ignore
31        pub const VENDOR: u32 = 0x1010;
32    }
33    pub mod intel {
34        /// cbindgen:ignore
35        pub const VENDOR: u32 = 0x8086;
36        pub const DEVICE_KABY_LAKE_MASK: u32 = 0x5900;
37        pub const DEVICE_SKY_LAKE_MASK: u32 = 0x1900;
38    }
39    pub mod mesa {
40        // Mesa does not actually have a PCI vendor id.
41        //
42        // To match Vulkan, we use the VkVendorId for Mesa in the gles backend so that lavapipe (Vulkan) and
43        // llvmpipe (OpenGL) have the same vendor id.
44        /// cbindgen:ignore
45        pub const VENDOR: u32 = 0x10005;
46    }
47    pub mod nvidia {
48        /// cbindgen:ignore
49        pub const VENDOR: u32 = 0x10DE;
50    }
51    pub mod qualcomm {
52        /// cbindgen:ignore
53        pub const VENDOR: u32 = 0x5143;
54    }
55}
56
57/// Maximum binding size for the shaders that only support `i32` indexing.
58/// Interestingly, the index itself can't reach that high, because the minimum
59/// element size is 4 bytes, but the compiler toolchain still computes the
60/// offset at some intermediate point, internally, as i32.
61pub const MAX_I32_BINDING_SIZE: u32 = (1 << 31) - 1;
62
63pub use wgpu_naga_bridge::map_naga_stage;
64
65impl crate::CopyExtent {
66    pub fn map_extent_to_copy_size(extent: &wgt::Extent3d, dim: wgt::TextureDimension) -> Self {
67        Self {
68            width: extent.width,
69            height: extent.height,
70            depth: match dim {
71                wgt::TextureDimension::D1 | wgt::TextureDimension::D2 => 1,
72                wgt::TextureDimension::D3 => extent.depth_or_array_layers,
73            },
74        }
75    }
76
77    pub fn min(&self, other: &Self) -> Self {
78        Self {
79            width: self.width.min(other.width),
80            height: self.height.min(other.height),
81            depth: self.depth.min(other.depth),
82        }
83    }
84
85    // Get the copy size at a specific mipmap level. This doesn't make most sense,
86    // since the copy extents are provided *for* a mipmap level to start with.
87    // But backends use `CopyExtent` more sparingly, and this piece is shared.
88    pub fn at_mip_level(&self, level: u32) -> Self {
89        Self {
90            width: (self.width >> level).max(1),
91            height: (self.height >> level).max(1),
92            depth: (self.depth >> level).max(1),
93        }
94    }
95}
96
97impl crate::TextureCopyBase {
98    pub fn max_copy_size(&self, full_size: &crate::CopyExtent) -> crate::CopyExtent {
99        let mip = full_size.at_mip_level(self.mip_level);
100        crate::CopyExtent {
101            width: mip.width - self.origin.x,
102            height: mip.height - self.origin.y,
103            depth: mip.depth - self.origin.z,
104        }
105    }
106}
107
108impl crate::BufferTextureCopy {
109    pub fn clamp_size_to_virtual(&mut self, full_size: &crate::CopyExtent) {
110        let max_size = self.texture_base.max_copy_size(full_size);
111        self.size = self.size.min(&max_size);
112    }
113}
114
115impl crate::TextureCopy {
116    pub fn clamp_size_to_virtual(
117        &mut self,
118        full_src_size: &crate::CopyExtent,
119        full_dst_size: &crate::CopyExtent,
120    ) {
121        let max_src_size = self.src_base.max_copy_size(full_src_size);
122        let max_dst_size = self.dst_base.max_copy_size(full_dst_size);
123        self.size = self.size.min(&max_src_size).min(&max_dst_size);
124    }
125}
126
127/// Adjust `limits` to honor HAL-imposed maximums and comply with WebGPU's
128/// adapter capability guarantees.
129#[cfg_attr(not(any_backend), allow(dead_code))]
130pub(crate) fn adjust_raw_limits(mut limits: wgt::Limits) -> wgt::Limits {
131    // Apply hal limits.
132    limits.max_bind_groups = limits.max_bind_groups.min(crate::MAX_BIND_GROUPS as u32);
133    limits.max_vertex_buffers = limits
134        .max_vertex_buffers
135        .min(crate::MAX_VERTEX_BUFFERS as u32);
136    // Once we allow the 2 limits above to be higher than 24 we should use
137    // `cap_limits_to_be_under_the_sum_limit` to cap them under
138    // `max_bind_groups_plus_vertex_buffers`.
139    const { assert!(crate::MAX_BIND_GROUPS + crate::MAX_VERTEX_BUFFERS == 24) };
140    limits.max_bind_groups_plus_vertex_buffers = limits.max_bind_groups_plus_vertex_buffers.min(24);
141    limits.max_color_attachments = limits
142        .max_color_attachments
143        .min(crate::MAX_COLOR_ATTACHMENTS as u32);
144
145    // Adjust limits according to WebGPU adapter capability guarantees.
146    // See <https://gpuweb.github.io/gpuweb/#adapter-capability-guarantees>.
147
148    // WebGPU requires maxBindingsPerBindGroup to be at least the sum of all
149    // per-stage limits multiplied with the maximum shader stages per pipeline.
150    //
151    // Since backends already report their maximum maxBindingsPerBindGroup,
152    // we need to lower all per-stage limits to satisfy this guarantee.
153    const MAX_SHADER_STAGES_PER_PIPELINE: u32 = 2;
154    let max_per_stage_resources =
155        limits.max_bindings_per_bind_group / MAX_SHADER_STAGES_PER_PIPELINE;
156
157    cap_limits_to_be_under_the_sum_limit(
158        [
159            &mut limits.max_sampled_textures_per_shader_stage,
160            &mut limits.max_uniform_buffers_per_shader_stage,
161            &mut limits.max_storage_textures_per_shader_stage,
162            &mut limits.max_storage_buffers_per_shader_stage,
163            &mut limits.max_samplers_per_shader_stage,
164            &mut limits.max_acceleration_structures_per_shader_stage,
165        ],
166        max_per_stage_resources,
167    );
168
169    // Not required by the spec but dynamic buffers count
170    // towards non-dynamic buffer limits as well.
171    limits.max_dynamic_uniform_buffers_per_pipeline_layout = limits
172        .max_dynamic_uniform_buffers_per_pipeline_layout
173        .min(limits.max_uniform_buffers_per_shader_stage);
174    limits.max_dynamic_storage_buffers_per_pipeline_layout = limits
175        .max_dynamic_storage_buffers_per_pipeline_layout
176        .min(limits.max_storage_buffers_per_shader_stage);
177
178    limits.min_uniform_buffer_offset_alignment = limits.min_uniform_buffer_offset_alignment.max(32);
179    limits.min_storage_buffer_offset_alignment = limits.min_storage_buffer_offset_alignment.max(32);
180
181    limits.max_uniform_buffer_binding_size = limits
182        .max_uniform_buffer_binding_size
183        .min(limits.max_buffer_size);
184    limits.max_storage_buffer_binding_size = limits
185        .max_storage_buffer_binding_size
186        .min(limits.max_buffer_size);
187
188    limits.max_storage_buffer_binding_size &= !(u64::from(wgt::STORAGE_BINDING_SIZE_ALIGNMENT) - 1);
189    limits.max_vertex_buffer_array_stride &= !(wgt::VERTEX_ALIGNMENT as u32 - 1);
190
191    let x = limits.max_compute_workgroup_size_x;
192    let y = limits.max_compute_workgroup_size_y;
193    let z = limits.max_compute_workgroup_size_z;
194    let m = limits.max_compute_invocations_per_workgroup;
195    limits.max_compute_workgroup_size_x = x.min(m);
196    limits.max_compute_workgroup_size_y = y.min(m);
197    limits.max_compute_workgroup_size_z = z.min(m);
198    limits.max_compute_invocations_per_workgroup = m.min(x.saturating_mul(y).saturating_mul(z));
199
200    limits.max_immediate_size = limits.max_immediate_size.min(256);
201
202    limits
203}
204
205/// Evenly allocates space to each limit,
206/// capping them only if strictly necessary.
207pub fn cap_limits_to_be_under_the_sum_limit<const N: usize>(
208    mut limits: [&mut u32; N],
209    sum_limit: u32,
210) {
211    limits.sort();
212
213    let mut rem_limit = sum_limit;
214    let mut divisor = limits.len() as u32;
215    for limit_to_adjust in limits {
216        let limit = rem_limit / divisor;
217        *limit_to_adjust = (*limit_to_adjust).min(limit);
218        rem_limit -= *limit_to_adjust;
219        divisor -= 1;
220    }
221}
222
223#[cfg(test)]
224mod tests {
225    use super::*;
226
227    #[test]
228    fn test_cap_limits_to_be_under_the_sum_limit() {
229        test([3, 3, 3], 3, [1, 1, 1]);
230        test([3, 2, 1], 3, [1, 1, 1]);
231        test([1, 2, 3], 6, [1, 2, 3]);
232        test([1, 2, 3], 3, [1, 1, 1]);
233        test([1, 8, 100], 6, [1, 2, 3]);
234        test([2, 80, 80], 6, [2, 2, 2]);
235        test([2, 80, 80], 12, [2, 5, 5]);
236
237        #[track_caller]
238        fn test<const N: usize>(mut input: [u32; N], limit: u32, output: [u32; N]) {
239            cap_limits_to_be_under_the_sum_limit(input.each_mut(), limit);
240            assert_eq!(input, output);
241        }
242    }
243}