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