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