fluxa-desktop/src-tauri/src/linux_vulkan.rs
KhooLy f5aedac0b5 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.
2026-07-30 03:35:14 +03:00

521 lines
15 KiB
Rust

use std::ffi::{c_void, CString};
use std::ptr;
use std::sync::atomic::{AtomicBool, Ordering};
type VkInstance = *mut c_void;
type VkPhysicalDevice = *mut c_void;
type VkDevice = *mut c_void;
type VkQueue = *mut c_void;
type VkSurfaceKHR = u64;
type VkSwapchainKHR = u64;
type VkSemaphore = u64;
type VkImage = u64;
type VkFence = u64;
type VkCommandPool = *mut c_void;
type VkCommandBuffer = *mut c_void;
type VkResult = i32;
const VK_SUCCESS: VkResult = 0;
const VK_TIMEOUT: VkResult = 2;
const VK_SUBOPTIMAL_KHR: VkResult = 1000001003;
const VK_STRUCTURE_TYPE_APPLICATION_INFO: i32 = 0;
const VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO: i32 = 1;
const VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO: i32 = 2;
const VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO: i32 = 3;
const VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO: i32 = 9;
const VK_STRUCTURE_TYPE_FENCE_CREATE_INFO: i32 = 8;
const VK_FENCE_CREATE_SIGNALED_BIT: u32 = 1;
const VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR: i32 = 1000001000;
const VK_STRUCTURE_TYPE_PRESENT_INFO_KHR: i32 = 1000001001;
const VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR: i32 = 1000004000;
const VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR: i32 = 1000006000;
const VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TIMELINE_SEMAPHORE_FEATURES: i32 = 1000207000;
const VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SYNCHRONIZATION_2_FEATURES: i32 = 1000314007;
const VK_STRUCTURE_TYPE_SUBMIT_INFO: i32 = 4;
const VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO: i32 = 39;
const VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT: u32 = 2;
const VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO: i32 = 40;
const VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO: i32 = 42;
const VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER: i32 = 45;
const VK_QUEUE_GRAPHICS_BIT: u32 = 0x1;
const VK_IMAGE_USAGE_TRANSFER_DST_BIT: u32 = 0x2;
const VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT: u32 = 0x10;
const VK_FORMAT_B8G8R8A8_UNORM: i32 = 44;
const VK_COLOR_SPACE_SRGB_NONLINEAR_KHR: i32 = 0;
const VK_SHARING_MODE_EXCLUSIVE: i32 = 0;
const VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR: u32 = 0x1;
const VK_PRESENT_MODE_MAILBOX_KHR: i32 = 1;
const VK_PRESENT_MODE_FIFO_KHR: i32 = 2;
const VK_API_VERSION_1_3: u32 = (1 << 22) | (3 << 12);
const VK_IMAGE_LAYOUT_PRESENT_SRC_KHR: i32 = 1000001002;
const VK_IMAGE_ASPECT_COLOR_BIT: u32 = 0x1;
const VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT: u32 = 0x00002000;
const VK_PIPELINE_STAGE_ALL_COMMANDS_BIT: u32 = 0x00010000;
const VK_ACCESS_MEMORY_READ_BIT: u32 = 0x00008000;
const VK_ACCESS_MEMORY_WRITE_BIT: u32 = 0x00010000;
const VK_COMMAND_BUFFER_LEVEL_PRIMARY: i32 = 0;
const VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT: u32 = 0x1;
const VK_QUEUE_FAMILY_IGNORED: u32 = 0xFFFFFFFF;
#[derive(Debug)]
pub enum NativeSurface {
Xlib {
display: *mut c_void,
window: u64,
},
Wayland {
display: *mut c_void,
surface: *mut c_void,
},
}
#[repr(C)]
struct VkApplicationInfo {
s_type: i32,
p_next: *const c_void,
p_application_name: *const i8,
application_version: u32,
p_engine_name: *const i8,
engine_version: u32,
api_version: u32,
}
#[repr(C)]
struct VkInstanceCreateInfo {
s_type: i32,
p_next: *const c_void,
flags: u32,
p_application_info: *const VkApplicationInfo,
enabled_layer_count: u32,
pp_enabled_layer_names: *const *const i8,
enabled_extension_count: u32,
pp_enabled_extension_names: *const *const i8,
}
#[repr(C)]
#[derive(Clone, Copy, Default)]
struct VkExtent2D {
width: u32,
height: u32,
}
#[repr(C)]
struct VkSurfaceCapabilitiesKHR {
min_image_count: u32,
max_image_count: u32,
current_extent: VkExtent2D,
min_image_extent: VkExtent2D,
max_image_extent: VkExtent2D,
max_image_array_layers: u32,
supported_transforms: u32,
current_transform: u32,
supported_composite_alpha: u32,
supported_usage_flags: u32,
}
#[repr(C)]
#[derive(Clone, Copy)]
struct VkSurfaceFormatKHR {
format: i32,
color_space: i32,
}
#[repr(C)]
struct VkQueueFamilyProperties {
queue_flags: u32,
queue_count: u32,
timestamp_valid_bits: u32,
min_image_transfer_granularity: [u32; 3],
}
#[repr(C)]
struct VkDeviceQueueCreateInfo {
s_type: i32,
p_next: *const c_void,
flags: u32,
queue_family_index: u32,
queue_count: u32,
p_queue_priorities: *const f32,
}
#[repr(C)]
struct VkDeviceCreateInfo {
s_type: i32,
p_next: *const c_void,
flags: u32,
queue_create_info_count: u32,
p_queue_create_infos: *const VkDeviceQueueCreateInfo,
enabled_layer_count: u32,
pp_enabled_layer_names: *const *const i8,
enabled_extension_count: u32,
pp_enabled_extension_names: *const *const i8,
p_enabled_features: *const c_void,
}
#[repr(C)]
struct VkPhysicalDeviceTimelineSemaphoreFeatures {
s_type: i32,
p_next: *mut c_void,
timeline_semaphore: u32,
}
#[repr(C)]
struct VkPhysicalDeviceSynchronization2Features {
s_type: i32,
p_next: *mut c_void,
synchronization2: u32,
}
#[repr(C)]
struct VkXlibSurfaceCreateInfoKHR {
s_type: i32,
p_next: *const c_void,
flags: u32,
dpy: *mut c_void,
window: u64,
}
#[repr(C)]
struct VkWaylandSurfaceCreateInfoKHR {
s_type: i32,
p_next: *const c_void,
flags: u32,
display: *mut c_void,
surface: *mut c_void,
}
#[repr(C)]
struct VkSwapchainCreateInfoKHR {
s_type: i32,
p_next: *const c_void,
flags: u32,
surface: VkSurfaceKHR,
min_image_count: u32,
image_format: i32,
image_color_space: i32,
image_extent: VkExtent2D,
image_array_layers: u32,
image_usage: u32,
image_sharing_mode: i32,
queue_family_index_count: u32,
p_queue_family_indices: *const u32,
pre_transform: u32,
composite_alpha: u32,
present_mode: i32,
clipped: u32,
old_swapchain: VkSwapchainKHR,
}
#[repr(C)]
struct VkSemaphoreCreateInfo {
s_type: i32,
p_next: *const c_void,
flags: u32,
}
#[repr(C)]
struct VkFenceCreateInfo {
s_type: i32,
p_next: *const c_void,
flags: u32,
}
#[repr(C)]
struct VkPresentInfoKHR {
s_type: i32,
p_next: *const c_void,
wait_semaphore_count: u32,
p_wait_semaphores: *const VkSemaphore,
swapchain_count: u32,
p_swapchains: *const VkSwapchainKHR,
p_image_indices: *const u32,
p_results: *mut VkResult,
}
#[repr(C)]
#[derive(Clone, Copy)]
struct VkImageSubresourceRange {
aspect_mask: u32,
base_mip_level: u32,
level_count: u32,
base_array_layer: u32,
layer_count: u32,
}
#[repr(C)]
struct VkImageMemoryBarrier {
s_type: i32,
p_next: *const c_void,
src_access_mask: u32,
dst_access_mask: u32,
old_layout: i32,
new_layout: i32,
src_queue_family_index: u32,
dst_queue_family_index: u32,
image: VkImage,
subresource_range: VkImageSubresourceRange,
}
#[repr(C)]
struct VkCommandPoolCreateInfo {
s_type: i32,
p_next: *const c_void,
flags: u32,
queue_family_index: u32,
}
#[repr(C)]
struct VkCommandBufferAllocateInfo {
s_type: i32,
p_next: *const c_void,
command_pool: VkCommandPool,
level: i32,
command_buffer_count: u32,
}
#[repr(C)]
struct VkCommandBufferBeginInfo {
s_type: i32,
p_next: *const c_void,
flags: u32,
p_inheritance_info: *const c_void,
}
#[repr(C)]
struct VkSubmitInfo {
s_type: i32,
p_next: *const c_void,
wait_semaphore_count: u32,
p_wait_semaphores: *const VkSemaphore,
p_wait_dst_stage_mask: *const u32,
command_buffer_count: u32,
p_command_buffers: *const VkCommandBuffer,
signal_semaphore_count: u32,
p_signal_semaphores: *const VkSemaphore,
}
type PfnGetInstanceProcAddr =
unsafe extern "system" fn(instance: VkInstance, name: *const i8) -> *mut c_void;
type PfnCreateInstance = unsafe extern "system" fn(
*const VkInstanceCreateInfo,
*const c_void,
*mut VkInstance,
) -> VkResult;
type PfnDestroyInstance = unsafe extern "system" fn(VkInstance, *const c_void);
type PfnEnumeratePhysicalDevices =
unsafe extern "system" fn(VkInstance, *mut u32, *mut VkPhysicalDevice) -> VkResult;
type PfnEnumerateDeviceExtensionProperties = unsafe extern "system" fn(
VkPhysicalDevice,
*const i8,
*mut u32,
*mut VkExtensionProperties,
) -> VkResult;
#[repr(C)]
#[derive(Clone, Copy)]
struct VkExtensionProperties {
extension_name: [u8; 256],
spec_version: u32,
}
type PfnGetPhysicalDeviceProperties = unsafe extern "system" fn(VkPhysicalDevice, *mut c_void);
type PfnGetPhysicalDeviceQueueFamilyProperties =
unsafe extern "system" fn(VkPhysicalDevice, *mut u32, *mut VkQueueFamilyProperties);
type PfnGetPhysicalDeviceSurfaceSupportKHR =
unsafe extern "system" fn(VkPhysicalDevice, u32, VkSurfaceKHR, *mut u32) -> VkResult;
type PfnGetPhysicalDeviceSurfaceCapabilitiesKHR = unsafe extern "system" fn(
VkPhysicalDevice,
VkSurfaceKHR,
*mut VkSurfaceCapabilitiesKHR,
) -> VkResult;
type PfnGetPhysicalDeviceSurfacePresentModesKHR =
unsafe extern "system" fn(VkPhysicalDevice, VkSurfaceKHR, *mut u32, *mut i32) -> VkResult;
type PfnGetPhysicalDeviceSurfaceFormatsKHR = unsafe extern "system" fn(
VkPhysicalDevice,
VkSurfaceKHR,
*mut u32,
*mut VkSurfaceFormatKHR,
) -> VkResult;
type PfnCreateXlibSurfaceKHR = unsafe extern "system" fn(
VkInstance,
*const VkXlibSurfaceCreateInfoKHR,
*const c_void,
*mut VkSurfaceKHR,
) -> VkResult;
type PfnCreateWaylandSurfaceKHR = unsafe extern "system" fn(
VkInstance,
*const VkWaylandSurfaceCreateInfoKHR,
*const c_void,
*mut VkSurfaceKHR,
) -> VkResult;
type PfnDestroySurfaceKHR = unsafe extern "system" fn(VkInstance, VkSurfaceKHR, *const c_void);
type PfnCreateDevice = unsafe extern "system" fn(
VkPhysicalDevice,
*const VkDeviceCreateInfo,
*const c_void,
*mut VkDevice,
) -> VkResult;
type PfnDestroyDevice = unsafe extern "system" fn(VkDevice, *const c_void);
type PfnGetDeviceQueue = unsafe extern "system" fn(VkDevice, u32, u32, *mut VkQueue);
type PfnCreateSwapchainKHR = unsafe extern "system" fn(
VkDevice,
*const VkSwapchainCreateInfoKHR,
*const c_void,
*mut VkSwapchainKHR,
) -> VkResult;
type PfnDestroySwapchainKHR = unsafe extern "system" fn(VkDevice, VkSwapchainKHR, *const c_void);
type PfnGetSwapchainImagesKHR =
unsafe extern "system" fn(VkDevice, VkSwapchainKHR, *mut u32, *mut VkImage) -> VkResult;
type PfnAcquireNextImageKHR = unsafe extern "system" fn(
VkDevice,
VkSwapchainKHR,
u64,
VkSemaphore,
*mut c_void,
*mut u32,
) -> VkResult;
type PfnQueuePresentKHR = unsafe extern "system" fn(VkQueue, *const VkPresentInfoKHR) -> VkResult;
type PfnCreateSemaphore = unsafe extern "system" fn(
VkDevice,
*const VkSemaphoreCreateInfo,
*const c_void,
*mut VkSemaphore,
) -> VkResult;
type PfnDestroySemaphore = unsafe extern "system" fn(VkDevice, VkSemaphore, *const c_void);
type PfnCreateFence = unsafe extern "system" fn(
VkDevice,
*const VkFenceCreateInfo,
*const c_void,
*mut VkFence,
) -> VkResult;
type PfnDestroyFence = unsafe extern "system" fn(VkDevice, VkFence, *const c_void);
type PfnWaitForFences =
unsafe extern "system" fn(VkDevice, u32, *const VkFence, u32, u64) -> VkResult;
type PfnResetFences = unsafe extern "system" fn(VkDevice, u32, *const VkFence) -> VkResult;
type PfnDeviceWaitIdle = unsafe extern "system" fn(VkDevice) -> VkResult;
type PfnCreateCommandPool = unsafe extern "system" fn(
VkDevice,
*const VkCommandPoolCreateInfo,
*const c_void,
*mut VkCommandPool,
) -> VkResult;
type PfnDestroyCommandPool = unsafe extern "system" fn(VkDevice, VkCommandPool, *const c_void);
type PfnAllocateCommandBuffers = unsafe extern "system" fn(
VkDevice,
*const VkCommandBufferAllocateInfo,
*mut VkCommandBuffer,
) -> VkResult;
type PfnResetCommandBuffer = unsafe extern "system" fn(VkCommandBuffer, u32) -> VkResult;
type PfnBeginCommandBuffer =
unsafe extern "system" fn(VkCommandBuffer, *const VkCommandBufferBeginInfo) -> VkResult;
type PfnEndCommandBuffer = unsafe extern "system" fn(VkCommandBuffer) -> VkResult;
type PfnCmdPipelineBarrier = unsafe extern "system" fn(
VkCommandBuffer,
u32,
u32,
u32,
u32,
*const c_void,
u32,
*const c_void,
u32,
*const VkImageMemoryBarrier,
);
type PfnQueueSubmit =
unsafe extern "system" fn(VkQueue, u32, *const VkSubmitInfo, VkFence) -> VkResult;
struct VkFns {
get_instance_proc_addr: PfnGetInstanceProcAddr,
destroy_instance: PfnDestroyInstance,
get_physical_device_surface_capabilities_khr: PfnGetPhysicalDeviceSurfaceCapabilitiesKHR,
get_physical_device_surface_formats_khr: PfnGetPhysicalDeviceSurfaceFormatsKHR,
get_physical_device_surface_present_modes_khr: PfnGetPhysicalDeviceSurfacePresentModesKHR,
destroy_surface_khr: PfnDestroySurfaceKHR,
destroy_device: PfnDestroyDevice,
create_swapchain_khr: PfnCreateSwapchainKHR,
destroy_swapchain_khr: PfnDestroySwapchainKHR,
get_swapchain_images_khr: PfnGetSwapchainImagesKHR,
acquire_next_image_khr: PfnAcquireNextImageKHR,
queue_present_khr: PfnQueuePresentKHR,
create_semaphore: PfnCreateSemaphore,
destroy_semaphore: PfnDestroySemaphore,
create_fence: PfnCreateFence,
destroy_fence: PfnDestroyFence,
wait_for_fences: PfnWaitForFences,
reset_fences: PfnResetFences,
device_wait_idle: PfnDeviceWaitIdle,
create_command_pool: PfnCreateCommandPool,
destroy_command_pool: PfnDestroyCommandPool,
allocate_command_buffers: PfnAllocateCommandBuffers,
reset_command_buffer: PfnResetCommandBuffer,
begin_command_buffer: PfnBeginCommandBuffer,
end_command_buffer: PfnEndCommandBuffer,
cmd_pipeline_barrier: PfnCmdPipelineBarrier,
queue_submit: PfnQueueSubmit,
}
fn dlopen_first(names: &[&str]) -> Option<isize> {
for name in names {
let cname = CString::new(*name).ok()?;
let handle = unsafe { libc::dlopen(cname.as_ptr(), libc::RTLD_NOW | libc::RTLD_LOCAL) };
if !handle.is_null() {
return Some(handle as isize);
}
}
None
}
fn dlsym_typed(module: isize, name: &str) -> Result<*mut c_void, String> {
let cname = CString::new(name).map_err(|e| e.to_string())?;
let addr = unsafe { libc::dlsym(module as *mut c_void, cname.as_ptr()) };
if addr.is_null() {
Err(format!("libvulkan.so is missing {name}"))
} else {
Ok(addr)
}
}
fn get_instance_proc(
get_instance_proc_addr: PfnGetInstanceProcAddr,
instance: VkInstance,
name: &str,
) -> Result<*mut c_void, String> {
let cname = CString::new(name).unwrap();
let addr = unsafe { get_instance_proc_addr(instance, cname.as_ptr()) };
if addr.is_null() {
Err(format!("vkGetInstanceProcAddr could not resolve {name}"))
} else {
Ok(addr)
}
}
pub struct VulkanContext {
fns: VkFns,
instance: VkInstance,
phys_device: VkPhysicalDevice,
device: VkDevice,
queue: VkQueue,
queue_family_index: u32,
surface: VkSurfaceKHR,
swapchain: VkSwapchainKHR,
images: Vec<VkImage>,
image_format: i32,
image_usage: u32,
extent: VkExtent2D,
acquire_semaphore: VkSemaphore,
render_done_semaphore: VkSemaphore,
transition_semaphore: VkSemaphore,
in_flight_fence: VkFence,
command_pool: VkCommandPool,
command_buffer: VkCommandBuffer,
hdr: AtomicBool,
enabled_device_extensions: Vec<CString>,
owned_xlib_display: *mut c_void,
}
unsafe impl Send for VulkanContext {}
mod init;
mod render;
mod swapchain;