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