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