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