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