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