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