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