vulkan_lib/vulkan-rs/src/device.rs

2946 lines
84 KiB
Rust

pub use super::memory::Memory;
use anyhow::Result;
use vma_rs::prelude::*;
use crate::prelude::*;
use crate::sampler_manager::SamplerManager;
use std::cmp::min;
use std::fmt;
use std::mem::{size_of, MaybeUninit};
use std::ptr;
use std::sync::{Arc, Mutex};
use std::time::Duration;
use core::ffi::c_void;
Extensions!(DeviceExtensions, {
(amd_rasterization_order, "VK_AMD_rasterization_order"),
(maintenance3, "VK_KHR_maintenance3"),
(descriptor_indexing, "VK_EXT_descriptor_indexing"),
(memory_requirements2, "VK_KHR_get_memory_requirements2"),
(swapchain, "VK_KHR_swapchain"),
(memory_budget, "VK_EXT_memory_budget"),
(memory_priority, "VK_EXT_memory_priority"),
(debug_marker, "VK_EXT_debug_marker"),
(ray_tracing_pipeline, "VK_KHR_ray_tracing_pipeline"),
(buffer_device_address, "VK_KHR_buffer_device_address"),
(deferred_host_operations, "VK_KHR_deferred_host_operations"),
(pipeline_library, "VK_KHR_pipeline_library"),
(acceleration_structure, "VK_KHR_acceleration_structure"),
(spirv_1_4, "VK_KHR_spirv_1_4"),
(shader_float_controls, "VK_KHR_shader_float_controls"),
});
pub use vulkan_sys::prelude::VkPhysicalDeviceFeatures as DeviceFeatures;
pub struct MemoryHeap {
pub usage: VkDeviceSize,
pub budget: VkDeviceSize,
}
pub struct Device {
device_functions: DeviceFunctions,
device_wsi_functions: DeviceWSIFunctions,
maintenance3_functions: Maintenance3Functions,
_acceleration_structure_functions: AccelerationStructureFunctions,
_ray_tracing_pipeline_functions: RayTracingPipelineFunctions,
deferred_operation_functions: DeferredOperationsFunctions,
enabled_extensions: DeviceExtensions,
physical_device: Arc<PhysicalDevice>,
device: VkDevice,
memory_allocator: Allocator,
sampler_manager: Mutex<SamplerManager>,
}
impl Device {
pub fn preinitialized(
device: VkDevice,
proc_addr: PFN_vkGetDeviceProcAddr,
physical_device: Arc<PhysicalDevice>,
extensions: &[VkString],
) -> Result<Arc<Device>> {
let device_functions = DeviceFunctions::load(|name| {
proc_addr(device, name.as_ptr()) as *const std::ffi::c_void
});
let device_wsi_functions = DeviceWSIFunctions::load(|name| {
proc_addr(device, name.as_ptr()) as *const std::ffi::c_void
});
let maintenance3_functions = Maintenance3Functions::load(|name| {
proc_addr(device, name.as_ptr()) as *const std::ffi::c_void
});
let ray_tracing_functions = RayTracingPipelineFunctions::load(|name| {
proc_addr(device, name.as_ptr()) as *const std::ffi::c_void
});
let acceleration_structure_functions = AccelerationStructureFunctions::load(|name| {
proc_addr(device, name.as_ptr()) as *const std::ffi::c_void
});
let deferred_operation_functions = DeferredOperationsFunctions::load(|name| {
proc_addr(device, name.as_ptr()) as *const std::ffi::c_void
});
let vma_fns = VmaVulkanFunctions {
vkGetPhysicalDeviceProperties: physical_device
.instance()
.instance_functions
.vkGetPhysicalDeviceProperties,
vkGetPhysicalDeviceMemoryProperties: physical_device
.instance()
.instance_functions
.vkGetPhysicalDeviceMemoryProperties,
vkAllocateMemory: device_functions.vkAllocateMemory,
vkFreeMemory: device_functions.vkFreeMemory,
vkMapMemory: device_functions.vkMapMemory,
vkUnmapMemory: device_functions.vkUnmapMemory,
vkFlushMappedMemoryRanges: device_functions.vkFlushMappedMemoryRanges,
vkInvalidateMappedMemoryRanges: device_functions.vkInvalidateMappedMemoryRanges,
vkBindBufferMemory: device_functions.vkBindBufferMemory,
vkBindImageMemory: device_functions.vkBindImageMemory,
vkGetBufferMemoryRequirements: device_functions.vkGetBufferMemoryRequirements,
vkGetImageMemoryRequirements: device_functions.vkGetImageMemoryRequirements,
vkCreateBuffer: device_functions.vkCreateBuffer,
vkDestroyBuffer: device_functions.vkDestroyBuffer,
vkCreateImage: device_functions.vkCreateImage,
vkDestroyImage: device_functions.vkDestroyImage,
vkCmdCopyBuffer: device_functions.vkCmdCopyBuffer,
vkGetBufferMemoryRequirements2KHR: device_functions.vkGetBufferMemoryRequirements2,
vkGetImageMemoryRequirements2KHR: device_functions.vkGetImageMemoryRequirements2,
vkBindBufferMemory2KHR: device_functions.vkBindBufferMemory2,
vkBindImageMemory2KHR: device_functions.vkBindImageMemory2,
vkGetPhysicalDeviceMemoryProperties2KHR: physical_device
.instance()
.instance_functions
.vkGetPhysicalDeviceMemoryProperties2,
};
Self::verify_vma_vk_functions(&vma_fns)?;
let memory_allocator = Allocator::builder()
.set_vulkan_functions(vma_fns)
.set_flags(VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT as u32)
.build(
physical_device.instance().vk_handle(),
device,
physical_device.vk_handle(),
physical_device.instance().api_version(),
)?;
Ok(Arc::new(Device {
memory_allocator,
device_functions,
device_wsi_functions,
maintenance3_functions,
deferred_operation_functions,
_acceleration_structure_functions: acceleration_structure_functions,
_ray_tracing_pipeline_functions: ray_tracing_functions,
enabled_extensions: DeviceExtensions::from_list(extensions),
physical_device,
device,
sampler_manager: SamplerManager::new(),
}))
}
pub fn new(
physical_device: Arc<PhysicalDevice>,
mut extensions: DeviceExtensions,
queue_infos: &[VkDeviceQueueCreateInfo],
requested_device_features: DeviceFeatures,
) -> Result<Arc<Device>> {
// buffer device address is required in the current library implementation
extensions.buffer_device_address = true;
let device_extensions = physical_device.extensions();
let mut checked_extensions = Vec::new();
let extension_list = extensions.as_list();
for extension in extension_list {
for ext_prop in device_extensions {
if *ext_prop == extension {
checked_extensions.push(extension);
break;
}
}
}
let names = VkNames::new(checked_extensions.as_slice());
println!("\nenabled device extensions ({}):", names.len());
for extension_name in names.iter() {
println!("\t- {:?}", extension_name);
}
println!();
if !requested_device_features.is_subset_of(&physical_device.features()) {
return Err(anyhow::Error::msg(
"Requested features are not supported by the device",
));
}
let mut device_ci = VkDeviceCreateInfo::new(
VK_DEVICE_CREATE_NULL_BIT,
queue_infos,
&names,
&requested_device_features,
);
let enabled_extensions = DeviceExtensions::from_list(&checked_extensions);
if let Err(missing_extensions) = extensions.check_availability(&enabled_extensions) {
for m in missing_extensions {
println!("{}", m);
}
}
if enabled_extensions.descriptor_indexing {
device_ci.chain(physical_device.descriptor_indexing_features());
}
// only required for khr ray tracing
// -----
if enabled_extensions.buffer_device_address {
device_ci.chain(physical_device.buffer_device_address_features());
}
if enabled_extensions.acceleration_structure {
device_ci.chain(physical_device.acceleration_structure_features());
}
if enabled_extensions.ray_tracing_pipeline {
device_ci.chain(physical_device.ray_tracing_features());
}
// -----
let instance = physical_device.instance();
let device = instance.create_device(physical_device.vk_handle(), &device_ci)?;
let device_functions = DeviceFunctions::new(&instance.instance_functions, device);
let device_wsi_functions = DeviceWSIFunctions::new(&instance.instance_functions, device);
let maintenance3_functions =
Maintenance3Functions::new(&instance.instance_functions, device);
let ray_tracing_functions =
RayTracingPipelineFunctions::new(&instance.instance_functions, device);
let acceleration_structure_functions =
AccelerationStructureFunctions::new(&instance.instance_functions, device);
let deferred_operation_functions =
DeferredOperationsFunctions::new(&instance.instance_functions, device);
let memory_allocator = Allocator::builder()
.set_vulkan_functions(VmaVulkanFunctions {
vkGetPhysicalDeviceProperties: physical_device
.instance()
.instance_functions
.vkGetPhysicalDeviceProperties,
vkGetPhysicalDeviceMemoryProperties: physical_device
.instance()
.instance_functions
.vkGetPhysicalDeviceMemoryProperties,
vkAllocateMemory: device_functions.vkAllocateMemory,
vkFreeMemory: device_functions.vkFreeMemory,
vkMapMemory: device_functions.vkMapMemory,
vkUnmapMemory: device_functions.vkUnmapMemory,
vkFlushMappedMemoryRanges: device_functions.vkFlushMappedMemoryRanges,
vkInvalidateMappedMemoryRanges: device_functions.vkInvalidateMappedMemoryRanges,
vkBindBufferMemory: device_functions.vkBindBufferMemory,
vkBindImageMemory: device_functions.vkBindImageMemory,
vkGetBufferMemoryRequirements: device_functions.vkGetBufferMemoryRequirements,
vkGetImageMemoryRequirements: device_functions.vkGetImageMemoryRequirements,
vkCreateBuffer: device_functions.vkCreateBuffer,
vkDestroyBuffer: device_functions.vkDestroyBuffer,
vkCreateImage: device_functions.vkCreateImage,
vkDestroyImage: device_functions.vkDestroyImage,
vkCmdCopyBuffer: device_functions.vkCmdCopyBuffer,
vkGetBufferMemoryRequirements2KHR: device_functions.vkGetBufferMemoryRequirements2,
vkGetImageMemoryRequirements2KHR: device_functions.vkGetImageMemoryRequirements2,
vkBindBufferMemory2KHR: device_functions.vkBindBufferMemory2,
vkBindImageMemory2KHR: device_functions.vkBindImageMemory2,
vkGetPhysicalDeviceMemoryProperties2KHR: physical_device
.instance()
.instance_functions
.vkGetPhysicalDeviceMemoryProperties2,
})
.set_flags(VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT as u32)
.build(
physical_device.instance().vk_handle(),
device,
physical_device.vk_handle(),
physical_device.instance().api_version(),
)?;
Ok(Arc::new(Device {
memory_allocator,
device_functions,
device_wsi_functions,
maintenance3_functions,
deferred_operation_functions,
_acceleration_structure_functions: acceleration_structure_functions,
_ray_tracing_pipeline_functions: ray_tracing_functions,
enabled_extensions,
physical_device,
device,
sampler_manager: SamplerManager::new(),
}))
}
fn verify_vma_vk_functions(fns: &VmaVulkanFunctions) -> Result<()> {
macro_rules! test_vma_fn {
($([$var:ident: $pfn:ident],)*) => {
$(
if unsafe { std::mem::transmute::<$pfn, *const c_void>(fns.$var) } == ptr::null() {
return Err(anyhow::anyhow!(format!("vma function {} is null ptr", stringify!($var))));
}
)*
};
}
test_vma_fn!(
[vkGetPhysicalDeviceProperties: PFN_vkGetPhysicalDeviceProperties],
[vkGetPhysicalDeviceMemoryProperties: PFN_vkGetPhysicalDeviceMemoryProperties],
[vkAllocateMemory: PFN_vkAllocateMemory],
[vkFreeMemory: PFN_vkFreeMemory],
[vkMapMemory: PFN_vkMapMemory],
[vkUnmapMemory: PFN_vkUnmapMemory],
[vkFlushMappedMemoryRanges: PFN_vkFlushMappedMemoryRanges],
[vkInvalidateMappedMemoryRanges: PFN_vkInvalidateMappedMemoryRanges],
[vkBindBufferMemory: PFN_vkBindBufferMemory],
[vkBindImageMemory: PFN_vkBindImageMemory],
[vkGetBufferMemoryRequirements: PFN_vkGetBufferMemoryRequirements],
[vkGetImageMemoryRequirements: PFN_vkGetImageMemoryRequirements],
[vkCreateBuffer: PFN_vkCreateBuffer],
[vkDestroyBuffer: PFN_vkDestroyBuffer],
[vkCreateImage: PFN_vkCreateImage],
[vkDestroyImage: PFN_vkDestroyImage],
[vkCmdCopyBuffer: PFN_vkCmdCopyBuffer],
[vkGetBufferMemoryRequirements2KHR: PFN_vkGetBufferMemoryRequirements2],
[vkGetImageMemoryRequirements2KHR: PFN_vkGetImageMemoryRequirements2],
[vkBindBufferMemory2KHR: PFN_vkBindBufferMemory2],
[vkBindImageMemory2KHR: PFN_vkBindImageMemory2],
[
vkGetPhysicalDeviceMemoryProperties2KHR:
PFN_vkGetPhysicalDeviceMemoryProperties2KHR
],
);
Ok(())
}
pub fn get_queue(
self: &Arc<Self>,
queue_family_index: u32,
queue_index: u32,
) -> Arc<Mutex<Queue>> {
Queue::new(
self.clone(),
self.get_device_queue(queue_family_index, queue_index),
queue_family_index,
queue_index,
)
}
pub fn physical_device(&self) -> &Arc<PhysicalDevice> {
&self.physical_device
}
pub fn wait_for_fences(
&self,
fences: &[&Arc<Fence>],
wait_all: bool,
timeout: Duration,
) -> Result<()> {
let vkfences: Vec<VkFence> = fences.iter().map(|fence| fence.vk_handle()).collect();
self.device_wait_for_fences(vkfences.as_slice(), wait_all, timeout.as_nanos() as u64)?;
Ok(())
}
pub fn enabled_extensions(&self) -> &DeviceExtensions {
&self.enabled_extensions
}
pub fn memory_budgets(&self) -> Vec<MemoryHeap> {
let phys_dev = self.physical_device();
let (budget, count) = phys_dev
.instance()
.physical_device_memory_budget(phys_dev.vk_handle());
let mut heaps = Vec::with_capacity(count as usize);
let usages = budget.heap_usages(count);
let budgets = budget.heap_budgets(count);
for i in 0..count {
heaps.push(MemoryHeap {
usage: usages[i as usize],
budget: budgets[i as usize],
})
}
heaps
}
pub fn max_supported_sample_count(
&self,
requested_sample_count: VkSampleCountFlags,
) -> VkSampleCountFlags {
let dev_props = self.physical_device.properties();
let phys_counts = min(
dev_props.limits.framebufferColorSampleCounts,
dev_props.limits.framebufferDepthSampleCounts,
);
let counts = min(phys_counts, requested_sample_count.into());
if (counts & VK_SAMPLE_COUNT_64_BIT) != 0 {
VK_SAMPLE_COUNT_64_BIT
} else if (counts & VK_SAMPLE_COUNT_32_BIT) != 0 {
VK_SAMPLE_COUNT_32_BIT
} else if (counts & VK_SAMPLE_COUNT_16_BIT) != 0 {
VK_SAMPLE_COUNT_16_BIT
} else if (counts & VK_SAMPLE_COUNT_8_BIT) != 0 {
VK_SAMPLE_COUNT_8_BIT
} else if (counts & VK_SAMPLE_COUNT_4_BIT) != 0 {
VK_SAMPLE_COUNT_4_BIT
} else if (counts & VK_SAMPLE_COUNT_2_BIT) != 0 {
VK_SAMPLE_COUNT_2_BIT
} else {
VK_SAMPLE_COUNT_1_BIT
}
}
pub fn memory_statistics(&self) -> VmaStats {
self.memory_allocator.statistics()
}
}
impl_vk_handle!(Device, VkDevice, device);
impl fmt::Debug for Device {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"Device {{ device: {:#?}, physical_device: {:#?} }}",
self.device, self.physical_device
)
}
}
impl Drop for Device {
fn drop(&mut self) {
unsafe {
self.sampler_manager
.lock()
.expect("failed to lock sampler manager at drop of device")
.clear(self);
}
self.destroy_device();
}
}
impl Device {
#[inline]
pub fn device_proc_addr(&self, name: VkString) -> PFN_vkVoidFunction {
self.physical_device
.instance()
.get_device_proc_addr(self.device, name)
}
#[inline]
fn destroy_device(&self) {
unsafe {
self.device_functions
.vkDestroyDevice(self.device, ptr::null());
}
}
#[inline]
pub fn wait_idle(&self) -> Result<()> {
let result = unsafe { self.device_functions.vkDeviceWaitIdle(self.device) };
match result {
VK_SUCCESS => Ok(()),
_ => Err(anyhow::Error::new(result)),
}
}
#[inline]
fn get_device_queue(&self, queue_family_index: u32, queue_index: u32) -> VkQueue {
unsafe {
let mut queue = MaybeUninit::uninit();
self.device_functions.vkGetDeviceQueue(
self.device,
queue_family_index,
queue_index,
queue.as_mut_ptr(),
);
queue.assume_init()
}
}
#[inline]
fn device_wait_for_fences(
&self,
fences: &[VkFence],
wait_all: impl Into<VkBool32>,
timeout: u64,
) -> Result<()> {
unsafe {
let result = self.device_functions.vkWaitForFences(
self.device,
fences.len() as u32,
fences.as_ptr(),
wait_all.into(),
timeout,
);
if result == VK_SUCCESS {
Ok(())
} else {
Err(anyhow::Error::new(result))
}
}
}
#[inline]
pub fn query_pool_results<T>(
&self,
query_pool: VkQueryPool,
first_query: u32,
query_count: u32,
data: &mut T,
stride: VkDeviceSize,
flags: impl Into<VkQueryResultFlagBits>,
) -> Result<()> {
unsafe {
let result = self.device_functions.vkGetQueryPoolResults(
self.device,
query_pool,
first_query,
query_count,
size_of::<T>(),
data as *mut T as *mut c_void,
stride,
flags.into(),
);
if result == VK_SUCCESS {
Ok(())
} else {
Err(anyhow::Error::new(result))
}
}
}
#[inline]
pub fn queue_submit(
&self,
queue: VkQueue,
submits: &[VkSubmitInfo],
fence: VkFence,
) -> Result<()> {
unsafe {
let result = self.device_functions.vkQueueSubmit(
queue,
submits.len() as u32,
submits.as_ptr(),
fence,
);
if result == VK_SUCCESS {
Ok(())
} else {
Err(anyhow::Error::new(result))
}
}
}
#[inline]
pub fn queue_wait_idle(&self, queue: VkQueue) -> Result<()> {
unsafe {
let result = self.device_functions.vkQueueWaitIdle(queue);
if result == VK_SUCCESS {
Ok(())
} else {
Err(anyhow::Error::new(result))
}
}
}
#[inline]
pub fn create_buffer(&self, create_info: &VkBufferCreateInfo) -> Result<VkBuffer> {
unsafe {
let mut buffer = MaybeUninit::uninit();
let result = self.device_functions.vkCreateBuffer(
self.device,
create_info,
ptr::null(),
buffer.as_mut_ptr(),
);
if result == VK_SUCCESS {
Ok(buffer.assume_init())
} else {
Err(anyhow::Error::new(result))
}
}
}
#[inline]
pub fn destroy_buffer(&self, buffer: VkBuffer) {
unsafe {
self.device_functions
.vkDestroyBuffer(self.device, buffer, ptr::null())
};
}
#[inline]
pub fn buffer_memory_requirements(&self, buffer: VkBuffer) -> VkMemoryRequirements {
unsafe {
let mut memory_requirements = MaybeUninit::uninit();
self.device_functions.vkGetBufferMemoryRequirements(
self.device,
buffer,
memory_requirements.as_mut_ptr(),
);
memory_requirements.assume_init()
}
}
#[inline]
pub fn get_buffer_device_address(&self, buffer: VkBuffer) -> Address {
Address::from(unsafe {
self.device_functions
.vkGetBufferDeviceAddress(self.device, &VkBufferDeviceAddressInfo::new(buffer))
})
}
pub(crate) fn allocator(&self) -> &Allocator {
&self.memory_allocator
}
#[inline]
pub fn allocate_memory(&self, allocate_info: &VkMemoryAllocateInfo) -> Result<VkDeviceMemory> {
unsafe {
let mut memory = MaybeUninit::uninit();
let result = self.device_functions.vkAllocateMemory(
self.device,
allocate_info,
ptr::null(),
memory.as_mut_ptr(),
);
if result == VK_SUCCESS {
Ok(memory.assume_init())
} else {
Err(anyhow::Error::new(result))
}
}
}
#[inline]
pub fn free_memory(&self, memory: VkDeviceMemory) {
unsafe {
self.device_functions
.vkFreeMemory(self.device, memory, ptr::null())
};
}
#[inline]
pub fn unmap_memory(&self, memory: VkDeviceMemory) {
unsafe { self.device_functions.vkUnmapMemory(self.device, memory) };
}
#[inline]
pub fn bind_buffer_memory(
&self,
buffer: VkBuffer,
memory: VkDeviceMemory,
offset: VkDeviceSize,
) -> Result<()> {
unsafe {
let result =
self.device_functions
.vkBindBufferMemory(self.device, buffer, memory, offset);
if result == VK_SUCCESS {
Ok(())
} else {
Err(anyhow::Error::new(result))
}
}
}
#[inline]
pub fn create_render_pass(&self, create_info: &VkRenderPassCreateInfo) -> Result<VkRenderPass> {
unsafe {
let mut render_pass = MaybeUninit::uninit();
let result = self.device_functions.vkCreateRenderPass(
self.device,
create_info,
ptr::null(),
render_pass.as_mut_ptr(),
);
if result == VK_SUCCESS {
Ok(render_pass.assume_init())
} else {
Err(anyhow::Error::new(result))
}
}
}
#[inline]
pub fn destroy_render_pass(&self, render_pass: VkRenderPass) {
unsafe {
self.device_functions
.vkDestroyRenderPass(self.device, render_pass, ptr::null())
};
}
#[inline]
pub fn create_image(&self, create_info: &VkImageCreateInfo) -> Result<VkImage> {
unsafe {
let mut image = MaybeUninit::uninit();
let result = self.device_functions.vkCreateImage(
self.device,
create_info,
ptr::null(),
image.as_mut_ptr(),
);
if result == VK_SUCCESS {
Ok(image.assume_init())
} else {
Err(anyhow::Error::new(result))
}
}
}
#[inline]
pub fn destroy_image(&self, image: VkImage) {
unsafe {
self.device_functions
.vkDestroyImage(self.device, image, ptr::null())
};
}
#[inline]
pub fn image_subresource_layout(
&self,
image: VkImage,
subresource: &VkImageSubresource,
) -> VkSubresourceLayout {
unsafe {
let mut subresource_layout = MaybeUninit::uninit();
self.device_functions.vkGetImageSubresourceLayout(
self.device,
image,
subresource,
subresource_layout.as_mut_ptr(),
);
subresource_layout.assume_init()
}
}
#[inline]
pub fn create_image_view(&self, create_info: &VkImageViewCreateInfo) -> Result<VkImageView> {
unsafe {
let mut image_view = MaybeUninit::uninit();
let result = self.device_functions.vkCreateImageView(
self.device,
create_info,
ptr::null(),
image_view.as_mut_ptr(),
);
if result == VK_SUCCESS {
Ok(image_view.assume_init())
} else {
Err(anyhow::Error::new(result))
}
}
}
#[inline]
pub fn destroy_image_view(&self, image_view: VkImageView) {
unsafe {
self.device_functions
.vkDestroyImageView(self.device, image_view, ptr::null())
};
}
#[inline]
pub fn image_memory_requirements(&self, image: VkImage) -> VkMemoryRequirements {
unsafe {
let mut memory_requirements = MaybeUninit::uninit();
self.device_functions.vkGetImageMemoryRequirements(
self.device,
image,
memory_requirements.as_mut_ptr(),
);
memory_requirements.assume_init()
}
}
#[inline]
pub fn image_sparse_memory_requirements(
&self,
image: VkImage,
) -> Vec<VkSparseImageMemoryRequirements> {
let mut count: u32 = 0;
unsafe {
self.device_functions.vkGetImageSparseMemoryRequirements(
self.device,
image,
&mut count,
ptr::null_mut(),
)
};
let mut sparse_memory_requirements = Vec::with_capacity(count as usize);
unsafe { sparse_memory_requirements.set_len(count as usize) };
unsafe {
self.device_functions.vkGetImageSparseMemoryRequirements(
self.device,
image,
&mut count,
sparse_memory_requirements.as_mut_ptr(),
)
};
sparse_memory_requirements
}
#[inline]
pub fn bind_image_memory(
&self,
image: VkImage,
memory: VkDeviceMemory,
offset: VkDeviceSize,
) -> Result<()> {
unsafe {
let result =
self.device_functions
.vkBindImageMemory(self.device, image, memory, offset);
if result == VK_SUCCESS {
Ok(())
} else {
Err(anyhow::Error::new(result))
}
}
}
#[inline]
pub(crate) fn create_sampler_from_manager(
&self,
create_info: VkSamplerCreateInfo,
) -> Result<Arc<Sampler>> {
self.sampler_manager
.lock()
.unwrap()
.create_sampler(create_info, self)
}
#[inline]
pub fn create_sampler(&self, create_info: &VkSamplerCreateInfo) -> Result<VkSampler> {
unsafe {
let mut sampler = MaybeUninit::uninit();
let result = self.device_functions.vkCreateSampler(
self.device,
create_info,
ptr::null(),
sampler.as_mut_ptr(),
);
if result == VK_SUCCESS {
Ok(sampler.assume_init())
} else {
Err(anyhow::Error::new(result))
}
}
}
#[inline]
pub fn destroy_sampler(&self, sampler: VkSampler) {
unsafe {
self.device_functions
.vkDestroySampler(self.device, sampler, ptr::null())
};
}
#[inline]
pub fn create_buffer_view(&self, create_info: &VkBufferViewCreateInfo) -> Result<VkBufferView> {
unsafe {
let mut buffer_view = MaybeUninit::uninit();
let result = self.device_functions.vkCreateBufferView(
self.device,
create_info,
ptr::null(),
buffer_view.as_mut_ptr(),
);
if result == VK_SUCCESS {
Ok(buffer_view.assume_init())
} else {
Err(anyhow::Error::new(result))
}
}
}
#[inline]
pub fn destroy_buffer_view(&self, buffer_view: VkBufferView) {
unsafe {
self.device_functions
.vkDestroyBufferView(self.device, buffer_view, ptr::null())
};
}
#[inline]
pub fn create_fence(&self, create_info: &VkFenceCreateInfo) -> Result<VkFence> {
unsafe {
let mut fence = MaybeUninit::uninit();
let result = self.device_functions.vkCreateFence(
self.device,
create_info,
ptr::null(),
fence.as_mut_ptr(),
);
if result == VK_SUCCESS {
Ok(fence.assume_init())
} else {
Err(anyhow::Error::new(result))
}
}
}
#[inline]
pub fn destroy_fence(&self, fence: VkFence) {
unsafe {
self.device_functions
.vkDestroyFence(self.device, fence, ptr::null())
};
}
#[inline]
pub fn reset_fences(&self, fences: &[VkFence]) -> Result<()> {
unsafe {
let result = self.device_functions.vkResetFences(
self.device,
fences.len() as u32,
fences.as_ptr(),
);
if result == VK_SUCCESS {
Ok(())
} else {
Err(anyhow::Error::new(result))
}
}
}
#[inline]
pub fn create_semaphore(&self, create_info: &VkSemaphoreCreateInfo) -> Result<VkSemaphore> {
unsafe {
let mut semaphore = MaybeUninit::uninit();
let result = self.device_functions.vkCreateSemaphore(
self.device,
create_info,
ptr::null(),
semaphore.as_mut_ptr(),
);
if result == VK_SUCCESS {
Ok(semaphore.assume_init())
} else {
Err(anyhow::Error::new(result))
}
}
}
#[inline]
pub fn destroy_semaphore(&self, semaphore: VkSemaphore) {
unsafe {
self.device_functions
.vkDestroySemaphore(self.device, semaphore, ptr::null())
};
}
#[inline]
pub fn create_shader_module(
&self,
create_info: &VkShaderModuleCreateInfo,
) -> Result<VkShaderModule> {
unsafe {
let mut shader_module = MaybeUninit::uninit();
let result = self.device_functions.vkCreateShaderModule(
self.device,
create_info,
ptr::null(),
shader_module.as_mut_ptr(),
);
if result == VK_SUCCESS {
Ok(shader_module.assume_init())
} else {
Err(anyhow::Error::new(result))
}
}
}
#[inline]
pub fn destroy_shader_module(&self, shader_module: VkShaderModule) {
unsafe {
self.device_functions
.vkDestroyShaderModule(self.device, shader_module, ptr::null())
};
}
#[inline]
pub fn create_descriptor_pool(
&self,
create_info: &VkDescriptorPoolCreateInfo,
) -> Result<VkDescriptorPool> {
unsafe {
let mut descriptor_pool = MaybeUninit::uninit();
let result = self.device_functions.vkCreateDescriptorPool(
self.device,
create_info,
ptr::null(),
descriptor_pool.as_mut_ptr(),
);
if result == VK_SUCCESS {
Ok(descriptor_pool.assume_init())
} else {
Err(anyhow::Error::new(result))
}
}
}
#[inline]
pub fn destroy_descriptor_pool(&self, descriptor_pool: VkDescriptorPool) {
unsafe {
self.device_functions
.vkDestroyDescriptorPool(self.device, descriptor_pool, ptr::null())
};
}
#[inline]
pub fn reset_descriptor_pool<T>(
&self,
descriptor_pool: VkDescriptorPool,
flags: T,
) -> Result<()>
where
T: Into<VkDescriptorPoolResetFlags>,
{
unsafe {
let result = self.device_functions.vkResetDescriptorPool(
self.device,
descriptor_pool,
flags.into(),
);
if result == VK_SUCCESS {
Ok(())
} else {
Err(anyhow::Error::new(result))
}
}
}
#[inline]
pub fn create_descriptor_set_layout(
&self,
create_info: &VkDescriptorSetLayoutCreateInfo,
) -> Result<VkDescriptorSetLayout> {
unsafe {
let mut descriptor_set_layout = MaybeUninit::uninit();
let result = self.device_functions.vkCreateDescriptorSetLayout(
self.device,
create_info,
ptr::null(),
descriptor_set_layout.as_mut_ptr(),
);
if result == VK_SUCCESS {
Ok(descriptor_set_layout.assume_init())
} else {
Err(anyhow::Error::new(result))
}
}
}
#[inline]
pub fn destroy_descriptor_set_layout(&self, descriptor_set_layout: VkDescriptorSetLayout) {
unsafe {
self.device_functions.vkDestroyDescriptorSetLayout(
self.device,
descriptor_set_layout,
ptr::null(),
)
};
}
#[inline]
pub fn allocate_descriptor_sets<'a>(
&self,
allocate_info: &VkDescriptorSetAllocateInfo<'a>,
) -> Result<Vec<VkDescriptorSet>> {
unsafe {
let count = allocate_info.descriptorSetCount as usize;
let mut descriptor_sets = Vec::with_capacity(count);
descriptor_sets.set_len(count);
let result = self.device_functions.vkAllocateDescriptorSets(
self.device,
allocate_info,
descriptor_sets.as_mut_ptr(),
);
if result == VK_SUCCESS {
Ok(descriptor_sets)
} else {
Err(anyhow::Error::new(result))
}
}
}
#[inline]
pub fn free_descriptor_sets(
&self,
descriptor_pool: VkDescriptorPool,
descriptor_sets: &[VkDescriptorSet],
) -> Result<()> {
unsafe {
let result = self.device_functions.vkFreeDescriptorSets(
self.device,
descriptor_pool,
descriptor_sets.len() as u32,
descriptor_sets.as_ptr(),
);
if result == VK_SUCCESS {
Ok(())
} else {
Err(anyhow::Error::new(result))
}
}
}
#[inline]
pub fn update_descriptor_sets(
&self,
writes: &[VkWriteDescriptorSet],
copies: &[VkCopyDescriptorSet],
) {
unsafe {
self.device_functions.vkUpdateDescriptorSets(
self.device,
writes.len() as u32,
writes.as_ptr(),
copies.len() as u32,
copies.as_ptr(),
);
}
}
#[inline]
pub fn create_event(&self, create_info: &VkEventCreateInfo) -> Result<VkEvent> {
unsafe {
let mut event = MaybeUninit::uninit();
let result = self.device_functions.vkCreateEvent(
self.device,
create_info,
ptr::null(),
event.as_mut_ptr(),
);
if result == VK_SUCCESS {
Ok(event.assume_init())
} else {
Err(anyhow::Error::new(result))
}
}
}
#[inline]
pub fn destroy_event(&self, event: VkEvent) {
unsafe {
self.device_functions
.vkDestroyEvent(self.device, event, ptr::null())
};
}
#[inline]
pub fn event_status(&self, event: VkEvent) -> Result<()> {
unsafe {
let result = self.device_functions.vkGetEventStatus(self.device, event);
if result == VK_SUCCESS {
Ok(())
} else {
Err(anyhow::Error::new(result))
}
}
}
#[inline]
pub fn set_event(&self, event: VkEvent) -> Result<()> {
unsafe {
let result = self.device_functions.vkSetEvent(self.device, event);
if result == VK_SUCCESS {
Ok(())
} else {
Err(anyhow::Error::new(result))
}
}
}
#[inline]
pub fn reset_event(&self, event: VkEvent) -> Result<()> {
unsafe {
let result = self.device_functions.vkResetEvent(self.device, event);
if result == VK_SUCCESS {
Ok(())
} else {
Err(anyhow::Error::new(result))
}
}
}
#[inline]
pub fn create_command_pool(
&self,
create_info: &VkCommandPoolCreateInfo,
) -> Result<VkCommandPool> {
unsafe {
let mut command_pool = MaybeUninit::uninit();
let result = self.device_functions.vkCreateCommandPool(
self.device,
create_info,
ptr::null(),
command_pool.as_mut_ptr(),
);
if result == VK_SUCCESS {
Ok(command_pool.assume_init())
} else {
Err(anyhow::Error::new(result))
}
}
}
#[inline]
pub fn destroy_command_pool(&self, command_pool: VkCommandPool) {
unsafe {
self.device_functions
.vkDestroyCommandPool(self.device, command_pool, ptr::null())
};
}
#[inline]
pub fn reset_command_pool(
&self,
command_pool: VkCommandPool,
flags: impl Into<VkCommandPoolResetFlags>,
) -> Result<()> {
unsafe {
let result =
self.device_functions
.vkResetCommandPool(self.device, command_pool, flags.into());
if result == VK_SUCCESS {
Ok(())
} else {
Err(anyhow::Error::new(result))
}
}
}
#[inline]
pub fn trim_command_pool<T>(&self, command_pool: VkCommandPool, flags: T)
where
T: Into<VkCommandPoolTrimFlags>,
{
unsafe {
self.device_functions
.vkTrimCommandPool(self.device, command_pool, flags.into());
}
}
#[inline]
pub fn create_framebuffer(
&self,
create_info: &VkFramebufferCreateInfo,
) -> Result<VkFramebuffer> {
unsafe {
let mut framebuffer = MaybeUninit::uninit();
let result = self.device_functions.vkCreateFramebuffer(
self.device,
create_info,
ptr::null(),
framebuffer.as_mut_ptr(),
);
if result == VK_SUCCESS {
Ok(framebuffer.assume_init())
} else {
Err(anyhow::Error::new(result))
}
}
}
#[inline]
pub fn destroy_framebuffer(&self, framebuffer: VkFramebuffer) {
unsafe {
self.device_functions
.vkDestroyFramebuffer(self.device, framebuffer, ptr::null())
};
}
#[inline]
pub fn allocate_command_buffers(
&self,
allocate_info: &VkCommandBufferAllocateInfo,
) -> Result<Vec<VkCommandBuffer>> {
unsafe {
let count = allocate_info.commandBufferCount as usize;
let mut command_buffers = Vec::with_capacity(count);
command_buffers.set_len(count);
let result = self.device_functions.vkAllocateCommandBuffers(
self.device,
allocate_info,
command_buffers.as_mut_ptr(),
);
if result == VK_SUCCESS {
Ok(command_buffers)
} else {
Err(anyhow::Error::new(result))
}
}
}
#[inline]
pub fn free_command_buffers(
&self,
command_pool: VkCommandPool,
command_buffers: &[VkCommandBuffer],
) {
unsafe {
self.device_functions.vkFreeCommandBuffers(
self.device,
command_pool,
command_buffers.len() as u32,
command_buffers.as_ptr(),
)
}
}
#[inline]
pub fn create_query_pool(&self, create_info: &VkQueryPoolCreateInfo) -> Result<VkQueryPool> {
unsafe {
let mut query_pool = MaybeUninit::uninit();
let result = self.device_functions.vkCreateQueryPool(
self.device,
create_info,
ptr::null(),
query_pool.as_mut_ptr(),
);
if result == VK_SUCCESS {
Ok(query_pool.assume_init())
} else {
Err(anyhow::Error::new(result))
}
}
}
#[inline]
pub fn destroy_query_pool(&self, query_pool: VkQueryPool) {
unsafe {
self.device_functions
.vkDestroyQueryPool(self.device, query_pool, ptr::null())
};
}
#[inline]
pub fn create_pipeline_cache(
&self,
create_info: &VkPipelineCacheCreateInfo,
) -> Result<VkPipelineCache> {
unsafe {
let mut pipeline_cache = MaybeUninit::uninit();
let result = self.device_functions.vkCreatePipelineCache(
self.device,
create_info,
ptr::null(),
pipeline_cache.as_mut_ptr(),
);
if result == VK_SUCCESS {
Ok(pipeline_cache.assume_init())
} else {
Err(anyhow::Error::new(result))
}
}
}
#[inline]
pub fn destroy_pipeline_cache(&self, pipeline_cache: VkPipelineCache) {
unsafe {
self.device_functions
.vkDestroyPipelineCache(self.device, pipeline_cache, ptr::null())
};
}
#[inline]
pub fn pipeline_cache_data<T>(&self, pipeline_cache: VkPipelineCache) -> Result<T> {
let mut count = 0;
let result = unsafe {
self.device_functions.vkGetPipelineCacheData(
self.device,
pipeline_cache,
&mut count,
ptr::null_mut(),
)
};
if result != VK_SUCCESS || count != size_of::<T>() {
return Err(anyhow::Error::new(result));
}
unsafe {
let mut data = MaybeUninit::<T>::uninit();
let result = self.device_functions.vkGetPipelineCacheData(
self.device,
pipeline_cache,
&mut count,
data.as_mut_ptr() as *mut c_void,
);
if result == VK_SUCCESS {
Ok(data.assume_init())
} else {
Err(anyhow::Error::new(result))
}
}
}
#[inline]
pub fn merge_pipeline_cache(
&self,
sources: &[VkPipelineCache],
destination: VkPipelineCache,
) -> Result<()> {
unsafe {
let result = self.device_functions.vkMergePipelineCaches(
self.device,
destination,
sources.len() as u32,
sources.as_ptr(),
);
if result == VK_SUCCESS {
Ok(())
} else {
Err(anyhow::Error::new(result))
}
}
}
#[inline]
pub fn create_pipeline_layout(
&self,
create_info: &VkPipelineLayoutCreateInfo,
) -> Result<VkPipelineLayout> {
unsafe {
let mut pipeline_layout = MaybeUninit::uninit();
let result = self.device_functions.vkCreatePipelineLayout(
self.device,
create_info,
ptr::null(),
pipeline_layout.as_mut_ptr(),
);
if result == VK_SUCCESS {
Ok(pipeline_layout.assume_init())
} else {
Err(anyhow::Error::new(result))
}
}
}
#[inline]
pub fn destroy_pipeline_layout(&self, pipeline_layout: VkPipelineLayout) {
unsafe {
self.device_functions
.vkDestroyPipelineLayout(self.device, pipeline_layout, ptr::null())
};
}
#[inline]
pub fn create_graphics_pipelines(
&self,
pipeline_cache: Option<VkPipelineCache>,
create_infos: &[VkGraphicsPipelineCreateInfo],
) -> Result<Vec<VkPipeline>> {
unsafe {
let count = create_infos.len() as usize;
let mut pipelines = Vec::with_capacity(count);
pipelines.set_len(count);
let result = self.device_functions.vkCreateGraphicsPipelines(
self.device,
match pipeline_cache {
Some(cache) => cache,
None => VkPipelineCache::NULL_HANDLE,
},
create_infos.len() as u32,
create_infos.as_ptr(),
ptr::null(),
pipelines.as_mut_ptr(),
);
if result == VK_SUCCESS {
Ok(pipelines)
} else {
Err(anyhow::Error::new(result))
}
}
}
#[inline]
pub fn create_compute_pipelines(
&self,
pipeline_cache: Option<VkPipelineCache>,
create_infos: &[VkComputePipelineCreateInfo],
) -> Result<Vec<VkPipeline>> {
unsafe {
let count = create_infos.len() as usize;
let mut pipelines = Vec::with_capacity(count);
pipelines.set_len(count);
let result = self.device_functions.vkCreateComputePipelines(
self.device,
match pipeline_cache {
Some(cache) => cache,
None => VkPipelineCache::NULL_HANDLE,
},
create_infos.len() as u32,
create_infos.as_ptr(),
ptr::null(),
pipelines.as_mut_ptr(),
);
if result == VK_SUCCESS {
Ok(pipelines)
} else {
Err(anyhow::Error::new(result))
}
}
}
#[inline]
pub fn destroy_pipeline(&self, pipeline: VkPipeline) {
unsafe {
self.device_functions
.vkDestroyPipeline(self.device, pipeline, ptr::null())
};
}
#[inline]
pub fn queue_present(
&self,
queue: VkQueue,
present_info: &VkPresentInfoKHR,
) -> Result<OutOfDate<()>> {
unsafe {
let result = self
.device_wsi_functions
.vkQueuePresentKHR(queue, present_info);
if result == VK_SUCCESS {
Ok(OutOfDate::Ok(()))
} else if result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR {
Ok(OutOfDate::OutOfDate)
} else {
Err(anyhow::Error::new(result))
}
}
}
#[inline]
pub fn create_swapchain(
&self,
create_info: &VkSwapchainCreateInfoKHR,
) -> Result<VkSwapchainKHR> {
unsafe {
let mut swapchain = MaybeUninit::uninit();
let result = self.device_wsi_functions.vkCreateSwapchainKHR(
self.device,
create_info,
ptr::null(),
swapchain.as_mut_ptr(),
);
if result == VK_SUCCESS {
Ok(swapchain.assume_init())
} else {
Err(anyhow::Error::new(result))
}
}
}
#[inline]
pub fn destroy_swapchain(&self, swapchain: VkSwapchainKHR) {
unsafe {
self.device_wsi_functions
.vkDestroySwapchainKHR(self.device, swapchain, ptr::null())
};
}
#[inline]
pub fn swapchain_images(&self, swapchain: VkSwapchainKHR) -> Result<Vec<VkImage>> {
let mut count = 0;
let result = unsafe {
self.device_wsi_functions.vkGetSwapchainImagesKHR(
self.device,
swapchain,
&mut count,
ptr::null_mut(),
)
};
if result != VK_SUCCESS {
return Err(anyhow::Error::new(result));
}
let mut images = Vec::with_capacity(count as usize);
unsafe { images.set_len(count as usize) };
let result = unsafe {
self.device_wsi_functions.vkGetSwapchainImagesKHR(
self.device,
swapchain,
&mut count,
images.as_mut_ptr(),
)
};
if result == VK_SUCCESS {
Ok(images)
} else {
Err(anyhow::Error::new(result))
}
}
#[inline]
pub fn acquire_next_image(
&self,
swapchain: VkSwapchainKHR,
timeout: u64,
semaphore: Option<VkSemaphore>,
fence: Option<VkFence>,
) -> Result<OutOfDate<u32>> {
unsafe {
let mut image_index = 0;
let result = self.device_wsi_functions.vkAcquireNextImageKHR(
self.device,
swapchain,
timeout,
match semaphore {
Some(sem) => sem,
None => VkSemaphore::NULL_HANDLE,
},
match fence {
Some(fence) => fence,
None => VkFence::NULL_HANDLE,
},
&mut image_index,
);
match result {
VK_SUCCESS => Ok(OutOfDate::Ok(image_index)),
VK_ERROR_OUT_OF_DATE_KHR | VK_SUBOPTIMAL_KHR => Ok(OutOfDate::OutOfDate),
VK_TIMEOUT | VK_NOT_READY => Ok(OutOfDate::TimeOut),
_ => Err(anyhow::Error::new(result)),
}
}
}
}
// command buffer functions
impl Device {
#[inline]
pub fn begin_command_buffer(
&self,
command_buffer: VkCommandBuffer,
begin_info: &VkCommandBufferBeginInfo,
) -> Result<()> {
unsafe {
if std::mem::transmute::<PFN_vkBeginCommandBuffer, *const c_void>(
self.device_functions.vkBeginCommandBuffer,
) == ptr::null()
{
return Err(anyhow::anyhow!("vkBeginCommandBuffer is null"));
}
let result = self
.device_functions
.vkBeginCommandBuffer(command_buffer, begin_info);
if result == VK_SUCCESS {
Ok(())
} else {
Err(anyhow::Error::new(result))
}
}
}
#[inline]
pub fn end_command_buffer(&self, command_buffer: VkCommandBuffer) -> Result<()> {
unsafe {
let result = self.device_functions.vkEndCommandBuffer(command_buffer);
if result == VK_SUCCESS {
Ok(())
} else {
Err(anyhow::Error::new(result))
}
}
}
#[inline]
pub fn reset_command_buffer(
&self,
command_buffer: VkCommandBuffer,
flags: impl Into<VkCommandBufferResetFlagBits>,
) -> Result<()> {
unsafe {
let result = self
.device_functions
.vkResetCommandBuffer(command_buffer, flags.into());
if result == VK_SUCCESS {
Ok(())
} else {
Err(anyhow::Error::new(result))
}
}
}
#[inline]
pub fn cmd_bind_pipeline(
&self,
command_buffer: VkCommandBuffer,
pipeline_bind_point: VkPipelineBindPoint,
pipeline: VkPipeline,
) {
unsafe {
self.device_functions
.vkCmdBindPipeline(command_buffer, pipeline_bind_point, pipeline);
}
}
#[inline]
pub fn cmd_resolve_image(
&self,
command_buffer: VkCommandBuffer,
src_image: VkImage,
src_image_layout: VkImageLayout,
dst_image: VkImage,
dst_image_layout: VkImageLayout,
regions: &[VkImageResolve],
) {
unsafe {
self.device_functions.vkCmdResolveImage(
command_buffer,
src_image,
src_image_layout,
dst_image,
dst_image_layout,
regions.len() as u32,
regions.as_ptr(),
)
}
}
#[inline]
pub fn cmd_set_viewport(
&self,
command_buffer: VkCommandBuffer,
first: u32,
viewports: &[VkViewport],
) {
unsafe {
self.device_functions.vkCmdSetViewport(
command_buffer,
first,
viewports.len() as u32,
viewports.as_ptr(),
)
}
}
#[inline]
pub fn cmd_set_scissor(
&self,
command_buffer: VkCommandBuffer,
first: u32,
scissors: &[VkRect2D],
) {
unsafe {
self.device_functions.vkCmdSetScissor(
command_buffer,
first,
scissors.len() as u32,
scissors.as_ptr(),
)
}
}
#[inline]
pub fn cmd_set_depth_bias(
&self,
command_buffer: VkCommandBuffer,
depth_bias_constant_factor: f32,
depth_bias_clamp: f32,
depth_bias_slope_factor: f32,
) {
unsafe {
self.device_functions.vkCmdSetDepthBias(
command_buffer,
depth_bias_constant_factor,
depth_bias_clamp,
depth_bias_slope_factor,
)
}
}
#[inline]
pub fn cmd_bind_descriptor_sets(
&self,
command_buffer: VkCommandBuffer,
pipeline_bind_point: VkPipelineBindPoint,
pipeline_layout: VkPipelineLayout,
first_set: u32,
descriptor_sets: &[VkDescriptorSet],
dynamic_offsets: &[u32],
) {
unsafe {
self.device_functions.vkCmdBindDescriptorSets(
command_buffer,
pipeline_bind_point,
pipeline_layout,
first_set,
descriptor_sets.len() as u32,
descriptor_sets.as_ptr(),
dynamic_offsets.len() as u32,
dynamic_offsets.as_ptr(),
)
}
}
#[inline]
pub fn cmd_bind_index_buffer(
&self,
command_buffer: VkCommandBuffer,
buffer: VkBuffer,
offset: VkDeviceSize,
index_type: VkIndexType,
) {
unsafe {
self.device_functions
.vkCmdBindIndexBuffer(command_buffer, buffer, offset, index_type)
}
}
#[inline]
pub fn cmd_bind_vertex_buffers(
&self,
command_buffer: VkCommandBuffer,
first_binding: u32,
buffers: &[VkBuffer],
offsets: &[VkDeviceSize],
) {
// sanity check
debug_assert!(buffers.len() == offsets.len());
unsafe {
self.device_functions.vkCmdBindVertexBuffers(
command_buffer,
first_binding,
buffers.len() as u32,
buffers.as_ptr(),
offsets.as_ptr(),
)
}
}
#[inline]
pub fn cmd_draw(
&self,
command_buffer: VkCommandBuffer,
vertex_count: u32,
instance_count: u32,
first_vertex: u32,
first_instance: u32,
) {
unsafe {
self.device_functions.vkCmdDraw(
command_buffer,
vertex_count,
instance_count,
first_vertex,
first_instance,
)
}
}
#[inline]
pub fn cmd_draw_indexed(
&self,
command_buffer: VkCommandBuffer,
index_count: u32,
instance_count: u32,
first_index: u32,
vertex_offset: i32,
first_instance: u32,
) {
unsafe {
self.device_functions.vkCmdDrawIndexed(
command_buffer,
index_count,
instance_count,
first_index,
vertex_offset,
first_instance,
);
}
}
#[inline]
pub fn cmd_dispatch(&self, command_buffer: VkCommandBuffer, x: u32, y: u32, z: u32) {
unsafe { self.device_functions.vkCmdDispatch(command_buffer, x, y, z) }
}
#[inline]
pub fn cmd_begin_render_pass(
&self,
command_buffer: VkCommandBuffer,
render_pass_begin: &VkRenderPassBeginInfo,
contents: VkSubpassContents,
) {
unsafe {
self.device_functions
.vkCmdBeginRenderPass(command_buffer, render_pass_begin, contents)
}
}
#[inline]
pub fn cmd_next_subpass(&self, command_buffer: VkCommandBuffer, contents: VkSubpassContents) {
unsafe {
self.device_functions
.vkCmdNextSubpass(command_buffer, contents)
}
}
#[inline]
pub fn cmd_end_render_pass(&self, command_buffer: VkCommandBuffer) {
unsafe { self.device_functions.vkCmdEndRenderPass(command_buffer) }
}
#[inline]
pub fn cmd_execute_commands(
&self,
command_buffer: VkCommandBuffer,
command_buffers: &[VkCommandBuffer],
) {
unsafe {
self.device_functions.vkCmdExecuteCommands(
command_buffer,
command_buffers.len() as u32,
command_buffers.as_ptr(),
)
}
}
#[inline]
pub fn cmd_pipeline_barrier(
&self,
command_buffer: VkCommandBuffer,
src_stage_mask: impl Into<VkPipelineStageFlagBits>,
dst_stage_mask: impl Into<VkPipelineStageFlagBits>,
dependency_flags: impl Into<VkDependencyFlagBits>,
memory_barriers: &[VkMemoryBarrier],
buffer_memory_barriers: &[VkBufferMemoryBarrier],
image_memory_barriers: &[VkImageMemoryBarrier],
) {
unsafe {
self.device_functions.vkCmdPipelineBarrier(
command_buffer,
src_stage_mask.into(),
dst_stage_mask.into(),
dependency_flags.into(),
memory_barriers.len() as u32,
memory_barriers.as_ptr(),
buffer_memory_barriers.len() as u32,
buffer_memory_barriers.as_ptr(),
image_memory_barriers.len() as u32,
image_memory_barriers.as_ptr(),
)
}
}
#[inline]
pub fn cmd_copy_buffer(
&self,
command_buffer: VkCommandBuffer,
src_buffer: VkBuffer,
dst_buffer: VkBuffer,
regions: &[VkBufferCopy],
) {
unsafe {
self.device_functions.vkCmdCopyBuffer(
command_buffer,
src_buffer,
dst_buffer,
regions.len() as u32,
regions.as_ptr(),
)
}
}
#[inline]
pub fn cmd_copy_image(
&self,
command_buffer: VkCommandBuffer,
src_image: VkImage,
src_image_layout: VkImageLayout,
dst_image: VkImage,
dst_image_layout: VkImageLayout,
regions: &[VkImageCopy],
) {
unsafe {
self.device_functions.vkCmdCopyImage(
command_buffer,
src_image,
src_image_layout,
dst_image,
dst_image_layout,
regions.len() as u32,
regions.as_ptr(),
)
}
}
#[inline]
pub fn cmd_blit_image(
&self,
command_buffer: VkCommandBuffer,
src_image: VkImage,
src_image_layout: VkImageLayout,
dst_image: VkImage,
dst_image_layout: VkImageLayout,
regions: &[VkImageBlit],
filter: VkFilter,
) {
unsafe {
self.device_functions.vkCmdBlitImage(
command_buffer,
src_image,
src_image_layout,
dst_image,
dst_image_layout,
regions.len() as u32,
regions.as_ptr(),
filter,
)
}
}
#[inline]
pub fn cmd_copy_buffer_to_image(
&self,
command_buffer: VkCommandBuffer,
src_buffer: VkBuffer,
dst_image: VkImage,
dst_image_layout: VkImageLayout,
regions: &[VkBufferImageCopy],
) {
unsafe {
self.device_functions.vkCmdCopyBufferToImage(
command_buffer,
src_buffer,
dst_image,
dst_image_layout,
regions.len() as u32,
regions.as_ptr(),
)
}
}
#[inline]
pub fn cmd_copy_image_to_buffer(
&self,
command_buffer: VkCommandBuffer,
src_image: VkImage,
src_image_layout: VkImageLayout,
dst_buffer: VkBuffer,
regions: &[VkBufferImageCopy],
) {
unsafe {
self.device_functions.vkCmdCopyImageToBuffer(
command_buffer,
src_image,
src_image_layout,
dst_buffer,
regions.len() as u32,
regions.as_ptr(),
)
}
}
#[inline]
pub fn cmd_push_constants<T>(
&self,
command_buffer: VkCommandBuffer,
pipeline_layout: VkPipelineLayout,
stage_flags: impl Into<VkShaderStageFlagBits>,
offset: u32,
data: &T,
) {
unsafe {
self.device_functions.vkCmdPushConstants(
command_buffer,
pipeline_layout,
stage_flags.into(),
offset,
size_of::<T>() as u32,
data as *const T as *const c_void,
)
}
}
#[inline]
pub fn cmd_begin_query(
&self,
command_buffer: VkCommandBuffer,
query_pool: VkQueryPool,
query: u32,
flags: impl Into<VkQueryControlFlagBits>,
) {
unsafe {
self.device_functions
.vkCmdBeginQuery(command_buffer, query_pool, query, flags.into())
}
}
#[inline]
pub fn cmd_end_query(
&self,
command_buffer: VkCommandBuffer,
query_pool: VkQueryPool,
query: u32,
) {
unsafe {
self.device_functions
.vkCmdEndQuery(command_buffer, query_pool, query)
}
}
#[inline]
pub fn cmd_reset_query_pool(
&self,
command_buffer: VkCommandBuffer,
query_pool: VkQueryPool,
first_query: u32,
query_count: u32,
) {
unsafe {
self.device_functions.vkCmdResetQueryPool(
command_buffer,
query_pool,
first_query,
query_count,
)
}
}
#[inline]
pub fn cmd_write_timestamp(
&self,
command_buffer: VkCommandBuffer,
pipeline_stage: impl Into<VkPipelineStageFlagBits>,
query_pool: VkQueryPool,
query: u32,
) {
unsafe {
self.device_functions.vkCmdWriteTimestamp(
command_buffer,
pipeline_stage.into(),
query_pool,
query,
)
}
}
#[inline]
pub fn cmd_clear_color_image(
&self,
command_buffer: VkCommandBuffer,
image: VkImage,
image_layout: VkImageLayout,
clear_color: VkClearColorValue,
ranges: &[VkImageSubresourceRange],
) {
unsafe {
self.device_functions.vkCmdClearColorImage(
command_buffer,
image,
image_layout,
&clear_color,
ranges.len() as u32,
ranges.as_ptr(),
)
}
}
#[inline]
pub fn descriptor_set_layout_support(
&self,
create_info: &VkDescriptorSetLayoutCreateInfo,
support: &mut VkDescriptorSetLayoutSupport,
) {
unsafe {
self.maintenance3_functions.vkGetDescriptorSetLayoutSupport(
self.device,
create_info,
support,
);
}
}
}
// khr ray tracing pipeline & acceleration structure
impl Device {
#[inline]
pub fn build_acceleration_structures(
&self,
deferred_operation: Option<VkDeferredOperationKHR>,
infos: &[VkAccelerationStructureBuildGeometryInfoKHR],
range_infos: &[&VkAccelerationStructureBuildRangeInfoKHR],
) -> Result<()> {
debug_assert_eq!(
self.physical_device
.acceleration_structure_features()
.accelerationStructure,
VK_TRUE
);
let result = unsafe {
self._acceleration_structure_functions
.vkBuildAccelerationStructuresKHR(
self.device,
match deferred_operation {
Some(deferred_operation) => deferred_operation,
None => VkDeferredOperationKHR::NULL_HANDLE,
},
infos.len() as u32,
infos.as_ptr(),
range_infos.as_ptr() as *const *const _,
)
};
if result == VK_SUCCESS {
Ok(())
} else {
Err(anyhow::Error::new(result))
}
}
#[inline]
pub fn cmd_build_acceleration_structure_indirect(
&self,
command_buffer: VkCommandBuffer,
infos: &[VkAccelerationStructureBuildGeometryInfoKHR],
device_addresses: &[VkDeviceAddress],
strides: &[u32],
max_primitive_counts: &[&u32],
) {
debug_assert_eq!(
self.physical_device
.acceleration_structure_features()
.accelerationStructure,
VK_TRUE
);
let count = infos.len();
debug_assert_eq!(infos.len(), count);
debug_assert_eq!(device_addresses.len(), count);
debug_assert_eq!(strides.len(), count);
debug_assert_eq!(max_primitive_counts.len(), count);
unsafe {
self._acceleration_structure_functions
.vkCmdBuildAccelerationStructuresIndirectKHR(
command_buffer,
count as u32,
infos.as_ptr(),
device_addresses.as_ptr(),
strides.as_ptr(),
max_primitive_counts.as_ptr() as *const *const u32,
)
}
}
#[inline]
pub fn cmd_build_acceleration_structures(
&self,
command_buffer: VkCommandBuffer,
infos: &[VkAccelerationStructureBuildGeometryInfoKHR],
range_infos: &[&[VkAccelerationStructureBuildRangeInfoKHR]],
) {
debug_assert_eq!(
self.physical_device
.acceleration_structure_features()
.accelerationStructure,
VK_TRUE
);
let range_info_ptr = range_infos
.iter()
.map(|slice| slice.as_ptr())
.collect::<Vec<*const VkAccelerationStructureBuildRangeInfoKHR>>();
unsafe {
self._acceleration_structure_functions
.vkCmdBuildAccelerationStructuresKHR(
command_buffer,
infos.len() as u32,
infos.as_ptr(),
range_info_ptr.as_ptr(),
)
}
}
#[inline]
pub fn cmd_copy_acceleration_structure(
&self,
command_buffer: VkCommandBuffer,
info: &VkCopyAccelerationStructureInfoKHR,
) {
debug_assert_eq!(
self.physical_device
.acceleration_structure_features()
.accelerationStructure,
VK_TRUE
);
unsafe {
self._acceleration_structure_functions
.vkCmdCopyAccelerationStructureKHR(command_buffer, info)
}
}
#[inline]
pub fn cmd_copy_acceleration_structure_to_memory(
&self,
command_buffer: VkCommandBuffer,
info: &VkCopyAccelerationStructureToMemoryInfoKHR,
) {
debug_assert_eq!(
self.physical_device
.acceleration_structure_features()
.accelerationStructure,
VK_TRUE
);
unsafe {
self._acceleration_structure_functions
.vkCmdCopyAccelerationStructureToMemoryKHR(command_buffer, info)
}
}
#[inline]
pub fn cmd_copy_memory_to_acceleration_structure(
&self,
command_buffer: VkCommandBuffer,
info: &VkCopyMemoryToAccelerationStructureInfoKHR,
) {
debug_assert_eq!(
self.physical_device
.acceleration_structure_features()
.accelerationStructure,
VK_TRUE
);
unsafe {
self._acceleration_structure_functions
.vkCmdCopyMemoryToAccelerationStructureKHR(command_buffer, info)
}
}
#[inline]
pub fn cmd_trace_rays_indirect(
&self,
command_buffer: VkCommandBuffer,
raygen_shader_binding_table: &VkStridedDeviceAddressRegionKHR,
miss_shader_binding_table: &VkStridedDeviceAddressRegionKHR,
hit_shader_binding_table: &VkStridedDeviceAddressRegionKHR,
callable_shader_binding_table: &VkStridedDeviceAddressRegionKHR,
device_address: VkDeviceAddress,
) {
debug_assert_eq!(
self.physical_device
.ray_tracing_features()
.rayTracingPipeline,
VK_TRUE
);
unsafe {
self._ray_tracing_pipeline_functions
.vkCmdTraceRaysIndirectKHR(
command_buffer,
raygen_shader_binding_table,
miss_shader_binding_table,
hit_shader_binding_table,
callable_shader_binding_table,
device_address,
)
}
}
#[inline]
pub fn cmd_trace_rays(
&self,
command_buffer: VkCommandBuffer,
raygen_shader_binding_table: &VkStridedDeviceAddressRegionKHR,
miss_shader_binding_table: &VkStridedDeviceAddressRegionKHR,
hit_shader_binding_table: &VkStridedDeviceAddressRegionKHR,
callable_shader_binding_table: &VkStridedDeviceAddressRegionKHR,
width: u32,
height: u32,
depth: u32,
) {
debug_assert_eq!(
self.physical_device
.ray_tracing_features()
.rayTracingPipeline,
VK_TRUE
);
unsafe {
self._ray_tracing_pipeline_functions.vkCmdTraceRaysKHR(
command_buffer,
raygen_shader_binding_table,
miss_shader_binding_table,
hit_shader_binding_table,
callable_shader_binding_table,
width,
height,
depth,
)
}
}
#[inline]
pub fn cmd_write_acceleration_structure_properties(
&self,
command_buffer: VkCommandBuffer,
acceleration_structures: &[VkAccelerationStructureKHR],
query_type: VkQueryType,
query_pool: VkQueryPool,
first_query: u32,
) {
debug_assert_eq!(
self.physical_device
.acceleration_structure_features()
.accelerationStructure,
VK_TRUE
);
unsafe {
self._acceleration_structure_functions
.vkCmdWriteAccelerationStructuresPropertiesKHR(
command_buffer,
acceleration_structures.len() as u32,
acceleration_structures.as_ptr(),
query_type,
query_pool,
first_query,
)
}
}
#[inline]
pub fn create_acceleration_structure(
&self,
create_info: &VkAccelerationStructureCreateInfoKHR,
allocator: Option<&VkAllocationCallbacks>,
) -> Result<VkAccelerationStructureKHR> {
debug_assert_eq!(
self.physical_device
.acceleration_structure_features()
.accelerationStructure,
VK_TRUE
);
unsafe {
let mut handle = MaybeUninit::uninit();
let result = self
._acceleration_structure_functions
.vkCreateAccelerationStructureKHR(
self.device,
create_info,
match allocator {
Some(alloc) => alloc,
None => ptr::null(),
},
handle.as_mut_ptr(),
);
if result == VK_SUCCESS {
Ok(handle.assume_init())
} else {
Err(anyhow::Error::new(result))
}
}
}
#[inline]
pub fn create_ray_tracing_pipelines(
&self,
deferred_operation: Option<VkDeferredOperationKHR>,
pipeline_cache: Option<VkPipelineCache>,
pipeline_create_infos: &[VkRayTracingPipelineCreateInfoKHR],
allocator: Option<&VkAllocationCallbacks>,
) -> Result<Vec<VkPipeline>> {
debug_assert_eq!(
self.physical_device
.ray_tracing_features()
.rayTracingPipeline,
VK_TRUE
);
unsafe {
let count = pipeline_create_infos.len() as usize;
let mut pipelines = Vec::with_capacity(count);
pipelines.set_len(count);
let result = self
._ray_tracing_pipeline_functions
.vkCreateRayTracingPipelinesKHR(
self.device,
match deferred_operation {
Some(deferred_operation) => deferred_operation,
None => VkDeferredOperationKHR::NULL_HANDLE,
},
match pipeline_cache {
Some(cache) => cache,
None => VkPipelineCache::NULL_HANDLE,
},
pipeline_create_infos.len() as u32,
pipeline_create_infos.as_ptr(),
match allocator {
Some(alloc) => alloc,
None => ptr::null(),
},
pipelines.as_mut_ptr(),
);
if result == VK_SUCCESS {
Ok(pipelines)
} else {
Err(anyhow::Error::new(result))
}
}
}
#[inline]
pub fn copy_acceleration_structure(
&self,
deferred_operation: Option<VkDeferredOperationKHR>,
info: &VkCopyAccelerationStructureInfoKHR,
) -> Result<()> {
debug_assert_eq!(
self.physical_device
.acceleration_structure_features()
.accelerationStructure,
VK_TRUE
);
let result = unsafe {
self._acceleration_structure_functions
.vkCopyAccelerationStructureKHR(
self.device,
match deferred_operation {
Some(deferred_operation) => deferred_operation,
None => VkDeferredOperationKHR::NULL_HANDLE,
},
info,
)
};
if result == VK_SUCCESS {
Ok(())
} else {
Err(anyhow::Error::new(result))
}
}
#[inline]
pub fn copy_acceleration_structure_to_memory(
&self,
deferred_operation: Option<VkDeferredOperationKHR>,
info: &VkCopyAccelerationStructureToMemoryInfoKHR,
) -> Result<()> {
debug_assert_eq!(
self.physical_device
.acceleration_structure_features()
.accelerationStructure,
VK_TRUE
);
let result = unsafe {
self._acceleration_structure_functions
.vkCopyAccelerationStructureToMemoryKHR(
self.device,
match deferred_operation {
Some(deferred_operation) => deferred_operation,
None => VkDeferredOperationKHR::NULL_HANDLE,
},
info,
)
};
if result == VK_SUCCESS {
Ok(())
} else {
Err(anyhow::Error::new(result))
}
}
#[inline]
pub fn destroy_acceleration_structure(
&self,
acceleration_structure: VkAccelerationStructureKHR,
allocator: Option<&VkAllocationCallbacks>,
) {
debug_assert_eq!(
self.physical_device
.acceleration_structure_features()
.accelerationStructure,
VK_TRUE
);
unsafe {
self._acceleration_structure_functions
.vkDestroyAccelerationStructureKHR(
self.device,
acceleration_structure,
match allocator {
Some(alloc) => alloc,
None => ptr::null(),
},
)
}
}
#[inline]
pub fn get_acceleration_structure_device_address(
&self,
info: &VkAccelerationStructureDeviceAddressInfoKHR,
) -> VkDeviceAddress {
debug_assert_eq!(
self.physical_device
.acceleration_structure_features()
.accelerationStructure,
VK_TRUE
);
unsafe {
self._acceleration_structure_functions
.vkGetAccelerationStructureDeviceAddressKHR(self.device, info)
}
}
#[inline]
pub fn get_device_acceleration_structure_compatibility(
&self,
version: &VkAccelerationStructureVersionInfoKHR,
) -> VkAccelerationStructureCompatibilityKHR {
debug_assert_eq!(
self.physical_device
.acceleration_structure_features()
.accelerationStructure,
VK_TRUE
);
unsafe {
let mut compatibility = MaybeUninit::zeroed();
self._acceleration_structure_functions
.vkGetDeviceAccelerationStructureCompatibilityKHR(
self.device,
version,
compatibility.as_mut_ptr(),
);
compatibility.assume_init()
}
}
#[inline]
pub fn get_ray_tracing_capture_replay_shader_group_handles<T>(
&self,
pipeline: VkPipeline,
first_group: u32,
group_count: u32,
data: &mut [T],
) -> Result<()> {
debug_assert_eq!(
self.physical_device
.ray_tracing_features()
.rayTracingPipeline,
VK_TRUE
);
let result = unsafe {
self._ray_tracing_pipeline_functions
.vkGetRayTracingCaptureReplayShaderGroupHandlesKHR(
self.device,
pipeline,
first_group,
group_count,
(data.len() * size_of::<T>()) as isize,
data.as_mut_ptr() as *mut c_void,
)
};
if result == VK_SUCCESS {
Ok(())
} else {
Err(anyhow::Error::new(result))
}
}
pub fn get_acceleration_structure_build_sizes(
&self,
build_type: VkAccelerationStructureBuildTypeKHR,
build_info: &VkAccelerationStructureBuildGeometryInfoKHR,
max_primitive_counts: &u32,
) -> VkAccelerationStructureBuildSizesInfoKHR {
debug_assert_eq!(
self.physical_device
.acceleration_structure_features()
.accelerationStructure,
VK_TRUE
);
unsafe {
let mut res = VkAccelerationStructureBuildSizesInfoKHR::new(0, 0, 0);
self._acceleration_structure_functions
.vkGetAccelerationStructureBuildSizesKHR(
self.device,
build_type,
build_info,
max_primitive_counts,
&mut res,
);
res
}
}
pub fn get_ray_tracing_shader_group_handles(
&self,
pipeline: VkPipeline,
first_group: u32,
group_count: u32,
shader_group_handle_size: u32,
) -> Result<Vec<u8>> {
debug_assert_eq!(
self.physical_device
.ray_tracing_features()
.rayTracingPipeline,
VK_TRUE
);
unsafe {
let mut data = vec![255; (group_count * shader_group_handle_size) as usize];
let result = self
._ray_tracing_pipeline_functions
.vkGetRayTracingShaderGroupHandlesKHR(
self.device,
pipeline,
first_group,
group_count,
data.len() as isize,
data.as_mut_ptr() as *mut c_void,
);
if result == VK_SUCCESS {
Ok(data)
} else {
Err(anyhow::Error::new(result))
}
}
}
}
// deferred operations
impl Device {
#[inline]
pub fn create_deferred_operation(
&self,
allocator: Option<&VkAllocationCallbacks>,
) -> Result<VkDeferredOperationKHR> {
unsafe {
let mut handle = MaybeUninit::uninit();
let result = self
.deferred_operation_functions
.vkCreateDeferredOperationKHR(
self.device,
match allocator {
Some(alloc) => alloc,
None => ptr::null(),
},
handle.as_mut_ptr(),
);
if result == VK_SUCCESS {
Ok(handle.assume_init())
} else {
Err(anyhow::Error::new(result))
}
}
}
#[inline]
pub fn destroy_deferred_operation(
&self,
deferred_operation: VkDeferredOperationKHR,
allocator: Option<&VkAllocationCallbacks>,
) {
unsafe {
self.deferred_operation_functions
.vkDestroyDeferredOperationKHR(
self.device,
deferred_operation,
match allocator {
Some(alloc) => alloc,
None => ptr::null(),
},
)
}
}
#[inline]
pub fn get_deferred_operation_max_concurrency(
&self,
deferred_operation: VkDeferredOperationKHR,
) -> u32 {
unsafe {
self.deferred_operation_functions
.vkGetDeferredOperationMaxConcurrencyKHR(self.device, deferred_operation)
}
}
#[inline]
pub fn get_deferred_operation_result(
&self,
deferred_operation: VkDeferredOperationKHR,
) -> VkResult {
unsafe {
self.deferred_operation_functions
.vkGetDeferredOperationResultKHR(self.device, deferred_operation)
}
}
#[inline]
pub fn deferred_operation_join(&self, deferred_operation: VkDeferredOperationKHR) -> VkResult {
unsafe {
self.deferred_operation_functions
.vkDeferredOperationJoinKHR(self.device, deferred_operation)
}
}
}