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