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