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