1#[cfg(feature = "trace")]
2use crate::device::trace;
3use crate::{
4 binding_model::{self, BindGroup, BindGroupLayout, BindGroupLayoutEntryError},
5 command, conv,
6 device::{
7 bgl,
8 life::{LifetimeTracker, WaitIdleError},
9 queue::PendingWrites,
10 AttachmentData, DeviceLostInvocation, MissingDownlevelFlags, MissingFeatures,
11 RenderPassContext, CLEANUP_WAIT_MS,
12 },
13 hal_api::HalApi,
14 hal_label,
15 hub::Hub,
16 id,
17 init_tracker::{
18 BufferInitTracker, BufferInitTrackerAction, MemoryInitKind, TextureInitRange,
19 TextureInitTracker, TextureInitTrackerAction,
20 },
21 instance::Adapter,
22 lock::{rank, Mutex, MutexGuard, RwLock},
23 pipeline,
24 pool::ResourcePool,
25 registry::Registry,
26 resource::{
27 self, Buffer, QuerySet, Resource, ResourceInfo, ResourceType, Sampler, Texture,
28 TextureView, TextureViewNotRenderableReason,
29 },
30 resource_log,
31 snatch::{SnatchGuard, SnatchLock, Snatchable},
32 storage::Storage,
33 track::{
34 BindGroupStates, TextureSelector, Tracker, TrackerIndexAllocators, UsageScope,
35 UsageScopePool,
36 },
37 validation::{
38 self, check_buffer_usage, check_texture_usage, validate_color_attachment_bytes_per_sample,
39 },
40 FastHashMap, LabelHelpers as _, SubmissionIndex,
41};
42
43use arrayvec::ArrayVec;
44use hal::{CommandEncoder as _, Device as _};
45use once_cell::sync::OnceCell;
46
47use smallvec::SmallVec;
48use thiserror::Error;
49use wgt::{DeviceLostReason, TextureFormat, TextureSampleType, TextureViewDimension};
50
51use std::{
52 borrow::Cow,
53 iter,
54 num::NonZeroU32,
55 sync::{
56 atomic::{AtomicBool, AtomicU64, Ordering},
57 Arc, Weak,
58 },
59};
60
61use super::{
62 life::{self, ResourceMaps},
63 queue::{self, Queue},
64 DeviceDescriptor, DeviceError, ImplicitPipelineContext, UserClosures, ENTRYPOINT_FAILURE_ERROR,
65 IMPLICIT_BIND_GROUP_LAYOUT_ERROR_LABEL, ZERO_BUFFER_SIZE,
66};
67
68pub struct Device<A: HalApi> {
94 raw: Option<A::Device>,
95 pub(crate) adapter: Arc<Adapter<A>>,
96 pub(crate) queue: OnceCell<Weak<Queue<A>>>,
97 queue_to_drop: OnceCell<A::Queue>,
98 pub(crate) zero_buffer: Option<A::Buffer>,
99 pub(crate) info: ResourceInfo<Device<A>>,
100
101 pub(crate) command_allocator: command::CommandAllocator<A>,
102 pub(crate) active_submission_index: AtomicU64, pub(crate) fence: RwLock<Option<A::Fence>>,
107 pub(crate) snatchable_lock: SnatchLock,
108
109 pub(crate) valid: AtomicBool,
121
122 pub(crate) trackers: Mutex<Tracker<A>>,
127 pub(crate) tracker_indices: TrackerIndexAllocators,
128 life_tracker: Mutex<LifetimeTracker<A>>,
130 pub(crate) temp_suspected: Mutex<Option<ResourceMaps<A>>>,
133 pub(crate) bgl_pool: ResourcePool<bgl::EntryMap, BindGroupLayout<A>>,
135 pub(crate) alignments: hal::Alignments,
136 pub(crate) limits: wgt::Limits,
137 pub(crate) features: wgt::Features,
138 pub(crate) downlevel: wgt::DownlevelCapabilities,
139 pub(crate) instance_flags: wgt::InstanceFlags,
140 pub(crate) pending_writes: Mutex<Option<PendingWrites<A>>>,
141 pub(crate) deferred_destroy: Mutex<Vec<DeferredDestroy<A>>>,
142 #[cfg(feature = "trace")]
143 pub(crate) trace: Mutex<Option<trace::Trace>>,
144 pub(crate) usage_scopes: UsageScopePool<A>,
145}
146
147pub(crate) enum DeferredDestroy<A: HalApi> {
148 TextureView(Weak<TextureView<A>>),
149 BindGroup(Weak<BindGroup<A>>),
150}
151
152impl<A: HalApi> std::fmt::Debug for Device<A> {
153 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
154 f.debug_struct("Device")
155 .field("adapter", &self.adapter.info.label())
156 .field("limits", &self.limits)
157 .field("features", &self.features)
158 .field("downlevel", &self.downlevel)
159 .finish()
160 }
161}
162
163impl<A: HalApi> Drop for Device<A> {
164 fn drop(&mut self) {
165 resource_log!("Destroy raw Device {:?}", self.info.label());
166 let raw = self.raw.take().unwrap();
167 let pending_writes = self.pending_writes.lock().take().unwrap();
168 pending_writes.dispose(&raw);
169 self.command_allocator.dispose(&raw);
170 unsafe {
171 raw.destroy_buffer(self.zero_buffer.take().unwrap());
172 raw.destroy_fence(self.fence.write().take().unwrap());
173 let queue = self.queue_to_drop.take().unwrap();
174 raw.exit(queue);
175 }
176 }
177}
178
179#[derive(Clone, Debug, Error)]
180pub enum CreateDeviceError {
181 #[error("Not enough memory left to create device")]
182 OutOfMemory,
183 #[error("Failed to create internal buffer for initializing textures")]
184 FailedToCreateZeroBuffer(#[from] DeviceError),
185}
186
187impl<A: HalApi> Device<A> {
188 pub(crate) fn raw(&self) -> &A::Device {
189 self.raw.as_ref().unwrap()
190 }
191 pub(crate) fn require_features(&self, feature: wgt::Features) -> Result<(), MissingFeatures> {
192 if self.features.contains(feature) {
193 Ok(())
194 } else {
195 Err(MissingFeatures(feature))
196 }
197 }
198
199 pub(crate) fn require_downlevel_flags(
200 &self,
201 flags: wgt::DownlevelFlags,
202 ) -> Result<(), MissingDownlevelFlags> {
203 if self.downlevel.flags.contains(flags) {
204 Ok(())
205 } else {
206 Err(MissingDownlevelFlags(flags))
207 }
208 }
209}
210
211impl<A: HalApi> Device<A> {
212 pub(crate) fn new(
213 raw_device: A::Device,
214 raw_queue: &A::Queue,
215 adapter: &Arc<Adapter<A>>,
216 desc: &DeviceDescriptor,
217 trace_path: Option<&std::path::Path>,
218 instance_flags: wgt::InstanceFlags,
219 ) -> Result<Self, CreateDeviceError> {
220 #[cfg(not(feature = "trace"))]
221 if let Some(_) = trace_path {
222 log::error!("Feature 'trace' is not enabled");
223 }
224 let fence =
225 unsafe { raw_device.create_fence() }.map_err(|_| CreateDeviceError::OutOfMemory)?;
226
227 let command_allocator = command::CommandAllocator::new();
228 let pending_encoder = command_allocator
229 .acquire_encoder(&raw_device, raw_queue)
230 .map_err(|_| CreateDeviceError::OutOfMemory)?;
231 let mut pending_writes = queue::PendingWrites::<A>::new(pending_encoder);
232
233 let zero_buffer = unsafe {
235 raw_device
236 .create_buffer(&hal::BufferDescriptor {
237 label: hal_label(Some("(wgpu internal) zero init buffer"), instance_flags),
238 size: ZERO_BUFFER_SIZE,
239 usage: hal::BufferUses::COPY_SRC | hal::BufferUses::COPY_DST,
240 memory_flags: hal::MemoryFlags::empty(),
241 })
242 .map_err(DeviceError::from)?
243 };
244 pending_writes.activate();
245 unsafe {
246 pending_writes
247 .command_encoder
248 .transition_buffers(iter::once(hal::BufferBarrier {
249 buffer: &zero_buffer,
250 usage: hal::BufferUses::empty()..hal::BufferUses::COPY_DST,
251 }));
252 pending_writes
253 .command_encoder
254 .clear_buffer(&zero_buffer, 0..ZERO_BUFFER_SIZE);
255 pending_writes
256 .command_encoder
257 .transition_buffers(iter::once(hal::BufferBarrier {
258 buffer: &zero_buffer,
259 usage: hal::BufferUses::COPY_DST..hal::BufferUses::COPY_SRC,
260 }));
261 }
262
263 let alignments = adapter.raw.capabilities.alignments.clone();
264 let downlevel = adapter.raw.capabilities.downlevel.clone();
265
266 Ok(Self {
267 raw: Some(raw_device),
268 adapter: adapter.clone(),
269 queue: OnceCell::new(),
270 queue_to_drop: OnceCell::new(),
271 zero_buffer: Some(zero_buffer),
272 info: ResourceInfo::new("<device>", None),
273 command_allocator,
274 active_submission_index: AtomicU64::new(0),
275 fence: RwLock::new(rank::DEVICE_FENCE, Some(fence)),
276 snatchable_lock: unsafe { SnatchLock::new(rank::DEVICE_SNATCHABLE_LOCK) },
277 valid: AtomicBool::new(true),
278 trackers: Mutex::new(rank::DEVICE_TRACKERS, Tracker::new()),
279 tracker_indices: TrackerIndexAllocators::new(),
280 life_tracker: Mutex::new(rank::DEVICE_LIFE_TRACKER, life::LifetimeTracker::new()),
281 temp_suspected: Mutex::new(
282 rank::DEVICE_TEMP_SUSPECTED,
283 Some(life::ResourceMaps::new()),
284 ),
285 bgl_pool: ResourcePool::new(),
286 #[cfg(feature = "trace")]
287 trace: Mutex::new(
288 rank::DEVICE_TRACE,
289 trace_path.and_then(|path| match trace::Trace::new(path) {
290 Ok(mut trace) => {
291 trace.add(trace::Action::Init {
292 desc: desc.clone(),
293 backend: A::VARIANT,
294 });
295 Some(trace)
296 }
297 Err(e) => {
298 log::error!("Unable to start a trace in '{path:?}': {e}");
299 None
300 }
301 }),
302 ),
303 alignments,
304 limits: desc.required_limits.clone(),
305 features: desc.required_features,
306 downlevel,
307 instance_flags,
308 pending_writes: Mutex::new(rank::DEVICE_PENDING_WRITES, Some(pending_writes)),
309 deferred_destroy: Mutex::new(rank::DEVICE_DEFERRED_DESTROY, Vec::new()),
310 usage_scopes: Mutex::new(rank::DEVICE_USAGE_SCOPES, Default::default()),
311 })
312 }
313
314 pub fn is_valid(&self) -> bool {
315 self.valid.load(Ordering::Acquire)
316 }
317
318 pub(crate) fn release_queue(&self, queue: A::Queue) {
319 assert!(self.queue_to_drop.set(queue).is_ok());
320 }
321
322 pub(crate) fn lock_life<'a>(&'a self) -> MutexGuard<'a, LifetimeTracker<A>> {
323 self.life_tracker.lock()
324 }
325
326 pub(crate) fn deferred_resource_destruction(&self) {
334 while let Some(item) = self.deferred_destroy.lock().pop() {
335 match item {
336 DeferredDestroy::TextureView(view) => {
337 let Some(view) = view.upgrade() else {
338 continue;
339 };
340 let Some(raw_view) = view.raw.snatch(self.snatchable_lock.write()) else {
341 continue;
342 };
343
344 resource_log!("Destroy raw TextureView (destroyed) {:?}", view.label());
345 #[cfg(feature = "trace")]
346 if let Some(t) = self.trace.lock().as_mut() {
347 t.add(trace::Action::DestroyTextureView(view.info.id()));
348 }
349 unsafe {
350 use hal::Device;
351 self.raw().destroy_texture_view(raw_view);
352 }
353 }
354 DeferredDestroy::BindGroup(bind_group) => {
355 let Some(bind_group) = bind_group.upgrade() else {
356 continue;
357 };
358 let Some(raw_bind_group) = bind_group.raw.snatch(self.snatchable_lock.write())
359 else {
360 continue;
361 };
362
363 resource_log!("Destroy raw BindGroup (destroyed) {:?}", bind_group.label());
364 #[cfg(feature = "trace")]
365 if let Some(t) = self.trace.lock().as_mut() {
366 t.add(trace::Action::DestroyBindGroup(bind_group.info.id()));
367 }
368 unsafe {
369 use hal::Device;
370 self.raw().destroy_bind_group(raw_bind_group);
371 }
372 }
373 }
374 }
375 }
376
377 pub fn get_queue(&self) -> Option<Arc<Queue<A>>> {
378 self.queue.get().as_ref()?.upgrade()
379 }
380
381 pub fn set_queue(&self, queue: Arc<Queue<A>>) {
382 assert!(self.queue.set(Arc::downgrade(&queue)).is_ok());
383 }
384
385 pub(crate) fn maintain<'this>(
399 &'this self,
400 fence: &A::Fence,
401 maintain: wgt::Maintain<queue::WrappedSubmissionIndex>,
402 snatch_guard: SnatchGuard,
403 ) -> Result<(UserClosures, bool), WaitIdleError> {
404 profiling::scope!("Device::maintain");
405 let last_done_index = if maintain.is_wait() {
406 let index_to_wait_for = match maintain {
407 wgt::Maintain::WaitForSubmissionIndex(submission_index) => {
408 submission_index.index
411 }
412 _ => self.active_submission_index.load(Ordering::Relaxed),
413 };
414 unsafe {
415 self.raw
416 .as_ref()
417 .unwrap()
418 .wait(fence, index_to_wait_for, CLEANUP_WAIT_MS)
419 .map_err(DeviceError::from)?
420 };
421 index_to_wait_for
422 } else {
423 unsafe {
424 self.raw
425 .as_ref()
426 .unwrap()
427 .get_fence_value(fence)
428 .map_err(DeviceError::from)?
429 }
430 };
431
432 let mut life_tracker = self.lock_life();
433 let submission_closures =
434 life_tracker.triage_submissions(last_done_index, &self.command_allocator);
435
436 {
437 let temp_suspected = self
443 .temp_suspected
444 .lock()
445 .replace(ResourceMaps::new())
446 .unwrap();
447
448 life_tracker.suspected_resources.extend(temp_suspected);
449
450 life_tracker.triage_suspected(&self.trackers);
451 life_tracker.triage_mapped();
452 }
453
454 let mapping_closures =
455 life_tracker.handle_mapping(self.raw(), &self.trackers, &snatch_guard);
456
457 let queue_empty = life_tracker.queue_empty();
458
459 let mut device_lost_invocations = SmallVec::new();
465 let mut should_release_gpu_resource = false;
466 if !self.is_valid() && queue_empty {
467 should_release_gpu_resource = true;
470
471 if life_tracker.device_lost_closure.is_some() {
474 device_lost_invocations.push(DeviceLostInvocation {
475 closure: life_tracker.device_lost_closure.take().unwrap(),
476 reason: DeviceLostReason::Destroyed,
477 message: String::new(),
478 });
479 }
480 }
481
482 drop(life_tracker);
484 drop(snatch_guard);
485
486 if should_release_gpu_resource {
487 self.release_gpu_resources();
488 }
489
490 let closures = UserClosures {
491 mappings: mapping_closures,
492 submissions: submission_closures,
493 device_lost_invocations,
494 };
495 Ok((closures, queue_empty))
496 }
497
498 pub(crate) fn untrack(&self, trackers: &Tracker<A>) {
499 let mut temp_suspected = self
500 .temp_suspected
501 .lock()
502 .replace(ResourceMaps::new())
503 .unwrap();
504 temp_suspected.clear();
505 {
508 for resource in trackers.buffers.used_resources() {
509 if resource.is_unique() {
510 temp_suspected
511 .buffers
512 .insert(resource.as_info().tracker_index(), resource.clone());
513 }
514 }
515 for resource in trackers.textures.used_resources() {
516 if resource.is_unique() {
517 temp_suspected
518 .textures
519 .insert(resource.as_info().tracker_index(), resource.clone());
520 }
521 }
522 for resource in trackers.views.used_resources() {
523 if resource.is_unique() {
524 temp_suspected
525 .texture_views
526 .insert(resource.as_info().tracker_index(), resource.clone());
527 }
528 }
529 for resource in trackers.bind_groups.used_resources() {
530 if resource.is_unique() {
531 temp_suspected
532 .bind_groups
533 .insert(resource.as_info().tracker_index(), resource.clone());
534 }
535 }
536 for resource in trackers.samplers.used_resources() {
537 if resource.is_unique() {
538 temp_suspected
539 .samplers
540 .insert(resource.as_info().tracker_index(), resource.clone());
541 }
542 }
543 for resource in trackers.compute_pipelines.used_resources() {
544 if resource.is_unique() {
545 temp_suspected
546 .compute_pipelines
547 .insert(resource.as_info().tracker_index(), resource.clone());
548 }
549 }
550 for resource in trackers.render_pipelines.used_resources() {
551 if resource.is_unique() {
552 temp_suspected
553 .render_pipelines
554 .insert(resource.as_info().tracker_index(), resource.clone());
555 }
556 }
557 for resource in trackers.query_sets.used_resources() {
558 if resource.is_unique() {
559 temp_suspected
560 .query_sets
561 .insert(resource.as_info().tracker_index(), resource.clone());
562 }
563 }
564 }
565 self.lock_life().suspected_resources.extend(temp_suspected);
566 }
567
568 pub(crate) fn create_buffer(
569 self: &Arc<Self>,
570 desc: &resource::BufferDescriptor,
571 transient: bool,
572 ) -> Result<Buffer<A>, resource::CreateBufferError> {
573 debug_assert_eq!(self.as_info().id().backend(), A::VARIANT);
574
575 if desc.size > self.limits.max_buffer_size {
576 return Err(resource::CreateBufferError::MaxBufferSize {
577 requested: desc.size,
578 maximum: self.limits.max_buffer_size,
579 });
580 }
581
582 if desc.usage.contains(wgt::BufferUsages::INDEX)
583 && desc.usage.contains(
584 wgt::BufferUsages::VERTEX
585 | wgt::BufferUsages::UNIFORM
586 | wgt::BufferUsages::INDIRECT
587 | wgt::BufferUsages::STORAGE,
588 )
589 {
590 self.require_downlevel_flags(wgt::DownlevelFlags::UNRESTRICTED_INDEX_BUFFER)?;
591 }
592
593 let mut usage = conv::map_buffer_usage(desc.usage);
594
595 if desc.usage.is_empty() || desc.usage.contains_invalid_bits() {
596 return Err(resource::CreateBufferError::InvalidUsage(desc.usage));
597 }
598
599 if !self
600 .features
601 .contains(wgt::Features::MAPPABLE_PRIMARY_BUFFERS)
602 {
603 use wgt::BufferUsages as Bu;
604 let write_mismatch = desc.usage.contains(Bu::MAP_WRITE)
605 && !(Bu::MAP_WRITE | Bu::COPY_SRC).contains(desc.usage);
606 let read_mismatch = desc.usage.contains(Bu::MAP_READ)
607 && !(Bu::MAP_READ | Bu::COPY_DST).contains(desc.usage);
608 if write_mismatch || read_mismatch {
609 return Err(resource::CreateBufferError::UsageMismatch(desc.usage));
610 }
611 }
612
613 if desc.mapped_at_creation {
614 if desc.size % wgt::COPY_BUFFER_ALIGNMENT != 0 {
615 return Err(resource::CreateBufferError::UnalignedSize);
616 }
617 if !desc.usage.contains(wgt::BufferUsages::MAP_WRITE) {
618 usage |= hal::BufferUses::COPY_DST;
620 }
621 } else {
622 usage |= hal::BufferUses::COPY_DST;
625 }
626
627 let actual_size = if desc.size == 0 {
628 wgt::COPY_BUFFER_ALIGNMENT
629 } else if desc.usage.contains(wgt::BufferUsages::VERTEX) {
630 desc.size + 1
633 } else {
634 desc.size
635 };
636 let clear_remainder = actual_size % wgt::COPY_BUFFER_ALIGNMENT;
637 let aligned_size = if clear_remainder != 0 {
638 actual_size + wgt::COPY_BUFFER_ALIGNMENT - clear_remainder
639 } else {
640 actual_size
641 };
642
643 let mut memory_flags = hal::MemoryFlags::empty();
644 memory_flags.set(hal::MemoryFlags::TRANSIENT, transient);
645
646 let hal_desc = hal::BufferDescriptor {
647 label: desc.label.to_hal(self.instance_flags),
648 size: aligned_size,
649 usage,
650 memory_flags,
651 };
652 let buffer = unsafe { self.raw().create_buffer(&hal_desc) }.map_err(DeviceError::from)?;
653
654 Ok(Buffer {
655 raw: Snatchable::new(buffer),
656 device: self.clone(),
657 usage: desc.usage,
658 size: desc.size,
659 initialization_status: RwLock::new(
660 rank::BUFFER_INITIALIZATION_STATUS,
661 BufferInitTracker::new(aligned_size),
662 ),
663 sync_mapped_writes: Mutex::new(rank::BUFFER_SYNC_MAPPED_WRITES, None),
664 map_state: Mutex::new(rank::BUFFER_MAP_STATE, resource::BufferMapState::Idle),
665 info: ResourceInfo::new(
666 desc.label.borrow_or_default(),
667 Some(self.tracker_indices.buffers.clone()),
668 ),
669 bind_groups: Mutex::new(rank::BUFFER_BIND_GROUPS, Vec::new()),
670 })
671 }
672
673 pub(crate) fn create_texture_from_hal(
674 self: &Arc<Self>,
675 hal_texture: A::Texture,
676 hal_usage: hal::TextureUses,
677 desc: &resource::TextureDescriptor,
678 format_features: wgt::TextureFormatFeatures,
679 clear_mode: resource::TextureClearMode<A>,
680 ) -> Texture<A> {
681 debug_assert_eq!(self.as_info().id().backend(), A::VARIANT);
682
683 Texture {
684 inner: Snatchable::new(resource::TextureInner::Native { raw: hal_texture }),
685 device: self.clone(),
686 desc: desc.map_label(|_| ()),
687 hal_usage,
688 format_features,
689 initialization_status: RwLock::new(
690 rank::TEXTURE_INITIALIZATION_STATUS,
691 TextureInitTracker::new(desc.mip_level_count, desc.array_layer_count()),
692 ),
693 full_range: TextureSelector {
694 mips: 0..desc.mip_level_count,
695 layers: 0..desc.array_layer_count(),
696 },
697 info: ResourceInfo::new(
698 desc.label.borrow_or_default(),
699 Some(self.tracker_indices.textures.clone()),
700 ),
701 clear_mode: RwLock::new(rank::TEXTURE_CLEAR_MODE, clear_mode),
702 views: Mutex::new(rank::TEXTURE_VIEWS, Vec::new()),
703 bind_groups: Mutex::new(rank::TEXTURE_BIND_GROUPS, Vec::new()),
704 }
705 }
706
707 pub fn create_buffer_from_hal(
708 self: &Arc<Self>,
709 hal_buffer: A::Buffer,
710 desc: &resource::BufferDescriptor,
711 ) -> Buffer<A> {
712 debug_assert_eq!(self.as_info().id().backend(), A::VARIANT);
713
714 Buffer {
715 raw: Snatchable::new(hal_buffer),
716 device: self.clone(),
717 usage: desc.usage,
718 size: desc.size,
719 initialization_status: RwLock::new(
720 rank::BUFFER_INITIALIZATION_STATUS,
721 BufferInitTracker::new(0),
722 ),
723 sync_mapped_writes: Mutex::new(rank::BUFFER_SYNC_MAPPED_WRITES, None),
724 map_state: Mutex::new(rank::BUFFER_MAP_STATE, resource::BufferMapState::Idle),
725 info: ResourceInfo::new(
726 desc.label.borrow_or_default(),
727 Some(self.tracker_indices.buffers.clone()),
728 ),
729 bind_groups: Mutex::new(rank::BUFFER_BIND_GROUPS, Vec::new()),
730 }
731 }
732
733 pub(crate) fn create_texture(
734 self: &Arc<Self>,
735 adapter: &Adapter<A>,
736 desc: &resource::TextureDescriptor,
737 ) -> Result<Texture<A>, resource::CreateTextureError> {
738 use resource::{CreateTextureError, TextureDimensionError};
739
740 if desc.usage.is_empty() || desc.usage.contains_invalid_bits() {
741 return Err(CreateTextureError::InvalidUsage(desc.usage));
742 }
743
744 conv::check_texture_dimension_size(
745 desc.dimension,
746 desc.size,
747 desc.sample_count,
748 &self.limits,
749 )?;
750
751 if desc.dimension != wgt::TextureDimension::D2 {
752 if desc.format.is_depth_stencil_format() {
754 return Err(CreateTextureError::InvalidDepthDimension(
755 desc.dimension,
756 desc.format,
757 ));
758 }
759 if desc.usage.contains(wgt::TextureUsages::RENDER_ATTACHMENT) {
761 return Err(CreateTextureError::InvalidDimensionUsages(
762 wgt::TextureUsages::RENDER_ATTACHMENT,
763 desc.dimension,
764 ));
765 }
766
767 if desc.format.is_compressed() {
769 return Err(CreateTextureError::InvalidCompressedDimension(
770 desc.dimension,
771 desc.format,
772 ));
773 }
774 }
775
776 if desc.format.is_compressed() {
777 let (block_width, block_height) = desc.format.block_dimensions();
778
779 if desc.size.width % block_width != 0 {
780 return Err(CreateTextureError::InvalidDimension(
781 TextureDimensionError::NotMultipleOfBlockWidth {
782 width: desc.size.width,
783 block_width,
784 format: desc.format,
785 },
786 ));
787 }
788
789 if desc.size.height % block_height != 0 {
790 return Err(CreateTextureError::InvalidDimension(
791 TextureDimensionError::NotMultipleOfBlockHeight {
792 height: desc.size.height,
793 block_height,
794 format: desc.format,
795 },
796 ));
797 }
798 }
799
800 {
801 let (width_multiple, height_multiple) = desc.format.size_multiple_requirement();
802
803 if desc.size.width % width_multiple != 0 {
804 return Err(CreateTextureError::InvalidDimension(
805 TextureDimensionError::WidthNotMultipleOf {
806 width: desc.size.width,
807 multiple: width_multiple,
808 format: desc.format,
809 },
810 ));
811 }
812
813 if desc.size.height % height_multiple != 0 {
814 return Err(CreateTextureError::InvalidDimension(
815 TextureDimensionError::HeightNotMultipleOf {
816 height: desc.size.height,
817 multiple: height_multiple,
818 format: desc.format,
819 },
820 ));
821 }
822 }
823
824 let format_features = self
825 .describe_format_features(adapter, desc.format)
826 .map_err(|error| CreateTextureError::MissingFeatures(desc.format, error))?;
827
828 if desc.sample_count > 1 {
829 if desc.mip_level_count != 1 {
830 return Err(CreateTextureError::InvalidMipLevelCount {
831 requested: desc.mip_level_count,
832 maximum: 1,
833 });
834 }
835
836 if desc.size.depth_or_array_layers != 1 {
837 return Err(CreateTextureError::InvalidDimension(
838 TextureDimensionError::MultisampledDepthOrArrayLayer(
839 desc.size.depth_or_array_layers,
840 ),
841 ));
842 }
843
844 if desc.usage.contains(wgt::TextureUsages::STORAGE_BINDING) {
845 return Err(CreateTextureError::InvalidMultisampledStorageBinding);
846 }
847
848 if !desc.usage.contains(wgt::TextureUsages::RENDER_ATTACHMENT) {
849 return Err(CreateTextureError::MultisampledNotRenderAttachment);
850 }
851
852 if !format_features.flags.intersects(
853 wgt::TextureFormatFeatureFlags::MULTISAMPLE_X4
854 | wgt::TextureFormatFeatureFlags::MULTISAMPLE_X2
855 | wgt::TextureFormatFeatureFlags::MULTISAMPLE_X8
856 | wgt::TextureFormatFeatureFlags::MULTISAMPLE_X16,
857 ) {
858 return Err(CreateTextureError::InvalidMultisampledFormat(desc.format));
859 }
860
861 if !format_features
862 .flags
863 .sample_count_supported(desc.sample_count)
864 {
865 return Err(CreateTextureError::InvalidSampleCount(
866 desc.sample_count,
867 desc.format,
868 desc.format
869 .guaranteed_format_features(self.features)
870 .flags
871 .supported_sample_counts(),
872 adapter
873 .get_texture_format_features(desc.format)
874 .flags
875 .supported_sample_counts(),
876 ));
877 };
878 }
879
880 let mips = desc.mip_level_count;
881 let max_levels_allowed = desc.size.max_mips(desc.dimension).min(hal::MAX_MIP_LEVELS);
882 if mips == 0 || mips > max_levels_allowed {
883 return Err(CreateTextureError::InvalidMipLevelCount {
884 requested: mips,
885 maximum: max_levels_allowed,
886 });
887 }
888
889 let missing_allowed_usages = desc.usage - format_features.allowed_usages;
890 if !missing_allowed_usages.is_empty() {
891 let wgpu_allowed_usages = desc
893 .format
894 .guaranteed_format_features(self.features)
895 .allowed_usages;
896 let wgpu_missing_usages = desc.usage - wgpu_allowed_usages;
897 return Err(CreateTextureError::InvalidFormatUsages(
898 missing_allowed_usages,
899 desc.format,
900 wgpu_missing_usages.is_empty(),
901 ));
902 }
903
904 let mut hal_view_formats = vec![];
905 for format in desc.view_formats.iter() {
906 if desc.format == *format {
907 continue;
908 }
909 if desc.format.remove_srgb_suffix() != format.remove_srgb_suffix() {
910 return Err(CreateTextureError::InvalidViewFormat(*format, desc.format));
911 }
912 hal_view_formats.push(*format);
913 }
914 if !hal_view_formats.is_empty() {
915 self.require_downlevel_flags(wgt::DownlevelFlags::VIEW_FORMATS)?;
916 }
917
918 let hal_usage = conv::map_texture_usage_for_texture(desc, &format_features);
919
920 let hal_desc = hal::TextureDescriptor {
921 label: desc.label.to_hal(self.instance_flags),
922 size: desc.size,
923 mip_level_count: desc.mip_level_count,
924 sample_count: desc.sample_count,
925 dimension: desc.dimension,
926 format: desc.format,
927 usage: hal_usage,
928 memory_flags: hal::MemoryFlags::empty(),
929 view_formats: hal_view_formats,
930 };
931
932 let raw_texture = unsafe {
933 self.raw
934 .as_ref()
935 .unwrap()
936 .create_texture(&hal_desc)
937 .map_err(DeviceError::from)?
938 };
939
940 let clear_mode = if hal_usage
941 .intersects(hal::TextureUses::DEPTH_STENCIL_WRITE | hal::TextureUses::COLOR_TARGET)
942 {
943 let (is_color, usage) = if desc.format.is_depth_stencil_format() {
944 (false, hal::TextureUses::DEPTH_STENCIL_WRITE)
945 } else {
946 (true, hal::TextureUses::COLOR_TARGET)
947 };
948 let dimension = match desc.dimension {
949 wgt::TextureDimension::D1 => wgt::TextureViewDimension::D1,
950 wgt::TextureDimension::D2 => wgt::TextureViewDimension::D2,
951 wgt::TextureDimension::D3 => unreachable!(),
952 };
953
954 let clear_label = hal_label(
955 Some("(wgpu internal) clear texture view"),
956 self.instance_flags,
957 );
958
959 let mut clear_views = SmallVec::new();
960 for mip_level in 0..desc.mip_level_count {
961 for array_layer in 0..desc.size.depth_or_array_layers {
962 macro_rules! push_clear_view {
963 ($format:expr, $aspect:expr) => {
964 let desc = hal::TextureViewDescriptor {
965 label: clear_label,
966 format: $format,
967 dimension,
968 usage,
969 range: wgt::ImageSubresourceRange {
970 aspect: $aspect,
971 base_mip_level: mip_level,
972 mip_level_count: Some(1),
973 base_array_layer: array_layer,
974 array_layer_count: Some(1),
975 },
976 };
977 clear_views.push(Some(
978 unsafe { self.raw().create_texture_view(&raw_texture, &desc) }
979 .map_err(DeviceError::from)?,
980 ));
981 };
982 }
983
984 if let Some(planes) = desc.format.planes() {
985 for plane in 0..planes {
986 let aspect = wgt::TextureAspect::from_plane(plane).unwrap();
987 let format = desc.format.aspect_specific_format(aspect).unwrap();
988 push_clear_view!(format, aspect);
989 }
990 } else {
991 push_clear_view!(desc.format, wgt::TextureAspect::All);
992 }
993 }
994 }
995 resource::TextureClearMode::RenderPass {
996 clear_views,
997 is_color,
998 }
999 } else {
1000 resource::TextureClearMode::BufferCopy
1001 };
1002
1003 let mut texture =
1004 self.create_texture_from_hal(raw_texture, hal_usage, desc, format_features, clear_mode);
1005 texture.hal_usage = hal_usage;
1006 Ok(texture)
1007 }
1008
1009 pub(crate) fn create_texture_view(
1010 self: &Arc<Self>,
1011 texture: &Arc<Texture<A>>,
1012 desc: &resource::TextureViewDescriptor,
1013 ) -> Result<TextureView<A>, resource::CreateTextureViewError> {
1014 let snatch_guard = texture.device.snatchable_lock.read();
1015
1016 let texture_raw = texture
1017 .raw(&snatch_guard)
1018 .ok_or(resource::CreateTextureViewError::InvalidTexture)?;
1019
1020 let resolved_format = desc.format.unwrap_or_else(|| {
1023 texture
1024 .desc
1025 .format
1026 .aspect_specific_format(desc.range.aspect)
1027 .unwrap_or(texture.desc.format)
1028 });
1029
1030 let resolved_dimension = desc
1031 .dimension
1032 .unwrap_or_else(|| match texture.desc.dimension {
1033 wgt::TextureDimension::D1 => wgt::TextureViewDimension::D1,
1034 wgt::TextureDimension::D2 => {
1035 if texture.desc.array_layer_count() == 1 {
1036 wgt::TextureViewDimension::D2
1037 } else {
1038 wgt::TextureViewDimension::D2Array
1039 }
1040 }
1041 wgt::TextureDimension::D3 => wgt::TextureViewDimension::D3,
1042 });
1043
1044 let resolved_mip_level_count = desc.range.mip_level_count.unwrap_or_else(|| {
1045 texture
1046 .desc
1047 .mip_level_count
1048 .saturating_sub(desc.range.base_mip_level)
1049 });
1050
1051 let resolved_array_layer_count =
1052 desc.range
1053 .array_layer_count
1054 .unwrap_or_else(|| match resolved_dimension {
1055 wgt::TextureViewDimension::D1
1056 | wgt::TextureViewDimension::D2
1057 | wgt::TextureViewDimension::D3 => 1,
1058 wgt::TextureViewDimension::Cube => 6,
1059 wgt::TextureViewDimension::D2Array | wgt::TextureViewDimension::CubeArray => {
1060 texture
1061 .desc
1062 .array_layer_count()
1063 .saturating_sub(desc.range.base_array_layer)
1064 }
1065 });
1066
1067 let aspects = hal::FormatAspects::new(texture.desc.format, desc.range.aspect);
1070 if aspects.is_empty() {
1071 return Err(resource::CreateTextureViewError::InvalidAspect {
1072 texture_format: texture.desc.format,
1073 requested_aspect: desc.range.aspect,
1074 });
1075 }
1076
1077 let format_is_good = if desc.range.aspect == wgt::TextureAspect::All {
1078 resolved_format == texture.desc.format
1079 || texture.desc.view_formats.contains(&resolved_format)
1080 } else {
1081 Some(resolved_format)
1082 == texture
1083 .desc
1084 .format
1085 .aspect_specific_format(desc.range.aspect)
1086 };
1087 if !format_is_good {
1088 return Err(resource::CreateTextureViewError::FormatReinterpretation {
1089 texture: texture.desc.format,
1090 view: resolved_format,
1091 });
1092 }
1093
1094 if texture.desc.sample_count > 1 && resolved_dimension != wgt::TextureViewDimension::D2 {
1096 return Err(
1097 resource::CreateTextureViewError::InvalidMultisampledTextureViewDimension(
1098 resolved_dimension,
1099 ),
1100 );
1101 }
1102
1103 if texture.desc.dimension != resolved_dimension.compatible_texture_dimension() {
1105 return Err(
1106 resource::CreateTextureViewError::InvalidTextureViewDimension {
1107 view: resolved_dimension,
1108 texture: texture.desc.dimension,
1109 },
1110 );
1111 }
1112
1113 match resolved_dimension {
1114 TextureViewDimension::D1 | TextureViewDimension::D2 | TextureViewDimension::D3 => {
1115 if resolved_array_layer_count != 1 {
1116 return Err(resource::CreateTextureViewError::InvalidArrayLayerCount {
1117 requested: resolved_array_layer_count,
1118 dim: resolved_dimension,
1119 });
1120 }
1121 }
1122 TextureViewDimension::Cube => {
1123 if resolved_array_layer_count != 6 {
1124 return Err(
1125 resource::CreateTextureViewError::InvalidCubemapTextureDepth {
1126 depth: resolved_array_layer_count,
1127 },
1128 );
1129 }
1130 }
1131 TextureViewDimension::CubeArray => {
1132 if resolved_array_layer_count % 6 != 0 {
1133 return Err(
1134 resource::CreateTextureViewError::InvalidCubemapArrayTextureDepth {
1135 depth: resolved_array_layer_count,
1136 },
1137 );
1138 }
1139 }
1140 _ => {}
1141 }
1142
1143 match resolved_dimension {
1144 TextureViewDimension::Cube | TextureViewDimension::CubeArray => {
1145 if texture.desc.size.width != texture.desc.size.height {
1146 return Err(resource::CreateTextureViewError::InvalidCubeTextureViewSize);
1147 }
1148 }
1149 _ => {}
1150 }
1151
1152 if resolved_mip_level_count == 0 {
1153 return Err(resource::CreateTextureViewError::ZeroMipLevelCount);
1154 }
1155
1156 let mip_level_end = desc
1157 .range
1158 .base_mip_level
1159 .saturating_add(resolved_mip_level_count);
1160
1161 let level_end = texture.desc.mip_level_count;
1162 if mip_level_end > level_end {
1163 return Err(resource::CreateTextureViewError::TooManyMipLevels {
1164 requested: mip_level_end,
1165 total: level_end,
1166 });
1167 }
1168
1169 if resolved_array_layer_count == 0 {
1170 return Err(resource::CreateTextureViewError::ZeroArrayLayerCount);
1171 }
1172
1173 let array_layer_end = desc
1174 .range
1175 .base_array_layer
1176 .saturating_add(resolved_array_layer_count);
1177
1178 let layer_end = texture.desc.array_layer_count();
1179 if array_layer_end > layer_end {
1180 return Err(resource::CreateTextureViewError::TooManyArrayLayers {
1181 requested: array_layer_end,
1182 total: layer_end,
1183 });
1184 };
1185
1186 let render_extent = 'b: loop {
1188 if !texture
1189 .desc
1190 .usage
1191 .contains(wgt::TextureUsages::RENDER_ATTACHMENT)
1192 {
1193 break 'b Err(TextureViewNotRenderableReason::Usage(texture.desc.usage));
1194 }
1195
1196 if !(resolved_dimension == TextureViewDimension::D2
1197 || (self.features.contains(wgt::Features::MULTIVIEW)
1198 && resolved_dimension == TextureViewDimension::D2Array))
1199 {
1200 break 'b Err(TextureViewNotRenderableReason::Dimension(
1201 resolved_dimension,
1202 ));
1203 }
1204
1205 if resolved_mip_level_count != 1 {
1206 break 'b Err(TextureViewNotRenderableReason::MipLevelCount(
1207 resolved_mip_level_count,
1208 ));
1209 }
1210
1211 if resolved_array_layer_count != 1
1212 && !(self.features.contains(wgt::Features::MULTIVIEW))
1213 {
1214 break 'b Err(TextureViewNotRenderableReason::ArrayLayerCount(
1215 resolved_array_layer_count,
1216 ));
1217 }
1218
1219 if aspects != hal::FormatAspects::from(texture.desc.format) {
1220 break 'b Err(TextureViewNotRenderableReason::Aspects(aspects));
1221 }
1222
1223 break 'b Ok(texture
1224 .desc
1225 .compute_render_extent(desc.range.base_mip_level));
1226 };
1227
1228 let usage = {
1230 let mask_copy = !(hal::TextureUses::COPY_SRC | hal::TextureUses::COPY_DST);
1231 let mask_dimension = match resolved_dimension {
1232 wgt::TextureViewDimension::Cube | wgt::TextureViewDimension::CubeArray => {
1233 hal::TextureUses::RESOURCE
1234 }
1235 wgt::TextureViewDimension::D3 => {
1236 hal::TextureUses::RESOURCE
1237 | hal::TextureUses::STORAGE_READ
1238 | hal::TextureUses::STORAGE_READ_WRITE
1239 }
1240 _ => hal::TextureUses::all(),
1241 };
1242 let mask_mip_level = if resolved_mip_level_count == 1 {
1243 hal::TextureUses::all()
1244 } else {
1245 hal::TextureUses::RESOURCE
1246 };
1247 texture.hal_usage & mask_copy & mask_dimension & mask_mip_level
1248 };
1249
1250 log::debug!(
1251 "Create view for texture {:?} filters usages to {:?}",
1252 texture.as_info().id(),
1253 usage
1254 );
1255
1256 let format = if resolved_format.is_depth_stencil_component(texture.desc.format) {
1258 texture.desc.format
1259 } else {
1260 resolved_format
1261 };
1262
1263 let resolved_range = wgt::ImageSubresourceRange {
1264 aspect: desc.range.aspect,
1265 base_mip_level: desc.range.base_mip_level,
1266 mip_level_count: Some(resolved_mip_level_count),
1267 base_array_layer: desc.range.base_array_layer,
1268 array_layer_count: Some(resolved_array_layer_count),
1269 };
1270
1271 let hal_desc = hal::TextureViewDescriptor {
1272 label: desc.label.to_hal(self.instance_flags),
1273 format,
1274 dimension: resolved_dimension,
1275 usage,
1276 range: resolved_range,
1277 };
1278
1279 let raw = unsafe {
1280 self.raw
1281 .as_ref()
1282 .unwrap()
1283 .create_texture_view(texture_raw, &hal_desc)
1284 .map_err(|_| resource::CreateTextureViewError::OutOfMemory)?
1285 };
1286
1287 let selector = TextureSelector {
1288 mips: desc.range.base_mip_level..mip_level_end,
1289 layers: desc.range.base_array_layer..array_layer_end,
1290 };
1291
1292 Ok(TextureView {
1293 raw: Snatchable::new(raw),
1294 parent: texture.clone(),
1295 device: self.clone(),
1296 desc: resource::HalTextureViewDescriptor {
1297 texture_format: texture.desc.format,
1298 format: resolved_format,
1299 dimension: resolved_dimension,
1300 range: resolved_range,
1301 },
1302 format_features: texture.format_features,
1303 render_extent,
1304 samples: texture.desc.sample_count,
1305 selector,
1306 info: ResourceInfo::new(
1307 desc.label.borrow_or_default(),
1308 Some(self.tracker_indices.texture_views.clone()),
1309 ),
1310 })
1311 }
1312
1313 pub(crate) fn create_sampler(
1314 self: &Arc<Self>,
1315 desc: &resource::SamplerDescriptor,
1316 ) -> Result<Sampler<A>, resource::CreateSamplerError> {
1317 if desc
1318 .address_modes
1319 .iter()
1320 .any(|am| am == &wgt::AddressMode::ClampToBorder)
1321 {
1322 self.require_features(wgt::Features::ADDRESS_MODE_CLAMP_TO_BORDER)?;
1323 }
1324
1325 if desc.border_color == Some(wgt::SamplerBorderColor::Zero) {
1326 self.require_features(wgt::Features::ADDRESS_MODE_CLAMP_TO_ZERO)?;
1327 }
1328
1329 if desc.lod_min_clamp < 0.0 {
1330 return Err(resource::CreateSamplerError::InvalidLodMinClamp(
1331 desc.lod_min_clamp,
1332 ));
1333 }
1334 if desc.lod_max_clamp < desc.lod_min_clamp {
1335 return Err(resource::CreateSamplerError::InvalidLodMaxClamp {
1336 lod_min_clamp: desc.lod_min_clamp,
1337 lod_max_clamp: desc.lod_max_clamp,
1338 });
1339 }
1340
1341 if desc.anisotropy_clamp < 1 {
1342 return Err(resource::CreateSamplerError::InvalidAnisotropy(
1343 desc.anisotropy_clamp,
1344 ));
1345 }
1346
1347 if desc.anisotropy_clamp != 1 {
1348 if !matches!(desc.min_filter, wgt::FilterMode::Linear) {
1349 return Err(
1350 resource::CreateSamplerError::InvalidFilterModeWithAnisotropy {
1351 filter_type: resource::SamplerFilterErrorType::MinFilter,
1352 filter_mode: desc.min_filter,
1353 anisotropic_clamp: desc.anisotropy_clamp,
1354 },
1355 );
1356 }
1357 if !matches!(desc.mag_filter, wgt::FilterMode::Linear) {
1358 return Err(
1359 resource::CreateSamplerError::InvalidFilterModeWithAnisotropy {
1360 filter_type: resource::SamplerFilterErrorType::MagFilter,
1361 filter_mode: desc.mag_filter,
1362 anisotropic_clamp: desc.anisotropy_clamp,
1363 },
1364 );
1365 }
1366 if !matches!(desc.mipmap_filter, wgt::FilterMode::Linear) {
1367 return Err(
1368 resource::CreateSamplerError::InvalidFilterModeWithAnisotropy {
1369 filter_type: resource::SamplerFilterErrorType::MipmapFilter,
1370 filter_mode: desc.mipmap_filter,
1371 anisotropic_clamp: desc.anisotropy_clamp,
1372 },
1373 );
1374 }
1375 }
1376
1377 let anisotropy_clamp = if self
1378 .downlevel
1379 .flags
1380 .contains(wgt::DownlevelFlags::ANISOTROPIC_FILTERING)
1381 {
1382 desc.anisotropy_clamp.min(16)
1384 } else {
1385 1
1387 };
1388
1389 let hal_desc = hal::SamplerDescriptor {
1392 label: desc.label.to_hal(self.instance_flags),
1393 address_modes: desc.address_modes,
1394 mag_filter: desc.mag_filter,
1395 min_filter: desc.min_filter,
1396 mipmap_filter: desc.mipmap_filter,
1397 lod_clamp: desc.lod_min_clamp..desc.lod_max_clamp,
1398 compare: desc.compare,
1399 anisotropy_clamp,
1400 border_color: desc.border_color,
1401 };
1402
1403 let raw = unsafe {
1404 self.raw
1405 .as_ref()
1406 .unwrap()
1407 .create_sampler(&hal_desc)
1408 .map_err(DeviceError::from)?
1409 };
1410 Ok(Sampler {
1411 raw: Some(raw),
1412 device: self.clone(),
1413 info: ResourceInfo::new(
1414 desc.label.borrow_or_default(),
1415 Some(self.tracker_indices.samplers.clone()),
1416 ),
1417 comparison: desc.compare.is_some(),
1418 filtering: desc.min_filter == wgt::FilterMode::Linear
1419 || desc.mag_filter == wgt::FilterMode::Linear,
1420 })
1421 }
1422
1423 pub(crate) fn create_shader_module<'a>(
1424 self: &Arc<Self>,
1425 desc: &pipeline::ShaderModuleDescriptor<'a>,
1426 source: pipeline::ShaderModuleSource<'a>,
1427 ) -> Result<pipeline::ShaderModule<A>, pipeline::CreateShaderModuleError> {
1428 let (module, source) = match source {
1429 #[cfg(feature = "wgsl")]
1430 pipeline::ShaderModuleSource::Wgsl(code) => {
1431 profiling::scope!("naga::front::wgsl::parse_str");
1432 let module = naga::front::wgsl::parse_str(&code).map_err(|inner| {
1433 pipeline::CreateShaderModuleError::Parsing(pipeline::ShaderError {
1434 source: code.to_string(),
1435 label: desc.label.as_ref().map(|l| l.to_string()),
1436 inner: Box::new(inner),
1437 })
1438 })?;
1439 (Cow::Owned(module), code.into_owned())
1440 }
1441 #[cfg(feature = "spirv")]
1442 pipeline::ShaderModuleSource::SpirV(spv, options) => {
1443 let parser = naga::front::spv::Frontend::new(spv.iter().cloned(), &options);
1444 profiling::scope!("naga::front::spv::Frontend");
1445 let module = parser.parse().map_err(|inner| {
1446 pipeline::CreateShaderModuleError::ParsingSpirV(pipeline::ShaderError {
1447 source: String::new(),
1448 label: desc.label.as_ref().map(|l| l.to_string()),
1449 inner: Box::new(inner),
1450 })
1451 })?;
1452 (Cow::Owned(module), String::new())
1453 }
1454 #[cfg(feature = "glsl")]
1455 pipeline::ShaderModuleSource::Glsl(code, options) => {
1456 let mut parser = naga::front::glsl::Frontend::default();
1457 profiling::scope!("naga::front::glsl::Frontend.parse");
1458 let module = parser.parse(&options, &code).map_err(|inner| {
1459 pipeline::CreateShaderModuleError::ParsingGlsl(pipeline::ShaderError {
1460 source: code.to_string(),
1461 label: desc.label.as_ref().map(|l| l.to_string()),
1462 inner: Box::new(inner),
1463 })
1464 })?;
1465 (Cow::Owned(module), code.into_owned())
1466 }
1467 pipeline::ShaderModuleSource::Naga(module) => (module, String::new()),
1468 pipeline::ShaderModuleSource::Dummy(_) => panic!("found `ShaderModuleSource::Dummy`"),
1469 };
1470 for (_, var) in module.global_variables.iter() {
1471 match var.binding {
1472 Some(ref br) if br.group >= self.limits.max_bind_groups => {
1473 return Err(pipeline::CreateShaderModuleError::InvalidGroupIndex {
1474 bind: br.clone(),
1475 group: br.group,
1476 limit: self.limits.max_bind_groups,
1477 });
1478 }
1479 _ => continue,
1480 };
1481 }
1482
1483 profiling::scope!("naga::validate");
1484 let debug_source =
1485 if self.instance_flags.contains(wgt::InstanceFlags::DEBUG) && !source.is_empty() {
1486 Some(hal::DebugSource {
1487 file_name: Cow::Owned(
1488 desc.label
1489 .as_ref()
1490 .map_or("shader".to_string(), |l| l.to_string()),
1491 ),
1492 source_code: Cow::Owned(source.clone()),
1493 })
1494 } else {
1495 None
1496 };
1497
1498 let info = self
1499 .create_validator(naga::valid::ValidationFlags::all())
1500 .validate(&module)
1501 .map_err(|inner| {
1502 pipeline::CreateShaderModuleError::Validation(pipeline::ShaderError {
1503 source,
1504 label: desc.label.as_ref().map(|l| l.to_string()),
1505 inner: Box::new(inner),
1506 })
1507 })?;
1508
1509 let interface =
1510 validation::Interface::new(&module, &info, self.limits.clone(), self.features);
1511 let hal_shader = hal::ShaderInput::Naga(hal::NagaShader {
1512 module,
1513 info,
1514 debug_source,
1515 });
1516 let hal_desc = hal::ShaderModuleDescriptor {
1517 label: desc.label.to_hal(self.instance_flags),
1518 runtime_checks: desc.shader_bound_checks.runtime_checks(),
1519 };
1520 let raw = match unsafe {
1521 self.raw
1522 .as_ref()
1523 .unwrap()
1524 .create_shader_module(&hal_desc, hal_shader)
1525 } {
1526 Ok(raw) => raw,
1527 Err(error) => {
1528 return Err(match error {
1529 hal::ShaderError::Device(error) => {
1530 pipeline::CreateShaderModuleError::Device(error.into())
1531 }
1532 hal::ShaderError::Compilation(ref msg) => {
1533 log::error!("Shader error: {}", msg);
1534 pipeline::CreateShaderModuleError::Generation
1535 }
1536 })
1537 }
1538 };
1539
1540 Ok(pipeline::ShaderModule {
1541 raw: Some(raw),
1542 device: self.clone(),
1543 interface: Some(interface),
1544 info: ResourceInfo::new(desc.label.borrow_or_default(), None),
1545 label: desc.label.borrow_or_default().to_string(),
1546 })
1547 }
1548
1549 pub fn create_validator(
1551 self: &Arc<Self>,
1552 flags: naga::valid::ValidationFlags,
1553 ) -> naga::valid::Validator {
1554 use naga::valid::Capabilities as Caps;
1555 let mut caps = Caps::empty();
1556 caps.set(
1557 Caps::PUSH_CONSTANT,
1558 self.features.contains(wgt::Features::PUSH_CONSTANTS),
1559 );
1560 caps.set(
1561 Caps::FLOAT64,
1562 self.features.contains(wgt::Features::SHADER_F64),
1563 );
1564 caps.set(
1565 Caps::PRIMITIVE_INDEX,
1566 self.features
1567 .contains(wgt::Features::SHADER_PRIMITIVE_INDEX),
1568 );
1569 caps.set(
1570 Caps::SAMPLED_TEXTURE_AND_STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING,
1571 self.features.contains(
1572 wgt::Features::SAMPLED_TEXTURE_AND_STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING,
1573 ),
1574 );
1575 caps.set(
1576 Caps::UNIFORM_BUFFER_AND_STORAGE_TEXTURE_ARRAY_NON_UNIFORM_INDEXING,
1577 self.features.contains(
1578 wgt::Features::UNIFORM_BUFFER_AND_STORAGE_TEXTURE_ARRAY_NON_UNIFORM_INDEXING,
1579 ),
1580 );
1581 caps.set(
1583 Caps::SAMPLER_NON_UNIFORM_INDEXING,
1584 self.features.contains(
1585 wgt::Features::SAMPLED_TEXTURE_AND_STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING,
1586 ),
1587 );
1588 caps.set(
1589 Caps::STORAGE_TEXTURE_16BIT_NORM_FORMATS,
1590 self.features
1591 .contains(wgt::Features::TEXTURE_FORMAT_16BIT_NORM),
1592 );
1593 caps.set(
1594 Caps::MULTIVIEW,
1595 self.features.contains(wgt::Features::MULTIVIEW),
1596 );
1597 caps.set(
1598 Caps::EARLY_DEPTH_TEST,
1599 self.features
1600 .contains(wgt::Features::SHADER_EARLY_DEPTH_TEST),
1601 );
1602 caps.set(
1603 Caps::SHADER_INT64,
1604 self.features.contains(wgt::Features::SHADER_INT64),
1605 );
1606 caps.set(
1607 Caps::MULTISAMPLED_SHADING,
1608 self.downlevel
1609 .flags
1610 .contains(wgt::DownlevelFlags::MULTISAMPLED_SHADING),
1611 );
1612 caps.set(
1613 Caps::DUAL_SOURCE_BLENDING,
1614 self.features.contains(wgt::Features::DUAL_SOURCE_BLENDING),
1615 );
1616 caps.set(
1617 Caps::CUBE_ARRAY_TEXTURES,
1618 self.downlevel
1619 .flags
1620 .contains(wgt::DownlevelFlags::CUBE_ARRAY_TEXTURES),
1621 );
1622 caps.set(
1623 Caps::SUBGROUP,
1624 self.features
1625 .intersects(wgt::Features::SUBGROUP | wgt::Features::SUBGROUP_VERTEX),
1626 );
1627 caps.set(
1628 Caps::SUBGROUP_BARRIER,
1629 self.features.intersects(wgt::Features::SUBGROUP_BARRIER),
1630 );
1631
1632 let mut subgroup_stages = naga::valid::ShaderStages::empty();
1633 subgroup_stages.set(
1634 naga::valid::ShaderStages::COMPUTE | naga::valid::ShaderStages::FRAGMENT,
1635 self.features.contains(wgt::Features::SUBGROUP),
1636 );
1637 subgroup_stages.set(
1638 naga::valid::ShaderStages::VERTEX,
1639 self.features.contains(wgt::Features::SUBGROUP_VERTEX),
1640 );
1641
1642 let subgroup_operations = if caps.contains(Caps::SUBGROUP) {
1643 use naga::valid::SubgroupOperationSet as S;
1644 S::BASIC | S::VOTE | S::ARITHMETIC | S::BALLOT | S::SHUFFLE | S::SHUFFLE_RELATIVE
1645 } else {
1646 naga::valid::SubgroupOperationSet::empty()
1647 };
1648 let mut validator = naga::valid::Validator::new(flags, caps);
1649 validator.subgroup_stages(subgroup_stages);
1650 validator.subgroup_operations(subgroup_operations);
1651 validator
1652 }
1653
1654 #[allow(unused_unsafe)]
1655 pub(crate) unsafe fn create_shader_module_spirv<'a>(
1656 self: &Arc<Self>,
1657 desc: &pipeline::ShaderModuleDescriptor<'a>,
1658 source: &'a [u32],
1659 ) -> Result<pipeline::ShaderModule<A>, pipeline::CreateShaderModuleError> {
1660 self.require_features(wgt::Features::SPIRV_SHADER_PASSTHROUGH)?;
1661 let hal_desc = hal::ShaderModuleDescriptor {
1662 label: desc.label.to_hal(self.instance_flags),
1663 runtime_checks: desc.shader_bound_checks.runtime_checks(),
1664 };
1665 let hal_shader = hal::ShaderInput::SpirV(source);
1666 let raw = match unsafe {
1667 self.raw
1668 .as_ref()
1669 .unwrap()
1670 .create_shader_module(&hal_desc, hal_shader)
1671 } {
1672 Ok(raw) => raw,
1673 Err(error) => {
1674 return Err(match error {
1675 hal::ShaderError::Device(error) => {
1676 pipeline::CreateShaderModuleError::Device(error.into())
1677 }
1678 hal::ShaderError::Compilation(ref msg) => {
1679 log::error!("Shader error: {}", msg);
1680 pipeline::CreateShaderModuleError::Generation
1681 }
1682 })
1683 }
1684 };
1685
1686 Ok(pipeline::ShaderModule {
1687 raw: Some(raw),
1688 device: self.clone(),
1689 interface: None,
1690 info: ResourceInfo::new(desc.label.borrow_or_default(), None),
1691 label: desc.label.borrow_or_default().to_string(),
1692 })
1693 }
1694
1695 pub(crate) fn make_late_sized_buffer_groups(
1698 shader_binding_sizes: &FastHashMap<naga::ResourceBinding, wgt::BufferSize>,
1699 layout: &binding_model::PipelineLayout<A>,
1700 ) -> ArrayVec<pipeline::LateSizedBufferGroup, { hal::MAX_BIND_GROUPS }> {
1701 layout
1705 .bind_group_layouts
1706 .iter()
1707 .enumerate()
1708 .map(|(group_index, bgl)| pipeline::LateSizedBufferGroup {
1709 shader_sizes: bgl
1710 .entries
1711 .values()
1712 .filter_map(|entry| match entry.ty {
1713 wgt::BindingType::Buffer {
1714 min_binding_size: None,
1715 ..
1716 } => {
1717 let rb = naga::ResourceBinding {
1718 group: group_index as u32,
1719 binding: entry.binding,
1720 };
1721 let shader_size =
1722 shader_binding_sizes.get(&rb).map_or(0, |nz| nz.get());
1723 Some(shader_size)
1724 }
1725 _ => None,
1726 })
1727 .collect(),
1728 })
1729 .collect()
1730 }
1731
1732 pub(crate) fn create_bind_group_layout(
1733 self: &Arc<Self>,
1734 label: &crate::Label,
1735 entry_map: bgl::EntryMap,
1736 origin: bgl::Origin,
1737 ) -> Result<BindGroupLayout<A>, binding_model::CreateBindGroupLayoutError> {
1738 #[derive(PartialEq)]
1739 enum WritableStorage {
1740 Yes,
1741 No,
1742 }
1743
1744 for entry in entry_map.values() {
1745 use wgt::BindingType as Bt;
1746
1747 let mut required_features = wgt::Features::empty();
1748 let mut required_downlevel_flags = wgt::DownlevelFlags::empty();
1749 let (array_feature, writable_storage) = match entry.ty {
1750 Bt::Buffer {
1751 ty: wgt::BufferBindingType::Uniform,
1752 has_dynamic_offset: false,
1753 min_binding_size: _,
1754 } => (
1755 Some(wgt::Features::BUFFER_BINDING_ARRAY),
1756 WritableStorage::No,
1757 ),
1758 Bt::Buffer {
1759 ty: wgt::BufferBindingType::Uniform,
1760 has_dynamic_offset: true,
1761 min_binding_size: _,
1762 } => (
1763 Some(wgt::Features::BUFFER_BINDING_ARRAY),
1764 WritableStorage::No,
1765 ),
1766 Bt::Buffer {
1767 ty: wgt::BufferBindingType::Storage { read_only },
1768 ..
1769 } => (
1770 Some(
1771 wgt::Features::BUFFER_BINDING_ARRAY
1772 | wgt::Features::STORAGE_RESOURCE_BINDING_ARRAY,
1773 ),
1774 match read_only {
1775 true => WritableStorage::No,
1776 false => WritableStorage::Yes,
1777 },
1778 ),
1779 Bt::Sampler { .. } => (
1780 Some(wgt::Features::TEXTURE_BINDING_ARRAY),
1781 WritableStorage::No,
1782 ),
1783 Bt::Texture {
1784 multisampled: true,
1785 sample_type: TextureSampleType::Float { filterable: true },
1786 ..
1787 } => {
1788 return Err(binding_model::CreateBindGroupLayoutError::Entry {
1789 binding: entry.binding,
1790 error:
1791 BindGroupLayoutEntryError::SampleTypeFloatFilterableBindingMultisampled,
1792 });
1793 }
1794 Bt::Texture {
1795 multisampled,
1796 view_dimension,
1797 ..
1798 } => {
1799 if multisampled && view_dimension != TextureViewDimension::D2 {
1800 return Err(binding_model::CreateBindGroupLayoutError::Entry {
1801 binding: entry.binding,
1802 error: BindGroupLayoutEntryError::Non2DMultisampled(view_dimension),
1803 });
1804 }
1805
1806 (
1807 Some(wgt::Features::TEXTURE_BINDING_ARRAY),
1808 WritableStorage::No,
1809 )
1810 }
1811 Bt::StorageTexture {
1812 access,
1813 view_dimension,
1814 format: _,
1815 } => {
1816 match view_dimension {
1817 wgt::TextureViewDimension::Cube | wgt::TextureViewDimension::CubeArray => {
1818 return Err(binding_model::CreateBindGroupLayoutError::Entry {
1819 binding: entry.binding,
1820 error: BindGroupLayoutEntryError::StorageTextureCube,
1821 })
1822 }
1823 _ => (),
1824 }
1825 match access {
1826 wgt::StorageTextureAccess::ReadOnly
1827 | wgt::StorageTextureAccess::ReadWrite
1828 if !self.features.contains(
1829 wgt::Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES,
1830 ) =>
1831 {
1832 return Err(binding_model::CreateBindGroupLayoutError::Entry {
1833 binding: entry.binding,
1834 error: BindGroupLayoutEntryError::StorageTextureReadWrite,
1835 });
1836 }
1837 _ => (),
1838 }
1839 (
1840 Some(
1841 wgt::Features::TEXTURE_BINDING_ARRAY
1842 | wgt::Features::STORAGE_RESOURCE_BINDING_ARRAY,
1843 ),
1844 match access {
1845 wgt::StorageTextureAccess::WriteOnly => WritableStorage::Yes,
1846 wgt::StorageTextureAccess::ReadOnly => {
1847 required_features |=
1848 wgt::Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES;
1849 WritableStorage::No
1850 }
1851 wgt::StorageTextureAccess::ReadWrite => {
1852 required_features |=
1853 wgt::Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES;
1854 WritableStorage::Yes
1855 }
1856 },
1857 )
1858 }
1859 Bt::AccelerationStructure => todo!(),
1860 };
1861
1862 if entry.count.is_some() {
1864 required_features |= array_feature
1865 .ok_or(BindGroupLayoutEntryError::ArrayUnsupported)
1866 .map_err(|error| binding_model::CreateBindGroupLayoutError::Entry {
1867 binding: entry.binding,
1868 error,
1869 })?;
1870 }
1871
1872 if entry.visibility.contains_invalid_bits() {
1873 return Err(
1874 binding_model::CreateBindGroupLayoutError::InvalidVisibility(entry.visibility),
1875 );
1876 }
1877
1878 if entry.visibility.contains(wgt::ShaderStages::VERTEX) {
1879 if writable_storage == WritableStorage::Yes {
1880 required_features |= wgt::Features::VERTEX_WRITABLE_STORAGE;
1881 }
1882 if let Bt::Buffer {
1883 ty: wgt::BufferBindingType::Storage { .. },
1884 ..
1885 } = entry.ty
1886 {
1887 required_downlevel_flags |= wgt::DownlevelFlags::VERTEX_STORAGE;
1888 }
1889 }
1890 if writable_storage == WritableStorage::Yes
1891 && entry.visibility.contains(wgt::ShaderStages::FRAGMENT)
1892 {
1893 required_downlevel_flags |= wgt::DownlevelFlags::FRAGMENT_WRITABLE_STORAGE;
1894 }
1895
1896 self.require_features(required_features)
1897 .map_err(BindGroupLayoutEntryError::MissingFeatures)
1898 .map_err(|error| binding_model::CreateBindGroupLayoutError::Entry {
1899 binding: entry.binding,
1900 error,
1901 })?;
1902 self.require_downlevel_flags(required_downlevel_flags)
1903 .map_err(BindGroupLayoutEntryError::MissingDownlevelFlags)
1904 .map_err(|error| binding_model::CreateBindGroupLayoutError::Entry {
1905 binding: entry.binding,
1906 error,
1907 })?;
1908 }
1909
1910 let bgl_flags = conv::bind_group_layout_flags(self.features);
1911
1912 let hal_bindings = entry_map.values().copied().collect::<Vec<_>>();
1913 let label = label.to_hal(self.instance_flags);
1914 let hal_desc = hal::BindGroupLayoutDescriptor {
1915 label,
1916 flags: bgl_flags,
1917 entries: &hal_bindings,
1918 };
1919 let raw = unsafe {
1920 self.raw
1921 .as_ref()
1922 .unwrap()
1923 .create_bind_group_layout(&hal_desc)
1924 .map_err(DeviceError::from)?
1925 };
1926
1927 let mut count_validator = binding_model::BindingTypeMaxCountValidator::default();
1928 for entry in entry_map.values() {
1929 count_validator.add_binding(entry);
1930 }
1931 count_validator
1934 .validate(&self.limits)
1935 .map_err(binding_model::CreateBindGroupLayoutError::TooManyBindings)?;
1936
1937 Ok(BindGroupLayout {
1938 raw: Some(raw),
1939 device: self.clone(),
1940 entries: entry_map,
1941 origin,
1942 binding_count_validator: count_validator,
1943 info: ResourceInfo::new(
1944 label.unwrap_or("<BindGroupLayout>"),
1945 Some(self.tracker_indices.bind_group_layouts.clone()),
1946 ),
1947 label: label.unwrap_or_default().to_string(),
1948 })
1949 }
1950
1951 pub(crate) fn create_buffer_binding<'a>(
1952 bb: &binding_model::BufferBinding,
1953 binding: u32,
1954 decl: &wgt::BindGroupLayoutEntry,
1955 used_buffer_ranges: &mut Vec<BufferInitTrackerAction<A>>,
1956 dynamic_binding_info: &mut Vec<binding_model::BindGroupDynamicBindingData>,
1957 late_buffer_binding_sizes: &mut FastHashMap<u32, wgt::BufferSize>,
1958 used: &mut BindGroupStates<A>,
1959 storage: &'a Storage<Buffer<A>>,
1960 limits: &wgt::Limits,
1961 device_id: id::Id<id::markers::Device>,
1962 snatch_guard: &'a SnatchGuard<'a>,
1963 ) -> Result<hal::BufferBinding<'a, A>, binding_model::CreateBindGroupError> {
1964 use crate::binding_model::CreateBindGroupError as Error;
1965
1966 let (binding_ty, dynamic, min_size) = match decl.ty {
1967 wgt::BindingType::Buffer {
1968 ty,
1969 has_dynamic_offset,
1970 min_binding_size,
1971 } => (ty, has_dynamic_offset, min_binding_size),
1972 _ => {
1973 return Err(Error::WrongBindingType {
1974 binding,
1975 actual: decl.ty,
1976 expected: "UniformBuffer, StorageBuffer or ReadonlyStorageBuffer",
1977 })
1978 }
1979 };
1980
1981 let (pub_usage, internal_use, range_limit) = match binding_ty {
1982 wgt::BufferBindingType::Uniform => (
1983 wgt::BufferUsages::UNIFORM,
1984 hal::BufferUses::UNIFORM,
1985 limits.max_uniform_buffer_binding_size,
1986 ),
1987 wgt::BufferBindingType::Storage { read_only } => (
1988 wgt::BufferUsages::STORAGE,
1989 if read_only {
1990 hal::BufferUses::STORAGE_READ
1991 } else {
1992 hal::BufferUses::STORAGE_READ_WRITE
1993 },
1994 limits.max_storage_buffer_binding_size,
1995 ),
1996 };
1997
1998 let (align, align_limit_name) =
1999 binding_model::buffer_binding_type_alignment(limits, binding_ty);
2000 if bb.offset % align as u64 != 0 {
2001 return Err(Error::UnalignedBufferOffset(
2002 bb.offset,
2003 align_limit_name,
2004 align,
2005 ));
2006 }
2007
2008 let buffer = used
2009 .buffers
2010 .add_single(storage, bb.buffer_id, internal_use)
2011 .ok_or(Error::InvalidBuffer(bb.buffer_id))?;
2012
2013 if buffer.device.as_info().id() != device_id {
2014 return Err(DeviceError::WrongDevice.into());
2015 }
2016
2017 check_buffer_usage(bb.buffer_id, buffer.usage, pub_usage)?;
2018 let raw_buffer = buffer
2019 .raw
2020 .get(snatch_guard)
2021 .ok_or(Error::InvalidBuffer(bb.buffer_id))?;
2022
2023 let (bind_size, bind_end) = match bb.size {
2024 Some(size) => {
2025 let end = bb.offset + size.get();
2026 if end > buffer.size {
2027 return Err(Error::BindingRangeTooLarge {
2028 buffer: bb.buffer_id,
2029 range: bb.offset..end,
2030 size: buffer.size,
2031 });
2032 }
2033 (size.get(), end)
2034 }
2035 None => {
2036 if buffer.size < bb.offset {
2037 return Err(Error::BindingRangeTooLarge {
2038 buffer: bb.buffer_id,
2039 range: bb.offset..bb.offset,
2040 size: buffer.size,
2041 });
2042 }
2043 (buffer.size - bb.offset, buffer.size)
2044 }
2045 };
2046
2047 if bind_size > range_limit as u64 {
2048 return Err(Error::BufferRangeTooLarge {
2049 binding,
2050 given: bind_size as u32,
2051 limit: range_limit,
2052 });
2053 }
2054
2055 if dynamic {
2057 dynamic_binding_info.push(binding_model::BindGroupDynamicBindingData {
2058 binding_idx: binding,
2059 buffer_size: buffer.size,
2060 binding_range: bb.offset..bind_end,
2061 maximum_dynamic_offset: buffer.size - bind_end,
2062 binding_type: binding_ty,
2063 });
2064 }
2065
2066 if let Some(non_zero) = min_size {
2067 let min_size = non_zero.get();
2068 if min_size > bind_size {
2069 return Err(Error::BindingSizeTooSmall {
2070 buffer: bb.buffer_id,
2071 actual: bind_size,
2072 min: min_size,
2073 });
2074 }
2075 } else {
2076 let late_size =
2077 wgt::BufferSize::new(bind_size).ok_or(Error::BindingZeroSize(bb.buffer_id))?;
2078 late_buffer_binding_sizes.insert(binding, late_size);
2079 }
2080
2081 assert_eq!(bb.offset % wgt::COPY_BUFFER_ALIGNMENT, 0);
2082 used_buffer_ranges.extend(buffer.initialization_status.read().create_action(
2083 buffer,
2084 bb.offset..bb.offset + bind_size,
2085 MemoryInitKind::NeedsInitializedMemory,
2086 ));
2087
2088 Ok(hal::BufferBinding {
2089 buffer: raw_buffer,
2090 offset: bb.offset,
2091 size: bb.size,
2092 })
2093 }
2094
2095 fn create_sampler_binding<'a>(
2096 used: &BindGroupStates<A>,
2097 storage: &'a Storage<Sampler<A>>,
2098 id: id::Id<id::markers::Sampler>,
2099 device_id: id::Id<id::markers::Device>,
2100 ) -> Result<&'a Sampler<A>, binding_model::CreateBindGroupError> {
2101 use crate::binding_model::CreateBindGroupError as Error;
2102
2103 let sampler = used
2104 .samplers
2105 .add_single(storage, id)
2106 .ok_or(Error::InvalidSampler(id))?;
2107
2108 if sampler.device.as_info().id() != device_id {
2109 return Err(DeviceError::WrongDevice.into());
2110 }
2111
2112 Ok(sampler)
2113 }
2114
2115 pub(crate) fn create_texture_binding<'a>(
2116 self: &Arc<Self>,
2117 binding: u32,
2118 decl: &wgt::BindGroupLayoutEntry,
2119 storage: &'a Storage<TextureView<A>>,
2120 id: id::Id<id::markers::TextureView>,
2121 used: &mut BindGroupStates<A>,
2122 used_texture_ranges: &mut Vec<TextureInitTrackerAction<A>>,
2123 snatch_guard: &'a SnatchGuard<'a>,
2124 ) -> Result<hal::TextureBinding<'a, A>, binding_model::CreateBindGroupError> {
2125 use crate::binding_model::CreateBindGroupError as Error;
2126
2127 let view = used
2128 .views
2129 .add_single(storage, id)
2130 .ok_or(Error::InvalidTextureView(id))?;
2131
2132 if view.device.as_info().id() != self.as_info().id() {
2133 return Err(DeviceError::WrongDevice.into());
2134 }
2135
2136 let (pub_usage, internal_use) = self.texture_use_parameters(
2137 binding,
2138 decl,
2139 view,
2140 "SampledTexture, ReadonlyStorageTexture or WriteonlyStorageTexture",
2141 )?;
2142 let texture = &view.parent;
2143 let texture_id = texture.as_info().id();
2144 let texture = used
2147 .textures
2148 .add_single(texture, Some(view.selector.clone()), internal_use)
2149 .ok_or(binding_model::CreateBindGroupError::InvalidTexture(
2150 texture_id,
2151 ))?;
2152
2153 if texture.device.as_info().id() != view.device.as_info().id() {
2154 return Err(DeviceError::WrongDevice.into());
2155 }
2156
2157 check_texture_usage(texture.desc.usage, pub_usage)?;
2158
2159 used_texture_ranges.push(TextureInitTrackerAction {
2160 texture: texture.clone(),
2161 range: TextureInitRange {
2162 mip_range: view.desc.range.mip_range(texture.desc.mip_level_count),
2163 layer_range: view
2164 .desc
2165 .range
2166 .layer_range(texture.desc.array_layer_count()),
2167 },
2168 kind: MemoryInitKind::NeedsInitializedMemory,
2169 });
2170
2171 Ok(hal::TextureBinding {
2172 view: view
2173 .raw(snatch_guard)
2174 .ok_or(Error::InvalidTextureView(id))?,
2175 usage: internal_use,
2176 })
2177 }
2178
2179 pub(crate) fn create_bind_group(
2182 self: &Arc<Self>,
2183 layout: &Arc<BindGroupLayout<A>>,
2184 desc: &binding_model::BindGroupDescriptor,
2185 hub: &Hub<A>,
2186 ) -> Result<BindGroup<A>, binding_model::CreateBindGroupError> {
2187 use crate::binding_model::{BindingResource as Br, CreateBindGroupError as Error};
2188 {
2189 let actual = desc.entries.len();
2192 let expected = layout.entries.len();
2193 if actual != expected {
2194 return Err(Error::BindingsNumMismatch { expected, actual });
2195 }
2196 }
2197
2198 let mut dynamic_binding_info = Vec::new();
2201 let mut late_buffer_binding_sizes = FastHashMap::default();
2205 let mut used = BindGroupStates::new();
2207
2208 let buffer_guard = hub.buffers.read();
2209 let texture_view_guard = hub.texture_views.read();
2210 let sampler_guard = hub.samplers.read();
2211
2212 let mut used_buffer_ranges = Vec::new();
2213 let mut used_texture_ranges = Vec::new();
2214 let mut hal_entries = Vec::with_capacity(desc.entries.len());
2215 let mut hal_buffers = Vec::new();
2216 let mut hal_samplers = Vec::new();
2217 let mut hal_textures = Vec::new();
2218 let snatch_guard = self.snatchable_lock.read();
2219 for entry in desc.entries.iter() {
2220 let binding = entry.binding;
2221 let decl = layout
2223 .entries
2224 .get(binding)
2225 .ok_or(Error::MissingBindingDeclaration(binding))?;
2226 let (res_index, count) = match entry.resource {
2227 Br::Buffer(ref bb) => {
2228 let bb = Self::create_buffer_binding(
2229 bb,
2230 binding,
2231 decl,
2232 &mut used_buffer_ranges,
2233 &mut dynamic_binding_info,
2234 &mut late_buffer_binding_sizes,
2235 &mut used,
2236 &*buffer_guard,
2237 &self.limits,
2238 self.as_info().id(),
2239 &snatch_guard,
2240 )?;
2241
2242 let res_index = hal_buffers.len();
2243 hal_buffers.push(bb);
2244 (res_index, 1)
2245 }
2246 Br::BufferArray(ref bindings_array) => {
2247 let num_bindings = bindings_array.len();
2248 Self::check_array_binding(self.features, decl.count, num_bindings)?;
2249
2250 let res_index = hal_buffers.len();
2251 for bb in bindings_array.iter() {
2252 let bb = Self::create_buffer_binding(
2253 bb,
2254 binding,
2255 decl,
2256 &mut used_buffer_ranges,
2257 &mut dynamic_binding_info,
2258 &mut late_buffer_binding_sizes,
2259 &mut used,
2260 &*buffer_guard,
2261 &self.limits,
2262 self.as_info().id(),
2263 &snatch_guard,
2264 )?;
2265 hal_buffers.push(bb);
2266 }
2267 (res_index, num_bindings)
2268 }
2269 Br::Sampler(id) => match decl.ty {
2270 wgt::BindingType::Sampler(ty) => {
2271 let sampler = Self::create_sampler_binding(
2272 &used,
2273 &sampler_guard,
2274 id,
2275 self.as_info().id(),
2276 )?;
2277
2278 let (allowed_filtering, allowed_comparison) = match ty {
2279 wgt::SamplerBindingType::Filtering => (None, false),
2280 wgt::SamplerBindingType::NonFiltering => (Some(false), false),
2281 wgt::SamplerBindingType::Comparison => (None, true),
2282 };
2283 if let Some(allowed_filtering) = allowed_filtering {
2284 if allowed_filtering != sampler.filtering {
2285 return Err(Error::WrongSamplerFiltering {
2286 binding,
2287 layout_flt: allowed_filtering,
2288 sampler_flt: sampler.filtering,
2289 });
2290 }
2291 }
2292 if allowed_comparison != sampler.comparison {
2293 return Err(Error::WrongSamplerComparison {
2294 binding,
2295 layout_cmp: allowed_comparison,
2296 sampler_cmp: sampler.comparison,
2297 });
2298 }
2299
2300 let res_index = hal_samplers.len();
2301 hal_samplers.push(sampler.raw());
2302 (res_index, 1)
2303 }
2304 _ => {
2305 return Err(Error::WrongBindingType {
2306 binding,
2307 actual: decl.ty,
2308 expected: "Sampler",
2309 })
2310 }
2311 },
2312 Br::SamplerArray(ref bindings_array) => {
2313 let num_bindings = bindings_array.len();
2314 Self::check_array_binding(self.features, decl.count, num_bindings)?;
2315
2316 let res_index = hal_samplers.len();
2317 for &id in bindings_array.iter() {
2318 let sampler = Self::create_sampler_binding(
2319 &used,
2320 &sampler_guard,
2321 id,
2322 self.as_info().id(),
2323 )?;
2324
2325 hal_samplers.push(sampler.raw());
2326 }
2327
2328 (res_index, num_bindings)
2329 }
2330 Br::TextureView(id) => {
2331 let tb = self.create_texture_binding(
2332 binding,
2333 decl,
2334 &texture_view_guard,
2335 id,
2336 &mut used,
2337 &mut used_texture_ranges,
2338 &snatch_guard,
2339 )?;
2340 let res_index = hal_textures.len();
2341 hal_textures.push(tb);
2342 (res_index, 1)
2343 }
2344 Br::TextureViewArray(ref bindings_array) => {
2345 let num_bindings = bindings_array.len();
2346 Self::check_array_binding(self.features, decl.count, num_bindings)?;
2347
2348 let res_index = hal_textures.len();
2349 for &id in bindings_array.iter() {
2350 let tb = self.create_texture_binding(
2351 binding,
2352 decl,
2353 &texture_view_guard,
2354 id,
2355 &mut used,
2356 &mut used_texture_ranges,
2357 &snatch_guard,
2358 )?;
2359
2360 hal_textures.push(tb);
2361 }
2362
2363 (res_index, num_bindings)
2364 }
2365 };
2366
2367 hal_entries.push(hal::BindGroupEntry {
2368 binding,
2369 resource_index: res_index as u32,
2370 count: count as u32,
2371 });
2372 }
2373
2374 used.optimize();
2375
2376 hal_entries.sort_by_key(|entry| entry.binding);
2377 for (a, b) in hal_entries.iter().zip(hal_entries.iter().skip(1)) {
2378 if a.binding == b.binding {
2379 return Err(Error::DuplicateBinding(a.binding));
2380 }
2381 }
2382 let hal_desc = hal::BindGroupDescriptor {
2383 label: desc.label.to_hal(self.instance_flags),
2384 layout: layout.raw(),
2385 entries: &hal_entries,
2386 buffers: &hal_buffers,
2387 samplers: &hal_samplers,
2388 textures: &hal_textures,
2389 acceleration_structures: &[],
2390 };
2391 let raw = unsafe {
2392 self.raw
2393 .as_ref()
2394 .unwrap()
2395 .create_bind_group(&hal_desc)
2396 .map_err(DeviceError::from)?
2397 };
2398
2399 Ok(BindGroup {
2400 raw: Snatchable::new(raw),
2401 device: self.clone(),
2402 layout: layout.clone(),
2403 info: ResourceInfo::new(
2404 desc.label.borrow_or_default(),
2405 Some(self.tracker_indices.bind_groups.clone()),
2406 ),
2407 used,
2408 used_buffer_ranges,
2409 used_texture_ranges,
2410 dynamic_binding_info,
2411 late_buffer_binding_sizes: layout
2413 .entries
2414 .indices()
2415 .flat_map(|binding| late_buffer_binding_sizes.get(&binding).cloned())
2416 .collect(),
2417 })
2418 }
2419
2420 pub(crate) fn check_array_binding(
2421 features: wgt::Features,
2422 count: Option<NonZeroU32>,
2423 num_bindings: usize,
2424 ) -> Result<(), super::binding_model::CreateBindGroupError> {
2425 use super::binding_model::CreateBindGroupError as Error;
2426
2427 if let Some(count) = count {
2428 let count = count.get() as usize;
2429 if count < num_bindings {
2430 return Err(Error::BindingArrayPartialLengthMismatch {
2431 actual: num_bindings,
2432 expected: count,
2433 });
2434 }
2435 if count != num_bindings
2436 && !features.contains(wgt::Features::PARTIALLY_BOUND_BINDING_ARRAY)
2437 {
2438 return Err(Error::BindingArrayLengthMismatch {
2439 actual: num_bindings,
2440 expected: count,
2441 });
2442 }
2443 if num_bindings == 0 {
2444 return Err(Error::BindingArrayZeroLength);
2445 }
2446 } else {
2447 return Err(Error::SingleBindingExpected);
2448 };
2449
2450 Ok(())
2451 }
2452
2453 pub(crate) fn texture_use_parameters(
2454 self: &Arc<Self>,
2455 binding: u32,
2456 decl: &wgt::BindGroupLayoutEntry,
2457 view: &TextureView<A>,
2458 expected: &'static str,
2459 ) -> Result<(wgt::TextureUsages, hal::TextureUses), binding_model::CreateBindGroupError> {
2460 use crate::binding_model::CreateBindGroupError as Error;
2461 if view
2462 .desc
2463 .aspects()
2464 .contains(hal::FormatAspects::DEPTH | hal::FormatAspects::STENCIL)
2465 {
2466 return Err(Error::DepthStencilAspect);
2467 }
2468 match decl.ty {
2469 wgt::BindingType::Texture {
2470 sample_type,
2471 view_dimension,
2472 multisampled,
2473 } => {
2474 use wgt::TextureSampleType as Tst;
2475 if multisampled != (view.samples != 1) {
2476 return Err(Error::InvalidTextureMultisample {
2477 binding,
2478 layout_multisampled: multisampled,
2479 view_samples: view.samples,
2480 });
2481 }
2482 let compat_sample_type = view
2483 .desc
2484 .format
2485 .sample_type(Some(view.desc.range.aspect), Some(self.features))
2486 .unwrap();
2487 match (sample_type, compat_sample_type) {
2488 (Tst::Uint, Tst::Uint) |
2489 (Tst::Sint, Tst::Sint) |
2490 (Tst::Depth, Tst::Depth) |
2491 (Tst::Float { filterable: false }, Tst::Float { .. }) |
2493 (Tst::Float { filterable: true }, Tst::Float { filterable: true }) |
2495 (Tst::Float { filterable: false }, Tst::Depth) => {}
2497 (Tst::Float { filterable: true }, Tst::Float { .. }) if view.format_features.flags.contains(wgt::TextureFormatFeatureFlags::FILTERABLE) => {}
2502 _ => {
2503 return Err(Error::InvalidTextureSampleType {
2504 binding,
2505 layout_sample_type: sample_type,
2506 view_format: view.desc.format,
2507 })
2508 }
2509 }
2510 if view_dimension != view.desc.dimension {
2511 return Err(Error::InvalidTextureDimension {
2512 binding,
2513 layout_dimension: view_dimension,
2514 view_dimension: view.desc.dimension,
2515 });
2516 }
2517 Ok((
2518 wgt::TextureUsages::TEXTURE_BINDING,
2519 hal::TextureUses::RESOURCE,
2520 ))
2521 }
2522 wgt::BindingType::StorageTexture {
2523 access,
2524 format,
2525 view_dimension,
2526 } => {
2527 if format != view.desc.format {
2528 return Err(Error::InvalidStorageTextureFormat {
2529 binding,
2530 layout_format: format,
2531 view_format: view.desc.format,
2532 });
2533 }
2534 if view_dimension != view.desc.dimension {
2535 return Err(Error::InvalidTextureDimension {
2536 binding,
2537 layout_dimension: view_dimension,
2538 view_dimension: view.desc.dimension,
2539 });
2540 }
2541
2542 let mip_level_count = view.selector.mips.end - view.selector.mips.start;
2543 if mip_level_count != 1 {
2544 return Err(Error::InvalidStorageTextureMipLevelCount {
2545 binding,
2546 mip_level_count,
2547 });
2548 }
2549
2550 let internal_use = match access {
2551 wgt::StorageTextureAccess::WriteOnly => hal::TextureUses::STORAGE_READ_WRITE,
2552 wgt::StorageTextureAccess::ReadOnly => {
2553 if !view
2554 .format_features
2555 .flags
2556 .contains(wgt::TextureFormatFeatureFlags::STORAGE_READ_WRITE)
2557 {
2558 return Err(Error::StorageReadNotSupported(view.desc.format));
2559 }
2560 hal::TextureUses::STORAGE_READ
2561 }
2562 wgt::StorageTextureAccess::ReadWrite => {
2563 if !view
2564 .format_features
2565 .flags
2566 .contains(wgt::TextureFormatFeatureFlags::STORAGE_READ_WRITE)
2567 {
2568 return Err(Error::StorageReadNotSupported(view.desc.format));
2569 }
2570
2571 hal::TextureUses::STORAGE_READ_WRITE
2572 }
2573 };
2574 Ok((wgt::TextureUsages::STORAGE_BINDING, internal_use))
2575 }
2576 _ => Err(Error::WrongBindingType {
2577 binding,
2578 actual: decl.ty,
2579 expected,
2580 }),
2581 }
2582 }
2583
2584 pub(crate) fn create_pipeline_layout(
2585 self: &Arc<Self>,
2586 desc: &binding_model::PipelineLayoutDescriptor,
2587 bgl_registry: &Registry<BindGroupLayout<A>>,
2588 ) -> Result<binding_model::PipelineLayout<A>, binding_model::CreatePipelineLayoutError> {
2589 use crate::binding_model::CreatePipelineLayoutError as Error;
2590
2591 let bind_group_layouts_count = desc.bind_group_layouts.len();
2592 let device_max_bind_groups = self.limits.max_bind_groups as usize;
2593 if bind_group_layouts_count > device_max_bind_groups {
2594 return Err(Error::TooManyGroups {
2595 actual: bind_group_layouts_count,
2596 max: device_max_bind_groups,
2597 });
2598 }
2599
2600 if !desc.push_constant_ranges.is_empty() {
2601 self.require_features(wgt::Features::PUSH_CONSTANTS)?;
2602 }
2603
2604 let mut used_stages = wgt::ShaderStages::empty();
2605 for (index, pc) in desc.push_constant_ranges.iter().enumerate() {
2606 if pc.stages.intersects(used_stages) {
2607 return Err(Error::MoreThanOnePushConstantRangePerStage {
2608 index,
2609 provided: pc.stages,
2610 intersected: pc.stages & used_stages,
2611 });
2612 }
2613 used_stages |= pc.stages;
2614
2615 let device_max_pc_size = self.limits.max_push_constant_size;
2616 if device_max_pc_size < pc.range.end {
2617 return Err(Error::PushConstantRangeTooLarge {
2618 index,
2619 range: pc.range.clone(),
2620 max: device_max_pc_size,
2621 });
2622 }
2623
2624 if pc.range.start % wgt::PUSH_CONSTANT_ALIGNMENT != 0 {
2625 return Err(Error::MisalignedPushConstantRange {
2626 index,
2627 bound: pc.range.start,
2628 });
2629 }
2630 if pc.range.end % wgt::PUSH_CONSTANT_ALIGNMENT != 0 {
2631 return Err(Error::MisalignedPushConstantRange {
2632 index,
2633 bound: pc.range.end,
2634 });
2635 }
2636 }
2637
2638 let mut count_validator = binding_model::BindingTypeMaxCountValidator::default();
2639
2640 let mut bind_group_layouts = ArrayVec::new();
2642 for &id in desc.bind_group_layouts.iter() {
2643 let Ok(bgl) = bgl_registry.get(id) else {
2644 return Err(Error::InvalidBindGroupLayout(id));
2645 };
2646
2647 bind_group_layouts.push(bgl);
2648 }
2649
2650 for bgl in &bind_group_layouts {
2652 if bgl.device.as_info().id() != self.as_info().id() {
2653 return Err(DeviceError::WrongDevice.into());
2654 }
2655
2656 count_validator.merge(&bgl.binding_count_validator);
2657 }
2658
2659 count_validator
2660 .validate(&self.limits)
2661 .map_err(Error::TooManyBindings)?;
2662
2663 let raw_bind_group_layouts = bind_group_layouts
2664 .iter()
2665 .map(|bgl| bgl.raw())
2666 .collect::<ArrayVec<_, { hal::MAX_BIND_GROUPS }>>();
2667
2668 let hal_desc = hal::PipelineLayoutDescriptor {
2669 label: desc.label.to_hal(self.instance_flags),
2670 flags: hal::PipelineLayoutFlags::FIRST_VERTEX_INSTANCE,
2671 bind_group_layouts: &raw_bind_group_layouts,
2672 push_constant_ranges: desc.push_constant_ranges.as_ref(),
2673 };
2674
2675 let raw = unsafe {
2676 self.raw
2677 .as_ref()
2678 .unwrap()
2679 .create_pipeline_layout(&hal_desc)
2680 .map_err(DeviceError::from)?
2681 };
2682
2683 drop(raw_bind_group_layouts);
2684
2685 Ok(binding_model::PipelineLayout {
2686 raw: Some(raw),
2687 device: self.clone(),
2688 info: ResourceInfo::new(
2689 desc.label.borrow_or_default(),
2690 Some(self.tracker_indices.pipeline_layouts.clone()),
2691 ),
2692 bind_group_layouts,
2693 push_constant_ranges: desc.push_constant_ranges.iter().cloned().collect(),
2694 })
2695 }
2696
2697 pub(crate) fn derive_pipeline_layout(
2700 self: &Arc<Self>,
2701 implicit_context: Option<ImplicitPipelineContext>,
2702 mut derived_group_layouts: ArrayVec<bgl::EntryMap, { hal::MAX_BIND_GROUPS }>,
2703 bgl_registry: &Registry<BindGroupLayout<A>>,
2704 pipeline_layout_registry: &Registry<binding_model::PipelineLayout<A>>,
2705 ) -> Result<Arc<binding_model::PipelineLayout<A>>, pipeline::ImplicitLayoutError> {
2706 while derived_group_layouts
2707 .last()
2708 .map_or(false, |map| map.is_empty())
2709 {
2710 derived_group_layouts.pop();
2711 }
2712 let mut ids = implicit_context.ok_or(pipeline::ImplicitLayoutError::MissingIds(0))?;
2713 let group_count = derived_group_layouts.len();
2714 if ids.group_ids.len() < group_count {
2715 log::error!(
2716 "Not enough bind group IDs ({}) specified for the implicit layout ({})",
2717 ids.group_ids.len(),
2718 derived_group_layouts.len()
2719 );
2720 return Err(pipeline::ImplicitLayoutError::MissingIds(group_count as _));
2721 }
2722
2723 for (bgl_id, map) in ids.group_ids.iter_mut().zip(derived_group_layouts) {
2724 let bgl = self.create_bind_group_layout(&None, map, bgl::Origin::Derived)?;
2725 bgl_registry.force_replace(*bgl_id, bgl);
2726 }
2727
2728 let layout_desc = binding_model::PipelineLayoutDescriptor {
2729 label: None,
2730 bind_group_layouts: Cow::Borrowed(&ids.group_ids[..group_count]),
2731 push_constant_ranges: Cow::Borrowed(&[]), };
2733 let layout = self.create_pipeline_layout(&layout_desc, bgl_registry)?;
2734 pipeline_layout_registry.force_replace(ids.root_id, layout);
2735 Ok(pipeline_layout_registry.get(ids.root_id).unwrap())
2736 }
2737
2738 pub(crate) fn create_compute_pipeline(
2739 self: &Arc<Self>,
2740 desc: &pipeline::ComputePipelineDescriptor,
2741 implicit_context: Option<ImplicitPipelineContext>,
2742 hub: &Hub<A>,
2743 ) -> Result<pipeline::ComputePipeline<A>, pipeline::CreateComputePipelineError> {
2744 if let Some(ref ids) = implicit_context {
2747 let mut pipeline_layout_guard = hub.pipeline_layouts.write();
2748 pipeline_layout_guard.insert_error(ids.root_id, IMPLICIT_BIND_GROUP_LAYOUT_ERROR_LABEL);
2749 let mut bgl_guard = hub.bind_group_layouts.write();
2750 for &bgl_id in ids.group_ids.iter() {
2751 bgl_guard.insert_error(bgl_id, IMPLICIT_BIND_GROUP_LAYOUT_ERROR_LABEL);
2752 }
2753 }
2754
2755 self.require_downlevel_flags(wgt::DownlevelFlags::COMPUTE_SHADERS)?;
2756
2757 let shader_module = hub
2758 .shader_modules
2759 .get(desc.stage.module)
2760 .map_err(|_| validation::StageError::InvalidModule)?;
2761
2762 if shader_module.device.as_info().id() != self.as_info().id() {
2763 return Err(DeviceError::WrongDevice.into());
2764 }
2765
2766 let pipeline_layout = match desc.layout {
2768 Some(pipeline_layout_id) => {
2769 let pipeline_layout = hub
2770 .pipeline_layouts
2771 .get(pipeline_layout_id)
2772 .map_err(|_| pipeline::CreateComputePipelineError::InvalidLayout)?;
2773
2774 if pipeline_layout.device.as_info().id() != self.as_info().id() {
2775 return Err(DeviceError::WrongDevice.into());
2776 }
2777
2778 Some(pipeline_layout)
2779 }
2780 None => None,
2781 };
2782
2783 let mut binding_layout_source = match pipeline_layout {
2784 Some(ref pipeline_layout) => {
2785 validation::BindingLayoutSource::Provided(pipeline_layout.get_binding_maps())
2786 }
2787 None => validation::BindingLayoutSource::new_derived(&self.limits),
2788 };
2789 let mut shader_binding_sizes = FastHashMap::default();
2790 let io = validation::StageIo::default();
2791
2792 let final_entry_point_name;
2793
2794 {
2795 let stage = wgt::ShaderStages::COMPUTE;
2796
2797 final_entry_point_name = shader_module.finalize_entry_point_name(
2798 stage,
2799 desc.stage.entry_point.as_ref().map(|ep| ep.as_ref()),
2800 )?;
2801
2802 if let Some(ref interface) = shader_module.interface {
2803 let _ = interface.check_stage(
2804 &mut binding_layout_source,
2805 &mut shader_binding_sizes,
2806 &final_entry_point_name,
2807 stage,
2808 io,
2809 None,
2810 )?;
2811 }
2812 }
2813
2814 let pipeline_layout = match binding_layout_source {
2815 validation::BindingLayoutSource::Provided(_) => {
2816 drop(binding_layout_source);
2817 pipeline_layout.unwrap()
2818 }
2819 validation::BindingLayoutSource::Derived(entries) => self.derive_pipeline_layout(
2820 implicit_context,
2821 entries,
2822 &hub.bind_group_layouts,
2823 &hub.pipeline_layouts,
2824 )?,
2825 };
2826
2827 let late_sized_buffer_groups =
2828 Device::make_late_sized_buffer_groups(&shader_binding_sizes, &pipeline_layout);
2829
2830 let pipeline_desc = hal::ComputePipelineDescriptor {
2831 label: desc.label.to_hal(self.instance_flags),
2832 layout: pipeline_layout.raw(),
2833 stage: hal::ProgrammableStage {
2834 module: shader_module.raw(),
2835 entry_point: final_entry_point_name.as_ref(),
2836 constants: desc.stage.constants.as_ref(),
2837 zero_initialize_workgroup_memory: desc.stage.zero_initialize_workgroup_memory,
2838 },
2839 };
2840
2841 let raw = unsafe {
2842 self.raw
2843 .as_ref()
2844 .unwrap()
2845 .create_compute_pipeline(&pipeline_desc)
2846 }
2847 .map_err(|err| match err {
2848 hal::PipelineError::Device(error) => {
2849 pipeline::CreateComputePipelineError::Device(error.into())
2850 }
2851 hal::PipelineError::Linkage(_stages, msg) => {
2852 pipeline::CreateComputePipelineError::Internal(msg)
2853 }
2854 hal::PipelineError::EntryPoint(_stage) => {
2855 pipeline::CreateComputePipelineError::Internal(ENTRYPOINT_FAILURE_ERROR.to_string())
2856 }
2857 })?;
2858
2859 let pipeline = pipeline::ComputePipeline {
2860 raw: Some(raw),
2861 layout: pipeline_layout,
2862 device: self.clone(),
2863 _shader_module: shader_module,
2864 late_sized_buffer_groups,
2865 info: ResourceInfo::new(
2866 desc.label.borrow_or_default(),
2867 Some(self.tracker_indices.compute_pipelines.clone()),
2868 ),
2869 };
2870 Ok(pipeline)
2871 }
2872
2873 pub(crate) fn create_render_pipeline(
2874 self: &Arc<Self>,
2875 adapter: &Adapter<A>,
2876 desc: &pipeline::RenderPipelineDescriptor,
2877 implicit_context: Option<ImplicitPipelineContext>,
2878 hub: &Hub<A>,
2879 ) -> Result<pipeline::RenderPipeline<A>, pipeline::CreateRenderPipelineError> {
2880 use wgt::TextureFormatFeatureFlags as Tfff;
2881
2882 if let Some(ref ids) = implicit_context {
2885 let mut pipeline_layout_guard = hub.pipeline_layouts.write();
2887 let mut bgl_guard = hub.bind_group_layouts.write();
2888 pipeline_layout_guard.insert_error(ids.root_id, IMPLICIT_BIND_GROUP_LAYOUT_ERROR_LABEL);
2889 for &bgl_id in ids.group_ids.iter() {
2890 bgl_guard.insert_error(bgl_id, IMPLICIT_BIND_GROUP_LAYOUT_ERROR_LABEL);
2891 }
2892 }
2893
2894 let mut shader_binding_sizes = FastHashMap::default();
2895
2896 let num_attachments = desc.fragment.as_ref().map(|f| f.targets.len()).unwrap_or(0);
2897 let max_attachments = self.limits.max_color_attachments as usize;
2898 if num_attachments > max_attachments {
2899 return Err(pipeline::CreateRenderPipelineError::ColorAttachment(
2900 command::ColorAttachmentError::TooMany {
2901 given: num_attachments,
2902 limit: max_attachments,
2903 },
2904 ));
2905 }
2906
2907 let color_targets = desc
2908 .fragment
2909 .as_ref()
2910 .map_or(&[][..], |fragment| &fragment.targets);
2911 let depth_stencil_state = desc.depth_stencil.as_ref();
2912
2913 let cts: ArrayVec<_, { hal::MAX_COLOR_ATTACHMENTS }> =
2914 color_targets.iter().filter_map(|x| x.as_ref()).collect();
2915 if !cts.is_empty() && {
2916 let first = &cts[0];
2917 cts[1..]
2918 .iter()
2919 .any(|ct| ct.write_mask != first.write_mask || ct.blend != first.blend)
2920 } {
2921 log::debug!("Color targets: {:?}", color_targets);
2922 self.require_downlevel_flags(wgt::DownlevelFlags::INDEPENDENT_BLEND)?;
2923 }
2924
2925 let mut io = validation::StageIo::default();
2926 let mut validated_stages = wgt::ShaderStages::empty();
2927
2928 let mut vertex_steps = Vec::with_capacity(desc.vertex.buffers.len());
2929 let mut vertex_buffers = Vec::with_capacity(desc.vertex.buffers.len());
2930 let mut total_attributes = 0;
2931 let mut shader_expects_dual_source_blending = false;
2932 let mut pipeline_expects_dual_source_blending = false;
2933 for (i, vb_state) in desc.vertex.buffers.iter().enumerate() {
2934 let mut last_stride = 0;
2935 for attribute in vb_state.attributes.iter() {
2936 last_stride = last_stride.max(attribute.offset + attribute.format.size());
2937 }
2938 vertex_steps.push(pipeline::VertexStep {
2939 stride: vb_state.array_stride,
2940 last_stride,
2941 mode: vb_state.step_mode,
2942 });
2943 if vb_state.attributes.is_empty() {
2944 continue;
2945 }
2946 if vb_state.array_stride > self.limits.max_vertex_buffer_array_stride as u64 {
2947 return Err(pipeline::CreateRenderPipelineError::VertexStrideTooLarge {
2948 index: i as u32,
2949 given: vb_state.array_stride as u32,
2950 limit: self.limits.max_vertex_buffer_array_stride,
2951 });
2952 }
2953 if vb_state.array_stride % wgt::VERTEX_STRIDE_ALIGNMENT != 0 {
2954 return Err(pipeline::CreateRenderPipelineError::UnalignedVertexStride {
2955 index: i as u32,
2956 stride: vb_state.array_stride,
2957 });
2958 }
2959 vertex_buffers.push(hal::VertexBufferLayout {
2960 array_stride: vb_state.array_stride,
2961 step_mode: vb_state.step_mode,
2962 attributes: vb_state.attributes.as_ref(),
2963 });
2964
2965 for attribute in vb_state.attributes.iter() {
2966 if attribute.offset >= 0x10000000 {
2967 return Err(
2968 pipeline::CreateRenderPipelineError::InvalidVertexAttributeOffset {
2969 location: attribute.shader_location,
2970 offset: attribute.offset,
2971 },
2972 );
2973 }
2974
2975 if let wgt::VertexFormat::Float64
2976 | wgt::VertexFormat::Float64x2
2977 | wgt::VertexFormat::Float64x3
2978 | wgt::VertexFormat::Float64x4 = attribute.format
2979 {
2980 self.require_features(wgt::Features::VERTEX_ATTRIBUTE_64BIT)?;
2981 }
2982
2983 let previous = io.insert(
2984 attribute.shader_location,
2985 validation::InterfaceVar::vertex_attribute(attribute.format),
2986 );
2987
2988 if previous.is_some() {
2989 return Err(pipeline::CreateRenderPipelineError::ShaderLocationClash(
2990 attribute.shader_location,
2991 ));
2992 }
2993 }
2994 total_attributes += vb_state.attributes.len();
2995 }
2996
2997 if vertex_buffers.len() > self.limits.max_vertex_buffers as usize {
2998 return Err(pipeline::CreateRenderPipelineError::TooManyVertexBuffers {
2999 given: vertex_buffers.len() as u32,
3000 limit: self.limits.max_vertex_buffers,
3001 });
3002 }
3003 if total_attributes > self.limits.max_vertex_attributes as usize {
3004 return Err(
3005 pipeline::CreateRenderPipelineError::TooManyVertexAttributes {
3006 given: total_attributes as u32,
3007 limit: self.limits.max_vertex_attributes,
3008 },
3009 );
3010 }
3011
3012 if desc.primitive.strip_index_format.is_some() && !desc.primitive.topology.is_strip() {
3013 return Err(
3014 pipeline::CreateRenderPipelineError::StripIndexFormatForNonStripTopology {
3015 strip_index_format: desc.primitive.strip_index_format,
3016 topology: desc.primitive.topology,
3017 },
3018 );
3019 }
3020
3021 if desc.primitive.unclipped_depth {
3022 self.require_features(wgt::Features::DEPTH_CLIP_CONTROL)?;
3023 }
3024
3025 if desc.primitive.polygon_mode == wgt::PolygonMode::Line {
3026 self.require_features(wgt::Features::POLYGON_MODE_LINE)?;
3027 }
3028 if desc.primitive.polygon_mode == wgt::PolygonMode::Point {
3029 self.require_features(wgt::Features::POLYGON_MODE_POINT)?;
3030 }
3031
3032 if desc.primitive.conservative {
3033 self.require_features(wgt::Features::CONSERVATIVE_RASTERIZATION)?;
3034 }
3035
3036 if desc.primitive.conservative && desc.primitive.polygon_mode != wgt::PolygonMode::Fill {
3037 return Err(
3038 pipeline::CreateRenderPipelineError::ConservativeRasterizationNonFillPolygonMode,
3039 );
3040 }
3041
3042 for (i, cs) in color_targets.iter().enumerate() {
3043 if let Some(cs) = cs.as_ref() {
3044 let error = loop {
3045 if cs.write_mask.contains_invalid_bits() {
3046 break Some(pipeline::ColorStateError::InvalidWriteMask(cs.write_mask));
3047 }
3048
3049 let format_features = self.describe_format_features(adapter, cs.format)?;
3050 if !format_features
3051 .allowed_usages
3052 .contains(wgt::TextureUsages::RENDER_ATTACHMENT)
3053 {
3054 break Some(pipeline::ColorStateError::FormatNotRenderable(cs.format));
3055 }
3056 let blendable = format_features.flags.contains(Tfff::BLENDABLE);
3057 let filterable = format_features.flags.contains(Tfff::FILTERABLE);
3058 let adapter_specific = self
3059 .features
3060 .contains(wgt::Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES);
3061 if cs.blend.is_some() && (!blendable || (!filterable && !adapter_specific)) {
3066 break Some(pipeline::ColorStateError::FormatNotBlendable(cs.format));
3067 }
3068 if !hal::FormatAspects::from(cs.format).contains(hal::FormatAspects::COLOR) {
3069 break Some(pipeline::ColorStateError::FormatNotColor(cs.format));
3070 }
3071 if desc.multisample.count > 1
3072 && !format_features
3073 .flags
3074 .sample_count_supported(desc.multisample.count)
3075 {
3076 break Some(pipeline::ColorStateError::InvalidSampleCount(
3077 desc.multisample.count,
3078 cs.format,
3079 cs.format
3080 .guaranteed_format_features(self.features)
3081 .flags
3082 .supported_sample_counts(),
3083 adapter
3084 .get_texture_format_features(cs.format)
3085 .flags
3086 .supported_sample_counts(),
3087 ));
3088 }
3089 if let Some(blend_mode) = cs.blend {
3090 for factor in [
3091 blend_mode.color.src_factor,
3092 blend_mode.color.dst_factor,
3093 blend_mode.alpha.src_factor,
3094 blend_mode.alpha.dst_factor,
3095 ] {
3096 if factor.ref_second_blend_source() {
3097 self.require_features(wgt::Features::DUAL_SOURCE_BLENDING)?;
3098 if i == 0 {
3099 pipeline_expects_dual_source_blending = true;
3100 break;
3101 } else {
3102 return Err(crate::pipeline::CreateRenderPipelineError
3103 ::BlendFactorOnUnsupportedTarget { factor, target: i as u32 });
3104 }
3105 }
3106 }
3107 }
3108
3109 break None;
3110 };
3111 if let Some(e) = error {
3112 return Err(pipeline::CreateRenderPipelineError::ColorState(i as u8, e));
3113 }
3114 }
3115 }
3116
3117 let limit = self.limits.max_color_attachment_bytes_per_sample;
3118 let formats = color_targets
3119 .iter()
3120 .map(|cs| cs.as_ref().map(|cs| cs.format));
3121 if let Err(total) = validate_color_attachment_bytes_per_sample(formats, limit) {
3122 return Err(pipeline::CreateRenderPipelineError::ColorAttachment(
3123 command::ColorAttachmentError::TooManyBytesPerSample { total, limit },
3124 ));
3125 }
3126
3127 if let Some(ds) = depth_stencil_state {
3128 let error = loop {
3129 let format_features = self.describe_format_features(adapter, ds.format)?;
3130 if !format_features
3131 .allowed_usages
3132 .contains(wgt::TextureUsages::RENDER_ATTACHMENT)
3133 {
3134 break Some(pipeline::DepthStencilStateError::FormatNotRenderable(
3135 ds.format,
3136 ));
3137 }
3138
3139 let aspect = hal::FormatAspects::from(ds.format);
3140 if ds.is_depth_enabled() && !aspect.contains(hal::FormatAspects::DEPTH) {
3141 break Some(pipeline::DepthStencilStateError::FormatNotDepth(ds.format));
3142 }
3143 if ds.stencil.is_enabled() && !aspect.contains(hal::FormatAspects::STENCIL) {
3144 break Some(pipeline::DepthStencilStateError::FormatNotStencil(
3145 ds.format,
3146 ));
3147 }
3148 if desc.multisample.count > 1
3149 && !format_features
3150 .flags
3151 .sample_count_supported(desc.multisample.count)
3152 {
3153 break Some(pipeline::DepthStencilStateError::InvalidSampleCount(
3154 desc.multisample.count,
3155 ds.format,
3156 ds.format
3157 .guaranteed_format_features(self.features)
3158 .flags
3159 .supported_sample_counts(),
3160 adapter
3161 .get_texture_format_features(ds.format)
3162 .flags
3163 .supported_sample_counts(),
3164 ));
3165 }
3166
3167 break None;
3168 };
3169 if let Some(e) = error {
3170 return Err(pipeline::CreateRenderPipelineError::DepthStencilState(e));
3171 }
3172
3173 if ds.bias.clamp != 0.0 {
3174 self.require_downlevel_flags(wgt::DownlevelFlags::DEPTH_BIAS_CLAMP)?;
3175 }
3176 }
3177
3178 let pipeline_layout = match desc.layout {
3180 Some(pipeline_layout_id) => {
3181 let pipeline_layout = hub
3182 .pipeline_layouts
3183 .get(pipeline_layout_id)
3184 .map_err(|_| pipeline::CreateRenderPipelineError::InvalidLayout)?;
3185
3186 if pipeline_layout.device.as_info().id() != self.as_info().id() {
3187 return Err(DeviceError::WrongDevice.into());
3188 }
3189
3190 Some(pipeline_layout)
3191 }
3192 None => None,
3193 };
3194
3195 let mut binding_layout_source = match pipeline_layout {
3196 Some(ref pipeline_layout) => {
3197 validation::BindingLayoutSource::Provided(pipeline_layout.get_binding_maps())
3198 }
3199 None => validation::BindingLayoutSource::new_derived(&self.limits),
3200 };
3201
3202 let samples = {
3203 let sc = desc.multisample.count;
3204 if sc == 0 || sc > 32 || !conv::is_power_of_two_u32(sc) {
3205 return Err(pipeline::CreateRenderPipelineError::InvalidSampleCount(sc));
3206 }
3207 sc
3208 };
3209
3210 let vertex_shader_module;
3211 let vertex_entry_point_name;
3212 let vertex_stage = {
3213 let stage_desc = &desc.vertex.stage;
3214 let stage = wgt::ShaderStages::VERTEX;
3215
3216 vertex_shader_module = hub.shader_modules.get(stage_desc.module).map_err(|_| {
3217 pipeline::CreateRenderPipelineError::Stage {
3218 stage,
3219 error: validation::StageError::InvalidModule,
3220 }
3221 })?;
3222 if vertex_shader_module.device.as_info().id() != self.as_info().id() {
3223 return Err(DeviceError::WrongDevice.into());
3224 }
3225
3226 let stage_err = |error| pipeline::CreateRenderPipelineError::Stage { stage, error };
3227
3228 vertex_entry_point_name = vertex_shader_module
3229 .finalize_entry_point_name(
3230 stage,
3231 stage_desc.entry_point.as_ref().map(|ep| ep.as_ref()),
3232 )
3233 .map_err(stage_err)?;
3234
3235 if let Some(ref interface) = vertex_shader_module.interface {
3236 io = interface
3237 .check_stage(
3238 &mut binding_layout_source,
3239 &mut shader_binding_sizes,
3240 &vertex_entry_point_name,
3241 stage,
3242 io,
3243 desc.depth_stencil.as_ref().map(|d| d.depth_compare),
3244 )
3245 .map_err(stage_err)?;
3246 validated_stages |= stage;
3247 }
3248
3249 hal::ProgrammableStage {
3250 module: vertex_shader_module.raw(),
3251 entry_point: &vertex_entry_point_name,
3252 constants: stage_desc.constants.as_ref(),
3253 zero_initialize_workgroup_memory: stage_desc.zero_initialize_workgroup_memory,
3254 }
3255 };
3256
3257 let mut fragment_shader_module = None;
3258 let fragment_entry_point_name;
3259 let fragment_stage = match desc.fragment {
3260 Some(ref fragment_state) => {
3261 let stage = wgt::ShaderStages::FRAGMENT;
3262
3263 let shader_module = fragment_shader_module.insert(
3264 hub.shader_modules
3265 .get(fragment_state.stage.module)
3266 .map_err(|_| pipeline::CreateRenderPipelineError::Stage {
3267 stage,
3268 error: validation::StageError::InvalidModule,
3269 })?,
3270 );
3271
3272 let stage_err = |error| pipeline::CreateRenderPipelineError::Stage { stage, error };
3273
3274 fragment_entry_point_name = shader_module
3275 .finalize_entry_point_name(
3276 stage,
3277 fragment_state
3278 .stage
3279 .entry_point
3280 .as_ref()
3281 .map(|ep| ep.as_ref()),
3282 )
3283 .map_err(stage_err)?;
3284
3285 if validated_stages == wgt::ShaderStages::VERTEX {
3286 if let Some(ref interface) = shader_module.interface {
3287 io = interface
3288 .check_stage(
3289 &mut binding_layout_source,
3290 &mut shader_binding_sizes,
3291 &fragment_entry_point_name,
3292 stage,
3293 io,
3294 desc.depth_stencil.as_ref().map(|d| d.depth_compare),
3295 )
3296 .map_err(stage_err)?;
3297 validated_stages |= stage;
3298 }
3299 }
3300
3301 if let Some(ref interface) = shader_module.interface {
3302 shader_expects_dual_source_blending = interface
3303 .fragment_uses_dual_source_blending(&fragment_entry_point_name)
3304 .map_err(|error| pipeline::CreateRenderPipelineError::Stage {
3305 stage,
3306 error,
3307 })?;
3308 }
3309
3310 Some(hal::ProgrammableStage {
3311 module: shader_module.raw(),
3312 entry_point: &fragment_entry_point_name,
3313 constants: fragment_state.stage.constants.as_ref(),
3314 zero_initialize_workgroup_memory: fragment_state
3315 .stage
3316 .zero_initialize_workgroup_memory,
3317 })
3318 }
3319 None => None,
3320 };
3321
3322 if !pipeline_expects_dual_source_blending && shader_expects_dual_source_blending {
3323 return Err(
3324 pipeline::CreateRenderPipelineError::ShaderExpectsPipelineToUseDualSourceBlending,
3325 );
3326 }
3327 if pipeline_expects_dual_source_blending && !shader_expects_dual_source_blending {
3328 return Err(
3329 pipeline::CreateRenderPipelineError::PipelineExpectsShaderToUseDualSourceBlending,
3330 );
3331 }
3332
3333 if validated_stages.contains(wgt::ShaderStages::FRAGMENT) {
3334 for (i, output) in io.iter() {
3335 match color_targets.get(*i as usize) {
3336 Some(Some(state)) => {
3337 validation::check_texture_format(state.format, &output.ty).map_err(
3338 |pipeline| {
3339 pipeline::CreateRenderPipelineError::ColorState(
3340 *i as u8,
3341 pipeline::ColorStateError::IncompatibleFormat {
3342 pipeline,
3343 shader: output.ty,
3344 },
3345 )
3346 },
3347 )?;
3348 }
3349 _ => {
3350 log::warn!(
3351 "The fragment stage {:?} output @location({}) values are ignored",
3352 fragment_stage
3353 .as_ref()
3354 .map_or("", |stage| stage.entry_point),
3355 i
3356 );
3357 }
3358 }
3359 }
3360 }
3361 let last_stage = match desc.fragment {
3362 Some(_) => wgt::ShaderStages::FRAGMENT,
3363 None => wgt::ShaderStages::VERTEX,
3364 };
3365 if desc.layout.is_none() && !validated_stages.contains(last_stage) {
3366 return Err(pipeline::ImplicitLayoutError::ReflectionError(last_stage).into());
3367 }
3368
3369 let pipeline_layout = match binding_layout_source {
3370 validation::BindingLayoutSource::Provided(_) => {
3371 drop(binding_layout_source);
3372 pipeline_layout.unwrap()
3373 }
3374 validation::BindingLayoutSource::Derived(entries) => self.derive_pipeline_layout(
3375 implicit_context,
3376 entries,
3377 &hub.bind_group_layouts,
3378 &hub.pipeline_layouts,
3379 )?,
3380 };
3381
3382 if desc.multiview.is_some() {
3384 self.require_features(wgt::Features::MULTIVIEW)?;
3385 }
3386
3387 if !self
3388 .downlevel
3389 .flags
3390 .contains(wgt::DownlevelFlags::BUFFER_BINDINGS_NOT_16_BYTE_ALIGNED)
3391 {
3392 for (binding, size) in shader_binding_sizes.iter() {
3393 if size.get() % 16 != 0 {
3394 return Err(pipeline::CreateRenderPipelineError::UnalignedShader {
3395 binding: binding.binding,
3396 group: binding.group,
3397 size: size.get(),
3398 });
3399 }
3400 }
3401 }
3402
3403 let late_sized_buffer_groups =
3404 Device::make_late_sized_buffer_groups(&shader_binding_sizes, &pipeline_layout);
3405
3406 let pipeline_desc = hal::RenderPipelineDescriptor {
3407 label: desc.label.to_hal(self.instance_flags),
3408 layout: pipeline_layout.raw(),
3409 vertex_buffers: &vertex_buffers,
3410 vertex_stage,
3411 primitive: desc.primitive,
3412 depth_stencil: desc.depth_stencil.clone(),
3413 multisample: desc.multisample,
3414 fragment_stage,
3415 color_targets,
3416 multiview: desc.multiview,
3417 };
3418 let raw = unsafe {
3419 self.raw
3420 .as_ref()
3421 .unwrap()
3422 .create_render_pipeline(&pipeline_desc)
3423 }
3424 .map_err(|err| match err {
3425 hal::PipelineError::Device(error) => {
3426 pipeline::CreateRenderPipelineError::Device(error.into())
3427 }
3428 hal::PipelineError::Linkage(stage, msg) => {
3429 pipeline::CreateRenderPipelineError::Internal { stage, error: msg }
3430 }
3431 hal::PipelineError::EntryPoint(stage) => {
3432 pipeline::CreateRenderPipelineError::Internal {
3433 stage: hal::auxil::map_naga_stage(stage),
3434 error: ENTRYPOINT_FAILURE_ERROR.to_string(),
3435 }
3436 }
3437 })?;
3438
3439 let pass_context = RenderPassContext {
3440 attachments: AttachmentData {
3441 colors: color_targets
3442 .iter()
3443 .map(|state| state.as_ref().map(|s| s.format))
3444 .collect(),
3445 resolves: ArrayVec::new(),
3446 depth_stencil: depth_stencil_state.as_ref().map(|state| state.format),
3447 },
3448 sample_count: samples,
3449 multiview: desc.multiview,
3450 };
3451
3452 let mut flags = pipeline::PipelineFlags::empty();
3453 for state in color_targets.iter().filter_map(|s| s.as_ref()) {
3454 if let Some(ref bs) = state.blend {
3455 if bs.color.uses_constant() | bs.alpha.uses_constant() {
3456 flags |= pipeline::PipelineFlags::BLEND_CONSTANT;
3457 }
3458 }
3459 }
3460 if let Some(ds) = depth_stencil_state.as_ref() {
3461 if ds.stencil.is_enabled() && ds.stencil.needs_ref_value() {
3462 flags |= pipeline::PipelineFlags::STENCIL_REFERENCE;
3463 }
3464 if !ds.is_depth_read_only() {
3465 flags |= pipeline::PipelineFlags::WRITES_DEPTH;
3466 }
3467 if !ds.is_stencil_read_only(desc.primitive.cull_mode) {
3468 flags |= pipeline::PipelineFlags::WRITES_STENCIL;
3469 }
3470 }
3471
3472 let shader_modules = {
3473 let mut shader_modules = ArrayVec::new();
3474 shader_modules.push(vertex_shader_module);
3475 shader_modules.extend(fragment_shader_module);
3476 shader_modules
3477 };
3478
3479 let pipeline = pipeline::RenderPipeline {
3480 raw: Some(raw),
3481 layout: pipeline_layout,
3482 device: self.clone(),
3483 pass_context,
3484 _shader_modules: shader_modules,
3485 flags,
3486 strip_index_format: desc.primitive.strip_index_format,
3487 vertex_steps,
3488 late_sized_buffer_groups,
3489 info: ResourceInfo::new(
3490 desc.label.borrow_or_default(),
3491 Some(self.tracker_indices.render_pipelines.clone()),
3492 ),
3493 };
3494 Ok(pipeline)
3495 }
3496
3497 pub(crate) fn get_texture_format_features(
3498 &self,
3499 adapter: &Adapter<A>,
3500 format: TextureFormat,
3501 ) -> wgt::TextureFormatFeatures {
3502 use wgt::TextureFormatFeatureFlags as tfsc;
3504 let mut format_features = adapter.get_texture_format_features(format);
3505 if (format == TextureFormat::R32Float
3506 || format == TextureFormat::Rg32Float
3507 || format == TextureFormat::Rgba32Float)
3508 && !self.features.contains(wgt::Features::FLOAT32_FILTERABLE)
3509 {
3510 format_features.flags.set(tfsc::FILTERABLE, false);
3511 }
3512 format_features
3513 }
3514
3515 pub(crate) fn describe_format_features(
3516 &self,
3517 adapter: &Adapter<A>,
3518 format: TextureFormat,
3519 ) -> Result<wgt::TextureFormatFeatures, MissingFeatures> {
3520 self.require_features(format.required_features())?;
3521
3522 let using_device_features = self
3523 .features
3524 .contains(wgt::Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES);
3525 let downlevel = !self
3528 .downlevel
3529 .flags
3530 .contains(wgt::DownlevelFlags::WEBGPU_TEXTURE_FORMAT_SUPPORT);
3531
3532 if using_device_features || downlevel {
3533 Ok(self.get_texture_format_features(adapter, format))
3534 } else {
3535 Ok(format.guaranteed_format_features(self.features))
3536 }
3537 }
3538
3539 pub(crate) fn wait_for_submit(
3540 &self,
3541 submission_index: SubmissionIndex,
3542 ) -> Result<(), WaitIdleError> {
3543 let guard = self.fence.read();
3544 let fence = guard.as_ref().unwrap();
3545 let last_done_index = unsafe {
3546 self.raw
3547 .as_ref()
3548 .unwrap()
3549 .get_fence_value(fence)
3550 .map_err(DeviceError::from)?
3551 };
3552 if last_done_index < submission_index {
3553 log::info!("Waiting for submission {:?}", submission_index);
3554 unsafe {
3555 self.raw
3556 .as_ref()
3557 .unwrap()
3558 .wait(fence, submission_index, !0)
3559 .map_err(DeviceError::from)?
3560 };
3561 drop(guard);
3562 let closures = self
3563 .lock_life()
3564 .triage_submissions(submission_index, &self.command_allocator);
3565 assert!(
3566 closures.is_empty(),
3567 "wait_for_submit is not expected to work with closures"
3568 );
3569 }
3570 Ok(())
3571 }
3572
3573 pub(crate) fn create_query_set(
3574 self: &Arc<Self>,
3575 desc: &resource::QuerySetDescriptor,
3576 ) -> Result<QuerySet<A>, resource::CreateQuerySetError> {
3577 use resource::CreateQuerySetError as Error;
3578
3579 match desc.ty {
3580 wgt::QueryType::Occlusion => {}
3581 wgt::QueryType::Timestamp => {
3582 self.require_features(wgt::Features::TIMESTAMP_QUERY)?;
3583 }
3584 wgt::QueryType::PipelineStatistics(..) => {
3585 self.require_features(wgt::Features::PIPELINE_STATISTICS_QUERY)?;
3586 }
3587 }
3588
3589 if desc.count == 0 {
3590 return Err(Error::ZeroCount);
3591 }
3592
3593 if desc.count > wgt::QUERY_SET_MAX_QUERIES {
3594 return Err(Error::TooManyQueries {
3595 count: desc.count,
3596 maximum: wgt::QUERY_SET_MAX_QUERIES,
3597 });
3598 }
3599
3600 let hal_desc = desc.map_label(|label| label.to_hal(self.instance_flags));
3601 Ok(QuerySet {
3602 raw: Some(unsafe { self.raw().create_query_set(&hal_desc).unwrap() }),
3603 device: self.clone(),
3604 info: ResourceInfo::new("", Some(self.tracker_indices.query_sets.clone())),
3605 desc: desc.map_label(|_| ()),
3606 })
3607 }
3608
3609 pub(crate) fn lose(&self, message: &str) {
3610 self.valid.store(false, Ordering::Release);
3615
3616 let mut life_lock = self.lock_life();
3618 let closure = life_lock.device_lost_closure.take();
3619 drop(life_lock);
3622
3623 if let Some(device_lost_closure) = closure {
3624 device_lost_closure.call(DeviceLostReason::Unknown, message.to_string());
3625 }
3626
3627 self.release_gpu_resources();
3637 }
3638
3639 pub(crate) fn release_gpu_resources(&self) {
3640 let trackers = self.trackers.lock();
3650 for buffer in trackers.buffers.used_resources() {
3651 let _ = buffer.destroy();
3652 }
3653 for texture in trackers.textures.used_resources() {
3654 let _ = texture.destroy();
3655 }
3656 }
3657
3658 pub(crate) fn new_usage_scope(&self) -> UsageScope<'_, A> {
3659 UsageScope::new_pooled(&self.usage_scopes, &self.tracker_indices)
3660 }
3661}
3662
3663impl<A: HalApi> Device<A> {
3664 pub(crate) fn destroy_command_buffer(&self, mut cmd_buf: command::CommandBuffer<A>) {
3665 let mut baked = cmd_buf.extract_baked_commands();
3666 unsafe {
3667 baked.encoder.reset_all(baked.list.into_iter());
3668 }
3669 unsafe {
3670 self.raw
3671 .as_ref()
3672 .unwrap()
3673 .destroy_command_encoder(baked.encoder);
3674 }
3675 }
3676
3677 pub(crate) fn prepare_to_die(&self) {
3679 self.pending_writes.lock().as_mut().unwrap().deactivate();
3680 let current_index = self.active_submission_index.load(Ordering::Relaxed);
3681 if let Err(error) = unsafe {
3682 let fence = self.fence.read();
3683 let fence = fence.as_ref().unwrap();
3684 self.raw
3685 .as_ref()
3686 .unwrap()
3687 .wait(fence, current_index, CLEANUP_WAIT_MS)
3688 } {
3689 log::error!("failed to wait for the device: {error}");
3690 }
3691 let mut life_tracker = self.lock_life();
3692 let _ = life_tracker.triage_submissions(current_index, &self.command_allocator);
3693 if let Some(device_lost_closure) = life_tracker.device_lost_closure.take() {
3694 drop(life_tracker);
3696 device_lost_closure.call(DeviceLostReason::Dropped, "Device is dying.".to_string());
3697 }
3698 #[cfg(feature = "trace")]
3699 {
3700 *self.trace.lock() = None;
3701 }
3702 }
3703}
3704
3705impl<A: HalApi> Resource for Device<A> {
3706 const TYPE: ResourceType = "Device";
3707
3708 type Marker = crate::id::markers::Device;
3709
3710 fn as_info(&self) -> &ResourceInfo<Self> {
3711 &self.info
3712 }
3713
3714 fn as_info_mut(&mut self) -> &mut ResourceInfo<Self> {
3715 &mut self.info
3716 }
3717}