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