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