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