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