mirror of
https://github.com/FluxaMedia/fluxa-desktop.git
synced 2026-08-09 08:27:30 +00:00
refactor: split VulkanContext's impl block across linux_vulkan/ submodules
linux_vulkan.rs's single impl VulkanContext spanned ~840 lines. Split into three sibling submodules under linux_vulkan/, same file+dir mod pattern used for mpv_render/: init.rs (new() bootstrap + Drop teardown), swapchain.rs (semaphore/command-buffer/swapchain creation + resize), render.rs (frame acquire/submit/present loop + accessors). create_semaphores, create_command_buffer, create_swapchain needed pub(super) since new() (in init.rs) and recover_after_render_error() (in render.rs) call them across the new file boundary. No logic changed — verified with cargo check.
This commit is contained in:
parent
b5085abee2
commit
f5aedac0b5
4 changed files with 880 additions and 868 deletions
|
|
@ -515,872 +515,7 @@ pub struct VulkanContext {
|
|||
|
||||
unsafe impl Send for VulkanContext {}
|
||||
|
||||
impl VulkanContext {
|
||||
pub fn new(native_surface: NativeSurface, width: i32, height: i32) -> Result<Self, String> {
|
||||
let module = dlopen_first(&["libvulkan.so.1", "libvulkan.so"])
|
||||
.ok_or("libvulkan.so.1 not found (no Vulkan-capable driver installed?)")?;
|
||||
|
||||
let get_instance_proc_addr: PfnGetInstanceProcAddr =
|
||||
unsafe { std::mem::transmute(dlsym_typed(module, "vkGetInstanceProcAddr")?) };
|
||||
let create_instance: PfnCreateInstance = unsafe {
|
||||
std::mem::transmute(get_instance_proc(
|
||||
get_instance_proc_addr,
|
||||
ptr::null_mut(),
|
||||
"vkCreateInstance",
|
||||
)?)
|
||||
};
|
||||
|
||||
let app_name = CString::new("fluxa-desktop").unwrap();
|
||||
let app_info = VkApplicationInfo {
|
||||
s_type: VK_STRUCTURE_TYPE_APPLICATION_INFO,
|
||||
p_next: ptr::null(),
|
||||
p_application_name: app_name.as_ptr(),
|
||||
application_version: 0,
|
||||
p_engine_name: app_name.as_ptr(),
|
||||
engine_version: 0,
|
||||
api_version: VK_API_VERSION_1_3,
|
||||
};
|
||||
let surface_ext_name = match native_surface {
|
||||
NativeSurface::Xlib { .. } => "VK_KHR_xlib_surface",
|
||||
NativeSurface::Wayland { .. } => "VK_KHR_wayland_surface",
|
||||
};
|
||||
let extensions = [
|
||||
CString::new("VK_KHR_surface").unwrap(),
|
||||
CString::new(surface_ext_name).unwrap(),
|
||||
CString::new("VK_EXT_swapchain_colorspace").unwrap(),
|
||||
];
|
||||
let extension_ptrs: Vec<*const i8> = extensions.iter().map(|e| e.as_ptr()).collect();
|
||||
let validation_layer = CString::new("VK_LAYER_KHRONOS_validation").unwrap();
|
||||
let want_validation = std::env::var_os("FLUXA_VULKAN_VALIDATION").is_some();
|
||||
let layer_ptrs: Vec<*const i8> = if want_validation {
|
||||
vec![validation_layer.as_ptr()]
|
||||
} else {
|
||||
vec![]
|
||||
};
|
||||
let instance_create_info = VkInstanceCreateInfo {
|
||||
s_type: VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
|
||||
p_next: ptr::null(),
|
||||
flags: 0,
|
||||
p_application_info: &app_info,
|
||||
enabled_layer_count: layer_ptrs.len() as u32,
|
||||
pp_enabled_layer_names: layer_ptrs.as_ptr(),
|
||||
enabled_extension_count: extension_ptrs.len() as u32,
|
||||
pp_enabled_extension_names: extension_ptrs.as_ptr(),
|
||||
};
|
||||
|
||||
let mut instance: VkInstance = ptr::null_mut();
|
||||
let result = unsafe { create_instance(&instance_create_info, ptr::null(), &mut instance) };
|
||||
if result != VK_SUCCESS {
|
||||
return Err(format!("vkCreateInstance failed: VkResult {result}"));
|
||||
}
|
||||
|
||||
macro_rules! iproc {
|
||||
($name:expr_2021) => {
|
||||
unsafe {
|
||||
std::mem::transmute(get_instance_proc(get_instance_proc_addr, instance, $name)?)
|
||||
}
|
||||
};
|
||||
}
|
||||
let destroy_instance: PfnDestroyInstance = iproc!("vkDestroyInstance");
|
||||
let enumerate_physical_devices: PfnEnumeratePhysicalDevices =
|
||||
iproc!("vkEnumeratePhysicalDevices");
|
||||
let get_physical_device_properties: PfnGetPhysicalDeviceProperties =
|
||||
iproc!("vkGetPhysicalDeviceProperties");
|
||||
let get_queue_family_properties: PfnGetPhysicalDeviceQueueFamilyProperties =
|
||||
iproc!("vkGetPhysicalDeviceQueueFamilyProperties");
|
||||
let get_surface_support_khr: PfnGetPhysicalDeviceSurfaceSupportKHR =
|
||||
iproc!("vkGetPhysicalDeviceSurfaceSupportKHR");
|
||||
let get_surface_capabilities_khr: PfnGetPhysicalDeviceSurfaceCapabilitiesKHR =
|
||||
iproc!("vkGetPhysicalDeviceSurfaceCapabilitiesKHR");
|
||||
let get_surface_formats_khr: PfnGetPhysicalDeviceSurfaceFormatsKHR =
|
||||
iproc!("vkGetPhysicalDeviceSurfaceFormatsKHR");
|
||||
let get_surface_present_modes_khr: PfnGetPhysicalDeviceSurfacePresentModesKHR =
|
||||
iproc!("vkGetPhysicalDeviceSurfacePresentModesKHR");
|
||||
let destroy_surface_khr: PfnDestroySurfaceKHR = iproc!("vkDestroySurfaceKHR");
|
||||
let create_device: PfnCreateDevice = iproc!("vkCreateDevice");
|
||||
let enumerate_device_extension_properties: PfnEnumerateDeviceExtensionProperties =
|
||||
iproc!("vkEnumerateDeviceExtensionProperties");
|
||||
|
||||
let mut surface: VkSurfaceKHR = 0;
|
||||
let mut owned_xlib_display: *mut c_void = ptr::null_mut();
|
||||
let result = match native_surface {
|
||||
NativeSurface::Xlib { display, window } => {
|
||||
let create_xlib_surface_khr: PfnCreateXlibSurfaceKHR =
|
||||
iproc!("vkCreateXlibSurfaceKHR");
|
||||
// The render loop runs on its own thread, but GTK's Display
|
||||
// connection is not thread-safe (XInitThreads is never called).
|
||||
// A private connection keeps Mesa's WSI traffic off GTK's.
|
||||
owned_xlib_display = unsafe { x11::xlib::XOpenDisplay(ptr::null()) as *mut c_void };
|
||||
let dpy = if owned_xlib_display.is_null() {
|
||||
display
|
||||
} else {
|
||||
owned_xlib_display
|
||||
};
|
||||
let create_info = VkXlibSurfaceCreateInfoKHR {
|
||||
s_type: VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR,
|
||||
p_next: ptr::null(),
|
||||
flags: 0,
|
||||
dpy,
|
||||
window,
|
||||
};
|
||||
unsafe {
|
||||
create_xlib_surface_khr(instance, &create_info, ptr::null(), &mut surface)
|
||||
}
|
||||
}
|
||||
NativeSurface::Wayland {
|
||||
display,
|
||||
surface: wl_surface,
|
||||
} => {
|
||||
let create_wayland_surface_khr: PfnCreateWaylandSurfaceKHR =
|
||||
iproc!("vkCreateWaylandSurfaceKHR");
|
||||
let create_info = VkWaylandSurfaceCreateInfoKHR {
|
||||
s_type: VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR,
|
||||
p_next: ptr::null(),
|
||||
flags: 0,
|
||||
display,
|
||||
surface: wl_surface,
|
||||
};
|
||||
unsafe {
|
||||
create_wayland_surface_khr(instance, &create_info, ptr::null(), &mut surface)
|
||||
}
|
||||
}
|
||||
};
|
||||
if result != VK_SUCCESS {
|
||||
unsafe { destroy_instance(instance, ptr::null()) };
|
||||
return Err(format!("vkCreate*SurfaceKHR failed: VkResult {result}"));
|
||||
}
|
||||
|
||||
let mut device_count: u32 = 0;
|
||||
unsafe { enumerate_physical_devices(instance, &mut device_count, ptr::null_mut()) };
|
||||
if device_count == 0 {
|
||||
unsafe {
|
||||
destroy_surface_khr(instance, surface, ptr::null());
|
||||
destroy_instance(instance, ptr::null());
|
||||
}
|
||||
return Err("vkEnumeratePhysicalDevices found no GPUs".into());
|
||||
}
|
||||
let mut physical_devices = vec![ptr::null_mut(); device_count as usize];
|
||||
unsafe {
|
||||
enumerate_physical_devices(instance, &mut device_count, physical_devices.as_mut_ptr())
|
||||
};
|
||||
|
||||
const VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU: u32 = 2;
|
||||
const VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU: u32 = 1;
|
||||
const VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU: u32 = 3;
|
||||
fn device_type_rank(device_type: u32) -> u32 {
|
||||
match device_type {
|
||||
VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU => 0,
|
||||
VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU => 1,
|
||||
VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU => 2,
|
||||
_ => 3,
|
||||
}
|
||||
}
|
||||
|
||||
let mut candidates: Vec<(VkPhysicalDevice, u32, u32)> = Vec::new();
|
||||
for &phys_device in &physical_devices {
|
||||
let mut family_count: u32 = 0;
|
||||
unsafe { get_queue_family_properties(phys_device, &mut family_count, ptr::null_mut()) };
|
||||
let mut families: Vec<VkQueueFamilyProperties> = (0..family_count)
|
||||
.map(|_| VkQueueFamilyProperties {
|
||||
queue_flags: 0,
|
||||
queue_count: 0,
|
||||
timestamp_valid_bits: 0,
|
||||
min_image_transfer_granularity: [0; 3],
|
||||
})
|
||||
.collect();
|
||||
unsafe {
|
||||
get_queue_family_properties(phys_device, &mut family_count, families.as_mut_ptr())
|
||||
};
|
||||
for (index, family) in families.iter().enumerate() {
|
||||
if family.queue_flags & VK_QUEUE_GRAPHICS_BIT == 0 {
|
||||
continue;
|
||||
}
|
||||
let mut present_supported: u32 = 0;
|
||||
unsafe {
|
||||
get_surface_support_khr(
|
||||
phys_device,
|
||||
index as u32,
|
||||
surface,
|
||||
&mut present_supported,
|
||||
)
|
||||
};
|
||||
if present_supported != 0 {
|
||||
let mut props = [0u8; 1024];
|
||||
unsafe {
|
||||
get_physical_device_properties(
|
||||
phys_device,
|
||||
props.as_mut_ptr() as *mut c_void,
|
||||
)
|
||||
};
|
||||
let device_type = u32::from_ne_bytes(props[16..20].try_into().unwrap());
|
||||
candidates.push((phys_device, index as u32, device_type));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
candidates.sort_by_key(|&(_, _, device_type)| device_type_rank(device_type));
|
||||
let chosen = candidates
|
||||
.first()
|
||||
.map(|&(phys_device, queue_family_index, _)| (phys_device, queue_family_index));
|
||||
let Some((phys_device, queue_family_index)) = chosen else {
|
||||
unsafe {
|
||||
destroy_surface_khr(instance, surface, ptr::null());
|
||||
destroy_instance(instance, ptr::null());
|
||||
}
|
||||
return Err(
|
||||
"no Vulkan queue family supports both graphics and presenting to this window"
|
||||
.to_string(),
|
||||
);
|
||||
};
|
||||
|
||||
let queue_priority: f32 = 1.0;
|
||||
let queue_create_info = VkDeviceQueueCreateInfo {
|
||||
s_type: VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
|
||||
p_next: ptr::null(),
|
||||
flags: 0,
|
||||
queue_family_index,
|
||||
queue_count: 1,
|
||||
p_queue_priorities: &queue_priority,
|
||||
};
|
||||
let mut ext_count: u32 = 0;
|
||||
unsafe {
|
||||
enumerate_device_extension_properties(
|
||||
phys_device,
|
||||
ptr::null(),
|
||||
&mut ext_count,
|
||||
ptr::null_mut(),
|
||||
)
|
||||
};
|
||||
let mut supported_extensions = vec![
|
||||
VkExtensionProperties {
|
||||
extension_name: [0; 256],
|
||||
spec_version: 0,
|
||||
};
|
||||
ext_count as usize
|
||||
];
|
||||
unsafe {
|
||||
enumerate_device_extension_properties(
|
||||
phys_device,
|
||||
ptr::null(),
|
||||
&mut ext_count,
|
||||
supported_extensions.as_mut_ptr(),
|
||||
)
|
||||
};
|
||||
let device_extensions: Vec<CString> = supported_extensions
|
||||
.iter()
|
||||
.filter_map(|ext| {
|
||||
let nul = ext.extension_name.iter().position(|&b| b == 0)?;
|
||||
CString::new(&ext.extension_name[..nul]).ok()
|
||||
})
|
||||
.collect();
|
||||
let device_extension_ptrs: Vec<*const i8> =
|
||||
device_extensions.iter().map(|e| e.as_ptr()).collect();
|
||||
let mut synchronization2_features = VkPhysicalDeviceSynchronization2Features {
|
||||
s_type: VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SYNCHRONIZATION_2_FEATURES,
|
||||
p_next: ptr::null_mut(),
|
||||
synchronization2: 1,
|
||||
};
|
||||
let mut timeline_semaphore_features = VkPhysicalDeviceTimelineSemaphoreFeatures {
|
||||
s_type: VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TIMELINE_SEMAPHORE_FEATURES,
|
||||
p_next: &mut synchronization2_features as *mut _ as *mut c_void,
|
||||
timeline_semaphore: 1,
|
||||
};
|
||||
let device_create_info = VkDeviceCreateInfo {
|
||||
s_type: VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
|
||||
p_next: &mut timeline_semaphore_features as *mut _ as *const c_void,
|
||||
flags: 0,
|
||||
queue_create_info_count: 1,
|
||||
p_queue_create_infos: &queue_create_info,
|
||||
enabled_layer_count: 0,
|
||||
pp_enabled_layer_names: ptr::null(),
|
||||
enabled_extension_count: device_extension_ptrs.len() as u32,
|
||||
pp_enabled_extension_names: device_extension_ptrs.as_ptr(),
|
||||
p_enabled_features: ptr::null(),
|
||||
};
|
||||
let mut device: VkDevice = ptr::null_mut();
|
||||
let result =
|
||||
unsafe { create_device(phys_device, &device_create_info, ptr::null(), &mut device) };
|
||||
if result != VK_SUCCESS {
|
||||
unsafe {
|
||||
destroy_surface_khr(instance, surface, ptr::null());
|
||||
destroy_instance(instance, ptr::null());
|
||||
}
|
||||
return Err(format!("vkCreateDevice failed: VkResult {result}"));
|
||||
}
|
||||
|
||||
macro_rules! dproc {
|
||||
($name:expr_2021) => {
|
||||
unsafe {
|
||||
std::mem::transmute(get_instance_proc(get_instance_proc_addr, instance, $name)?)
|
||||
}
|
||||
};
|
||||
}
|
||||
let destroy_device: PfnDestroyDevice = dproc!("vkDestroyDevice");
|
||||
let get_device_queue: PfnGetDeviceQueue = dproc!("vkGetDeviceQueue");
|
||||
let create_swapchain_khr: PfnCreateSwapchainKHR = dproc!("vkCreateSwapchainKHR");
|
||||
let destroy_swapchain_khr: PfnDestroySwapchainKHR = dproc!("vkDestroySwapchainKHR");
|
||||
let get_swapchain_images_khr: PfnGetSwapchainImagesKHR = dproc!("vkGetSwapchainImagesKHR");
|
||||
let acquire_next_image_khr: PfnAcquireNextImageKHR = dproc!("vkAcquireNextImageKHR");
|
||||
let queue_present_khr: PfnQueuePresentKHR = dproc!("vkQueuePresentKHR");
|
||||
let create_semaphore: PfnCreateSemaphore = dproc!("vkCreateSemaphore");
|
||||
let destroy_semaphore: PfnDestroySemaphore = dproc!("vkDestroySemaphore");
|
||||
let create_fence: PfnCreateFence = dproc!("vkCreateFence");
|
||||
let destroy_fence: PfnDestroyFence = dproc!("vkDestroyFence");
|
||||
let wait_for_fences: PfnWaitForFences = dproc!("vkWaitForFences");
|
||||
let reset_fences: PfnResetFences = dproc!("vkResetFences");
|
||||
let device_wait_idle: PfnDeviceWaitIdle = dproc!("vkDeviceWaitIdle");
|
||||
let create_command_pool: PfnCreateCommandPool = dproc!("vkCreateCommandPool");
|
||||
let destroy_command_pool: PfnDestroyCommandPool = dproc!("vkDestroyCommandPool");
|
||||
let allocate_command_buffers: PfnAllocateCommandBuffers =
|
||||
dproc!("vkAllocateCommandBuffers");
|
||||
let reset_command_buffer: PfnResetCommandBuffer = dproc!("vkResetCommandBuffer");
|
||||
let begin_command_buffer: PfnBeginCommandBuffer = dproc!("vkBeginCommandBuffer");
|
||||
let end_command_buffer: PfnEndCommandBuffer = dproc!("vkEndCommandBuffer");
|
||||
let cmd_pipeline_barrier: PfnCmdPipelineBarrier = dproc!("vkCmdPipelineBarrier");
|
||||
let queue_submit: PfnQueueSubmit = dproc!("vkQueueSubmit");
|
||||
|
||||
let mut queue: VkQueue = ptr::null_mut();
|
||||
unsafe { get_device_queue(device, queue_family_index, 0, &mut queue) };
|
||||
|
||||
let fns = VkFns {
|
||||
get_instance_proc_addr,
|
||||
destroy_instance,
|
||||
get_physical_device_surface_capabilities_khr: get_surface_capabilities_khr,
|
||||
get_physical_device_surface_formats_khr: get_surface_formats_khr,
|
||||
get_physical_device_surface_present_modes_khr: get_surface_present_modes_khr,
|
||||
destroy_surface_khr,
|
||||
destroy_device,
|
||||
create_swapchain_khr,
|
||||
destroy_swapchain_khr,
|
||||
get_swapchain_images_khr,
|
||||
acquire_next_image_khr,
|
||||
queue_present_khr,
|
||||
create_semaphore,
|
||||
destroy_semaphore,
|
||||
create_fence,
|
||||
destroy_fence,
|
||||
wait_for_fences,
|
||||
reset_fences,
|
||||
device_wait_idle,
|
||||
create_command_pool,
|
||||
destroy_command_pool,
|
||||
allocate_command_buffers,
|
||||
reset_command_buffer,
|
||||
begin_command_buffer,
|
||||
end_command_buffer,
|
||||
cmd_pipeline_barrier,
|
||||
queue_submit,
|
||||
};
|
||||
|
||||
let mut ctx = Self {
|
||||
fns,
|
||||
instance,
|
||||
phys_device,
|
||||
device,
|
||||
queue,
|
||||
queue_family_index,
|
||||
surface,
|
||||
swapchain: 0,
|
||||
images: Vec::new(),
|
||||
image_format: VK_FORMAT_B8G8R8A8_UNORM,
|
||||
image_usage: VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,
|
||||
extent: VkExtent2D {
|
||||
width: width.max(2) as u32,
|
||||
height: height.max(2) as u32,
|
||||
},
|
||||
acquire_semaphore: 0,
|
||||
render_done_semaphore: 0,
|
||||
transition_semaphore: 0,
|
||||
in_flight_fence: 0,
|
||||
command_pool: ptr::null_mut(),
|
||||
command_buffer: ptr::null_mut(),
|
||||
hdr: AtomicBool::new(false),
|
||||
enabled_device_extensions: device_extensions,
|
||||
owned_xlib_display,
|
||||
};
|
||||
ctx.create_swapchain(width.max(2) as u32, height.max(2) as u32)?;
|
||||
ctx.create_semaphores()?;
|
||||
ctx.create_command_buffer()?;
|
||||
Ok(ctx)
|
||||
}
|
||||
|
||||
fn create_semaphores(&mut self) -> Result<(), String> {
|
||||
let info = VkSemaphoreCreateInfo {
|
||||
s_type: VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,
|
||||
p_next: ptr::null(),
|
||||
flags: 0,
|
||||
};
|
||||
let mut acquire: VkSemaphore = 0;
|
||||
let mut render_done: VkSemaphore = 0;
|
||||
let mut transition: VkSemaphore = 0;
|
||||
let r1 =
|
||||
unsafe { (self.fns.create_semaphore)(self.device, &info, ptr::null(), &mut acquire) };
|
||||
let r2 = unsafe {
|
||||
(self.fns.create_semaphore)(self.device, &info, ptr::null(), &mut render_done)
|
||||
};
|
||||
let r3 = unsafe {
|
||||
(self.fns.create_semaphore)(self.device, &info, ptr::null(), &mut transition)
|
||||
};
|
||||
if r1 != VK_SUCCESS || r2 != VK_SUCCESS || r3 != VK_SUCCESS {
|
||||
return Err(format!("vkCreateSemaphore failed: {r1}/{r2}/{r3}"));
|
||||
}
|
||||
self.acquire_semaphore = acquire;
|
||||
self.render_done_semaphore = render_done;
|
||||
self.transition_semaphore = transition;
|
||||
|
||||
let fence_info = VkFenceCreateInfo {
|
||||
s_type: VK_STRUCTURE_TYPE_FENCE_CREATE_INFO,
|
||||
p_next: ptr::null(),
|
||||
flags: VK_FENCE_CREATE_SIGNALED_BIT,
|
||||
};
|
||||
let mut fence: VkFence = 0;
|
||||
let r4 =
|
||||
unsafe { (self.fns.create_fence)(self.device, &fence_info, ptr::null(), &mut fence) };
|
||||
if r4 != VK_SUCCESS {
|
||||
return Err(format!("vkCreateFence failed: {r4}"));
|
||||
}
|
||||
self.in_flight_fence = fence;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn create_command_buffer(&mut self) -> Result<(), String> {
|
||||
let pool_info = VkCommandPoolCreateInfo {
|
||||
s_type: VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
|
||||
p_next: ptr::null(),
|
||||
flags: VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT,
|
||||
queue_family_index: self.queue_family_index,
|
||||
};
|
||||
let mut pool: VkCommandPool = ptr::null_mut();
|
||||
let result = unsafe {
|
||||
(self.fns.create_command_pool)(self.device, &pool_info, ptr::null(), &mut pool)
|
||||
};
|
||||
if result != VK_SUCCESS {
|
||||
return Err(format!("vkCreateCommandPool failed: VkResult {result}"));
|
||||
}
|
||||
let alloc_info = VkCommandBufferAllocateInfo {
|
||||
s_type: VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
|
||||
p_next: ptr::null(),
|
||||
command_pool: pool,
|
||||
level: VK_COMMAND_BUFFER_LEVEL_PRIMARY,
|
||||
command_buffer_count: 1,
|
||||
};
|
||||
let mut command_buffer: VkCommandBuffer = ptr::null_mut();
|
||||
let result = unsafe {
|
||||
(self.fns.allocate_command_buffers)(self.device, &alloc_info, &mut command_buffer)
|
||||
};
|
||||
if result != VK_SUCCESS {
|
||||
unsafe { (self.fns.destroy_command_pool)(self.device, pool, ptr::null()) };
|
||||
return Err(format!(
|
||||
"vkAllocateCommandBuffers failed: VkResult {result}"
|
||||
));
|
||||
}
|
||||
self.command_pool = pool;
|
||||
self.command_buffer = command_buffer;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn create_swapchain(&mut self, width: u32, height: u32) -> Result<(), String> {
|
||||
let mut caps = VkSurfaceCapabilitiesKHR {
|
||||
min_image_count: 0,
|
||||
max_image_count: 0,
|
||||
current_extent: VkExtent2D::default(),
|
||||
min_image_extent: VkExtent2D::default(),
|
||||
max_image_extent: VkExtent2D::default(),
|
||||
max_image_array_layers: 0,
|
||||
supported_transforms: 0,
|
||||
current_transform: 0,
|
||||
supported_composite_alpha: 0,
|
||||
supported_usage_flags: 0,
|
||||
};
|
||||
let result = unsafe {
|
||||
(self.fns.get_physical_device_surface_capabilities_khr)(
|
||||
self.phys_device,
|
||||
self.surface,
|
||||
&mut caps,
|
||||
)
|
||||
};
|
||||
if result != VK_SUCCESS {
|
||||
return Err(format!(
|
||||
"vkGetPhysicalDeviceSurfaceCapabilitiesKHR failed: {result}"
|
||||
));
|
||||
}
|
||||
|
||||
let mut format_count: u32 = 0;
|
||||
unsafe {
|
||||
(self.fns.get_physical_device_surface_formats_khr)(
|
||||
self.phys_device,
|
||||
self.surface,
|
||||
&mut format_count,
|
||||
ptr::null_mut(),
|
||||
)
|
||||
};
|
||||
let mut formats = vec![
|
||||
VkSurfaceFormatKHR {
|
||||
format: 0,
|
||||
color_space: 0
|
||||
};
|
||||
format_count as usize
|
||||
];
|
||||
unsafe {
|
||||
(self.fns.get_physical_device_surface_formats_khr)(
|
||||
self.phys_device,
|
||||
self.surface,
|
||||
&mut format_count,
|
||||
formats.as_mut_ptr(),
|
||||
)
|
||||
};
|
||||
|
||||
let hdr_format: Option<&VkSurfaceFormatKHR> = None;
|
||||
let (chosen_format, hdr) = if let Some(f) = hdr_format {
|
||||
(*f, true)
|
||||
} else {
|
||||
let f = formats
|
||||
.iter()
|
||||
.find(|f| {
|
||||
f.format == VK_FORMAT_B8G8R8A8_UNORM
|
||||
&& f.color_space == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR
|
||||
})
|
||||
.or_else(|| {
|
||||
formats
|
||||
.iter()
|
||||
.find(|f| f.format == VK_FORMAT_B8G8R8A8_UNORM)
|
||||
})
|
||||
.or_else(|| formats.first())
|
||||
.copied()
|
||||
.unwrap_or(VkSurfaceFormatKHR {
|
||||
format: VK_FORMAT_B8G8R8A8_UNORM,
|
||||
color_space: VK_COLOR_SPACE_SRGB_NONLINEAR_KHR,
|
||||
});
|
||||
(f, false)
|
||||
};
|
||||
self.hdr.store(hdr, Ordering::Release);
|
||||
|
||||
let mut image_count = caps.min_image_count + 1;
|
||||
if caps.max_image_count > 0 && image_count > caps.max_image_count {
|
||||
image_count = caps.max_image_count;
|
||||
}
|
||||
|
||||
let extent = if caps.current_extent.width != u32::MAX {
|
||||
caps.current_extent
|
||||
} else {
|
||||
VkExtent2D { width, height }
|
||||
};
|
||||
|
||||
let image_usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT
|
||||
| (caps.supported_usage_flags & VK_IMAGE_USAGE_TRANSFER_DST_BIT);
|
||||
self.image_usage = image_usage;
|
||||
|
||||
let mut mode_count: u32 = 0;
|
||||
unsafe {
|
||||
(self.fns.get_physical_device_surface_present_modes_khr)(
|
||||
self.phys_device,
|
||||
self.surface,
|
||||
&mut mode_count,
|
||||
ptr::null_mut(),
|
||||
)
|
||||
};
|
||||
let mut modes = vec![0i32; mode_count as usize];
|
||||
if mode_count > 0 {
|
||||
unsafe {
|
||||
(self.fns.get_physical_device_surface_present_modes_khr)(
|
||||
self.phys_device,
|
||||
self.surface,
|
||||
&mut mode_count,
|
||||
modes.as_mut_ptr(),
|
||||
)
|
||||
};
|
||||
}
|
||||
let present_mode = if modes.contains(&VK_PRESENT_MODE_MAILBOX_KHR) {
|
||||
VK_PRESENT_MODE_MAILBOX_KHR
|
||||
} else {
|
||||
VK_PRESENT_MODE_FIFO_KHR
|
||||
};
|
||||
|
||||
let old_swapchain = self.swapchain;
|
||||
let create_info = VkSwapchainCreateInfoKHR {
|
||||
s_type: VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR,
|
||||
p_next: ptr::null(),
|
||||
flags: 0,
|
||||
surface: self.surface,
|
||||
min_image_count: image_count,
|
||||
image_format: chosen_format.format,
|
||||
image_color_space: chosen_format.color_space,
|
||||
image_extent: extent,
|
||||
image_array_layers: 1,
|
||||
image_usage,
|
||||
image_sharing_mode: VK_SHARING_MODE_EXCLUSIVE,
|
||||
queue_family_index_count: 0,
|
||||
p_queue_family_indices: ptr::null(),
|
||||
pre_transform: caps.current_transform,
|
||||
composite_alpha: VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR,
|
||||
present_mode,
|
||||
clipped: 1,
|
||||
old_swapchain,
|
||||
};
|
||||
|
||||
let mut swapchain: VkSwapchainKHR = 0;
|
||||
let result = unsafe {
|
||||
(self.fns.create_swapchain_khr)(self.device, &create_info, ptr::null(), &mut swapchain)
|
||||
};
|
||||
if old_swapchain != 0 {
|
||||
unsafe { (self.fns.destroy_swapchain_khr)(self.device, old_swapchain, ptr::null()) };
|
||||
}
|
||||
if result != VK_SUCCESS {
|
||||
return Err(format!("vkCreateSwapchainKHR failed: VkResult {result}"));
|
||||
}
|
||||
|
||||
let mut image_count_out: u32 = 0;
|
||||
unsafe {
|
||||
(self.fns.get_swapchain_images_khr)(
|
||||
self.device,
|
||||
swapchain,
|
||||
&mut image_count_out,
|
||||
ptr::null_mut(),
|
||||
)
|
||||
};
|
||||
let mut images = vec![0u64; image_count_out as usize];
|
||||
unsafe {
|
||||
(self.fns.get_swapchain_images_khr)(
|
||||
self.device,
|
||||
swapchain,
|
||||
&mut image_count_out,
|
||||
images.as_mut_ptr(),
|
||||
)
|
||||
};
|
||||
|
||||
self.swapchain = swapchain;
|
||||
self.images = images;
|
||||
self.image_format = chosen_format.format;
|
||||
self.extent = extent;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn device_handles(&self) -> (*mut c_void, *mut c_void, *mut c_void, u32, u32, *mut c_void) {
|
||||
(
|
||||
self.instance,
|
||||
self.phys_device,
|
||||
self.device,
|
||||
self.queue_family_index,
|
||||
1,
|
||||
self.fns.get_instance_proc_addr as *mut c_void,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn image_usage(&self) -> u32 {
|
||||
self.image_usage
|
||||
}
|
||||
|
||||
pub fn is_hdr(&self) -> bool {
|
||||
self.hdr.load(Ordering::Acquire)
|
||||
}
|
||||
|
||||
pub fn enabled_device_extension_ptrs(&self) -> Vec<*const i8> {
|
||||
self.enabled_device_extensions
|
||||
.iter()
|
||||
.map(|e| e.as_ptr())
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn resize(&mut self, width: i32, height: i32) -> Result<(), String> {
|
||||
let width = width.max(2) as u32;
|
||||
let height = height.max(2) as u32;
|
||||
if self.extent.width == width && self.extent.height == height {
|
||||
return Ok(());
|
||||
}
|
||||
unsafe { (self.fns.device_wait_idle)(self.device) };
|
||||
self.create_swapchain(width, height)
|
||||
}
|
||||
|
||||
pub fn render_and_present<F>(&mut self, render: F) -> Result<(), String>
|
||||
where
|
||||
F: FnMut(u64, i32, u32, u32, u64, u64) -> Result<i32, String>,
|
||||
{
|
||||
let result = self.render_and_present_inner(render);
|
||||
if result.is_err() {
|
||||
self.recover_after_render_error();
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
fn recover_after_render_error(&mut self) {
|
||||
unsafe {
|
||||
(self.fns.device_wait_idle)(self.device);
|
||||
for sem in [
|
||||
self.acquire_semaphore,
|
||||
self.render_done_semaphore,
|
||||
self.transition_semaphore,
|
||||
] {
|
||||
if sem != 0 {
|
||||
(self.fns.destroy_semaphore)(self.device, sem, ptr::null());
|
||||
}
|
||||
}
|
||||
if self.in_flight_fence != 0 {
|
||||
(self.fns.destroy_fence)(self.device, self.in_flight_fence, ptr::null());
|
||||
}
|
||||
}
|
||||
self.acquire_semaphore = 0;
|
||||
self.render_done_semaphore = 0;
|
||||
self.transition_semaphore = 0;
|
||||
self.in_flight_fence = 0;
|
||||
let extent = self.extent;
|
||||
let _ = self.create_semaphores();
|
||||
let _ = self.create_swapchain(extent.width, extent.height);
|
||||
}
|
||||
|
||||
fn render_and_present_inner<F>(&mut self, mut render: F) -> Result<(), String>
|
||||
where
|
||||
F: FnMut(u64, i32, u32, u32, u64, u64) -> Result<i32, String>,
|
||||
{
|
||||
unsafe {
|
||||
let wait_result =
|
||||
(self.fns.wait_for_fences)(self.device, 1, &self.in_flight_fence, 1, u64::MAX);
|
||||
if wait_result != VK_SUCCESS {
|
||||
return Err(format!("vkWaitForFences failed: {wait_result}"));
|
||||
}
|
||||
let reset_result = (self.fns.reset_fences)(self.device, 1, &self.in_flight_fence);
|
||||
if reset_result != VK_SUCCESS {
|
||||
return Err(format!("vkResetFences failed: {reset_result}"));
|
||||
}
|
||||
}
|
||||
let mut image_index: u32 = 0;
|
||||
let result = unsafe {
|
||||
(self.fns.acquire_next_image_khr)(
|
||||
self.device,
|
||||
self.swapchain,
|
||||
100_000_000,
|
||||
self.acquire_semaphore,
|
||||
ptr::null_mut(),
|
||||
&mut image_index,
|
||||
)
|
||||
};
|
||||
if result == VK_TIMEOUT {
|
||||
return Ok(());
|
||||
}
|
||||
if result != VK_SUCCESS && result != VK_SUBOPTIMAL_KHR {
|
||||
return Err(format!("vkAcquireNextImageKHR failed: VkResult {result}"));
|
||||
}
|
||||
|
||||
let image = self.images[image_index as usize];
|
||||
let out_layout = render(
|
||||
image,
|
||||
self.image_format,
|
||||
self.extent.width,
|
||||
self.extent.height,
|
||||
self.acquire_semaphore,
|
||||
self.render_done_semaphore,
|
||||
)?;
|
||||
|
||||
unsafe {
|
||||
(self.fns.reset_command_buffer)(self.command_buffer, 0);
|
||||
let begin_info = VkCommandBufferBeginInfo {
|
||||
s_type: VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
|
||||
p_next: ptr::null(),
|
||||
flags: VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT,
|
||||
p_inheritance_info: ptr::null(),
|
||||
};
|
||||
let result = (self.fns.begin_command_buffer)(self.command_buffer, &begin_info);
|
||||
if result != VK_SUCCESS {
|
||||
return Err(format!("vkBeginCommandBuffer failed: VkResult {result}"));
|
||||
}
|
||||
let barrier = VkImageMemoryBarrier {
|
||||
s_type: VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
||||
p_next: ptr::null(),
|
||||
src_access_mask: VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT,
|
||||
dst_access_mask: 0,
|
||||
old_layout: out_layout,
|
||||
new_layout: VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
|
||||
src_queue_family_index: VK_QUEUE_FAMILY_IGNORED,
|
||||
dst_queue_family_index: VK_QUEUE_FAMILY_IGNORED,
|
||||
image,
|
||||
subresource_range: VkImageSubresourceRange {
|
||||
aspect_mask: VK_IMAGE_ASPECT_COLOR_BIT,
|
||||
base_mip_level: 0,
|
||||
level_count: 1,
|
||||
base_array_layer: 0,
|
||||
layer_count: 1,
|
||||
},
|
||||
};
|
||||
(self.fns.cmd_pipeline_barrier)(
|
||||
self.command_buffer,
|
||||
VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
|
||||
VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT,
|
||||
0,
|
||||
0,
|
||||
ptr::null(),
|
||||
0,
|
||||
ptr::null(),
|
||||
1,
|
||||
&barrier,
|
||||
);
|
||||
let result = (self.fns.end_command_buffer)(self.command_buffer);
|
||||
if result != VK_SUCCESS {
|
||||
return Err(format!("vkEndCommandBuffer failed: VkResult {result}"));
|
||||
}
|
||||
|
||||
let wait_stage: u32 = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT;
|
||||
let submit_info = VkSubmitInfo {
|
||||
s_type: VK_STRUCTURE_TYPE_SUBMIT_INFO,
|
||||
p_next: ptr::null(),
|
||||
wait_semaphore_count: 1,
|
||||
p_wait_semaphores: &self.render_done_semaphore,
|
||||
p_wait_dst_stage_mask: &wait_stage,
|
||||
command_buffer_count: 1,
|
||||
p_command_buffers: &self.command_buffer,
|
||||
signal_semaphore_count: 1,
|
||||
p_signal_semaphores: &self.transition_semaphore,
|
||||
};
|
||||
let result = (self.fns.queue_submit)(self.queue, 1, &submit_info, self.in_flight_fence);
|
||||
if result != VK_SUCCESS {
|
||||
return Err(format!("vkQueueSubmit failed: VkResult {result}"));
|
||||
}
|
||||
}
|
||||
|
||||
let present_info = VkPresentInfoKHR {
|
||||
s_type: VK_STRUCTURE_TYPE_PRESENT_INFO_KHR,
|
||||
p_next: ptr::null(),
|
||||
wait_semaphore_count: 1,
|
||||
p_wait_semaphores: &self.transition_semaphore,
|
||||
swapchain_count: 1,
|
||||
p_swapchains: &self.swapchain,
|
||||
p_image_indices: &image_index,
|
||||
p_results: ptr::null_mut(),
|
||||
};
|
||||
let result = unsafe { (self.fns.queue_present_khr)(self.queue, &present_info) };
|
||||
if result != VK_SUCCESS && result != VK_SUBOPTIMAL_KHR {
|
||||
return Err(format!("vkQueuePresentKHR failed: VkResult {result}"));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for VulkanContext {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
(self.fns.device_wait_idle)(self.device);
|
||||
if self.acquire_semaphore != 0 {
|
||||
(self.fns.destroy_semaphore)(self.device, self.acquire_semaphore, ptr::null());
|
||||
}
|
||||
if self.render_done_semaphore != 0 {
|
||||
(self.fns.destroy_semaphore)(self.device, self.render_done_semaphore, ptr::null());
|
||||
}
|
||||
if self.transition_semaphore != 0 {
|
||||
(self.fns.destroy_semaphore)(self.device, self.transition_semaphore, ptr::null());
|
||||
}
|
||||
if self.in_flight_fence != 0 {
|
||||
(self.fns.destroy_fence)(self.device, self.in_flight_fence, ptr::null());
|
||||
}
|
||||
if !self.command_pool.is_null() {
|
||||
(self.fns.destroy_command_pool)(self.device, self.command_pool, ptr::null());
|
||||
}
|
||||
if self.swapchain != 0 {
|
||||
(self.fns.destroy_swapchain_khr)(self.device, self.swapchain, ptr::null());
|
||||
}
|
||||
(self.fns.destroy_device)(self.device, ptr::null());
|
||||
(self.fns.destroy_surface_khr)(self.instance, self.surface, ptr::null());
|
||||
(self.fns.destroy_instance)(self.instance, ptr::null());
|
||||
if !self.owned_xlib_display.is_null() {
|
||||
x11::xlib::XCloseDisplay(self.owned_xlib_display as *mut x11::xlib::Display);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
mod init;
|
||||
mod render;
|
||||
mod swapchain;
|
||||
|
|
|
|||
423
src-tauri/src/linux_vulkan/init.rs
Normal file
423
src-tauri/src/linux_vulkan/init.rs
Normal file
|
|
@ -0,0 +1,423 @@
|
|||
use super::*;
|
||||
|
||||
impl VulkanContext {
|
||||
pub fn new(native_surface: NativeSurface, width: i32, height: i32) -> Result<Self, String> {
|
||||
let module = dlopen_first(&["libvulkan.so.1", "libvulkan.so"])
|
||||
.ok_or("libvulkan.so.1 not found (no Vulkan-capable driver installed?)")?;
|
||||
|
||||
let get_instance_proc_addr: PfnGetInstanceProcAddr =
|
||||
unsafe { std::mem::transmute(dlsym_typed(module, "vkGetInstanceProcAddr")?) };
|
||||
let create_instance: PfnCreateInstance = unsafe {
|
||||
std::mem::transmute(get_instance_proc(
|
||||
get_instance_proc_addr,
|
||||
ptr::null_mut(),
|
||||
"vkCreateInstance",
|
||||
)?)
|
||||
};
|
||||
|
||||
let app_name = CString::new("fluxa-desktop").unwrap();
|
||||
let app_info = VkApplicationInfo {
|
||||
s_type: VK_STRUCTURE_TYPE_APPLICATION_INFO,
|
||||
p_next: ptr::null(),
|
||||
p_application_name: app_name.as_ptr(),
|
||||
application_version: 0,
|
||||
p_engine_name: app_name.as_ptr(),
|
||||
engine_version: 0,
|
||||
api_version: VK_API_VERSION_1_3,
|
||||
};
|
||||
let surface_ext_name = match native_surface {
|
||||
NativeSurface::Xlib { .. } => "VK_KHR_xlib_surface",
|
||||
NativeSurface::Wayland { .. } => "VK_KHR_wayland_surface",
|
||||
};
|
||||
let extensions = [
|
||||
CString::new("VK_KHR_surface").unwrap(),
|
||||
CString::new(surface_ext_name).unwrap(),
|
||||
CString::new("VK_EXT_swapchain_colorspace").unwrap(),
|
||||
];
|
||||
let extension_ptrs: Vec<*const i8> = extensions.iter().map(|e| e.as_ptr()).collect();
|
||||
let validation_layer = CString::new("VK_LAYER_KHRONOS_validation").unwrap();
|
||||
let want_validation = std::env::var_os("FLUXA_VULKAN_VALIDATION").is_some();
|
||||
let layer_ptrs: Vec<*const i8> = if want_validation {
|
||||
vec![validation_layer.as_ptr()]
|
||||
} else {
|
||||
vec![]
|
||||
};
|
||||
let instance_create_info = VkInstanceCreateInfo {
|
||||
s_type: VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
|
||||
p_next: ptr::null(),
|
||||
flags: 0,
|
||||
p_application_info: &app_info,
|
||||
enabled_layer_count: layer_ptrs.len() as u32,
|
||||
pp_enabled_layer_names: layer_ptrs.as_ptr(),
|
||||
enabled_extension_count: extension_ptrs.len() as u32,
|
||||
pp_enabled_extension_names: extension_ptrs.as_ptr(),
|
||||
};
|
||||
|
||||
let mut instance: VkInstance = ptr::null_mut();
|
||||
let result = unsafe { create_instance(&instance_create_info, ptr::null(), &mut instance) };
|
||||
if result != VK_SUCCESS {
|
||||
return Err(format!("vkCreateInstance failed: VkResult {result}"));
|
||||
}
|
||||
|
||||
macro_rules! iproc {
|
||||
($name:expr_2021) => {
|
||||
unsafe {
|
||||
std::mem::transmute(get_instance_proc(get_instance_proc_addr, instance, $name)?)
|
||||
}
|
||||
};
|
||||
}
|
||||
let destroy_instance: PfnDestroyInstance = iproc!("vkDestroyInstance");
|
||||
let enumerate_physical_devices: PfnEnumeratePhysicalDevices =
|
||||
iproc!("vkEnumeratePhysicalDevices");
|
||||
let get_physical_device_properties: PfnGetPhysicalDeviceProperties =
|
||||
iproc!("vkGetPhysicalDeviceProperties");
|
||||
let get_queue_family_properties: PfnGetPhysicalDeviceQueueFamilyProperties =
|
||||
iproc!("vkGetPhysicalDeviceQueueFamilyProperties");
|
||||
let get_surface_support_khr: PfnGetPhysicalDeviceSurfaceSupportKHR =
|
||||
iproc!("vkGetPhysicalDeviceSurfaceSupportKHR");
|
||||
let get_surface_capabilities_khr: PfnGetPhysicalDeviceSurfaceCapabilitiesKHR =
|
||||
iproc!("vkGetPhysicalDeviceSurfaceCapabilitiesKHR");
|
||||
let get_surface_formats_khr: PfnGetPhysicalDeviceSurfaceFormatsKHR =
|
||||
iproc!("vkGetPhysicalDeviceSurfaceFormatsKHR");
|
||||
let get_surface_present_modes_khr: PfnGetPhysicalDeviceSurfacePresentModesKHR =
|
||||
iproc!("vkGetPhysicalDeviceSurfacePresentModesKHR");
|
||||
let destroy_surface_khr: PfnDestroySurfaceKHR = iproc!("vkDestroySurfaceKHR");
|
||||
let create_device: PfnCreateDevice = iproc!("vkCreateDevice");
|
||||
let enumerate_device_extension_properties: PfnEnumerateDeviceExtensionProperties =
|
||||
iproc!("vkEnumerateDeviceExtensionProperties");
|
||||
|
||||
let mut surface: VkSurfaceKHR = 0;
|
||||
let mut owned_xlib_display: *mut c_void = ptr::null_mut();
|
||||
let result = match native_surface {
|
||||
NativeSurface::Xlib { display, window } => {
|
||||
let create_xlib_surface_khr: PfnCreateXlibSurfaceKHR =
|
||||
iproc!("vkCreateXlibSurfaceKHR");
|
||||
// The render loop runs on its own thread, but GTK's Display
|
||||
// connection is not thread-safe (XInitThreads is never called).
|
||||
// A private connection keeps Mesa's WSI traffic off GTK's.
|
||||
owned_xlib_display = unsafe { x11::xlib::XOpenDisplay(ptr::null()) as *mut c_void };
|
||||
let dpy = if owned_xlib_display.is_null() {
|
||||
display
|
||||
} else {
|
||||
owned_xlib_display
|
||||
};
|
||||
let create_info = VkXlibSurfaceCreateInfoKHR {
|
||||
s_type: VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR,
|
||||
p_next: ptr::null(),
|
||||
flags: 0,
|
||||
dpy,
|
||||
window,
|
||||
};
|
||||
unsafe {
|
||||
create_xlib_surface_khr(instance, &create_info, ptr::null(), &mut surface)
|
||||
}
|
||||
}
|
||||
NativeSurface::Wayland {
|
||||
display,
|
||||
surface: wl_surface,
|
||||
} => {
|
||||
let create_wayland_surface_khr: PfnCreateWaylandSurfaceKHR =
|
||||
iproc!("vkCreateWaylandSurfaceKHR");
|
||||
let create_info = VkWaylandSurfaceCreateInfoKHR {
|
||||
s_type: VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR,
|
||||
p_next: ptr::null(),
|
||||
flags: 0,
|
||||
display,
|
||||
surface: wl_surface,
|
||||
};
|
||||
unsafe {
|
||||
create_wayland_surface_khr(instance, &create_info, ptr::null(), &mut surface)
|
||||
}
|
||||
}
|
||||
};
|
||||
if result != VK_SUCCESS {
|
||||
unsafe { destroy_instance(instance, ptr::null()) };
|
||||
return Err(format!("vkCreate*SurfaceKHR failed: VkResult {result}"));
|
||||
}
|
||||
|
||||
let mut device_count: u32 = 0;
|
||||
unsafe { enumerate_physical_devices(instance, &mut device_count, ptr::null_mut()) };
|
||||
if device_count == 0 {
|
||||
unsafe {
|
||||
destroy_surface_khr(instance, surface, ptr::null());
|
||||
destroy_instance(instance, ptr::null());
|
||||
}
|
||||
return Err("vkEnumeratePhysicalDevices found no GPUs".into());
|
||||
}
|
||||
let mut physical_devices = vec![ptr::null_mut(); device_count as usize];
|
||||
unsafe {
|
||||
enumerate_physical_devices(instance, &mut device_count, physical_devices.as_mut_ptr())
|
||||
};
|
||||
|
||||
const VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU: u32 = 2;
|
||||
const VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU: u32 = 1;
|
||||
const VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU: u32 = 3;
|
||||
fn device_type_rank(device_type: u32) -> u32 {
|
||||
match device_type {
|
||||
VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU => 0,
|
||||
VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU => 1,
|
||||
VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU => 2,
|
||||
_ => 3,
|
||||
}
|
||||
}
|
||||
|
||||
let mut candidates: Vec<(VkPhysicalDevice, u32, u32)> = Vec::new();
|
||||
for &phys_device in &physical_devices {
|
||||
let mut family_count: u32 = 0;
|
||||
unsafe { get_queue_family_properties(phys_device, &mut family_count, ptr::null_mut()) };
|
||||
let mut families: Vec<VkQueueFamilyProperties> = (0..family_count)
|
||||
.map(|_| VkQueueFamilyProperties {
|
||||
queue_flags: 0,
|
||||
queue_count: 0,
|
||||
timestamp_valid_bits: 0,
|
||||
min_image_transfer_granularity: [0; 3],
|
||||
})
|
||||
.collect();
|
||||
unsafe {
|
||||
get_queue_family_properties(phys_device, &mut family_count, families.as_mut_ptr())
|
||||
};
|
||||
for (index, family) in families.iter().enumerate() {
|
||||
if family.queue_flags & VK_QUEUE_GRAPHICS_BIT == 0 {
|
||||
continue;
|
||||
}
|
||||
let mut present_supported: u32 = 0;
|
||||
unsafe {
|
||||
get_surface_support_khr(
|
||||
phys_device,
|
||||
index as u32,
|
||||
surface,
|
||||
&mut present_supported,
|
||||
)
|
||||
};
|
||||
if present_supported != 0 {
|
||||
let mut props = [0u8; 1024];
|
||||
unsafe {
|
||||
get_physical_device_properties(
|
||||
phys_device,
|
||||
props.as_mut_ptr() as *mut c_void,
|
||||
)
|
||||
};
|
||||
let device_type = u32::from_ne_bytes(props[16..20].try_into().unwrap());
|
||||
candidates.push((phys_device, index as u32, device_type));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
candidates.sort_by_key(|&(_, _, device_type)| device_type_rank(device_type));
|
||||
let chosen = candidates
|
||||
.first()
|
||||
.map(|&(phys_device, queue_family_index, _)| (phys_device, queue_family_index));
|
||||
let Some((phys_device, queue_family_index)) = chosen else {
|
||||
unsafe {
|
||||
destroy_surface_khr(instance, surface, ptr::null());
|
||||
destroy_instance(instance, ptr::null());
|
||||
}
|
||||
return Err(
|
||||
"no Vulkan queue family supports both graphics and presenting to this window"
|
||||
.to_string(),
|
||||
);
|
||||
};
|
||||
|
||||
let queue_priority: f32 = 1.0;
|
||||
let queue_create_info = VkDeviceQueueCreateInfo {
|
||||
s_type: VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
|
||||
p_next: ptr::null(),
|
||||
flags: 0,
|
||||
queue_family_index,
|
||||
queue_count: 1,
|
||||
p_queue_priorities: &queue_priority,
|
||||
};
|
||||
let mut ext_count: u32 = 0;
|
||||
unsafe {
|
||||
enumerate_device_extension_properties(
|
||||
phys_device,
|
||||
ptr::null(),
|
||||
&mut ext_count,
|
||||
ptr::null_mut(),
|
||||
)
|
||||
};
|
||||
let mut supported_extensions = vec![
|
||||
VkExtensionProperties {
|
||||
extension_name: [0; 256],
|
||||
spec_version: 0,
|
||||
};
|
||||
ext_count as usize
|
||||
];
|
||||
unsafe {
|
||||
enumerate_device_extension_properties(
|
||||
phys_device,
|
||||
ptr::null(),
|
||||
&mut ext_count,
|
||||
supported_extensions.as_mut_ptr(),
|
||||
)
|
||||
};
|
||||
let device_extensions: Vec<CString> = supported_extensions
|
||||
.iter()
|
||||
.filter_map(|ext| {
|
||||
let nul = ext.extension_name.iter().position(|&b| b == 0)?;
|
||||
CString::new(&ext.extension_name[..nul]).ok()
|
||||
})
|
||||
.collect();
|
||||
let device_extension_ptrs: Vec<*const i8> =
|
||||
device_extensions.iter().map(|e| e.as_ptr()).collect();
|
||||
let mut synchronization2_features = VkPhysicalDeviceSynchronization2Features {
|
||||
s_type: VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SYNCHRONIZATION_2_FEATURES,
|
||||
p_next: ptr::null_mut(),
|
||||
synchronization2: 1,
|
||||
};
|
||||
let mut timeline_semaphore_features = VkPhysicalDeviceTimelineSemaphoreFeatures {
|
||||
s_type: VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TIMELINE_SEMAPHORE_FEATURES,
|
||||
p_next: &mut synchronization2_features as *mut _ as *mut c_void,
|
||||
timeline_semaphore: 1,
|
||||
};
|
||||
let device_create_info = VkDeviceCreateInfo {
|
||||
s_type: VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
|
||||
p_next: &mut timeline_semaphore_features as *mut _ as *const c_void,
|
||||
flags: 0,
|
||||
queue_create_info_count: 1,
|
||||
p_queue_create_infos: &queue_create_info,
|
||||
enabled_layer_count: 0,
|
||||
pp_enabled_layer_names: ptr::null(),
|
||||
enabled_extension_count: device_extension_ptrs.len() as u32,
|
||||
pp_enabled_extension_names: device_extension_ptrs.as_ptr(),
|
||||
p_enabled_features: ptr::null(),
|
||||
};
|
||||
let mut device: VkDevice = ptr::null_mut();
|
||||
let result =
|
||||
unsafe { create_device(phys_device, &device_create_info, ptr::null(), &mut device) };
|
||||
if result != VK_SUCCESS {
|
||||
unsafe {
|
||||
destroy_surface_khr(instance, surface, ptr::null());
|
||||
destroy_instance(instance, ptr::null());
|
||||
}
|
||||
return Err(format!("vkCreateDevice failed: VkResult {result}"));
|
||||
}
|
||||
|
||||
macro_rules! dproc {
|
||||
($name:expr_2021) => {
|
||||
unsafe {
|
||||
std::mem::transmute(get_instance_proc(get_instance_proc_addr, instance, $name)?)
|
||||
}
|
||||
};
|
||||
}
|
||||
let destroy_device: PfnDestroyDevice = dproc!("vkDestroyDevice");
|
||||
let get_device_queue: PfnGetDeviceQueue = dproc!("vkGetDeviceQueue");
|
||||
let create_swapchain_khr: PfnCreateSwapchainKHR = dproc!("vkCreateSwapchainKHR");
|
||||
let destroy_swapchain_khr: PfnDestroySwapchainKHR = dproc!("vkDestroySwapchainKHR");
|
||||
let get_swapchain_images_khr: PfnGetSwapchainImagesKHR = dproc!("vkGetSwapchainImagesKHR");
|
||||
let acquire_next_image_khr: PfnAcquireNextImageKHR = dproc!("vkAcquireNextImageKHR");
|
||||
let queue_present_khr: PfnQueuePresentKHR = dproc!("vkQueuePresentKHR");
|
||||
let create_semaphore: PfnCreateSemaphore = dproc!("vkCreateSemaphore");
|
||||
let destroy_semaphore: PfnDestroySemaphore = dproc!("vkDestroySemaphore");
|
||||
let create_fence: PfnCreateFence = dproc!("vkCreateFence");
|
||||
let destroy_fence: PfnDestroyFence = dproc!("vkDestroyFence");
|
||||
let wait_for_fences: PfnWaitForFences = dproc!("vkWaitForFences");
|
||||
let reset_fences: PfnResetFences = dproc!("vkResetFences");
|
||||
let device_wait_idle: PfnDeviceWaitIdle = dproc!("vkDeviceWaitIdle");
|
||||
let create_command_pool: PfnCreateCommandPool = dproc!("vkCreateCommandPool");
|
||||
let destroy_command_pool: PfnDestroyCommandPool = dproc!("vkDestroyCommandPool");
|
||||
let allocate_command_buffers: PfnAllocateCommandBuffers =
|
||||
dproc!("vkAllocateCommandBuffers");
|
||||
let reset_command_buffer: PfnResetCommandBuffer = dproc!("vkResetCommandBuffer");
|
||||
let begin_command_buffer: PfnBeginCommandBuffer = dproc!("vkBeginCommandBuffer");
|
||||
let end_command_buffer: PfnEndCommandBuffer = dproc!("vkEndCommandBuffer");
|
||||
let cmd_pipeline_barrier: PfnCmdPipelineBarrier = dproc!("vkCmdPipelineBarrier");
|
||||
let queue_submit: PfnQueueSubmit = dproc!("vkQueueSubmit");
|
||||
|
||||
let mut queue: VkQueue = ptr::null_mut();
|
||||
unsafe { get_device_queue(device, queue_family_index, 0, &mut queue) };
|
||||
|
||||
let fns = VkFns {
|
||||
get_instance_proc_addr,
|
||||
destroy_instance,
|
||||
get_physical_device_surface_capabilities_khr: get_surface_capabilities_khr,
|
||||
get_physical_device_surface_formats_khr: get_surface_formats_khr,
|
||||
get_physical_device_surface_present_modes_khr: get_surface_present_modes_khr,
|
||||
destroy_surface_khr,
|
||||
destroy_device,
|
||||
create_swapchain_khr,
|
||||
destroy_swapchain_khr,
|
||||
get_swapchain_images_khr,
|
||||
acquire_next_image_khr,
|
||||
queue_present_khr,
|
||||
create_semaphore,
|
||||
destroy_semaphore,
|
||||
create_fence,
|
||||
destroy_fence,
|
||||
wait_for_fences,
|
||||
reset_fences,
|
||||
device_wait_idle,
|
||||
create_command_pool,
|
||||
destroy_command_pool,
|
||||
allocate_command_buffers,
|
||||
reset_command_buffer,
|
||||
begin_command_buffer,
|
||||
end_command_buffer,
|
||||
cmd_pipeline_barrier,
|
||||
queue_submit,
|
||||
};
|
||||
|
||||
let mut ctx = Self {
|
||||
fns,
|
||||
instance,
|
||||
phys_device,
|
||||
device,
|
||||
queue,
|
||||
queue_family_index,
|
||||
surface,
|
||||
swapchain: 0,
|
||||
images: Vec::new(),
|
||||
image_format: VK_FORMAT_B8G8R8A8_UNORM,
|
||||
image_usage: VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,
|
||||
extent: VkExtent2D {
|
||||
width: width.max(2) as u32,
|
||||
height: height.max(2) as u32,
|
||||
},
|
||||
acquire_semaphore: 0,
|
||||
render_done_semaphore: 0,
|
||||
transition_semaphore: 0,
|
||||
in_flight_fence: 0,
|
||||
command_pool: ptr::null_mut(),
|
||||
command_buffer: ptr::null_mut(),
|
||||
hdr: AtomicBool::new(false),
|
||||
enabled_device_extensions: device_extensions,
|
||||
owned_xlib_display,
|
||||
};
|
||||
ctx.create_swapchain(width.max(2) as u32, height.max(2) as u32)?;
|
||||
ctx.create_semaphores()?;
|
||||
ctx.create_command_buffer()?;
|
||||
Ok(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for VulkanContext {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
(self.fns.device_wait_idle)(self.device);
|
||||
if self.acquire_semaphore != 0 {
|
||||
(self.fns.destroy_semaphore)(self.device, self.acquire_semaphore, ptr::null());
|
||||
}
|
||||
if self.render_done_semaphore != 0 {
|
||||
(self.fns.destroy_semaphore)(self.device, self.render_done_semaphore, ptr::null());
|
||||
}
|
||||
if self.transition_semaphore != 0 {
|
||||
(self.fns.destroy_semaphore)(self.device, self.transition_semaphore, ptr::null());
|
||||
}
|
||||
if self.in_flight_fence != 0 {
|
||||
(self.fns.destroy_fence)(self.device, self.in_flight_fence, ptr::null());
|
||||
}
|
||||
if !self.command_pool.is_null() {
|
||||
(self.fns.destroy_command_pool)(self.device, self.command_pool, ptr::null());
|
||||
}
|
||||
if self.swapchain != 0 {
|
||||
(self.fns.destroy_swapchain_khr)(self.device, self.swapchain, ptr::null());
|
||||
}
|
||||
(self.fns.destroy_device)(self.device, ptr::null());
|
||||
(self.fns.destroy_surface_khr)(self.instance, self.surface, ptr::null());
|
||||
(self.fns.destroy_instance)(self.instance, ptr::null());
|
||||
if !self.owned_xlib_display.is_null() {
|
||||
x11::xlib::XCloseDisplay(self.owned_xlib_display as *mut x11::xlib::Display);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
190
src-tauri/src/linux_vulkan/render.rs
Normal file
190
src-tauri/src/linux_vulkan/render.rs
Normal file
|
|
@ -0,0 +1,190 @@
|
|||
use super::*;
|
||||
|
||||
impl VulkanContext {
|
||||
pub fn device_handles(&self) -> (*mut c_void, *mut c_void, *mut c_void, u32, u32, *mut c_void) {
|
||||
(
|
||||
self.instance,
|
||||
self.phys_device,
|
||||
self.device,
|
||||
self.queue_family_index,
|
||||
1,
|
||||
self.fns.get_instance_proc_addr as *mut c_void,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn image_usage(&self) -> u32 {
|
||||
self.image_usage
|
||||
}
|
||||
|
||||
pub fn is_hdr(&self) -> bool {
|
||||
self.hdr.load(Ordering::Acquire)
|
||||
}
|
||||
|
||||
pub fn enabled_device_extension_ptrs(&self) -> Vec<*const i8> {
|
||||
self.enabled_device_extensions
|
||||
.iter()
|
||||
.map(|e| e.as_ptr())
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn render_and_present<F>(&mut self, render: F) -> Result<(), String>
|
||||
where
|
||||
F: FnMut(u64, i32, u32, u32, u64, u64) -> Result<i32, String>,
|
||||
{
|
||||
let result = self.render_and_present_inner(render);
|
||||
if result.is_err() {
|
||||
self.recover_after_render_error();
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
fn recover_after_render_error(&mut self) {
|
||||
unsafe {
|
||||
(self.fns.device_wait_idle)(self.device);
|
||||
for sem in [
|
||||
self.acquire_semaphore,
|
||||
self.render_done_semaphore,
|
||||
self.transition_semaphore,
|
||||
] {
|
||||
if sem != 0 {
|
||||
(self.fns.destroy_semaphore)(self.device, sem, ptr::null());
|
||||
}
|
||||
}
|
||||
if self.in_flight_fence != 0 {
|
||||
(self.fns.destroy_fence)(self.device, self.in_flight_fence, ptr::null());
|
||||
}
|
||||
}
|
||||
self.acquire_semaphore = 0;
|
||||
self.render_done_semaphore = 0;
|
||||
self.transition_semaphore = 0;
|
||||
self.in_flight_fence = 0;
|
||||
let extent = self.extent;
|
||||
let _ = self.create_semaphores();
|
||||
let _ = self.create_swapchain(extent.width, extent.height);
|
||||
}
|
||||
|
||||
fn render_and_present_inner<F>(&mut self, mut render: F) -> Result<(), String>
|
||||
where
|
||||
F: FnMut(u64, i32, u32, u32, u64, u64) -> Result<i32, String>,
|
||||
{
|
||||
unsafe {
|
||||
let wait_result =
|
||||
(self.fns.wait_for_fences)(self.device, 1, &self.in_flight_fence, 1, u64::MAX);
|
||||
if wait_result != VK_SUCCESS {
|
||||
return Err(format!("vkWaitForFences failed: {wait_result}"));
|
||||
}
|
||||
let reset_result = (self.fns.reset_fences)(self.device, 1, &self.in_flight_fence);
|
||||
if reset_result != VK_SUCCESS {
|
||||
return Err(format!("vkResetFences failed: {reset_result}"));
|
||||
}
|
||||
}
|
||||
let mut image_index: u32 = 0;
|
||||
let result = unsafe {
|
||||
(self.fns.acquire_next_image_khr)(
|
||||
self.device,
|
||||
self.swapchain,
|
||||
100_000_000,
|
||||
self.acquire_semaphore,
|
||||
ptr::null_mut(),
|
||||
&mut image_index,
|
||||
)
|
||||
};
|
||||
if result == VK_TIMEOUT {
|
||||
return Ok(());
|
||||
}
|
||||
if result != VK_SUCCESS && result != VK_SUBOPTIMAL_KHR {
|
||||
return Err(format!("vkAcquireNextImageKHR failed: VkResult {result}"));
|
||||
}
|
||||
|
||||
let image = self.images[image_index as usize];
|
||||
let out_layout = render(
|
||||
image,
|
||||
self.image_format,
|
||||
self.extent.width,
|
||||
self.extent.height,
|
||||
self.acquire_semaphore,
|
||||
self.render_done_semaphore,
|
||||
)?;
|
||||
|
||||
unsafe {
|
||||
(self.fns.reset_command_buffer)(self.command_buffer, 0);
|
||||
let begin_info = VkCommandBufferBeginInfo {
|
||||
s_type: VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
|
||||
p_next: ptr::null(),
|
||||
flags: VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT,
|
||||
p_inheritance_info: ptr::null(),
|
||||
};
|
||||
let result = (self.fns.begin_command_buffer)(self.command_buffer, &begin_info);
|
||||
if result != VK_SUCCESS {
|
||||
return Err(format!("vkBeginCommandBuffer failed: VkResult {result}"));
|
||||
}
|
||||
let barrier = VkImageMemoryBarrier {
|
||||
s_type: VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
||||
p_next: ptr::null(),
|
||||
src_access_mask: VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT,
|
||||
dst_access_mask: 0,
|
||||
old_layout: out_layout,
|
||||
new_layout: VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
|
||||
src_queue_family_index: VK_QUEUE_FAMILY_IGNORED,
|
||||
dst_queue_family_index: VK_QUEUE_FAMILY_IGNORED,
|
||||
image,
|
||||
subresource_range: VkImageSubresourceRange {
|
||||
aspect_mask: VK_IMAGE_ASPECT_COLOR_BIT,
|
||||
base_mip_level: 0,
|
||||
level_count: 1,
|
||||
base_array_layer: 0,
|
||||
layer_count: 1,
|
||||
},
|
||||
};
|
||||
(self.fns.cmd_pipeline_barrier)(
|
||||
self.command_buffer,
|
||||
VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
|
||||
VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT,
|
||||
0,
|
||||
0,
|
||||
ptr::null(),
|
||||
0,
|
||||
ptr::null(),
|
||||
1,
|
||||
&barrier,
|
||||
);
|
||||
let result = (self.fns.end_command_buffer)(self.command_buffer);
|
||||
if result != VK_SUCCESS {
|
||||
return Err(format!("vkEndCommandBuffer failed: VkResult {result}"));
|
||||
}
|
||||
|
||||
let wait_stage: u32 = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT;
|
||||
let submit_info = VkSubmitInfo {
|
||||
s_type: VK_STRUCTURE_TYPE_SUBMIT_INFO,
|
||||
p_next: ptr::null(),
|
||||
wait_semaphore_count: 1,
|
||||
p_wait_semaphores: &self.render_done_semaphore,
|
||||
p_wait_dst_stage_mask: &wait_stage,
|
||||
command_buffer_count: 1,
|
||||
p_command_buffers: &self.command_buffer,
|
||||
signal_semaphore_count: 1,
|
||||
p_signal_semaphores: &self.transition_semaphore,
|
||||
};
|
||||
let result = (self.fns.queue_submit)(self.queue, 1, &submit_info, self.in_flight_fence);
|
||||
if result != VK_SUCCESS {
|
||||
return Err(format!("vkQueueSubmit failed: VkResult {result}"));
|
||||
}
|
||||
}
|
||||
|
||||
let present_info = VkPresentInfoKHR {
|
||||
s_type: VK_STRUCTURE_TYPE_PRESENT_INFO_KHR,
|
||||
p_next: ptr::null(),
|
||||
wait_semaphore_count: 1,
|
||||
p_wait_semaphores: &self.transition_semaphore,
|
||||
swapchain_count: 1,
|
||||
p_swapchains: &self.swapchain,
|
||||
p_image_indices: &image_index,
|
||||
p_results: ptr::null_mut(),
|
||||
};
|
||||
let result = unsafe { (self.fns.queue_present_khr)(self.queue, &present_info) };
|
||||
if result != VK_SUCCESS && result != VK_SUBOPTIMAL_KHR {
|
||||
return Err(format!("vkQueuePresentKHR failed: VkResult {result}"));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
264
src-tauri/src/linux_vulkan/swapchain.rs
Normal file
264
src-tauri/src/linux_vulkan/swapchain.rs
Normal file
|
|
@ -0,0 +1,264 @@
|
|||
use super::*;
|
||||
|
||||
impl VulkanContext {
|
||||
pub(super) fn create_semaphores(&mut self) -> Result<(), String> {
|
||||
let info = VkSemaphoreCreateInfo {
|
||||
s_type: VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,
|
||||
p_next: ptr::null(),
|
||||
flags: 0,
|
||||
};
|
||||
let mut acquire: VkSemaphore = 0;
|
||||
let mut render_done: VkSemaphore = 0;
|
||||
let mut transition: VkSemaphore = 0;
|
||||
let r1 =
|
||||
unsafe { (self.fns.create_semaphore)(self.device, &info, ptr::null(), &mut acquire) };
|
||||
let r2 = unsafe {
|
||||
(self.fns.create_semaphore)(self.device, &info, ptr::null(), &mut render_done)
|
||||
};
|
||||
let r3 = unsafe {
|
||||
(self.fns.create_semaphore)(self.device, &info, ptr::null(), &mut transition)
|
||||
};
|
||||
if r1 != VK_SUCCESS || r2 != VK_SUCCESS || r3 != VK_SUCCESS {
|
||||
return Err(format!("vkCreateSemaphore failed: {r1}/{r2}/{r3}"));
|
||||
}
|
||||
self.acquire_semaphore = acquire;
|
||||
self.render_done_semaphore = render_done;
|
||||
self.transition_semaphore = transition;
|
||||
|
||||
let fence_info = VkFenceCreateInfo {
|
||||
s_type: VK_STRUCTURE_TYPE_FENCE_CREATE_INFO,
|
||||
p_next: ptr::null(),
|
||||
flags: VK_FENCE_CREATE_SIGNALED_BIT,
|
||||
};
|
||||
let mut fence: VkFence = 0;
|
||||
let r4 =
|
||||
unsafe { (self.fns.create_fence)(self.device, &fence_info, ptr::null(), &mut fence) };
|
||||
if r4 != VK_SUCCESS {
|
||||
return Err(format!("vkCreateFence failed: {r4}"));
|
||||
}
|
||||
self.in_flight_fence = fence;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn create_command_buffer(&mut self) -> Result<(), String> {
|
||||
let pool_info = VkCommandPoolCreateInfo {
|
||||
s_type: VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
|
||||
p_next: ptr::null(),
|
||||
flags: VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT,
|
||||
queue_family_index: self.queue_family_index,
|
||||
};
|
||||
let mut pool: VkCommandPool = ptr::null_mut();
|
||||
let result = unsafe {
|
||||
(self.fns.create_command_pool)(self.device, &pool_info, ptr::null(), &mut pool)
|
||||
};
|
||||
if result != VK_SUCCESS {
|
||||
return Err(format!("vkCreateCommandPool failed: VkResult {result}"));
|
||||
}
|
||||
let alloc_info = VkCommandBufferAllocateInfo {
|
||||
s_type: VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
|
||||
p_next: ptr::null(),
|
||||
command_pool: pool,
|
||||
level: VK_COMMAND_BUFFER_LEVEL_PRIMARY,
|
||||
command_buffer_count: 1,
|
||||
};
|
||||
let mut command_buffer: VkCommandBuffer = ptr::null_mut();
|
||||
let result = unsafe {
|
||||
(self.fns.allocate_command_buffers)(self.device, &alloc_info, &mut command_buffer)
|
||||
};
|
||||
if result != VK_SUCCESS {
|
||||
unsafe { (self.fns.destroy_command_pool)(self.device, pool, ptr::null()) };
|
||||
return Err(format!(
|
||||
"vkAllocateCommandBuffers failed: VkResult {result}"
|
||||
));
|
||||
}
|
||||
self.command_pool = pool;
|
||||
self.command_buffer = command_buffer;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn create_swapchain(&mut self, width: u32, height: u32) -> Result<(), String> {
|
||||
let mut caps = VkSurfaceCapabilitiesKHR {
|
||||
min_image_count: 0,
|
||||
max_image_count: 0,
|
||||
current_extent: VkExtent2D::default(),
|
||||
min_image_extent: VkExtent2D::default(),
|
||||
max_image_extent: VkExtent2D::default(),
|
||||
max_image_array_layers: 0,
|
||||
supported_transforms: 0,
|
||||
current_transform: 0,
|
||||
supported_composite_alpha: 0,
|
||||
supported_usage_flags: 0,
|
||||
};
|
||||
let result = unsafe {
|
||||
(self.fns.get_physical_device_surface_capabilities_khr)(
|
||||
self.phys_device,
|
||||
self.surface,
|
||||
&mut caps,
|
||||
)
|
||||
};
|
||||
if result != VK_SUCCESS {
|
||||
return Err(format!(
|
||||
"vkGetPhysicalDeviceSurfaceCapabilitiesKHR failed: {result}"
|
||||
));
|
||||
}
|
||||
|
||||
let mut format_count: u32 = 0;
|
||||
unsafe {
|
||||
(self.fns.get_physical_device_surface_formats_khr)(
|
||||
self.phys_device,
|
||||
self.surface,
|
||||
&mut format_count,
|
||||
ptr::null_mut(),
|
||||
)
|
||||
};
|
||||
let mut formats = vec![
|
||||
VkSurfaceFormatKHR {
|
||||
format: 0,
|
||||
color_space: 0
|
||||
};
|
||||
format_count as usize
|
||||
];
|
||||
unsafe {
|
||||
(self.fns.get_physical_device_surface_formats_khr)(
|
||||
self.phys_device,
|
||||
self.surface,
|
||||
&mut format_count,
|
||||
formats.as_mut_ptr(),
|
||||
)
|
||||
};
|
||||
|
||||
let hdr_format: Option<&VkSurfaceFormatKHR> = None;
|
||||
let (chosen_format, hdr) = if let Some(f) = hdr_format {
|
||||
(*f, true)
|
||||
} else {
|
||||
let f = formats
|
||||
.iter()
|
||||
.find(|f| {
|
||||
f.format == VK_FORMAT_B8G8R8A8_UNORM
|
||||
&& f.color_space == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR
|
||||
})
|
||||
.or_else(|| {
|
||||
formats
|
||||
.iter()
|
||||
.find(|f| f.format == VK_FORMAT_B8G8R8A8_UNORM)
|
||||
})
|
||||
.or_else(|| formats.first())
|
||||
.copied()
|
||||
.unwrap_or(VkSurfaceFormatKHR {
|
||||
format: VK_FORMAT_B8G8R8A8_UNORM,
|
||||
color_space: VK_COLOR_SPACE_SRGB_NONLINEAR_KHR,
|
||||
});
|
||||
(f, false)
|
||||
};
|
||||
self.hdr.store(hdr, Ordering::Release);
|
||||
|
||||
let mut image_count = caps.min_image_count + 1;
|
||||
if caps.max_image_count > 0 && image_count > caps.max_image_count {
|
||||
image_count = caps.max_image_count;
|
||||
}
|
||||
|
||||
let extent = if caps.current_extent.width != u32::MAX {
|
||||
caps.current_extent
|
||||
} else {
|
||||
VkExtent2D { width, height }
|
||||
};
|
||||
|
||||
let image_usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT
|
||||
| (caps.supported_usage_flags & VK_IMAGE_USAGE_TRANSFER_DST_BIT);
|
||||
self.image_usage = image_usage;
|
||||
|
||||
let mut mode_count: u32 = 0;
|
||||
unsafe {
|
||||
(self.fns.get_physical_device_surface_present_modes_khr)(
|
||||
self.phys_device,
|
||||
self.surface,
|
||||
&mut mode_count,
|
||||
ptr::null_mut(),
|
||||
)
|
||||
};
|
||||
let mut modes = vec![0i32; mode_count as usize];
|
||||
if mode_count > 0 {
|
||||
unsafe {
|
||||
(self.fns.get_physical_device_surface_present_modes_khr)(
|
||||
self.phys_device,
|
||||
self.surface,
|
||||
&mut mode_count,
|
||||
modes.as_mut_ptr(),
|
||||
)
|
||||
};
|
||||
}
|
||||
let present_mode = if modes.contains(&VK_PRESENT_MODE_MAILBOX_KHR) {
|
||||
VK_PRESENT_MODE_MAILBOX_KHR
|
||||
} else {
|
||||
VK_PRESENT_MODE_FIFO_KHR
|
||||
};
|
||||
|
||||
let old_swapchain = self.swapchain;
|
||||
let create_info = VkSwapchainCreateInfoKHR {
|
||||
s_type: VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR,
|
||||
p_next: ptr::null(),
|
||||
flags: 0,
|
||||
surface: self.surface,
|
||||
min_image_count: image_count,
|
||||
image_format: chosen_format.format,
|
||||
image_color_space: chosen_format.color_space,
|
||||
image_extent: extent,
|
||||
image_array_layers: 1,
|
||||
image_usage,
|
||||
image_sharing_mode: VK_SHARING_MODE_EXCLUSIVE,
|
||||
queue_family_index_count: 0,
|
||||
p_queue_family_indices: ptr::null(),
|
||||
pre_transform: caps.current_transform,
|
||||
composite_alpha: VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR,
|
||||
present_mode,
|
||||
clipped: 1,
|
||||
old_swapchain,
|
||||
};
|
||||
|
||||
let mut swapchain: VkSwapchainKHR = 0;
|
||||
let result = unsafe {
|
||||
(self.fns.create_swapchain_khr)(self.device, &create_info, ptr::null(), &mut swapchain)
|
||||
};
|
||||
if old_swapchain != 0 {
|
||||
unsafe { (self.fns.destroy_swapchain_khr)(self.device, old_swapchain, ptr::null()) };
|
||||
}
|
||||
if result != VK_SUCCESS {
|
||||
return Err(format!("vkCreateSwapchainKHR failed: VkResult {result}"));
|
||||
}
|
||||
|
||||
let mut image_count_out: u32 = 0;
|
||||
unsafe {
|
||||
(self.fns.get_swapchain_images_khr)(
|
||||
self.device,
|
||||
swapchain,
|
||||
&mut image_count_out,
|
||||
ptr::null_mut(),
|
||||
)
|
||||
};
|
||||
let mut images = vec![0u64; image_count_out as usize];
|
||||
unsafe {
|
||||
(self.fns.get_swapchain_images_khr)(
|
||||
self.device,
|
||||
swapchain,
|
||||
&mut image_count_out,
|
||||
images.as_mut_ptr(),
|
||||
)
|
||||
};
|
||||
|
||||
self.swapchain = swapchain;
|
||||
self.images = images;
|
||||
self.image_format = chosen_format.format;
|
||||
self.extent = extent;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn resize(&mut self, width: i32, height: i32) -> Result<(), String> {
|
||||
let width = width.max(2) as u32;
|
||||
let height = height.max(2) as u32;
|
||||
if self.extent.width == width && self.extent.height == height {
|
||||
return Ok(());
|
||||
}
|
||||
unsafe { (self.fns.device_wait_idle)(self.device) };
|
||||
self.create_swapchain(width, height)
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue