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