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