1use alloc::{
2 borrow::Cow,
3 boxed::Box,
4 string::{String, ToString as _},
5 sync::{Arc, Weak},
6 vec::Vec,
7};
8use core::{
9 fmt,
10 mem::{self, ManuallyDrop},
11 num::NonZeroU32,
12 sync::atomic::{AtomicBool, Ordering},
13};
14use hal::ShouldBeNonZeroExt;
15
16use arrayvec::ArrayVec;
17use bitflags::Flags;
18use smallvec::SmallVec;
19use wgt::{
20 math::align_to, DeviceLostReason, TextureFormat, TextureSampleType, TextureSelector,
21 TextureViewDimension,
22};
23
24#[cfg(feature = "trace")]
25use crate::device::trace;
26use crate::{
27 api_log,
28 binding_model::{
29 self, BindGroup, BindGroupLateBufferBindingInfo, BindGroupLayout,
30 BindGroupLayoutEntryError, BindGroupLayoutState, CreateBindGroupError,
31 CreateBindGroupLayoutError,
32 },
33 command, conv,
34 device::{
35 bgl, create_validator, features_to_naga_capabilities, life::WaitIdleError, map_buffer,
36 AttachmentData, DeviceLostInvocation, HostMap, MissingDownlevelFlags, MissingFeatures,
37 RenderPassContext,
38 },
39 hal_label,
40 init_tracker::{
41 BufferInitTracker, BufferInitTrackerAction, MemoryInitKind, TextureInitRange,
42 TextureInitTrackerAction,
43 },
44 instance::{Adapter, RequestDeviceError},
45 lock::{rank, Mutex, RwLock},
46 pipeline::{self, ColorStateError},
47 pool::ResourcePool,
48 present,
49 resource::{
50 self, Buffer, ExternalTexture, Fallible, Labeled, ParentDevice, QuerySet, QuerySetState,
51 RawResourceAccess, ResourceState, Sampler, StagingBuffer, Texture, TextureView,
52 TextureViewNotRenderableReason, TextureViewState, Tlas, TrackingData,
53 },
54 resource_log,
55 snatch::{SnatchGuard, SnatchLock, Snatchable},
56 timestamp_normalization::TIMESTAMP_NORMALIZATION_BUFFER_USES,
57 track::{BindGroupStates, DeviceTracker, TrackerIndexAllocators, UsageScope, UsageScopePool},
58 validation::{self, check_color_attachment_count, PassthroughInterface, ShaderMetaData},
59 weak_vec::WeakVec,
60 FastHashMap, LabelHelpers, OnceCellOrLock,
61};
62
63use super::{
64 queue::Queue, surface_config::validate_surface_configuration, DeviceDescriptor, DeviceError,
65 DeviceLostClosure, UserClosures, ENTRYPOINT_FAILURE_ERROR, ZERO_BUFFER_SIZE,
66};
67
68#[cfg(supports_64bit_atomics)]
69use core::sync::atomic::AtomicU64;
70#[cfg(not(supports_64bit_atomics))]
71use portable_atomic::AtomicU64;
72
73pub(crate) struct CommandIndices {
74 pub(crate) active_submission_index: hal::FenceValue,
82 pub(crate) next_acceleration_structure_build_command_index: u64,
83}
84
85#[repr(C)]
92#[derive(Copy, Clone, bytemuck::Zeroable, bytemuck::Pod)]
93pub struct ExternalTextureParams {
94 pub yuv_conversion_matrix: [f32; 16],
99
100 pub gamut_conversion_matrix: [f32; 12],
113
114 pub src_transfer_function: wgt::ExternalTextureTransferFunction,
118
119 pub dst_transfer_function: wgt::ExternalTextureTransferFunction,
122
123 pub sample_transform: [f32; 6],
136
137 pub load_transform: [f32; 6],
153
154 pub size: [u32; 2],
167
168 pub num_planes: u32,
172 pub _padding: [u8; 4],
174}
175
176impl ExternalTextureParams {
177 pub fn from_desc<L>(desc: &wgt::ExternalTextureDescriptor<L>) -> Self {
178 let gamut_conversion_matrix = [
179 desc.gamut_conversion_matrix[0],
180 desc.gamut_conversion_matrix[1],
181 desc.gamut_conversion_matrix[2],
182 0.0, desc.gamut_conversion_matrix[3],
184 desc.gamut_conversion_matrix[4],
185 desc.gamut_conversion_matrix[5],
186 0.0, desc.gamut_conversion_matrix[6],
188 desc.gamut_conversion_matrix[7],
189 desc.gamut_conversion_matrix[8],
190 0.0, ];
192
193 Self {
194 yuv_conversion_matrix: desc.yuv_conversion_matrix,
195 gamut_conversion_matrix,
196 src_transfer_function: desc.src_transfer_function,
197 dst_transfer_function: desc.dst_transfer_function,
198 size: [desc.width, desc.height],
199 sample_transform: desc.sample_transform,
200 load_transform: desc.load_transform,
201 num_planes: desc.num_planes() as u32,
202 _padding: Default::default(),
203 }
204 }
205}
206
207pub struct Device {
210 raw: Box<dyn hal::DynDevice>,
211 pub(crate) adapter: Arc<Adapter>,
212 pub(crate) queue: OnceCellOrLock<Weak<Queue>>,
213 pub(crate) zero_buffer: ManuallyDrop<Box<dyn hal::DynBuffer>>,
214 pub(crate) empty_bgl: ManuallyDrop<Box<dyn hal::DynBindGroupLayout>>,
215 label: String,
217
218 pub(crate) command_allocator: command::CommandAllocator,
219
220 pub(crate) command_indices: RwLock<CommandIndices>,
221
222 pub(crate) last_successful_submission_index: hal::AtomicFenceValue,
232
233 pub(crate) fence: ManuallyDrop<Box<dyn hal::DynFence>>,
234 pub(crate) snatchable_lock: SnatchLock,
235
236 pub(crate) valid: AtomicBool,
248
249 pub(crate) device_lost_closure: Mutex<Option<DeviceLostClosure>>,
253
254 pub(crate) trackers: Mutex<DeviceTracker>,
256 pub(crate) tracker_indices: TrackerIndexAllocators,
257 pub(crate) bgl_pool: ResourcePool<bgl::EntryMap, BindGroupLayout>,
259 pub(crate) alignments: hal::Alignments,
260 pub(crate) limits: wgt::Limits,
261 pub(crate) features: wgt::Features,
262 pub(crate) downlevel: wgt::DownlevelCapabilities,
263 pub(crate) ordered_buffer_usages: wgt::BufferUses,
268 pub(crate) ordered_texture_usages: wgt::TextureUses,
273 pub(crate) instance_flags: wgt::InstanceFlags,
274 pub(crate) deferred_destroy: Mutex<Vec<DeferredDestroy>>,
275 pub(crate) usage_scopes: UsageScopePool,
276 pub(crate) indirect_validation: Option<crate::indirect_validation::IndirectValidation>,
277 pub(crate) timestamp_normalizer:
279 OnceCellOrLock<crate::timestamp_normalization::TimestampNormalizer>,
280 pub(crate) default_external_texture_params_buffer: ManuallyDrop<Box<dyn hal::DynBuffer>>,
285 #[cfg(feature = "trace")]
287 pub(crate) trace: Mutex<Option<Box<dyn trace::Trace + Send + Sync + 'static>>>,
288}
289
290pub(crate) enum DeferredDestroy {
291 TextureViews(WeakVec<TextureView>),
292 BindGroups(WeakVec<BindGroup>),
293}
294
295impl fmt::Debug for Device {
296 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
297 f.debug_struct("Device")
298 .field("label", &self.label())
299 .field("limits", &self.limits)
300 .field("features", &self.features)
301 .field("downlevel", &self.downlevel)
302 .finish()
303 }
304}
305
306impl Drop for Device {
307 fn drop(&mut self) {
308 resource_log!("Drop {}", self.error_ident());
309
310 let zero_buffer = unsafe { ManuallyDrop::take(&mut self.zero_buffer) };
313 let empty_bgl = unsafe { ManuallyDrop::take(&mut self.empty_bgl) };
315 let default_external_texture_params_buffer =
318 unsafe { ManuallyDrop::take(&mut self.default_external_texture_params_buffer) };
319 let fence = unsafe { ManuallyDrop::take(&mut self.fence) };
321 if let Some(indirect_validation) = self.indirect_validation.take() {
322 indirect_validation.dispose(self.raw.as_ref());
323 }
324 if let Some(timestamp_normalizer) = self.timestamp_normalizer.take() {
325 timestamp_normalizer.dispose(self.raw.as_ref());
326 }
327 unsafe {
328 self.raw.destroy_buffer(zero_buffer);
329 self.raw.destroy_bind_group_layout(empty_bgl);
330 self.raw
331 .destroy_buffer(default_external_texture_params_buffer);
332 self.raw.destroy_fence(fence);
333 }
334 }
335}
336
337impl Device {
338 pub(crate) fn raw(&self) -> &dyn hal::DynDevice {
339 self.raw.as_ref()
340 }
341 pub(crate) fn require_features(&self, feature: wgt::Features) -> Result<(), MissingFeatures> {
342 if self.features.contains(feature) {
343 Ok(())
344 } else {
345 Err(MissingFeatures(feature))
346 }
347 }
348
349 pub(crate) fn require_downlevel_flags(
350 &self,
351 flags: wgt::DownlevelFlags,
352 ) -> Result<(), MissingDownlevelFlags> {
353 if self.downlevel.flags.contains(flags) {
354 Ok(())
355 } else {
356 Err(MissingDownlevelFlags(flags))
357 }
358 }
359
360 pub unsafe fn start_graphics_debugger_capture(&self) {
366 api_log!("Device::start_graphics_debugger_capture");
367
368 if !self.is_valid() {
369 return;
370 }
371 unsafe { self.raw().start_graphics_debugger_capture() };
372 }
373
374 pub unsafe fn stop_graphics_debugger_capture(&self) {
380 api_log!("Device::stop_graphics_debugger_capture");
381
382 if !self.is_valid() {
383 return;
384 }
385 unsafe { self.raw().stop_graphics_debugger_capture() };
386 }
387}
388
389impl Device {
390 pub(crate) fn new(
391 raw_device: Box<dyn hal::DynDevice>,
392 adapter: &Arc<Adapter>,
393 desc: &DeviceDescriptor,
394 instance_flags: wgt::InstanceFlags,
395 ) -> Result<Self, DeviceError> {
396 #[cfg(not(feature = "trace"))]
397 match &desc.trace {
398 wgt::Trace::Off => {}
399 _ => {
400 log::error!("wgpu-core feature 'trace' is not enabled");
401 }
402 };
403 #[cfg(feature = "trace")]
404 let trace: Option<Box<dyn trace::Trace + Send + Sync + 'static>> = match &desc.trace {
405 wgt::Trace::Off => None,
406 wgt::Trace::Directory(dir) => match trace::DiskTrace::new(dir.clone()) {
407 Ok(mut trace) => {
408 trace::Trace::add(
409 &mut trace,
410 trace::Action::Init {
411 desc: wgt::DeviceDescriptor {
412 trace: wgt::Trace::Off,
413 ..desc.clone()
414 },
415 backend: adapter.backend(),
416 },
417 );
418 Some(Box::new(trace))
419 }
420 Err(e) => {
421 log::error!("Unable to start a trace in '{dir:?}': {e}");
422 None
423 }
424 },
425 wgt::Trace::Memory => {
426 let mut trace = trace::MemoryTrace::new();
427 trace::Trace::add(
428 &mut trace,
429 trace::Action::Init {
430 desc: wgt::DeviceDescriptor {
431 trace: wgt::Trace::Off,
432 ..desc.clone()
433 },
434 backend: adapter.backend(),
435 },
436 );
437 Some(Box::new(trace))
438 }
439 t => {
442 log::error!("unimplemented wgpu_types::Trace variant {t:?}");
443 None
444 }
445 };
446
447 let ordered_buffer_usages = adapter.raw.adapter.get_ordered_buffer_usages();
448 let ordered_texture_usages = adapter.raw.adapter.get_ordered_texture_usages();
449
450 let fence = unsafe { raw_device.create_fence() }.map_err(DeviceError::from_hal)?;
451
452 let command_allocator = command::CommandAllocator::new();
453
454 let rt_uses = if desc
455 .required_features
456 .intersects(wgt::Features::EXPERIMENTAL_RAY_QUERY)
457 {
458 wgt::BufferUses::TOP_LEVEL_ACCELERATION_STRUCTURE_INPUT
459 } else {
460 wgt::BufferUses::empty()
461 };
462
463 let zero_buffer = unsafe {
465 raw_device.create_buffer(&hal::BufferDescriptor {
466 label: hal_label(Some("(wgpu internal) zero init buffer"), instance_flags),
467 size: ZERO_BUFFER_SIZE,
468 usage: wgt::BufferUses::COPY_SRC | wgt::BufferUses::COPY_DST | rt_uses,
469 memory_flags: hal::MemoryFlags::empty(),
470 })
471 }
472 .map_err(DeviceError::from_hal)?;
473
474 let empty_bgl = unsafe {
475 raw_device.create_bind_group_layout(&hal::BindGroupLayoutDescriptor {
476 label: None,
477 flags: hal::BindGroupLayoutFlags::empty(),
478 entries: &[],
479 })
480 }
481 .map_err(DeviceError::from_hal)?;
482
483 let default_external_texture_params_buffer = unsafe {
484 raw_device.create_buffer(&hal::BufferDescriptor {
485 label: hal_label(
486 Some("(wgpu internal) default external texture params buffer"),
487 instance_flags,
488 ),
489 size: size_of::<ExternalTextureParams>() as _,
490 usage: wgt::BufferUses::COPY_DST | wgt::BufferUses::UNIFORM,
491 memory_flags: hal::MemoryFlags::empty(),
492 })
493 }
494 .map_err(DeviceError::from_hal)?;
495
496 let alignments = adapter.raw.capabilities.alignments.clone();
498 let downlevel = adapter.raw.capabilities.downlevel.clone();
499 let limits = &adapter.raw.capabilities.limits;
500
501 let enable_indirect_validation = instance_flags
502 .contains(wgt::InstanceFlags::VALIDATION_INDIRECT_CALL)
503 && downlevel.flags.contains(
504 wgt::DownlevelFlags::INDIRECT_EXECUTION | wgt::DownlevelFlags::COMPUTE_SHADERS,
505 )
506 && limits.max_storage_buffers_per_shader_stage >= 2;
507
508 let indirect_validation = if enable_indirect_validation {
509 Some(crate::indirect_validation::IndirectValidation::new(
510 raw_device.as_ref(),
511 &desc.required_limits,
512 &desc.required_features,
513 instance_flags,
514 adapter.backend(),
515 )?)
516 } else {
517 None
518 };
519
520 Ok(Self {
521 raw: raw_device,
522 adapter: adapter.clone(),
523 queue: OnceCellOrLock::new(),
524 zero_buffer: ManuallyDrop::new(zero_buffer),
525 empty_bgl: ManuallyDrop::new(empty_bgl),
526 default_external_texture_params_buffer: ManuallyDrop::new(
527 default_external_texture_params_buffer,
528 ),
529 label: desc.label.to_string(),
530 command_allocator,
531 command_indices: RwLock::new(
532 rank::DEVICE_COMMAND_INDICES,
533 CommandIndices {
534 active_submission_index: 0,
535 next_acceleration_structure_build_command_index: 1,
537 },
538 ),
539 last_successful_submission_index: AtomicU64::new(0),
540 fence: ManuallyDrop::new(fence),
541 snatchable_lock: unsafe { SnatchLock::new(rank::DEVICE_SNATCHABLE_LOCK) },
542 valid: AtomicBool::new(true),
543 device_lost_closure: Mutex::new(rank::DEVICE_LOST_CLOSURE, None),
544 trackers: Mutex::new(
545 rank::DEVICE_TRACKERS,
546 DeviceTracker::new(ordered_buffer_usages, ordered_texture_usages),
547 ),
548 tracker_indices: TrackerIndexAllocators::new(),
549 bgl_pool: ResourcePool::new(),
550 #[cfg(feature = "trace")]
551 trace: Mutex::new(rank::DEVICE_TRACE, trace),
552 alignments,
553 limits: desc.required_limits.clone(),
554 features: desc.required_features,
555 downlevel,
556 ordered_buffer_usages,
557 ordered_texture_usages,
558 instance_flags,
559 deferred_destroy: Mutex::new(rank::DEVICE_DEFERRED_DESTROY, Vec::new()),
560 usage_scopes: Mutex::new(rank::DEVICE_USAGE_SCOPES, Default::default()),
561 timestamp_normalizer: OnceCellOrLock::new(),
562 indirect_validation,
563 })
564 }
565
566 fn init_default_external_texture_params_buffer(self: &Arc<Self>) -> Result<(), DeviceError> {
571 let data = ExternalTextureParams {
572 #[rustfmt::skip]
573 yuv_conversion_matrix: [
574 1.0, 0.0, 0.0, 0.0,
575 0.0, 1.0, 0.0, 0.0,
576 0.0, 0.0, 1.0, 0.0,
577 0.0, 0.0, 0.0, 1.0,
578 ],
579 #[rustfmt::skip]
580 gamut_conversion_matrix: [
581 1.0, 0.0, 0.0, 0.0,
582 0.0, 1.0, 0.0, 0.0,
583 0.0, 0.0, 1.0, 0.0,
584 ],
585 src_transfer_function: Default::default(),
586 dst_transfer_function: Default::default(),
587 size: [0, 0],
588 #[rustfmt::skip]
589 sample_transform: [
590 1.0, 0.0,
591 0.0, 1.0,
592 0.0, 0.0
593 ],
594 #[rustfmt::skip]
595 load_transform: [
596 1.0, 0.0,
597 0.0, 1.0,
598 0.0, 0.0
599 ],
600 num_planes: 1,
601 _padding: Default::default(),
602 };
603 let mut staging_buffer =
604 StagingBuffer::new(self, wgt::BufferSize::new(size_of_val(&data) as _).unwrap())?;
605 staging_buffer.write(bytemuck::bytes_of(&data));
606 let staging_buffer = staging_buffer.flush();
607
608 let params_buffer = self.default_external_texture_params_buffer.as_ref();
609 let queue = self.get_queue().unwrap();
610 let mut pending_writes = queue.pending_writes.lock();
611
612 unsafe {
613 pending_writes
614 .command_encoder
615 .transition_buffers(&[hal::BufferBarrier {
616 buffer: params_buffer,
617 usage: hal::StateTransition {
618 from: wgt::BufferUses::MAP_WRITE,
619 to: wgt::BufferUses::COPY_DST,
620 },
621 }]);
622 pending_writes.command_encoder.copy_buffer_to_buffer(
623 staging_buffer.raw(),
624 params_buffer,
625 &[hal::BufferCopy {
626 src_offset: 0,
627 dst_offset: 0,
628 size: staging_buffer.size,
629 }],
630 );
631 pending_writes.consume(staging_buffer);
632 pending_writes
633 .command_encoder
634 .transition_buffers(&[hal::BufferBarrier {
635 buffer: params_buffer,
636 usage: hal::StateTransition {
637 from: wgt::BufferUses::COPY_DST,
638 to: wgt::BufferUses::UNIFORM,
639 },
640 }]);
641 }
642
643 Ok(())
644 }
645
646 pub fn late_init_resources_with_queue(self: &Arc<Self>) -> Result<(), RequestDeviceError> {
647 let queue = self.get_queue().unwrap();
648
649 let timestamp_normalizer = crate::timestamp_normalization::TimestampNormalizer::new(
650 self,
651 queue.get_timestamp_period(),
652 )?;
653
654 self.timestamp_normalizer
655 .set(timestamp_normalizer)
656 .unwrap_or_else(|_| panic!("Called late_init_resources_with_queue twice"));
657
658 self.init_default_external_texture_params_buffer()?;
659
660 Ok(())
661 }
662
663 pub fn backend(&self) -> wgt::Backend {
665 self.adapter.backend()
666 }
667
668 pub fn is_valid(&self) -> bool {
669 self.valid.load(Ordering::Acquire)
670 }
671
672 pub fn check_is_valid(&self) -> Result<(), DeviceError> {
673 if self.is_valid() {
674 Ok(())
675 } else {
676 Err(DeviceError::Lost)
677 }
678 }
679
680 #[cfg(feature = "trace")]
684 pub fn take_trace(&self) -> Option<Box<dyn trace::Trace + Send + Sync + 'static>> {
685 self.trace.lock().take()
686 }
687
688 pub fn lose_if_oom(&self) {
695 let _ = self
696 .raw()
697 .check_if_oom()
698 .map_err(|e| self.handle_hal_error(e));
699 }
700
701 pub fn handle_hal_error(&self, error: hal::DeviceError) -> DeviceError {
702 match error {
703 hal::DeviceError::OutOfMemory
704 | hal::DeviceError::Lost
705 | hal::DeviceError::Unexpected => {
706 self.lose(&error.to_string());
707 }
708 }
709 DeviceError::from_hal(error)
710 }
711
712 pub fn handle_hal_error_with_nonfatal_oom(&self, error: hal::DeviceError) -> DeviceError {
713 match error {
714 hal::DeviceError::OutOfMemory => DeviceError::from_hal(error),
715 error => self.handle_hal_error(error),
716 }
717 }
718
719 pub(crate) fn deferred_resource_destruction(&self) {
727 let deferred_destroy = mem::take(&mut *self.deferred_destroy.lock());
729 for item in deferred_destroy {
730 match item {
731 DeferredDestroy::TextureViews(views) => {
732 for view in views {
733 let Some(view) = view.upgrade() else {
734 continue;
735 };
736 let Ok(view_state) = view.state() else {
737 continue;
738 };
739 let Some(raw_view) =
740 view_state.raw.snatch(&mut self.snatchable_lock.write())
741 else {
742 continue;
743 };
744
745 resource_log!("Destroy raw {}", view.error_ident());
746
747 unsafe {
748 self.raw().destroy_texture_view(raw_view);
749 }
750 }
751 }
752 DeferredDestroy::BindGroups(bind_groups) => {
753 for bind_group in bind_groups {
754 let Some(bind_group) = bind_group.upgrade() else {
755 continue;
756 };
757 let Some(raw_bind_group) =
758 bind_group.raw.snatch(&mut self.snatchable_lock.write())
759 else {
760 continue;
761 };
762
763 resource_log!("Destroy raw {}", bind_group.error_ident());
764
765 unsafe {
766 self.raw().destroy_bind_group(raw_bind_group);
767 }
768 }
769 }
770 }
771 }
772 }
773
774 pub fn get_queue(&self) -> Option<Arc<Queue>> {
775 self.queue.get().as_ref()?.upgrade()
776 }
777
778 pub fn set_queue(&self, queue: &Arc<Queue>) {
779 assert!(self.queue.set(Arc::downgrade(queue)).is_ok());
780 }
781
782 pub fn poll(
783 &self,
784 poll_type: wgt::PollType<crate::SubmissionIndex>,
785 ) -> Result<wgt::PollStatus, WaitIdleError> {
786 let (user_closures, result) = self.poll_and_return_closures(poll_type);
787 user_closures.fire();
788 result
789 }
790
791 pub(crate) fn poll_and_return_closures(
798 &self,
799 poll_type: wgt::PollType<crate::SubmissionIndex>,
800 ) -> (UserClosures, Result<wgt::PollStatus, WaitIdleError>) {
801 let snatch_guard = self.snatchable_lock.read();
802 let maintain_result = self.maintain(poll_type, snatch_guard);
803
804 self.lose_if_oom();
805
806 self.deferred_resource_destruction();
809
810 maintain_result
811 }
812
813 pub(crate) fn maintain<'this>(
831 &'this self,
832 poll_type: wgt::PollType<crate::SubmissionIndex>,
833 snatch_guard: SnatchGuard,
834 ) -> (UserClosures, Result<wgt::PollStatus, WaitIdleError>) {
835 profiling::scope!("Device::maintain");
836
837 let mut user_closures = UserClosures::default();
838
839 let wait_submission_index = match poll_type {
841 wgt::PollType::Wait {
842 submission_index: Some(submission_index),
843 ..
844 } => {
845 let last_successful_submission_index = self
846 .last_successful_submission_index
847 .load(Ordering::Acquire);
848
849 if submission_index > last_successful_submission_index {
850 let result = Err(WaitIdleError::WrongSubmissionIndex(
851 submission_index,
852 last_successful_submission_index,
853 ));
854
855 return (user_closures, result);
856 }
857
858 Some(submission_index)
859 }
860 wgt::PollType::Wait {
861 submission_index: None,
862 ..
863 } => Some(
864 self.last_successful_submission_index
865 .load(Ordering::Acquire),
866 ),
867 wgt::PollType::Poll => None,
868 };
869
870 if let Some(target_submission_index) = wait_submission_index {
872 log::trace!("Device::maintain: waiting for submission index {target_submission_index}");
873
874 let wait_timeout = match poll_type {
875 wgt::PollType::Wait { timeout, .. } => timeout,
876 wgt::PollType::Poll => unreachable!(
877 "`wait_submission_index` index for poll type `Poll` should be None"
878 ),
879 };
880
881 let wait_result = unsafe {
882 self.raw()
883 .wait(self.fence.as_ref(), target_submission_index, wait_timeout)
884 };
885
886 if let Err(e) = wait_result {
889 let hal_error: WaitIdleError = self.handle_hal_error(e).into();
890 return (user_closures, Err(hal_error));
891 }
892 }
893
894 let fence_value_result = unsafe { self.raw().get_fence_value(self.fence.as_ref()) };
897 let current_finished_submission = match fence_value_result {
898 Ok(fence_value) => fence_value,
899 Err(e) => {
900 let hal_error: WaitIdleError = self.handle_hal_error(e).into();
901 return (user_closures, Err(hal_error));
902 }
903 };
904
905 let command_indices = self.command_indices.read();
907 let device_valid = self.is_valid();
912 drop(command_indices);
913
914 let mut queue_empty = false;
920 if let Some(queue) = self.get_queue() {
921 let queue_result = queue.maintain(current_finished_submission, &snatch_guard);
922 (
923 user_closures.submissions,
924 user_closures.mappings,
925 user_closures.blas_compact_ready,
926 queue_empty,
927 ) = queue_result;
928 drop(snatch_guard);
942 } else {
943 drop(snatch_guard);
944 };
945
946 let result = if queue_empty {
949 if let Some(wait_submission_index) = wait_submission_index {
950 assert!(
953 current_finished_submission >= wait_submission_index,
954 concat!(
955 "If the queue is empty, the current submission index ",
956 "({}) should be at least the wait submission index ({})",
957 ),
958 current_finished_submission,
959 wait_submission_index,
960 );
961 }
962
963 Ok(wgt::PollStatus::QueueEmpty)
964 } else if let Some(wait_submission_index) = wait_submission_index {
965 if current_finished_submission >= wait_submission_index {
969 Ok(wgt::PollStatus::WaitSucceeded)
970 } else {
971 Err(WaitIdleError::Timeout)
972 }
973 } else {
974 Ok(wgt::PollStatus::Poll)
975 };
976
977 let mut should_release_gpu_resource = false;
984 if !device_valid && queue_empty {
985 should_release_gpu_resource = true;
988
989 if let Some(device_lost_closure) = self.device_lost_closure.lock().take() {
992 user_closures
993 .device_lost_invocations
994 .push(DeviceLostInvocation {
995 closure: device_lost_closure,
996 reason: DeviceLostReason::Destroyed,
997 message: String::new(),
998 });
999 }
1000 }
1001
1002 if should_release_gpu_resource {
1003 self.release_gpu_resources();
1004 }
1005
1006 (user_closures, result)
1007 }
1008
1009 pub fn create_buffer(
1010 self: &Arc<Self>,
1011 desc: &resource::BufferDescriptor,
1012 ) -> Result<Arc<Buffer>, resource::CreateBufferError> {
1013 self.check_is_valid()?;
1014
1015 if desc.size > self.limits.max_buffer_size {
1016 return Err(resource::CreateBufferError::MaxBufferSize {
1017 requested: desc.size,
1018 maximum: self.limits.max_buffer_size,
1019 });
1020 }
1021
1022 if desc
1023 .usage
1024 .intersects(wgt::BufferUsages::BLAS_INPUT | wgt::BufferUsages::TLAS_INPUT)
1025 {
1026 self.require_features(wgt::Features::EXPERIMENTAL_RAY_QUERY)?;
1027 }
1028
1029 if desc.usage.contains(wgt::BufferUsages::INDEX)
1030 && desc.usage.contains(
1031 wgt::BufferUsages::VERTEX
1032 | wgt::BufferUsages::UNIFORM
1033 | wgt::BufferUsages::INDIRECT
1034 | wgt::BufferUsages::STORAGE,
1035 )
1036 {
1037 self.require_downlevel_flags(wgt::DownlevelFlags::UNRESTRICTED_INDEX_BUFFER)?;
1038 }
1039
1040 if desc.usage.is_empty() || desc.usage.contains_unknown_bits() {
1041 return Err(resource::CreateBufferError::InvalidUsage(desc.usage));
1042 }
1043
1044 if !self
1045 .features
1046 .contains(wgt::Features::MAPPABLE_PRIMARY_BUFFERS)
1047 {
1048 use wgt::BufferUsages as Bu;
1049 let write_mismatch = desc.usage.contains(Bu::MAP_WRITE)
1050 && !(Bu::MAP_WRITE | Bu::COPY_SRC).contains(desc.usage);
1051 let read_mismatch = desc.usage.contains(Bu::MAP_READ)
1052 && !(Bu::MAP_READ | Bu::COPY_DST).contains(desc.usage);
1053 if write_mismatch || read_mismatch {
1054 return Err(resource::CreateBufferError::UsageMismatch(desc.usage));
1055 }
1056 }
1057
1058 let mut usage = conv::map_buffer_usage(desc.usage);
1059
1060 if desc.usage.contains(wgt::BufferUsages::INDIRECT) {
1061 self.require_downlevel_flags(wgt::DownlevelFlags::INDIRECT_EXECUTION)?;
1062 usage |= wgt::BufferUses::STORAGE_READ_ONLY | wgt::BufferUses::STORAGE_READ_WRITE;
1065 }
1066
1067 if desc.usage.contains(wgt::BufferUsages::QUERY_RESOLVE) {
1068 usage |= TIMESTAMP_NORMALIZATION_BUFFER_USES;
1069 }
1070
1071 if desc.mapped_at_creation {
1072 if !desc.size.is_multiple_of(wgt::COPY_BUFFER_ALIGNMENT) {
1073 return Err(resource::CreateBufferError::UnalignedSize);
1074 }
1075 if !desc.usage.contains(wgt::BufferUsages::MAP_WRITE) {
1076 usage |= wgt::BufferUses::COPY_DST;
1078 }
1079 } else {
1080 usage |= wgt::BufferUses::COPY_DST;
1083 }
1084
1085 let actual_size = if desc.size == 0 {
1086 wgt::COPY_BUFFER_ALIGNMENT
1087 } else if desc.usage.contains(wgt::BufferUsages::VERTEX) {
1088 desc.size + 1
1091 } else {
1092 desc.size
1093 };
1094 let clear_remainder = actual_size % wgt::COPY_BUFFER_ALIGNMENT;
1095 let aligned_size = if clear_remainder != 0 {
1096 actual_size + wgt::COPY_BUFFER_ALIGNMENT - clear_remainder
1097 } else {
1098 actual_size
1099 };
1100
1101 let hal_desc = hal::BufferDescriptor {
1102 label: desc.label.to_hal(self.instance_flags),
1103 size: aligned_size,
1104 usage,
1105 memory_flags: hal::MemoryFlags::empty(),
1106 };
1107 let buffer = unsafe { self.raw().create_buffer(&hal_desc) }
1108 .map_err(|e| self.handle_hal_error_with_nonfatal_oom(e))?;
1109
1110 let timestamp_normalization_bind_group = Snatchable::new(unsafe {
1111 self.timestamp_normalizer
1113 .get()
1114 .unwrap()
1115 .create_normalization_bind_group(
1116 self,
1117 &*buffer,
1118 desc.label.as_deref(),
1119 wgt::BufferSize::new(hal_desc.size).unwrap(),
1120 desc.usage,
1121 )
1122 }?);
1123
1124 let indirect_validation_bind_groups =
1125 self.create_indirect_validation_bind_groups(buffer.as_ref(), desc.size, desc.usage)?;
1126
1127 let buffer = Buffer {
1128 raw: Snatchable::new(buffer),
1129 device: self.clone(),
1130 usage: desc.usage,
1131 size: desc.size,
1132 initialization_status: RwLock::new(
1133 rank::BUFFER_INITIALIZATION_STATUS,
1134 BufferInitTracker::new(aligned_size),
1135 ),
1136 map_state: Mutex::new(rank::BUFFER_MAP_STATE, resource::BufferMapState::Idle),
1137 label: desc.label.to_string(),
1138 tracking_data: TrackingData::new(self.tracker_indices.buffers.clone()),
1139 bind_groups: Mutex::new(rank::BUFFER_BIND_GROUPS, WeakVec::new()),
1140 timestamp_normalization_bind_group,
1141 indirect_validation_bind_groups,
1142 };
1143
1144 let buffer = Arc::new(buffer);
1145
1146 let buffer_use = if !desc.mapped_at_creation {
1147 wgt::BufferUses::empty()
1148 } else if desc.usage.contains(wgt::BufferUsages::MAP_WRITE) {
1149 let map_size = buffer.size;
1151 let mapping = if map_size == 0 {
1152 hal::BufferMapping {
1153 ptr: core::ptr::NonNull::dangling(),
1154 is_coherent: true,
1155 }
1156 } else {
1157 let snatch_guard: SnatchGuard = self.snatchable_lock.read();
1158 map_buffer(&buffer, 0, map_size, HostMap::Write, &snatch_guard)?
1159 };
1160 *buffer.map_state.lock() = resource::BufferMapState::Active {
1161 mapping,
1162 range: 0..map_size,
1163 host: HostMap::Write,
1164 };
1165 wgt::BufferUses::MAP_WRITE
1166 } else {
1167 let mut staging_buffer =
1168 StagingBuffer::new(self, wgt::BufferSize::new(aligned_size).unwrap())?;
1169
1170 staging_buffer.write_zeros();
1173 buffer.initialization_status.write().drain(0..aligned_size);
1174
1175 *buffer.map_state.lock() = resource::BufferMapState::Init { staging_buffer };
1176 wgt::BufferUses::COPY_DST
1177 };
1178
1179 self.trackers
1180 .lock()
1181 .buffers
1182 .insert_single(&buffer, buffer_use);
1183
1184 Ok(buffer)
1185 }
1186
1187 #[cfg(feature = "replay")]
1188 pub fn set_buffer_data(
1189 self: &Arc<Self>,
1190 buffer: &Arc<Buffer>,
1191 offset: wgt::BufferAddress,
1192 data: &[u8],
1193 ) -> resource::BufferAccessResult {
1194 use crate::resource::RawResourceAccess;
1195
1196 let device = &buffer.device;
1197
1198 device.check_is_valid()?;
1199 buffer.check_usage(wgt::BufferUsages::MAP_WRITE)?;
1200
1201 let last_submission = device
1202 .get_queue()
1203 .and_then(|queue| queue.lock_life().get_buffer_latest_submission_index(buffer));
1204
1205 if let Some(last_submission) = last_submission {
1206 device.wait_for_submit(last_submission)?;
1207 }
1208
1209 let snatch_guard = device.snatchable_lock.read();
1210 let raw_buf = buffer.try_raw(&snatch_guard)?;
1211
1212 if offset > buffer.size {
1213 return Err(resource::BufferAccessError::OutOfBoundsStartOffsetOverrun {
1214 index: offset,
1215 max: buffer.size,
1216 });
1217 } else if buffer.size - offset < u64::try_from(data.len()).unwrap() {
1218 return Err(resource::BufferAccessError::OutOfBoundsEndOffsetOverrun {
1219 index: offset,
1220 size: u64::try_from(data.len()).unwrap(),
1221 max: buffer.size,
1222 });
1223 }
1224
1225 let mapping = unsafe {
1226 device
1227 .raw()
1228 .map_buffer(raw_buf, offset..offset + u64::try_from(data.len()).unwrap())
1229 }
1230 .map_err(|e| device.handle_hal_error(e))?;
1231
1232 unsafe { core::ptr::copy_nonoverlapping(data.as_ptr(), mapping.ptr.as_ptr(), data.len()) };
1233
1234 if !mapping.is_coherent {
1235 #[allow(clippy::single_range_in_vec_init)]
1236 unsafe {
1237 device
1238 .raw()
1239 .flush_mapped_ranges(raw_buf, &[offset..offset + data.len() as u64])
1240 };
1241 }
1242
1243 unsafe { device.raw().unmap_buffer(raw_buf) };
1244
1245 Ok(())
1246 }
1247
1248 pub(crate) fn create_texture_from_hal(
1249 self: &Arc<Self>,
1250 hal_texture: Box<dyn hal::DynTexture>,
1251 desc: &resource::TextureDescriptor,
1252 initial_state: wgt::TextureUses,
1253 ) -> Result<Arc<Texture>, resource::CreateTextureError> {
1254 let format_features = self
1255 .describe_format_features(desc.format)
1256 .map_err(|error| resource::CreateTextureError::MissingFeatures(desc.format, error))?;
1257
1258 unsafe { self.raw().add_raw_texture(&*hal_texture) };
1259
1260 let texture = Texture::new(
1261 self,
1262 resource::TextureInner::Native { raw: hal_texture },
1263 conv::map_texture_usage(desc.usage, desc.format.into(), format_features.flags),
1264 desc,
1265 format_features,
1266 resource::TextureClearMode::None,
1267 false,
1268 );
1269
1270 let texture = Arc::new(texture);
1271
1272 self.trackers
1273 .lock()
1274 .textures
1275 .insert_single(&texture, initial_state);
1276
1277 Ok(texture)
1278 }
1279
1280 pub(crate) unsafe fn create_buffer_from_hal(
1287 self: &Arc<Self>,
1288 hal_buffer: Box<dyn hal::DynBuffer>,
1289 desc: &resource::BufferDescriptor,
1290 ) -> (Fallible<Buffer>, Option<resource::CreateBufferError>) {
1291 let timestamp_normalization_bind_group = unsafe {
1292 match self
1293 .timestamp_normalizer
1294 .get()
1295 .unwrap()
1296 .create_normalization_bind_group(
1297 self,
1298 &*hal_buffer,
1299 desc.label.as_deref(),
1300 wgt::BufferSize::new(desc.size).unwrap(),
1301 desc.usage,
1302 ) {
1303 Ok(bg) => Snatchable::new(bg),
1304 Err(e) => {
1305 return (
1306 Fallible::Invalid(Arc::new(desc.label.to_string())),
1307 Some(e.into()),
1308 )
1309 }
1310 }
1311 };
1312
1313 let indirect_validation_bind_groups = match self.create_indirect_validation_bind_groups(
1314 hal_buffer.as_ref(),
1315 desc.size,
1316 desc.usage,
1317 ) {
1318 Ok(ok) => ok,
1319 Err(e) => return (Fallible::Invalid(Arc::new(desc.label.to_string())), Some(e)),
1320 };
1321
1322 unsafe { self.raw().add_raw_buffer(&*hal_buffer) };
1323
1324 let buffer = Buffer {
1325 raw: Snatchable::new(hal_buffer),
1326 device: self.clone(),
1327 usage: desc.usage,
1328 size: desc.size,
1329 initialization_status: RwLock::new(
1330 rank::BUFFER_INITIALIZATION_STATUS,
1331 BufferInitTracker::new(0),
1332 ),
1333 map_state: Mutex::new(rank::BUFFER_MAP_STATE, resource::BufferMapState::Idle),
1334 label: desc.label.to_string(),
1335 tracking_data: TrackingData::new(self.tracker_indices.buffers.clone()),
1336 bind_groups: Mutex::new(rank::BUFFER_BIND_GROUPS, WeakVec::new()),
1337 timestamp_normalization_bind_group,
1338 indirect_validation_bind_groups,
1339 };
1340
1341 let buffer = Arc::new(buffer);
1342
1343 self.trackers
1344 .lock()
1345 .buffers
1346 .insert_single(&buffer, wgt::BufferUses::empty());
1347
1348 (Fallible::Valid(buffer), None)
1349 }
1350
1351 fn create_indirect_validation_bind_groups(
1352 &self,
1353 raw_buffer: &dyn hal::DynBuffer,
1354 buffer_size: u64,
1355 usage: wgt::BufferUsages,
1356 ) -> Result<Snatchable<crate::indirect_validation::BindGroups>, resource::CreateBufferError>
1357 {
1358 if !usage.contains(wgt::BufferUsages::INDIRECT) {
1359 return Ok(Snatchable::empty());
1360 }
1361
1362 let Some(ref indirect_validation) = self.indirect_validation else {
1363 return Ok(Snatchable::empty());
1364 };
1365
1366 let bind_groups = crate::indirect_validation::BindGroups::new(
1367 indirect_validation,
1368 self,
1369 buffer_size,
1370 raw_buffer,
1371 )
1372 .map_err(resource::CreateBufferError::IndirectValidationBindGroup)?;
1373
1374 if let Some(bind_groups) = bind_groups {
1375 Ok(Snatchable::new(bind_groups))
1376 } else {
1377 Ok(Snatchable::empty())
1378 }
1379 }
1380
1381 fn create_texture_inner(
1382 self: &Arc<Self>,
1383 desc: &resource::TextureDescriptor,
1384 ) -> Result<Arc<Texture>, resource::CreateTextureError> {
1385 use resource::{CreateTextureError, TextureDimensionError};
1386
1387 self.check_is_valid()?;
1388
1389 if desc.usage.is_empty() || desc.usage.contains_unknown_bits() {
1390 return Err(CreateTextureError::InvalidUsage(desc.usage));
1391 }
1392
1393 conv::check_texture_dimension_size(
1394 desc.dimension,
1395 desc.size,
1396 desc.sample_count,
1397 &self.limits,
1398 )?;
1399
1400 if desc.dimension != wgt::TextureDimension::D2 {
1401 if desc.format.is_depth_stencil_format() {
1403 return Err(CreateTextureError::InvalidDepthDimension(
1404 desc.dimension,
1405 desc.format,
1406 ));
1407 }
1408 if desc
1410 .usage
1411 .contains(wgt::TextureUsages::TRANSIENT_ATTACHMENT)
1412 {
1413 return Err(CreateTextureError::InvalidDimensionUsages(
1414 wgt::TextureUsages::TRANSIENT_ATTACHMENT,
1415 desc.dimension,
1416 ));
1417 }
1418 }
1419
1420 if desc.dimension != wgt::TextureDimension::D2
1421 && desc.dimension != wgt::TextureDimension::D3
1422 {
1423 if desc.format.is_compressed() {
1425 return Err(CreateTextureError::InvalidCompressedDimension(
1426 desc.dimension,
1427 desc.format,
1428 ));
1429 }
1430
1431 if desc.usage.contains(wgt::TextureUsages::RENDER_ATTACHMENT) {
1433 return Err(CreateTextureError::InvalidDimensionUsages(
1434 wgt::TextureUsages::RENDER_ATTACHMENT,
1435 desc.dimension,
1436 ));
1437 }
1438 }
1439
1440 if desc.format.is_compressed() {
1441 let (block_width, block_height) = desc.format.block_dimensions();
1442
1443 if !desc.size.width.is_multiple_of(block_width) {
1444 return Err(CreateTextureError::InvalidDimension(
1445 TextureDimensionError::NotMultipleOfBlockWidth {
1446 width: desc.size.width,
1447 block_width,
1448 format: desc.format,
1449 },
1450 ));
1451 }
1452
1453 if !desc.size.height.is_multiple_of(block_height) {
1454 return Err(CreateTextureError::InvalidDimension(
1455 TextureDimensionError::NotMultipleOfBlockHeight {
1456 height: desc.size.height,
1457 block_height,
1458 format: desc.format,
1459 },
1460 ));
1461 }
1462
1463 if desc.dimension == wgt::TextureDimension::D3 {
1464 if desc.format.is_bcn() {
1466 self.require_features(wgt::Features::TEXTURE_COMPRESSION_BC_SLICED_3D)
1467 .map_err(|error| CreateTextureError::MissingFeatures(desc.format, error))?;
1468 } else if desc.format.is_astc() {
1469 self.require_features(wgt::Features::TEXTURE_COMPRESSION_ASTC_SLICED_3D)
1470 .map_err(|error| CreateTextureError::MissingFeatures(desc.format, error))?;
1471 } else {
1472 return Err(CreateTextureError::InvalidCompressedDimension(
1473 desc.dimension,
1474 desc.format,
1475 ));
1476 }
1477 }
1478 }
1479
1480 let mips = desc.mip_level_count;
1481 let max_levels_allowed = desc.size.max_mips(desc.dimension).min(hal::MAX_MIP_LEVELS);
1482 if mips == 0 || mips > max_levels_allowed {
1483 return Err(CreateTextureError::InvalidMipLevelCount {
1484 requested: mips,
1485 maximum: max_levels_allowed,
1486 });
1487 }
1488
1489 {
1490 let (mut width_multiple, mut height_multiple) = desc.format.size_multiple_requirement();
1491
1492 if desc.format.is_multi_planar_format() {
1493 width_multiple <<= desc.mip_level_count.saturating_sub(1);
1497 height_multiple <<= desc.mip_level_count.saturating_sub(1);
1498 }
1499
1500 if !desc.size.width.is_multiple_of(width_multiple) {
1501 return Err(CreateTextureError::InvalidDimension(
1502 TextureDimensionError::WidthNotMultipleOf {
1503 width: desc.size.width,
1504 multiple: width_multiple,
1505 format: desc.format,
1506 },
1507 ));
1508 }
1509
1510 if !desc.size.height.is_multiple_of(height_multiple) {
1511 return Err(CreateTextureError::InvalidDimension(
1512 TextureDimensionError::HeightNotMultipleOf {
1513 height: desc.size.height,
1514 multiple: height_multiple,
1515 format: desc.format,
1516 },
1517 ));
1518 }
1519 }
1520
1521 if desc
1522 .usage
1523 .contains(wgt::TextureUsages::TRANSIENT_ATTACHMENT)
1524 {
1525 if desc.usage
1526 != (wgt::TextureUsages::TRANSIENT_ATTACHMENT
1527 | wgt::TextureUsages::RENDER_ATTACHMENT)
1528 {
1529 return Err(CreateTextureError::InvalidTransientTextureUsage(desc.usage));
1530 }
1531
1532 if desc.mip_level_count != 1 {
1533 return Err(CreateTextureError::InvalidTransientTextureMipLevelCount(
1534 desc.mip_level_count,
1535 ));
1536 }
1537
1538 if desc.size.depth_or_array_layers != 1 {
1539 return Err(CreateTextureError::InvalidTransientTextureLayerCount(
1540 desc.size.depth_or_array_layers,
1541 ));
1542 }
1543
1544 if !desc.view_formats.is_empty() {
1545 return Err(CreateTextureError::InvalidTransientTextureViewFormats);
1546 }
1547 }
1548
1549 let format_features = self
1550 .describe_format_features(desc.format)
1551 .map_err(|error| CreateTextureError::MissingFeatures(desc.format, error))?;
1552
1553 if desc.sample_count > 1 {
1554 if desc.mip_level_count != 1 {
1560 return Err(CreateTextureError::InvalidMipLevelCount {
1561 requested: desc.mip_level_count,
1562 maximum: 1,
1563 });
1564 }
1565
1566 if desc.size.depth_or_array_layers != 1
1567 && !self.features.contains(wgt::Features::MULTISAMPLE_ARRAY)
1568 {
1569 return Err(CreateTextureError::InvalidDimension(
1570 TextureDimensionError::MultisampledDepthOrArrayLayer(
1571 desc.size.depth_or_array_layers,
1572 ),
1573 ));
1574 }
1575
1576 if desc.usage.contains(wgt::TextureUsages::STORAGE_BINDING) {
1577 return Err(CreateTextureError::InvalidMultisampledStorageBinding);
1578 }
1579
1580 if !desc.usage.contains(wgt::TextureUsages::RENDER_ATTACHMENT) {
1581 return Err(CreateTextureError::MultisampledNotRenderAttachment);
1582 }
1583
1584 if !format_features.flags.intersects(
1585 wgt::TextureFormatFeatureFlags::MULTISAMPLE_X4
1586 | wgt::TextureFormatFeatureFlags::MULTISAMPLE_X2
1587 | wgt::TextureFormatFeatureFlags::MULTISAMPLE_X8
1588 | wgt::TextureFormatFeatureFlags::MULTISAMPLE_X16,
1589 ) {
1590 return Err(CreateTextureError::InvalidMultisampledFormat(desc.format));
1591 }
1592
1593 if !format_features
1594 .flags
1595 .sample_count_supported(desc.sample_count)
1596 {
1597 return Err(CreateTextureError::InvalidSampleCount(
1598 desc.sample_count,
1599 desc.format,
1600 desc.format
1601 .guaranteed_format_features(self.features)
1602 .flags
1603 .supported_sample_counts(),
1604 self.adapter
1605 .get_texture_format_features(desc.format)
1606 .flags
1607 .supported_sample_counts(),
1608 ));
1609 };
1610 }
1611
1612 let missing_allowed_usages = match desc.format.planes() {
1613 Some(planes) => {
1614 let mut planes_usages = wgt::TextureUsages::all();
1615 for plane in 0..planes {
1616 let aspect = wgt::TextureAspect::from_plane(plane).unwrap();
1617 let format = desc.format.aspect_specific_format(aspect).unwrap();
1618 let format_features = self
1619 .describe_format_features(format)
1620 .map_err(|error| CreateTextureError::MissingFeatures(desc.format, error))?;
1621
1622 planes_usages &= format_features.allowed_usages;
1623 }
1624
1625 desc.usage - planes_usages
1626 }
1627 None => desc.usage - format_features.allowed_usages,
1628 };
1629
1630 if !missing_allowed_usages.is_empty() {
1631 let wgpu_allowed_usages = desc
1633 .format
1634 .guaranteed_format_features(self.features)
1635 .allowed_usages;
1636 let wgpu_missing_usages = desc.usage - wgpu_allowed_usages;
1637 return Err(CreateTextureError::InvalidFormatUsages(
1638 missing_allowed_usages,
1639 desc.format,
1640 wgpu_missing_usages.is_empty(),
1641 ));
1642 }
1643
1644 let mut hal_view_formats = Vec::new();
1645 for format in desc.view_formats.iter() {
1646 if desc.format == *format {
1647 continue;
1648 }
1649 if desc.format.remove_srgb_suffix() != format.remove_srgb_suffix() {
1650 return Err(CreateTextureError::InvalidViewFormat(*format, desc.format));
1651 }
1652 hal_view_formats.push(*format);
1653 }
1654 if !hal_view_formats.is_empty() {
1655 self.require_downlevel_flags(wgt::DownlevelFlags::VIEW_FORMATS)?;
1656 }
1657
1658 let hal_usage = conv::map_texture_usage_for_texture(desc, &format_features);
1659
1660 let hal_desc = hal::TextureDescriptor {
1661 label: desc.label.to_hal(self.instance_flags),
1662 size: desc.size,
1663 mip_level_count: desc.mip_level_count,
1664 sample_count: desc.sample_count,
1665 dimension: desc.dimension,
1666 format: desc.format,
1667 usage: hal_usage,
1668 memory_flags: hal::MemoryFlags::empty(),
1669 view_formats: hal_view_formats,
1670 };
1671
1672 let raw_texture = unsafe { self.raw().create_texture(&hal_desc) }
1673 .map_err(|e| self.handle_hal_error_with_nonfatal_oom(e))?;
1674
1675 let clear_mode = if hal_usage
1676 .intersects(wgt::TextureUses::DEPTH_STENCIL_WRITE | wgt::TextureUses::COLOR_TARGET)
1677 && desc.dimension == wgt::TextureDimension::D2
1678 {
1679 let (is_color, usage) = if desc.format.is_depth_stencil_format() {
1680 (false, wgt::TextureUses::DEPTH_STENCIL_WRITE)
1681 } else {
1682 (true, wgt::TextureUses::COLOR_TARGET)
1683 };
1684
1685 let clear_label = hal_label(
1686 Some("(wgpu internal) clear texture view"),
1687 self.instance_flags,
1688 );
1689
1690 let mut clear_views = SmallVec::new();
1691 for mip_level in 0..desc.mip_level_count {
1692 for array_layer in 0..desc.size.depth_or_array_layers {
1693 macro_rules! push_clear_view {
1694 ($format:expr, $aspect:expr) => {
1695 let desc = hal::TextureViewDescriptor {
1696 label: clear_label,
1697 format: $format,
1698 dimension: TextureViewDimension::D2,
1699 usage,
1700 range: wgt::ImageSubresourceRange {
1701 aspect: $aspect,
1702 base_mip_level: mip_level,
1703 mip_level_count: Some(1),
1704 base_array_layer: array_layer,
1705 array_layer_count: Some(1),
1706 },
1707 };
1708 clear_views.push(ManuallyDrop::new(
1709 unsafe {
1710 self.raw().create_texture_view(raw_texture.as_ref(), &desc)
1711 }
1712 .map_err(|e| self.handle_hal_error(e))?,
1713 ));
1714 };
1715 }
1716
1717 if let Some(planes) = desc.format.planes() {
1718 for plane in 0..planes {
1719 let aspect = wgt::TextureAspect::from_plane(plane).unwrap();
1720 let format = desc.format.aspect_specific_format(aspect).unwrap();
1721 push_clear_view!(format, aspect);
1722 }
1723 } else {
1724 push_clear_view!(desc.format, wgt::TextureAspect::All);
1725 }
1726 }
1727 }
1728 resource::TextureClearMode::RenderPass {
1729 clear_views,
1730 is_color,
1731 }
1732 } else {
1733 resource::TextureClearMode::BufferCopy
1734 };
1735
1736 let texture = Texture::new(
1737 self,
1738 resource::TextureInner::Native { raw: raw_texture },
1739 hal_usage,
1740 desc,
1741 format_features,
1742 clear_mode,
1743 true,
1744 );
1745
1746 let texture = Arc::new(texture);
1747
1748 self.trackers
1749 .lock()
1750 .textures
1751 .insert_single(&texture, wgt::TextureUses::UNINITIALIZED);
1752
1753 Ok(texture)
1754 }
1755
1756 pub fn create_texture(
1757 self: &Arc<Self>,
1758 desc: &resource::TextureDescriptor,
1759 ) -> (Arc<Texture>, Option<resource::CreateTextureError>) {
1760 let (texture, error) = match self.create_texture_inner(desc) {
1761 Ok(texture) => (texture, None),
1762 Err(e) => {
1763 let texture = Texture::invalid(self, desc);
1764 (Arc::new(texture), Some(e))
1765 }
1766 };
1767 api_log!(
1768 "Device::create_texture({desc:?}) -> {:?}",
1769 Arc::as_ptr(&texture)
1770 );
1771
1772 #[cfg(feature = "trace")]
1773 if let Some(ref mut trace) = *self.trace.lock() {
1774 use crate::device::trace::IntoTrace as _;
1775
1776 trace.add(trace::Action::CreateTexture(
1777 texture.to_trace(),
1778 desc.clone(),
1779 ));
1780 }
1781 (texture, error)
1782 }
1783
1784 pub fn create_texture_error(
1786 self: &Arc<Self>,
1787 desc: &resource::TextureDescriptor,
1788 ) -> Arc<Texture> {
1789 let texture = Arc::new(Texture::invalid(self, desc));
1790 #[cfg(feature = "trace")]
1791 if let Some(ref mut trace) = *self.trace.lock() {
1792 use crate::device::trace::IntoTrace as _;
1793
1794 trace.add(trace::Action::CreateTextureError(
1795 texture.to_trace(),
1796 desc.clone(),
1797 ));
1798 }
1799 texture
1800 }
1801
1802 fn create_texture_view_inner(
1803 self: &Arc<Self>,
1804 texture: &Arc<Texture>,
1805 desc: &resource::TextureViewDescriptor,
1806 ) -> Result<Arc<TextureView>, resource::CreateTextureViewError> {
1807 self.check_is_valid()?;
1808
1809 let snatch_guard = texture.device.snatchable_lock.read();
1810
1811 let texture_raw = texture.try_inner(&snatch_guard)?.raw();
1812
1813 let resolved_format = desc.format.unwrap_or_else(|| {
1816 texture
1817 .desc
1818 .format
1819 .aspect_specific_format(desc.range.aspect)
1820 .unwrap_or(texture.desc.format)
1821 });
1822
1823 let resolved_dimension = desc
1824 .dimension
1825 .unwrap_or_else(|| match texture.desc.dimension {
1826 wgt::TextureDimension::D1 => TextureViewDimension::D1,
1827 wgt::TextureDimension::D2 => {
1828 if texture.desc.array_layer_count() == 1 {
1829 TextureViewDimension::D2
1830 } else {
1831 TextureViewDimension::D2Array
1832 }
1833 }
1834 wgt::TextureDimension::D3 => TextureViewDimension::D3,
1835 });
1836
1837 let resolved_mip_level_count = desc.range.mip_level_count.unwrap_or_else(|| {
1838 texture
1839 .desc
1840 .mip_level_count
1841 .saturating_sub(desc.range.base_mip_level)
1842 });
1843
1844 let resolved_array_layer_count =
1845 desc.range
1846 .array_layer_count
1847 .unwrap_or_else(|| match resolved_dimension {
1848 TextureViewDimension::D1
1849 | TextureViewDimension::D2
1850 | TextureViewDimension::D3 => 1,
1851 TextureViewDimension::Cube => 6,
1852 TextureViewDimension::D2Array | TextureViewDimension::CubeArray => texture
1853 .desc
1854 .array_layer_count()
1855 .saturating_sub(desc.range.base_array_layer),
1856 });
1857
1858 let resolved_usage = {
1859 let usage = desc.usage.unwrap_or(wgt::TextureUsages::empty());
1860 if usage.is_empty() {
1861 texture.desc.usage
1862 } else if texture.desc.usage.contains(usage) {
1863 if texture
1865 .desc
1866 .usage
1867 .contains(wgt::TextureUsages::TRANSIENT_ATTACHMENT)
1868 && texture.desc.usage != usage
1869 {
1870 return Err(
1871 resource::CreateTextureViewError::InvalidTransientTextureViewUsage {
1872 texture: texture.desc.usage,
1873 view: usage,
1874 },
1875 );
1876 }
1877
1878 usage
1879 } else {
1880 return Err(resource::CreateTextureViewError::InvalidTextureViewUsage {
1881 view: usage,
1882 texture: texture.desc.usage,
1883 });
1884 }
1885 };
1886
1887 let format_features = self.describe_format_features(resolved_format)?;
1888 let allowed_format_usages = format_features.allowed_usages;
1889 if resolved_usage.contains(wgt::TextureUsages::RENDER_ATTACHMENT)
1890 && !allowed_format_usages.contains(wgt::TextureUsages::RENDER_ATTACHMENT)
1891 {
1892 return Err(
1893 resource::CreateTextureViewError::TextureViewFormatNotRenderable(resolved_format),
1894 );
1895 }
1896
1897 if resolved_usage.contains(wgt::TextureUsages::STORAGE_BINDING)
1898 && !allowed_format_usages.contains(wgt::TextureUsages::STORAGE_BINDING)
1899 {
1900 return Err(
1901 resource::CreateTextureViewError::TextureViewFormatNotStorage(resolved_format),
1902 );
1903 }
1904
1905 let aspects = hal::FormatAspects::new(texture.desc.format, desc.range.aspect);
1908 if aspects.is_empty() {
1909 return Err(resource::CreateTextureViewError::InvalidAspect {
1910 texture_format: texture.desc.format,
1911 requested_aspect: desc.range.aspect,
1912 });
1913 }
1914
1915 let format_is_good = if desc.range.aspect == wgt::TextureAspect::All {
1916 resolved_format == texture.desc.format
1917 || texture.desc.view_formats.contains(&resolved_format)
1918 } else {
1919 Some(resolved_format)
1920 == texture
1921 .desc
1922 .format
1923 .aspect_specific_format(desc.range.aspect)
1924 };
1925 if !format_is_good {
1926 return Err(resource::CreateTextureViewError::FormatReinterpretation {
1927 texture: texture.desc.format,
1928 view: resolved_format,
1929 });
1930 }
1931
1932 if texture.desc.sample_count > 1 && resolved_dimension != TextureViewDimension::D2 {
1934 let multisample_array_exception = resolved_dimension == TextureViewDimension::D2Array
1936 && self.features.contains(wgt::Features::MULTISAMPLE_ARRAY);
1937
1938 if !multisample_array_exception {
1939 return Err(
1940 resource::CreateTextureViewError::InvalidMultisampledTextureViewDimension(
1941 resolved_dimension,
1942 ),
1943 );
1944 }
1945 }
1946
1947 if texture.desc.dimension != resolved_dimension.compatible_texture_dimension() {
1949 return Err(
1950 resource::CreateTextureViewError::InvalidTextureViewDimension {
1951 view: resolved_dimension,
1952 texture: texture.desc.dimension,
1953 },
1954 );
1955 }
1956
1957 match resolved_dimension {
1958 TextureViewDimension::D1 | TextureViewDimension::D2 | TextureViewDimension::D3 => {
1959 if resolved_array_layer_count != 1 {
1960 return Err(resource::CreateTextureViewError::InvalidArrayLayerCount {
1961 requested: resolved_array_layer_count,
1962 dim: resolved_dimension,
1963 });
1964 }
1965 }
1966 TextureViewDimension::Cube => {
1967 if resolved_array_layer_count != 6 {
1968 return Err(
1969 resource::CreateTextureViewError::InvalidCubemapTextureDepth {
1970 depth: resolved_array_layer_count,
1971 },
1972 );
1973 }
1974 }
1975 TextureViewDimension::CubeArray => {
1976 if !resolved_array_layer_count.is_multiple_of(6) {
1977 return Err(
1978 resource::CreateTextureViewError::InvalidCubemapArrayTextureDepth {
1979 depth: resolved_array_layer_count,
1980 },
1981 );
1982 }
1983 }
1984 _ => {}
1985 }
1986
1987 match resolved_dimension {
1988 TextureViewDimension::Cube | TextureViewDimension::CubeArray => {
1989 if texture.desc.size.width != texture.desc.size.height {
1990 return Err(resource::CreateTextureViewError::InvalidCubeTextureViewSize);
1991 }
1992 }
1993 _ => {}
1994 }
1995
1996 if resolved_mip_level_count == 0 {
1997 return Err(resource::CreateTextureViewError::ZeroMipLevelCount);
1998 }
1999
2000 let mip_level_end = desc
2001 .range
2002 .base_mip_level
2003 .saturating_add(resolved_mip_level_count);
2004
2005 let level_end = texture.desc.mip_level_count;
2006 if mip_level_end > level_end {
2007 return Err(resource::CreateTextureViewError::TooManyMipLevels {
2008 base_mip_level: desc.range.base_mip_level,
2009 mip_level_count: resolved_mip_level_count,
2010 total: level_end,
2011 });
2012 }
2013
2014 if resolved_array_layer_count == 0 {
2015 return Err(resource::CreateTextureViewError::ZeroArrayLayerCount);
2016 }
2017
2018 let array_layer_end = desc
2019 .range
2020 .base_array_layer
2021 .saturating_add(resolved_array_layer_count);
2022
2023 let layer_end = texture.desc.array_layer_count();
2024 if array_layer_end > layer_end {
2025 return Err(resource::CreateTextureViewError::TooManyArrayLayers {
2026 base_array_layer: desc.range.base_array_layer,
2027 array_layer_count: resolved_array_layer_count,
2028 total: layer_end,
2029 });
2030 };
2031
2032 let render_extent = 'error: {
2034 if !resolved_usage.contains(wgt::TextureUsages::RENDER_ATTACHMENT) {
2035 break 'error Err(TextureViewNotRenderableReason::Usage(resolved_usage));
2036 }
2037
2038 let allowed_view_dimensions = [
2039 TextureViewDimension::D2,
2040 TextureViewDimension::D2Array,
2041 TextureViewDimension::D3,
2042 ];
2043 if !allowed_view_dimensions.contains(&resolved_dimension) {
2044 break 'error Err(TextureViewNotRenderableReason::Dimension(
2045 resolved_dimension,
2046 ));
2047 }
2048
2049 if resolved_mip_level_count != 1 {
2050 break 'error Err(TextureViewNotRenderableReason::MipLevelCount(
2051 resolved_mip_level_count,
2052 ));
2053 }
2054
2055 if resolved_array_layer_count != 1
2056 && !(self.features.contains(wgt::Features::MULTIVIEW))
2057 {
2058 break 'error Err(TextureViewNotRenderableReason::ArrayLayerCount(
2059 resolved_array_layer_count,
2060 ));
2061 }
2062
2063 if !texture.desc.format.is_multi_planar_format()
2064 && aspects != hal::FormatAspects::from(texture.desc.format)
2065 {
2066 break 'error Err(TextureViewNotRenderableReason::Aspects(aspects));
2067 }
2068
2069 Ok(texture
2070 .desc
2071 .compute_render_extent(desc.range.base_mip_level, desc.range.aspect.to_plane()))
2072 };
2073
2074 let usage = {
2076 let resolved_hal_usage = conv::map_texture_usage(
2077 resolved_usage,
2078 resolved_format.into(),
2079 format_features.flags,
2080 );
2081 let mask_copy = !(wgt::TextureUses::COPY_SRC | wgt::TextureUses::COPY_DST);
2082 let mask_dimension = match resolved_dimension {
2083 TextureViewDimension::Cube | TextureViewDimension::CubeArray => {
2084 wgt::TextureUses::RESOURCE
2085 }
2086 TextureViewDimension::D3 => {
2087 wgt::TextureUses::RESOURCE
2088 | wgt::TextureUses::STORAGE_READ_ONLY
2089 | wgt::TextureUses::STORAGE_WRITE_ONLY
2090 | wgt::TextureUses::STORAGE_READ_WRITE
2091 }
2092 _ => wgt::TextureUses::all(),
2093 };
2094 let mask_mip_level = if resolved_mip_level_count == 1 {
2095 wgt::TextureUses::all()
2096 } else {
2097 wgt::TextureUses::RESOURCE
2098 };
2099 resolved_hal_usage & mask_copy & mask_dimension & mask_mip_level
2100 };
2101
2102 let format = if resolved_format.is_depth_stencil_component(texture.desc.format) {
2104 texture.desc.format
2105 } else {
2106 resolved_format
2107 };
2108
2109 let resolved_range = wgt::ImageSubresourceRange {
2110 aspect: desc.range.aspect,
2111 base_mip_level: desc.range.base_mip_level,
2112 mip_level_count: Some(resolved_mip_level_count),
2113 base_array_layer: desc.range.base_array_layer,
2114 array_layer_count: Some(resolved_array_layer_count),
2115 };
2116
2117 let hal_desc = hal::TextureViewDescriptor {
2118 label: desc.label.to_hal(self.instance_flags),
2119 format,
2120 dimension: resolved_dimension,
2121 usage,
2122 range: resolved_range,
2123 };
2124
2125 let raw = unsafe { self.raw().create_texture_view(texture_raw, &hal_desc) }
2126 .map_err(|e| self.handle_hal_error(e))?;
2127
2128 let selector = TextureSelector {
2129 mips: desc.range.base_mip_level..mip_level_end,
2130 layers: desc.range.base_array_layer..array_layer_end,
2131 };
2132
2133 let view = TextureView {
2134 state: ResourceState::Valid(TextureViewState {
2135 raw: Snatchable::new(raw),
2136 render_extent,
2137 }),
2138 parent: texture.clone(),
2139 device: self.clone(),
2140 desc: resource::HalTextureViewDescriptor {
2141 texture_format: texture.desc.format,
2142 format: resolved_format,
2143 dimension: resolved_dimension,
2144 usage: resolved_usage,
2145 range: resolved_range,
2146 },
2147 format_features: texture.format_features,
2148 samples: texture.desc.sample_count,
2149 selector,
2150 label: desc.label.to_string(),
2151 };
2152
2153 let view = Arc::new(view);
2154
2155 {
2156 let mut views = texture.views.lock();
2157 views.push(Arc::downgrade(&view));
2158 }
2159
2160 Ok(view)
2161 }
2162
2163 pub fn create_texture_view(
2164 self: &Arc<Self>,
2165 texture: &Arc<Texture>,
2166 desc: &resource::TextureViewDescriptor,
2167 ) -> (Arc<TextureView>, Option<resource::CreateTextureViewError>) {
2168 let (view, error) = match self.create_texture_view_inner(texture, desc) {
2169 Ok(view) => (view, None),
2170 Err(e) => (TextureView::invalid(self, texture, desc), Some(e)),
2171 };
2172
2173 api_log!(
2174 "Texture::create_view({:?}) -> {:?}",
2175 Arc::as_ptr(texture),
2176 Arc::as_ptr(&view)
2177 );
2178
2179 #[cfg(feature = "trace")]
2180 if let Some(ref mut trace) = *self.trace.lock() {
2181 use crate::device::trace;
2182 use trace::IntoTrace as _;
2183 trace.add(trace::Action::CreateTextureView {
2184 id: view.to_trace(),
2185 parent: texture.to_trace(),
2186 desc: desc.clone(),
2187 });
2188 }
2189
2190 (view, error)
2191 }
2192
2193 pub fn create_external_texture(
2194 self: &Arc<Self>,
2195 desc: &resource::ExternalTextureDescriptor,
2196 planes: &[Arc<TextureView>],
2197 ) -> Result<Arc<ExternalTexture>, resource::CreateExternalTextureError> {
2198 use resource::CreateExternalTextureError;
2199 self.require_features(wgt::Features::EXTERNAL_TEXTURE)?;
2200 self.check_is_valid()?;
2201
2202 if desc.num_planes() != planes.len() {
2203 return Err(CreateExternalTextureError::IncorrectPlaneCount {
2204 format: desc.format,
2205 expected: desc.num_planes(),
2206 provided: planes.len(),
2207 });
2208 }
2209
2210 let planes = planes
2211 .iter()
2212 .enumerate()
2213 .map(|(i, plane)| {
2214 if plane.samples != 1 {
2215 return Err(CreateExternalTextureError::InvalidPlaneMultisample(
2216 plane.samples,
2217 ));
2218 }
2219
2220 let sample_type = plane
2221 .desc
2222 .format
2223 .sample_type(Some(plane.desc.range.aspect), Some(self.features))
2224 .unwrap();
2225 if !matches!(sample_type, TextureSampleType::Float { filterable: true }) {
2226 return Err(CreateExternalTextureError::InvalidPlaneSampleType {
2227 format: plane.desc.format,
2228 sample_type,
2229 });
2230 }
2231
2232 if plane.desc.dimension != TextureViewDimension::D2 {
2233 return Err(CreateExternalTextureError::InvalidPlaneDimension(
2234 plane.desc.dimension,
2235 ));
2236 }
2237
2238 let expected_components = match desc.format {
2239 wgt::ExternalTextureFormat::Rgba => 4,
2240 wgt::ExternalTextureFormat::Nv12 => match i {
2241 0 => 1,
2242 1 => 2,
2243 _ => unreachable!(),
2244 },
2245 wgt::ExternalTextureFormat::Yu12 => 1,
2246 };
2247 if plane.desc.format.components() != expected_components {
2248 return Err(CreateExternalTextureError::InvalidPlaneFormat {
2249 format: desc.format,
2250 plane: i,
2251 expected: expected_components,
2252 provided: plane.desc.format,
2253 });
2254 }
2255
2256 plane.check_usage(wgt::TextureUsages::TEXTURE_BINDING)?;
2257 Ok(plane.clone())
2258 })
2259 .collect::<Result<_, _>>()?;
2260
2261 let params_data = ExternalTextureParams::from_desc(desc);
2262 let label = desc.label.as_ref().map(|l| alloc::format!("{l} params"));
2263 let params_desc = resource::BufferDescriptor {
2264 label: label.map(Cow::Owned),
2265 size: size_of_val(¶ms_data) as wgt::BufferAddress,
2266 usage: wgt::BufferUsages::UNIFORM | wgt::BufferUsages::COPY_DST,
2267 mapped_at_creation: false,
2268 };
2269 let params = self.create_buffer(¶ms_desc)?;
2270 self.get_queue().unwrap().write_buffer(
2271 params.clone(),
2272 0,
2273 bytemuck::bytes_of(¶ms_data),
2274 )?;
2275
2276 let external_texture = ExternalTexture {
2277 device: self.clone(),
2278 planes,
2279 params,
2280 label: desc.label.to_string(),
2281 tracking_data: TrackingData::new(self.tracker_indices.external_textures.clone()),
2282 };
2283 let external_texture = Arc::new(external_texture);
2284
2285 Ok(external_texture)
2286 }
2287
2288 pub fn create_sampler(
2289 self: &Arc<Self>,
2290 desc: &resource::SamplerDescriptor,
2291 ) -> (Arc<Sampler>, Option<resource::CreateSamplerError>) {
2292 profiling::scope!("Device::create_sampler");
2293
2294 let (sampler, error) = match self.create_sampler_inner(desc) {
2295 Ok(sampler) => (sampler, None),
2296 Err(e) => (Sampler::invalid(Arc::clone(self), desc), Some(e)),
2297 };
2298
2299 #[cfg(feature = "trace")]
2300 if let Some(ref mut trace) = *self.trace.lock() {
2301 use crate::device::trace::{Action, IntoTrace as _};
2302 trace.add(Action::CreateSampler(sampler.to_trace(), desc.clone()));
2303 }
2304
2305 api_log!("Device::create_sampler -> {:?}", Arc::as_ptr(&sampler));
2306
2307 (sampler, error)
2308 }
2309
2310 pub(crate) fn create_sampler_inner(
2311 self: &Arc<Self>,
2312 desc: &resource::SamplerDescriptor,
2313 ) -> Result<Arc<Sampler>, resource::CreateSamplerError> {
2314 self.check_is_valid()?;
2315
2316 if desc
2317 .address_modes
2318 .iter()
2319 .any(|am| am == &wgt::AddressMode::ClampToBorder)
2320 {
2321 self.require_features(wgt::Features::ADDRESS_MODE_CLAMP_TO_BORDER)?;
2322 }
2323
2324 if desc.border_color == Some(wgt::SamplerBorderColor::Zero) {
2325 self.require_features(wgt::Features::ADDRESS_MODE_CLAMP_TO_ZERO)?;
2326 }
2327
2328 if desc.lod_min_clamp < 0.0 {
2329 return Err(resource::CreateSamplerError::InvalidLodMinClamp(
2330 desc.lod_min_clamp,
2331 ));
2332 }
2333 if desc.lod_max_clamp < desc.lod_min_clamp {
2334 return Err(resource::CreateSamplerError::InvalidLodMaxClamp {
2335 lod_min_clamp: desc.lod_min_clamp,
2336 lod_max_clamp: desc.lod_max_clamp,
2337 });
2338 }
2339
2340 if desc.anisotropy_clamp < 1 {
2341 return Err(resource::CreateSamplerError::InvalidAnisotropy(
2342 desc.anisotropy_clamp,
2343 ));
2344 }
2345
2346 if desc.anisotropy_clamp != 1 {
2347 if !matches!(desc.min_filter, wgt::FilterMode::Linear) {
2348 return Err(
2349 resource::CreateSamplerError::InvalidFilterModeWithAnisotropy {
2350 filter_type: resource::SamplerFilterErrorType::MinFilter,
2351 filter_mode: desc.min_filter,
2352 anisotropic_clamp: desc.anisotropy_clamp,
2353 },
2354 );
2355 }
2356 if !matches!(desc.mag_filter, wgt::FilterMode::Linear) {
2357 return Err(
2358 resource::CreateSamplerError::InvalidFilterModeWithAnisotropy {
2359 filter_type: resource::SamplerFilterErrorType::MagFilter,
2360 filter_mode: desc.mag_filter,
2361 anisotropic_clamp: desc.anisotropy_clamp,
2362 },
2363 );
2364 }
2365 if !matches!(desc.mipmap_filter, wgt::MipmapFilterMode::Linear) {
2366 return Err(
2367 resource::CreateSamplerError::InvalidMipmapFilterModeWithAnisotropy {
2368 filter_type: resource::SamplerFilterErrorType::MipmapFilter,
2369 filter_mode: desc.mipmap_filter,
2370 anisotropic_clamp: desc.anisotropy_clamp,
2371 },
2372 );
2373 }
2374 }
2375
2376 let anisotropy_clamp = if self
2377 .downlevel
2378 .flags
2379 .contains(wgt::DownlevelFlags::ANISOTROPIC_FILTERING)
2380 {
2381 desc.anisotropy_clamp.min(16)
2383 } else {
2384 1
2386 };
2387
2388 let hal_desc = hal::SamplerDescriptor {
2391 label: desc.label.to_hal(self.instance_flags),
2392 address_modes: desc.address_modes,
2393 mag_filter: desc.mag_filter,
2394 min_filter: desc.min_filter,
2395 mipmap_filter: desc.mipmap_filter,
2396 lod_clamp: desc.lod_min_clamp..desc.lod_max_clamp,
2397 compare: desc.compare,
2398 anisotropy_clamp,
2399 border_color: desc.border_color,
2400 };
2401
2402 let raw = unsafe { self.raw().create_sampler(&hal_desc) }
2403 .map_err(|e| self.handle_hal_error_with_nonfatal_oom(e))?;
2404
2405 let sampler = Sampler {
2406 raw: ResourceState::Valid(raw),
2407 device: self.clone(),
2408 label: desc.label.to_string(),
2409 tracking_data: TrackingData::new(self.tracker_indices.samplers.clone()),
2410 comparison: desc.compare.is_some(),
2411 filtering: desc.min_filter == wgt::FilterMode::Linear
2412 || desc.mag_filter == wgt::FilterMode::Linear
2413 || desc.mipmap_filter == wgt::MipmapFilterMode::Linear,
2414 };
2415
2416 let sampler = Arc::new(sampler);
2417
2418 Ok(sampler)
2419 }
2420
2421 pub fn create_shader_module<'a>(
2422 self: &Arc<Self>,
2423 desc: &pipeline::ShaderModuleDescriptor<'a>,
2424 source: pipeline::ShaderModuleSource<'a>,
2425 ) -> (
2426 Arc<pipeline::ShaderModule>,
2427 Option<pipeline::CreateShaderModuleError>,
2428 ) {
2429 #[cfg(feature = "trace")]
2430 let data = self.trace.lock().as_mut().map(|trace| {
2431 use crate::device::trace::DataKind;
2432
2433 match source {
2434 #[cfg(feature = "wgsl")]
2435 pipeline::ShaderModuleSource::Wgsl(ref code) => {
2436 trace.make_binary(DataKind::Wgsl, code.as_bytes())
2437 }
2438 #[cfg(feature = "glsl")]
2439 pipeline::ShaderModuleSource::Glsl(ref code, _) => {
2440 trace.make_binary(DataKind::Glsl, code.as_bytes())
2441 }
2442 #[cfg(feature = "spirv")]
2443 pipeline::ShaderModuleSource::SpirV(ref code, _) => {
2444 trace.make_binary(DataKind::Spv, bytemuck::cast_slice::<u32, u8>(code))
2445 }
2446 pipeline::ShaderModuleSource::Naga(ref module) => {
2447 let string =
2448 ron::ser::to_string_pretty(module, ron::ser::PrettyConfig::default())
2449 .unwrap();
2450 trace.make_binary(DataKind::Ron, string.as_bytes())
2451 }
2452 pipeline::ShaderModuleSource::Dummy(_) => {
2453 panic!("found `ShaderModuleSource::Dummy`")
2454 }
2455 }
2456 });
2457 let (shader, error) = match self.create_shader_module_inner(desc, source) {
2458 Ok(shader) => (shader, None),
2459 Err(e) => {
2460 let shader =
2461 pipeline::ShaderModule::invalid(Arc::clone(self), desc.label.to_string());
2462 (shader, Some(e))
2463 }
2464 };
2465 api_log!("Device::create_shader_module -> {:?}", Arc::as_ptr(&shader));
2466
2467 #[cfg(feature = "trace")]
2468 if let Some(data) = data {
2469 use crate::device::trace::IntoTrace as _;
2472 self.trace
2473 .lock()
2474 .as_mut()
2475 .expect("trace went away during create_shader_module?")
2476 .add(trace::Action::CreateShaderModule {
2477 id: shader.to_trace(),
2478 desc: desc.clone(),
2479 data,
2480 });
2481 };
2482 (shader, error)
2483 }
2484
2485 pub(crate) fn create_shader_module_inner<'a>(
2486 self: &Arc<Self>,
2487 desc: &pipeline::ShaderModuleDescriptor<'a>,
2488 source: pipeline::ShaderModuleSource<'a>,
2489 ) -> Result<Arc<pipeline::ShaderModule>, pipeline::CreateShaderModuleError> {
2490 self.check_is_valid()?;
2491
2492 let (module, source) = match source {
2493 #[cfg(feature = "wgsl")]
2494 pipeline::ShaderModuleSource::Wgsl(code) => {
2495 profiling::scope!("naga::front::wgsl::parse");
2496 let capabilities =
2497 features_to_naga_capabilities(self.features, self.downlevel.flags);
2498 let mut options = naga::front::wgsl::Options::new();
2499 options.capabilities = capabilities;
2500 let mut frontend = naga::front::wgsl::Frontend::new_with_options(options);
2501 let module = frontend.parse(&code).map_err(|inner| {
2502 pipeline::CreateShaderModuleError::Parsing(naga::error::ShaderError {
2503 source: code.to_string(),
2504 label: desc.label.as_ref().map(|l| l.to_string()),
2505 inner: Box::new(inner),
2506 })
2507 })?;
2508 (Cow::Owned(module), code.into_owned())
2509 }
2510 #[cfg(feature = "spirv")]
2511 pipeline::ShaderModuleSource::SpirV(spv, options) => {
2512 let parser = naga::front::spv::Frontend::new(spv.iter().cloned(), &options);
2513 profiling::scope!("naga::front::spv::Frontend");
2514 let module = parser.parse().map_err(|inner| {
2515 pipeline::CreateShaderModuleError::ParsingSpirV(naga::error::ShaderError {
2516 source: String::new(),
2517 label: desc.label.as_ref().map(|l| l.to_string()),
2518 inner: Box::new(inner),
2519 })
2520 })?;
2521 (Cow::Owned(module), String::new())
2522 }
2523 #[cfg(feature = "glsl")]
2524 pipeline::ShaderModuleSource::Glsl(code, options) => {
2525 let mut parser = naga::front::glsl::Frontend::default();
2526 profiling::scope!("naga::front::glsl::Frontend.parse");
2527 let module = parser.parse(&options, &code).map_err(|inner| {
2528 pipeline::CreateShaderModuleError::ParsingGlsl(naga::error::ShaderError {
2529 source: code.to_string(),
2530 label: desc.label.as_ref().map(|l| l.to_string()),
2531 inner: Box::new(inner),
2532 })
2533 })?;
2534 (Cow::Owned(module), code.into_owned())
2535 }
2536 pipeline::ShaderModuleSource::Naga(module) => (module, String::new()),
2537 pipeline::ShaderModuleSource::Dummy(_) => panic!("found `ShaderModuleSource::Dummy`"),
2538 };
2539 for (_, var) in module.global_variables.iter() {
2540 match var.binding {
2541 Some(br) if br.group >= self.limits.max_bind_groups => {
2542 return Err(pipeline::CreateShaderModuleError::InvalidGroupIndex {
2543 bind: br,
2544 group: br.group,
2545 limit: self.limits.max_bind_groups,
2546 });
2547 }
2548 _ => continue,
2549 };
2550 }
2551
2552 profiling::scope!("naga::validate");
2553 let debug_source =
2554 if self.instance_flags.contains(wgt::InstanceFlags::DEBUG) && !source.is_empty() {
2555 Some(hal::DebugSource {
2556 file_name: Cow::Owned(
2557 desc.label
2558 .as_ref()
2559 .map_or("shader".to_string(), |l| l.to_string()),
2560 ),
2561 source_code: Cow::Owned(source.clone()),
2562 })
2563 } else {
2564 None
2565 };
2566
2567 let info = create_validator(
2568 self.features,
2569 self.downlevel.flags,
2570 naga::valid::ValidationFlags::all(),
2571 )
2572 .validate(&module)
2573 .map_err(|inner| {
2574 pipeline::CreateShaderModuleError::Validation(naga::error::ShaderError {
2575 source,
2576 label: desc.label.as_ref().map(|l| l.to_string()),
2577 inner: Box::new(inner),
2578 })
2579 })?;
2580
2581 let interface = validation::Interface::new(&module, &info, self.limits.clone());
2582 let hal_shader = hal::ShaderInput::Naga(hal::NagaShader {
2583 module,
2584 info,
2585 debug_source,
2586 });
2587 let hal_desc = hal::ShaderModuleDescriptor {
2588 label: desc.label.to_hal(self.instance_flags),
2589 runtime_checks: desc.runtime_checks,
2590 };
2591 let raw = match unsafe { self.raw().create_shader_module(&hal_desc, hal_shader) } {
2592 Ok(raw) => raw,
2593 Err(error) => {
2594 return Err(match error {
2595 hal::ShaderError::Device(error) => {
2596 pipeline::CreateShaderModuleError::Device(self.handle_hal_error(error))
2597 }
2598 hal::ShaderError::Compilation(ref msg) => {
2599 log::error!("Shader error: {msg}");
2600 pipeline::CreateShaderModuleError::Generation
2601 }
2602 })
2603 }
2604 };
2605
2606 let module = pipeline::ShaderModule {
2607 state: ResourceState::Valid(pipeline::ShaderModuleState {
2608 raw,
2609 interface: ShaderMetaData::Interface(interface),
2610 }),
2611 device: self.clone(),
2612 label: desc.label.to_string(),
2613 };
2614
2615 let module = Arc::new(module);
2616
2617 Ok(module)
2618 }
2619
2620 pub unsafe fn create_shader_module_passthrough<'a>(
2625 self: &Arc<Self>,
2626 desc: &pipeline::ShaderModuleDescriptorPassthrough<'a>,
2627 ) -> (
2628 Arc<pipeline::ShaderModule>,
2629 Option<pipeline::CreateShaderModuleError>,
2630 ) {
2631 profiling::scope!("Device::create_shader_module_passthrough");
2632
2633 let (shader, error) = match unsafe { self.create_shader_module_passthrough_inner(desc) } {
2634 Ok(shader) => (shader, None),
2635 Err(e) => {
2636 let shader =
2637 pipeline::ShaderModule::invalid(Arc::clone(self), desc.label.to_string());
2638 (shader, Some(e))
2639 }
2640 };
2641 #[cfg(feature = "trace")]
2642 if let Some(ref mut trace) = *self.trace.lock() {
2643 use crate::device::trace::{DataKind, IntoTrace as _};
2644
2645 let mut file_names = Vec::new();
2646 for (data, kind) in [
2647 (
2648 desc.spirv.as_ref().map(|a| bytemuck::cast_slice(a)),
2649 DataKind::Spv,
2650 ),
2651 (desc.dxil.as_deref(), DataKind::Dxil),
2652 (desc.hlsl.as_ref().map(|a| a.as_bytes()), DataKind::Hlsl),
2653 (desc.metallib.as_deref(), DataKind::MetalLib),
2654 (desc.msl.as_ref().map(|a| a.as_bytes()), DataKind::Msl),
2655 (desc.glsl.as_ref().map(|a| a.as_bytes()), DataKind::Glsl),
2656 (desc.wgsl.as_ref().map(|a| a.as_bytes()), DataKind::Wgsl),
2657 ] {
2658 if let Some(data) = data {
2659 file_names.push(trace.make_binary(kind, data));
2660 }
2661 }
2662 trace.add(trace::Action::CreateShaderModulePassthrough {
2663 id: shader.to_trace(),
2664 data: file_names,
2665 label: desc.label.clone(),
2666 entry_points: desc.entry_points.clone(),
2667 });
2668 };
2669 api_log!(
2670 "Device::create_shader_module_spirv -> {:?}",
2671 Arc::as_ptr(&shader)
2672 );
2673 (shader, error)
2674 }
2675
2676 pub(crate) unsafe fn create_shader_module_passthrough_inner<'a>(
2677 self: &Arc<Self>,
2678 descriptor: &pipeline::ShaderModuleDescriptorPassthrough<'a>,
2679 ) -> Result<Arc<pipeline::ShaderModule>, pipeline::CreateShaderModuleError> {
2680 self.check_is_valid()?;
2681 self.require_features(wgt::Features::PASSTHROUGH_SHADERS)?;
2682
2683 if (descriptor.dxil.is_some() || descriptor.glsl.is_some())
2685 && descriptor.entry_points.len() != 1
2686 {
2687 return Err(pipeline::CreateShaderModuleError::IncorrectPassthroughEntryPointCount);
2688 }
2689
2690 let entry_point_hashmap = || {
2691 descriptor
2692 .entry_points
2693 .iter()
2694 .map(|e| (e.name.to_string(), e.workgroup_size))
2695 .collect()
2696 };
2697
2698 let hal_shader = match self.backend() {
2699 wgt::Backend::Vulkan => hal::ShaderInput::SpirV(
2700 descriptor
2701 .spirv
2702 .as_ref()
2703 .ok_or(pipeline::CreateShaderModuleError::NotCompiledForBackend)?,
2704 ),
2705 wgt::Backend::Dx12 => {
2706 if let Some(dxil) = &descriptor.dxil {
2707 hal::ShaderInput::Dxil { shader: dxil }
2708 } else if let Some(hlsl) = &descriptor.hlsl {
2709 hal::ShaderInput::Hlsl { shader: hlsl }
2710 } else {
2711 return Err(pipeline::CreateShaderModuleError::NotCompiledForBackend);
2712 }
2713 }
2714 wgt::Backend::Metal => {
2715 if let Some(metallib) = &descriptor.metallib {
2716 hal::ShaderInput::MetalLib {
2717 file: metallib,
2718 num_workgroups: entry_point_hashmap(),
2719 }
2720 } else if let Some(msl) = &descriptor.msl {
2721 hal::ShaderInput::Msl {
2722 shader: msl,
2723 num_workgroups: entry_point_hashmap(),
2724 }
2725 } else {
2726 return Err(pipeline::CreateShaderModuleError::NotCompiledForBackend);
2727 }
2728 }
2729 wgt::Backend::Gl => hal::ShaderInput::Glsl {
2730 shader: descriptor
2731 .glsl
2732 .as_ref()
2733 .ok_or(pipeline::CreateShaderModuleError::NotCompiledForBackend)?,
2734 },
2735 wgt::Backend::Noop => {
2736 return Err(pipeline::CreateShaderModuleError::NotCompiledForBackend)
2737 }
2738 wgt::Backend::BrowserWebGpu => unreachable!(),
2739 };
2740
2741 let hal_desc = hal::ShaderModuleDescriptor {
2742 label: descriptor.label.to_hal(self.instance_flags),
2743 runtime_checks: wgt::ShaderRuntimeChecks::unchecked(),
2744 };
2745
2746 let raw = match unsafe { self.raw().create_shader_module(&hal_desc, hal_shader) } {
2747 Ok(raw) => raw,
2748 Err(error) => {
2749 return Err(match error {
2750 hal::ShaderError::Device(error) => {
2751 pipeline::CreateShaderModuleError::Device(self.handle_hal_error(error))
2752 }
2753 hal::ShaderError::Compilation(ref msg) => {
2754 log::error!("Shader error: {msg}");
2755 pipeline::CreateShaderModuleError::Generation
2756 }
2757 })
2758 }
2759 };
2760
2761 let module = pipeline::ShaderModule {
2762 state: ResourceState::Valid(pipeline::ShaderModuleState {
2763 raw,
2764 interface: ShaderMetaData::Passthrough(PassthroughInterface {
2765 entry_point_names: descriptor
2766 .entry_points
2767 .iter()
2768 .map(|e| e.name.to_string())
2769 .collect(),
2770 }),
2771 }),
2772 device: self.clone(),
2773 label: descriptor.label.to_string(),
2774 };
2775
2776 Ok(Arc::new(module))
2777 }
2778
2779 pub(crate) fn create_command_encoder(
2780 self: &Arc<Self>,
2781 label: &crate::Label,
2782 ) -> Result<Arc<command::CommandEncoder>, DeviceError> {
2783 self.check_is_valid()?;
2784
2785 let queue = self.get_queue().unwrap();
2786
2787 let encoder = self
2788 .command_allocator
2789 .acquire_encoder(self.raw(), queue.raw())
2790 .map_err(|e| self.handle_hal_error(e))?;
2791
2792 let cmd_enc = command::CommandEncoder::new(encoder, self, label);
2793
2794 let cmd_enc = Arc::new(cmd_enc);
2795
2796 Ok(cmd_enc)
2797 }
2798
2799 fn make_late_sized_buffer_groups(
2802 shader_binding_sizes: &FastHashMap<naga::ResourceBinding, wgt::BufferSize>,
2803 layout: &binding_model::PipelineLayout,
2804 ) -> ArrayVec<pipeline::LateSizedBufferGroup, { hal::MAX_BIND_GROUPS }> {
2805 layout
2809 .bind_group_layouts
2810 .iter()
2811 .enumerate()
2812 .map(|(group_index, bgl)| {
2813 let Some(bgl) = bgl else {
2814 return pipeline::LateSizedBufferGroup::default();
2815 };
2816
2817 let shader_sizes = bgl
2818 .entries
2819 .values()
2820 .filter_map(|entry| match entry.ty {
2821 wgt::BindingType::Buffer {
2822 min_binding_size: None,
2823 ..
2824 } => {
2825 let rb = naga::ResourceBinding {
2826 group: group_index as u32,
2827 binding: entry.binding,
2828 };
2829 let shader_size =
2830 shader_binding_sizes.get(&rb).map_or(0, |nz| nz.get());
2831 Some(shader_size)
2832 }
2833 _ => None,
2834 })
2835 .collect();
2836 pipeline::LateSizedBufferGroup { shader_sizes }
2837 })
2838 .collect()
2839 }
2840
2841 pub fn create_bind_group_layout(
2842 self: &Arc<Self>,
2843 desc: &binding_model::BindGroupLayoutDescriptor,
2844 ) -> (Arc<BindGroupLayout>, Option<CreateBindGroupLayoutError>) {
2845 let (bgl, error) = match self.create_bind_group_layout_inner(desc) {
2846 Ok(layout) => (layout, None),
2847 Err(e) => (
2848 BindGroupLayout::invalid(self, desc.label.to_string()),
2849 Some(e),
2850 ),
2851 };
2852 #[cfg(feature = "trace")]
2853 if let Some(ref mut trace) = *self.trace.lock() {
2854 use crate::device::trace::IntoTrace;
2855
2856 trace.add(trace::Action::CreateBindGroupLayout(
2857 bgl.to_trace(),
2858 desc.clone(),
2859 ));
2860 }
2861 (bgl, error)
2862 }
2863
2864 fn create_bind_group_layout_inner(
2865 self: &Arc<Device>,
2866 desc: &binding_model::BindGroupLayoutDescriptor,
2867 ) -> Result<Arc<BindGroupLayout>, CreateBindGroupLayoutError> {
2868 self.check_is_valid()?;
2869
2870 let entry_map = bgl::EntryMap::from_entries(&desc.entries)?;
2871
2872 let bgl_result = self.bgl_pool.get_or_init(entry_map, |entry_map| {
2873 let bgl =
2874 self.create_bind_group_layout_impl(&desc.label, entry_map, bgl::Origin::Pool)?;
2875 bgl.exclusive_pipeline
2876 .set(binding_model::ExclusivePipeline::None)
2877 .unwrap();
2878 Ok(bgl)
2879 });
2880
2881 match bgl_result {
2882 Ok(layout) => Ok(layout),
2883 Err(e) => Err(e),
2884 }
2885 }
2886
2887 fn create_bind_group_layout_impl(
2888 self: &Arc<Self>,
2889 label: &crate::Label,
2890 entry_map: bgl::EntryMap,
2891 origin: bgl::Origin,
2892 ) -> Result<Arc<BindGroupLayout>, CreateBindGroupLayoutError> {
2893 #[derive(PartialEq)]
2894 enum WritableStorage {
2895 Yes,
2896 No,
2897 }
2898
2899 for entry in entry_map.values() {
2900 if entry.binding >= self.limits.max_bindings_per_bind_group {
2901 return Err(CreateBindGroupLayoutError::InvalidBindingIndex {
2902 binding: entry.binding,
2903 maximum: self.limits.max_bindings_per_bind_group,
2904 });
2905 }
2906
2907 use wgt::BindingType as Bt;
2908
2909 let mut required_features = wgt::Features::empty();
2910 let mut required_downlevel_flags = wgt::DownlevelFlags::empty();
2911 let (array_feature, writable_storage) = match entry.ty {
2912 Bt::Buffer {
2913 ty: wgt::BufferBindingType::Uniform,
2914 has_dynamic_offset: false,
2915 min_binding_size: _,
2916 } => (
2917 Some(wgt::Features::BUFFER_BINDING_ARRAY),
2918 WritableStorage::No,
2919 ),
2920 Bt::Buffer {
2921 ty: wgt::BufferBindingType::Uniform,
2922 has_dynamic_offset: true,
2923 min_binding_size: _,
2924 } => (
2925 Some(wgt::Features::BUFFER_BINDING_ARRAY),
2926 WritableStorage::No,
2927 ),
2928 Bt::Buffer {
2929 ty: wgt::BufferBindingType::Storage { read_only },
2930 ..
2931 } => (
2932 Some(
2933 wgt::Features::BUFFER_BINDING_ARRAY
2934 | wgt::Features::STORAGE_RESOURCE_BINDING_ARRAY,
2935 ),
2936 match read_only {
2937 true => WritableStorage::No,
2938 false => WritableStorage::Yes,
2939 },
2940 ),
2941 Bt::Sampler { .. } => (
2942 Some(wgt::Features::TEXTURE_BINDING_ARRAY),
2943 WritableStorage::No,
2944 ),
2945 Bt::Texture {
2946 multisampled: true,
2947 sample_type: TextureSampleType::Float { filterable: true },
2948 ..
2949 } => {
2950 return Err(CreateBindGroupLayoutError::Entry {
2951 binding: entry.binding,
2952 error:
2953 BindGroupLayoutEntryError::SampleTypeFloatFilterableBindingMultisampled,
2954 });
2955 }
2956 Bt::Texture {
2957 multisampled,
2958 view_dimension,
2959 ..
2960 } => {
2961 if multisampled && view_dimension != TextureViewDimension::D2 {
2962 return Err(CreateBindGroupLayoutError::Entry {
2963 binding: entry.binding,
2964 error: BindGroupLayoutEntryError::Non2DMultisampled(view_dimension),
2965 });
2966 }
2967
2968 (
2969 Some(wgt::Features::TEXTURE_BINDING_ARRAY),
2970 WritableStorage::No,
2971 )
2972 }
2973 Bt::StorageTexture {
2974 access,
2975 view_dimension,
2976 format,
2977 } => {
2978 use wgt::{StorageTextureAccess as Access, TextureFormatFeatureFlags as Flags};
2979
2980 match view_dimension {
2981 TextureViewDimension::Cube | TextureViewDimension::CubeArray => {
2982 return Err(CreateBindGroupLayoutError::Entry {
2983 binding: entry.binding,
2984 error: BindGroupLayoutEntryError::StorageTextureCube,
2985 })
2986 }
2987 _ => (),
2988 }
2989 match access {
2990 wgt::StorageTextureAccess::Atomic
2991 if !self.features.contains(wgt::Features::TEXTURE_ATOMIC) =>
2992 {
2993 return Err(CreateBindGroupLayoutError::Entry {
2994 binding: entry.binding,
2995 error: BindGroupLayoutEntryError::StorageTextureAtomic,
2996 });
2997 }
2998 _ => (),
2999 }
3000
3001 let format_features =
3002 self.describe_format_features(format).map_err(|error| {
3003 CreateBindGroupLayoutError::Entry {
3004 binding: entry.binding,
3005 error: BindGroupLayoutEntryError::MissingFeatures(error),
3006 }
3007 })?;
3008
3009 let required_feature_flag = match access {
3010 Access::WriteOnly => Flags::STORAGE_WRITE_ONLY,
3011 Access::ReadOnly => Flags::STORAGE_READ_ONLY,
3012 Access::ReadWrite => Flags::STORAGE_READ_WRITE,
3013 Access::Atomic => Flags::STORAGE_ATOMIC,
3014 };
3015
3016 if !format_features.flags.contains(required_feature_flag) {
3017 return Err(
3018 CreateBindGroupLayoutError::UnsupportedStorageTextureAccess {
3019 binding: entry.binding,
3020 access,
3021 format,
3022 },
3023 );
3024 }
3025
3026 (
3027 Some(
3028 wgt::Features::TEXTURE_BINDING_ARRAY
3029 | wgt::Features::STORAGE_RESOURCE_BINDING_ARRAY,
3030 ),
3031 match access {
3032 wgt::StorageTextureAccess::WriteOnly => WritableStorage::Yes,
3033 wgt::StorageTextureAccess::ReadOnly => WritableStorage::No,
3034 wgt::StorageTextureAccess::ReadWrite => WritableStorage::Yes,
3035 wgt::StorageTextureAccess::Atomic => {
3036 required_features |= wgt::Features::TEXTURE_ATOMIC;
3037 WritableStorage::Yes
3038 }
3039 },
3040 )
3041 }
3042 Bt::AccelerationStructure { vertex_return } => {
3043 self.require_features(wgt::Features::EXPERIMENTAL_RAY_QUERY)
3044 .map_err(|e| CreateBindGroupLayoutError::Entry {
3045 binding: entry.binding,
3046 error: e.into(),
3047 })?;
3048 if vertex_return {
3049 self.require_features(wgt::Features::EXPERIMENTAL_RAY_HIT_VERTEX_RETURN)
3050 .map_err(|e| CreateBindGroupLayoutError::Entry {
3051 binding: entry.binding,
3052 error: e.into(),
3053 })?;
3054 }
3055 (
3056 Some(wgt::Features::ACCELERATION_STRUCTURE_BINDING_ARRAY),
3057 WritableStorage::No,
3058 )
3059 }
3060 Bt::ExternalTexture => {
3061 self.require_features(wgt::Features::EXTERNAL_TEXTURE)
3062 .map_err(|e| CreateBindGroupLayoutError::Entry {
3063 binding: entry.binding,
3064 error: e.into(),
3065 })?;
3066 (None, WritableStorage::No)
3067 }
3068 };
3069
3070 if entry.count.is_some() {
3072 required_features |= array_feature
3073 .ok_or(BindGroupLayoutEntryError::ArrayUnsupported)
3074 .map_err(|error| CreateBindGroupLayoutError::Entry {
3075 binding: entry.binding,
3076 error,
3077 })?;
3078 }
3079
3080 if entry.visibility.contains_unknown_bits() {
3081 return Err(CreateBindGroupLayoutError::InvalidVisibility(
3082 entry.visibility,
3083 ));
3084 }
3085
3086 if entry.visibility.contains(wgt::ShaderStages::VERTEX) {
3087 if writable_storage == WritableStorage::Yes {
3088 required_features |= wgt::Features::VERTEX_WRITABLE_STORAGE;
3089 }
3090 if let Bt::Buffer {
3091 ty: wgt::BufferBindingType::Storage { .. },
3092 ..
3093 } = entry.ty
3094 {
3095 required_downlevel_flags |= wgt::DownlevelFlags::VERTEX_STORAGE;
3096 }
3097 }
3098 if writable_storage == WritableStorage::Yes
3099 && entry.visibility.contains(wgt::ShaderStages::FRAGMENT)
3100 {
3101 required_downlevel_flags |= wgt::DownlevelFlags::FRAGMENT_WRITABLE_STORAGE;
3102 }
3103
3104 self.require_features(required_features)
3105 .map_err(BindGroupLayoutEntryError::MissingFeatures)
3106 .map_err(|error| CreateBindGroupLayoutError::Entry {
3107 binding: entry.binding,
3108 error,
3109 })?;
3110 self.require_downlevel_flags(required_downlevel_flags)
3111 .map_err(BindGroupLayoutEntryError::MissingDownlevelFlags)
3112 .map_err(|error| CreateBindGroupLayoutError::Entry {
3113 binding: entry.binding,
3114 error,
3115 })?;
3116 }
3117
3118 let bgl_flags = conv::bind_group_layout_flags(self.features);
3119
3120 let hal_bindings = entry_map.values().copied().collect::<Vec<_>>();
3121 let hal_desc = hal::BindGroupLayoutDescriptor {
3122 label: label.to_hal(self.instance_flags),
3123 flags: bgl_flags,
3124 entries: &hal_bindings,
3125 };
3126
3127 let mut count_validator = binding_model::BindingTypeMaxCountValidator::default();
3128 for entry in entry_map.values() {
3129 count_validator.add_binding(entry);
3130 }
3131 count_validator
3134 .validate(&self.limits, self.instance_flags)
3135 .map_err(CreateBindGroupLayoutError::TooManyBindings)?;
3136
3137 count_validator.validate_binding_arrays()?;
3139
3140 let raw = unsafe { self.raw().create_bind_group_layout(&hal_desc) }
3141 .map_err(|e| self.handle_hal_error(e))?;
3142
3143 let bgl = BindGroupLayout {
3144 state: ResourceState::Valid(BindGroupLayoutState {
3145 raw: binding_model::RawBindGroupLayout::Owning(ManuallyDrop::new(raw)),
3146 origin,
3147 binding_count_validator: count_validator,
3148 }),
3149 device: self.clone(),
3150 entries: entry_map,
3151 exclusive_pipeline: OnceCellOrLock::new(),
3152 label: label.to_string(),
3153 };
3154
3155 let bgl = Arc::new(bgl);
3156
3157 Ok(bgl)
3158 }
3159
3160 fn create_buffer_binding<'a>(
3161 &self,
3162 bb: &'a binding_model::ResolvedBufferBinding,
3163 binding: u32,
3164 decl: &wgt::BindGroupLayoutEntry,
3165 buffer_init_actions: &mut Vec<BufferInitTrackerAction>,
3166 dynamic_binding_info: &mut Vec<binding_model::BindGroupDynamicBindingData>,
3167 late_buffer_binding_sizes: &mut FastHashMap<u32, wgt::BufferSize>,
3168 used: &mut BindGroupStates,
3169 snatch_guard: &'a SnatchGuard<'a>,
3170 ) -> Result<hal::BufferBinding<'a, dyn hal::DynBuffer>, CreateBindGroupError> {
3171 use crate::binding_model::CreateBindGroupError as Error;
3172
3173 let (binding_ty, dynamic, min_size) = match decl.ty {
3174 wgt::BindingType::Buffer {
3175 ty,
3176 has_dynamic_offset,
3177 min_binding_size,
3178 } => (ty, has_dynamic_offset, min_binding_size),
3179 _ => {
3180 return Err(Error::WrongBindingType {
3181 binding,
3182 actual: decl.ty,
3183 expected: "UniformBuffer, StorageBuffer or ReadonlyStorageBuffer",
3184 })
3185 }
3186 };
3187
3188 let (pub_usage, internal_use, range_limit) = match binding_ty {
3189 wgt::BufferBindingType::Uniform => (
3190 wgt::BufferUsages::UNIFORM,
3191 wgt::BufferUses::UNIFORM,
3192 self.limits.max_uniform_buffer_binding_size,
3193 ),
3194 wgt::BufferBindingType::Storage { read_only } => (
3195 wgt::BufferUsages::STORAGE,
3196 if read_only {
3197 wgt::BufferUses::STORAGE_READ_ONLY
3198 } else {
3199 wgt::BufferUses::STORAGE_READ_WRITE
3200 },
3201 self.limits.max_storage_buffer_binding_size,
3202 ),
3203 };
3204
3205 let (align, align_limit_name) =
3206 binding_model::buffer_binding_type_alignment(&self.limits, binding_ty);
3207 if !bb.offset.is_multiple_of(align as u64) {
3208 return Err(Error::UnalignedBufferOffset(
3209 bb.offset,
3210 align_limit_name,
3211 align,
3212 ));
3213 }
3214
3215 let buffer = &bb.buffer;
3216
3217 used.buffers.insert_single(buffer.clone(), internal_use);
3218
3219 buffer.same_device(self)?;
3220
3221 buffer.check_usage(pub_usage)?;
3222
3223 let req_size = match bb.size.map(wgt::BufferSize::new) {
3224 Some(non_zero @ Some(_)) => non_zero,
3226 None => None,
3228 Some(None) => return Err(CreateBindGroupError::BindingZeroSize(buffer.error_ident())),
3230 };
3231 let (bb, bind_size) = buffer.binding(bb.offset, req_size, snatch_guard)?;
3232
3233 if matches!(binding_ty, wgt::BufferBindingType::Storage { .. })
3234 && bind_size % u64::from(wgt::STORAGE_BINDING_SIZE_ALIGNMENT) != 0
3235 {
3236 return Err(Error::UnalignedEffectiveBufferBindingSizeForStorage {
3237 alignment: wgt::STORAGE_BINDING_SIZE_ALIGNMENT,
3238 size: bind_size,
3239 });
3240 }
3241
3242 let bind_end = bb.offset + bind_size;
3243
3244 if bind_size > range_limit {
3245 return Err(Error::BufferRangeTooLarge {
3246 binding,
3247 given: bind_size,
3248 limit: range_limit,
3249 });
3250 }
3251
3252 if dynamic {
3254 dynamic_binding_info.push(binding_model::BindGroupDynamicBindingData {
3255 binding_idx: binding,
3256 buffer_size: buffer.size,
3257 binding_range: bb.offset..bind_end,
3258 maximum_dynamic_offset: buffer.size - bind_end,
3259 binding_type: binding_ty,
3260 });
3261 }
3262
3263 if let Some(non_zero) = min_size {
3264 let min_size = non_zero.get();
3265 if min_size > bind_size {
3266 return Err(Error::BindingSizeTooSmall {
3267 buffer: buffer.error_ident(),
3268 actual: bind_size,
3269 min: min_size,
3270 });
3271 }
3272 } else {
3273 let late_size = wgt::BufferSize::new(bind_size)
3274 .ok_or_else(|| Error::BindingZeroSize(buffer.error_ident()))?;
3275 late_buffer_binding_sizes.insert(binding, late_size);
3276 }
3277
3278 assert_eq!(bb.offset % wgt::COPY_BUFFER_ALIGNMENT, 0);
3281
3282 let init_range = if dynamic {
3283 0..buffer.size
3286 } else {
3287 let bounds_check_alignment = binding_model::buffer_binding_type_bounds_check_alignment(
3292 &self.alignments,
3293 binding_ty,
3294 );
3295 let visible_size = align_to(bind_size, bounds_check_alignment);
3296
3297 bb.offset..bb.offset + visible_size
3298 };
3299
3300 buffer_init_actions.extend(buffer.initialization_status.read().create_action(
3301 buffer,
3302 init_range,
3303 MemoryInitKind::NeedsInitializedMemory,
3304 ));
3305
3306 Ok(bb)
3307 }
3308
3309 fn create_sampler_binding<'a>(
3310 &self,
3311 used: &mut BindGroupStates,
3312 binding: u32,
3313 decl: &wgt::BindGroupLayoutEntry,
3314 sampler: &'a Arc<Sampler>,
3315 ) -> Result<&'a dyn hal::DynSampler, CreateBindGroupError> {
3316 use crate::binding_model::CreateBindGroupError as Error;
3317
3318 used.samplers.insert_single(sampler.clone());
3319
3320 sampler.same_device(self)?;
3321
3322 match decl.ty {
3323 wgt::BindingType::Sampler(ty) => {
3324 let (allowed_filtering, allowed_comparison) = match ty {
3325 wgt::SamplerBindingType::Filtering => (None, false),
3326 wgt::SamplerBindingType::NonFiltering => (Some(false), false),
3327 wgt::SamplerBindingType::Comparison => (None, true),
3328 };
3329 if let Some(allowed_filtering) = allowed_filtering {
3330 if allowed_filtering != sampler.filtering {
3331 return Err(Error::WrongSamplerFiltering {
3332 binding,
3333 layout_flt: allowed_filtering,
3334 sampler_flt: sampler.filtering,
3335 });
3336 }
3337 }
3338 if allowed_comparison != sampler.comparison {
3339 return Err(Error::WrongSamplerComparison {
3340 binding,
3341 layout_cmp: allowed_comparison,
3342 sampler_cmp: sampler.comparison,
3343 });
3344 }
3345 }
3346 _ => {
3347 return Err(Error::WrongBindingType {
3348 binding,
3349 actual: decl.ty,
3350 expected: "Sampler",
3351 })
3352 }
3353 }
3354
3355 Ok(sampler.raw()?)
3356 }
3357
3358 fn create_texture_binding<'a>(
3359 &self,
3360 binding: u32,
3361 decl: &wgt::BindGroupLayoutEntry,
3362 view: &'a Arc<TextureView>,
3363 used: &mut BindGroupStates,
3364 texture_init_actions: &mut Vec<TextureInitTrackerAction>,
3365 snatch_guard: &'a SnatchGuard<'a>,
3366 ) -> Result<hal::TextureBinding<'a, dyn hal::DynTextureView>, CreateBindGroupError> {
3367 view.check_valid()?;
3368 view.same_device(self)?;
3369
3370 let internal_use = self.texture_use_parameters(
3371 binding,
3372 decl,
3373 view,
3374 "SampledTexture, ReadonlyStorageTexture or WriteonlyStorageTexture",
3375 )?;
3376
3377 used.views.insert_single(view.clone(), internal_use);
3378
3379 let texture = &view.parent;
3380
3381 texture_init_actions.push(TextureInitTrackerAction {
3382 texture: texture.clone(),
3383 range: TextureInitRange {
3384 mip_range: view.desc.range.mip_range(texture.desc.mip_level_count),
3385 layer_range: view
3386 .desc
3387 .range
3388 .layer_range(texture.desc.array_layer_count()),
3389 },
3390 kind: MemoryInitKind::NeedsInitializedMemory,
3391 });
3392
3393 Ok(hal::TextureBinding {
3394 view: view.try_raw(snatch_guard)?,
3395 usage: internal_use,
3396 })
3397 }
3398
3399 fn create_tlas_binding<'a>(
3400 self: &Arc<Self>,
3401 used: &mut BindGroupStates,
3402 binding: u32,
3403 decl: &wgt::BindGroupLayoutEntry,
3404 tlas: &'a Arc<Tlas>,
3405 snatch_guard: &'a SnatchGuard<'a>,
3406 ) -> Result<&'a dyn hal::DynAccelerationStructure, CreateBindGroupError> {
3407 use crate::binding_model::CreateBindGroupError as Error;
3408
3409 used.acceleration_structures.insert_single(tlas.clone());
3410
3411 tlas.same_device(self)?;
3412
3413 match decl.ty {
3414 wgt::BindingType::AccelerationStructure { vertex_return } => {
3415 if vertex_return
3416 && !tlas.flags.contains(
3417 wgpu_types::AccelerationStructureFlags::ALLOW_RAY_HIT_VERTEX_RETURN,
3418 )
3419 {
3420 return Err(Error::MissingTLASVertexReturn { binding });
3421 }
3422 }
3423 _ => {
3424 return Err(Error::WrongBindingType {
3425 binding,
3426 actual: decl.ty,
3427 expected: "Tlas",
3428 });
3429 }
3430 }
3431
3432 Ok(tlas.try_raw(snatch_guard)?)
3433 }
3434
3435 fn create_external_texture_binding<'a>(
3436 &'a self,
3437 binding: u32,
3438 decl: &wgt::BindGroupLayoutEntry,
3439 external_texture: &'a Arc<ExternalTexture>,
3440 used: &mut BindGroupStates,
3441 snatch_guard: &'a SnatchGuard,
3442 ) -> Result<
3443 hal::ExternalTextureBinding<'a, dyn hal::DynBuffer, dyn hal::DynTextureView>,
3444 CreateBindGroupError,
3445 > {
3446 use crate::binding_model::CreateBindGroupError as Error;
3447
3448 external_texture.same_device(self)?;
3449
3450 used.external_textures
3451 .insert_single(external_texture.clone());
3452
3453 match decl.ty {
3454 wgt::BindingType::ExternalTexture => {}
3455 _ => {
3456 return Err(Error::WrongBindingType {
3457 binding,
3458 actual: decl.ty,
3459 expected: "ExternalTexture",
3460 });
3461 }
3462 }
3463
3464 let planes = (0..3)
3465 .map(|i| {
3466 let plane = external_texture
3470 .planes
3471 .get(i)
3472 .unwrap_or(&external_texture.planes[0]);
3473 let internal_use = wgt::TextureUses::RESOURCE;
3474 used.views.insert_single(plane.clone(), internal_use);
3475 let view = plane.try_raw(snatch_guard)?;
3476 Ok(hal::TextureBinding {
3477 view,
3478 usage: internal_use,
3479 })
3480 })
3481 .collect::<Result<Vec<_>, Error>>()?;
3484 let planes = planes.try_into().unwrap();
3485
3486 used.buffers
3487 .insert_single(external_texture.params.clone(), wgt::BufferUses::UNIFORM);
3488 let params = external_texture.params.binding(0, None, snatch_guard)?.0;
3489
3490 Ok(hal::ExternalTextureBinding { planes, params })
3491 }
3492
3493 fn create_external_texture_binding_from_view<'a>(
3494 &'a self,
3495 binding: u32,
3496 decl: &wgt::BindGroupLayoutEntry,
3497 view: &'a Arc<TextureView>,
3498 used: &mut BindGroupStates,
3499 snatch_guard: &'a SnatchGuard,
3500 ) -> Result<
3501 hal::ExternalTextureBinding<'a, dyn hal::DynBuffer, dyn hal::DynTextureView>,
3502 CreateBindGroupError,
3503 > {
3504 use crate::binding_model::CreateBindGroupError as Error;
3505
3506 view.same_device(self)?;
3507
3508 let internal_use = self.texture_use_parameters(binding, decl, view, "SampledTexture")?;
3509 used.views.insert_single(view.clone(), internal_use);
3510
3511 match decl.ty {
3512 wgt::BindingType::ExternalTexture => {}
3513 _ => {
3514 return Err(Error::WrongBindingType {
3515 binding,
3516 actual: decl.ty,
3517 expected: "ExternalTexture",
3518 });
3519 }
3520 }
3521
3522 let planes = [
3524 hal::TextureBinding {
3525 view: view.try_raw(snatch_guard)?,
3526 usage: internal_use,
3527 },
3528 hal::TextureBinding {
3529 view: view.try_raw(snatch_guard)?,
3530 usage: internal_use,
3531 },
3532 hal::TextureBinding {
3533 view: view.try_raw(snatch_guard)?,
3534 usage: internal_use,
3535 },
3536 ];
3537 let params = hal::BufferBinding::new_unchecked(
3538 self.default_external_texture_params_buffer.as_ref(),
3539 0,
3540 None,
3541 );
3542
3543 Ok(hal::ExternalTextureBinding { planes, params })
3544 }
3545
3546 pub fn create_bind_group(
3549 self: &Arc<Self>,
3550 desc: binding_model::ResolvedBindGroupDescriptor,
3551 ) -> Result<Arc<BindGroup>, CreateBindGroupError> {
3552 use crate::binding_model::{CreateBindGroupError as Error, ResolvedBindingResource as Br};
3553
3554 let layout = desc.layout;
3555
3556 self.check_is_valid()?;
3557 layout.same_device(self)?;
3558 layout.check_is_valid()?;
3559
3560 {
3561 let actual = desc.entries.len();
3564 let expected = layout.entries.len();
3565 if actual != expected {
3566 return Err(Error::BindingsNumMismatch { expected, actual });
3567 }
3568 }
3569
3570 let mut dynamic_binding_info = Vec::new();
3573 let mut late_buffer_binding_sizes = FastHashMap::default();
3577 let mut used = BindGroupStates::new();
3579
3580 let mut buffer_init_actions = Vec::new();
3581 let mut texture_init_actions = Vec::new();
3582 let mut hal_entries = Vec::with_capacity(desc.entries.len());
3583 let mut hal_buffers = Vec::new();
3584 let mut hal_samplers = Vec::new();
3585 let mut hal_textures = Vec::new();
3586 let mut hal_tlas_s = Vec::new();
3587 let mut hal_external_textures = Vec::new();
3588 let snatch_guard = self.snatchable_lock.read();
3589 for entry in desc.entries.iter() {
3590 let binding = entry.binding;
3591 let decl = layout
3593 .entries
3594 .get(binding)
3595 .ok_or(Error::MissingBindingDeclaration(binding))?;
3596 let (res_index, count) = match entry.resource {
3597 Br::Buffer(ref bb) => {
3598 let bb = self.create_buffer_binding(
3599 bb,
3600 binding,
3601 decl,
3602 &mut buffer_init_actions,
3603 &mut dynamic_binding_info,
3604 &mut late_buffer_binding_sizes,
3605 &mut used,
3606 &snatch_guard,
3607 )?;
3608
3609 let res_index = hal_buffers.len();
3610 hal_buffers.push(bb);
3611 (res_index, 1)
3612 }
3613 Br::BufferArray(ref bindings_array) => {
3614 let num_bindings = bindings_array.len();
3615 Self::check_array_binding(self.features, decl.count, num_bindings)?;
3616
3617 let res_index = hal_buffers.len();
3618 for bb in bindings_array.iter() {
3619 let bb = self.create_buffer_binding(
3620 bb,
3621 binding,
3622 decl,
3623 &mut buffer_init_actions,
3624 &mut dynamic_binding_info,
3625 &mut late_buffer_binding_sizes,
3626 &mut used,
3627 &snatch_guard,
3628 )?;
3629 hal_buffers.push(bb);
3630 }
3631 (res_index, num_bindings)
3632 }
3633 Br::Sampler(ref sampler) => {
3634 let sampler = self.create_sampler_binding(&mut used, binding, decl, sampler)?;
3635
3636 let res_index = hal_samplers.len();
3637 hal_samplers.push(sampler);
3638 (res_index, 1)
3639 }
3640 Br::SamplerArray(ref samplers) => {
3641 let num_bindings = samplers.len();
3642 Self::check_array_binding(self.features, decl.count, num_bindings)?;
3643
3644 let res_index = hal_samplers.len();
3645 for sampler in samplers.iter() {
3646 let sampler =
3647 self.create_sampler_binding(&mut used, binding, decl, sampler)?;
3648
3649 hal_samplers.push(sampler);
3650 }
3651
3652 (res_index, num_bindings)
3653 }
3654 Br::TextureView(ref view) => match decl.ty {
3655 wgt::BindingType::ExternalTexture => {
3656 let et = self.create_external_texture_binding_from_view(
3657 binding,
3658 decl,
3659 view,
3660 &mut used,
3661 &snatch_guard,
3662 )?;
3663 let res_index = hal_external_textures.len();
3664 hal_external_textures.push(et);
3665 (res_index, 1)
3666 }
3667 _ => {
3668 let tb = self.create_texture_binding(
3669 binding,
3670 decl,
3671 view,
3672 &mut used,
3673 &mut texture_init_actions,
3674 &snatch_guard,
3675 )?;
3676 let res_index = hal_textures.len();
3677 hal_textures.push(tb);
3678 (res_index, 1)
3679 }
3680 },
3681 Br::TextureViewArray(ref views) => {
3682 let num_bindings = views.len();
3683 Self::check_array_binding(self.features, decl.count, num_bindings)?;
3684
3685 let res_index = hal_textures.len();
3686 for view in views.iter() {
3687 let tb = self.create_texture_binding(
3688 binding,
3689 decl,
3690 view,
3691 &mut used,
3692 &mut texture_init_actions,
3693 &snatch_guard,
3694 )?;
3695
3696 hal_textures.push(tb);
3697 }
3698
3699 (res_index, num_bindings)
3700 }
3701 Br::AccelerationStructure(ref tlas) => {
3702 let tlas =
3703 self.create_tlas_binding(&mut used, binding, decl, tlas, &snatch_guard)?;
3704 let res_index = hal_tlas_s.len();
3705 hal_tlas_s.push(tlas);
3706 (res_index, 1)
3707 }
3708 Br::AccelerationStructureArray(ref tlas_array) => {
3709 let num_bindings = tlas_array.len();
3713 Self::check_array_binding(self.features, decl.count, num_bindings)?;
3714
3715 let res_index = hal_tlas_s.len();
3716 for tlas in tlas_array.iter() {
3717 let tlas = self.create_tlas_binding(
3718 &mut used,
3719 binding,
3720 decl,
3721 tlas,
3722 &snatch_guard,
3723 )?;
3724 hal_tlas_s.push(tlas);
3725 }
3726 (res_index, num_bindings)
3727 }
3728 Br::ExternalTexture(ref et) => {
3729 let et = self.create_external_texture_binding(
3730 binding,
3731 decl,
3732 et,
3733 &mut used,
3734 &snatch_guard,
3735 )?;
3736 let res_index = hal_external_textures.len();
3737 hal_external_textures.push(et);
3738 (res_index, 1)
3739 }
3740 };
3741
3742 hal_entries.push(hal::BindGroupEntry {
3743 binding,
3744 resource_index: res_index as u32,
3745 count: count as u32,
3746 });
3747 }
3748
3749 used.optimize();
3750
3751 hal_entries.sort_by_key(|entry| entry.binding);
3752 for (a, b) in hal_entries.iter().zip(hal_entries.iter().skip(1)) {
3753 if a.binding == b.binding {
3754 return Err(Error::DuplicateBinding(a.binding));
3755 }
3756 }
3757
3758 dynamic_binding_info.sort_by_key(|i| i.binding_idx);
3759
3760 let hal_desc = hal::BindGroupDescriptor {
3761 label: desc.label.to_hal(self.instance_flags),
3762 layout: layout.try_raw()?,
3763 entries: &hal_entries,
3764 buffers: &hal_buffers,
3765 samplers: &hal_samplers,
3766 textures: &hal_textures,
3767 acceleration_structures: &hal_tlas_s,
3768 external_textures: &hal_external_textures,
3769 };
3770 let raw = unsafe { self.raw().create_bind_group(&hal_desc) }
3771 .map_err(|e| self.handle_hal_error(e))?;
3772
3773 let late_buffer_binding_infos = layout
3775 .entries
3776 .indices()
3777 .flat_map(|binding| {
3778 let size = late_buffer_binding_sizes.get(&binding).cloned()?;
3779 Some(BindGroupLateBufferBindingInfo {
3780 binding_index: binding,
3781 size,
3782 })
3783 })
3784 .collect();
3785
3786 let bind_group = BindGroup {
3787 raw: Snatchable::new(raw),
3788 device: self.clone(),
3789 layout,
3790 label: desc.label.to_string(),
3791 tracking_data: TrackingData::new(self.tracker_indices.bind_groups.clone()),
3792 used,
3793 buffer_init_actions,
3794 texture_init_actions,
3795 dynamic_binding_info,
3796 late_buffer_binding_infos,
3797 };
3798
3799 let bind_group = Arc::new(bind_group);
3800
3801 let weak_ref = Arc::downgrade(&bind_group);
3802 for texture in bind_group.used.views.used_textures() {
3803 let mut bind_groups = texture.bind_groups.lock();
3804 bind_groups.push(weak_ref.clone());
3805 }
3806 for buffer in bind_group.used.buffers.used_resources() {
3807 let mut bind_groups = buffer.bind_groups.lock();
3808 bind_groups.push(weak_ref.clone());
3809 }
3810
3811 Ok(bind_group)
3812 }
3813
3814 fn check_array_binding(
3815 features: wgt::Features,
3816 count: Option<NonZeroU32>,
3817 num_bindings: usize,
3818 ) -> Result<(), CreateBindGroupError> {
3819 use super::binding_model::CreateBindGroupError as Error;
3820
3821 if let Some(count) = count {
3822 let count = count.get() as usize;
3823 if count < num_bindings {
3824 return Err(Error::BindingArrayPartialLengthMismatch {
3825 actual: num_bindings,
3826 expected: count,
3827 });
3828 }
3829 if count != num_bindings
3830 && !features.contains(wgt::Features::PARTIALLY_BOUND_BINDING_ARRAY)
3831 {
3832 return Err(Error::BindingArrayLengthMismatch {
3833 actual: num_bindings,
3834 expected: count,
3835 });
3836 }
3837 if num_bindings == 0 {
3838 return Err(Error::BindingArrayZeroLength);
3839 }
3840 } else {
3841 return Err(Error::SingleBindingExpected);
3842 };
3843
3844 Ok(())
3845 }
3846
3847 fn texture_use_parameters(
3848 &self,
3849 binding: u32,
3850 decl: &wgt::BindGroupLayoutEntry,
3851 view: &TextureView,
3852 expected: &'static str,
3853 ) -> Result<wgt::TextureUses, CreateBindGroupError> {
3854 use crate::binding_model::CreateBindGroupError as Error;
3855 if view
3856 .desc
3857 .aspects()
3858 .contains(hal::FormatAspects::DEPTH | hal::FormatAspects::STENCIL)
3859 {
3860 return Err(Error::DepthStencilAspect);
3861 }
3862 match decl.ty {
3863 wgt::BindingType::Texture {
3864 sample_type,
3865 view_dimension,
3866 multisampled,
3867 } => {
3868 use wgt::TextureSampleType as Tst;
3869 if multisampled != (view.samples != 1) {
3870 return Err(Error::InvalidTextureMultisample {
3871 binding,
3872 layout_multisampled: multisampled,
3873 view_samples: view.samples,
3874 });
3875 }
3876 let compat_sample_type = view
3877 .desc
3878 .format
3879 .sample_type(Some(view.desc.range.aspect), Some(self.features))
3880 .unwrap();
3881 match (sample_type, compat_sample_type) {
3882 (Tst::Uint, Tst::Uint) |
3883 (Tst::Sint, Tst::Sint) |
3884 (Tst::Depth, Tst::Depth) |
3885 (Tst::Float { filterable: false }, Tst::Float { .. }) |
3887 (Tst::Float { filterable: true }, Tst::Float { filterable: true }) |
3889 (Tst::Float { filterable: false }, Tst::Depth) => {}
3891 (Tst::Float { filterable: true }, Tst::Float { .. })
3896 if view.format_features.flags
3897 .contains(wgt::TextureFormatFeatureFlags::FILTERABLE) => {}
3898 _ => {
3899 return Err(Error::InvalidTextureSampleType {
3900 binding,
3901 layout_sample_type: sample_type,
3902 view_format: view.desc.format,
3903 view_sample_type: compat_sample_type,
3904 })
3905 }
3906 }
3907 if view_dimension != view.desc.dimension {
3908 return Err(Error::InvalidTextureDimension {
3909 binding,
3910 layout_dimension: view_dimension,
3911 view_dimension: view.desc.dimension,
3912 });
3913 }
3914 view.check_usage(wgt::TextureUsages::TEXTURE_BINDING)?;
3915 Ok(wgt::TextureUses::RESOURCE)
3916 }
3917 wgt::BindingType::StorageTexture {
3918 access,
3919 format,
3920 view_dimension,
3921 } => {
3922 if format != view.desc.format {
3923 return Err(Error::InvalidStorageTextureFormat {
3924 binding,
3925 layout_format: format,
3926 view_format: view.desc.format,
3927 });
3928 }
3929 if view_dimension != view.desc.dimension {
3930 return Err(Error::InvalidTextureDimension {
3931 binding,
3932 layout_dimension: view_dimension,
3933 view_dimension: view.desc.dimension,
3934 });
3935 }
3936
3937 let mip_level_count = view.selector.mips.end - view.selector.mips.start;
3938 if mip_level_count != 1 {
3939 return Err(Error::InvalidStorageTextureMipLevelCount {
3940 binding,
3941 mip_level_count,
3942 });
3943 }
3944
3945 view.check_usage(wgt::TextureUsages::STORAGE_BINDING)?;
3946
3947 Ok(match access {
3948 wgt::StorageTextureAccess::ReadOnly => wgt::TextureUses::STORAGE_READ_ONLY,
3949 wgt::StorageTextureAccess::WriteOnly => wgt::TextureUses::STORAGE_WRITE_ONLY,
3950 wgt::StorageTextureAccess::ReadWrite => wgt::TextureUses::STORAGE_READ_WRITE,
3951 wgt::StorageTextureAccess::Atomic => wgt::TextureUses::STORAGE_ATOMIC,
3952 })
3953 }
3954 wgt::BindingType::ExternalTexture => {
3955 if view.desc.dimension != TextureViewDimension::D2 {
3956 return Err(Error::InvalidTextureDimension {
3957 binding,
3958 layout_dimension: TextureViewDimension::D2,
3959 view_dimension: view.desc.dimension,
3960 });
3961 }
3962 let mip_level_count = view.selector.mips.end - view.selector.mips.start;
3963 if mip_level_count != 1 {
3964 return Err(Error::InvalidExternalTextureMipLevelCount {
3965 binding,
3966 mip_level_count,
3967 });
3968 }
3969 if view.desc.format != TextureFormat::Rgba8Unorm
3970 && view.desc.format != TextureFormat::Bgra8Unorm
3971 && view.desc.format != TextureFormat::Rgba16Float
3972 {
3973 return Err(Error::InvalidExternalTextureFormat {
3974 binding,
3975 format: view.desc.format,
3976 });
3977 }
3978 if view.samples != 1 {
3979 return Err(Error::InvalidTextureMultisample {
3980 binding,
3981 layout_multisampled: false,
3982 view_samples: view.samples,
3983 });
3984 }
3985
3986 view.check_usage(wgt::TextureUsages::TEXTURE_BINDING)?;
3987 Ok(wgt::TextureUses::RESOURCE)
3988 }
3989 _ => Err(Error::WrongBindingType {
3990 binding,
3991 actual: decl.ty,
3992 expected,
3993 }),
3994 }
3995 }
3996
3997 pub fn create_pipeline_layout(
3998 self: &Arc<Self>,
3999 desc: &binding_model::ResolvedPipelineLayoutDescriptor,
4000 ) -> (
4001 Arc<binding_model::PipelineLayout>,
4002 Option<binding_model::CreatePipelineLayoutError>,
4003 ) {
4004 let (layout, error) = match self.create_pipeline_layout_impl(desc, false) {
4005 Ok(layout) => (layout, None),
4006 Err(e) => (
4007 binding_model::PipelineLayout::invalid(Arc::clone(self), desc.label.to_string()),
4008 Some(e),
4009 ),
4010 };
4011 #[cfg(feature = "trace")]
4012 if let Some(ref mut trace) = *self.trace.lock() {
4013 use crate::device::trace::IntoTrace;
4014 trace.add(trace::Action::CreatePipelineLayout(
4015 layout.to_trace(),
4016 desc.to_trace(),
4017 ));
4018 }
4019 api_log!(
4020 "Device::create_pipeline_layout -> {:?}",
4021 Arc::as_ptr(&layout)
4022 );
4023 (layout, error)
4024 }
4025
4026 fn create_pipeline_layout_impl(
4027 self: &Arc<Self>,
4028 desc: &binding_model::ResolvedPipelineLayoutDescriptor,
4029 ignore_exclusive_pipeline_check: bool,
4030 ) -> Result<Arc<binding_model::PipelineLayout>, binding_model::CreatePipelineLayoutError> {
4031 use crate::binding_model::CreatePipelineLayoutError as Error;
4032
4033 self.check_is_valid()?;
4034
4035 let bind_group_layouts_count = desc.bind_group_layouts.len();
4036 let device_max_bind_groups = self.limits.max_bind_groups as usize;
4037 if bind_group_layouts_count > device_max_bind_groups {
4038 return Err(Error::TooManyGroups {
4039 actual: bind_group_layouts_count,
4040 max: device_max_bind_groups,
4041 });
4042 }
4043
4044 if desc.immediate_size != 0 {
4045 self.require_features(wgt::Features::IMMEDIATES)?;
4046 }
4047 if self.limits.max_immediate_size < desc.immediate_size {
4048 return Err(Error::ImmediateRangeTooLarge {
4049 size: desc.immediate_size,
4050 max: self.limits.max_immediate_size,
4051 });
4052 }
4053 if !desc
4054 .immediate_size
4055 .is_multiple_of(wgt::IMMEDIATE_DATA_ALIGNMENT)
4056 {
4057 return Err(Error::MisalignedImmediateSize {
4058 size: desc.immediate_size,
4059 });
4060 }
4061
4062 let mut count_validator = binding_model::BindingTypeMaxCountValidator::default();
4063
4064 for (index, bgl) in desc.bind_group_layouts.iter().enumerate() {
4065 let Some(bgl) = bgl else {
4066 continue;
4067 };
4068
4069 bgl.same_device(self)?;
4070
4071 if !ignore_exclusive_pipeline_check {
4072 let exclusive_pipeline = bgl.exclusive_pipeline.get().unwrap();
4073 if !matches!(exclusive_pipeline, binding_model::ExclusivePipeline::None) {
4074 return Err(Error::BglHasExclusivePipeline {
4075 index,
4076 pipeline: alloc::format!("{exclusive_pipeline}"),
4077 });
4078 }
4079 }
4080
4081 count_validator.merge(&bgl.state()?.binding_count_validator);
4082 }
4083
4084 count_validator
4085 .validate(&self.limits, self.instance_flags)
4086 .map_err(Error::TooManyBindings)?;
4087
4088 let buffers_and_acceleration_structures_in_vertex_stage =
4089 count_validator.buffers_and_acceleration_structures_in_vertex_stage();
4090
4091 let get_bgl_iter = || {
4092 desc.bind_group_layouts
4093 .iter()
4094 .map(|bgl| bgl.as_ref().filter(|bgl| !bgl.entries.is_empty()))
4095 };
4096
4097 let bind_group_layouts = get_bgl_iter()
4098 .map(|bgl| bgl.cloned())
4099 .collect::<ArrayVec<_, { hal::MAX_BIND_GROUPS }>>();
4100
4101 let raw_bind_group_layouts = get_bgl_iter()
4102 .map(|bgl| bgl.map(|bgl| bgl.try_raw()).transpose())
4103 .collect::<Result<ArrayVec<_, { hal::MAX_BIND_GROUPS }>, _>>()?;
4104
4105 let additional_flags = if self.indirect_validation.is_some() {
4106 hal::PipelineLayoutFlags::INDIRECT_BUILTIN_UPDATE
4107 } else {
4108 hal::PipelineLayoutFlags::empty()
4109 };
4110
4111 let hal_desc = hal::PipelineLayoutDescriptor {
4112 label: desc.label.to_hal(self.instance_flags),
4113 flags: hal::PipelineLayoutFlags::FIRST_VERTEX_INSTANCE
4114 | hal::PipelineLayoutFlags::NUM_WORK_GROUPS
4115 | additional_flags,
4116 bind_group_layouts: &raw_bind_group_layouts,
4117 immediate_size: desc.immediate_size,
4118 };
4119
4120 let raw = unsafe { self.raw().create_pipeline_layout(&hal_desc) }
4121 .map_err(|e| self.handle_hal_error(e))?;
4122
4123 drop(raw_bind_group_layouts);
4124
4125 let layout = binding_model::PipelineLayout {
4126 raw: ResourceState::Valid(raw),
4127 device: self.clone(),
4128 label: desc.label.to_string(),
4129 bind_group_layouts,
4130 immediate_size: desc.immediate_size,
4131 buffers_and_acceleration_structures_in_vertex_stage,
4132 };
4133
4134 let layout = Arc::new(layout);
4135
4136 Ok(layout)
4137 }
4138
4139 fn create_derived_pipeline_layout(
4140 self: &Arc<Self>,
4141 mut derived_group_layouts: Box<ArrayVec<bgl::EntryMap, { hal::MAX_BIND_GROUPS }>>,
4142 immediate_size: u32,
4143 ) -> Result<Arc<binding_model::PipelineLayout>, pipeline::ImplicitLayoutError> {
4144 while derived_group_layouts
4145 .last()
4146 .is_some_and(|map| map.is_empty())
4147 {
4148 derived_group_layouts.pop();
4149 }
4150
4151 let mut unique_bind_group_layouts = FastHashMap::default();
4152
4153 let bind_group_layouts = derived_group_layouts
4154 .into_iter()
4155 .map(|mut bgl_entry_map| {
4156 if bgl_entry_map.is_empty() {
4157 return Ok(None);
4158 }
4159
4160 bgl_entry_map.sort();
4161 match unique_bind_group_layouts.entry(bgl_entry_map) {
4162 hashbrown::hash_map::Entry::Occupied(v) => Ok(Some(Arc::clone(v.get()))),
4163 hashbrown::hash_map::Entry::Vacant(e) => {
4164 match self.create_bind_group_layout_impl(
4165 &None,
4166 e.key().clone(),
4167 bgl::Origin::Derived,
4168 ) {
4169 Ok(bgl) => {
4170 e.insert(bgl.clone());
4171 Ok(Some(bgl))
4172 }
4173 Err(e) => Err(e),
4174 }
4175 }
4176 }
4177 })
4178 .collect::<Result<Vec<_>, _>>()?;
4179
4180 let layout_desc = binding_model::ResolvedPipelineLayoutDescriptor {
4181 label: None,
4182 bind_group_layouts: Cow::Owned(bind_group_layouts),
4183 immediate_size,
4184 };
4185
4186 let layout = self.create_pipeline_layout_impl(&layout_desc, true)?;
4187 Ok(layout)
4188 }
4189
4190 pub fn create_compute_pipeline(
4191 self: &Arc<Self>,
4192 desc: pipeline::ResolvedComputePipelineDescriptor,
4193 ) -> (
4194 Arc<pipeline::ComputePipeline>,
4195 Option<pipeline::CreateComputePipelineError>,
4196 ) {
4197 let (compute_pipeline, error) = match self.create_compute_pipeline_inner(desc.clone()) {
4198 Ok(compute_pipeline) => (compute_pipeline, None),
4199 Err(error) => (
4200 pipeline::ComputePipeline::invalid(self.clone(), desc.label.to_string()),
4201 Some(error),
4202 ),
4203 };
4204 #[cfg(feature = "trace")]
4205 if let Some(ref mut trace) = *self.trace.lock() {
4206 use crate::device::trace;
4207 use crate::device::trace::IntoTrace;
4208 trace.add(trace::Action::CreateComputePipeline {
4209 id: compute_pipeline.to_trace(),
4210 desc: desc.to_trace(),
4211 });
4212 }
4213 (compute_pipeline, error)
4214 }
4215
4216 pub fn create_compute_pipeline_inner(
4217 self: &Arc<Self>,
4218 desc: pipeline::ResolvedComputePipelineDescriptor,
4219 ) -> Result<Arc<pipeline::ComputePipeline>, pipeline::CreateComputePipelineError> {
4220 self.check_is_valid()?;
4221
4222 self.require_downlevel_flags(wgt::DownlevelFlags::COMPUTE_SHADERS)?;
4223
4224 let shader_module = desc.stage.module;
4225
4226 let shader_module_state = shader_module.state()?;
4227 shader_module.same_device(self)?;
4228
4229 let is_auto_layout = desc.layout.is_none();
4230
4231 let pipeline_layout = match desc.layout {
4233 Some(pipeline_layout) => {
4234 pipeline_layout.same_device(self)?;
4235 pipeline_layout.check_valid()?;
4236 Some(pipeline_layout)
4237 }
4238 None => None,
4239 };
4240
4241 if shader_module_state.interface.interface().is_none() && pipeline_layout.is_none() {
4242 return Err(pipeline::CreateComputePipelineError::Implicit(
4243 pipeline::ImplicitLayoutError::Passthrough(wgt::ShaderStages::COMPUTE),
4244 ));
4245 }
4246
4247 let mut binding_layout_source = match pipeline_layout {
4248 Some(pipeline_layout) => validation::BindingLayoutSource::Provided(pipeline_layout),
4249 None => validation::BindingLayoutSource::new_derived(&self.limits),
4250 };
4251 let mut shader_binding_sizes = FastHashMap::default();
4252 let io = validation::StageIo::default();
4253
4254 let final_entry_point_name;
4255
4256 {
4257 let stage = validation::ShaderStageForValidation::Compute;
4258
4259 final_entry_point_name = shader_module.finalize_entry_point_name(
4260 stage.to_naga(),
4261 desc.stage.entry_point.as_ref().map(|ep| ep.as_ref()),
4262 )?;
4263
4264 if let Some(interface) = shader_module_state.interface.interface() {
4265 let _ = interface.check_stage(
4266 &mut binding_layout_source,
4267 &mut shader_binding_sizes,
4268 &final_entry_point_name,
4269 stage,
4270 io,
4271 None,
4272 )?;
4273 }
4274 }
4275
4276 let pipeline_layout = match binding_layout_source {
4277 validation::BindingLayoutSource::Provided(pipeline_layout) => pipeline_layout,
4278 validation::BindingLayoutSource::Derived(entries) => {
4279 let immediate_size = shader_module_state
4280 .interface
4281 .interface()
4282 .map_or(0, |i| i.immediate_size);
4283 self.create_derived_pipeline_layout(entries, immediate_size)?
4284 }
4285 };
4286
4287 let late_sized_buffer_groups =
4288 Device::make_late_sized_buffer_groups(&shader_binding_sizes, &pipeline_layout);
4289
4290 let cache = match desc.cache {
4291 Some(cache) => {
4292 cache.check_is_valid()?;
4293 cache.same_device(self)?;
4294 Some(cache)
4295 }
4296 None => None,
4297 };
4298
4299 let pipeline_desc = hal::ComputePipelineDescriptor {
4300 label: desc.label.to_hal(self.instance_flags),
4301 layout: pipeline_layout.raw()?,
4302 stage: hal::ProgrammableStage {
4303 module: shader_module_state.raw.as_ref(),
4304 entry_point: final_entry_point_name.as_ref(),
4305 constants: &desc.stage.constants,
4306 zero_initialize_workgroup_memory: desc.stage.zero_initialize_workgroup_memory,
4307 },
4308 cache: cache.as_ref().map(|it| it.raw()).transpose()?,
4309 };
4310
4311 let raw =
4312 unsafe { self.raw().create_compute_pipeline(&pipeline_desc) }.map_err(
4313 |err| match err {
4314 hal::PipelineError::Device(error) => {
4315 pipeline::CreateComputePipelineError::Device(self.handle_hal_error(error))
4316 }
4317 hal::PipelineError::Linkage(_stages, msg) => {
4318 pipeline::CreateComputePipelineError::Internal(msg)
4319 }
4320 hal::PipelineError::EntryPoint(_stage) => {
4321 pipeline::CreateComputePipelineError::Internal(
4322 ENTRYPOINT_FAILURE_ERROR.to_string(),
4323 )
4324 }
4325 hal::PipelineError::PipelineConstants(_stages, msg) => {
4326 pipeline::CreateComputePipelineError::PipelineConstants(msg)
4327 }
4328 },
4329 )?;
4330
4331 let immediate_slots_required =
4332 shader_module_state
4333 .interface
4334 .interface()
4335 .map_or(Default::default(), |iface| {
4336 iface.immediate_slots_required(
4337 naga::ShaderStage::Compute,
4338 &final_entry_point_name,
4339 )
4340 });
4341
4342 let pipeline = pipeline::ComputePipeline {
4343 state: ResourceState::Valid(pipeline::ComputePipelineState {
4344 raw: ManuallyDrop::new(raw),
4345 layout: pipeline_layout.clone(),
4346 _shader_module: shader_module,
4347 }),
4348 device: self.clone(),
4349 late_sized_buffer_groups,
4350 immediate_slots_required,
4351 label: desc.label.to_string(),
4352 tracking_data: TrackingData::new(self.tracker_indices.compute_pipelines.clone()),
4353 };
4354
4355 let pipeline = Arc::new(pipeline);
4356
4357 if is_auto_layout {
4358 for bgl in pipeline_layout.bind_group_layouts.iter() {
4359 let Some(bgl) = bgl else {
4360 continue;
4361 };
4362
4363 let _ = bgl.exclusive_pipeline.set((&pipeline).into());
4366 }
4367 }
4368
4369 Ok(pipeline)
4370 }
4371
4372 pub fn create_render_pipeline(
4373 self: &Arc<Self>,
4374 desc: pipeline::ResolvedGeneralRenderPipelineDescriptor,
4375 ) -> (
4376 Arc<pipeline::RenderPipeline>,
4377 Option<pipeline::CreateRenderPipelineError>,
4378 ) {
4379 let (render_pipeline, error) = match self.create_render_pipeline_inner(desc.clone()) {
4380 Ok(pipeline) => (pipeline, None),
4381 Err(e) => (
4382 pipeline::RenderPipeline::invalid(self.clone(), desc.label.to_string()),
4383 Some(e),
4384 ),
4385 };
4386 #[cfg(feature = "trace")]
4387 if let Some(ref mut trace) = *self.trace.lock() {
4388 use crate::device::trace::IntoTrace;
4389 trace.add(trace::Action::CreateGeneralRenderPipeline {
4390 id: render_pipeline.to_trace(),
4391 desc: desc.to_trace(),
4392 });
4393 }
4394 (render_pipeline, error)
4395 }
4396
4397 pub fn create_render_pipeline_inner(
4398 self: &Arc<Self>,
4399 desc: pipeline::ResolvedGeneralRenderPipelineDescriptor,
4400 ) -> Result<Arc<pipeline::RenderPipeline>, pipeline::CreateRenderPipelineError> {
4401 use wgt::TextureFormatFeatureFlags as Tfff;
4402
4403 self.check_is_valid()?;
4404
4405 let mut shader_binding_sizes = FastHashMap::default();
4406
4407 let color_targets = desc
4408 .fragment
4409 .as_ref()
4410 .map_or(&[][..], |fragment| &fragment.targets);
4411 let depth_stencil_state = desc.depth_stencil.as_ref();
4412
4413 check_color_attachment_count(color_targets.len(), self.limits.max_color_attachments)?;
4414
4415 {
4416 let cts: ArrayVec<_, { hal::MAX_COLOR_ATTACHMENTS }> =
4417 color_targets.iter().filter_map(|x| x.as_ref()).collect();
4418 if !cts.is_empty() && {
4419 let first = &cts[0];
4420 cts[1..]
4421 .iter()
4422 .any(|ct| ct.write_mask != first.write_mask || ct.blend != first.blend)
4423 } {
4424 self.require_downlevel_flags(wgt::DownlevelFlags::INDEPENDENT_BLEND)?;
4425 }
4426 }
4427
4428 let mut io = validation::StageIo::default();
4429 let mut validated_stages = wgt::ShaderStages::empty();
4430
4431 let mut vertex_steps;
4432 let mut hal_vertex_buffer_layouts;
4433 let mut total_attributes;
4434 let mut dual_source_blending = false;
4435 let mut has_depth_attachment = false;
4436 if let pipeline::RenderPipelineVertexProcessor::Vertex(ref vertex) = desc.vertex {
4437 if vertex.buffers.len() > self.limits.max_vertex_buffers as usize {
4438 return Err(pipeline::CreateRenderPipelineError::TooManyVertexBuffers {
4439 given: vertex.buffers.len() as u32,
4440 limit: self.limits.max_vertex_buffers,
4441 });
4442 }
4443
4444 vertex_steps = Vec::with_capacity(vertex.buffers.len());
4445 hal_vertex_buffer_layouts = Vec::with_capacity(vertex.buffers.len());
4446 total_attributes = 0;
4447 for (i, vb_state) in vertex.buffers.iter().enumerate() {
4448 let Some(vb_state) = vb_state else {
4449 vertex_steps.push(None);
4450 hal_vertex_buffer_layouts.push(None);
4451 continue;
4452 };
4453
4454 if vb_state.array_stride > self.limits.max_vertex_buffer_array_stride as u64 {
4457 return Err(pipeline::CreateRenderPipelineError::VertexStrideTooLarge {
4458 index: i as u32,
4459 given: vb_state.array_stride as u32,
4460 limit: self.limits.max_vertex_buffer_array_stride,
4461 });
4462 }
4463 if vb_state.array_stride % wgt::VERTEX_ALIGNMENT != 0 {
4464 return Err(pipeline::CreateRenderPipelineError::UnalignedVertexStride {
4465 index: i as u32,
4466 stride: vb_state.array_stride,
4467 });
4468 }
4469
4470 let max_stride = if vb_state.array_stride == 0 {
4471 self.limits.max_vertex_buffer_array_stride as u64
4472 } else {
4473 vb_state.array_stride
4474 };
4475 let mut last_stride = 0;
4476 for attribute in vb_state.attributes.iter() {
4477 let attribute_stride = attribute.offset + attribute.format.size();
4478 if attribute_stride > max_stride {
4479 return Err(
4480 pipeline::CreateRenderPipelineError::VertexAttributeStrideTooLarge {
4481 location: attribute.shader_location,
4482 given: attribute_stride as u32,
4483 limit: max_stride as u32,
4484 },
4485 );
4486 }
4487
4488 let required_offset_alignment = attribute.format.size().min(4);
4489 if attribute.offset % required_offset_alignment != 0 {
4490 return Err(
4491 pipeline::CreateRenderPipelineError::InvalidVertexAttributeOffset {
4492 location: attribute.shader_location,
4493 offset: attribute.offset,
4494 },
4495 );
4496 }
4497
4498 if attribute.shader_location >= self.limits.max_vertex_attributes {
4499 return Err(
4500 pipeline::CreateRenderPipelineError::VertexAttributeLocationTooLarge {
4501 given: attribute.shader_location,
4502 limit: self.limits.max_vertex_attributes,
4503 },
4504 );
4505 }
4506
4507 last_stride = last_stride.max(attribute_stride);
4508 }
4509
4510 vertex_steps.push(Some(pipeline::VertexStep {
4511 stride: vb_state.array_stride,
4512 last_stride,
4513 mode: vb_state.step_mode,
4514 }));
4515 hal_vertex_buffer_layouts.push(if vb_state.attributes.is_empty() {
4516 None
4517 } else {
4518 Some(hal::VertexBufferLayout {
4519 array_stride: vb_state.array_stride,
4520 step_mode: vb_state.step_mode,
4521 attributes: vb_state.attributes.as_ref(),
4522 })
4523 });
4524
4525 for attribute in vb_state.attributes.iter() {
4526 if attribute.offset >= 0x10000000 {
4527 return Err(
4528 pipeline::CreateRenderPipelineError::InvalidVertexAttributeOffset {
4529 location: attribute.shader_location,
4530 offset: attribute.offset,
4531 },
4532 );
4533 }
4534
4535 if let wgt::VertexFormat::Float64
4536 | wgt::VertexFormat::Float64x2
4537 | wgt::VertexFormat::Float64x3
4538 | wgt::VertexFormat::Float64x4 = attribute.format
4539 {
4540 self.require_features(wgt::Features::VERTEX_ATTRIBUTE_64BIT)?;
4541 }
4542
4543 let previous = io.varyings.insert(
4544 attribute.shader_location,
4545 validation::InterfaceVar::vertex_attribute(attribute.format),
4546 );
4547
4548 if previous.is_some() {
4549 return Err(pipeline::CreateRenderPipelineError::ShaderLocationClash(
4550 attribute.shader_location,
4551 ));
4552 }
4553 }
4554 total_attributes += vb_state.attributes.len();
4555 }
4556
4557 if total_attributes > self.limits.max_vertex_attributes as usize {
4558 return Err(
4559 pipeline::CreateRenderPipelineError::TooManyVertexAttributes {
4560 given: total_attributes as u32,
4561 limit: self.limits.max_vertex_attributes,
4562 },
4563 );
4564 }
4565 } else {
4566 vertex_steps = Vec::new();
4567 hal_vertex_buffer_layouts = Vec::new();
4568 };
4569
4570 if desc.primitive.strip_index_format.is_some() && !desc.primitive.topology.is_strip() {
4571 return Err(
4572 pipeline::CreateRenderPipelineError::StripIndexFormatForNonStripTopology {
4573 strip_index_format: desc.primitive.strip_index_format,
4574 topology: desc.primitive.topology,
4575 },
4576 );
4577 }
4578
4579 if desc.primitive.unclipped_depth {
4580 self.require_features(wgt::Features::DEPTH_CLIP_CONTROL)?;
4581 }
4582
4583 if desc.primitive.polygon_mode == wgt::PolygonMode::Line {
4584 self.require_features(wgt::Features::POLYGON_MODE_LINE)?;
4585 }
4586 if desc.primitive.polygon_mode == wgt::PolygonMode::Point {
4587 self.require_features(wgt::Features::POLYGON_MODE_POINT)?;
4588 }
4589
4590 if desc.primitive.conservative {
4591 self.require_features(wgt::Features::CONSERVATIVE_RASTERIZATION)?;
4592 }
4593
4594 if desc.primitive.conservative && desc.primitive.polygon_mode != wgt::PolygonMode::Fill {
4595 return Err(
4596 pipeline::CreateRenderPipelineError::ConservativeRasterizationNonFillPolygonMode,
4597 );
4598 }
4599
4600 let mut target_specified = false;
4601
4602 for (i, cs) in color_targets.iter().enumerate() {
4603 if let Some(cs) = cs.as_ref() {
4604 target_specified = true;
4605 let error = 'error: {
4606 if cs.write_mask.contains_unknown_bits() {
4610 break 'error Some(ColorStateError::InvalidWriteMask(cs.write_mask));
4611 }
4612
4613 let format_features = self.describe_format_features(cs.format)?;
4614 if !format_features
4615 .allowed_usages
4616 .contains(wgt::TextureUsages::RENDER_ATTACHMENT)
4617 {
4618 break 'error Some(ColorStateError::FormatNotRenderable(cs.format));
4619 }
4620 if cs.blend.is_some() && !format_features.flags.contains(Tfff::BLENDABLE) {
4621 break 'error Some(ColorStateError::FormatNotBlendable(cs.format));
4622 }
4623 if !hal::FormatAspects::from(cs.format).contains(hal::FormatAspects::COLOR) {
4624 break 'error Some(ColorStateError::FormatNotColor(cs.format));
4625 }
4626
4627 if desc.multisample.count > 1
4628 && !format_features
4629 .flags
4630 .sample_count_supported(desc.multisample.count)
4631 {
4632 break 'error Some(ColorStateError::InvalidSampleCount(
4633 desc.multisample.count,
4634 cs.format,
4635 cs.format
4636 .guaranteed_format_features(self.features)
4637 .flags
4638 .supported_sample_counts(),
4639 self.adapter
4640 .get_texture_format_features(cs.format)
4641 .flags
4642 .supported_sample_counts(),
4643 ));
4644 }
4645
4646 if let Some(blend_mode) = cs.blend {
4647 for component in [&blend_mode.color, &blend_mode.alpha] {
4648 for factor in [component.src_factor, component.dst_factor] {
4649 if factor.ref_second_blend_source() {
4650 self.require_features(wgt::Features::DUAL_SOURCE_BLENDING)?;
4651 if i == 0 {
4652 dual_source_blending = true;
4653 } else {
4654 break 'error Some(
4655 ColorStateError::BlendFactorOnUnsupportedTarget {
4656 factor,
4657 target: i as u32,
4658 },
4659 );
4660 }
4661 }
4662
4663 if [wgt::BlendOperation::Min, wgt::BlendOperation::Max]
4664 .contains(&component.operation)
4665 && factor != wgt::BlendFactor::One
4666 {
4667 break 'error Some(ColorStateError::InvalidMinMaxBlendFactor {
4668 factor,
4669 target: i as u32,
4670 });
4671 }
4672 }
4673 }
4674 }
4675
4676 break 'error None;
4677 };
4678 if let Some(e) = error {
4679 return Err(pipeline::CreateRenderPipelineError::ColorState(i as u8, e));
4680 }
4681 }
4682 }
4683
4684 if dual_source_blending && color_targets.len() > 1 {
4685 return Err(
4686 pipeline::CreateRenderPipelineError::DualSourceBlendingWithMultipleColorTargets {
4687 count: color_targets.len(),
4688 },
4689 );
4690 }
4691
4692 validation::validate_color_attachment_bytes_per_sample(
4693 color_targets.iter().flatten().map(|cs| cs.format),
4694 self.limits.max_color_attachment_bytes_per_sample,
4695 )
4696 .map_err(pipeline::CreateRenderPipelineError::ColorAttachment)?;
4697
4698 if let Some(ds) = depth_stencil_state {
4699 target_specified = true;
4701 let error = 'error: {
4702 if !ds.format.is_depth_stencil_format() {
4703 break 'error Some(pipeline::DepthStencilStateError::FormatNotDepthOrStencil(
4706 ds.format,
4707 ));
4708 }
4709
4710 let format_features = self.describe_format_features(ds.format)?;
4711 if !format_features
4712 .allowed_usages
4713 .contains(wgt::TextureUsages::RENDER_ATTACHMENT)
4714 {
4715 break 'error Some(pipeline::DepthStencilStateError::FormatNotRenderable(
4716 ds.format,
4717 ));
4718 }
4719
4720 let aspect = hal::FormatAspects::from(ds.format);
4721 if aspect.contains(hal::FormatAspects::DEPTH) {
4722 has_depth_attachment = true;
4723 } else if ds.is_depth_enabled() {
4724 break 'error Some(pipeline::DepthStencilStateError::FormatNotDepth(ds.format));
4725 }
4726 if has_depth_attachment {
4727 let Some(depth_write_enabled) = ds.depth_write_enabled else {
4728 break 'error Some(
4729 pipeline::DepthStencilStateError::MissingDepthWriteEnabled(ds.format),
4730 );
4731 };
4732
4733 let depth_compare_required = depth_write_enabled
4734 || ds.stencil.front.depth_fail_op != wgt::StencilOperation::Keep
4735 || ds.stencil.back.depth_fail_op != wgt::StencilOperation::Keep;
4736 if depth_compare_required && ds.depth_compare.is_none() {
4737 break 'error Some(pipeline::DepthStencilStateError::MissingDepthCompare(
4738 ds.format,
4739 ));
4740 }
4741 }
4742
4743 if ds.stencil.is_enabled() && !aspect.contains(hal::FormatAspects::STENCIL) {
4744 break 'error Some(pipeline::DepthStencilStateError::FormatNotStencil(
4745 ds.format,
4746 ));
4747 }
4748 if desc.multisample.count > 1
4749 && !format_features
4750 .flags
4751 .sample_count_supported(desc.multisample.count)
4752 {
4753 break 'error Some(pipeline::DepthStencilStateError::InvalidSampleCount(
4754 desc.multisample.count,
4755 ds.format,
4756 ds.format
4757 .guaranteed_format_features(self.features)
4758 .flags
4759 .supported_sample_counts(),
4760 self.adapter
4761 .get_texture_format_features(ds.format)
4762 .flags
4763 .supported_sample_counts(),
4764 ));
4765 }
4766
4767 break 'error None;
4768 };
4769 if let Some(e) = error {
4770 return Err(pipeline::CreateRenderPipelineError::DepthStencilState(e));
4771 }
4772
4773 if ds.bias.clamp != 0.0 {
4774 self.require_downlevel_flags(wgt::DownlevelFlags::DEPTH_BIAS_CLAMP)?;
4775 }
4776
4777 if (ds.bias.is_enabled() || ds.bias.clamp != 0.0)
4778 && !desc.primitive.topology.is_triangles()
4779 {
4780 return Err(pipeline::CreateRenderPipelineError::DepthStencilState(
4781 pipeline::DepthStencilStateError::DepthBiasWithIncompatibleTopology(
4782 desc.primitive.topology,
4783 ),
4784 ));
4785 }
4786 }
4787
4788 if !target_specified {
4789 return Err(pipeline::CreateRenderPipelineError::NoTargetSpecified);
4790 }
4791
4792 let is_auto_layout = desc.layout.is_none();
4793
4794 let pipeline_layout = match desc.layout {
4796 Some(pipeline_layout) => {
4797 pipeline_layout.same_device(self)?;
4798 pipeline_layout.check_valid()?;
4799 Some(pipeline_layout)
4800 }
4801 None => None,
4802 };
4803
4804 let mut binding_layout_source = match pipeline_layout {
4805 Some(pipeline_layout) => validation::BindingLayoutSource::Provided(pipeline_layout),
4806 None => validation::BindingLayoutSource::new_derived(&self.limits),
4807 };
4808
4809 let samples = {
4810 let sc = desc.multisample.count;
4811 if sc == 0 || sc > 32 || !sc.is_power_of_two() {
4812 return Err(pipeline::CreateRenderPipelineError::InvalidSampleCount(sc));
4813 }
4814 sc
4815 };
4816
4817 let mut vertex_stage = None;
4818 let mut task_stage = None;
4819 let mut mesh_stage = None;
4820 let mut _vertex_entry_point_name = String::new();
4821 let mut _task_entry_point_name = String::new();
4822 let mut _mesh_entry_point_name = String::new();
4823 let mut immediate_slots_required = naga::valid::ImmediateSlots::default();
4824 let mut passthrough_stages = wgt::ShaderStages::empty();
4825 match desc.vertex {
4826 pipeline::RenderPipelineVertexProcessor::Vertex(ref vertex) => {
4827 vertex_stage = {
4828 let stage_desc = &vertex.stage;
4829 let stage = validation::ShaderStageForValidation::Vertex {
4830 topology: desc.primitive.topology,
4831 compare_function: desc.depth_stencil.as_ref().and_then(|d| d.depth_compare),
4832 };
4833 let stage_bit = stage.to_wgt_bit();
4834 let stage_err = |error| pipeline::CreateRenderPipelineError::Stage {
4835 stage: stage_bit,
4836 error,
4837 };
4838
4839 let vertex_shader_module = &stage_desc.module;
4840 let vertex_shader_module_state = vertex_shader_module
4841 .state()
4842 .map_err(Into::into)
4843 .map_err(stage_err)?;
4844 vertex_shader_module.same_device(self)?;
4845
4846 if vertex_shader_module_state.interface.interface().is_none() {
4847 passthrough_stages |= stage_bit;
4848 }
4849
4850 _vertex_entry_point_name = vertex_shader_module
4851 .finalize_entry_point_name(
4852 stage.to_naga(),
4853 stage_desc.entry_point.as_ref().map(|ep| ep.as_ref()),
4854 )
4855 .map_err(stage_err)?;
4856
4857 if let Some(interface) = vertex_shader_module_state.interface.interface() {
4858 immediate_slots_required |= interface
4859 .immediate_slots_required(stage.to_naga(), &_vertex_entry_point_name);
4860 io = interface
4861 .check_stage(
4862 &mut binding_layout_source,
4863 &mut shader_binding_sizes,
4864 &_vertex_entry_point_name,
4865 stage,
4866 io,
4867 Some(desc.primitive.topology),
4868 )
4869 .map_err(stage_err)?;
4870 validated_stages |= stage_bit;
4871 }
4872 Some(hal::ProgrammableStage {
4873 module: vertex_shader_module_state.raw.as_ref(),
4874 entry_point: &_vertex_entry_point_name,
4875 constants: &stage_desc.constants,
4876 zero_initialize_workgroup_memory: stage_desc
4877 .zero_initialize_workgroup_memory,
4878 })
4879 };
4880 }
4881 pipeline::RenderPipelineVertexProcessor::Mesh(ref task, ref mesh) => {
4882 self.require_features(wgt::Features::EXPERIMENTAL_MESH_SHADER)?;
4883
4884 task_stage = if let Some(task) = task {
4885 let stage_desc = &task.stage;
4886 let stage = validation::ShaderStageForValidation::Task;
4887 let stage_bit = stage.to_wgt_bit();
4888 let stage_err = |error| pipeline::CreateRenderPipelineError::Stage {
4889 stage: stage_bit,
4890 error,
4891 };
4892
4893 let task_shader_module = &stage_desc.module;
4894 let task_shader_module_state = task_shader_module
4895 .state()
4896 .map_err(Into::into)
4897 .map_err(stage_err)?;
4898 task_shader_module.same_device(self)?;
4899
4900 if task_shader_module_state.interface.interface().is_none() {
4901 passthrough_stages |= stage_bit;
4902 }
4903
4904 _task_entry_point_name = task_shader_module
4905 .finalize_entry_point_name(
4906 stage.to_naga(),
4907 stage_desc.entry_point.as_ref().map(|ep| ep.as_ref()),
4908 )
4909 .map_err(stage_err)?;
4910
4911 if let Some(interface) = task_shader_module_state.interface.interface() {
4912 immediate_slots_required |= interface
4913 .immediate_slots_required(stage.to_naga(), &_task_entry_point_name);
4914 io = interface
4915 .check_stage(
4916 &mut binding_layout_source,
4917 &mut shader_binding_sizes,
4918 &_task_entry_point_name,
4919 stage,
4920 io,
4921 Some(desc.primitive.topology),
4922 )
4923 .map_err(stage_err)?;
4924 validated_stages |= stage_bit;
4925 }
4926 Some(hal::ProgrammableStage {
4927 module: task_shader_module_state.raw.as_ref(),
4928 entry_point: &_task_entry_point_name,
4929 constants: &stage_desc.constants,
4930 zero_initialize_workgroup_memory: stage_desc
4931 .zero_initialize_workgroup_memory,
4932 })
4933 } else {
4934 None
4935 };
4936 mesh_stage = {
4937 let stage_desc = &mesh.stage;
4938 let stage = validation::ShaderStageForValidation::Mesh;
4939 let stage_bit = stage.to_wgt_bit();
4940 let stage_err = |error| pipeline::CreateRenderPipelineError::Stage {
4941 stage: stage_bit,
4942 error,
4943 };
4944
4945 let mesh_shader_module = &stage_desc.module;
4946 let mesh_shader_module_state = mesh_shader_module
4947 .state()
4948 .map_err(Into::into)
4949 .map_err(stage_err)?;
4950 mesh_shader_module.same_device(self)?;
4951
4952 if mesh_shader_module_state.interface.interface().is_none() {
4953 passthrough_stages |= stage_bit;
4954 }
4955
4956 _mesh_entry_point_name = mesh_shader_module
4957 .finalize_entry_point_name(
4958 stage.to_naga(),
4959 stage_desc.entry_point.as_ref().map(|ep| ep.as_ref()),
4960 )
4961 .map_err(stage_err)?;
4962
4963 if let Some(interface) = mesh_shader_module_state.interface.interface() {
4964 immediate_slots_required |= interface
4965 .immediate_slots_required(stage.to_naga(), &_mesh_entry_point_name);
4966 io = interface
4967 .check_stage(
4968 &mut binding_layout_source,
4969 &mut shader_binding_sizes,
4970 &_mesh_entry_point_name,
4971 stage,
4972 io,
4973 Some(desc.primitive.topology),
4974 )
4975 .map_err(stage_err)?;
4976 validated_stages |= stage_bit;
4977 }
4978 Some(hal::ProgrammableStage {
4979 module: mesh_shader_module_state.raw.as_ref(),
4980 entry_point: &_mesh_entry_point_name,
4981 constants: &stage_desc.constants,
4982 zero_initialize_workgroup_memory: stage_desc
4983 .zero_initialize_workgroup_memory,
4984 })
4985 };
4986 }
4987 }
4988
4989 let fragment_entry_point_name;
4990 let fragment_stage = match desc.fragment {
4991 Some(ref fragment_state) => {
4992 let stage = validation::ShaderStageForValidation::Fragment {
4993 dual_source_blending,
4994 has_depth_attachment,
4995 };
4996 let stage_bit = stage.to_wgt_bit();
4997 let stage_err = |error| pipeline::CreateRenderPipelineError::Stage {
4998 stage: stage_bit,
4999 error,
5000 };
5001
5002 let shader_module = &fragment_state.stage.module;
5003 let shader_module_state = shader_module
5004 .state()
5005 .map_err(Into::into)
5006 .map_err(stage_err)?;
5007 shader_module.same_device(self)?;
5008
5009 if shader_module_state.interface.interface().is_none() {
5010 passthrough_stages |= stage_bit;
5011 }
5012
5013 fragment_entry_point_name = shader_module
5014 .finalize_entry_point_name(
5015 stage.to_naga(),
5016 fragment_state
5017 .stage
5018 .entry_point
5019 .as_ref()
5020 .map(|ep| ep.as_ref()),
5021 )
5022 .map_err(stage_err)?;
5023
5024 if let Some(interface) = shader_module_state.interface.interface() {
5025 immediate_slots_required |= interface
5026 .immediate_slots_required(stage.to_naga(), &fragment_entry_point_name);
5027 io = interface
5028 .check_stage(
5029 &mut binding_layout_source,
5030 &mut shader_binding_sizes,
5031 &fragment_entry_point_name,
5032 stage,
5033 io,
5034 Some(desc.primitive.topology),
5035 )
5036 .map_err(stage_err)?;
5037 validated_stages |= stage_bit;
5038 }
5039
5040 Some(hal::ProgrammableStage {
5041 module: shader_module_state.raw.as_ref(),
5042 entry_point: &fragment_entry_point_name,
5043 constants: &fragment_state.stage.constants,
5044 zero_initialize_workgroup_memory: fragment_state
5045 .stage
5046 .zero_initialize_workgroup_memory,
5047 })
5048 }
5049 None => None,
5050 };
5051
5052 if !passthrough_stages.is_empty() && is_auto_layout {
5053 return Err(pipeline::CreateRenderPipelineError::Implicit(
5054 pipeline::ImplicitLayoutError::Passthrough(passthrough_stages),
5055 ));
5056 }
5057
5058 if validated_stages.contains(wgt::ShaderStages::FRAGMENT) {
5059 for (i, output) in io.varyings.iter() {
5060 match color_targets.get(*i as usize) {
5061 Some(Some(state)) => {
5062 validation::check_texture_format(state.format, &output.ty).map_err(
5063 |pipeline| {
5064 pipeline::CreateRenderPipelineError::ColorState(
5065 *i as u8,
5066 ColorStateError::IncompatibleFormat {
5067 pipeline,
5068 shader: output.ty,
5069 },
5070 )
5071 },
5072 )?;
5073 }
5074 _ => {
5075 log::debug!(
5076 "The fragment stage {:?} output @location({}) values are ignored",
5077 fragment_stage
5078 .as_ref()
5079 .map_or("", |stage| stage.entry_point),
5080 i
5081 );
5082 }
5083 }
5084 }
5085 }
5086 let last_stage = match desc.fragment {
5087 Some(_) => wgt::ShaderStages::FRAGMENT,
5088 None => wgt::ShaderStages::VERTEX,
5089 };
5090 if is_auto_layout && !validated_stages.contains(last_stage) {
5091 return Err(pipeline::ImplicitLayoutError::ReflectionError(last_stage).into());
5092 }
5093
5094 let pipeline_layout = match binding_layout_source {
5095 validation::BindingLayoutSource::Provided(pipeline_layout) => pipeline_layout,
5096 validation::BindingLayoutSource::Derived(entries) => {
5097 let immediate_size = {
5098 let immediate_size_of = |sm: &pipeline::ShaderModule| {
5099 sm.state()
5100 .expect("Should be validated above")
5101 .interface
5102 .interface()
5103 .map(|i| i.immediate_size)
5104 };
5105 let vertex = match desc.vertex {
5106 pipeline::RenderPipelineVertexProcessor::Vertex(ref v) => {
5107 immediate_size_of(&v.stage.module)
5108 }
5109 pipeline::RenderPipelineVertexProcessor::Mesh(ref task, ref mesh) => task
5110 .as_ref()
5111 .and_then(|t| immediate_size_of(&t.stage.module))
5112 .max(immediate_size_of(&mesh.stage.module)),
5113 };
5114 let fragment = desc
5115 .fragment
5116 .as_ref()
5117 .and_then(|f| immediate_size_of(&f.stage.module));
5118 vertex.max(fragment).unwrap_or(0)
5119 };
5120 self.create_derived_pipeline_layout(entries, immediate_size)?
5121 }
5122 };
5123
5124 if let pipeline::RenderPipelineVertexProcessor::Vertex(ref vertex) = desc.vertex {
5125 let bind_groups_plus_vertex_buffers =
5126 u32::try_from(pipeline_layout.bind_group_layouts.len() + vertex.buffers.len())
5127 .unwrap();
5128 if bind_groups_plus_vertex_buffers > self.limits.max_bind_groups_plus_vertex_buffers {
5129 return Err(
5130 pipeline::CreateRenderPipelineError::TooManyBindGroupsPlusVertexBuffers {
5131 given: bind_groups_plus_vertex_buffers,
5132 limit: self.limits.max_bind_groups_plus_vertex_buffers,
5133 },
5134 );
5135 }
5136
5137 let given = pipeline_layout
5138 .buffers_and_acceleration_structures_in_vertex_stage
5139 .saturating_add(vertex.buffers.len() as u32);
5140 if !self
5141 .instance_flags
5142 .contains(wgt::InstanceFlags::STRICT_WEBGPU_COMPLIANCE)
5143 {
5144 let limit = self
5145 .limits
5146 .max_buffers_and_acceleration_structures_per_shader_stage;
5147 if given > limit {
5148 return Err(
5149 pipeline::CreateRenderPipelineError::TooManyBuffersAndAccelerationStructuresInVertexStage {
5150 given,
5151 limit,
5152 },
5153 );
5154 }
5155 }
5156 }
5157
5158 if let Some(mv_mask) = desc.multiview_mask {
5160 self.require_features(wgt::Features::MULTIVIEW)?;
5161 if !(mv_mask.get() + 1).is_power_of_two() {
5162 self.require_features(wgt::Features::SELECTIVE_MULTIVIEW)?;
5163 }
5164 }
5165
5166 if !self
5167 .downlevel
5168 .flags
5169 .contains(wgt::DownlevelFlags::BUFFER_BINDINGS_NOT_16_BYTE_ALIGNED)
5170 {
5171 for (binding, size) in shader_binding_sizes.iter() {
5172 if size.get() % 16 != 0 {
5173 return Err(pipeline::CreateRenderPipelineError::UnalignedShader {
5174 binding: binding.binding,
5175 group: binding.group,
5176 size: size.get(),
5177 });
5178 }
5179 }
5180 }
5181
5182 let late_sized_buffer_groups =
5183 Device::make_late_sized_buffer_groups(&shader_binding_sizes, &pipeline_layout);
5184
5185 let cache = match desc.cache {
5186 Some(cache) => {
5187 cache.check_is_valid()?;
5188 cache.same_device(self)?;
5189 Some(cache)
5190 }
5191 None => None,
5192 };
5193
5194 let is_mesh = mesh_stage.is_some();
5195 let has_task_shader = task_stage.is_some();
5196 let raw = {
5197 let pipeline_desc = hal::RenderPipelineDescriptor {
5198 label: desc.label.to_hal(self.instance_flags),
5199 layout: pipeline_layout.raw()?,
5200 vertex_processor: match vertex_stage {
5201 Some(vertex_stage) => hal::VertexProcessor::Standard {
5202 vertex_buffers: &hal_vertex_buffer_layouts,
5203 vertex_stage,
5204 },
5205 None => hal::VertexProcessor::Mesh {
5206 task_stage,
5207 mesh_stage: mesh_stage.unwrap(),
5208 },
5209 },
5210 primitive: desc.primitive,
5211 depth_stencil: desc.depth_stencil.clone(),
5212 multisample: desc.multisample,
5213 fragment_stage,
5214 color_targets,
5215 multiview_mask: desc.multiview_mask,
5216 cache: cache.as_ref().map(|it| it.raw()).transpose()?,
5217 };
5218 unsafe { self.raw().create_render_pipeline(&pipeline_desc) }.map_err(
5219 |err| match err {
5220 hal::PipelineError::Device(error) => {
5221 pipeline::CreateRenderPipelineError::Device(self.handle_hal_error(error))
5222 }
5223 hal::PipelineError::Linkage(stage, msg) => {
5224 pipeline::CreateRenderPipelineError::Internal { stage, error: msg }
5225 }
5226 hal::PipelineError::EntryPoint(stage) => {
5227 pipeline::CreateRenderPipelineError::Internal {
5228 stage: hal::auxil::map_naga_stage(stage),
5229 error: ENTRYPOINT_FAILURE_ERROR.to_string(),
5230 }
5231 }
5232 hal::PipelineError::PipelineConstants(stage, error) => {
5233 pipeline::CreateRenderPipelineError::PipelineConstants { stage, error }
5234 }
5235 },
5236 )?
5237 };
5238
5239 let pass_context = RenderPassContext {
5240 attachments: AttachmentData {
5241 colors: color_targets
5242 .iter()
5243 .map(|state| state.as_ref().map(|s| s.format))
5244 .collect(),
5245 resolves: ArrayVec::new(),
5246 depth_stencil: depth_stencil_state.as_ref().map(|state| state.format),
5247 },
5248 sample_count: samples,
5249 multiview_mask: desc.multiview_mask,
5250 };
5251
5252 let mut flags = pipeline::PipelineFlags::empty();
5253 for state in color_targets.iter().filter_map(|s| s.as_ref()) {
5254 if let Some(ref bs) = state.blend {
5255 if bs.color.uses_constant() | bs.alpha.uses_constant() {
5256 flags |= pipeline::PipelineFlags::BLEND_CONSTANT;
5257 }
5258 }
5259 }
5260 if let Some(ds) = depth_stencil_state.as_ref() {
5261 if ds.stencil.is_enabled() && ds.stencil.needs_ref_value() {
5262 flags |= pipeline::PipelineFlags::STENCIL_REFERENCE;
5263 }
5264 if !ds.is_depth_read_only() {
5265 flags |= pipeline::PipelineFlags::WRITES_DEPTH;
5266 }
5267 if !ds.is_stencil_read_only(desc.primitive.cull_mode) {
5268 flags |= pipeline::PipelineFlags::WRITES_STENCIL;
5269 }
5270 }
5271 let shader_modules = {
5272 let mut shader_modules = ArrayVec::new();
5273 match desc.vertex {
5274 pipeline::RenderPipelineVertexProcessor::Vertex(vertex) => {
5275 shader_modules.push(vertex.stage.module)
5276 }
5277 pipeline::RenderPipelineVertexProcessor::Mesh(task, mesh) => {
5278 if let Some(task) = task {
5279 shader_modules.push(task.stage.module);
5280 }
5281 shader_modules.push(mesh.stage.module);
5282 }
5283 }
5284 shader_modules.extend(desc.fragment.map(|f| f.stage.module));
5285 shader_modules
5286 };
5287
5288 let pipeline = pipeline::RenderPipeline {
5289 state: ResourceState::Valid(pipeline::RenderPipelineState {
5290 raw: ManuallyDrop::new(raw),
5291 layout: pipeline_layout.clone(),
5292 }),
5293 device: self.clone(),
5294 pass_context,
5295 _shader_modules: shader_modules,
5296 flags,
5297 topology: desc.primitive.topology,
5298 strip_index_format: desc.primitive.strip_index_format,
5299 vertex_steps,
5300 late_sized_buffer_groups,
5301 immediate_slots_required,
5302 label: desc.label.to_string(),
5303 tracking_data: TrackingData::new(self.tracker_indices.render_pipelines.clone()),
5304 is_mesh,
5305 has_task_shader,
5306 };
5307
5308 let pipeline = Arc::new(pipeline);
5309
5310 if is_auto_layout {
5311 for bgl in pipeline_layout.bind_group_layouts.iter() {
5312 let Some(bgl) = bgl else {
5313 continue;
5314 };
5315
5316 let _ = bgl.exclusive_pipeline.set((&pipeline).into());
5319 }
5320 }
5321
5322 Ok(pipeline)
5323 }
5324
5325 pub unsafe fn create_pipeline_cache(
5329 self: &Arc<Self>,
5330 desc: &pipeline::PipelineCacheDescriptor,
5331 ) -> (
5332 Arc<pipeline::PipelineCache>,
5333 Option<pipeline::CreatePipelineCacheError>,
5334 ) {
5335 let (cache, error) = match unsafe { self.create_pipeline_cache_inner(desc) } {
5336 Ok(cache) => (cache, None),
5337 Err(e) => (
5338 pipeline::PipelineCache::invalid(self.clone(), desc),
5339 Some(e),
5340 ),
5341 };
5342 #[cfg(feature = "trace")]
5343 if let Some(ref mut trace) = *self.trace.lock() {
5344 use trace::IntoTrace;
5345 trace.add(trace::Action::CreatePipelineCache {
5346 id: cache.to_trace(),
5347 desc: desc.clone(),
5348 });
5349 }
5350 api_log!("Device::create_pipeline_cache -> {:?}", Arc::as_ptr(&cache));
5351 (cache, error)
5352 }
5353
5354 pub(crate) unsafe fn create_pipeline_cache_inner(
5358 self: &Arc<Self>,
5359 desc: &pipeline::PipelineCacheDescriptor,
5360 ) -> Result<Arc<pipeline::PipelineCache>, pipeline::CreatePipelineCacheError> {
5361 use crate::pipeline_cache;
5362
5363 self.check_is_valid()?;
5364
5365 self.require_features(wgt::Features::PIPELINE_CACHE)?;
5366 let data = if let Some((data, validation_key)) = desc
5367 .data
5368 .as_ref()
5369 .zip(self.raw().pipeline_cache_validation_key())
5370 {
5371 let data = pipeline_cache::validate_pipeline_cache(
5372 data,
5373 &self.adapter.raw.info,
5374 validation_key,
5375 );
5376 match data {
5377 Ok(data) => Some(data),
5378 Err(e) if e.was_avoidable() || !desc.fallback => return Err(e.into()),
5379 Err(_) => None,
5381 }
5382 } else {
5383 None
5384 };
5385 let cache_desc = hal::PipelineCacheDescriptor {
5386 data,
5387 label: desc.label.to_hal(self.instance_flags),
5388 };
5389 let raw = match unsafe { self.raw().create_pipeline_cache(&cache_desc) } {
5390 Ok(raw) => raw,
5391 Err(e) => match e {
5392 hal::PipelineCacheError::Device(e) => return Err(self.handle_hal_error(e).into()),
5393 },
5394 };
5395 let cache = pipeline::PipelineCache {
5396 device: self.clone(),
5397 label: desc.label.to_string(),
5398 raw: ResourceState::Valid(raw),
5400 };
5401
5402 let cache = Arc::new(cache);
5403
5404 Ok(cache)
5405 }
5406
5407 fn get_texture_format_features(&self, format: TextureFormat) -> wgt::TextureFormatFeatures {
5408 use wgt::TextureFormatFeatureFlags as tfsc;
5410 let mut format_features = self.adapter.get_texture_format_features(format);
5411 if (format == TextureFormat::R32Float
5412 || format == TextureFormat::Rg32Float
5413 || format == TextureFormat::Rgba32Float)
5414 && !self.features.contains(wgt::Features::FLOAT32_FILTERABLE)
5415 {
5416 format_features.flags.set(tfsc::FILTERABLE, false);
5417 }
5418 format_features
5419 }
5420
5421 pub(crate) fn describe_format_features(
5422 &self,
5423 format: TextureFormat,
5424 ) -> Result<wgt::TextureFormatFeatures, MissingFeatures> {
5425 self.require_features(format.required_features())?;
5426
5427 let using_device_features = self
5428 .features
5429 .contains(wgt::Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES);
5430 let downlevel = !self
5433 .downlevel
5434 .flags
5435 .contains(wgt::DownlevelFlags::WEBGPU_TEXTURE_FORMAT_SUPPORT);
5436
5437 if using_device_features || downlevel {
5438 Ok(self.get_texture_format_features(format))
5439 } else {
5440 Ok(format.guaranteed_format_features(self.features))
5441 }
5442 }
5443
5444 #[cfg(feature = "replay")]
5445 pub(crate) fn wait_for_submit(
5446 &self,
5447 submission_index: crate::SubmissionIndex,
5448 ) -> Result<(), DeviceError> {
5449 let last_done_index = unsafe { self.raw().get_fence_value(self.fence.as_ref()) }
5450 .map_err(|e| self.handle_hal_error(e))?;
5451 if last_done_index < submission_index {
5452 unsafe { self.raw().wait(self.fence.as_ref(), submission_index, None) }
5453 .map_err(|e| self.handle_hal_error(e))?;
5454 if let Some(queue) = self.get_queue() {
5455 let closures = queue.lock_life().triage_submissions(submission_index);
5456 assert!(
5457 closures.is_empty(),
5458 "wait_for_submit is not expected to work with closures"
5459 );
5460 }
5461 }
5462 Ok(())
5463 }
5464
5465 pub fn create_query_set(
5466 self: &Arc<Self>,
5467 desc: &resource::QuerySetDescriptor,
5468 ) -> (Arc<QuerySet>, Option<resource::CreateQuerySetError>) {
5469 let (query_set, error) = match self.create_query_set_inner(desc) {
5470 Ok(query_set) => (query_set, None),
5471 Err(e) => (QuerySet::invalid(Arc::clone(self), desc), Some(e)),
5472 };
5473 #[cfg(feature = "trace")]
5474 if let Some(ref mut trace) = *self.trace.lock() {
5475 use trace::IntoTrace;
5476 trace.add(trace::Action::CreateQuerySet {
5477 id: query_set.to_trace(),
5478 desc: desc.clone(),
5479 });
5480 }
5481 api_log!("Device::create_query_set -> {:?}", Arc::as_ptr(&query_set));
5482 (query_set, error)
5483 }
5484
5485 pub(crate) fn create_query_set_inner(
5486 self: &Arc<Self>,
5487 desc: &resource::QuerySetDescriptor,
5488 ) -> Result<Arc<QuerySet>, resource::CreateQuerySetError> {
5489 use resource::CreateQuerySetError as Error;
5490
5491 self.check_is_valid()?;
5492
5493 match desc.ty {
5494 wgt::QueryType::Occlusion => {}
5495 wgt::QueryType::Timestamp => {
5496 self.require_features(wgt::Features::TIMESTAMP_QUERY)?;
5497 }
5498 wgt::QueryType::PipelineStatistics(..) => {
5499 self.require_features(wgt::Features::PIPELINE_STATISTICS_QUERY)?;
5500 }
5501 }
5502
5503 if desc.count == 0 {
5504 return Err(Error::ZeroCount);
5505 }
5506
5507 if desc.count > wgt::QUERY_SET_MAX_QUERIES {
5508 return Err(Error::TooManyQueries {
5509 count: desc.count,
5510 maximum: wgt::QUERY_SET_MAX_QUERIES,
5511 });
5512 }
5513
5514 let hal_desc = desc.map_label(|label| label.to_hal(self.instance_flags));
5515
5516 let raw = unsafe { self.raw().create_query_set(&hal_desc) }
5517 .map_err(|e| self.handle_hal_error_with_nonfatal_oom(e))?;
5518
5519 let query_set = QuerySet {
5520 state: ResourceState::Valid(QuerySetState {
5521 raw: Snatchable::new(raw),
5522 }),
5523 device: self.clone(),
5524 label: desc.label.to_string(),
5525 tracking_data: TrackingData::new(self.tracker_indices.query_sets.clone()),
5526 desc: desc.map_label(|_| ()),
5527 initialized_slots: Mutex::new(
5528 rank::QUERY_SET_INITIALIZED_SLOTS,
5529 bit_vec::BitVec::from_elem(desc.count as usize, false),
5530 ),
5531 };
5532
5533 let query_set = Arc::new(query_set);
5534
5535 Ok(query_set)
5536 }
5537
5538 pub fn configure_surface(
5539 self: &Arc<Self>,
5540 surface: &crate::instance::Surface,
5541 config: &wgt::SurfaceConfiguration<Vec<TextureFormat>>,
5542 ) -> Option<present::ConfigureSurfaceError> {
5543 use present::ConfigureSurfaceError as E;
5544 profiling::scope!("surface_configure");
5545
5546 log::debug!("configuring surface with {config:?}");
5547
5548 let error = 'error: {
5549 let user_callbacks;
5551 {
5552 if let Err(e) = self.check_is_valid() {
5553 break 'error e.into();
5554 }
5555
5556 let caps = match surface.get_capabilities(&self.adapter) {
5557 Ok(caps) => caps,
5558 Err(_) => break 'error E::UnsupportedQueueFamily,
5559 };
5560
5561 let mut hal_view_formats = Vec::new();
5562 for format in config.view_formats.iter() {
5563 if *format == config.format {
5564 continue;
5565 }
5566 if !caps.formats.iter().any(|fc| fc.format == config.format) {
5567 break 'error E::UnsupportedFormat {
5568 requested: config.format,
5569 available: caps.texture_formats().collect(),
5570 };
5571 }
5572 if config.format.remove_srgb_suffix() != format.remove_srgb_suffix() {
5573 break 'error E::InvalidViewFormat(*format, config.format);
5574 }
5575 hal_view_formats.push(*format);
5576 }
5577
5578 if !hal_view_formats.is_empty() {
5579 if let Err(missing_flag) =
5580 self.require_downlevel_flags(wgt::DownlevelFlags::SURFACE_VIEW_FORMATS)
5581 {
5582 break 'error E::MissingDownlevelFlags(missing_flag);
5583 }
5584 }
5585
5586 let maximum_frame_latency = config.desired_maximum_frame_latency.clamp(
5587 *caps.maximum_frame_latency.start(),
5588 *caps.maximum_frame_latency.end(),
5589 );
5590 let mut hal_config = hal::SurfaceConfiguration {
5591 maximum_frame_latency,
5592 present_mode: config.present_mode,
5593 composite_alpha_mode: config.alpha_mode,
5594 format: config.format,
5595 color_space: config.color_space,
5596 extent: wgt::Extent3d {
5597 width: config.width,
5598 height: config.height,
5599 depth_or_array_layers: 1,
5600 },
5601 usage: conv::map_texture_usage(
5602 config.usage,
5603 hal::FormatAspects::COLOR,
5604 wgt::TextureFormatFeatureFlags::STORAGE_READ_ONLY
5605 | wgt::TextureFormatFeatureFlags::STORAGE_WRITE_ONLY
5606 | wgt::TextureFormatFeatureFlags::STORAGE_READ_WRITE,
5607 ),
5608 view_formats: hal_view_formats,
5609 };
5610
5611 if let Err(error) = validate_surface_configuration(
5612 &mut hal_config,
5613 &caps,
5614 self.limits.max_texture_dimension_2d,
5615 ) {
5616 break 'error error;
5617 }
5618
5619 let snatch_guard = self.snatchable_lock.read();
5621
5622 let maintain_result;
5623 (user_callbacks, maintain_result) =
5624 self.maintain(wgt::PollType::wait_indefinitely(), snatch_guard);
5625
5626 match maintain_result {
5627 Ok(wgt::PollStatus::QueueEmpty) => {}
5629 Ok(wgt::PollStatus::WaitSucceeded) => {
5630 break 'error E::GpuWaitTimeout;
5633 }
5634 Ok(wgt::PollStatus::Poll) => {
5635 unreachable!("Cannot get a Poll result from a Wait action.")
5636 }
5637 Err(WaitIdleError::Timeout) if cfg!(target_arch = "wasm32") => {
5638 }
5643 Err(e) => {
5644 break 'error e.into();
5645 }
5646 }
5647
5648 if let Some(present) = surface.presentation.lock().take() {
5650 if present.acquired_texture.is_some() {
5651 break 'error E::PreviousOutputExists;
5652 }
5653 }
5654
5655 let surface_raw = surface.raw(self.backend()).unwrap();
5662 match unsafe { surface_raw.configure(self.raw(), &hal_config) } {
5663 Ok(()) => (),
5664 Err(error) => {
5665 break 'error match error {
5666 hal::SurfaceError::Outdated
5667 | hal::SurfaceError::Lost
5668 | hal::SurfaceError::Occluded
5669 | hal::SurfaceError::Timeout => E::InvalidSurface,
5670 hal::SurfaceError::Device(error) => {
5671 E::Device(self.handle_hal_error(error))
5672 }
5673 hal::SurfaceError::Other(message) => {
5674 log::error!("surface configuration failed: {message}");
5675 E::InvalidSurface
5676 }
5677 }
5678 }
5679 }
5680
5681 let mut presentation = surface.presentation.lock();
5682 *presentation = Some(present::Presentation {
5683 device: Arc::clone(self),
5684 config: config.clone(),
5685 acquired_texture: None,
5686 });
5687 }
5688
5689 user_callbacks.fire();
5690 return None;
5691 };
5692
5693 Some(error)
5694 }
5695
5696 fn lose(&self, message: &str) {
5697 self.valid.store(false, Ordering::Release);
5702
5703 if let Some(device_lost_closure) = self.device_lost_closure.lock().take() {
5705 device_lost_closure(DeviceLostReason::Unknown, message.to_string());
5706 }
5707
5708 }
5716
5717 fn release_gpu_resources(&self) {
5718 let trackers = self.trackers.lock();
5728 for buffer in trackers.buffers.used_resources() {
5729 if let Some(buffer) = Weak::upgrade(buffer) {
5730 buffer.destroy();
5731 }
5732 }
5733 for texture in trackers.textures.used_resources() {
5734 if let Some(texture) = Weak::upgrade(texture) {
5735 texture.destroy();
5736 }
5737 }
5738 }
5739
5740 pub(crate) fn new_usage_scope(&self) -> UsageScope<'_> {
5741 UsageScope::new_pooled(
5742 &self.usage_scopes,
5743 &self.tracker_indices,
5744 self.ordered_buffer_usages,
5745 self.ordered_texture_usages,
5746 )
5747 }
5748
5749 pub fn get_hal_counters(&self) -> wgt::HalCounters {
5750 self.raw().get_internal_counters()
5751 }
5752
5753 pub fn generate_allocator_report(&self) -> Option<wgt::AllocatorReport> {
5754 self.raw().generate_allocator_report()
5755 }
5756}
5757
5758crate::impl_resource_type!(Device);
5759crate::impl_labeled!(Device);
5760crate::impl_storage_item!(Device);