1use alloc::{boxed::Box, string::String, vec::Vec};
2use core::{fmt, num::NonZeroU32};
3
4use crate::{
5 binding_model,
6 ray_tracing::BlasCompactReadyPendingClosure,
7 resource::{
8 Buffer, BufferAccessError, BufferAccessResult, BufferMapCallback, Labeled,
9 RawResourceAccess, ResourceErrorIdent,
10 },
11 snatch::SnatchGuard,
12 Label, DOWNLEVEL_ERROR_MESSAGE,
13};
14
15use arrayvec::ArrayVec;
16use smallvec::SmallVec;
17use thiserror::Error;
18use wgt::{
19 error::{ErrorType, WebGpuError},
20 BufferAddress, DeviceLostReason, MapMode, TextureFormat,
21};
22
23pub(crate) mod bgl;
24mod life;
25pub mod queue;
26pub mod ray_tracing;
27pub mod resource;
28mod stage_compiler;
29pub(crate) mod surface_config;
30#[cfg(any(feature = "trace", feature = "replay"))]
31pub mod trace;
32pub use {life::WaitIdleError, resource::Device};
33
34pub const SHADER_STAGE_COUNT: usize = hal::MAX_CONCURRENT_SHADER_STAGES;
35pub(crate) const ZERO_BUFFER_SIZE: BufferAddress = 512 << 10;
38
39pub(crate) const ENTRYPOINT_FAILURE_ERROR: &str = "The given EntryPoint is Invalid";
40
41pub type DeviceDescriptor<'a> = wgt::DeviceDescriptor<Label<'a>>;
42pub type QueueDescriptor<'a> = wgt::QueueDescriptor<Label<'a>>;
43
44#[derive(Clone, Debug, Hash, PartialEq)]
45#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
46pub(crate) struct AttachmentData<T> {
47 pub colors: ArrayVec<Option<T>, { hal::MAX_COLOR_ATTACHMENTS }>,
48 pub resolves: ArrayVec<T, { hal::MAX_COLOR_ATTACHMENTS }>,
49 pub depth_stencil: Option<T>,
50}
51impl<T: PartialEq> Eq for AttachmentData<T> {}
52
53#[derive(Clone, Debug, Hash, PartialEq)]
54#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
55pub(crate) struct RenderPassContext {
56 pub attachments: AttachmentData<TextureFormat>,
57 pub sample_count: u32,
58 pub multiview_mask: Option<NonZeroU32>,
59}
60
61impl Default for RenderPassContext {
62 fn default() -> Self {
63 Self {
64 attachments: AttachmentData {
65 colors: ArrayVec::new(),
66 resolves: ArrayVec::new(),
67 depth_stencil: None,
68 },
69 sample_count: Default::default(),
70 multiview_mask: Default::default(),
71 }
72 }
73}
74
75#[derive(Clone, Debug, Error)]
76#[non_exhaustive]
77pub enum RenderPassCompatibilityError {
78 #[error(
79 "Incompatible color attachments at indices {indices:?}: the RenderPass uses textures with formats {expected:?} but the {res} uses attachments with formats {actual:?}",
80 )]
81 IncompatibleColorAttachment {
82 indices: Vec<usize>,
83 expected: Vec<Option<TextureFormat>>,
84 actual: Vec<Option<TextureFormat>>,
85 res: ResourceErrorIdent,
86 },
87 #[error(
88 "Incompatible depth-stencil attachment format: the RenderPass uses a texture with format {expected:?} but the {res} uses an attachment with format {actual:?}",
89 )]
90 IncompatibleDepthStencilAttachment {
91 expected: Option<TextureFormat>,
92 actual: Option<TextureFormat>,
93 res: ResourceErrorIdent,
94 },
95 #[error(
96 "Incompatible sample count: the RenderPass uses textures with sample count {expected:?} but the {res} uses attachments with format {actual:?}",
97 )]
98 IncompatibleSampleCount {
99 expected: u32,
100 actual: u32,
101 res: ResourceErrorIdent,
102 },
103 #[error("Incompatible multiview setting: the RenderPass uses setting {expected:?} but the {res} uses setting {actual:?}")]
104 IncompatibleMultiview {
105 expected: Option<NonZeroU32>,
106 actual: Option<NonZeroU32>,
107 res: ResourceErrorIdent,
108 },
109}
110
111impl WebGpuError for RenderPassCompatibilityError {
112 fn webgpu_error_type(&self) -> ErrorType {
113 ErrorType::Validation
114 }
115}
116
117impl RenderPassContext {
118 pub(crate) fn check_compatible<T: Labeled>(
120 &self,
121 other: &Self,
122 res: &T,
123 ) -> Result<(), RenderPassCompatibilityError> {
124 if self.attachments.colors != other.attachments.colors {
125 let indices = self
126 .attachments
127 .colors
128 .iter()
129 .zip(&other.attachments.colors)
130 .enumerate()
131 .filter_map(|(idx, (left, right))| (left != right).then_some(idx))
132 .collect();
133 return Err(RenderPassCompatibilityError::IncompatibleColorAttachment {
134 indices,
135 expected: self.attachments.colors.iter().cloned().collect(),
136 actual: other.attachments.colors.iter().cloned().collect(),
137 res: res.error_ident(),
138 });
139 }
140 if self.attachments.depth_stencil != other.attachments.depth_stencil {
141 return Err(
142 RenderPassCompatibilityError::IncompatibleDepthStencilAttachment {
143 expected: self.attachments.depth_stencil,
144 actual: other.attachments.depth_stencil,
145 res: res.error_ident(),
146 },
147 );
148 }
149 if self.sample_count != other.sample_count {
150 return Err(RenderPassCompatibilityError::IncompatibleSampleCount {
151 expected: self.sample_count,
152 actual: other.sample_count,
153 res: res.error_ident(),
154 });
155 }
156 if self.multiview_mask != other.multiview_mask {
157 return Err(RenderPassCompatibilityError::IncompatibleMultiview {
158 expected: self.multiview_mask,
159 actual: other.multiview_mask,
160 res: res.error_ident(),
161 });
162 }
163 Ok(())
164 }
165}
166
167pub type BufferMapPendingClosure = (Option<BufferMapCallback>, BufferAccessResult);
168
169#[must_use]
170#[derive(Default)]
171pub struct UserClosures {
172 pub mappings: Vec<BufferMapPendingClosure>,
173 pub blas_compact_ready: Vec<BlasCompactReadyPendingClosure>,
174 pub submissions: SmallVec<[queue::SubmittedWorkDoneClosure; 1]>,
175 pub device_lost_invocations: SmallVec<[DeviceLostInvocation; 1]>,
176}
177
178impl UserClosures {
179 pub(crate) fn extend(&mut self, other: Self) {
180 self.mappings.extend(other.mappings);
181 self.blas_compact_ready.extend(other.blas_compact_ready);
182 self.submissions.extend(other.submissions);
183 self.device_lost_invocations
184 .extend(other.device_lost_invocations);
185 }
186
187 pub(crate) fn fire(self) {
188 for (mut callback, status) in self.mappings {
194 if let Some(callback) = callback.take() {
195 callback(status);
196 }
197 }
198 for (mut operation, status) in self.blas_compact_ready {
199 if let Some(callback) = operation.take() {
200 callback(status);
201 }
202 }
203 for closure in self.submissions {
204 closure();
205 }
206 for invocation in self.device_lost_invocations {
207 (invocation.closure)(invocation.reason, invocation.message);
208 }
209 }
210}
211
212#[cfg(send_sync)]
213pub type DeviceLostClosure = Box<dyn FnOnce(DeviceLostReason, String) + Send + 'static>;
214#[cfg(not(send_sync))]
215pub type DeviceLostClosure = Box<dyn FnOnce(DeviceLostReason, String) + 'static>;
216
217pub struct DeviceLostInvocation {
218 closure: DeviceLostClosure,
219 reason: DeviceLostReason,
220 message: String,
221}
222
223pub(crate) fn map_buffer(
224 buffer: &Buffer,
225 offset: BufferAddress,
226 size: BufferAddress,
227 kind: MapMode,
228 snatch_guard: &SnatchGuard,
229) -> Result<hal::BufferMapping, BufferAccessError> {
230 let raw_device = buffer.device.raw();
231 let raw_buffer = buffer.try_raw(snatch_guard)?;
232 let mapping = unsafe {
233 raw_device
234 .map_buffer(raw_buffer, offset..offset + size)
235 .map_err(|e| buffer.device.handle_hal_error(e))?
236 };
237
238 if !mapping.is_coherent && kind == MapMode::Read {
239 #[allow(clippy::single_range_in_vec_init)]
240 unsafe {
241 raw_device.invalidate_mapped_ranges(raw_buffer, &[offset..offset + size]);
242 }
243 }
244
245 assert_eq!(offset % wgt::COPY_BUFFER_ALIGNMENT, 0);
246 assert_eq!(size % wgt::COPY_BUFFER_ALIGNMENT, 0);
247 let mapped = unsafe { core::slice::from_raw_parts_mut(mapping.ptr.as_ptr(), size as usize) };
262
263 if !mapping.is_coherent
265 && kind == MapMode::Read
266 && !buffer.usage.contains(wgt::BufferUsages::MAP_WRITE)
267 {
268 for uninitialized in buffer
269 .initialization_status
270 .write()
271 .uninitialized(offset..(size + offset))
272 {
273 let fill_range =
276 (uninitialized.start - offset) as usize..(uninitialized.end - offset) as usize;
277 mapped[fill_range].fill(0);
278 }
279 } else {
280 for uninitialized in buffer
281 .initialization_status
282 .write()
283 .drain(offset..(size + offset))
284 {
285 let fill_range =
288 (uninitialized.start - offset) as usize..(uninitialized.end - offset) as usize;
289 mapped[fill_range].fill(0);
290
291 if !mapping.is_coherent
293 && kind == MapMode::Read
294 && buffer.usage.contains(wgt::BufferUsages::MAP_WRITE)
295 {
296 unsafe { raw_device.flush_mapped_ranges(raw_buffer, &[uninitialized]) };
297 }
298 }
299 }
300
301 Ok(mapping)
302}
303
304#[derive(Clone, Debug)]
305#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
306pub struct DeviceMismatch {
307 pub(super) res: ResourceErrorIdent,
308 pub(super) res_device: ResourceErrorIdent,
309 pub(super) target: Option<ResourceErrorIdent>,
310 pub(super) target_device: ResourceErrorIdent,
311}
312
313impl fmt::Display for DeviceMismatch {
314 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
315 write!(
316 f,
317 "{} of {} doesn't match {}",
318 self.res_device, self.res, self.target_device
319 )?;
320 if let Some(target) = self.target.as_ref() {
321 write!(f, " of {target}")?;
322 }
323 Ok(())
324 }
325}
326
327impl core::error::Error for DeviceMismatch {}
328
329impl WebGpuError for DeviceMismatch {
330 fn webgpu_error_type(&self) -> ErrorType {
331 ErrorType::Validation
332 }
333}
334
335#[derive(Clone, Debug, Error)]
336#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
337#[non_exhaustive]
338pub enum DeviceError {
339 #[error("Parent device is lost")]
340 Lost,
341 #[error("Not enough memory left.")]
342 OutOfMemory,
343 #[error(transparent)]
344 DeviceMismatch(#[from] Box<DeviceMismatch>),
345}
346
347impl WebGpuError for DeviceError {
348 fn webgpu_error_type(&self) -> ErrorType {
349 match self {
350 Self::DeviceMismatch(e) => e.webgpu_error_type(),
351 Self::Lost => ErrorType::DeviceLost,
352 Self::OutOfMemory => ErrorType::OutOfMemory,
353 }
354 }
355}
356
357impl DeviceError {
358 pub fn from_hal(error: hal::DeviceError) -> Self {
362 match error {
363 hal::DeviceError::Lost => Self::Lost,
364 hal::DeviceError::OutOfMemory => Self::OutOfMemory,
365 hal::DeviceError::Unexpected => Self::Lost,
366 }
367 }
368}
369
370#[derive(Clone, Debug, Error)]
371#[error("Features {0:?} are required but not enabled on the device")]
372pub struct MissingFeatures(pub wgt::Features);
373
374impl WebGpuError for MissingFeatures {
375 fn webgpu_error_type(&self) -> ErrorType {
376 ErrorType::Validation
377 }
378}
379
380#[derive(Clone, Debug, Error)]
381#[error(
382 "Downlevel flags {0:?} are required but not supported on the device.\n{DOWNLEVEL_ERROR_MESSAGE}",
383)]
384pub struct MissingDownlevelFlags(pub wgt::DownlevelFlags);
385
386impl WebGpuError for MissingDownlevelFlags {
387 fn webgpu_error_type(&self) -> ErrorType {
388 ErrorType::Validation
389 }
390}
391
392pub use wgpu_naga_bridge::create_validator;
393pub use wgpu_naga_bridge::features_to_naga_capabilities;