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 shared: Arc<DeviceShared>,
509 mem_allocator: Mutex<gpu_alloc::GpuAllocator<vk::DeviceMemory>>,
510 desc_allocator:
511 Mutex<gpu_descriptor::DescriptorAllocator<vk::DescriptorPool, vk::DescriptorSet>>,
512 valid_ash_memory_types: u32,
513 naga_options: naga::back::spv::Options<'static>,
514 #[cfg(feature = "renderdoc")]
515 render_doc: crate::auxil::renderdoc::RenderDoc,
516 counters: Arc<wgt::HalCounters>,
517}
518
519impl Drop for Device {
520 fn drop(&mut self) {
521 unsafe { self.mem_allocator.lock().cleanup(&*self.shared) };
522 unsafe { self.desc_allocator.lock().cleanup(&*self.shared) };
523 }
524}
525
526/// Semaphores for forcing queue submissions to run in order.
527///
528/// The [`wgpu_hal::Queue`] trait promises that if two calls to [`submit`] are
529/// ordered, then the first submission will finish on the GPU before the second
530/// submission begins. To get this behavior on Vulkan we need to pass semaphores
531/// to [`vkQueueSubmit`] for the commands to wait on before beginning execution,
532/// and to signal when their execution is done.
533///
534/// Normally this can be done with a single semaphore, waited on and then
535/// signalled for each submission. At any given time there's exactly one
536/// submission that would signal the semaphore, and exactly one waiting on it,
537/// as Vulkan requires.
538///
539/// However, as of Oct 2021, bug [#5508] in the Mesa ANV drivers caused them to
540/// hang if we use a single semaphore. The workaround is to alternate between
541/// two semaphores. The bug has been fixed in Mesa, but we should probably keep
542/// the workaround until, say, Oct 2026.
543///
544/// [`wgpu_hal::Queue`]: crate::Queue
545/// [`submit`]: crate::Queue::submit
546/// [`vkQueueSubmit`]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#vkQueueSubmit
547/// [#5508]: https://gitlab.freedesktop.org/mesa/mesa/-/issues/5508
548#[derive(Clone)]
549struct RelaySemaphores {
550 /// The semaphore the next submission should wait on before beginning
551 /// execution on the GPU. This is `None` for the first submission, which
552 /// should not wait on anything at all.
553 wait: Option<vk::Semaphore>,
554
555 /// The semaphore the next submission should signal when it has finished
556 /// execution on the GPU.
557 signal: vk::Semaphore,
558}
559
560impl RelaySemaphores {
561 fn new(device: &DeviceShared) -> Result<Self, crate::DeviceError> {
562 Ok(Self {
563 wait: None,
564 signal: device.new_binary_semaphore("RelaySemaphores: 1")?,
565 })
566 }
567
568 /// Advances the semaphores, returning the semaphores that should be used for a submission.
569 fn advance(&mut self, device: &DeviceShared) -> Result<Self, crate::DeviceError> {
570 let old = self.clone();
571
572 // Build the state for the next submission.
573 match self.wait {
574 None => {
575 // The `old` values describe the first submission to this queue.
576 // The second submission should wait on `old.signal`, and then
577 // signal a new semaphore which we'll create now.
578 self.wait = Some(old.signal);
579 self.signal = device.new_binary_semaphore("RelaySemaphores: 2")?;
580 }
581 Some(ref mut wait) => {
582 // What this submission signals, the next should wait.
583 mem::swap(wait, &mut self.signal);
584 }
585 };
586
587 Ok(old)
588 }
589
590 /// Destroys the semaphores.
591 unsafe fn destroy(&self, device: &ash::Device) {
592 unsafe {
593 if let Some(wait) = self.wait {
594 device.destroy_semaphore(wait, None);
595 }
596 device.destroy_semaphore(self.signal, None);
597 }
598 }
599}
600
601pub struct Queue {
602 raw: vk::Queue,
603 device: Arc<DeviceShared>,
604 family_index: u32,
605 relay_semaphores: Mutex<RelaySemaphores>,
606 signal_semaphores: Mutex<SemaphoreList>,
607}
608
609impl Queue {
610 pub fn as_raw(&self) -> vk::Queue {
611 self.raw
612 }
613}
614
615impl Drop for Queue {
616 fn drop(&mut self) {
617 unsafe { self.relay_semaphores.lock().destroy(&self.device.raw) };
618 }
619}
620#[derive(Debug)]
621enum BufferMemoryBacking {
622 Managed(gpu_alloc::MemoryBlock<vk::DeviceMemory>),
623 VulkanMemory {
624 memory: vk::DeviceMemory,
625 offset: u64,
626 size: u64,
627 },
628}
629impl BufferMemoryBacking {
630 fn memory(&self) -> &vk::DeviceMemory {
631 match self {
632 Self::Managed(m) => m.memory(),
633 Self::VulkanMemory { memory, .. } => memory,
634 }
635 }
636 fn offset(&self) -> u64 {
637 match self {
638 Self::Managed(m) => m.offset(),
639 Self::VulkanMemory { offset, .. } => *offset,
640 }
641 }
642 fn size(&self) -> u64 {
643 match self {
644 Self::Managed(m) => m.size(),
645 Self::VulkanMemory { size, .. } => *size,
646 }
647 }
648}
649#[derive(Debug)]
650pub struct Buffer {
651 raw: vk::Buffer,
652 block: Option<Mutex<BufferMemoryBacking>>,
653}
654impl Buffer {
655 /// # Safety
656 ///
657 /// - `vk_buffer`'s memory must be managed by the caller
658 /// - Externally imported buffers can't be mapped by `wgpu`
659 pub unsafe fn from_raw(vk_buffer: vk::Buffer) -> Self {
660 Self {
661 raw: vk_buffer,
662 block: None,
663 }
664 }
665 /// # Safety
666 /// - 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
667 /// - Externally imported buffers can't be mapped by `wgpu`
668 /// - `offset` and `size` must be valid with the allocation of `memory`
669 pub unsafe fn from_raw_managed(
670 vk_buffer: vk::Buffer,
671 memory: vk::DeviceMemory,
672 offset: u64,
673 size: u64,
674 ) -> Self {
675 Self {
676 raw: vk_buffer,
677 block: Some(Mutex::new(BufferMemoryBacking::VulkanMemory {
678 memory,
679 offset,
680 size,
681 })),
682 }
683 }
684}
685
686impl crate::DynBuffer for Buffer {}
687
688#[derive(Debug)]
689pub struct AccelerationStructure {
690 raw: vk::AccelerationStructureKHR,
691 buffer: vk::Buffer,
692 block: Mutex<gpu_alloc::MemoryBlock<vk::DeviceMemory>>,
693 compacted_size_query: Option<vk::QueryPool>,
694}
695
696impl crate::DynAccelerationStructure for AccelerationStructure {}
697
698#[derive(Debug)]
699pub struct Texture {
700 raw: vk::Image,
701 external_memory: Option<vk::DeviceMemory>,
702 block: Option<gpu_alloc::MemoryBlock<vk::DeviceMemory>>,
703 format: wgt::TextureFormat,
704 copy_size: crate::CopyExtent,
705 identity: ResourceIdentity<vk::Image>,
706
707 // The `drop_guard` field must be the last field of this struct so it is dropped last.
708 // Do not add new fields after it.
709 drop_guard: Option<crate::DropGuard>,
710}
711
712impl crate::DynTexture for Texture {}
713
714impl Texture {
715 /// # Safety
716 ///
717 /// - The image handle must not be manually destroyed
718 pub unsafe fn raw_handle(&self) -> vk::Image {
719 self.raw
720 }
721
722 /// # Safety
723 ///
724 /// - The external memory must not be manually freed
725 pub unsafe fn external_memory(&self) -> Option<vk::DeviceMemory> {
726 self.external_memory
727 }
728}
729
730#[derive(Debug)]
731pub struct TextureView {
732 raw_texture: vk::Image,
733 raw: vk::ImageView,
734 _layers: NonZeroU32,
735 format: wgt::TextureFormat,
736 raw_format: vk::Format,
737 base_mip_level: u32,
738 dimension: wgt::TextureViewDimension,
739 texture_identity: ResourceIdentity<vk::Image>,
740 view_identity: ResourceIdentity<vk::ImageView>,
741}
742
743impl crate::DynTextureView for TextureView {}
744
745impl TextureView {
746 /// # Safety
747 ///
748 /// - The image view handle must not be manually destroyed
749 pub unsafe fn raw_handle(&self) -> vk::ImageView {
750 self.raw
751 }
752
753 /// Returns the raw texture view, along with its identity.
754 fn identified_raw_view(&self) -> IdentifiedTextureView {
755 IdentifiedTextureView {
756 raw: self.raw,
757 identity: self.view_identity,
758 }
759 }
760}
761
762#[derive(Debug)]
763pub struct Sampler {
764 raw: vk::Sampler,
765 create_info: vk::SamplerCreateInfo<'static>,
766}
767
768impl crate::DynSampler for Sampler {}
769
770/// Information about a binding within a specific BindGroupLayout / BindGroup.
771/// This will be used to construct a [`naga::back::spv::BindingInfo`], where
772/// the descriptor set value will be taken from the index of the group.
773#[derive(Copy, Clone, Debug)]
774struct BindingInfo {
775 binding: u32,
776 binding_array_size: Option<NonZeroU32>,
777}
778
779#[derive(Debug)]
780pub struct BindGroupLayout {
781 raw: vk::DescriptorSetLayout,
782 desc_count: gpu_descriptor::DescriptorTotalCount,
783 /// Sorted list of entries.
784 entries: Box<[wgt::BindGroupLayoutEntry]>,
785 /// Map of original binding index to remapped binding index and optional
786 /// array size.
787 binding_map: Vec<(u32, BindingInfo)>,
788 contains_binding_arrays: bool,
789}
790
791impl crate::DynBindGroupLayout for BindGroupLayout {}
792
793#[derive(Debug)]
794pub struct PipelineLayout {
795 raw: vk::PipelineLayout,
796 binding_map: naga::back::spv::BindingMap,
797}
798
799impl crate::DynPipelineLayout for PipelineLayout {}
800
801#[derive(Debug)]
802pub struct BindGroup {
803 set: gpu_descriptor::DescriptorSet<vk::DescriptorSet>,
804}
805
806impl crate::DynBindGroup for BindGroup {}
807
808/// Miscellaneous allocation recycling pool for `CommandAllocator`.
809#[derive(Default)]
810struct Temp {
811 marker: Vec<u8>,
812 buffer_barriers: Vec<vk::BufferMemoryBarrier<'static>>,
813 image_barriers: Vec<vk::ImageMemoryBarrier<'static>>,
814}
815
816impl Temp {
817 fn clear(&mut self) {
818 self.marker.clear();
819 self.buffer_barriers.clear();
820 self.image_barriers.clear();
821 }
822
823 fn make_c_str(&mut self, name: &str) -> &CStr {
824 self.marker.clear();
825 self.marker.extend_from_slice(name.as_bytes());
826 self.marker.push(0);
827 unsafe { CStr::from_bytes_with_nul_unchecked(&self.marker) }
828 }
829}
830
831/// Generates unique IDs for each resource of type `T`.
832///
833/// Because vk handles are not permanently unique, this
834/// provides a way to generate unique IDs for each resource.
835struct ResourceIdentityFactory<T> {
836 #[cfg(not(target_has_atomic = "64"))]
837 next_id: Mutex<u64>,
838 #[cfg(target_has_atomic = "64")]
839 next_id: core::sync::atomic::AtomicU64,
840 _phantom: PhantomData<T>,
841}
842
843impl<T> ResourceIdentityFactory<T> {
844 fn new() -> Self {
845 Self {
846 #[cfg(not(target_has_atomic = "64"))]
847 next_id: Mutex::new(0),
848 #[cfg(target_has_atomic = "64")]
849 next_id: core::sync::atomic::AtomicU64::new(0),
850 _phantom: PhantomData,
851 }
852 }
853
854 /// Returns a new unique ID for a resource of type `T`.
855 fn next(&self) -> ResourceIdentity<T> {
856 #[cfg(not(target_has_atomic = "64"))]
857 {
858 let mut next_id = self.next_id.lock();
859 let id = *next_id;
860 *next_id += 1;
861 ResourceIdentity {
862 id,
863 _phantom: PhantomData,
864 }
865 }
866
867 #[cfg(target_has_atomic = "64")]
868 ResourceIdentity {
869 id: self
870 .next_id
871 .fetch_add(1, core::sync::atomic::Ordering::Relaxed),
872 _phantom: PhantomData,
873 }
874 }
875}
876
877/// A unique identifier for a resource of type `T`.
878///
879/// This is used as a hashable key for resources, which
880/// is permanently unique through the lifetime of the program.
881#[derive(Debug, Copy, Clone, Eq, Hash, PartialEq)]
882struct ResourceIdentity<T> {
883 id: u64,
884 _phantom: PhantomData<T>,
885}
886
887#[derive(Clone, Eq, Hash, PartialEq)]
888struct FramebufferKey {
889 raw_pass: vk::RenderPass,
890 /// Because this is used as a key in a hash map, we need to include the identity
891 /// so that this hashes differently, even if the ImageView handles are the same
892 /// between different views.
893 attachment_identities: ArrayVec<ResourceIdentity<vk::ImageView>, { MAX_TOTAL_ATTACHMENTS }>,
894 /// While this is redundant for calculating the hash, we need access to an array
895 /// of all the raw ImageViews when we are creating the actual framebuffer,
896 /// so we store this here.
897 attachment_views: ArrayVec<vk::ImageView, { MAX_TOTAL_ATTACHMENTS }>,
898 extent: wgt::Extent3d,
899}
900
901impl FramebufferKey {
902 fn push_view(&mut self, view: IdentifiedTextureView) {
903 self.attachment_identities.push(view.identity);
904 self.attachment_views.push(view.raw);
905 }
906}
907
908/// A texture view paired with its identity.
909#[derive(Copy, Clone)]
910struct IdentifiedTextureView {
911 raw: vk::ImageView,
912 identity: ResourceIdentity<vk::ImageView>,
913}
914
915#[derive(Clone, Eq, Hash, PartialEq)]
916struct TempTextureViewKey {
917 texture: vk::Image,
918 /// As this is used in a hashmap, we need to
919 /// include the identity so that this hashes differently,
920 /// even if the Image handles are the same between different images.
921 texture_identity: ResourceIdentity<vk::Image>,
922 format: vk::Format,
923 mip_level: u32,
924 depth_slice: u32,
925}
926
927pub struct CommandEncoder {
928 raw: vk::CommandPool,
929 device: Arc<DeviceShared>,
930
931 /// The current command buffer, if `self` is in the ["recording"]
932 /// state.
933 ///
934 /// ["recording"]: crate::CommandEncoder
935 ///
936 /// If non-`null`, the buffer is in the Vulkan "recording" state.
937 active: vk::CommandBuffer,
938
939 /// What kind of pass we are currently within: compute or render.
940 bind_point: vk::PipelineBindPoint,
941
942 /// Allocation recycling pool for this encoder.
943 temp: Temp,
944
945 /// A pool of available command buffers.
946 ///
947 /// These are all in the Vulkan "initial" state.
948 free: Vec<vk::CommandBuffer>,
949
950 /// A pool of discarded command buffers.
951 ///
952 /// These could be in any Vulkan state except "pending".
953 discarded: Vec<vk::CommandBuffer>,
954
955 /// If this is true, the active renderpass enabled a debug span,
956 /// and needs to be disabled on renderpass close.
957 rpass_debug_marker_active: bool,
958
959 /// If set, the end of the next render/compute pass will write a timestamp at
960 /// the given pool & location.
961 end_of_pass_timer_query: Option<(vk::QueryPool, u32)>,
962
963 framebuffers: FastHashMap<FramebufferKey, vk::Framebuffer>,
964 temp_texture_views: FastHashMap<TempTextureViewKey, IdentifiedTextureView>,
965
966 counters: Arc<wgt::HalCounters>,
967
968 current_pipeline_is_multiview: bool,
969}
970
971impl Drop for CommandEncoder {
972 fn drop(&mut self) {
973 // SAFETY:
974 //
975 // VUID-vkDestroyCommandPool-commandPool-00041: wgpu_hal requires that a
976 // `CommandBuffer` must live until its execution is complete, and that a
977 // `CommandBuffer` must not outlive the `CommandEncoder` that built it.
978 // Thus, we know that none of our `CommandBuffers` are in the "pending"
979 // state.
980 //
981 // The other VUIDs are pretty obvious.
982 unsafe {
983 // `vkDestroyCommandPool` also frees any command buffers allocated
984 // from that pool, so there's no need to explicitly call
985 // `vkFreeCommandBuffers` on `cmd_encoder`'s `free` and `discarded`
986 // fields.
987 self.device.raw.destroy_command_pool(self.raw, None);
988 }
989
990 for (_, fb) in self.framebuffers.drain() {
991 unsafe { self.device.raw.destroy_framebuffer(fb, None) };
992 }
993
994 for (_, view) in self.temp_texture_views.drain() {
995 unsafe { self.device.raw.destroy_image_view(view.raw, None) };
996 }
997
998 self.counters.command_encoders.sub(1);
999 }
1000}
1001
1002impl CommandEncoder {
1003 /// # Safety
1004 ///
1005 /// - The command buffer handle must not be manually destroyed
1006 pub unsafe fn raw_handle(&self) -> vk::CommandBuffer {
1007 self.active
1008 }
1009}
1010
1011impl fmt::Debug for CommandEncoder {
1012 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1013 f.debug_struct("CommandEncoder")
1014 .field("raw", &self.raw)
1015 .finish()
1016 }
1017}
1018
1019#[derive(Debug)]
1020pub struct CommandBuffer {
1021 raw: vk::CommandBuffer,
1022}
1023
1024impl crate::DynCommandBuffer for CommandBuffer {}
1025
1026#[derive(Debug)]
1027#[allow(clippy::large_enum_variant)]
1028pub enum ShaderModule {
1029 Raw(vk::ShaderModule),
1030 Intermediate {
1031 naga_shader: crate::NagaShader,
1032 runtime_checks: wgt::ShaderRuntimeChecks,
1033 },
1034}
1035
1036impl crate::DynShaderModule for ShaderModule {}
1037
1038#[derive(Debug)]
1039pub struct RenderPipeline {
1040 raw: vk::Pipeline,
1041 is_multiview: bool,
1042}
1043
1044impl crate::DynRenderPipeline for RenderPipeline {}
1045
1046#[derive(Debug)]
1047pub struct ComputePipeline {
1048 raw: vk::Pipeline,
1049}
1050
1051impl crate::DynComputePipeline for ComputePipeline {}
1052
1053#[derive(Debug)]
1054pub struct PipelineCache {
1055 raw: vk::PipelineCache,
1056}
1057
1058impl crate::DynPipelineCache for PipelineCache {}
1059
1060#[derive(Debug)]
1061pub struct QuerySet {
1062 raw: vk::QueryPool,
1063}
1064
1065impl crate::DynQuerySet for QuerySet {}
1066
1067/// The [`Api::Fence`] type for [`vulkan::Api`].
1068///
1069/// This is an `enum` because there are two possible implementations of
1070/// `wgpu-hal` fences on Vulkan: Vulkan fences, which work on any version of
1071/// Vulkan, and Vulkan timeline semaphores, which are easier and cheaper but
1072/// require non-1.0 features.
1073///
1074/// [`Device::create_fence`] returns a [`TimelineSemaphore`] if
1075/// [`VK_KHR_timeline_semaphore`] is available and enabled, and a [`FencePool`]
1076/// otherwise.
1077///
1078/// [`Api::Fence`]: crate::Api::Fence
1079/// [`vulkan::Api`]: Api
1080/// [`Device::create_fence`]: crate::Device::create_fence
1081/// [`TimelineSemaphore`]: Fence::TimelineSemaphore
1082/// [`VK_KHR_timeline_semaphore`]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VK_KHR_timeline_semaphore
1083/// [`FencePool`]: Fence::FencePool
1084#[derive(Debug)]
1085pub enum Fence {
1086 /// A Vulkan [timeline semaphore].
1087 ///
1088 /// These are simpler to use than Vulkan fences, since timeline semaphores
1089 /// work exactly the way [`wpgu_hal::Api::Fence`] is specified to work.
1090 ///
1091 /// [timeline semaphore]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#synchronization-semaphores
1092 /// [`wpgu_hal::Api::Fence`]: crate::Api::Fence
1093 TimelineSemaphore(vk::Semaphore),
1094
1095 /// A collection of Vulkan [fence]s, each associated with a [`FenceValue`].
1096 ///
1097 /// The effective [`FenceValue`] of this variant is the greater of
1098 /// `last_completed` and the maximum value associated with a signalled fence
1099 /// in `active`.
1100 ///
1101 /// Fences are available in all versions of Vulkan, but since they only have
1102 /// two states, "signaled" and "unsignaled", we need to use a separate fence
1103 /// for each queue submission we might want to wait for, and remember which
1104 /// [`FenceValue`] each one represents.
1105 ///
1106 /// [fence]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#synchronization-fences
1107 /// [`FenceValue`]: crate::FenceValue
1108 FencePool {
1109 last_completed: crate::FenceValue,
1110 /// The pending fence values have to be ascending.
1111 active: Vec<(crate::FenceValue, vk::Fence)>,
1112 free: Vec<vk::Fence>,
1113 },
1114}
1115
1116impl crate::DynFence for Fence {}
1117
1118impl Fence {
1119 /// Return the highest [`FenceValue`] among the signalled fences in `active`.
1120 ///
1121 /// As an optimization, assume that we already know that the fence has
1122 /// reached `last_completed`, and don't bother checking fences whose values
1123 /// are less than that: those fences remain in the `active` array only
1124 /// because we haven't called `maintain` yet to clean them up.
1125 ///
1126 /// [`FenceValue`]: crate::FenceValue
1127 fn check_active(
1128 device: &ash::Device,
1129 mut last_completed: crate::FenceValue,
1130 active: &[(crate::FenceValue, vk::Fence)],
1131 ) -> Result<crate::FenceValue, crate::DeviceError> {
1132 for &(value, raw) in active.iter() {
1133 unsafe {
1134 if value > last_completed
1135 && device
1136 .get_fence_status(raw)
1137 .map_err(map_host_device_oom_and_lost_err)?
1138 {
1139 last_completed = value;
1140 }
1141 }
1142 }
1143 Ok(last_completed)
1144 }
1145
1146 /// Return the highest signalled [`FenceValue`] for `self`.
1147 ///
1148 /// [`FenceValue`]: crate::FenceValue
1149 fn get_latest(
1150 &self,
1151 device: &ash::Device,
1152 extension: Option<&ExtensionFn<khr::timeline_semaphore::Device>>,
1153 ) -> Result<crate::FenceValue, crate::DeviceError> {
1154 match *self {
1155 Self::TimelineSemaphore(raw) => unsafe {
1156 Ok(match *extension.unwrap() {
1157 ExtensionFn::Extension(ref ext) => ext
1158 .get_semaphore_counter_value(raw)
1159 .map_err(map_host_device_oom_and_lost_err)?,
1160 ExtensionFn::Promoted => device
1161 .get_semaphore_counter_value(raw)
1162 .map_err(map_host_device_oom_and_lost_err)?,
1163 })
1164 },
1165 Self::FencePool {
1166 last_completed,
1167 ref active,
1168 free: _,
1169 } => Self::check_active(device, last_completed, active),
1170 }
1171 }
1172
1173 /// Trim the internal state of this [`Fence`].
1174 ///
1175 /// This function has no externally visible effect, but you should call it
1176 /// periodically to keep this fence's resource consumption under control.
1177 ///
1178 /// For fences using the [`FencePool`] implementation, this function
1179 /// recycles fences that have been signaled. If you don't call this,
1180 /// [`Queue::submit`] will just keep allocating a new Vulkan fence every
1181 /// time it's called.
1182 ///
1183 /// [`FencePool`]: Fence::FencePool
1184 /// [`Queue::submit`]: crate::Queue::submit
1185 fn maintain(&mut self, device: &ash::Device) -> Result<(), crate::DeviceError> {
1186 match *self {
1187 Self::TimelineSemaphore(_) => {}
1188 Self::FencePool {
1189 ref mut last_completed,
1190 ref mut active,
1191 ref mut free,
1192 } => {
1193 let latest = Self::check_active(device, *last_completed, active)?;
1194 let base_free = free.len();
1195 for &(value, raw) in active.iter() {
1196 if value <= latest {
1197 free.push(raw);
1198 }
1199 }
1200 if free.len() != base_free {
1201 active.retain(|&(value, _)| value > latest);
1202 unsafe { device.reset_fences(&free[base_free..]) }
1203 .map_err(map_device_oom_err)?
1204 }
1205 *last_completed = latest;
1206 }
1207 }
1208 Ok(())
1209 }
1210}
1211
1212impl crate::Queue for Queue {
1213 type A = Api;
1214
1215 unsafe fn submit(
1216 &self,
1217 command_buffers: &[&CommandBuffer],
1218 surface_textures: &[&SurfaceTexture],
1219 (signal_fence, signal_value): (&mut Fence, crate::FenceValue),
1220 ) -> Result<(), crate::DeviceError> {
1221 let mut fence_raw = vk::Fence::null();
1222
1223 let mut wait_semaphores = SemaphoreList::new(SemaphoreListMode::Wait);
1224 let mut signal_semaphores = SemaphoreList::new(SemaphoreListMode::Signal);
1225
1226 // Double check that the same swapchain image isn't being given to us multiple times,
1227 // as that will deadlock when we try to lock them all.
1228 debug_assert!(
1229 {
1230 let mut check = HashSet::with_capacity(surface_textures.len());
1231 // We compare the Box by pointer, as Eq isn't well defined for SurfaceSemaphores.
1232 for st in surface_textures {
1233 let ptr: *const () = <*const _>::cast(&*st.metadata);
1234 check.insert(ptr as usize);
1235 }
1236 check.len() == surface_textures.len()
1237 },
1238 "More than one surface texture is being used from the same swapchain. This will cause a deadlock in release."
1239 );
1240
1241 let locked_swapchain_semaphores = surface_textures
1242 .iter()
1243 .map(|st| st.metadata.get_semaphore_guard())
1244 .collect::<Vec<_>>();
1245
1246 for mut semaphores in locked_swapchain_semaphores {
1247 semaphores.set_used_fence_value(signal_value);
1248
1249 // If we're the first submission to operate on this image, wait on
1250 // its acquire semaphore, to make sure the presentation engine is
1251 // done with it.
1252 if let Some(sem) = semaphores.get_acquire_wait_semaphore() {
1253 wait_semaphores.push_wait(sem, vk::PipelineStageFlags::TOP_OF_PIPE);
1254 }
1255
1256 // Get a semaphore to signal when we're done writing to this surface
1257 // image. Presentation of this image will wait for this.
1258 let signal_semaphore = semaphores.get_submit_signal_semaphore(&self.device)?;
1259 signal_semaphores.push_signal(signal_semaphore);
1260 }
1261
1262 let mut guard = self.signal_semaphores.lock();
1263 if !guard.is_empty() {
1264 signal_semaphores.append(&mut guard);
1265 }
1266
1267 // In order for submissions to be strictly ordered, we encode a dependency between each submission
1268 // using a pair of semaphores. This adds a wait if it is needed, and signals the next semaphore.
1269 let semaphore_state = self.relay_semaphores.lock().advance(&self.device)?;
1270
1271 if let Some(sem) = semaphore_state.wait {
1272 wait_semaphores.push_wait(
1273 SemaphoreType::Binary(sem),
1274 vk::PipelineStageFlags::TOP_OF_PIPE,
1275 );
1276 }
1277
1278 signal_semaphores.push_signal(SemaphoreType::Binary(semaphore_state.signal));
1279
1280 // We need to signal our wgpu::Fence if we have one, this adds it to the signal list.
1281 signal_fence.maintain(&self.device.raw)?;
1282 match *signal_fence {
1283 Fence::TimelineSemaphore(raw) => {
1284 signal_semaphores.push_signal(SemaphoreType::Timeline(raw, signal_value));
1285 }
1286 Fence::FencePool {
1287 ref mut active,
1288 ref mut free,
1289 ..
1290 } => {
1291 fence_raw = match free.pop() {
1292 Some(raw) => raw,
1293 None => unsafe {
1294 self.device
1295 .raw
1296 .create_fence(&vk::FenceCreateInfo::default(), None)
1297 .map_err(map_host_device_oom_err)?
1298 },
1299 };
1300 active.push((signal_value, fence_raw));
1301 }
1302 }
1303
1304 let vk_cmd_buffers = command_buffers
1305 .iter()
1306 .map(|cmd| cmd.raw)
1307 .collect::<Vec<_>>();
1308
1309 let mut vk_info = vk::SubmitInfo::default().command_buffers(&vk_cmd_buffers);
1310 let mut vk_timeline_info = mem::MaybeUninit::uninit();
1311 vk_info = SemaphoreList::add_to_submit(
1312 &mut wait_semaphores,
1313 &mut signal_semaphores,
1314 vk_info,
1315 &mut vk_timeline_info,
1316 );
1317
1318 profiling::scope!("vkQueueSubmit");
1319 unsafe {
1320 self.device
1321 .raw
1322 .queue_submit(self.raw, &[vk_info], fence_raw)
1323 .map_err(map_host_device_oom_and_lost_err)?
1324 };
1325 Ok(())
1326 }
1327
1328 unsafe fn present(
1329 &self,
1330 surface: &Surface,
1331 texture: SurfaceTexture,
1332 ) -> Result<(), crate::SurfaceError> {
1333 let mut swapchain = surface.swapchain.write();
1334
1335 unsafe { swapchain.as_mut().unwrap().present(self, texture) }
1336 }
1337
1338 unsafe fn get_timestamp_period(&self) -> f32 {
1339 self.device.timestamp_period
1340 }
1341}
1342
1343impl Queue {
1344 pub fn raw_device(&self) -> &ash::Device {
1345 &self.device.raw
1346 }
1347
1348 pub fn add_signal_semaphore(&self, semaphore: vk::Semaphore, semaphore_value: Option<u64>) {
1349 let mut guard = self.signal_semaphores.lock();
1350 if let Some(value) = semaphore_value {
1351 guard.push_signal(SemaphoreType::Timeline(semaphore, value));
1352 } else {
1353 guard.push_signal(SemaphoreType::Binary(semaphore));
1354 }
1355 }
1356}
1357
1358/// Maps
1359///
1360/// - VK_ERROR_OUT_OF_HOST_MEMORY
1361/// - VK_ERROR_OUT_OF_DEVICE_MEMORY
1362fn map_host_device_oom_err(err: vk::Result) -> crate::DeviceError {
1363 match err {
1364 vk::Result::ERROR_OUT_OF_HOST_MEMORY | vk::Result::ERROR_OUT_OF_DEVICE_MEMORY => {
1365 get_oom_err(err)
1366 }
1367 e => get_unexpected_err(e),
1368 }
1369}
1370
1371/// Maps
1372///
1373/// - VK_ERROR_OUT_OF_HOST_MEMORY
1374/// - VK_ERROR_OUT_OF_DEVICE_MEMORY
1375/// - VK_ERROR_DEVICE_LOST
1376fn map_host_device_oom_and_lost_err(err: vk::Result) -> crate::DeviceError {
1377 match err {
1378 vk::Result::ERROR_DEVICE_LOST => get_lost_err(),
1379 other => map_host_device_oom_err(other),
1380 }
1381}
1382
1383/// Maps
1384///
1385/// - VK_ERROR_OUT_OF_HOST_MEMORY
1386/// - VK_ERROR_OUT_OF_DEVICE_MEMORY
1387/// - VK_ERROR_INVALID_OPAQUE_CAPTURE_ADDRESS_KHR
1388fn map_host_device_oom_and_ioca_err(err: vk::Result) -> crate::DeviceError {
1389 // We don't use VK_KHR_buffer_device_address
1390 // VK_ERROR_INVALID_OPAQUE_CAPTURE_ADDRESS_KHR
1391 map_host_device_oom_err(err)
1392}
1393
1394/// Maps
1395///
1396/// - VK_ERROR_OUT_OF_HOST_MEMORY
1397fn map_host_oom_err(err: vk::Result) -> crate::DeviceError {
1398 match err {
1399 vk::Result::ERROR_OUT_OF_HOST_MEMORY => get_oom_err(err),
1400 e => get_unexpected_err(e),
1401 }
1402}
1403
1404/// Maps
1405///
1406/// - VK_ERROR_OUT_OF_DEVICE_MEMORY
1407fn map_device_oom_err(err: vk::Result) -> crate::DeviceError {
1408 match err {
1409 vk::Result::ERROR_OUT_OF_DEVICE_MEMORY => get_oom_err(err),
1410 e => get_unexpected_err(e),
1411 }
1412}
1413
1414/// Maps
1415///
1416/// - VK_ERROR_OUT_OF_HOST_MEMORY
1417/// - VK_ERROR_INVALID_OPAQUE_CAPTURE_ADDRESS_KHR
1418fn map_host_oom_and_ioca_err(err: vk::Result) -> crate::DeviceError {
1419 // We don't use VK_KHR_buffer_device_address
1420 // VK_ERROR_INVALID_OPAQUE_CAPTURE_ADDRESS_KHR
1421 map_host_oom_err(err)
1422}
1423
1424/// Maps
1425///
1426/// - VK_ERROR_OUT_OF_HOST_MEMORY
1427/// - VK_ERROR_OUT_OF_DEVICE_MEMORY
1428/// - VK_PIPELINE_COMPILE_REQUIRED_EXT
1429/// - VK_ERROR_INVALID_SHADER_NV
1430fn map_pipeline_err(err: vk::Result) -> crate::DeviceError {
1431 // We don't use VK_EXT_pipeline_creation_cache_control
1432 // VK_PIPELINE_COMPILE_REQUIRED_EXT
1433 // We don't use VK_NV_glsl_shader
1434 // VK_ERROR_INVALID_SHADER_NV
1435 map_host_device_oom_err(err)
1436}
1437
1438/// Returns [`crate::DeviceError::Unexpected`] or panics if the `internal_error_panic`
1439/// feature flag is enabled.
1440fn get_unexpected_err(_err: vk::Result) -> crate::DeviceError {
1441 #[cfg(feature = "internal_error_panic")]
1442 panic!("Unexpected Vulkan error: {_err:?}");
1443
1444 #[allow(unreachable_code)]
1445 crate::DeviceError::Unexpected
1446}
1447
1448/// Returns [`crate::DeviceError::OutOfMemory`].
1449fn get_oom_err(_err: vk::Result) -> crate::DeviceError {
1450 crate::DeviceError::OutOfMemory
1451}
1452
1453/// Returns [`crate::DeviceError::Lost`] or panics if the `device_lost_panic`
1454/// feature flag is enabled.
1455fn get_lost_err() -> crate::DeviceError {
1456 #[cfg(feature = "device_lost_panic")]
1457 panic!("Device lost");
1458
1459 #[allow(unreachable_code)]
1460 crate::DeviceError::Lost
1461}
1462
1463#[derive(Clone, Copy, Pod, Zeroable)]
1464#[repr(C)]
1465struct RawTlasInstance {
1466 transform: [f32; 12],
1467 custom_data_and_mask: u32,
1468 shader_binding_table_record_offset_and_flags: u32,
1469 acceleration_structure_reference: u64,
1470}
1471
1472/// Arguments to the [`CreateDeviceCallback`].
1473pub struct CreateDeviceCallbackArgs<'arg, 'pnext, 'this>
1474where
1475 'this: 'pnext,
1476{
1477 /// The extensions to enable for the device. You must not remove anything from this list,
1478 /// but you may add to it.
1479 pub extensions: &'arg mut Vec<&'static CStr>,
1480 /// The physical device features to enable. You may enable features, but must not disable any.
1481 pub device_features: &'arg mut PhysicalDeviceFeatures,
1482 /// The queue create infos for the device. You may add or modify queue create infos as needed.
1483 pub queue_create_infos: &'arg mut Vec<vk::DeviceQueueCreateInfo<'pnext>>,
1484 /// The create info for the device. You may add or modify things in the pnext chain, but
1485 /// do not turn features off. Additionally, do not add things to the list of extensions,
1486 /// or to the feature set, as all changes to that member will be overwritten.
1487 pub create_info: &'arg mut vk::DeviceCreateInfo<'pnext>,
1488 /// We need to have `'this` in the struct, so we can declare that all lifetimes coming from
1489 /// captures in the closure will live longer (and hence satisfy) `'pnext`. However, we
1490 /// don't actually directly use `'this`
1491 _phantom: PhantomData<&'this ()>,
1492}
1493
1494/// Callback to allow changing the vulkan device creation parameters.
1495///
1496/// # Safety:
1497/// - If you want to add extensions, add the to the `Vec<'static CStr>` not the create info,
1498/// as the create info value will be overwritten.
1499/// - Callback must not remove features.
1500/// - Callback must not change anything to what the instance does not support.
1501pub type CreateDeviceCallback<'this> =
1502 dyn for<'arg, 'pnext> FnOnce(CreateDeviceCallbackArgs<'arg, 'pnext, 'this>) + 'this;
1503
1504/// Arguments to the [`CreateInstanceCallback`].
1505pub struct CreateInstanceCallbackArgs<'arg, 'pnext, 'this>
1506where
1507 'this: 'pnext,
1508{
1509 /// The extensions to enable for the instance. You must not remove anything from this list,
1510 /// but you may add to it.
1511 pub extensions: &'arg mut Vec<&'static CStr>,
1512 /// The create info for the instance. You may add or modify things in the pnext chain, but
1513 /// do not turn features off. Additionally, do not add things to the list of extensions,
1514 /// all changes to that member will be overwritten.
1515 pub create_info: &'arg mut vk::InstanceCreateInfo<'pnext>,
1516 /// Vulkan entry point.
1517 pub entry: &'arg ash::Entry,
1518 /// We need to have `'this` in the struct, so we can declare that all lifetimes coming from
1519 /// captures in the closure will live longer (and hence satisfy) `'pnext`. However, we
1520 /// don't actually directly use `'this`
1521 _phantom: PhantomData<&'this ()>,
1522}
1523
1524/// Callback to allow changing the vulkan instance creation parameters.
1525///
1526/// # Safety:
1527/// - If you want to add extensions, add the to the `Vec<'static CStr>` not the create info,
1528/// as the create info value will be overwritten.
1529/// - Callback must not remove features.
1530/// - Callback must not change anything to what the instance does not support.
1531pub type CreateInstanceCallback<'this> =
1532 dyn for<'arg, 'pnext> FnOnce(CreateInstanceCallbackArgs<'arg, 'pnext, 'this>) + 'this;