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 fn create_external_texture_binding<'a>(
3301 &'a self,
3302 binding: u32,
3303 decl: &wgt::BindGroupLayoutEntry,
3304 external_texture: &'a Arc<ExternalTexture>,
3305 used: &mut BindGroupStates,
3306 snatch_guard: &'a SnatchGuard,
3307 ) -> Result<
3308 hal::ExternalTextureBinding<'a, dyn hal::DynBuffer, dyn hal::DynTextureView>,
3309 CreateBindGroupError,
3310 > {
3311 use crate::binding_model::CreateBindGroupError as Error;
3312
3313 external_texture.same_device(self)?;
3314
3315 used.external_textures
3316 .insert_single(external_texture.clone());
3317
3318 match decl.ty {
3319 wgt::BindingType::ExternalTexture => {}
3320 _ => {
3321 return Err(Error::WrongBindingType {
3322 binding,
3323 actual: decl.ty,
3324 expected: "ExternalTexture",
3325 });
3326 }
3327 }
3328
3329 let planes = (0..3)
3330 .map(|i| {
3331 let plane = external_texture
3335 .planes
3336 .get(i)
3337 .unwrap_or(&external_texture.planes[0]);
3338 let internal_use = wgt::TextureUses::RESOURCE;
3339 used.views.insert_single(plane.clone(), internal_use);
3340 let view = plane.try_raw(snatch_guard)?;
3341 Ok(hal::TextureBinding {
3342 view,
3343 usage: internal_use,
3344 })
3345 })
3346 .collect::<Result<Vec<_>, Error>>()?;
3349 let planes = planes.try_into().unwrap();
3350
3351 used.buffers
3352 .insert_single(external_texture.params.clone(), wgt::BufferUses::UNIFORM);
3353 let params = external_texture.params.binding(0, None, snatch_guard)?.0;
3354
3355 Ok(hal::ExternalTextureBinding { planes, params })
3356 }
3357
3358 fn create_external_texture_binding_from_view<'a>(
3359 &'a self,
3360 binding: u32,
3361 decl: &wgt::BindGroupLayoutEntry,
3362 view: &'a Arc<TextureView>,
3363 used: &mut BindGroupStates,
3364 snatch_guard: &'a SnatchGuard,
3365 ) -> Result<
3366 hal::ExternalTextureBinding<'a, dyn hal::DynBuffer, dyn hal::DynTextureView>,
3367 CreateBindGroupError,
3368 > {
3369 use crate::binding_model::CreateBindGroupError as Error;
3370
3371 view.same_device(self)?;
3372
3373 let internal_use = self.texture_use_parameters(binding, decl, view, "SampledTexture")?;
3374 used.views.insert_single(view.clone(), internal_use);
3375
3376 match decl.ty {
3377 wgt::BindingType::ExternalTexture => {}
3378 _ => {
3379 return Err(Error::WrongBindingType {
3380 binding,
3381 actual: decl.ty,
3382 expected: "ExternalTexture",
3383 });
3384 }
3385 }
3386
3387 let planes = [
3389 hal::TextureBinding {
3390 view: view.try_raw(snatch_guard)?,
3391 usage: internal_use,
3392 },
3393 hal::TextureBinding {
3394 view: view.try_raw(snatch_guard)?,
3395 usage: internal_use,
3396 },
3397 hal::TextureBinding {
3398 view: view.try_raw(snatch_guard)?,
3399 usage: internal_use,
3400 },
3401 ];
3402 let params = hal::BufferBinding::new_unchecked(
3403 self.default_external_texture_params_buffer.as_ref(),
3404 0,
3405 None,
3406 );
3407
3408 Ok(hal::ExternalTextureBinding { planes, params })
3409 }
3410
3411 pub fn create_bind_group(
3414 self: &Arc<Self>,
3415 desc: binding_model::ResolvedBindGroupDescriptor,
3416 ) -> Result<Arc<BindGroup>, CreateBindGroupError> {
3417 use crate::binding_model::{CreateBindGroupError as Error, ResolvedBindingResource as Br};
3418
3419 let layout = desc.layout;
3420
3421 self.check_is_valid()?;
3422 layout.same_device(self)?;
3423 layout.check_is_valid()?;
3424
3425 {
3426 let actual = desc.entries.len();
3429 let expected = layout.entries.len();
3430 if actual != expected {
3431 return Err(Error::BindingsNumMismatch { expected, actual });
3432 }
3433 }
3434
3435 let mut dynamic_binding_info = Vec::new();
3438 let mut late_buffer_binding_sizes = FastHashMap::default();
3442 let mut used = BindGroupStates::new();
3444
3445 let mut buffer_init_actions = Vec::new();
3446 let mut texture_init_actions = Vec::new();
3447 let mut hal_entries = Vec::with_capacity(desc.entries.len());
3448 let mut hal_buffers = Vec::new();
3449 let mut hal_samplers = Vec::new();
3450 let mut hal_textures = Vec::new();
3451 let mut hal_tlas_s = Vec::new();
3452 let mut hal_external_textures = Vec::new();
3453 let snatch_guard = self.snatchable_lock.read();
3454 for entry in desc.entries.iter() {
3455 let binding = entry.binding;
3456 let decl = layout
3458 .entries
3459 .get(binding)
3460 .ok_or(Error::MissingBindingDeclaration(binding))?;
3461 let (res_index, count) = match entry.resource {
3462 Br::Buffer(ref bb) => {
3463 let bb = self.create_buffer_binding(
3464 bb,
3465 binding,
3466 decl,
3467 &mut buffer_init_actions,
3468 &mut dynamic_binding_info,
3469 &mut late_buffer_binding_sizes,
3470 &mut used,
3471 &snatch_guard,
3472 )?;
3473
3474 let res_index = hal_buffers.len();
3475 hal_buffers.push(bb);
3476 (res_index, 1)
3477 }
3478 Br::BufferArray(ref bindings_array) => {
3479 let num_bindings = bindings_array.len();
3480 Self::check_array_binding(self.features, decl.count, num_bindings)?;
3481
3482 let res_index = hal_buffers.len();
3483 for bb in bindings_array.iter() {
3484 let bb = self.create_buffer_binding(
3485 bb,
3486 binding,
3487 decl,
3488 &mut buffer_init_actions,
3489 &mut dynamic_binding_info,
3490 &mut late_buffer_binding_sizes,
3491 &mut used,
3492 &snatch_guard,
3493 )?;
3494 hal_buffers.push(bb);
3495 }
3496 (res_index, num_bindings)
3497 }
3498 Br::Sampler(ref sampler) => {
3499 let sampler = self.create_sampler_binding(&mut used, binding, decl, sampler)?;
3500
3501 let res_index = hal_samplers.len();
3502 hal_samplers.push(sampler);
3503 (res_index, 1)
3504 }
3505 Br::SamplerArray(ref samplers) => {
3506 let num_bindings = samplers.len();
3507 Self::check_array_binding(self.features, decl.count, num_bindings)?;
3508
3509 let res_index = hal_samplers.len();
3510 for sampler in samplers.iter() {
3511 let sampler =
3512 self.create_sampler_binding(&mut used, binding, decl, sampler)?;
3513
3514 hal_samplers.push(sampler);
3515 }
3516
3517 (res_index, num_bindings)
3518 }
3519 Br::TextureView(ref view) => match decl.ty {
3520 wgt::BindingType::ExternalTexture => {
3521 let et = self.create_external_texture_binding_from_view(
3522 binding,
3523 decl,
3524 view,
3525 &mut used,
3526 &snatch_guard,
3527 )?;
3528 let res_index = hal_external_textures.len();
3529 hal_external_textures.push(et);
3530 (res_index, 1)
3531 }
3532 _ => {
3533 let tb = self.create_texture_binding(
3534 binding,
3535 decl,
3536 view,
3537 &mut used,
3538 &mut texture_init_actions,
3539 &snatch_guard,
3540 )?;
3541 let res_index = hal_textures.len();
3542 hal_textures.push(tb);
3543 (res_index, 1)
3544 }
3545 },
3546 Br::TextureViewArray(ref views) => {
3547 let num_bindings = views.len();
3548 Self::check_array_binding(self.features, decl.count, num_bindings)?;
3549
3550 let res_index = hal_textures.len();
3551 for view in views.iter() {
3552 let tb = self.create_texture_binding(
3553 binding,
3554 decl,
3555 view,
3556 &mut used,
3557 &mut texture_init_actions,
3558 &snatch_guard,
3559 )?;
3560
3561 hal_textures.push(tb);
3562 }
3563
3564 (res_index, num_bindings)
3565 }
3566 Br::AccelerationStructure(ref tlas) => {
3567 let tlas =
3568 self.create_tlas_binding(&mut used, binding, decl, tlas, &snatch_guard)?;
3569 let res_index = hal_tlas_s.len();
3570 hal_tlas_s.push(tlas);
3571 (res_index, 1)
3572 }
3573 Br::AccelerationStructureArray(ref tlas_array) => {
3574 let num_bindings = tlas_array.len();
3578 Self::check_array_binding(self.features, decl.count, num_bindings)?;
3579
3580 let res_index = hal_tlas_s.len();
3581 for tlas in tlas_array.iter() {
3582 let tlas = self.create_tlas_binding(
3583 &mut used,
3584 binding,
3585 decl,
3586 tlas,
3587 &snatch_guard,
3588 )?;
3589 hal_tlas_s.push(tlas);
3590 }
3591 (res_index, num_bindings)
3592 }
3593 Br::ExternalTexture(ref et) => {
3594 let et = self.create_external_texture_binding(
3595 binding,
3596 decl,
3597 et,
3598 &mut used,
3599 &snatch_guard,
3600 )?;
3601 let res_index = hal_external_textures.len();
3602 hal_external_textures.push(et);
3603 (res_index, 1)
3604 }
3605 };
3606
3607 hal_entries.push(hal::BindGroupEntry {
3608 binding,
3609 resource_index: res_index as u32,
3610 count: count as u32,
3611 });
3612 }
3613
3614 used.optimize();
3615
3616 hal_entries.sort_by_key(|entry| entry.binding);
3617 for (a, b) in hal_entries.iter().zip(hal_entries.iter().skip(1)) {
3618 if a.binding == b.binding {
3619 return Err(Error::DuplicateBinding(a.binding));
3620 }
3621 }
3622
3623 dynamic_binding_info.sort_by_key(|i| i.binding_idx);
3624
3625 let hal_desc = hal::BindGroupDescriptor {
3626 label: desc.label.to_hal(self.instance_flags),
3627 layout: layout.try_raw()?,
3628 entries: &hal_entries,
3629 buffers: &hal_buffers,
3630 samplers: &hal_samplers,
3631 textures: &hal_textures,
3632 acceleration_structures: &hal_tlas_s,
3633 external_textures: &hal_external_textures,
3634 };
3635 let raw = unsafe { self.raw().create_bind_group(&hal_desc) }
3636 .map_err(|e| self.handle_hal_error(e))?;
3637
3638 let late_buffer_binding_infos = layout
3640 .entries
3641 .indices()
3642 .flat_map(|binding| {
3643 let size = late_buffer_binding_sizes.get(&binding).cloned()?;
3644 Some(BindGroupLateBufferBindingInfo {
3645 binding_index: binding,
3646 size,
3647 })
3648 })
3649 .collect();
3650
3651 let bind_group = BindGroup {
3652 raw: Snatchable::new(raw),
3653 device: self.clone(),
3654 layout,
3655 label: desc.label.to_string(),
3656 tracking_data: TrackingData::new(self.tracker_indices.bind_groups.clone()),
3657 used,
3658 buffer_init_actions,
3659 texture_init_actions,
3660 dynamic_binding_info,
3661 late_buffer_binding_infos,
3662 };
3663
3664 let bind_group = Arc::new(bind_group);
3665
3666 let weak_ref = Arc::downgrade(&bind_group);
3667 for texture in bind_group.used.views.used_textures() {
3668 let mut bind_groups = texture.bind_groups.lock();
3669 bind_groups.push(weak_ref.clone());
3670 }
3671 for buffer in bind_group.used.buffers.used_resources() {
3672 let mut bind_groups = buffer.bind_groups.lock();
3673 bind_groups.push(weak_ref.clone());
3674 }
3675
3676 Ok(bind_group)
3677 }
3678
3679 fn check_array_binding(
3680 features: wgt::Features,
3681 count: Option<NonZeroU32>,
3682 num_bindings: usize,
3683 ) -> Result<(), CreateBindGroupError> {
3684 use super::binding_model::CreateBindGroupError as Error;
3685
3686 if let Some(count) = count {
3687 let count = count.get() as usize;
3688 if count < num_bindings {
3689 return Err(Error::BindingArrayPartialLengthMismatch {
3690 actual: num_bindings,
3691 expected: count,
3692 });
3693 }
3694 if count != num_bindings
3695 && !features.contains(wgt::Features::PARTIALLY_BOUND_BINDING_ARRAY)
3696 {
3697 return Err(Error::BindingArrayLengthMismatch {
3698 actual: num_bindings,
3699 expected: count,
3700 });
3701 }
3702 if num_bindings == 0 {
3703 return Err(Error::BindingArrayZeroLength);
3704 }
3705 } else {
3706 return Err(Error::SingleBindingExpected);
3707 };
3708
3709 Ok(())
3710 }
3711
3712 fn texture_use_parameters(
3713 &self,
3714 binding: u32,
3715 decl: &wgt::BindGroupLayoutEntry,
3716 view: &TextureView,
3717 expected: &'static str,
3718 ) -> Result<wgt::TextureUses, CreateBindGroupError> {
3719 use crate::binding_model::CreateBindGroupError as Error;
3720 if view
3721 .desc
3722 .aspects()
3723 .contains(hal::FormatAspects::DEPTH | hal::FormatAspects::STENCIL)
3724 {
3725 return Err(Error::DepthStencilAspect);
3726 }
3727 match decl.ty {
3728 wgt::BindingType::Texture {
3729 sample_type,
3730 view_dimension,
3731 multisampled,
3732 } => {
3733 use wgt::TextureSampleType as Tst;
3734 if multisampled != (view.samples != 1) {
3735 return Err(Error::InvalidTextureMultisample {
3736 binding,
3737 layout_multisampled: multisampled,
3738 view_samples: view.samples,
3739 });
3740 }
3741 let compat_sample_type = view
3742 .desc
3743 .format
3744 .sample_type(Some(view.desc.range.aspect), Some(self.features))
3745 .unwrap();
3746 match (sample_type, compat_sample_type) {
3747 (Tst::Uint, Tst::Uint) |
3748 (Tst::Sint, Tst::Sint) |
3749 (Tst::Depth, Tst::Depth) |
3750 (Tst::Float { filterable: false }, Tst::Float { .. }) |
3752 (Tst::Float { filterable: true }, Tst::Float { filterable: true }) |
3754 (Tst::Float { filterable: false }, Tst::Depth) => {}
3756 (Tst::Float { filterable: true }, Tst::Float { .. })
3761 if view.format_features.flags
3762 .contains(wgt::TextureFormatFeatureFlags::FILTERABLE) => {}
3763 _ => {
3764 return Err(Error::InvalidTextureSampleType {
3765 binding,
3766 layout_sample_type: sample_type,
3767 view_format: view.desc.format,
3768 view_sample_type: compat_sample_type,
3769 })
3770 }
3771 }
3772 if view_dimension != view.desc.dimension {
3773 return Err(Error::InvalidTextureDimension {
3774 binding,
3775 layout_dimension: view_dimension,
3776 view_dimension: view.desc.dimension,
3777 });
3778 }
3779 view.check_usage(wgt::TextureUsages::TEXTURE_BINDING)?;
3780 Ok(wgt::TextureUses::RESOURCE)
3781 }
3782 wgt::BindingType::StorageTexture {
3783 access,
3784 format,
3785 view_dimension,
3786 } => {
3787 if format != view.desc.format {
3788 return Err(Error::InvalidStorageTextureFormat {
3789 binding,
3790 layout_format: format,
3791 view_format: view.desc.format,
3792 });
3793 }
3794 if view_dimension != view.desc.dimension {
3795 return Err(Error::InvalidTextureDimension {
3796 binding,
3797 layout_dimension: view_dimension,
3798 view_dimension: view.desc.dimension,
3799 });
3800 }
3801
3802 let mip_level_count = view.selector.mips.end - view.selector.mips.start;
3803 if mip_level_count != 1 {
3804 return Err(Error::InvalidStorageTextureMipLevelCount {
3805 binding,
3806 mip_level_count,
3807 });
3808 }
3809
3810 view.check_usage(wgt::TextureUsages::STORAGE_BINDING)?;
3811
3812 Ok(match access {
3813 wgt::StorageTextureAccess::ReadOnly => wgt::TextureUses::STORAGE_READ_ONLY,
3814 wgt::StorageTextureAccess::WriteOnly => wgt::TextureUses::STORAGE_WRITE_ONLY,
3815 wgt::StorageTextureAccess::ReadWrite => wgt::TextureUses::STORAGE_READ_WRITE,
3816 wgt::StorageTextureAccess::Atomic => wgt::TextureUses::STORAGE_ATOMIC,
3817 })
3818 }
3819 wgt::BindingType::ExternalTexture => {
3820 if view.desc.dimension != TextureViewDimension::D2 {
3821 return Err(Error::InvalidTextureDimension {
3822 binding,
3823 layout_dimension: TextureViewDimension::D2,
3824 view_dimension: view.desc.dimension,
3825 });
3826 }
3827 let mip_level_count = view.selector.mips.end - view.selector.mips.start;
3828 if mip_level_count != 1 {
3829 return Err(Error::InvalidExternalTextureMipLevelCount {
3830 binding,
3831 mip_level_count,
3832 });
3833 }
3834 if view.desc.format != TextureFormat::Rgba8Unorm
3835 && view.desc.format != TextureFormat::Bgra8Unorm
3836 && view.desc.format != TextureFormat::Rgba16Float
3837 {
3838 return Err(Error::InvalidExternalTextureFormat {
3839 binding,
3840 format: view.desc.format,
3841 });
3842 }
3843 if view.samples != 1 {
3844 return Err(Error::InvalidTextureMultisample {
3845 binding,
3846 layout_multisampled: false,
3847 view_samples: view.samples,
3848 });
3849 }
3850
3851 view.check_usage(wgt::TextureUsages::TEXTURE_BINDING)?;
3852 Ok(wgt::TextureUses::RESOURCE)
3853 }
3854 _ => Err(Error::WrongBindingType {
3855 binding,
3856 actual: decl.ty,
3857 expected,
3858 }),
3859 }
3860 }
3861
3862 pub fn create_pipeline_layout(
3863 self: &Arc<Self>,
3864 desc: &binding_model::ResolvedPipelineLayoutDescriptor,
3865 ) -> (
3866 Arc<binding_model::PipelineLayout>,
3867 Option<binding_model::CreatePipelineLayoutError>,
3868 ) {
3869 let (layout, error) = match self.create_pipeline_layout_impl(desc, false) {
3870 Ok(layout) => (layout, None),
3871 Err(e) => (
3872 binding_model::PipelineLayout::invalid(Arc::clone(self), desc.label.to_string()),
3873 Some(e),
3874 ),
3875 };
3876 #[cfg(feature = "trace")]
3877 if let Some(ref mut trace) = *self.trace.lock() {
3878 use crate::device::trace::IntoTrace;
3879 trace.add(trace::Action::CreatePipelineLayout(
3880 layout.to_trace(),
3881 desc.to_trace(),
3882 ));
3883 }
3884 api_log!(
3885 "Device::create_pipeline_layout -> {:?}",
3886 Arc::as_ptr(&layout)
3887 );
3888 (layout, error)
3889 }
3890
3891 fn create_pipeline_layout_impl(
3892 self: &Arc<Self>,
3893 desc: &binding_model::ResolvedPipelineLayoutDescriptor,
3894 ignore_exclusive_pipeline_check: bool,
3895 ) -> Result<Arc<binding_model::PipelineLayout>, binding_model::CreatePipelineLayoutError> {
3896 use crate::binding_model::CreatePipelineLayoutError as Error;
3897
3898 self.check_is_valid()?;
3899
3900 let bind_group_layouts_count = desc.bind_group_layouts.len();
3901 let device_max_bind_groups = self.limits.max_bind_groups as usize;
3902 if bind_group_layouts_count > device_max_bind_groups {
3903 return Err(Error::TooManyGroups {
3904 actual: bind_group_layouts_count,
3905 max: device_max_bind_groups,
3906 });
3907 }
3908
3909 if desc.immediate_size != 0 {
3910 self.require_features(wgt::Features::IMMEDIATES)?;
3911 }
3912 if self.limits.max_immediate_size < desc.immediate_size {
3913 return Err(Error::ImmediateRangeTooLarge {
3914 size: desc.immediate_size,
3915 max: self.limits.max_immediate_size,
3916 });
3917 }
3918 if !desc
3919 .immediate_size
3920 .is_multiple_of(wgt::IMMEDIATE_DATA_ALIGNMENT)
3921 {
3922 return Err(Error::MisalignedImmediateSize {
3923 size: desc.immediate_size,
3924 });
3925 }
3926
3927 let mut count_validator = binding_model::BindingTypeMaxCountValidator::default();
3928
3929 for (index, bgl) in desc.bind_group_layouts.iter().enumerate() {
3930 let Some(bgl) = bgl else {
3931 continue;
3932 };
3933
3934 bgl.same_device(self)?;
3935
3936 if !ignore_exclusive_pipeline_check {
3937 let exclusive_pipeline = bgl.exclusive_pipeline.get().unwrap();
3938 if !matches!(exclusive_pipeline, binding_model::ExclusivePipeline::None) {
3939 return Err(Error::BglHasExclusivePipeline {
3940 index,
3941 pipeline: alloc::format!("{exclusive_pipeline}"),
3942 });
3943 }
3944 }
3945
3946 count_validator.merge(&bgl.state()?.binding_count_validator);
3947 }
3948
3949 count_validator
3950 .validate(&self.limits, self.instance_flags)
3951 .map_err(Error::TooManyBindings)?;
3952
3953 let buffers_and_acceleration_structures_in_vertex_stage =
3954 count_validator.buffers_and_acceleration_structures_in_vertex_stage();
3955
3956 let get_bgl_iter = || {
3957 desc.bind_group_layouts
3958 .iter()
3959 .map(|bgl| bgl.as_ref().filter(|bgl| !bgl.entries.is_empty()))
3960 };
3961
3962 let bind_group_layouts = get_bgl_iter()
3963 .map(|bgl| bgl.cloned())
3964 .collect::<ArrayVec<_, { hal::MAX_BIND_GROUPS }>>();
3965
3966 let raw_bind_group_layouts = get_bgl_iter()
3967 .map(|bgl| bgl.map(|bgl| bgl.try_raw()).transpose())
3968 .collect::<Result<ArrayVec<_, { hal::MAX_BIND_GROUPS }>, _>>()?;
3969
3970 let additional_flags = if self.indirect_validation.is_some() {
3971 hal::PipelineLayoutFlags::INDIRECT_BUILTIN_UPDATE
3972 } else {
3973 hal::PipelineLayoutFlags::empty()
3974 };
3975
3976 let hal_desc = hal::PipelineLayoutDescriptor {
3977 label: desc.label.to_hal(self.instance_flags),
3978 flags: hal::PipelineLayoutFlags::FIRST_VERTEX_INSTANCE
3979 | hal::PipelineLayoutFlags::NUM_WORK_GROUPS
3980 | additional_flags,
3981 bind_group_layouts: &raw_bind_group_layouts,
3982 immediate_size: desc.immediate_size,
3983 };
3984
3985 let raw = unsafe { self.raw().create_pipeline_layout(&hal_desc) }
3986 .map_err(|e| self.handle_hal_error(e))?;
3987
3988 drop(raw_bind_group_layouts);
3989
3990 let layout = binding_model::PipelineLayout {
3991 raw: ResourceState::Valid(raw),
3992 device: self.clone(),
3993 label: desc.label.to_string(),
3994 bind_group_layouts,
3995 immediate_size: desc.immediate_size,
3996 buffers_and_acceleration_structures_in_vertex_stage,
3997 };
3998
3999 let layout = Arc::new(layout);
4000
4001 Ok(layout)
4002 }
4003
4004 fn create_derived_pipeline_layout(
4005 self: &Arc<Self>,
4006 mut derived_group_layouts: Box<ArrayVec<bgl::EntryMap, { hal::MAX_BIND_GROUPS }>>,
4007 immediate_size: u32,
4008 ) -> Result<Arc<binding_model::PipelineLayout>, pipeline::ImplicitLayoutError> {
4009 while derived_group_layouts
4010 .last()
4011 .is_some_and(|map| map.is_empty())
4012 {
4013 derived_group_layouts.pop();
4014 }
4015
4016 let mut unique_bind_group_layouts = FastHashMap::default();
4017
4018 let bind_group_layouts = derived_group_layouts
4019 .into_iter()
4020 .map(|mut bgl_entry_map| {
4021 if bgl_entry_map.is_empty() {
4022 return Ok(None);
4023 }
4024
4025 bgl_entry_map.sort();
4026 match unique_bind_group_layouts.entry(bgl_entry_map) {
4027 hashbrown::hash_map::Entry::Occupied(v) => Ok(Some(Arc::clone(v.get()))),
4028 hashbrown::hash_map::Entry::Vacant(e) => {
4029 match self.create_bind_group_layout_impl(
4030 &None,
4031 e.key().clone(),
4032 bgl::Origin::Derived,
4033 ) {
4034 Ok(bgl) => {
4035 e.insert(bgl.clone());
4036 Ok(Some(bgl))
4037 }
4038 Err(e) => Err(e),
4039 }
4040 }
4041 }
4042 })
4043 .collect::<Result<Vec<_>, _>>()?;
4044
4045 let layout_desc = binding_model::ResolvedPipelineLayoutDescriptor {
4046 label: None,
4047 bind_group_layouts: Cow::Owned(bind_group_layouts),
4048 immediate_size,
4049 };
4050
4051 let layout = self.create_pipeline_layout_impl(&layout_desc, true)?;
4052 Ok(layout)
4053 }
4054
4055 pub fn create_compute_pipeline(
4056 self: &Arc<Self>,
4057 desc: pipeline::ResolvedComputePipelineDescriptor,
4058 ) -> (
4059 Arc<pipeline::ComputePipeline>,
4060 Option<pipeline::CreateComputePipelineError>,
4061 ) {
4062 let (compute_pipeline, error) = match self.create_compute_pipeline_inner(desc.clone()) {
4063 Ok(compute_pipeline) => (compute_pipeline, None),
4064 Err(error) => (
4065 pipeline::ComputePipeline::invalid(self.clone(), desc.label.to_string()),
4066 Some(error),
4067 ),
4068 };
4069 #[cfg(feature = "trace")]
4070 if let Some(ref mut trace) = *self.trace.lock() {
4071 use crate::device::trace;
4072 use crate::device::trace::IntoTrace;
4073 trace.add(trace::Action::CreateComputePipeline {
4074 id: compute_pipeline.to_trace(),
4075 desc: desc.to_trace(),
4076 });
4077 }
4078 (compute_pipeline, error)
4079 }
4080
4081 pub fn create_compute_pipeline_inner(
4082 self: &Arc<Self>,
4083 desc: pipeline::ResolvedComputePipelineDescriptor,
4084 ) -> Result<Arc<pipeline::ComputePipeline>, pipeline::CreateComputePipelineError> {
4085 self.check_is_valid()?;
4086
4087 self.require_downlevel_flags(wgt::DownlevelFlags::COMPUTE_SHADERS)?;
4088
4089 let shader_module = desc.stage.module;
4090
4091 shader_module.same_device(self)?;
4092
4093 let is_auto_layout = desc.layout.is_none();
4094
4095 let pipeline_layout = match desc.layout {
4097 Some(pipeline_layout) => {
4098 pipeline_layout.same_device(self)?;
4099 pipeline_layout.check_valid()?;
4100 Some(pipeline_layout)
4101 }
4102 None => None,
4103 };
4104
4105 let mut binding_layout_source = match pipeline_layout {
4106 Some(pipeline_layout) => validation::BindingLayoutSource::Provided(pipeline_layout),
4107 None => validation::BindingLayoutSource::new_derived(&self.limits),
4108 };
4109 let mut shader_binding_sizes = FastHashMap::default();
4110 let io = validation::StageIo::default();
4111
4112 let final_entry_point_name;
4113
4114 {
4115 let stage = validation::ShaderStageForValidation::Compute;
4116
4117 final_entry_point_name = shader_module.finalize_entry_point_name(
4118 stage.to_naga(),
4119 desc.stage.entry_point.as_ref().map(|ep| ep.as_ref()),
4120 )?;
4121
4122 if let Some(interface) = shader_module.interface.interface() {
4123 let _ = interface.check_stage(
4124 &mut binding_layout_source,
4125 &mut shader_binding_sizes,
4126 &final_entry_point_name,
4127 stage,
4128 io,
4129 None,
4130 )?;
4131 }
4132 }
4133
4134 let pipeline_layout = match binding_layout_source {
4135 validation::BindingLayoutSource::Provided(pipeline_layout) => pipeline_layout,
4136 validation::BindingLayoutSource::Derived(entries) => {
4137 let immediate_size = shader_module
4138 .interface
4139 .interface()
4140 .map_or(0, |i| i.immediate_size);
4141 self.create_derived_pipeline_layout(entries, immediate_size)?
4142 }
4143 };
4144
4145 let late_sized_buffer_groups =
4146 Device::make_late_sized_buffer_groups(&shader_binding_sizes, &pipeline_layout);
4147
4148 let cache = match desc.cache {
4149 Some(cache) => {
4150 cache.same_device(self)?;
4151 Some(cache)
4152 }
4153 None => None,
4154 };
4155
4156 let pipeline_desc = hal::ComputePipelineDescriptor {
4157 label: desc.label.to_hal(self.instance_flags),
4158 layout: pipeline_layout.raw()?,
4159 stage: hal::ProgrammableStage {
4160 module: shader_module.raw(),
4161 entry_point: final_entry_point_name.as_ref(),
4162 constants: &desc.stage.constants,
4163 zero_initialize_workgroup_memory: desc.stage.zero_initialize_workgroup_memory,
4164 },
4165 cache: cache.as_ref().map(|it| it.raw()),
4166 };
4167
4168 let raw =
4169 unsafe { self.raw().create_compute_pipeline(&pipeline_desc) }.map_err(
4170 |err| match err {
4171 hal::PipelineError::Device(error) => {
4172 pipeline::CreateComputePipelineError::Device(self.handle_hal_error(error))
4173 }
4174 hal::PipelineError::Linkage(_stages, msg) => {
4175 pipeline::CreateComputePipelineError::Internal(msg)
4176 }
4177 hal::PipelineError::EntryPoint(_stage) => {
4178 pipeline::CreateComputePipelineError::Internal(
4179 ENTRYPOINT_FAILURE_ERROR.to_string(),
4180 )
4181 }
4182 hal::PipelineError::PipelineConstants(_stages, msg) => {
4183 pipeline::CreateComputePipelineError::PipelineConstants(msg)
4184 }
4185 },
4186 )?;
4187
4188 let immediate_slots_required =
4189 shader_module
4190 .interface
4191 .interface()
4192 .map_or(Default::default(), |iface| {
4193 iface.immediate_slots_required(
4194 naga::ShaderStage::Compute,
4195 &final_entry_point_name,
4196 )
4197 });
4198
4199 let pipeline = pipeline::ComputePipeline {
4200 state: ResourceState::Valid(pipeline::ComputePipelineState {
4201 raw: ManuallyDrop::new(raw),
4202 layout: pipeline_layout.clone(),
4203 _shader_module: shader_module,
4204 }),
4205 device: self.clone(),
4206 late_sized_buffer_groups,
4207 immediate_slots_required,
4208 label: desc.label.to_string(),
4209 tracking_data: TrackingData::new(self.tracker_indices.compute_pipelines.clone()),
4210 };
4211
4212 let pipeline = Arc::new(pipeline);
4213
4214 if is_auto_layout {
4215 for bgl in pipeline_layout.bind_group_layouts.iter() {
4216 let Some(bgl) = bgl else {
4217 continue;
4218 };
4219
4220 let _ = bgl.exclusive_pipeline.set((&pipeline).into());
4223 }
4224 }
4225
4226 Ok(pipeline)
4227 }
4228
4229 pub fn create_render_pipeline(
4230 self: &Arc<Self>,
4231 desc: pipeline::ResolvedGeneralRenderPipelineDescriptor,
4232 ) -> (
4233 Arc<pipeline::RenderPipeline>,
4234 Option<pipeline::CreateRenderPipelineError>,
4235 ) {
4236 let (render_pipeline, error) = match self.create_render_pipeline_inner(desc.clone()) {
4237 Ok(pipeline) => (pipeline, None),
4238 Err(e) => (
4239 pipeline::RenderPipeline::invalid(self.clone(), desc.label.to_string()),
4240 Some(e),
4241 ),
4242 };
4243 #[cfg(feature = "trace")]
4244 if let Some(ref mut trace) = *self.trace.lock() {
4245 use crate::device::trace::IntoTrace;
4246 trace.add(trace::Action::CreateGeneralRenderPipeline {
4247 id: render_pipeline.to_trace(),
4248 desc: desc.to_trace(),
4249 });
4250 }
4251 (render_pipeline, error)
4252 }
4253
4254 pub fn create_render_pipeline_inner(
4255 self: &Arc<Self>,
4256 desc: pipeline::ResolvedGeneralRenderPipelineDescriptor,
4257 ) -> Result<Arc<pipeline::RenderPipeline>, pipeline::CreateRenderPipelineError> {
4258 use wgt::TextureFormatFeatureFlags as Tfff;
4259
4260 self.check_is_valid()?;
4261
4262 let mut shader_binding_sizes = FastHashMap::default();
4263
4264 let color_targets = desc
4265 .fragment
4266 .as_ref()
4267 .map_or(&[][..], |fragment| &fragment.targets);
4268 let depth_stencil_state = desc.depth_stencil.as_ref();
4269
4270 check_color_attachment_count(color_targets.len(), self.limits.max_color_attachments)?;
4271
4272 {
4273 let cts: ArrayVec<_, { hal::MAX_COLOR_ATTACHMENTS }> =
4274 color_targets.iter().filter_map(|x| x.as_ref()).collect();
4275 if !cts.is_empty() && {
4276 let first = &cts[0];
4277 cts[1..]
4278 .iter()
4279 .any(|ct| ct.write_mask != first.write_mask || ct.blend != first.blend)
4280 } {
4281 self.require_downlevel_flags(wgt::DownlevelFlags::INDEPENDENT_BLEND)?;
4282 }
4283 }
4284
4285 let mut io = validation::StageIo::default();
4286 let mut validated_stages = wgt::ShaderStages::empty();
4287
4288 let mut vertex_steps;
4289 let mut hal_vertex_buffer_layouts;
4290 let mut total_attributes;
4291 let mut dual_source_blending = false;
4292 let mut has_depth_attachment = false;
4293 if let pipeline::RenderPipelineVertexProcessor::Vertex(ref vertex) = desc.vertex {
4294 if vertex.buffers.len() > self.limits.max_vertex_buffers as usize {
4295 return Err(pipeline::CreateRenderPipelineError::TooManyVertexBuffers {
4296 given: vertex.buffers.len() as u32,
4297 limit: self.limits.max_vertex_buffers,
4298 });
4299 }
4300
4301 vertex_steps = Vec::with_capacity(vertex.buffers.len());
4302 hal_vertex_buffer_layouts = Vec::with_capacity(vertex.buffers.len());
4303 total_attributes = 0;
4304 for (i, vb_state) in vertex.buffers.iter().enumerate() {
4305 let Some(vb_state) = vb_state else {
4306 vertex_steps.push(None);
4307 hal_vertex_buffer_layouts.push(None);
4308 continue;
4309 };
4310
4311 if vb_state.array_stride > self.limits.max_vertex_buffer_array_stride as u64 {
4314 return Err(pipeline::CreateRenderPipelineError::VertexStrideTooLarge {
4315 index: i as u32,
4316 given: vb_state.array_stride as u32,
4317 limit: self.limits.max_vertex_buffer_array_stride,
4318 });
4319 }
4320 if vb_state.array_stride % wgt::VERTEX_ALIGNMENT != 0 {
4321 return Err(pipeline::CreateRenderPipelineError::UnalignedVertexStride {
4322 index: i as u32,
4323 stride: vb_state.array_stride,
4324 });
4325 }
4326
4327 let max_stride = if vb_state.array_stride == 0 {
4328 self.limits.max_vertex_buffer_array_stride as u64
4329 } else {
4330 vb_state.array_stride
4331 };
4332 let mut last_stride = 0;
4333 for attribute in vb_state.attributes.iter() {
4334 let attribute_stride = attribute.offset + attribute.format.size();
4335 if attribute_stride > max_stride {
4336 return Err(
4337 pipeline::CreateRenderPipelineError::VertexAttributeStrideTooLarge {
4338 location: attribute.shader_location,
4339 given: attribute_stride as u32,
4340 limit: max_stride as u32,
4341 },
4342 );
4343 }
4344
4345 let required_offset_alignment = attribute.format.size().min(4);
4346 if attribute.offset % required_offset_alignment != 0 {
4347 return Err(
4348 pipeline::CreateRenderPipelineError::InvalidVertexAttributeOffset {
4349 location: attribute.shader_location,
4350 offset: attribute.offset,
4351 },
4352 );
4353 }
4354
4355 if attribute.shader_location >= self.limits.max_vertex_attributes {
4356 return Err(
4357 pipeline::CreateRenderPipelineError::VertexAttributeLocationTooLarge {
4358 given: attribute.shader_location,
4359 limit: self.limits.max_vertex_attributes,
4360 },
4361 );
4362 }
4363
4364 last_stride = last_stride.max(attribute_stride);
4365 }
4366
4367 vertex_steps.push(Some(pipeline::VertexStep {
4368 stride: vb_state.array_stride,
4369 last_stride,
4370 mode: vb_state.step_mode,
4371 }));
4372 hal_vertex_buffer_layouts.push(if vb_state.attributes.is_empty() {
4373 None
4374 } else {
4375 Some(hal::VertexBufferLayout {
4376 array_stride: vb_state.array_stride,
4377 step_mode: vb_state.step_mode,
4378 attributes: vb_state.attributes.as_ref(),
4379 })
4380 });
4381
4382 for attribute in vb_state.attributes.iter() {
4383 if attribute.offset >= 0x10000000 {
4384 return Err(
4385 pipeline::CreateRenderPipelineError::InvalidVertexAttributeOffset {
4386 location: attribute.shader_location,
4387 offset: attribute.offset,
4388 },
4389 );
4390 }
4391
4392 if let wgt::VertexFormat::Float64
4393 | wgt::VertexFormat::Float64x2
4394 | wgt::VertexFormat::Float64x3
4395 | wgt::VertexFormat::Float64x4 = attribute.format
4396 {
4397 self.require_features(wgt::Features::VERTEX_ATTRIBUTE_64BIT)?;
4398 }
4399
4400 let previous = io.varyings.insert(
4401 attribute.shader_location,
4402 validation::InterfaceVar::vertex_attribute(attribute.format),
4403 );
4404
4405 if previous.is_some() {
4406 return Err(pipeline::CreateRenderPipelineError::ShaderLocationClash(
4407 attribute.shader_location,
4408 ));
4409 }
4410 }
4411 total_attributes += vb_state.attributes.len();
4412 }
4413
4414 if total_attributes > self.limits.max_vertex_attributes as usize {
4415 return Err(
4416 pipeline::CreateRenderPipelineError::TooManyVertexAttributes {
4417 given: total_attributes as u32,
4418 limit: self.limits.max_vertex_attributes,
4419 },
4420 );
4421 }
4422 } else {
4423 vertex_steps = Vec::new();
4424 hal_vertex_buffer_layouts = Vec::new();
4425 };
4426
4427 if desc.primitive.strip_index_format.is_some() && !desc.primitive.topology.is_strip() {
4428 return Err(
4429 pipeline::CreateRenderPipelineError::StripIndexFormatForNonStripTopology {
4430 strip_index_format: desc.primitive.strip_index_format,
4431 topology: desc.primitive.topology,
4432 },
4433 );
4434 }
4435
4436 if desc.primitive.unclipped_depth {
4437 self.require_features(wgt::Features::DEPTH_CLIP_CONTROL)?;
4438 }
4439
4440 if desc.primitive.polygon_mode == wgt::PolygonMode::Line {
4441 self.require_features(wgt::Features::POLYGON_MODE_LINE)?;
4442 }
4443 if desc.primitive.polygon_mode == wgt::PolygonMode::Point {
4444 self.require_features(wgt::Features::POLYGON_MODE_POINT)?;
4445 }
4446
4447 if desc.primitive.conservative {
4448 self.require_features(wgt::Features::CONSERVATIVE_RASTERIZATION)?;
4449 }
4450
4451 if desc.primitive.conservative && desc.primitive.polygon_mode != wgt::PolygonMode::Fill {
4452 return Err(
4453 pipeline::CreateRenderPipelineError::ConservativeRasterizationNonFillPolygonMode,
4454 );
4455 }
4456
4457 let mut target_specified = false;
4458
4459 for (i, cs) in color_targets.iter().enumerate() {
4460 if let Some(cs) = cs.as_ref() {
4461 target_specified = true;
4462 let error = 'error: {
4463 if cs.write_mask.contains_unknown_bits() {
4467 break 'error Some(ColorStateError::InvalidWriteMask(cs.write_mask));
4468 }
4469
4470 let format_features = self.describe_format_features(cs.format)?;
4471 if !format_features
4472 .allowed_usages
4473 .contains(wgt::TextureUsages::RENDER_ATTACHMENT)
4474 {
4475 break 'error Some(ColorStateError::FormatNotRenderable(cs.format));
4476 }
4477 if cs.blend.is_some() && !format_features.flags.contains(Tfff::BLENDABLE) {
4478 break 'error Some(ColorStateError::FormatNotBlendable(cs.format));
4479 }
4480 if !hal::FormatAspects::from(cs.format).contains(hal::FormatAspects::COLOR) {
4481 break 'error Some(ColorStateError::FormatNotColor(cs.format));
4482 }
4483
4484 if desc.multisample.count > 1
4485 && !format_features
4486 .flags
4487 .sample_count_supported(desc.multisample.count)
4488 {
4489 break 'error Some(ColorStateError::InvalidSampleCount(
4490 desc.multisample.count,
4491 cs.format,
4492 cs.format
4493 .guaranteed_format_features(self.features)
4494 .flags
4495 .supported_sample_counts(),
4496 self.adapter
4497 .get_texture_format_features(cs.format)
4498 .flags
4499 .supported_sample_counts(),
4500 ));
4501 }
4502
4503 if let Some(blend_mode) = cs.blend {
4504 for component in [&blend_mode.color, &blend_mode.alpha] {
4505 for factor in [component.src_factor, component.dst_factor] {
4506 if factor.ref_second_blend_source() {
4507 self.require_features(wgt::Features::DUAL_SOURCE_BLENDING)?;
4508 if i == 0 {
4509 dual_source_blending = true;
4510 } else {
4511 break 'error Some(
4512 ColorStateError::BlendFactorOnUnsupportedTarget {
4513 factor,
4514 target: i as u32,
4515 },
4516 );
4517 }
4518 }
4519
4520 if [wgt::BlendOperation::Min, wgt::BlendOperation::Max]
4521 .contains(&component.operation)
4522 && factor != wgt::BlendFactor::One
4523 {
4524 break 'error Some(ColorStateError::InvalidMinMaxBlendFactor {
4525 factor,
4526 target: i as u32,
4527 });
4528 }
4529 }
4530 }
4531 }
4532
4533 break 'error None;
4534 };
4535 if let Some(e) = error {
4536 return Err(pipeline::CreateRenderPipelineError::ColorState(i as u8, e));
4537 }
4538 }
4539 }
4540
4541 if dual_source_blending && color_targets.len() > 1 {
4542 return Err(
4543 pipeline::CreateRenderPipelineError::DualSourceBlendingWithMultipleColorTargets {
4544 count: color_targets.len(),
4545 },
4546 );
4547 }
4548
4549 validation::validate_color_attachment_bytes_per_sample(
4550 color_targets.iter().flatten().map(|cs| cs.format),
4551 self.limits.max_color_attachment_bytes_per_sample,
4552 )
4553 .map_err(pipeline::CreateRenderPipelineError::ColorAttachment)?;
4554
4555 if let Some(ds) = depth_stencil_state {
4556 target_specified = true;
4558 let error = 'error: {
4559 if !ds.format.is_depth_stencil_format() {
4560 break 'error Some(pipeline::DepthStencilStateError::FormatNotDepthOrStencil(
4563 ds.format,
4564 ));
4565 }
4566
4567 let format_features = self.describe_format_features(ds.format)?;
4568 if !format_features
4569 .allowed_usages
4570 .contains(wgt::TextureUsages::RENDER_ATTACHMENT)
4571 {
4572 break 'error Some(pipeline::DepthStencilStateError::FormatNotRenderable(
4573 ds.format,
4574 ));
4575 }
4576
4577 let aspect = hal::FormatAspects::from(ds.format);
4578 if aspect.contains(hal::FormatAspects::DEPTH) {
4579 has_depth_attachment = true;
4580 } else if ds.is_depth_enabled() {
4581 break 'error Some(pipeline::DepthStencilStateError::FormatNotDepth(ds.format));
4582 }
4583 if has_depth_attachment {
4584 let Some(depth_write_enabled) = ds.depth_write_enabled else {
4585 break 'error Some(
4586 pipeline::DepthStencilStateError::MissingDepthWriteEnabled(ds.format),
4587 );
4588 };
4589
4590 let depth_compare_required = depth_write_enabled
4591 || ds.stencil.front.depth_fail_op != wgt::StencilOperation::Keep
4592 || ds.stencil.back.depth_fail_op != wgt::StencilOperation::Keep;
4593 if depth_compare_required && ds.depth_compare.is_none() {
4594 break 'error Some(pipeline::DepthStencilStateError::MissingDepthCompare(
4595 ds.format,
4596 ));
4597 }
4598 }
4599
4600 if ds.stencil.is_enabled() && !aspect.contains(hal::FormatAspects::STENCIL) {
4601 break 'error Some(pipeline::DepthStencilStateError::FormatNotStencil(
4602 ds.format,
4603 ));
4604 }
4605 if desc.multisample.count > 1
4606 && !format_features
4607 .flags
4608 .sample_count_supported(desc.multisample.count)
4609 {
4610 break 'error Some(pipeline::DepthStencilStateError::InvalidSampleCount(
4611 desc.multisample.count,
4612 ds.format,
4613 ds.format
4614 .guaranteed_format_features(self.features)
4615 .flags
4616 .supported_sample_counts(),
4617 self.adapter
4618 .get_texture_format_features(ds.format)
4619 .flags
4620 .supported_sample_counts(),
4621 ));
4622 }
4623
4624 break 'error None;
4625 };
4626 if let Some(e) = error {
4627 return Err(pipeline::CreateRenderPipelineError::DepthStencilState(e));
4628 }
4629
4630 if ds.bias.clamp != 0.0 {
4631 self.require_downlevel_flags(wgt::DownlevelFlags::DEPTH_BIAS_CLAMP)?;
4632 }
4633
4634 if (ds.bias.is_enabled() || ds.bias.clamp != 0.0)
4635 && !desc.primitive.topology.is_triangles()
4636 {
4637 return Err(pipeline::CreateRenderPipelineError::DepthStencilState(
4638 pipeline::DepthStencilStateError::DepthBiasWithIncompatibleTopology(
4639 desc.primitive.topology,
4640 ),
4641 ));
4642 }
4643 }
4644
4645 if !target_specified {
4646 return Err(pipeline::CreateRenderPipelineError::NoTargetSpecified);
4647 }
4648
4649 let is_auto_layout = desc.layout.is_none();
4650
4651 let pipeline_layout = match desc.layout {
4653 Some(pipeline_layout) => {
4654 pipeline_layout.same_device(self)?;
4655 pipeline_layout.check_valid()?;
4656 Some(pipeline_layout)
4657 }
4658 None => None,
4659 };
4660
4661 let mut binding_layout_source = match pipeline_layout {
4662 Some(pipeline_layout) => validation::BindingLayoutSource::Provided(pipeline_layout),
4663 None => validation::BindingLayoutSource::new_derived(&self.limits),
4664 };
4665
4666 let samples = {
4667 let sc = desc.multisample.count;
4668 if sc == 0 || sc > 32 || !sc.is_power_of_two() {
4669 return Err(pipeline::CreateRenderPipelineError::InvalidSampleCount(sc));
4670 }
4671 sc
4672 };
4673
4674 let mut vertex_stage = None;
4675 let mut task_stage = None;
4676 let mut mesh_stage = None;
4677 let mut _vertex_entry_point_name = String::new();
4678 let mut _task_entry_point_name = String::new();
4679 let mut _mesh_entry_point_name = String::new();
4680 let mut immediate_slots_required = naga::valid::ImmediateSlots::default();
4681 match desc.vertex {
4682 pipeline::RenderPipelineVertexProcessor::Vertex(ref vertex) => {
4683 vertex_stage = {
4684 let stage_desc = &vertex.stage;
4685 let stage = validation::ShaderStageForValidation::Vertex {
4686 topology: desc.primitive.topology,
4687 compare_function: desc.depth_stencil.as_ref().and_then(|d| d.depth_compare),
4688 };
4689 let stage_bit = stage.to_wgt_bit();
4690
4691 let vertex_shader_module = &stage_desc.module;
4692 vertex_shader_module.same_device(self)?;
4693
4694 let stage_err = |error| pipeline::CreateRenderPipelineError::Stage {
4695 stage: stage_bit,
4696 error,
4697 };
4698
4699 _vertex_entry_point_name = vertex_shader_module
4700 .finalize_entry_point_name(
4701 stage.to_naga(),
4702 stage_desc.entry_point.as_ref().map(|ep| ep.as_ref()),
4703 )
4704 .map_err(stage_err)?;
4705
4706 if let Some(interface) = vertex_shader_module.interface.interface() {
4707 immediate_slots_required |= interface
4708 .immediate_slots_required(stage.to_naga(), &_vertex_entry_point_name);
4709 io = interface
4710 .check_stage(
4711 &mut binding_layout_source,
4712 &mut shader_binding_sizes,
4713 &_vertex_entry_point_name,
4714 stage,
4715 io,
4716 Some(desc.primitive.topology),
4717 )
4718 .map_err(stage_err)?;
4719 validated_stages |= stage_bit;
4720 }
4721 Some(hal::ProgrammableStage {
4722 module: vertex_shader_module.raw(),
4723 entry_point: &_vertex_entry_point_name,
4724 constants: &stage_desc.constants,
4725 zero_initialize_workgroup_memory: stage_desc
4726 .zero_initialize_workgroup_memory,
4727 })
4728 };
4729 }
4730 pipeline::RenderPipelineVertexProcessor::Mesh(ref task, ref mesh) => {
4731 self.require_features(wgt::Features::EXPERIMENTAL_MESH_SHADER)?;
4732
4733 task_stage = if let Some(task) = task {
4734 let stage_desc = &task.stage;
4735 let stage = validation::ShaderStageForValidation::Task;
4736 let stage_bit = stage.to_wgt_bit();
4737 let task_shader_module = &stage_desc.module;
4738 task_shader_module.same_device(self)?;
4739
4740 let stage_err = |error| pipeline::CreateRenderPipelineError::Stage {
4741 stage: stage_bit,
4742 error,
4743 };
4744
4745 _task_entry_point_name = task_shader_module
4746 .finalize_entry_point_name(
4747 stage.to_naga(),
4748 stage_desc.entry_point.as_ref().map(|ep| ep.as_ref()),
4749 )
4750 .map_err(stage_err)?;
4751
4752 if let Some(interface) = task_shader_module.interface.interface() {
4753 immediate_slots_required |= interface
4754 .immediate_slots_required(stage.to_naga(), &_task_entry_point_name);
4755 io = interface
4756 .check_stage(
4757 &mut binding_layout_source,
4758 &mut shader_binding_sizes,
4759 &_task_entry_point_name,
4760 stage,
4761 io,
4762 Some(desc.primitive.topology),
4763 )
4764 .map_err(stage_err)?;
4765 validated_stages |= stage_bit;
4766 }
4767 Some(hal::ProgrammableStage {
4768 module: task_shader_module.raw(),
4769 entry_point: &_task_entry_point_name,
4770 constants: &stage_desc.constants,
4771 zero_initialize_workgroup_memory: stage_desc
4772 .zero_initialize_workgroup_memory,
4773 })
4774 } else {
4775 None
4776 };
4777 mesh_stage = {
4778 let stage_desc = &mesh.stage;
4779 let stage = validation::ShaderStageForValidation::Mesh;
4780 let stage_bit = stage.to_wgt_bit();
4781 let mesh_shader_module = &stage_desc.module;
4782 mesh_shader_module.same_device(self)?;
4783
4784 let stage_err = |error| pipeline::CreateRenderPipelineError::Stage {
4785 stage: stage_bit,
4786 error,
4787 };
4788
4789 _mesh_entry_point_name = mesh_shader_module
4790 .finalize_entry_point_name(
4791 stage.to_naga(),
4792 stage_desc.entry_point.as_ref().map(|ep| ep.as_ref()),
4793 )
4794 .map_err(stage_err)?;
4795
4796 if let Some(interface) = mesh_shader_module.interface.interface() {
4797 immediate_slots_required |= interface
4798 .immediate_slots_required(stage.to_naga(), &_mesh_entry_point_name);
4799 io = interface
4800 .check_stage(
4801 &mut binding_layout_source,
4802 &mut shader_binding_sizes,
4803 &_mesh_entry_point_name,
4804 stage,
4805 io,
4806 Some(desc.primitive.topology),
4807 )
4808 .map_err(stage_err)?;
4809 validated_stages |= stage_bit;
4810 }
4811 Some(hal::ProgrammableStage {
4812 module: mesh_shader_module.raw(),
4813 entry_point: &_mesh_entry_point_name,
4814 constants: &stage_desc.constants,
4815 zero_initialize_workgroup_memory: stage_desc
4816 .zero_initialize_workgroup_memory,
4817 })
4818 };
4819 }
4820 }
4821
4822 let fragment_entry_point_name;
4823 let fragment_stage = match desc.fragment {
4824 Some(ref fragment_state) => {
4825 let stage = validation::ShaderStageForValidation::Fragment {
4826 dual_source_blending,
4827 has_depth_attachment,
4828 };
4829 let stage_bit = stage.to_wgt_bit();
4830
4831 let shader_module = &fragment_state.stage.module;
4832 shader_module.same_device(self)?;
4833
4834 let stage_err = |error| pipeline::CreateRenderPipelineError::Stage {
4835 stage: stage_bit,
4836 error,
4837 };
4838
4839 fragment_entry_point_name = shader_module
4840 .finalize_entry_point_name(
4841 stage.to_naga(),
4842 fragment_state
4843 .stage
4844 .entry_point
4845 .as_ref()
4846 .map(|ep| ep.as_ref()),
4847 )
4848 .map_err(stage_err)?;
4849
4850 if let Some(interface) = shader_module.interface.interface() {
4851 immediate_slots_required |= interface
4852 .immediate_slots_required(stage.to_naga(), &fragment_entry_point_name);
4853 io = interface
4854 .check_stage(
4855 &mut binding_layout_source,
4856 &mut shader_binding_sizes,
4857 &fragment_entry_point_name,
4858 stage,
4859 io,
4860 Some(desc.primitive.topology),
4861 )
4862 .map_err(stage_err)?;
4863 validated_stages |= stage_bit;
4864 }
4865
4866 Some(hal::ProgrammableStage {
4867 module: shader_module.raw(),
4868 entry_point: &fragment_entry_point_name,
4869 constants: &fragment_state.stage.constants,
4870 zero_initialize_workgroup_memory: fragment_state
4871 .stage
4872 .zero_initialize_workgroup_memory,
4873 })
4874 }
4875 None => None,
4876 };
4877
4878 if validated_stages.contains(wgt::ShaderStages::FRAGMENT) {
4879 for (i, output) in io.varyings.iter() {
4880 match color_targets.get(*i as usize) {
4881 Some(Some(state)) => {
4882 validation::check_texture_format(state.format, &output.ty).map_err(
4883 |pipeline| {
4884 pipeline::CreateRenderPipelineError::ColorState(
4885 *i as u8,
4886 ColorStateError::IncompatibleFormat {
4887 pipeline,
4888 shader: output.ty,
4889 },
4890 )
4891 },
4892 )?;
4893 }
4894 _ => {
4895 log::debug!(
4896 "The fragment stage {:?} output @location({}) values are ignored",
4897 fragment_stage
4898 .as_ref()
4899 .map_or("", |stage| stage.entry_point),
4900 i
4901 );
4902 }
4903 }
4904 }
4905 }
4906 let last_stage = match desc.fragment {
4907 Some(_) => wgt::ShaderStages::FRAGMENT,
4908 None => wgt::ShaderStages::VERTEX,
4909 };
4910 if is_auto_layout && !validated_stages.contains(last_stage) {
4911 return Err(pipeline::ImplicitLayoutError::ReflectionError(last_stage).into());
4912 }
4913
4914 let pipeline_layout = match binding_layout_source {
4915 validation::BindingLayoutSource::Provided(pipeline_layout) => pipeline_layout,
4916 validation::BindingLayoutSource::Derived(entries) => {
4917 let immediate_size = {
4918 let immediate_size_of = |sm: &pipeline::ShaderModule| {
4919 sm.interface.interface().map(|i| i.immediate_size)
4920 };
4921 let vertex = match desc.vertex {
4922 pipeline::RenderPipelineVertexProcessor::Vertex(ref v) => {
4923 immediate_size_of(&v.stage.module)
4924 }
4925 pipeline::RenderPipelineVertexProcessor::Mesh(ref task, ref mesh) => task
4926 .as_ref()
4927 .and_then(|t| immediate_size_of(&t.stage.module))
4928 .max(immediate_size_of(&mesh.stage.module)),
4929 };
4930 let fragment = desc
4931 .fragment
4932 .as_ref()
4933 .and_then(|f| immediate_size_of(&f.stage.module));
4934 vertex.max(fragment).unwrap_or(0)
4935 };
4936 self.create_derived_pipeline_layout(entries, immediate_size)?
4937 }
4938 };
4939
4940 if let pipeline::RenderPipelineVertexProcessor::Vertex(ref vertex) = desc.vertex {
4941 let bind_groups_plus_vertex_buffers =
4942 u32::try_from(pipeline_layout.bind_group_layouts.len() + vertex.buffers.len())
4943 .unwrap();
4944 if bind_groups_plus_vertex_buffers > self.limits.max_bind_groups_plus_vertex_buffers {
4945 return Err(
4946 pipeline::CreateRenderPipelineError::TooManyBindGroupsPlusVertexBuffers {
4947 given: bind_groups_plus_vertex_buffers,
4948 limit: self.limits.max_bind_groups_plus_vertex_buffers,
4949 },
4950 );
4951 }
4952
4953 let given = pipeline_layout
4954 .buffers_and_acceleration_structures_in_vertex_stage
4955 .saturating_add(vertex.buffers.len() as u32);
4956 if !self
4957 .instance_flags
4958 .contains(wgt::InstanceFlags::STRICT_WEBGPU_COMPLIANCE)
4959 {
4960 let limit = self
4961 .limits
4962 .max_buffers_and_acceleration_structures_per_shader_stage;
4963 if given > limit {
4964 return Err(
4965 pipeline::CreateRenderPipelineError::TooManyBuffersAndAccelerationStructuresInVertexStage {
4966 given,
4967 limit,
4968 },
4969 );
4970 }
4971 }
4972 }
4973
4974 if let Some(mv_mask) = desc.multiview_mask {
4976 self.require_features(wgt::Features::MULTIVIEW)?;
4977 if !(mv_mask.get() + 1).is_power_of_two() {
4978 self.require_features(wgt::Features::SELECTIVE_MULTIVIEW)?;
4979 }
4980 }
4981
4982 if !self
4983 .downlevel
4984 .flags
4985 .contains(wgt::DownlevelFlags::BUFFER_BINDINGS_NOT_16_BYTE_ALIGNED)
4986 {
4987 for (binding, size) in shader_binding_sizes.iter() {
4988 if size.get() % 16 != 0 {
4989 return Err(pipeline::CreateRenderPipelineError::UnalignedShader {
4990 binding: binding.binding,
4991 group: binding.group,
4992 size: size.get(),
4993 });
4994 }
4995 }
4996 }
4997
4998 let late_sized_buffer_groups =
4999 Device::make_late_sized_buffer_groups(&shader_binding_sizes, &pipeline_layout);
5000
5001 let cache = match desc.cache {
5002 Some(cache) => {
5003 cache.same_device(self)?;
5004 Some(cache)
5005 }
5006 None => None,
5007 };
5008
5009 let is_mesh = mesh_stage.is_some();
5010 let has_task_shader = task_stage.is_some();
5011 let raw = {
5012 let pipeline_desc = hal::RenderPipelineDescriptor {
5013 label: desc.label.to_hal(self.instance_flags),
5014 layout: pipeline_layout.raw()?,
5015 vertex_processor: match vertex_stage {
5016 Some(vertex_stage) => hal::VertexProcessor::Standard {
5017 vertex_buffers: &hal_vertex_buffer_layouts,
5018 vertex_stage,
5019 },
5020 None => hal::VertexProcessor::Mesh {
5021 task_stage,
5022 mesh_stage: mesh_stage.unwrap(),
5023 },
5024 },
5025 primitive: desc.primitive,
5026 depth_stencil: desc.depth_stencil.clone(),
5027 multisample: desc.multisample,
5028 fragment_stage,
5029 color_targets,
5030 multiview_mask: desc.multiview_mask,
5031 cache: cache.as_ref().map(|it| it.raw()),
5032 };
5033 unsafe { self.raw().create_render_pipeline(&pipeline_desc) }.map_err(
5034 |err| match err {
5035 hal::PipelineError::Device(error) => {
5036 pipeline::CreateRenderPipelineError::Device(self.handle_hal_error(error))
5037 }
5038 hal::PipelineError::Linkage(stage, msg) => {
5039 pipeline::CreateRenderPipelineError::Internal { stage, error: msg }
5040 }
5041 hal::PipelineError::EntryPoint(stage) => {
5042 pipeline::CreateRenderPipelineError::Internal {
5043 stage: hal::auxil::map_naga_stage(stage),
5044 error: ENTRYPOINT_FAILURE_ERROR.to_string(),
5045 }
5046 }
5047 hal::PipelineError::PipelineConstants(stage, error) => {
5048 pipeline::CreateRenderPipelineError::PipelineConstants { stage, error }
5049 }
5050 },
5051 )?
5052 };
5053
5054 let pass_context = RenderPassContext {
5055 attachments: AttachmentData {
5056 colors: color_targets
5057 .iter()
5058 .map(|state| state.as_ref().map(|s| s.format))
5059 .collect(),
5060 resolves: ArrayVec::new(),
5061 depth_stencil: depth_stencil_state.as_ref().map(|state| state.format),
5062 },
5063 sample_count: samples,
5064 multiview_mask: desc.multiview_mask,
5065 };
5066
5067 let mut flags = pipeline::PipelineFlags::empty();
5068 for state in color_targets.iter().filter_map(|s| s.as_ref()) {
5069 if let Some(ref bs) = state.blend {
5070 if bs.color.uses_constant() | bs.alpha.uses_constant() {
5071 flags |= pipeline::PipelineFlags::BLEND_CONSTANT;
5072 }
5073 }
5074 }
5075 if let Some(ds) = depth_stencil_state.as_ref() {
5076 if ds.stencil.is_enabled() && ds.stencil.needs_ref_value() {
5077 flags |= pipeline::PipelineFlags::STENCIL_REFERENCE;
5078 }
5079 if !ds.is_depth_read_only() {
5080 flags |= pipeline::PipelineFlags::WRITES_DEPTH;
5081 }
5082 if !ds.is_stencil_read_only(desc.primitive.cull_mode) {
5083 flags |= pipeline::PipelineFlags::WRITES_STENCIL;
5084 }
5085 }
5086 let shader_modules = {
5087 let mut shader_modules = ArrayVec::new();
5088 match desc.vertex {
5089 pipeline::RenderPipelineVertexProcessor::Vertex(vertex) => {
5090 shader_modules.push(vertex.stage.module)
5091 }
5092 pipeline::RenderPipelineVertexProcessor::Mesh(task, mesh) => {
5093 if let Some(task) = task {
5094 shader_modules.push(task.stage.module);
5095 }
5096 shader_modules.push(mesh.stage.module);
5097 }
5098 }
5099 shader_modules.extend(desc.fragment.map(|f| f.stage.module));
5100 shader_modules
5101 };
5102
5103 let pipeline = pipeline::RenderPipeline {
5104 state: ResourceState::Valid(pipeline::RenderPipelineState {
5105 raw: ManuallyDrop::new(raw),
5106 layout: pipeline_layout.clone(),
5107 }),
5108 device: self.clone(),
5109 pass_context,
5110 _shader_modules: shader_modules,
5111 flags,
5112 topology: desc.primitive.topology,
5113 strip_index_format: desc.primitive.strip_index_format,
5114 vertex_steps,
5115 late_sized_buffer_groups,
5116 immediate_slots_required,
5117 label: desc.label.to_string(),
5118 tracking_data: TrackingData::new(self.tracker_indices.render_pipelines.clone()),
5119 is_mesh,
5120 has_task_shader,
5121 };
5122
5123 let pipeline = Arc::new(pipeline);
5124
5125 if is_auto_layout {
5126 for bgl in pipeline_layout.bind_group_layouts.iter() {
5127 let Some(bgl) = bgl else {
5128 continue;
5129 };
5130
5131 let _ = bgl.exclusive_pipeline.set((&pipeline).into());
5134 }
5135 }
5136
5137 Ok(pipeline)
5138 }
5139
5140 pub unsafe fn create_pipeline_cache(
5144 self: &Arc<Self>,
5145 desc: &pipeline::PipelineCacheDescriptor,
5146 ) -> Result<Arc<pipeline::PipelineCache>, pipeline::CreatePipelineCacheError> {
5147 use crate::pipeline_cache;
5148
5149 self.check_is_valid()?;
5150
5151 self.require_features(wgt::Features::PIPELINE_CACHE)?;
5152 let data = if let Some((data, validation_key)) = desc
5153 .data
5154 .as_ref()
5155 .zip(self.raw().pipeline_cache_validation_key())
5156 {
5157 let data = pipeline_cache::validate_pipeline_cache(
5158 data,
5159 &self.adapter.raw.info,
5160 validation_key,
5161 );
5162 match data {
5163 Ok(data) => Some(data),
5164 Err(e) if e.was_avoidable() || !desc.fallback => return Err(e.into()),
5165 Err(_) => None,
5167 }
5168 } else {
5169 None
5170 };
5171 let cache_desc = hal::PipelineCacheDescriptor {
5172 data,
5173 label: desc.label.to_hal(self.instance_flags),
5174 };
5175 let raw = match unsafe { self.raw().create_pipeline_cache(&cache_desc) } {
5176 Ok(raw) => raw,
5177 Err(e) => match e {
5178 hal::PipelineCacheError::Device(e) => return Err(self.handle_hal_error(e).into()),
5179 },
5180 };
5181 let cache = pipeline::PipelineCache {
5182 device: self.clone(),
5183 label: desc.label.to_string(),
5184 raw: ManuallyDrop::new(raw),
5186 };
5187
5188 let cache = Arc::new(cache);
5189
5190 Ok(cache)
5191 }
5192
5193 fn get_texture_format_features(&self, format: TextureFormat) -> wgt::TextureFormatFeatures {
5194 use wgt::TextureFormatFeatureFlags as tfsc;
5196 let mut format_features = self.adapter.get_texture_format_features(format);
5197 if (format == TextureFormat::R32Float
5198 || format == TextureFormat::Rg32Float
5199 || format == TextureFormat::Rgba32Float)
5200 && !self.features.contains(wgt::Features::FLOAT32_FILTERABLE)
5201 {
5202 format_features.flags.set(tfsc::FILTERABLE, false);
5203 }
5204 format_features
5205 }
5206
5207 pub(crate) fn describe_format_features(
5208 &self,
5209 format: TextureFormat,
5210 ) -> Result<wgt::TextureFormatFeatures, MissingFeatures> {
5211 self.require_features(format.required_features())?;
5212
5213 let using_device_features = self
5214 .features
5215 .contains(wgt::Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES);
5216 let downlevel = !self
5219 .downlevel
5220 .flags
5221 .contains(wgt::DownlevelFlags::WEBGPU_TEXTURE_FORMAT_SUPPORT);
5222
5223 if using_device_features || downlevel {
5224 Ok(self.get_texture_format_features(format))
5225 } else {
5226 Ok(format.guaranteed_format_features(self.features))
5227 }
5228 }
5229
5230 #[cfg(feature = "replay")]
5231 pub(crate) fn wait_for_submit(
5232 &self,
5233 submission_index: crate::SubmissionIndex,
5234 ) -> Result<(), DeviceError> {
5235 let last_done_index = unsafe { self.raw().get_fence_value(self.fence.as_ref()) }
5236 .map_err(|e| self.handle_hal_error(e))?;
5237 if last_done_index < submission_index {
5238 unsafe { self.raw().wait(self.fence.as_ref(), submission_index, None) }
5239 .map_err(|e| self.handle_hal_error(e))?;
5240 if let Some(queue) = self.get_queue() {
5241 let closures = queue.lock_life().triage_submissions(submission_index);
5242 assert!(
5243 closures.is_empty(),
5244 "wait_for_submit is not expected to work with closures"
5245 );
5246 }
5247 }
5248 Ok(())
5249 }
5250
5251 pub fn create_query_set(
5252 self: &Arc<Self>,
5253 desc: &resource::QuerySetDescriptor,
5254 ) -> Result<Arc<QuerySet>, resource::CreateQuerySetError> {
5255 use resource::CreateQuerySetError as Error;
5256
5257 self.check_is_valid()?;
5258
5259 match desc.ty {
5260 wgt::QueryType::Occlusion => {}
5261 wgt::QueryType::Timestamp => {
5262 self.require_features(wgt::Features::TIMESTAMP_QUERY)?;
5263 }
5264 wgt::QueryType::PipelineStatistics(..) => {
5265 self.require_features(wgt::Features::PIPELINE_STATISTICS_QUERY)?;
5266 }
5267 }
5268
5269 if desc.count == 0 {
5270 return Err(Error::ZeroCount);
5271 }
5272
5273 if desc.count > wgt::QUERY_SET_MAX_QUERIES {
5274 return Err(Error::TooManyQueries {
5275 count: desc.count,
5276 maximum: wgt::QUERY_SET_MAX_QUERIES,
5277 });
5278 }
5279
5280 let hal_desc = desc.map_label(|label| label.to_hal(self.instance_flags));
5281
5282 let raw = unsafe { self.raw().create_query_set(&hal_desc) }
5283 .map_err(|e| self.handle_hal_error_with_nonfatal_oom(e))?;
5284
5285 let query_set = QuerySet {
5286 raw: Snatchable::new(raw),
5287 device: self.clone(),
5288 label: desc.label.to_string(),
5289 tracking_data: TrackingData::new(self.tracker_indices.query_sets.clone()),
5290 desc: desc.map_label(|_| ()),
5291 initialized_slots: Mutex::new(
5292 rank::QUERY_SET_INITIALIZED_SLOTS,
5293 bit_vec::BitVec::from_elem(desc.count as usize, false),
5294 ),
5295 };
5296
5297 let query_set = Arc::new(query_set);
5298
5299 Ok(query_set)
5300 }
5301
5302 pub fn configure_surface(
5303 self: &Arc<Self>,
5304 surface: &crate::instance::Surface,
5305 config: &wgt::SurfaceConfiguration<Vec<TextureFormat>>,
5306 ) -> Option<present::ConfigureSurfaceError> {
5307 use present::ConfigureSurfaceError as E;
5308 profiling::scope!("surface_configure");
5309
5310 log::debug!("configuring surface with {config:?}");
5311
5312 let error = 'error: {
5313 let user_callbacks;
5315 {
5316 if let Err(e) = self.check_is_valid() {
5317 break 'error e.into();
5318 }
5319
5320 let caps = match surface.get_capabilities(&self.adapter) {
5321 Ok(caps) => caps,
5322 Err(_) => break 'error E::UnsupportedQueueFamily,
5323 };
5324
5325 let mut hal_view_formats = Vec::new();
5326 for format in config.view_formats.iter() {
5327 if *format == config.format {
5328 continue;
5329 }
5330 if !caps.formats.iter().any(|fc| fc.format == config.format) {
5331 break 'error E::UnsupportedFormat {
5332 requested: config.format,
5333 available: caps.texture_formats().collect(),
5334 };
5335 }
5336 if config.format.remove_srgb_suffix() != format.remove_srgb_suffix() {
5337 break 'error E::InvalidViewFormat(*format, config.format);
5338 }
5339 hal_view_formats.push(*format);
5340 }
5341
5342 if !hal_view_formats.is_empty() {
5343 if let Err(missing_flag) =
5344 self.require_downlevel_flags(wgt::DownlevelFlags::SURFACE_VIEW_FORMATS)
5345 {
5346 break 'error E::MissingDownlevelFlags(missing_flag);
5347 }
5348 }
5349
5350 let maximum_frame_latency = config.desired_maximum_frame_latency.clamp(
5351 *caps.maximum_frame_latency.start(),
5352 *caps.maximum_frame_latency.end(),
5353 );
5354 let mut hal_config = hal::SurfaceConfiguration {
5355 maximum_frame_latency,
5356 present_mode: config.present_mode,
5357 composite_alpha_mode: config.alpha_mode,
5358 format: config.format,
5359 color_space: config.color_space,
5360 extent: wgt::Extent3d {
5361 width: config.width,
5362 height: config.height,
5363 depth_or_array_layers: 1,
5364 },
5365 usage: conv::map_texture_usage(
5366 config.usage,
5367 hal::FormatAspects::COLOR,
5368 wgt::TextureFormatFeatureFlags::STORAGE_READ_ONLY
5369 | wgt::TextureFormatFeatureFlags::STORAGE_WRITE_ONLY
5370 | wgt::TextureFormatFeatureFlags::STORAGE_READ_WRITE,
5371 ),
5372 view_formats: hal_view_formats,
5373 };
5374
5375 if let Err(error) = validate_surface_configuration(
5376 &mut hal_config,
5377 &caps,
5378 self.limits.max_texture_dimension_2d,
5379 ) {
5380 break 'error error;
5381 }
5382
5383 let snatch_guard = self.snatchable_lock.read();
5385
5386 let maintain_result;
5387 (user_callbacks, maintain_result) =
5388 self.maintain(wgt::PollType::wait_indefinitely(), snatch_guard);
5389
5390 match maintain_result {
5391 Ok(wgt::PollStatus::QueueEmpty) => {}
5393 Ok(wgt::PollStatus::WaitSucceeded) => {
5394 break 'error E::GpuWaitTimeout;
5397 }
5398 Ok(wgt::PollStatus::Poll) => {
5399 unreachable!("Cannot get a Poll result from a Wait action.")
5400 }
5401 Err(WaitIdleError::Timeout) if cfg!(target_arch = "wasm32") => {
5402 }
5407 Err(e) => {
5408 break 'error e.into();
5409 }
5410 }
5411
5412 if let Some(present) = surface.presentation.lock().take() {
5414 if present.acquired_texture.is_some() {
5415 break 'error E::PreviousOutputExists;
5416 }
5417 }
5418
5419 let surface_raw = surface.raw(self.backend()).unwrap();
5426 match unsafe { surface_raw.configure(self.raw(), &hal_config) } {
5427 Ok(()) => (),
5428 Err(error) => {
5429 break 'error match error {
5430 hal::SurfaceError::Outdated
5431 | hal::SurfaceError::Lost
5432 | hal::SurfaceError::Occluded
5433 | hal::SurfaceError::Timeout => E::InvalidSurface,
5434 hal::SurfaceError::Device(error) => {
5435 E::Device(self.handle_hal_error(error))
5436 }
5437 hal::SurfaceError::Other(message) => {
5438 log::error!("surface configuration failed: {message}");
5439 E::InvalidSurface
5440 }
5441 }
5442 }
5443 }
5444
5445 let mut presentation = surface.presentation.lock();
5446 *presentation = Some(present::Presentation {
5447 device: Arc::clone(self),
5448 config: config.clone(),
5449 acquired_texture: None,
5450 });
5451 }
5452
5453 user_callbacks.fire();
5454 return None;
5455 };
5456
5457 Some(error)
5458 }
5459
5460 fn lose(&self, message: &str) {
5461 self.valid.store(false, Ordering::Release);
5466
5467 if let Some(device_lost_closure) = self.device_lost_closure.lock().take() {
5469 device_lost_closure(DeviceLostReason::Unknown, message.to_string());
5470 }
5471
5472 }
5480
5481 fn release_gpu_resources(&self) {
5482 let trackers = self.trackers.lock();
5492 for buffer in trackers.buffers.used_resources() {
5493 if let Some(buffer) = Weak::upgrade(buffer) {
5494 buffer.destroy();
5495 }
5496 }
5497 for texture in trackers.textures.used_resources() {
5498 if let Some(texture) = Weak::upgrade(texture) {
5499 texture.destroy();
5500 }
5501 }
5502 }
5503
5504 pub(crate) fn new_usage_scope(&self) -> UsageScope<'_> {
5505 UsageScope::new_pooled(
5506 &self.usage_scopes,
5507 &self.tracker_indices,
5508 self.ordered_buffer_usages,
5509 self.ordered_texture_usages,
5510 )
5511 }
5512
5513 pub fn get_hal_counters(&self) -> wgt::HalCounters {
5514 self.raw().get_internal_counters()
5515 }
5516
5517 pub fn generate_allocator_report(&self) -> Option<wgt::AllocatorReport> {
5518 self.raw().generate_allocator_report()
5519 }
5520}
5521
5522crate::impl_resource_type!(Device);
5523crate::impl_labeled!(Device);
5524crate::impl_storage_item!(Device);