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