wgpu_hal/vulkan/mod.rs
1/*!
2# Vulkan API internals.
3
4## Stack memory
5
6Ash expects slices, which we don't generally have available.
7We cope with this requirement by the combination of the following ways:
8 - temporarily allocating `Vec` on heap, where overhead is permitted
9 - growing temporary local storage
10
11## Framebuffers and Render passes
12
13Render passes are cached on the device and kept forever.
14
15Framebuffers are also cached on the device, but they are removed when
16any of the image views (they have) gets removed.
17If Vulkan supports image-less framebuffers,
18then the actual views are excluded from the framebuffer key.
19
20## Fences
21
22If timeline semaphores are available, they are used 1:1 with wgpu-hal fences.
23Otherwise, we manage a pool of `VkFence` objects behind each `hal::Fence`.
24
25!*/
26
27mod adapter;
28mod command;
29pub mod conv;
30mod descriptor;
31mod device;
32mod drm;
33mod instance;
34mod sampler;
35mod semaphore_list;
36mod swapchain;
37
38pub use adapter::PhysicalDeviceFeatures;
39
40use alloc::{boxed::Box, ffi::CString, sync::Arc, vec::Vec};
41use core::{borrow::Borrow, ffi::CStr, fmt, marker::PhantomData, mem, num::NonZeroU32};
42
43use arrayvec::ArrayVec;
44use ash::{ext, khr, vk};
45use bytemuck::{Pod, Zeroable};
46use hashbrown::HashSet;
47use parking_lot::{Mutex, RwLock};
48
49use naga::FastHashMap;
50use wgt::InternalCounter;
51
52use semaphore_list::SemaphoreList;
53
54use crate::vulkan::semaphore_list::{SemaphoreListMode, SemaphoreType};
55
56const MAX_TOTAL_ATTACHMENTS: usize = crate::MAX_COLOR_ATTACHMENTS * 2 + 1;
57
58#[derive(Clone, Debug)]
59pub struct Api;
60
61impl crate::Api for Api {
62 const VARIANT: wgt::Backend = wgt::Backend::Vulkan;
63
64 type Instance = Instance;
65 type Surface = Surface;
66 type Adapter = Adapter;
67 type Device = Device;
68
69 type Queue = Queue;
70 type CommandEncoder = CommandEncoder;
71 type CommandBuffer = CommandBuffer;
72
73 type Buffer = Buffer;
74 type Texture = Texture;
75 type SurfaceTexture = SurfaceTexture;
76 type TextureView = TextureView;
77 type Sampler = Sampler;
78 type QuerySet = QuerySet;
79 type Fence = Fence;
80 type AccelerationStructure = AccelerationStructure;
81 type PipelineCache = PipelineCache;
82
83 type BindGroupLayout = BindGroupLayout;
84 type BindGroup = BindGroup;
85 type PipelineLayout = PipelineLayout;
86 type ShaderModule = ShaderModule;
87 type RenderPipeline = RenderPipeline;
88 type ComputePipeline = ComputePipeline;
89 type RayTracingPipeline = RayTracingPipeline;
90}
91
92crate::impl_dyn_resource!(
93 Adapter,
94 AccelerationStructure,
95 BindGroup,
96 BindGroupLayout,
97 Buffer,
98 CommandBuffer,
99 CommandEncoder,
100 ComputePipeline,
101 Device,
102 Fence,
103 Instance,
104 PipelineCache,
105 PipelineLayout,
106 QuerySet,
107 Queue,
108 RenderPipeline,
109 RayTracingPipeline,
110 Sampler,
111 ShaderModule,
112 Surface,
113 SurfaceTexture,
114 Texture,
115 TextureView
116);
117
118struct DebugUtils {
119 extension: ext::debug_utils::Instance,
120 messenger: vk::DebugUtilsMessengerEXT,
121
122 /// Owning pointer to the debug messenger callback user data.
123 ///
124 /// `InstanceShared::drop` destroys the debug messenger before
125 /// dropping this, so the callback should never receive a dangling
126 /// user data pointer.
127 #[allow(dead_code)]
128 callback_data: Box<DebugUtilsMessengerUserData>,
129}
130
131#[derive(Debug)]
132pub struct DebugUtilsCreateInfo {
133 severity: vk::DebugUtilsMessageSeverityFlagsEXT,
134 message_type: vk::DebugUtilsMessageTypeFlagsEXT,
135 callback_data: Box<DebugUtilsMessengerUserData>,
136}
137
138#[derive(Debug)]
139/// The properties related to the validation layer needed for the
140/// DebugUtilsMessenger for their workarounds
141struct ValidationLayerProperties {
142 /// Validation layer description, from `vk::LayerProperties`.
143 layer_description: CString,
144
145 /// Validation layer specification version, from `vk::LayerProperties`.
146 layer_spec_version: u32,
147}
148
149/// User data needed by `instance::debug_utils_messenger_callback`.
150///
151/// When we create the [`vk::DebugUtilsMessengerEXT`], the `pUserData`
152/// pointer refers to one of these values.
153#[derive(Debug)]
154pub struct DebugUtilsMessengerUserData {
155 /// The properties related to the validation layer, if present
156 validation_layer_properties: Option<ValidationLayerProperties>,
157
158 /// If the OBS layer is present. OBS never increments the version of their layer,
159 /// so there's no reason to have the version.
160 has_obs_layer: bool,
161}
162
163pub struct InstanceShared {
164 raw: ash::Instance,
165 extensions: Vec<&'static CStr>,
166 flags: wgt::InstanceFlags,
167 memory_budget_thresholds: wgt::MemoryBudgetThresholds,
168 debug_utils: Option<DebugUtils>,
169 get_physical_device_properties: Option<khr::get_physical_device_properties2::Instance>,
170 entry: ash::Entry,
171 has_nv_optimus: bool,
172 android_sdk_version: u32,
173 /// The instance API version.
174 ///
175 /// Which is the version of Vulkan supported for instance-level functionality.
176 ///
177 /// It is associated with a `VkInstance` and its children,
178 /// except for a `VkPhysicalDevice` and its children.
179 instance_api_version: u32,
180
181 // The `drop_guard` field must be the last field of this struct so it is dropped last.
182 // Do not add new fields after it.
183 drop_guard: Option<crate::DropGuard>,
184}
185
186impl fmt::Debug for InstanceShared {
187 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
188 let Self {
189 raw: _,
190 extensions,
191 flags,
192 memory_budget_thresholds,
193 debug_utils: _,
194 get_physical_device_properties: _,
195 entry: _,
196 has_nv_optimus,
197 android_sdk_version,
198 instance_api_version,
199 drop_guard: _,
200 } = self;
201 f.debug_struct("InstanceShared")
202 .field("extensions", extensions)
203 .field("flags", flags)
204 .field("memory_budget_thresholds", memory_budget_thresholds)
205 .field("has_nv_optimus", has_nv_optimus)
206 .field("android_sdk_version", android_sdk_version)
207 .field("instance_api_version", instance_api_version)
208 .finish_non_exhaustive()
209 }
210}
211
212#[derive(Debug)]
213pub struct Instance {
214 shared: Arc<InstanceShared>,
215}
216
217#[expect(missing_debug_implementations, reason = "TODO?")]
218pub struct Surface {
219 swapchain: RwLock<Option<Box<dyn swapchain::Swapchain>>>,
220 inner: Box<dyn swapchain::Surface>,
221}
222
223impl Surface {
224 /// Returns the raw Vulkan surface handle.
225 ///
226 /// Returns `None` if the surface is a DXGI surface.
227 pub unsafe fn raw_native_handle(&self) -> Option<vk::SurfaceKHR> {
228 Some(
229 self.inner
230 .as_any()
231 .downcast_ref::<swapchain::NativeSurface>()?
232 .as_raw(),
233 )
234 }
235
236 /// Get the raw Vulkan swapchain associated with this surface.
237 ///
238 /// Returns [`None`] if the surface is not configured or if the swapchain
239 /// is a DXGI swapchain.
240 pub fn raw_native_swapchain(&self) -> Option<vk::SwapchainKHR> {
241 let read = self.swapchain.read();
242 Some(
243 read.as_ref()?
244 .as_any()
245 .downcast_ref::<swapchain::NativeSwapchain>()?
246 .as_raw(),
247 )
248 }
249
250 /// Set the present timing information which will be used for the next [presentation](crate::Queue::present()) of this surface,
251 /// using [VK_GOOGLE_display_timing].
252 ///
253 /// This can be used to give an id to presentations, for future use of [`vk::PastPresentationTimingGOOGLE`].
254 /// Note that `wgpu-hal` does *not* provide a way to use that API - you should manually access this through [`ash`].
255 ///
256 /// This can also be used to add a "not before" timestamp to the presentation.
257 ///
258 /// The exact semantics of the fields are also documented in the [specification](https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkPresentTimeGOOGLE.html) for the extension.
259 ///
260 /// # Panics
261 ///
262 /// - If the surface hasn't been configured.
263 /// - If the surface has been configured for a DXGI swapchain.
264 /// - If the device doesn't [support present timing](wgt::Features::VULKAN_GOOGLE_DISPLAY_TIMING).
265 ///
266 /// [VK_GOOGLE_display_timing]: https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VK_GOOGLE_display_timing.html
267 #[track_caller]
268 pub fn set_next_present_time(&self, present_timing: vk::PresentTimeGOOGLE) {
269 let mut swapchain = self.swapchain.write();
270 swapchain
271 .as_mut()
272 .expect("Surface should have been configured")
273 .as_any_mut()
274 .downcast_mut::<swapchain::NativeSwapchain>()
275 .expect("Surface should have a native Vulkan swapchain")
276 .set_next_present_time(present_timing);
277 }
278}
279
280#[derive(Debug)]
281pub struct SurfaceTexture {
282 index: u32,
283 texture: Texture,
284 metadata: Box<dyn swapchain::SurfaceTextureMetadata>,
285}
286
287impl crate::DynSurfaceTexture for SurfaceTexture {}
288
289impl Borrow<Texture> for SurfaceTexture {
290 fn borrow(&self) -> &Texture {
291 &self.texture
292 }
293}
294
295impl Borrow<dyn crate::DynTexture> for SurfaceTexture {
296 fn borrow(&self) -> &dyn crate::DynTexture {
297 &self.texture
298 }
299}
300
301#[derive(Debug)]
302pub struct Adapter {
303 raw: vk::PhysicalDevice,
304 instance: Arc<InstanceShared>,
305 //queue_families: Vec<vk::QueueFamilyProperties>,
306 known_memory_flags: vk::MemoryPropertyFlags,
307 phd_capabilities: adapter::PhysicalDeviceProperties,
308 phd_features: PhysicalDeviceFeatures,
309 downlevel_flags: wgt::DownlevelFlags,
310 private_caps: PrivateCapabilities,
311 workarounds: Workarounds,
312}
313
314// TODO there's no reason why this can't be unified--the function pointers should all be the same--it's not clear how to do this with `ash`.
315enum ExtensionFn<T> {
316 /// The loaded function pointer struct for an extension.
317 Extension(T),
318 /// The extension was promoted to a core version of Vulkan and the functions on `ash`'s `DeviceV1_x` traits should be used.
319 Promoted,
320}
321
322struct DeviceExtensionFunctions {
323 debug_utils: Option<ext::debug_utils::Device>,
324 draw_indirect_count: Option<khr::draw_indirect_count::Device>,
325 timeline_semaphore: Option<ExtensionFn<khr::timeline_semaphore::Device>>,
326 ray_tracing: Option<RayTracingDeviceExtensionFunctions>,
327 ray_tracing_pipelines: Option<khr::ray_tracing_pipeline::Device>,
328 mesh_shading: Option<ext::mesh_shader::Device>,
329 #[cfg_attr(not(unix), allow(dead_code))]
330 external_memory_fd: Option<khr::external_memory_fd::Device>,
331}
332
333struct RayTracingDeviceExtensionFunctions {
334 acceleration_structure: khr::acceleration_structure::Device,
335 buffer_device_address: khr::buffer_device_address::Device,
336}
337
338/// Set of internal capabilities, which don't show up in the exposed
339/// device geometry, but affect the code paths taken internally.
340#[derive(Clone, Debug)]
341struct PrivateCapabilities {
342 image_view_usage: bool,
343 timeline_semaphores: bool,
344 texture_d24: bool,
345 texture_d24_s8: bool,
346 texture_s8: bool,
347 /// Ability to present contents to any screen. Only needed to work around broken platform configurations.
348 can_present: bool,
349 non_coherent_map_mask: wgt::BufferAddress,
350 multi_draw_indirect: bool,
351 max_draw_indirect_count: u32,
352
353 /// True if this adapter advertises the [`robustBufferAccess`][vrba] feature.
354 ///
355 /// Note that Vulkan's `robustBufferAccess` is not sufficient to implement
356 /// `wgpu_hal`'s guarantee that shaders will not access buffer contents via
357 /// a given bindgroup binding outside that binding's [accessible
358 /// region][ar]. Enabling `robustBufferAccess` does ensure that
359 /// out-of-bounds reads and writes are not undefined behavior (that's good),
360 /// but still permits out-of-bounds reads to return data from anywhere
361 /// within the buffer, not just the accessible region.
362 ///
363 /// [ar]: ../struct.BufferBinding.html#accessible-region
364 /// [vrba]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#features-robustBufferAccess
365 robust_buffer_access: bool,
366
367 robust_image_access: bool,
368
369 /// True if this adapter supports the [`VK_EXT_robustness2`] extension's
370 /// [`robustBufferAccess2`] feature.
371 ///
372 /// This is sufficient to implement `wgpu_hal`'s [required bounds-checking][ar] of
373 /// shader accesses to buffer contents. If this feature is not available,
374 /// this backend must have Naga inject bounds checks in the generated
375 /// SPIR-V.
376 ///
377 /// [`VK_EXT_robustness2`]: https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VK_EXT_robustness2.html
378 /// [`robustBufferAccess2`]: https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkPhysicalDeviceRobustness2FeaturesEXT.html#features-robustBufferAccess2
379 /// [ar]: ../struct.BufferBinding.html#accessible-region
380 robust_buffer_access2: bool,
381
382 robust_image_access2: bool,
383 zero_initialize_workgroup_memory: bool,
384 image_format_list: bool,
385 maximum_samplers: u32,
386
387 /// True if this adapter supports the [`VK_KHR_shader_integer_dot_product`] extension
388 /// (promoted to Vulkan 1.3).
389 ///
390 /// This is used to generate optimized code for WGSL's `dot4{I, U}8Packed`.
391 ///
392 /// [`VK_KHR_shader_integer_dot_product`]: https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VK_KHR_shader_integer_dot_product.html
393 shader_integer_dot_product: bool,
394
395 /// True if this adapter supports 8-bit integers provided by the
396 /// [`VK_KHR_shader_float16_int8`] extension (promoted to Vulkan 1.2).
397 ///
398 /// Allows shaders to declare the "Int8" capability. Note, however, that this
399 /// feature alone allows the use of 8-bit integers "only in the `Private`,
400 /// `Workgroup` (for non-Block variables), and `Function` storage classes"
401 /// ([see spec]). To use 8-bit integers in the interface storage classes (e.g.,
402 /// `StorageBuffer`), you also need to enable the corresponding feature in
403 /// `VkPhysicalDevice8BitStorageFeatures` and declare the corresponding SPIR-V
404 /// capability (e.g., `StorageBuffer8BitAccess`).
405 ///
406 /// [`VK_KHR_shader_float16_int8`]: https://registry.khronos.org/vulkan/specs/latest/man/html/VK_KHR_shader_float16_int8.html
407 /// [see spec]: https://registry.khronos.org/vulkan/specs/latest/man/html/VkPhysicalDeviceShaderFloat16Int8Features.html#extension-features-shaderInt8
408 shader_int8: bool,
409
410 /// This is done to panic before undefined behavior, and is imperfect.
411 /// Basically, to allow implementations to emulate mv using instancing, if you
412 /// want to draw `n` instances to VR, you must draw `2n` instances, but you
413 /// can never draw more than `u32::MAX` instances. Therefore, when drawing
414 /// multiview on some vulkan implementations, it might restrict the instance
415 /// count, which isn't usually a thing in webgpu. We don't expose this limit
416 /// because its strange, i.e. only occurs on certain vulkan implementations
417 /// if you are drawing more than 128 million instances. We still want to avoid
418 /// undefined behavior in this situation, so we panic if the limit is violated.
419 multiview_instance_index_limit: u32,
420
421 /// BufferUsages::ACCELERATION_STRUCTURE_SCRATCH allows usage as a scratch buffer.
422 /// Vulkan has no way to specify this as a usage, and it maps to other usages, but
423 /// these usages do not have as high of an alignment requirement using the buffer as
424 /// a scratch buffer when building acceleration structures.
425 scratch_buffer_alignment: u32,
426
427 /// `get_raytracing_pipeline_group_data` requires both a group count and a data size.
428 /// The data size parameter is just this * the group count, so we store this to not
429 /// require an unnecessary parameter.
430 ray_tracing_pipeline_group_data_size: u32,
431}
432
433bitflags::bitflags!(
434 /// Workaround flags.
435 #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
436 pub struct Workarounds: u32 {
437 /// Only generate SPIR-V for one entry point at a time.
438 const SEPARATE_ENTRY_POINTS = 0x1;
439 /// Qualcomm OOMs when there are zero color attachments but a non-null pointer
440 /// to a subpass resolve attachment array. This nulls out that pointer in that case.
441 const EMPTY_RESOLVE_ATTACHMENT_LISTS = 0x2;
442 /// If the following code returns false, then nvidia will end up filling the wrong range.
443 ///
444 /// ```skip
445 /// fn nvidia_succeeds() -> bool {
446 /// # let (copy_length, start_offset) = (0, 0);
447 /// if copy_length >= 4096 {
448 /// if start_offset % 16 != 0 {
449 /// if copy_length == 4096 {
450 /// return true;
451 /// }
452 /// if copy_length % 16 == 0 {
453 /// return false;
454 /// }
455 /// }
456 /// }
457 /// true
458 /// }
459 /// ```
460 ///
461 /// As such, we need to make sure all calls to vkCmdFillBuffer are aligned to 16 bytes
462 /// if they cover a range of 4096 bytes or more.
463 const FORCE_FILL_BUFFER_WITH_SIZE_GREATER_4096_ALIGNED_OFFSET_16 = 0x4;
464 }
465);
466
467#[derive(Clone, Debug, Eq, Hash, PartialEq)]
468struct AttachmentKey {
469 format: vk::Format,
470 layout: vk::ImageLayout,
471 ops: crate::AttachmentOps,
472}
473
474impl AttachmentKey {
475 /// Returns an attachment key for a compatible attachment.
476 fn compatible(format: vk::Format, layout: vk::ImageLayout) -> Self {
477 Self {
478 format,
479 layout,
480 ops: crate::AttachmentOps::all(),
481 }
482 }
483}
484
485#[derive(Clone, Eq, Hash, PartialEq)]
486struct ColorAttachmentKey {
487 base: AttachmentKey,
488 resolve: Option<AttachmentKey>,
489}
490
491#[derive(Clone, Eq, Hash, PartialEq)]
492struct DepthStencilAttachmentKey {
493 base: AttachmentKey,
494 stencil_ops: crate::AttachmentOps,
495}
496
497#[derive(Clone, Eq, Default, Hash, PartialEq)]
498struct RenderPassKey {
499 colors: ArrayVec<Option<ColorAttachmentKey>, { crate::MAX_COLOR_ATTACHMENTS }>,
500 depth_stencil: Option<DepthStencilAttachmentKey>,
501 sample_count: u32,
502 multiview_mask: Option<NonZeroU32>,
503}
504
505struct DeviceShared {
506 raw: ash::Device,
507 family_index: u32,
508 queue_index: u32,
509 raw_queue: vk::Queue,
510 instance: Arc<InstanceShared>,
511 physical_device: vk::PhysicalDevice,
512 enabled_extensions: Vec<&'static CStr>,
513 extension_fns: DeviceExtensionFunctions,
514 vendor_id: u32,
515 pipeline_cache_validation_key: [u8; 16],
516 timestamp_period: f32,
517 private_caps: PrivateCapabilities,
518 workarounds: Workarounds,
519 features: wgt::Features,
520 render_passes: Mutex<FastHashMap<RenderPassKey, vk::RenderPass>>,
521 sampler_cache: Mutex<sampler::SamplerCache>,
522 memory_allocations_counter: InternalCounter,
523
524 /// Because we have cached framebuffers which are not deleted from until
525 /// the device is destroyed, if the implementation of vulkan re-uses handles
526 /// we need some way to differentiate between the old handle and the new handle.
527 /// This factory allows us to have a dedicated identity value for each texture.
528 texture_identity_factory: ResourceIdentityFactory<vk::Image>,
529 /// As above, for texture views.
530 texture_view_identity_factory: ResourceIdentityFactory<vk::ImageView>,
531
532 empty_descriptor_set_layout: vk::DescriptorSetLayout,
533
534 /// Whether a bind group layout lets a stage other than fragment or
535 /// compute read a texture. Until one does, a texture read by shaders is
536 /// waited for only in those two stages, so a pass that samples what the
537 /// previous pass drew no longer holds its vertex work behind that pass's
538 /// fragment work. Set once, never cleared.
539 texture_reads_before_fragment: core::sync::atomic::AtomicBool,
540
541 // The `drop_guard` field must be the last field of this struct so it is dropped last.
542 // Do not add new fields after it.
543 drop_guard: Option<crate::DropGuard>,
544}
545
546impl Drop for DeviceShared {
547 fn drop(&mut self) {
548 for &raw in self.render_passes.lock().values() {
549 unsafe { self.raw.destroy_render_pass(raw, None) };
550 }
551 unsafe {
552 self.raw
553 .destroy_descriptor_set_layout(self.empty_descriptor_set_layout, None)
554 };
555 if self.drop_guard.is_none() {
556 unsafe { self.raw.destroy_device(None) };
557 }
558 }
559}
560
561#[expect(
562 missing_debug_implementations,
563 reason = "needs work to not be disastrously verbose"
564)]
565pub struct Device {
566 mem_allocator: Mutex<gpu_allocator::vulkan::Allocator>,
567 desc_allocator: Mutex<descriptor::DescriptorAllocator>,
568 valid_ash_memory_types: u32,
569 naga_options: naga::back::spv::Options<'static>,
570 #[cfg(feature = "renderdoc")]
571 render_doc: crate::auxil::renderdoc::RenderDoc,
572 counters: Arc<wgt::HalCounters>,
573 // Struct members are dropped from first to last, put the Device last to ensure that
574 // all resources that depends on it are destroyed before it like the mem_allocator
575 shared: Arc<DeviceShared>,
576}
577
578impl Drop for Device {
579 fn drop(&mut self) {}
580}
581
582/// Semaphores for forcing queue submissions to run in order.
583///
584/// The [`wgpu_hal::Queue`] trait promises that if two calls to [`submit`] are
585/// ordered, then the first submission will finish on the GPU before the second
586/// submission begins. To get this behavior on Vulkan we need to pass semaphores
587/// to [`vkQueueSubmit`] for the commands to wait on before beginning execution,
588/// and to signal when their execution is done.
589///
590/// Normally this can be done with a single semaphore, waited on and then
591/// signalled for each submission. At any given time there's exactly one
592/// submission that would signal the semaphore, and exactly one waiting on it,
593/// as Vulkan requires.
594///
595/// However, as of Oct 2021, bug [#5508] in the Mesa ANV drivers caused them to
596/// hang if we use a single semaphore. The workaround is to alternate between
597/// two semaphores. The bug has been fixed in Mesa, but we should probably keep
598/// the workaround until, say, Oct 2026.
599///
600/// [`wgpu_hal::Queue`]: crate::Queue
601/// [`submit`]: crate::Queue::submit
602/// [`vkQueueSubmit`]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#vkQueueSubmit
603/// [#5508]: https://gitlab.freedesktop.org/mesa/mesa/-/issues/5508
604#[derive(Clone)]
605struct RelaySemaphores {
606 /// The semaphore the next submission should wait on before beginning
607 /// execution on the GPU. This is `None` for the first submission, which
608 /// should not wait on anything at all.
609 wait: Option<vk::Semaphore>,
610
611 /// The semaphore the next submission should signal when it has finished
612 /// execution on the GPU.
613 signal: vk::Semaphore,
614}
615
616impl RelaySemaphores {
617 fn new(device: &DeviceShared) -> Result<Self, crate::DeviceError> {
618 Ok(Self {
619 wait: None,
620 signal: device.new_binary_semaphore("RelaySemaphores: 1")?,
621 })
622 }
623
624 /// Advances the semaphores, returning the semaphores that should be used for a submission.
625 fn advance(&mut self, device: &DeviceShared) -> Result<Self, crate::DeviceError> {
626 let old = self.clone();
627
628 // Build the state for the next submission.
629 match self.wait {
630 None => {
631 // The `old` values describe the first submission to this queue.
632 // The second submission should wait on `old.signal`, and then
633 // signal a new semaphore which we'll create now.
634 self.wait = Some(old.signal);
635 self.signal = device.new_binary_semaphore("RelaySemaphores: 2")?;
636 }
637 Some(ref mut wait) => {
638 // What this submission signals, the next should wait.
639 mem::swap(wait, &mut self.signal);
640 }
641 };
642
643 Ok(old)
644 }
645
646 /// Destroys the semaphores.
647 unsafe fn destroy(&self, device: &ash::Device) {
648 unsafe {
649 if let Some(wait) = self.wait {
650 device.destroy_semaphore(wait, None);
651 }
652 device.destroy_semaphore(self.signal, None);
653 }
654 }
655}
656
657pub struct Queue {
658 raw: vk::Queue,
659 device: Arc<DeviceShared>,
660 family_index: u32,
661 relay_semaphores: Mutex<RelaySemaphores>,
662 signal_semaphores: Mutex<SemaphoreList>,
663 wait_semaphores: Mutex<SemaphoreList>,
664}
665
666impl fmt::Debug for Queue {
667 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
668 let Self {
669 raw: _,
670 device: _,
671 family_index,
672 relay_semaphores: _,
673 signal_semaphores: _,
674 wait_semaphores: _,
675 } = self;
676 f.debug_struct("Queue")
677 .field("family_index", family_index)
678 .finish_non_exhaustive()
679 }
680}
681
682impl Queue {
683 pub fn as_raw(&self) -> vk::Queue {
684 self.raw
685 }
686}
687
688impl Drop for Queue {
689 fn drop(&mut self) {
690 unsafe { self.relay_semaphores.lock().destroy(&self.device.raw) };
691 }
692}
693#[derive(Debug)]
694enum BufferMemoryBacking {
695 Managed(gpu_allocator::vulkan::Allocation),
696 VulkanMemory {
697 memory: vk::DeviceMemory,
698 offset: u64,
699 size: u64,
700 },
701}
702impl BufferMemoryBacking {
703 fn memory(&self) -> vk::DeviceMemory {
704 match self {
705 Self::Managed(m) => unsafe { m.memory() },
706 Self::VulkanMemory { memory, .. } => *memory,
707 }
708 }
709 fn offset(&self) -> u64 {
710 match self {
711 Self::Managed(m) => m.offset(),
712 Self::VulkanMemory { offset, .. } => *offset,
713 }
714 }
715 fn size(&self) -> u64 {
716 match self {
717 Self::Managed(m) => m.size(),
718 Self::VulkanMemory { size, .. } => *size,
719 }
720 }
721}
722/// Describes who owns a [`Buffer`]'s `vk::Buffer` handle and its backing memory,
723/// and therefore what cleanup is required when the buffer is destroyed.
724#[derive(Debug)]
725enum BufferOwnership {
726 /// wgpu-hal owns the `vk::Buffer` and its backing memory. On cleanup the buffer
727 /// handle is destroyed and the memory is released.
728 Managed(Mutex<BufferMemoryBacking>),
729 /// wgpu-hal owns the `vk::Buffer` handle but the backing memory is kept alive
730 /// by the caller. On cleanup only the buffer handle is destroyed.
731 RawHandle,
732 /// Caller owns the `vk::Buffer` and its backing memory. On cleanup the
733 /// [`crate::DropGuard`] runs the caller's cleanup callback and wgpu-hal touches
734 /// neither the handle nor the memory.
735 External(crate::DropGuard),
736}
737
738#[derive(Debug)]
739pub struct Buffer {
740 raw: vk::Buffer,
741
742 // This field must be last, because it may contain a `DropGuard` which needs to be dropped after all other fields.
743 ownership: BufferOwnership,
744}
745impl Buffer {
746 /// # Safety
747 ///
748 /// - `vk_buffer`'s memory must be managed by the caller
749 /// - Externally imported buffers can't be mapped by `wgpu`
750 pub unsafe fn from_raw(vk_buffer: vk::Buffer) -> Self {
751 Self {
752 raw: vk_buffer,
753 ownership: BufferOwnership::RawHandle,
754 }
755 }
756
757 /// # Safety
758 /// - `vk_buffer` must outlive the returned `Buffer`.
759 /// - wgpu-hal will NOT call `vkDestroyBuffer`; the caller remains responsible for the buffer handle's destruction.
760 /// The `drop_callback` runs when the `Buffer` drops and may be used to release caller-side bookkeeping.
761 /// - Externally imported buffers can't be mapped by `wgpu`.
762 pub unsafe fn from_raw_externally_owned(
763 vk_buffer: vk::Buffer,
764 drop_callback: crate::DropCallback,
765 ) -> Self {
766 Self {
767 raw: vk_buffer,
768 ownership: BufferOwnership::External(crate::DropGuard::new(drop_callback)),
769 }
770 }
771
772 /// # Safety
773 /// - We will use this buffer and the buffer's backing memory range as if we have exclusive ownership over it, until the wgpu resource is dropped and the wgpu-hal object is cleaned up
774 /// - Externally imported buffers can't be mapped by `wgpu`
775 /// - `offset` and `size` must be valid with the allocation of `memory`
776 pub unsafe fn from_raw_managed(
777 vk_buffer: vk::Buffer,
778 memory: vk::DeviceMemory,
779 offset: u64,
780 size: u64,
781 ) -> Self {
782 Self {
783 raw: vk_buffer,
784 ownership: BufferOwnership::Managed(Mutex::new(BufferMemoryBacking::VulkanMemory {
785 memory,
786 offset,
787 size,
788 })),
789 }
790 }
791
792 /// # Safety
793 /// - The buffer handle must not be manually destroyed
794 pub unsafe fn raw_handle(&self) -> vk::Buffer {
795 self.raw
796 }
797}
798
799impl crate::DynBuffer for Buffer {}
800
801#[derive(Debug)]
802pub struct AccelerationStructure {
803 raw: vk::AccelerationStructureKHR,
804 buffer: vk::Buffer,
805 allocation: gpu_allocator::vulkan::Allocation,
806 compacted_size_query: Option<vk::QueryPool>,
807}
808
809impl crate::DynAccelerationStructure for AccelerationStructure {}
810
811#[derive(Debug)]
812pub enum TextureMemory {
813 // shared memory in GPU allocator (owned by wgpu-hal)
814 Allocation(gpu_allocator::vulkan::Allocation),
815
816 // dedicated memory (owned by wgpu-hal)
817 Dedicated(vk::DeviceMemory),
818
819 // memory not owned by wgpu
820 External,
821}
822
823#[derive(Debug)]
824pub struct Texture {
825 raw: vk::Image,
826 memory: TextureMemory,
827 format: wgt::TextureFormat,
828 copy_size: crate::CopyExtent,
829 identity: ResourceIdentity<vk::Image>,
830
831 // The `drop_guard` field must be the last field of this struct so it is dropped last.
832 // Do not add new fields after it.
833 drop_guard: Option<crate::DropGuard>,
834}
835
836impl crate::DynTexture for Texture {}
837
838impl Texture {
839 /// # Safety
840 ///
841 /// - The image handle must not be manually destroyed
842 pub unsafe fn raw_handle(&self) -> vk::Image {
843 self.raw
844 }
845
846 /// # Safety
847 ///
848 /// - The caller must not free the `vk::DeviceMemory` or
849 /// `gpu_alloc::MemoryBlock` in the returned `TextureMemory`.
850 pub unsafe fn memory(&self) -> &TextureMemory {
851 &self.memory
852 }
853}
854
855#[derive(Debug)]
856pub struct TextureView {
857 raw_texture: vk::Image,
858 raw: vk::ImageView,
859 _layers: NonZeroU32,
860 format: wgt::TextureFormat,
861 raw_format: vk::Format,
862 base_mip_level: u32,
863 dimension: wgt::TextureViewDimension,
864 texture_identity: ResourceIdentity<vk::Image>,
865 view_identity: ResourceIdentity<vk::ImageView>,
866}
867
868impl crate::DynTextureView for TextureView {}
869
870impl TextureView {
871 /// # Safety
872 ///
873 /// - The image view handle must not be manually destroyed
874 pub unsafe fn raw_handle(&self) -> vk::ImageView {
875 self.raw
876 }
877
878 /// Returns the raw texture view, along with its identity.
879 fn identified_raw_view(&self) -> IdentifiedTextureView {
880 IdentifiedTextureView {
881 raw: self.raw,
882 identity: self.view_identity,
883 }
884 }
885}
886
887#[derive(Debug)]
888pub struct Sampler {
889 raw: vk::Sampler,
890 create_info: vk::SamplerCreateInfo<'static>,
891}
892
893impl crate::DynSampler for Sampler {}
894
895/// Information about a binding within a specific BindGroupLayout / BindGroup.
896/// This will be used to construct a [`naga::back::spv::BindingInfo`], where
897/// the descriptor set value will be taken from the index of the group.
898#[derive(Copy, Clone, Debug)]
899struct BindingInfo {
900 binding: u32,
901 binding_array_size: Option<NonZeroU32>,
902}
903
904#[derive(Debug)]
905pub struct BindGroupLayout {
906 raw: vk::DescriptorSetLayout,
907 desc_count: descriptor::DescriptorCounts,
908 /// Sorted list of entries.
909 entries: Box<[wgt::BindGroupLayoutEntry]>,
910 /// Map of original binding index to remapped binding index and optional
911 /// array size.
912 binding_map: Vec<(u32, BindingInfo)>,
913 contains_binding_arrays: bool,
914}
915
916impl crate::DynBindGroupLayout for BindGroupLayout {}
917
918#[derive(Debug)]
919pub struct PipelineLayout {
920 raw: vk::PipelineLayout,
921 binding_map: naga::back::spv::BindingMap,
922}
923
924impl crate::DynPipelineLayout for PipelineLayout {}
925
926#[derive(Debug)]
927pub struct BindGroup {
928 set: descriptor::DescriptorSet,
929}
930
931impl crate::DynBindGroup for BindGroup {}
932
933/// Miscellaneous allocation recycling pool for `CommandAllocator`.
934#[derive(Default)]
935struct Temp {
936 marker: Vec<u8>,
937 buffer_barriers: Vec<vk::BufferMemoryBarrier<'static>>,
938 image_barriers: Vec<vk::ImageMemoryBarrier<'static>>,
939}
940
941impl Temp {
942 fn clear(&mut self) {
943 self.marker.clear();
944 self.buffer_barriers.clear();
945 self.image_barriers.clear();
946 }
947
948 fn make_c_str(&mut self, name: &str) -> &CStr {
949 self.marker.clear();
950 self.marker.extend_from_slice(name.as_bytes());
951 self.marker.push(0);
952 unsafe { CStr::from_bytes_with_nul_unchecked(&self.marker) }
953 }
954}
955
956/// Generates unique IDs for each resource of type `T`.
957///
958/// Because vk handles are not permanently unique, this
959/// provides a way to generate unique IDs for each resource.
960struct ResourceIdentityFactory<T> {
961 #[cfg(not(target_has_atomic = "64"))]
962 next_id: Mutex<u64>,
963 #[cfg(target_has_atomic = "64")]
964 next_id: core::sync::atomic::AtomicU64,
965 _phantom: PhantomData<T>,
966}
967
968impl<T> ResourceIdentityFactory<T> {
969 fn new() -> Self {
970 Self {
971 #[cfg(not(target_has_atomic = "64"))]
972 next_id: Mutex::new(0),
973 #[cfg(target_has_atomic = "64")]
974 next_id: core::sync::atomic::AtomicU64::new(0),
975 _phantom: PhantomData,
976 }
977 }
978
979 /// Returns a new unique ID for a resource of type `T`.
980 fn next(&self) -> ResourceIdentity<T> {
981 #[cfg(not(target_has_atomic = "64"))]
982 {
983 let mut next_id = self.next_id.lock();
984 let id = *next_id;
985 *next_id += 1;
986 ResourceIdentity {
987 id,
988 _phantom: PhantomData,
989 }
990 }
991
992 #[cfg(target_has_atomic = "64")]
993 ResourceIdentity {
994 id: self
995 .next_id
996 .fetch_add(1, core::sync::atomic::Ordering::Relaxed),
997 _phantom: PhantomData,
998 }
999 }
1000}
1001
1002/// A unique identifier for a resource of type `T`.
1003///
1004/// This is used as a hashable key for resources, which
1005/// is permanently unique through the lifetime of the program.
1006#[derive(Debug, Copy, Clone, Eq, Hash, PartialEq)]
1007struct ResourceIdentity<T> {
1008 id: u64,
1009 _phantom: PhantomData<T>,
1010}
1011
1012#[derive(Clone, Eq, Hash, PartialEq)]
1013struct FramebufferKey {
1014 raw_pass: vk::RenderPass,
1015 /// Because this is used as a key in a hash map, we need to include the identity
1016 /// so that this hashes differently, even if the ImageView handles are the same
1017 /// between different views.
1018 attachment_identities: ArrayVec<ResourceIdentity<vk::ImageView>, { MAX_TOTAL_ATTACHMENTS }>,
1019 /// While this is redundant for calculating the hash, we need access to an array
1020 /// of all the raw ImageViews when we are creating the actual framebuffer,
1021 /// so we store this here.
1022 attachment_views: ArrayVec<vk::ImageView, { MAX_TOTAL_ATTACHMENTS }>,
1023 extent: wgt::Extent3d,
1024}
1025
1026impl FramebufferKey {
1027 fn push_view(&mut self, view: IdentifiedTextureView) {
1028 self.attachment_identities.push(view.identity);
1029 self.attachment_views.push(view.raw);
1030 }
1031}
1032
1033/// A texture view paired with its identity.
1034#[derive(Copy, Clone)]
1035struct IdentifiedTextureView {
1036 raw: vk::ImageView,
1037 identity: ResourceIdentity<vk::ImageView>,
1038}
1039
1040#[derive(Clone, Eq, Hash, PartialEq)]
1041struct TempTextureViewKey {
1042 texture: vk::Image,
1043 /// As this is used in a hashmap, we need to
1044 /// include the identity so that this hashes differently,
1045 /// even if the Image handles are the same between different images.
1046 texture_identity: ResourceIdentity<vk::Image>,
1047 format: vk::Format,
1048 mip_level: u32,
1049 depth_slice: u32,
1050}
1051
1052// Any state in this struct that may be dirty after an abandoned encoding must
1053// be reset for reused encoders in `begin_encoding`.
1054pub struct CommandEncoder {
1055 raw: vk::CommandPool,
1056 device: Arc<DeviceShared>,
1057
1058 /// The current command buffer, if `self` is in the ["recording"]
1059 /// state.
1060 ///
1061 /// ["recording"]: crate::CommandEncoder
1062 ///
1063 /// If non-`null`, the buffer is in the Vulkan "recording" state.
1064 active: vk::CommandBuffer,
1065
1066 /// What kind of pass we are currently within: compute or render.
1067 bind_point: vk::PipelineBindPoint,
1068
1069 /// Allocation recycling pool for this encoder.
1070 temp: Temp,
1071
1072 /// A pool of available command buffers.
1073 ///
1074 /// These are all in the Vulkan "initial" state.
1075 free: Vec<vk::CommandBuffer>,
1076
1077 /// A pool of discarded command buffers.
1078 ///
1079 /// These could be in any Vulkan state except "pending".
1080 discarded: Vec<vk::CommandBuffer>,
1081
1082 /// If this is true, the active renderpass enabled a debug span,
1083 /// and needs to be disabled on renderpass close.
1084 rpass_debug_marker_active: bool,
1085
1086 /// If set, the end of the next render/compute pass will write a timestamp at
1087 /// the given pool & location.
1088 end_of_pass_timer_query: Option<(vk::QueryPool, u32)>,
1089
1090 framebuffers: FastHashMap<FramebufferKey, vk::Framebuffer>,
1091 temp_texture_views: FastHashMap<TempTextureViewKey, IdentifiedTextureView>,
1092
1093 counters: Arc<wgt::HalCounters>,
1094
1095 current_pipeline_is_multiview: bool,
1096}
1097
1098impl Drop for CommandEncoder {
1099 fn drop(&mut self) {
1100 // SAFETY:
1101 //
1102 // VUID-vkDestroyCommandPool-commandPool-00041: wgpu_hal requires that a
1103 // `CommandBuffer` must live until its execution is complete, and that a
1104 // `CommandBuffer` must not outlive the `CommandEncoder` that built it.
1105 // Thus, we know that none of our `CommandBuffers` are in the "pending"
1106 // state.
1107 //
1108 // The other VUIDs are pretty obvious.
1109 unsafe {
1110 // `vkDestroyCommandPool` also frees any command buffers allocated
1111 // from that pool, so there's no need to explicitly call
1112 // `vkFreeCommandBuffers` on `cmd_encoder`'s `free` and `discarded`
1113 // fields.
1114 self.device.raw.destroy_command_pool(self.raw, None);
1115 }
1116
1117 for (_, fb) in self.framebuffers.drain() {
1118 unsafe { self.device.raw.destroy_framebuffer(fb, None) };
1119 }
1120
1121 for (_, view) in self.temp_texture_views.drain() {
1122 unsafe { self.device.raw.destroy_image_view(view.raw, None) };
1123 }
1124
1125 self.counters.command_encoders.sub(1);
1126 }
1127}
1128
1129impl CommandEncoder {
1130 /// # Safety
1131 ///
1132 /// - The command buffer handle must not be manually destroyed
1133 pub unsafe fn raw_handle(&self) -> vk::CommandBuffer {
1134 self.active
1135 }
1136}
1137
1138impl fmt::Debug for CommandEncoder {
1139 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1140 f.debug_struct("CommandEncoder")
1141 .field("raw", &self.raw)
1142 .finish()
1143 }
1144}
1145
1146#[derive(Debug)]
1147pub struct CommandBuffer {
1148 raw: vk::CommandBuffer,
1149}
1150
1151impl crate::DynCommandBuffer for CommandBuffer {}
1152
1153#[derive(Debug)]
1154pub enum ShaderModule {
1155 Raw(vk::ShaderModule),
1156 Intermediate {
1157 naga_shader: crate::NagaShader,
1158 runtime_checks: wgt::ShaderRuntimeChecks,
1159 },
1160}
1161
1162impl crate::DynShaderModule for ShaderModule {}
1163
1164#[derive(Debug)]
1165pub struct RenderPipeline {
1166 raw: vk::Pipeline,
1167 is_multiview: bool,
1168}
1169
1170impl crate::DynRenderPipeline for RenderPipeline {}
1171
1172#[derive(Debug)]
1173pub struct ComputePipeline {
1174 raw: vk::Pipeline,
1175}
1176
1177impl crate::DynComputePipeline for ComputePipeline {}
1178
1179#[derive(Debug)]
1180pub struct RayTracingPipeline {
1181 raw: vk::Pipeline,
1182}
1183
1184impl crate::DynRayTracingPipeline for RayTracingPipeline {}
1185
1186#[derive(Debug)]
1187pub struct PipelineCache {
1188 raw: vk::PipelineCache,
1189}
1190
1191impl crate::DynPipelineCache for PipelineCache {}
1192
1193#[derive(Debug)]
1194pub struct QuerySet {
1195 raw: vk::QueryPool,
1196}
1197
1198impl crate::DynQuerySet for QuerySet {}
1199
1200/// The [`Api::Fence`] type for [`vulkan::Api`].
1201///
1202/// This is an `enum` because there are two possible implementations of
1203/// `wgpu-hal` fences on Vulkan: Vulkan fences, which work on any version of
1204/// Vulkan, and Vulkan timeline semaphores, which are easier and cheaper but
1205/// require non-1.0 features.
1206///
1207/// [`Device::create_fence`] returns a [`TimelineSemaphore`] if
1208/// [`VK_KHR_timeline_semaphore`] is available and enabled, and a [`FencePool`]
1209/// otherwise.
1210///
1211/// [`Api::Fence`]: crate::Api::Fence
1212/// [`vulkan::Api`]: Api
1213/// [`Device::create_fence`]: crate::Device::create_fence
1214/// [`TimelineSemaphore`]: Fence::TimelineSemaphore
1215/// [`VK_KHR_timeline_semaphore`]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VK_KHR_timeline_semaphore
1216/// [`FencePool`]: Fence::FencePool
1217#[derive(Debug)]
1218pub enum Fence {
1219 /// A Vulkan [timeline semaphore].
1220 ///
1221 /// These are simpler to use than Vulkan fences, since timeline semaphores
1222 /// work exactly the way [`wpgu_hal::Api::Fence`] is specified to work.
1223 ///
1224 /// [timeline semaphore]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#synchronization-semaphores
1225 /// [`wpgu_hal::Api::Fence`]: crate::Api::Fence
1226 TimelineSemaphore(vk::Semaphore),
1227
1228 /// A collection of Vulkan [fence]s, each associated with a [`FenceValue`].
1229 ///
1230 /// The effective [`FenceValue`] of this variant is the greater of
1231 /// `last_completed` and the maximum value associated with a signalled fence
1232 /// in `active`.
1233 ///
1234 /// Fences are available in all versions of Vulkan, but since they only have
1235 /// two states, "signaled" and "unsignaled", we need to use a separate fence
1236 /// for each queue submission we might want to wait for, and remember which
1237 /// [`FenceValue`] each one represents.
1238 ///
1239 /// One should keep the fence pool read while there are any references to the
1240 /// fences inside of them. This ensures there are no race conditions when
1241 /// resetting the fences
1242 ///
1243 /// [fence]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#synchronization-fences
1244 /// [`FenceValue`]: crate::FenceValue
1245 FencePool(RwLock<FencePool>),
1246}
1247
1248/// A shared fence type. The arc is expect to have a ref-count of one once a function has finished being called
1249///
1250/// A fence should have access synchronised as fence resetting might happen at any point. Resetting checks the ref-count
1251/// of the fence, so instead of copying the fence, it should have its `Arc` container cloned which shows not to reset
1252/// this fence as it is being used.
1253pub(super) type SynchronizedFence = Arc<vk::Fence>;
1254
1255#[derive(Debug)]
1256pub struct FencePool {
1257 last_completed: crate::FenceValue,
1258 /// The pending fence values have to be ascending.
1259 active: Vec<(crate::FenceValue, SynchronizedFence)>,
1260 // Don't need extra synchronisation around the fences here, if they are used they should be put into active.
1261 free: Vec<vk::Fence>,
1262}
1263
1264impl crate::DynFence for Fence {}
1265
1266impl Fence {
1267 /// Return the highest [`FenceValue`] among the signalled fences in `active`.
1268 ///
1269 /// As an optimization, assume that we already know that the fence has
1270 /// reached `last_completed`, and don't bother checking fences whose values
1271 /// are less than that: those fences remain in the `active` array only
1272 /// because we haven't called `maintain` yet to clean them up.
1273 ///
1274 /// [`FenceValue`]: crate::FenceValue
1275 fn check_active(
1276 device: &ash::Device,
1277 mut last_completed: crate::FenceValue,
1278 active: &[(crate::FenceValue, SynchronizedFence)],
1279 ) -> Result<crate::FenceValue, crate::DeviceError> {
1280 for &(value, ref raw) in active.iter() {
1281 unsafe {
1282 if value > last_completed
1283 && device
1284 // Don't need to clone as active should be from a read or
1285 // write lock which means this is already synchronised.
1286 .get_fence_status(**raw)
1287 .map_err(map_host_device_oom_and_lost_err)?
1288 {
1289 last_completed = value;
1290 }
1291 }
1292 }
1293 Ok(last_completed)
1294 }
1295
1296 /// Return the highest signalled [`FenceValue`] for `self`.
1297 ///
1298 /// [`FenceValue`]: crate::FenceValue
1299 fn get_latest(
1300 &self,
1301 device: &ash::Device,
1302 extension: Option<&ExtensionFn<khr::timeline_semaphore::Device>>,
1303 ) -> Result<crate::FenceValue, crate::DeviceError> {
1304 match *self {
1305 Self::TimelineSemaphore(raw) => unsafe {
1306 Ok(match *extension.unwrap() {
1307 ExtensionFn::Extension(ref ext) => ext
1308 .get_semaphore_counter_value(raw)
1309 .map_err(map_host_device_oom_and_lost_err)?,
1310 ExtensionFn::Promoted => device
1311 .get_semaphore_counter_value(raw)
1312 .map_err(map_host_device_oom_and_lost_err)?,
1313 })
1314 },
1315 Self::FencePool(ref pool) => {
1316 let FencePool {
1317 last_completed,
1318 ref active,
1319 free: _,
1320 } = *pool.read();
1321 Self::check_active(device, last_completed, active)
1322 }
1323 }
1324 }
1325
1326 /// Trim the internal state of this [`Fence`].
1327 ///
1328 /// This function has no externally visible effect, but you should call it
1329 /// periodically to keep this fence's resource consumption under control.
1330 ///
1331 /// For fences using the [`FencePool`] implementation, this function
1332 /// recycles fences that have been signaled. If you don't call this,
1333 /// [`Queue::submit`] will just keep allocating a new Vulkan fence every
1334 /// time it's called.
1335 ///
1336 /// [`FencePool`]: Fence::FencePool
1337 /// [`Queue::submit`]: crate::Queue::submit
1338 fn maintain(&self, device: &ash::Device) -> Result<(), crate::DeviceError> {
1339 match *self {
1340 Self::TimelineSemaphore(_) => {}
1341 Self::FencePool(ref pool) => {
1342 let FencePool {
1343 ref mut last_completed,
1344 ref mut active,
1345 ref mut free,
1346 } = *pool.write();
1347
1348 let base_free = free.len();
1349 let latest = Self::check_active(device, *last_completed, active)?;
1350
1351 active.retain_mut(|&mut (value, ref mut fence)| {
1352 if value > latest {
1353 true
1354 } else if let Some(fence) = Arc::get_mut(fence) {
1355 // No other references to these, so we have exclusive access. Add them to free and reset them later,
1356 // but drop them from active immediately
1357 free.push(*fence);
1358 false
1359 } else {
1360 // some other function is using it. Although this shouldn't be to long,
1361 // maintain shouldn't block, and it should be cleared up by the next time it happens
1362 true
1363 }
1364 });
1365
1366 if free.len() != base_free {
1367 unsafe { device.reset_fences(&free[base_free..]) }
1368 .map_err(map_device_oom_err)?
1369 }
1370 *last_completed = latest;
1371 }
1372 }
1373 Ok(())
1374 }
1375}
1376
1377impl crate::Queue for Queue {
1378 type A = Api;
1379
1380 unsafe fn submit(
1381 &self,
1382 command_buffers: &[&CommandBuffer],
1383 surface_textures: &[&SurfaceTexture],
1384 (signal_fence, signal_value): (&Fence, crate::FenceValue),
1385 ) -> Result<(), crate::DeviceError> {
1386 let mut fence_raw = vk::Fence::null();
1387
1388 let mut wait_semaphores = SemaphoreList::new(SemaphoreListMode::Wait);
1389 let mut signal_semaphores = SemaphoreList::new(SemaphoreListMode::Signal);
1390
1391 // Double check that the same swapchain image isn't being given to us multiple times,
1392 // as that will deadlock when we try to lock them all.
1393 debug_assert!(
1394 {
1395 let mut check = HashSet::with_capacity(surface_textures.len());
1396 // We compare the Box by pointer, as Eq isn't well defined for SurfaceSemaphores.
1397 for st in surface_textures {
1398 let ptr: *const () = <*const _>::cast(&*st.metadata);
1399 check.insert(ptr as usize);
1400 }
1401 check.len() == surface_textures.len()
1402 },
1403 "More than one surface texture is being used from the same swapchain. This will cause a deadlock in release."
1404 );
1405
1406 let locked_swapchain_semaphores = surface_textures
1407 .iter()
1408 .map(|st| st.metadata.get_semaphore_guard())
1409 .collect::<Vec<_>>();
1410
1411 for mut semaphores in locked_swapchain_semaphores {
1412 semaphores.set_used_fence_value(signal_value);
1413
1414 // If we're the first submission to operate on this image, wait on
1415 // its acquire semaphore, to make sure the presentation engine is
1416 // done with it.
1417 if let Some(sem) = semaphores.get_acquire_wait_semaphore() {
1418 wait_semaphores.push_wait(sem, vk::PipelineStageFlags::TOP_OF_PIPE);
1419 }
1420
1421 // Get a semaphore to signal when we're done writing to this surface
1422 // image. Presentation of this image will wait for this.
1423 let signal_semaphore = semaphores.get_submit_signal_semaphore(&self.device)?;
1424 signal_semaphores.push_signal(signal_semaphore);
1425 }
1426
1427 let mut guard = self.signal_semaphores.lock();
1428 if !guard.is_empty() {
1429 signal_semaphores.append(&mut guard);
1430 }
1431
1432 let mut wait_guard = self.wait_semaphores.lock();
1433 if !wait_guard.is_empty() {
1434 wait_semaphores.append(&mut wait_guard);
1435 }
1436
1437 // In order for submissions to be strictly ordered, we encode a dependency between each submission
1438 // using a pair of semaphores. This adds a wait if it is needed, and signals the next semaphore.
1439 let semaphore_state = self.relay_semaphores.lock().advance(&self.device)?;
1440
1441 if let Some(sem) = semaphore_state.wait {
1442 wait_semaphores.push_wait(
1443 SemaphoreType::Binary(sem),
1444 vk::PipelineStageFlags::TOP_OF_PIPE,
1445 );
1446 }
1447
1448 signal_semaphores.push_signal(SemaphoreType::Binary(semaphore_state.signal));
1449
1450 // We need to signal our wgpu::Fence if we have one, this adds it to the signal list.
1451 signal_fence.maintain(&self.device.raw)?;
1452 // Keeping the Arc around is probably unneeded - the fence should never be signaled as it was reset,
1453 // and newer submits should not happen until this submit is done. Therefore, it should be too high
1454 // to be reset.
1455 let shared_fence;
1456 match *signal_fence {
1457 Fence::TimelineSemaphore(raw) => {
1458 signal_semaphores.push_signal(SemaphoreType::Timeline(raw, signal_value));
1459 }
1460 Fence::FencePool(ref pool) => {
1461 let FencePool {
1462 ref mut active,
1463 ref mut free,
1464 ..
1465 } = *pool.write();
1466 shared_fence = match free.pop() {
1467 Some(raw) => Arc::new(raw),
1468 None => unsafe {
1469 let fence = self
1470 .device
1471 .raw
1472 .create_fence(&vk::FenceCreateInfo::default(), None)
1473 .map_err(map_host_device_oom_err)?;
1474 Arc::new(fence)
1475 },
1476 };
1477 fence_raw = *shared_fence;
1478 active.push((signal_value, shared_fence.clone()));
1479 }
1480 }
1481
1482 let vk_cmd_buffers = command_buffers
1483 .iter()
1484 .map(|cmd| cmd.raw)
1485 .collect::<Vec<_>>();
1486
1487 let mut vk_info = vk::SubmitInfo::default().command_buffers(&vk_cmd_buffers);
1488 let mut vk_timeline_info = mem::MaybeUninit::uninit();
1489 vk_info = SemaphoreList::add_to_submit(
1490 &mut wait_semaphores,
1491 &mut signal_semaphores,
1492 vk_info,
1493 &mut vk_timeline_info,
1494 );
1495
1496 profiling::scope!("vkQueueSubmit");
1497 unsafe {
1498 self.device
1499 .raw
1500 .queue_submit(self.raw, &[vk_info], fence_raw)
1501 .map_err(map_host_device_oom_and_lost_err)?
1502 };
1503 Ok(())
1504 }
1505
1506 unsafe fn present(
1507 &self,
1508 surface: &Surface,
1509 texture: SurfaceTexture,
1510 ) -> Result<(), crate::SurfaceError> {
1511 let mut swapchain = surface.swapchain.write();
1512
1513 unsafe { swapchain.as_mut().unwrap().present(self, texture) }
1514 }
1515
1516 unsafe fn get_timestamp_period(&self) -> f32 {
1517 self.device.timestamp_period
1518 }
1519
1520 unsafe fn wait_for_idle(&self) -> Result<(), crate::DeviceError> {
1521 unsafe { self.device.raw.queue_wait_idle(self.raw) }
1522 .map_err(map_host_device_oom_and_lost_err)
1523 }
1524}
1525
1526impl Queue {
1527 pub fn raw_device(&self) -> &ash::Device {
1528 &self.device.raw
1529 }
1530
1531 pub fn add_signal_semaphore(&self, semaphore: vk::Semaphore, semaphore_value: Option<u64>) {
1532 let mut guard = self.signal_semaphores.lock();
1533 if let Some(value) = semaphore_value {
1534 guard.push_signal(SemaphoreType::Timeline(semaphore, value));
1535 } else {
1536 guard.push_signal(SemaphoreType::Binary(semaphore));
1537 }
1538 }
1539
1540 /// Remove `semaphore` from the pending signal list if it is still present.
1541 ///
1542 /// Returns `true` if the semaphore was found and removed. If the submit
1543 /// already consumed it, this is a harmless no-op that returns `false`.
1544 pub fn remove_signal_semaphore(&self, semaphore: vk::Semaphore) -> bool {
1545 self.signal_semaphores.lock().remove(semaphore)
1546 }
1547
1548 /// Stage a semaphore wait on the next [`crate::Queue::submit`] call.
1549 ///
1550 /// `semaphore_value` selects the kind of payload the wait targets:
1551 ///
1552 /// - `Some(value)` - wait until `semaphore` (a timeline semaphore) has been signalled to at least `value`.
1553 /// - `None` - wait on a binary semaphore signal.
1554 ///
1555 /// `stage` is the pipeline stage at which the wait blocks downstream
1556 /// work (e.g. `vk::PipelineStageFlags::TOP_OF_PIPE` to gate the
1557 /// entire submission, or a more specific stage when only that stage
1558 /// reads the synchronised resource).
1559 pub fn add_wait_semaphore(
1560 &self,
1561 semaphore: vk::Semaphore,
1562 semaphore_value: Option<u64>,
1563 stage: vk::PipelineStageFlags,
1564 ) {
1565 let mut guard = self.wait_semaphores.lock();
1566 if let Some(value) = semaphore_value {
1567 guard.push_wait(SemaphoreType::Timeline(semaphore, value), stage);
1568 } else {
1569 guard.push_wait(SemaphoreType::Binary(semaphore), stage);
1570 }
1571 }
1572
1573 /// Remove `semaphore` from the pending wait list if it is still present.
1574 ///
1575 /// Returns `true` if the semaphore was found and removed. If the submit
1576 /// already consumed it, this is a no-op that returns `false`.
1577 pub fn remove_wait_semaphore(&self, semaphore: vk::Semaphore) -> bool {
1578 self.wait_semaphores.lock().remove(semaphore)
1579 }
1580}
1581
1582/// Maps
1583///
1584/// - VK_ERROR_OUT_OF_HOST_MEMORY
1585/// - VK_ERROR_OUT_OF_DEVICE_MEMORY
1586fn map_host_device_oom_err(err: vk::Result) -> crate::DeviceError {
1587 match err {
1588 vk::Result::ERROR_OUT_OF_HOST_MEMORY | vk::Result::ERROR_OUT_OF_DEVICE_MEMORY => {
1589 get_oom_err(err)
1590 }
1591 e => get_unexpected_err(e),
1592 }
1593}
1594
1595/// Maps
1596///
1597/// - VK_ERROR_OUT_OF_HOST_MEMORY
1598/// - VK_ERROR_OUT_OF_DEVICE_MEMORY
1599/// - VK_ERROR_DEVICE_LOST
1600fn map_host_device_oom_and_lost_err(err: vk::Result) -> crate::DeviceError {
1601 match err {
1602 vk::Result::ERROR_DEVICE_LOST => get_lost_err(),
1603 other => map_host_device_oom_err(other),
1604 }
1605}
1606
1607/// Maps
1608///
1609/// - VK_ERROR_OUT_OF_HOST_MEMORY
1610/// - VK_ERROR_OUT_OF_DEVICE_MEMORY
1611/// - VK_ERROR_FRAGMENTATION
1612fn map_host_device_oom_and_fragmentation_err(err: vk::Result) -> crate::DeviceError {
1613 match err {
1614 vk::Result::ERROR_FRAGMENTATION => get_oom_err(err),
1615 other => map_host_device_oom_err(other),
1616 }
1617}
1618
1619/// Maps
1620///
1621/// - VK_ERROR_OUT_OF_HOST_MEMORY
1622/// - VK_ERROR_OUT_OF_DEVICE_MEMORY
1623/// - VK_ERROR_INVALID_OPAQUE_CAPTURE_ADDRESS_KHR
1624fn map_host_device_oom_and_ioca_err(err: vk::Result) -> crate::DeviceError {
1625 // We don't use VK_KHR_buffer_device_address
1626 // VK_ERROR_INVALID_OPAQUE_CAPTURE_ADDRESS_KHR
1627 map_host_device_oom_err(err)
1628}
1629
1630/// Maps
1631///
1632/// - VK_ERROR_OUT_OF_HOST_MEMORY
1633fn map_host_oom_err(err: vk::Result) -> crate::DeviceError {
1634 match err {
1635 vk::Result::ERROR_OUT_OF_HOST_MEMORY => get_oom_err(err),
1636 e => get_unexpected_err(e),
1637 }
1638}
1639
1640/// Maps
1641///
1642/// - VK_ERROR_OUT_OF_DEVICE_MEMORY
1643fn map_device_oom_err(err: vk::Result) -> crate::DeviceError {
1644 match err {
1645 vk::Result::ERROR_OUT_OF_DEVICE_MEMORY => get_oom_err(err),
1646 e => get_unexpected_err(e),
1647 }
1648}
1649
1650/// Maps
1651///
1652/// - VK_ERROR_OUT_OF_HOST_MEMORY
1653/// - VK_ERROR_INVALID_OPAQUE_CAPTURE_ADDRESS_KHR
1654fn map_host_oom_and_ioca_err(err: vk::Result) -> crate::DeviceError {
1655 // We don't use VK_KHR_buffer_device_address
1656 // VK_ERROR_INVALID_OPAQUE_CAPTURE_ADDRESS_KHR
1657 map_host_oom_err(err)
1658}
1659
1660/// Maps
1661///
1662/// - VK_ERROR_OUT_OF_HOST_MEMORY
1663/// - VK_ERROR_OUT_OF_DEVICE_MEMORY
1664/// - VK_PIPELINE_COMPILE_REQUIRED_EXT
1665/// - VK_ERROR_INVALID_SHADER_NV
1666fn map_pipeline_err(err: vk::Result) -> crate::DeviceError {
1667 // We don't use VK_EXT_pipeline_creation_cache_control
1668 // VK_PIPELINE_COMPILE_REQUIRED_EXT
1669 // We don't use VK_NV_glsl_shader
1670 // VK_ERROR_INVALID_SHADER_NV
1671 map_host_device_oom_err(err)
1672}
1673
1674/// Returns [`crate::DeviceError::Unexpected`] or panics if the `internal_error_panic`
1675/// feature flag is enabled.
1676fn get_unexpected_err(_err: vk::Result) -> crate::DeviceError {
1677 #[cfg(feature = "internal_error_panic")]
1678 panic!("Unexpected Vulkan error: {_err:?}");
1679
1680 #[allow(unreachable_code)]
1681 crate::DeviceError::Unexpected
1682}
1683
1684/// Returns [`crate::DeviceError::OutOfMemory`].
1685fn get_oom_err(_err: vk::Result) -> crate::DeviceError {
1686 crate::DeviceError::OutOfMemory
1687}
1688
1689/// Returns [`crate::DeviceError::Lost`] or panics if the `device_lost_panic`
1690/// feature flag is enabled.
1691fn get_lost_err() -> crate::DeviceError {
1692 #[cfg(feature = "device_lost_panic")]
1693 panic!("Device lost");
1694
1695 #[allow(unreachable_code)]
1696 crate::DeviceError::Lost
1697}
1698
1699#[derive(Clone, Copy, Pod, Zeroable)]
1700#[repr(C)]
1701struct RawTlasInstance {
1702 transform: [f32; 12],
1703 custom_data_and_mask: u32,
1704 shader_binding_table_record_offset_and_flags: u32,
1705 acceleration_structure_reference: u64,
1706}
1707
1708/// Arguments to the [`CreateDeviceCallback`].
1709#[derive(Debug)]
1710pub struct CreateDeviceCallbackArgs<'arg, 'pnext, 'this>
1711where
1712 'this: 'pnext,
1713{
1714 /// The extensions to enable for the device. You must not remove anything from this list,
1715 /// but you may add to it.
1716 pub extensions: &'arg mut Vec<&'static CStr>,
1717 /// The physical device features to enable. You may enable features, but must not disable any.
1718 pub device_features: &'arg mut PhysicalDeviceFeatures,
1719 /// The queue create infos for the device. You may add or modify queue create infos as needed.
1720 pub queue_create_infos: &'arg mut Vec<vk::DeviceQueueCreateInfo<'pnext>>,
1721 /// The create info for the device. You may add or modify things in the pnext chain, but
1722 /// do not turn features off. Additionally, do not add things to the list of extensions,
1723 /// or to the feature set, as all changes to that member will be overwritten.
1724 pub create_info: &'arg mut vk::DeviceCreateInfo<'pnext>,
1725 /// We need to have `'this` in the struct, so we can declare that all lifetimes coming from
1726 /// captures in the closure will live longer (and hence satisfy) `'pnext`. However, we
1727 /// don't actually directly use `'this`
1728 _phantom: PhantomData<&'this ()>,
1729}
1730
1731/// Callback to allow changing the vulkan device creation parameters.
1732///
1733/// # Safety:
1734/// - If you want to add extensions, add the to the `Vec<'static CStr>` not the create info,
1735/// as the create info value will be overwritten.
1736/// - Callback must not remove features.
1737/// - Callback must not change anything to what the instance does not support.
1738pub type CreateDeviceCallback<'this> =
1739 dyn for<'arg, 'pnext> FnOnce(CreateDeviceCallbackArgs<'arg, 'pnext, 'this>) + 'this;
1740
1741/// Arguments to the [`CreateInstanceCallback`].
1742#[expect(missing_debug_implementations, reason = "TODO?")]
1743pub struct CreateInstanceCallbackArgs<'arg, 'pnext, 'this>
1744where
1745 'this: 'pnext,
1746{
1747 /// The extensions to enable for the instance. You must not remove anything from this list,
1748 /// but you may add to it.
1749 pub extensions: &'arg mut Vec<&'static CStr>,
1750 /// The create info for the instance. You may add or modify things in the pnext chain, but
1751 /// do not turn features off. Additionally, do not add things to the list of extensions,
1752 /// all changes to that member will be overwritten.
1753 pub create_info: &'arg mut vk::InstanceCreateInfo<'pnext>,
1754 /// Vulkan entry point.
1755 pub entry: &'arg ash::Entry,
1756 /// We need to have `'this` in the struct, so we can declare that all lifetimes coming from
1757 /// captures in the closure will live longer (and hence satisfy) `'pnext`. However, we
1758 /// don't actually directly use `'this`
1759 _phantom: PhantomData<&'this ()>,
1760}
1761
1762/// Callback to allow changing the vulkan instance creation parameters.
1763///
1764/// # Safety:
1765/// - If you want to add extensions, add the to the `Vec<'static CStr>` not the create info,
1766/// as the create info value will be overwritten.
1767/// - Callback must not remove features.
1768/// - Callback must not change anything to what the instance does not support.
1769pub type CreateInstanceCallback<'this> =
1770 dyn for<'arg, 'pnext> FnOnce(CreateInstanceCallbackArgs<'arg, 'pnext, 'this>) + 'this;