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