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