From 4af14c9278171b1c5ee1878392b72cde9cc85791 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20H=C3=BCbner?= Date: Wed, 11 Jan 2023 15:03:32 +0100 Subject: [PATCH 01/85] Load symbols from files --- Cargo.toml | 4 +- src/device_handles.rs | 65 ------ src/instance_handles.rs | 86 -------- src/lib.rs | 446 +++------------------------------------- src/vk_handles.rs | 90 ++++++++ 5 files changed, 117 insertions(+), 574 deletions(-) delete mode 100644 src/device_handles.rs delete mode 100644 src/instance_handles.rs create mode 100644 src/vk_handles.rs diff --git a/Cargo.toml b/Cargo.toml index 05811ec..97dd5e9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,5 +9,5 @@ edition = "2021" crate-type = ["cdylib"] [dependencies] -vulkan-sys = { path = "/home/michael/Dokumente/Workspace/Gavania/vulkan-sys" } -# vulkan_sys = { git = "ssh://gitea@gavania.de:23/Gavania/Gavania.git", branch = "0.2.0_dev" } \ No newline at end of file +vulkan-sys = { git = "https://gavania.de/Gavania/Gavania.git", branch = "0.2.0_dev" } +anyhow = { version = "1.0.68", features = ["backtrace"] } \ No newline at end of file diff --git a/src/device_handles.rs b/src/device_handles.rs deleted file mode 100644 index 9338bc3..0000000 --- a/src/device_handles.rs +++ /dev/null @@ -1,65 +0,0 @@ -use crate::instance_handles::instance_fns; -use vulkan_sys::prelude::*; - -static mut DEVICE_FN_HANDLES: Option = None; - -pub fn is_device_fns_initialized() -> bool { - unsafe { DEVICE_FN_HANDLES.is_some() } -} - -pub fn device_fns() -> &'static VkDeviceHandles { - unsafe { DEVICE_FN_HANDLES.as_ref().unwrap() } -} - -pub fn set_device_fns(handles: VkDeviceHandles) { - unsafe { DEVICE_FN_HANDLES = Some(handles) }; -} - -pub struct VkDeviceHandles { - pub device: VkDevice, - - pub device_functions: DeviceFunctions, - pub device_wsi_functions: DeviceWSIFunctions, - pub maintenance3_functions: Maintenance3Functions, - - pub acceleration_structure_functions: AccelerationStructureFunctions, - pub ray_tracing_pipeline_functions: RayTracingPipelineFunctions, - - pub deferred_operation_functions: DeferredOperationsFunctions, -} - -impl VkDeviceHandles { - pub fn new(device: VkDevice) -> Self { - let instance = &instance_fns().instance_functions; - - Self { - device, - - device_functions: DeviceFunctions::new(instance, device), - device_wsi_functions: DeviceWSIFunctions::new(instance, device), - maintenance3_functions: Maintenance3Functions::new(instance, device), - acceleration_structure_functions: AccelerationStructureFunctions::new(instance, device), - ray_tracing_pipeline_functions: RayTracingPipelineFunctions::new(instance, device), - deferred_operation_functions: DeferredOperationsFunctions::new(instance, device), - } - } -} - -macro_rules! cmp_device_fn { - ($name:ident, {$([$fns:ident, $fn_name:ident],)*}) => { - match $name { - $( - stringify!($fn_name) => return unsafe { - mem::transmute( - device_fns() - .$fns - .$fn_name, - ) - }, - )* - - _ => () - } - - } -} diff --git a/src/instance_handles.rs b/src/instance_handles.rs deleted file mode 100644 index 5f0efef..0000000 --- a/src/instance_handles.rs +++ /dev/null @@ -1,86 +0,0 @@ -use vulkan_sys::prelude::*; - -static mut INSTANCE_FN_HANDLES: Option = None; - -pub fn is_instance_fns_initialized() -> bool { - unsafe { INSTANCE_FN_HANDLES.is_some() } -} - -pub fn instance_fns() -> &'static VkInstanceHandles { - unsafe { INSTANCE_FN_HANDLES.as_ref().unwrap() } -} - -pub fn set_instance_fns(handles: VkInstanceHandles) { - unsafe { INSTANCE_FN_HANDLES = Some(handles) }; -} - -pub fn replace_device_proc_addr(proc_addr: PFN_vkGetDeviceProcAddr) { - unsafe { - INSTANCE_FN_HANDLES - .as_mut() - .unwrap() - .instance_functions - .vkGetDeviceProcAddr = proc_addr - }; -} - -pub struct VkInstanceHandles { - pub instance: VkInstance, - - pub static_functions: StaticFunctions, - pub entry_functions: EntryFunctions, - - pub instance_functions: InstanceFunctions, - pub instance_wsi_functions: InstanceWSIFunctions, - pub physical_device_properties2_functions: PhysicalDeviceProperties2Functions, - pub debug_report_callback_functions: DebugReportCallbackFunctions, - pub debug_utils_messenger_functions: DebugUtilsMessengerFunctions, -} - -impl VkInstanceHandles { - pub fn load_instance( - static_functions: StaticFunctions, - entry_functions: EntryFunctions, - instance: VkInstance, - ) -> Self { - Self { - instance_functions: InstanceFunctions::new(&static_functions, instance), - instance_wsi_functions: InstanceWSIFunctions::new(&static_functions, instance), - physical_device_properties2_functions: PhysicalDeviceProperties2Functions::new( - &static_functions, - instance, - ), - debug_report_callback_functions: DebugReportCallbackFunctions::new( - &static_functions, - instance, - ), - debug_utils_messenger_functions: DebugUtilsMessengerFunctions::new( - &static_functions, - instance, - ), - - instance, - static_functions, - entry_functions, - } - } -} - -macro_rules! cmp_instance_fn { - ($name:ident, {$([$fns:ident, $fn_name:ident],)*}) => { - match $name { - $( - stringify!($fn_name) => return unsafe { - mem::transmute( - instance_fns() - .$fns - .$fn_name, - ) - }, - )* - - _ => () - } - - } -} diff --git a/src/lib.rs b/src/lib.rs index 2c42abd..68a8035 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,14 +1,9 @@ pub mod enums; pub mod structs; -#[macro_use] -mod instance_handles; - -#[macro_use] -mod device_handles; +mod vk_handles; use std::{ - ffi::c_void, fs::{File, OpenOptions}, io::Write, mem, @@ -16,10 +11,9 @@ use std::{ ptr, }; -use device_handles::*; use enums::*; -use instance_handles::*; use structs::*; +use vk_handles::*; use vulkan_sys::prelude::*; const LOG_FILE: &'static str = "/home/michael/rf2_vk_hud.log"; @@ -46,6 +40,16 @@ pub extern "C" fn vkNegotiateLoaderLayerInterfaceVersion( } }; + set_vk_handles( + VkTypedefHandles::new(&[ + "vulkan_symbols/vulkan_core_symbols", + "vulkan_symbols/vulkan_wayland_symbols", + "vulkan_symbols/vulkan_xcb_symbols", + "vulkan_symbols/vulkan_xlib_symbols", + ]) + .unwrap(), + ); + VK_SUCCESS } @@ -64,187 +68,6 @@ extern "system" fn get_device_proc_addr( let s = func_string.as_str(); - cmp_instance_fn!(s, { - [debug_utils_messenger_functions, vkCreateDebugUtilsMessengerEXT], - [debug_utils_messenger_functions, vkDestroyDebugUtilsMessengerEXT], - [debug_report_callback_functions, vkCreateDebugReportCallbackEXT], - [debug_report_callback_functions, vkDestroyDebugReportCallbackEXT], - [instance_functions, vkEnumeratePhysicalDevices], - [instance_functions, vkGetPhysicalDeviceFeatures], - [instance_functions, vkGetPhysicalDeviceFormatProperties], - [instance_functions, vkGetPhysicalDeviceImageFormatProperties], - [instance_functions, vkGetPhysicalDeviceProperties], - [instance_functions, vkGetPhysicalDeviceQueueFamilyProperties], - [instance_functions, vkGetPhysicalDeviceMemoryProperties], - [instance_functions, vkEnumerateDeviceExtensionProperties], - // [instance_functions, vkEnumerateDeviceLayerProperties], - [instance_functions, vkGetPhysicalDeviceSparseImageFormatProperties], - // [instance_functions, vkEnumeratePhysicalDeviceGroups], - [physical_device_properties2_functions, vkGetPhysicalDeviceFeatures2KHR], - [physical_device_properties2_functions, vkGetPhysicalDeviceProperties2KHR], - [physical_device_properties2_functions, vkGetPhysicalDeviceFormatProperties2KHR], - [physical_device_properties2_functions, vkGetPhysicalDeviceFeatures2KHR], - [physical_device_properties2_functions, vkGetPhysicalDeviceFeatures2KHR], - [instance_wsi_functions, vkDestroySurfaceKHR], - [instance_wsi_functions, vkGetPhysicalDeviceSurfaceSupportKHR], - [instance_wsi_functions, vkGetPhysicalDeviceSurfaceCapabilitiesKHR], - [instance_wsi_functions, vkGetPhysicalDeviceSurfaceFormatsKHR], - [instance_wsi_functions, vkGetPhysicalDeviceSurfacePresentModesKHR], - [instance_wsi_functions, vkGetPhysicalDeviceDisplayPropertiesKHR], - [instance_wsi_functions, vkGetPhysicalDeviceDisplayPlanePropertiesKHR], - [instance_wsi_functions, vkGetDisplayPlaneSupportedDisplaysKHR], - [instance_wsi_functions, vkGetDisplayModePropertiesKHR], - [instance_wsi_functions, vkCreateDisplayModeKHR], - [instance_wsi_functions, vkGetDisplayPlaneCapabilitiesKHR], - [instance_wsi_functions, vkCreateDisplayPlaneSurfaceKHR], - [instance_wsi_functions, vkCreateXlibSurfaceKHR], - [instance_wsi_functions, vkGetPhysicalDeviceXlibPresentationSupportKHR], - [instance_wsi_functions, vkCreateXcbSurfaceKHR], - [instance_wsi_functions, vkGetPhysicalDeviceXcbPresentationSupportKHR], - [instance_wsi_functions, vkCreateWaylandSurfaceKHR], - [instance_wsi_functions, vkGetPhysicalDeviceWaylandPresentationSupportKHR], - } - ); - - cmp_device_fn!(s, { - [device_functions, vkGetDeviceQueue], - [device_functions, vkQueueSubmit], - [device_functions, vkQueueWaitIdle], - [device_functions, vkDeviceWaitIdle], - [device_functions, vkAllocateMemory], - [device_functions, vkFreeMemory], - [device_functions, vkMapMemory], - [device_functions, vkUnmapMemory], - [device_functions, vkBindBufferMemory], - [device_functions, vkBindImageMemory], - [device_functions, vkGetBufferMemoryRequirements], - [device_functions, vkGetImageMemoryRequirements], - [device_functions, vkCreateFence], - [device_functions, vkDestroyFence], - [device_functions, vkResetFences], - [device_functions, vkWaitForFences], - [device_functions, vkCreateSemaphore], - [device_functions, vkDestroySemaphore], - [device_functions, vkCreateEvent], - [device_functions, vkDestroyEvent], - [device_functions, vkGetEventStatus], - [device_functions, vkSetEvent], - [device_functions, vkResetEvent], - [device_functions, vkCreateBuffer], - [device_functions, vkDestroyBuffer], - [device_functions, vkCreateBufferView], - [device_functions, vkDestroyBufferView], - [device_functions, vkCreateImage], - [device_functions, vkDestroyImage], - [device_functions, vkGetImageSubresourceLayout], - [device_functions, vkCreateImageView], - [device_functions, vkDestroyImageView], - [device_functions, vkCreateShaderModule], - [device_functions, vkDestroyShaderModule], - [device_functions, vkCreatePipelineCache], - [device_functions, vkDestroyPipelineCache], - [device_functions, vkGetPipelineCacheData], - [device_functions, vkMergePipelineCaches], - [device_functions, vkCreateGraphicsPipelines], - [device_functions, vkCreateComputePipelines], - [device_functions, vkDestroyPipeline], - [device_functions, vkCreatePipelineLayout], - [device_functions, vkDestroyPipelineLayout], - [device_functions, vkCreateSampler], - [device_functions, vkDestroySampler], - [device_functions, vkCreateDescriptorSetLayout], - [device_functions, vkDestroyDescriptorSetLayout], - [device_functions, vkCreateDescriptorPool], - [device_functions, vkDestroyDescriptorPool], - [device_functions, vkResetDescriptorPool], - [device_functions, vkAllocateDescriptorSets], - [device_functions, vkFreeDescriptorSets], - [device_functions, vkUpdateDescriptorSets], - [device_functions, vkCreateFramebuffer], - [device_functions, vkDestroyFramebuffer], - [device_functions, vkCreateRenderPass], - [device_functions, vkDestroyRenderPass], - [device_functions, vkCreateCommandPool], - [device_functions, vkDestroyCommandPool], - [device_functions, vkResetCommandPool], - [device_functions, vkAllocateCommandBuffers], - [device_functions, vkFreeCommandBuffers], - [device_functions, vkBeginCommandBuffer], - [device_functions, vkEndCommandBuffer], - [device_functions, vkResetCommandBuffer], - [device_functions, vkCmdBindPipeline], - [device_functions, vkCmdSetViewport], - [device_functions, vkCmdSetScissor], - [device_functions, vkCmdSetLineWidth], - [device_functions, vkCmdSetDepthBias], - [device_functions, vkCmdSetBlendConstants], - [device_functions, vkCmdSetDepthBounds], - [device_functions, vkCmdSetStencilCompareMask], - [device_functions, vkCmdSetStencilWriteMask], - [device_functions, vkCmdSetStencilReference], - [device_functions, vkCmdBindDescriptorSets], - [device_functions, vkCmdBindIndexBuffer], - [device_functions, vkCmdBindVertexBuffers], - [device_functions, vkCmdDraw], - [device_functions, vkCmdDispatch], - [device_functions, vkCmdCopyBuffer], - [device_functions, vkCmdCopyImage], - [device_functions, vkCmdBlitImage], - [device_functions, vkCmdCopyBufferToImage], - [device_functions, vkCmdCopyImageToBuffer], - [device_functions, vkCmdUpdateBuffer], - [device_functions, vkCmdFillBuffer], - [device_functions, vkCmdClearColorImage], - [device_functions, vkCmdClearDepthStencilImage], - [device_functions, vkCmdClearAttachments], - [device_functions, vkCmdResolveImage], - [device_functions, vkCmdSetEvent], - [device_functions, vkCmdResetEvent], - [device_functions, vkCmdWaitEvents], - [device_functions, vkCmdPipelineBarrier], - [device_functions, vkCmdPushConstants], - [device_functions, vkCmdBeginRenderPass], - [device_functions, vkCmdNextSubpass], - [device_functions, vkCmdEndRenderPass], - [device_functions, vkCmdExecuteCommands], - [device_functions, vkTrimCommandPool], - [device_functions, vkGetBufferDeviceAddress], - [device_functions, vkGetBufferMemoryRequirements2], - [device_functions, vkGetImageMemoryRequirements2], - [device_functions, vkBindBufferMemory2], - [device_functions, vkBindImageMemory2], - [device_wsi_functions, vkCreateSwapchainKHR], - [device_wsi_functions, vkDestroySwapchainKHR], - [device_wsi_functions, vkAcquireNextImageKHR], - [device_wsi_functions, vkQueuePresentKHR], - [device_wsi_functions, vkGetSwapchainImagesKHR], - [acceleration_structure_functions, vkBuildAccelerationStructuresKHR], - [acceleration_structure_functions, vkCmdBuildAccelerationStructuresIndirectKHR], - [acceleration_structure_functions, vkCmdBuildAccelerationStructuresKHR], - [acceleration_structure_functions, vkCmdCopyAccelerationStructureKHR], - [acceleration_structure_functions, vkCmdCopyAccelerationStructureToMemoryKHR], - [acceleration_structure_functions, vkCmdCopyMemoryToAccelerationStructureKHR], - [acceleration_structure_functions, vkCmdWriteAccelerationStructuresPropertiesKHR], - [acceleration_structure_functions, vkCopyAccelerationStructureKHR], - [acceleration_structure_functions, vkCopyAccelerationStructureToMemoryKHR], - [acceleration_structure_functions, vkCopyMemoryToAccelerationStructureKHR], - [acceleration_structure_functions, vkCreateAccelerationStructureKHR], - [acceleration_structure_functions, vkDestroyAccelerationStructureKHR], - [acceleration_structure_functions, vkGetAccelerationStructureBuildSizesKHR], - [acceleration_structure_functions, vkGetAccelerationStructureDeviceAddressKHR], - [acceleration_structure_functions, vkGetDeviceAccelerationStructureCompatibilityKHR], - [acceleration_structure_functions, vkWriteAccelerationStructuresPropertiesKHR], - [ray_tracing_pipeline_functions, vkCmdSetRayTracingPipelineStackSizeKHR], - [ray_tracing_pipeline_functions, vkCmdTraceRaysIndirectKHR], - [ray_tracing_pipeline_functions, vkCmdTraceRaysKHR], - [ray_tracing_pipeline_functions, vkCreateRayTracingPipelinesKHR], - [ray_tracing_pipeline_functions, vkGetRayTracingCaptureReplayShaderGroupHandlesKHR], - [ray_tracing_pipeline_functions, vkGetRayTracingShaderGroupHandlesKHR], - [ray_tracing_pipeline_functions, vkGetRayTracingShaderGroupStackSizeKHR], - [maintenance3_functions, vkGetDescriptorSetLayoutSupport], - } - ); - match s { "vkCreateDevice" => return Functions::CreateDevice(create_device).convert(), "vkDestroyDevice" => return Functions::DestroyDevice(destroy_device).convert(), @@ -254,18 +77,6 @@ extern "system" fn get_device_proc_addr( _ => (), }; - unsafe { - if is_device_fns_initialized() { - let device_fn = instance_fns() - .instance_functions - .vkGetDeviceProcAddr(device_fns().device, func_string.as_ptr()); - - if mem::transmute::(device_fn) != ptr::null() { - return device_fn; - } - } - } - write_log(format!("\trequested fn: {} in device proc addr", s)); write_log(format!("\t-> not found")); Functions::Null.convert() @@ -286,187 +97,6 @@ extern "system" fn get_instance_proc_addr( let s = func_string.as_str(); - cmp_instance_fn!(s, { - [debug_utils_messenger_functions, vkCreateDebugUtilsMessengerEXT], - [debug_utils_messenger_functions, vkDestroyDebugUtilsMessengerEXT], - [debug_report_callback_functions, vkCreateDebugReportCallbackEXT], - [debug_report_callback_functions, vkDestroyDebugReportCallbackEXT], - [instance_functions, vkEnumeratePhysicalDevices], - [instance_functions, vkGetPhysicalDeviceFeatures], - [instance_functions, vkGetPhysicalDeviceFormatProperties], - [instance_functions, vkGetPhysicalDeviceImageFormatProperties], - [instance_functions, vkGetPhysicalDeviceProperties], - [instance_functions, vkGetPhysicalDeviceQueueFamilyProperties], - [instance_functions, vkGetPhysicalDeviceMemoryProperties], - [instance_functions, vkEnumerateDeviceExtensionProperties], - // [instance_functions, vkEnumerateDeviceLayerProperties], - [instance_functions, vkGetPhysicalDeviceSparseImageFormatProperties], - // [instance_functions, vkEnumeratePhysicalDeviceGroups], - [physical_device_properties2_functions, vkGetPhysicalDeviceFeatures2KHR], - [physical_device_properties2_functions, vkGetPhysicalDeviceProperties2KHR], - [physical_device_properties2_functions, vkGetPhysicalDeviceFormatProperties2KHR], - [physical_device_properties2_functions, vkGetPhysicalDeviceFeatures2KHR], - [physical_device_properties2_functions, vkGetPhysicalDeviceFeatures2KHR], - [instance_wsi_functions, vkDestroySurfaceKHR], - [instance_wsi_functions, vkGetPhysicalDeviceSurfaceSupportKHR], - [instance_wsi_functions, vkGetPhysicalDeviceSurfaceCapabilitiesKHR], - [instance_wsi_functions, vkGetPhysicalDeviceSurfaceFormatsKHR], - [instance_wsi_functions, vkGetPhysicalDeviceSurfacePresentModesKHR], - [instance_wsi_functions, vkGetPhysicalDeviceDisplayPropertiesKHR], - [instance_wsi_functions, vkGetPhysicalDeviceDisplayPlanePropertiesKHR], - [instance_wsi_functions, vkGetDisplayPlaneSupportedDisplaysKHR], - [instance_wsi_functions, vkGetDisplayModePropertiesKHR], - [instance_wsi_functions, vkCreateDisplayModeKHR], - [instance_wsi_functions, vkGetDisplayPlaneCapabilitiesKHR], - [instance_wsi_functions, vkCreateDisplayPlaneSurfaceKHR], - [instance_wsi_functions, vkCreateXlibSurfaceKHR], - [instance_wsi_functions, vkGetPhysicalDeviceXlibPresentationSupportKHR], - [instance_wsi_functions, vkCreateXcbSurfaceKHR], - [instance_wsi_functions, vkGetPhysicalDeviceXcbPresentationSupportKHR], - [instance_wsi_functions, vkCreateWaylandSurfaceKHR], - [instance_wsi_functions, vkGetPhysicalDeviceWaylandPresentationSupportKHR], - } - ); - - cmp_device_fn!(s, { - [device_functions, vkGetDeviceQueue], - [device_functions, vkQueueSubmit], - [device_functions, vkQueueWaitIdle], - [device_functions, vkDeviceWaitIdle], - [device_functions, vkAllocateMemory], - [device_functions, vkFreeMemory], - [device_functions, vkMapMemory], - [device_functions, vkUnmapMemory], - [device_functions, vkBindBufferMemory], - [device_functions, vkBindImageMemory], - [device_functions, vkGetBufferMemoryRequirements], - [device_functions, vkGetImageMemoryRequirements], - [device_functions, vkCreateFence], - [device_functions, vkDestroyFence], - [device_functions, vkResetFences], - [device_functions, vkWaitForFences], - [device_functions, vkCreateSemaphore], - [device_functions, vkDestroySemaphore], - [device_functions, vkCreateEvent], - [device_functions, vkDestroyEvent], - [device_functions, vkGetEventStatus], - [device_functions, vkSetEvent], - [device_functions, vkResetEvent], - [device_functions, vkCreateBuffer], - [device_functions, vkDestroyBuffer], - [device_functions, vkCreateBufferView], - [device_functions, vkDestroyBufferView], - [device_functions, vkCreateImage], - [device_functions, vkDestroyImage], - [device_functions, vkGetImageSubresourceLayout], - [device_functions, vkCreateImageView], - [device_functions, vkDestroyImageView], - [device_functions, vkCreateShaderModule], - [device_functions, vkDestroyShaderModule], - [device_functions, vkCreatePipelineCache], - [device_functions, vkDestroyPipelineCache], - [device_functions, vkGetPipelineCacheData], - [device_functions, vkMergePipelineCaches], - [device_functions, vkCreateGraphicsPipelines], - [device_functions, vkCreateComputePipelines], - [device_functions, vkDestroyPipeline], - [device_functions, vkCreatePipelineLayout], - [device_functions, vkDestroyPipelineLayout], - [device_functions, vkCreateSampler], - [device_functions, vkDestroySampler], - [device_functions, vkCreateDescriptorSetLayout], - [device_functions, vkDestroyDescriptorSetLayout], - [device_functions, vkCreateDescriptorPool], - [device_functions, vkDestroyDescriptorPool], - [device_functions, vkResetDescriptorPool], - [device_functions, vkAllocateDescriptorSets], - [device_functions, vkFreeDescriptorSets], - [device_functions, vkUpdateDescriptorSets], - [device_functions, vkCreateFramebuffer], - [device_functions, vkDestroyFramebuffer], - [device_functions, vkCreateRenderPass], - [device_functions, vkDestroyRenderPass], - [device_functions, vkCreateCommandPool], - [device_functions, vkDestroyCommandPool], - [device_functions, vkResetCommandPool], - [device_functions, vkAllocateCommandBuffers], - [device_functions, vkFreeCommandBuffers], - [device_functions, vkBeginCommandBuffer], - [device_functions, vkEndCommandBuffer], - [device_functions, vkResetCommandBuffer], - [device_functions, vkCmdBindPipeline], - [device_functions, vkCmdSetViewport], - [device_functions, vkCmdSetScissor], - [device_functions, vkCmdSetLineWidth], - [device_functions, vkCmdSetDepthBias], - [device_functions, vkCmdSetBlendConstants], - [device_functions, vkCmdSetDepthBounds], - [device_functions, vkCmdSetStencilCompareMask], - [device_functions, vkCmdSetStencilWriteMask], - [device_functions, vkCmdSetStencilReference], - [device_functions, vkCmdBindDescriptorSets], - [device_functions, vkCmdBindIndexBuffer], - [device_functions, vkCmdBindVertexBuffers], - [device_functions, vkCmdDraw], - [device_functions, vkCmdDispatch], - [device_functions, vkCmdCopyBuffer], - [device_functions, vkCmdCopyImage], - [device_functions, vkCmdBlitImage], - [device_functions, vkCmdCopyBufferToImage], - [device_functions, vkCmdCopyImageToBuffer], - [device_functions, vkCmdUpdateBuffer], - [device_functions, vkCmdFillBuffer], - [device_functions, vkCmdClearColorImage], - [device_functions, vkCmdClearDepthStencilImage], - [device_functions, vkCmdClearAttachments], - [device_functions, vkCmdResolveImage], - [device_functions, vkCmdSetEvent], - [device_functions, vkCmdResetEvent], - [device_functions, vkCmdWaitEvents], - [device_functions, vkCmdPipelineBarrier], - [device_functions, vkCmdPushConstants], - [device_functions, vkCmdBeginRenderPass], - [device_functions, vkCmdNextSubpass], - [device_functions, vkCmdEndRenderPass], - [device_functions, vkCmdExecuteCommands], - [device_functions, vkTrimCommandPool], - [device_functions, vkGetBufferDeviceAddress], - [device_functions, vkGetBufferMemoryRequirements2], - [device_functions, vkGetImageMemoryRequirements2], - [device_functions, vkBindBufferMemory2], - [device_functions, vkBindImageMemory2], - [device_wsi_functions, vkCreateSwapchainKHR], - [device_wsi_functions, vkDestroySwapchainKHR], - [device_wsi_functions, vkAcquireNextImageKHR], - [device_wsi_functions, vkQueuePresentKHR], - [device_wsi_functions, vkGetSwapchainImagesKHR], - [acceleration_structure_functions, vkBuildAccelerationStructuresKHR], - [acceleration_structure_functions, vkCmdBuildAccelerationStructuresIndirectKHR], - [acceleration_structure_functions, vkCmdBuildAccelerationStructuresKHR], - [acceleration_structure_functions, vkCmdCopyAccelerationStructureKHR], - [acceleration_structure_functions, vkCmdCopyAccelerationStructureToMemoryKHR], - [acceleration_structure_functions, vkCmdCopyMemoryToAccelerationStructureKHR], - [acceleration_structure_functions, vkCmdWriteAccelerationStructuresPropertiesKHR], - [acceleration_structure_functions, vkCopyAccelerationStructureKHR], - [acceleration_structure_functions, vkCopyAccelerationStructureToMemoryKHR], - [acceleration_structure_functions, vkCopyMemoryToAccelerationStructureKHR], - [acceleration_structure_functions, vkCreateAccelerationStructureKHR], - [acceleration_structure_functions, vkDestroyAccelerationStructureKHR], - [acceleration_structure_functions, vkGetAccelerationStructureBuildSizesKHR], - [acceleration_structure_functions, vkGetAccelerationStructureDeviceAddressKHR], - [acceleration_structure_functions, vkGetDeviceAccelerationStructureCompatibilityKHR], - [acceleration_structure_functions, vkWriteAccelerationStructuresPropertiesKHR], - [ray_tracing_pipeline_functions, vkCmdSetRayTracingPipelineStackSizeKHR], - [ray_tracing_pipeline_functions, vkCmdTraceRaysIndirectKHR], - [ray_tracing_pipeline_functions, vkCmdTraceRaysKHR], - [ray_tracing_pipeline_functions, vkCreateRayTracingPipelinesKHR], - [ray_tracing_pipeline_functions, vkGetRayTracingCaptureReplayShaderGroupHandlesKHR], - [ray_tracing_pipeline_functions, vkGetRayTracingShaderGroupHandlesKHR], - [ray_tracing_pipeline_functions, vkGetRayTracingShaderGroupStackSizeKHR], - [maintenance3_functions, vkGetDescriptorSetLayoutSupport], - } - ); - match s { "vkCreateDevice" => return Functions::CreateDevice(create_device).convert(), "vkDestroyDevice" => return Functions::DestroyDevice(destroy_device).convert(), @@ -476,16 +106,6 @@ extern "system" fn get_instance_proc_addr( _ => (), }; - unsafe { - let instance_fn = instance_fns() - .static_functions - .vkGetInstanceProcAddr(instance_fns().instance, func_string.as_ptr()); - - if mem::transmute::(instance_fn) != ptr::null() { - return instance_fn; - } - } - write_log(format!("\trequested fn: {} in instance proc addr", s)); write_log(format!("\t-> not found")); Functions::Null.convert() @@ -510,24 +130,20 @@ extern "system" fn create_instance( }; let proc_addr = chain_info.layer_info().next_instance_proc_addr; - - let static_functions = StaticFunctions { - _lib: None, - vkGetInstanceProcAddr: proc_addr, + let create_instance: PFN_vkCreateInstance = unsafe { + mem::transmute(proc_addr( + VkInstance::NULL_HANDLE, + VkString::new("vkCreateInstance").as_ptr(), + )) }; - let entry_functions = EntryFunctions::new(&static_functions); chain_info.advance_layer_info(); - let result = unsafe { entry_functions.vkCreateInstance(create_info, allocator, instance) }; + let result = create_instance(create_info, allocator, instance); if result != VK_SUCCESS { return result; }; - set_instance_fns(VkInstanceHandles::load_instance( - static_functions, - entry_functions, - unsafe { *instance }, - )); + vk_handles_mut().load_instance_functions(unsafe { *instance }, proc_addr); write_log("-> successfully created instance."); @@ -536,12 +152,6 @@ extern "system" fn create_instance( extern "system" fn destroy_instance(instance: VkInstance, allocator: *const VkAllocationCallbacks) { write_log(" ================== vulkan layer destroy instance =================="); - - unsafe { - instance_fns() - .instance_functions - .vkDestroyInstance(instance, allocator) - }; } extern "system" fn create_device( @@ -562,34 +172,28 @@ extern "system" fn create_device( } }; - replace_device_proc_addr(chain_info.layer_info().next_device_proc_addr); - let instance = instance_fns(); + let proc_addr = chain_info.layer_info().next_device_proc_addr; chain_info.advance_layer_info(); let result = unsafe { - instance - .instance_functions - .vkCreateDevice(physical_device, create_info, allocator, device) + let create_device: PFN_vkCreateDevice = + mem::transmute(vk_handles().handle("vkCreateDevice").unwrap()); + + create_device(physical_device, create_info, allocator, device) }; if result != VK_SUCCESS { return result; } - set_device_fns(VkDeviceHandles::new(unsafe { *device })); + vk_handles_mut().load_device_functions(unsafe { *device }, proc_addr); VK_SUCCESS } extern "system" fn destroy_device(device: VkDevice, allocator: *const VkAllocationCallbacks) { write_log(" ================== vulkan layer destroy device =================="); - - unsafe { - device_fns() - .device_functions - .vkDestroyDevice(device, allocator) - } } fn write_log(msg: impl ToString) { diff --git a/src/vk_handles.rs b/src/vk_handles.rs new file mode 100644 index 0000000..a81fdb5 --- /dev/null +++ b/src/vk_handles.rs @@ -0,0 +1,90 @@ +use anyhow::Result; +use std::{collections::HashMap, ffi::c_void, fs, mem, path::Path, ptr}; +use vulkan_sys::prelude::*; + +static mut FN_HANDLES: Option = None; + +pub fn vk_handles() -> &'static VkTypedefHandles { + unsafe { FN_HANDLES.as_ref().unwrap() } +} + +pub fn vk_handles_mut() -> &'static mut VkTypedefHandles { + unsafe { FN_HANDLES.as_mut().unwrap() } +} + +pub fn set_vk_handles(handles: VkTypedefHandles) { + unsafe { FN_HANDLES = Some(handles) }; +} + +pub struct VkTypedefHandles { + typedefs: Vec, + functions: HashMap, + instance: VkInstance, + device: VkDevice, +} + +impl VkTypedefHandles { + pub fn new(symbol_files: &[impl AsRef]) -> Result { + let mut symbols = Vec::new(); + + for symbol_file in symbol_files { + let s = fs::read_to_string(symbol_file)?; + + symbols.append(&mut s.lines().map(|s| s.to_string()).collect()); + } + + Ok(Self { + typedefs: symbols, + functions: HashMap::new(), + + instance: VkInstance::NULL_HANDLE, + device: VkDevice::NULL_HANDLE, + }) + } + + pub fn load_instance_functions( + &mut self, + instance: VkInstance, + proc_addr: PFN_vkGetInstanceProcAddr, + ) { + self.instance = instance; + + unsafe { + for symbol in &self.typedefs { + let name = VkString::new(symbol); + let function = proc_addr(instance, name.as_ptr()); + + if mem::transmute::(function) != ptr::null() { + self.functions.insert(symbol.clone(), function); + } + } + } + } + + pub fn load_device_functions(&mut self, device: VkDevice, proc_addr: PFN_vkGetDeviceProcAddr) { + self.device = device; + + unsafe { + for symbol in &self.typedefs { + let name = VkString::new(symbol); + let function = proc_addr(device, name.as_ptr()); + + if mem::transmute::(function) != ptr::null() { + self.functions.insert(symbol.clone(), function); + } + } + } + } + + pub fn handle(&self, symbol_name: impl ToString) -> Option { + self.functions.get(&symbol_name.to_string()).cloned() + } + + pub fn instance(&self) -> VkInstance { + self.instance + } + + pub fn device(&self) -> VkDevice { + self.device + } +} From f1bffacc093598a22b45e7f1645b5510ca7f30b3 Mon Sep 17 00:00:00 2001 From: hodasemi Date: Wed, 11 Jan 2023 18:51:31 +0100 Subject: [PATCH 02/85] Load symbols from vk headers --- .gitignore | 2 ++ build.rs | 43 +++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 34 ++++++++++++++++++++++++---------- src/vk_handles.rs | 13 ++++--------- 4 files changed, 73 insertions(+), 19 deletions(-) create mode 100644 build.rs diff --git a/.gitignore b/.gitignore index 4fffb2f..7aeb431 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ /target /Cargo.lock + +vk_functions \ No newline at end of file diff --git a/build.rs b/build.rs new file mode 100644 index 0000000..cae31dc --- /dev/null +++ b/build.rs @@ -0,0 +1,43 @@ +use std::{fs::File, io::Write, path::Path, process::Command, str::from_utf8}; + +const VK_INCLUDE: &str = "/usr/include/vulkan"; +const VK_HEADER: &[&str] = &[ + "vulkan_core.h", + "vulkan_xcb.h", + "vulkan_xlib.h", + "vulkan_wayland.h", +]; +const FN_PREFIX: &str = "PFN_"; + +fn main() { + let mut fns = Vec::new(); + + for header in VK_HEADER { + Command::new("ctags") + .arg("--help") + .output() + .expect("Failed to execute ctags. Maybe you need to install it first?"); + + let output = Command::new("ctags") + .arg("-x") + .arg("--c-types=t") + .arg(format!("{}/{}", VK_INCLUDE, header)) + .output() + .expect(&format!("failed to open file {}/{}", VK_INCLUDE, header)); + + let output_splits = from_utf8(&output.stdout).unwrap().split_whitespace(); + + for split in output_splits { + if split.starts_with(FN_PREFIX) { + fns.push(split.trim_start_matches(FN_PREFIX).to_string()) + } + } + } + + let path = Path::new("vk_functions"); + let mut file = File::create(path).unwrap(); + + for func in fns { + file.write_all(format!("{}\n", func).as_bytes()).unwrap(); + } +} diff --git a/src/lib.rs b/src/lib.rs index 68a8035..411de48 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -40,15 +40,7 @@ pub extern "C" fn vkNegotiateLoaderLayerInterfaceVersion( } }; - set_vk_handles( - VkTypedefHandles::new(&[ - "vulkan_symbols/vulkan_core_symbols", - "vulkan_symbols/vulkan_wayland_symbols", - "vulkan_symbols/vulkan_xcb_symbols", - "vulkan_symbols/vulkan_xlib_symbols", - ]) - .unwrap(), - ); + set_vk_handles(VkTypedefHandles::new().unwrap()); VK_SUCCESS } @@ -77,6 +69,10 @@ extern "system" fn get_device_proc_addr( _ => (), }; + if let Some(func) = vk_handles().handle(s) { + return func; + } + write_log(format!("\trequested fn: {} in device proc addr", s)); write_log(format!("\t-> not found")); Functions::Null.convert() @@ -106,6 +102,10 @@ extern "system" fn get_instance_proc_addr( _ => (), }; + if let Some(func) = vk_handles().handle(s) { + return func; + } + write_log(format!("\trequested fn: {} in instance proc addr", s)); write_log(format!("\t-> not found")); Functions::Null.convert() @@ -152,6 +152,13 @@ extern "system" fn create_instance( extern "system" fn destroy_instance(instance: VkInstance, allocator: *const VkAllocationCallbacks) { write_log(" ================== vulkan layer destroy instance =================="); + + unsafe { + let destroy_instance: PFN_vkDestroyInstance = + mem::transmute(vk_handles().handle("vkDestroyInstance").unwrap()); + + destroy_instance(instance, allocator); + } } extern "system" fn create_device( @@ -194,9 +201,16 @@ extern "system" fn create_device( extern "system" fn destroy_device(device: VkDevice, allocator: *const VkAllocationCallbacks) { write_log(" ================== vulkan layer destroy device =================="); + + unsafe { + let destroy_device: PFN_vkDestroyDevice = + mem::transmute(vk_handles().handle("vkDestroyDevice").unwrap()); + + destroy_device(device, allocator); + } } -fn write_log(msg: impl ToString) { +pub fn write_log(msg: impl ToString) { let mut file = OpenOptions::new() .append(true) .create(true) diff --git a/src/vk_handles.rs b/src/vk_handles.rs index a81fdb5..28b0afd 100644 --- a/src/vk_handles.rs +++ b/src/vk_handles.rs @@ -1,5 +1,5 @@ use anyhow::Result; -use std::{collections::HashMap, ffi::c_void, fs, mem, path::Path, ptr}; +use std::{collections::HashMap, ffi::c_void, mem, ptr}; use vulkan_sys::prelude::*; static mut FN_HANDLES: Option = None; @@ -24,14 +24,9 @@ pub struct VkTypedefHandles { } impl VkTypedefHandles { - pub fn new(symbol_files: &[impl AsRef]) -> Result { - let mut symbols = Vec::new(); - - for symbol_file in symbol_files { - let s = fs::read_to_string(symbol_file)?; - - symbols.append(&mut s.lines().map(|s| s.to_string()).collect()); - } + pub fn new() -> Result { + let fns = include_str!("../vk_functions"); + let symbols = fns.lines().map(|s| s.to_string()).collect(); Ok(Self { typedefs: symbols, From c3f7f1a4155d1b91058844d4f5d3a2a22f5af4a5 Mon Sep 17 00:00:00 2001 From: hodasemi Date: Wed, 11 Jan 2023 21:30:59 +0100 Subject: [PATCH 03/85] Remove symbol files --- vulkan_symbols/bash.sh | 8 - vulkan_symbols/vulkan_core_symbols | 1915 --------------------------- vulkan_symbols/vulkan_wayland_types | 4 - vulkan_symbols/vulkan_xcb_types | 4 - vulkan_symbols/vulkan_xlib_types | 4 - 5 files changed, 1935 deletions(-) delete mode 100644 vulkan_symbols/bash.sh delete mode 100644 vulkan_symbols/vulkan_core_symbols delete mode 100644 vulkan_symbols/vulkan_wayland_types delete mode 100644 vulkan_symbols/vulkan_xcb_types delete mode 100644 vulkan_symbols/vulkan_xlib_types diff --git a/vulkan_symbols/bash.sh b/vulkan_symbols/bash.sh deleted file mode 100644 index 457fcc4..0000000 --- a/vulkan_symbols/bash.sh +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/bash - - -# regex: (\S+) (.*?); -> $1 -ctags -x --c-types=t /usr/include/vulkan/vulkan_core.h > vulkan_core_types -ctags -x --c-types=t /usr/include/vulkan/vulkan_xcb.h > vulkan_xcb_types -ctags -x --c-types=t /usr/include/vulkan/vulkan_xlib.h > vulkan_xlib_types -ctags -x --c-types=t /usr/include/vulkan/vulkan_wayland.h > vulkan_wayland_types diff --git a/vulkan_symbols/vulkan_core_symbols b/vulkan_symbols/vulkan_core_symbols deleted file mode 100644 index d3c0086..0000000 --- a/vulkan_symbols/vulkan_core_symbols +++ /dev/null @@ -1,1915 +0,0 @@ -PFN_vkAcquireDrmDisplayEXT -PFN_vkAcquireNextImage2KHR -PFN_vkAcquireNextImageKHR -PFN_vkAcquirePerformanceConfigurationINTEL -PFN_vkAcquireProfilingLockKHR -PFN_vkAcquireWinrtDisplayNV -PFN_vkAllocateCommandBuffers -PFN_vkAllocateDescriptorSets -PFN_vkAllocateMemory -PFN_vkAllocationFunction -PFN_vkBeginCommandBuffer -PFN_vkBindAccelerationStructureMemoryNV -PFN_vkBindBufferMemory -PFN_vkBindBufferMemory2 -PFN_vkBindBufferMemory2KHR -PFN_vkBindImageMemory -PFN_vkBindImageMemory2 -PFN_vkBindImageMemory2KHR -PFN_vkBindOpticalFlowSessionImageNV -PFN_vkBuildAccelerationStructuresKHR -PFN_vkBuildMicromapsEXT -PFN_vkCmdBeginConditionalRenderingEXT -PFN_vkCmdBeginDebugUtilsLabelEXT -PFN_vkCmdBeginQuery -PFN_vkCmdBeginQueryIndexedEXT -PFN_vkCmdBeginRenderPass -PFN_vkCmdBeginRenderPass2 -PFN_vkCmdBeginRenderPass2KHR -PFN_vkCmdBeginRendering -PFN_vkCmdBeginRenderingKHR -PFN_vkCmdBeginTransformFeedbackEXT -PFN_vkCmdBindDescriptorBufferEmbeddedSamplersEXT -PFN_vkCmdBindDescriptorBuffersEXT -PFN_vkCmdBindDescriptorSets -PFN_vkCmdBindIndexBuffer -PFN_vkCmdBindInvocationMaskHUAWEI -PFN_vkCmdBindPipeline -PFN_vkCmdBindPipelineShaderGroupNV -PFN_vkCmdBindShadingRateImageNV -PFN_vkCmdBindTransformFeedbackBuffersEXT -PFN_vkCmdBindVertexBuffers -PFN_vkCmdBindVertexBuffers2 -PFN_vkCmdBindVertexBuffers2EXT -PFN_vkCmdBlitImage -PFN_vkCmdBlitImage2 -PFN_vkCmdBlitImage2KHR -PFN_vkCmdBuildAccelerationStructureNV -PFN_vkCmdBuildAccelerationStructuresIndirectKHR -PFN_vkCmdBuildAccelerationStructuresKHR -PFN_vkCmdBuildMicromapsEXT -PFN_vkCmdClearAttachments -PFN_vkCmdClearColorImage -PFN_vkCmdClearDepthStencilImage -PFN_vkCmdCopyAccelerationStructureKHR -PFN_vkCmdCopyAccelerationStructureNV -PFN_vkCmdCopyAccelerationStructureToMemoryKHR -PFN_vkCmdCopyBuffer -PFN_vkCmdCopyBuffer2 -PFN_vkCmdCopyBuffer2KHR -PFN_vkCmdCopyBufferToImage -PFN_vkCmdCopyBufferToImage2 -PFN_vkCmdCopyBufferToImage2KHR -PFN_vkCmdCopyImage -PFN_vkCmdCopyImage2 -PFN_vkCmdCopyImage2KHR -PFN_vkCmdCopyImageToBuffer -PFN_vkCmdCopyImageToBuffer2 -PFN_vkCmdCopyImageToBuffer2KHR -PFN_vkCmdCopyMemoryIndirectNV -PFN_vkCmdCopyMemoryToAccelerationStructureKHR -PFN_vkCmdCopyMemoryToImageIndirectNV -PFN_vkCmdCopyMemoryToMicromapEXT -PFN_vkCmdCopyMicromapEXT -PFN_vkCmdCopyMicromapToMemoryEXT -PFN_vkCmdCopyQueryPoolResults -PFN_vkCmdCuLaunchKernelNVX -PFN_vkCmdDebugMarkerBeginEXT -PFN_vkCmdDebugMarkerEndEXT -PFN_vkCmdDebugMarkerInsertEXT -PFN_vkCmdDecompressMemoryIndirectCountNV -PFN_vkCmdDecompressMemoryNV -PFN_vkCmdDispatch -PFN_vkCmdDispatchBase -PFN_vkCmdDispatchBaseKHR -PFN_vkCmdDispatchIndirect -PFN_vkCmdDraw -PFN_vkCmdDrawIndexed -PFN_vkCmdDrawIndexedIndirect -PFN_vkCmdDrawIndexedIndirectCount -PFN_vkCmdDrawIndexedIndirectCountAMD -PFN_vkCmdDrawIndexedIndirectCountKHR -PFN_vkCmdDrawIndirect -PFN_vkCmdDrawIndirectByteCountEXT -PFN_vkCmdDrawIndirectCount -PFN_vkCmdDrawIndirectCountAMD -PFN_vkCmdDrawIndirectCountKHR -PFN_vkCmdDrawMeshTasksEXT -PFN_vkCmdDrawMeshTasksIndirectCountEXT -PFN_vkCmdDrawMeshTasksIndirectCountNV -PFN_vkCmdDrawMeshTasksIndirectEXT -PFN_vkCmdDrawMeshTasksIndirectNV -PFN_vkCmdDrawMeshTasksNV -PFN_vkCmdDrawMultiEXT -PFN_vkCmdDrawMultiIndexedEXT -PFN_vkCmdEndConditionalRenderingEXT -PFN_vkCmdEndDebugUtilsLabelEXT -PFN_vkCmdEndQuery -PFN_vkCmdEndQueryIndexedEXT -PFN_vkCmdEndRenderPass -PFN_vkCmdEndRenderPass2 -PFN_vkCmdEndRenderPass2KHR -PFN_vkCmdEndRendering -PFN_vkCmdEndRenderingKHR -PFN_vkCmdEndTransformFeedbackEXT -PFN_vkCmdExecuteCommands -PFN_vkCmdExecuteGeneratedCommandsNV -PFN_vkCmdFillBuffer -PFN_vkCmdInsertDebugUtilsLabelEXT -PFN_vkCmdNextSubpass -PFN_vkCmdNextSubpass2 -PFN_vkCmdNextSubpass2KHR -PFN_vkCmdOpticalFlowExecuteNV -PFN_vkCmdPipelineBarrier -PFN_vkCmdPipelineBarrier2 -PFN_vkCmdPipelineBarrier2KHR -PFN_vkCmdPreprocessGeneratedCommandsNV -PFN_vkCmdPushConstants -PFN_vkCmdPushDescriptorSetKHR -PFN_vkCmdPushDescriptorSetWithTemplateKHR -PFN_vkCmdResetEvent -PFN_vkCmdResetEvent2 -PFN_vkCmdResetEvent2KHR -PFN_vkCmdResetQueryPool -PFN_vkCmdResolveImage -PFN_vkCmdResolveImage2 -PFN_vkCmdResolveImage2KHR -PFN_vkCmdSetAlphaToCoverageEnableEXT -PFN_vkCmdSetAlphaToOneEnableEXT -PFN_vkCmdSetBlendConstants -PFN_vkCmdSetCheckpointNV -PFN_vkCmdSetCoarseSampleOrderNV -PFN_vkCmdSetColorBlendAdvancedEXT -PFN_vkCmdSetColorBlendEnableEXT -PFN_vkCmdSetColorBlendEquationEXT -PFN_vkCmdSetColorWriteEnableEXT -PFN_vkCmdSetColorWriteMaskEXT -PFN_vkCmdSetConservativeRasterizationModeEXT -PFN_vkCmdSetCoverageModulationModeNV -PFN_vkCmdSetCoverageModulationTableEnableNV -PFN_vkCmdSetCoverageModulationTableNV -PFN_vkCmdSetCoverageReductionModeNV -PFN_vkCmdSetCoverageToColorEnableNV -PFN_vkCmdSetCoverageToColorLocationNV -PFN_vkCmdSetCullMode -PFN_vkCmdSetCullModeEXT -PFN_vkCmdSetDepthBias -PFN_vkCmdSetDepthBiasEnable -PFN_vkCmdSetDepthBiasEnableEXT -PFN_vkCmdSetDepthBounds -PFN_vkCmdSetDepthBoundsTestEnable -PFN_vkCmdSetDepthBoundsTestEnableEXT -PFN_vkCmdSetDepthClampEnableEXT -PFN_vkCmdSetDepthClipEnableEXT -PFN_vkCmdSetDepthClipNegativeOneToOneEXT -PFN_vkCmdSetDepthCompareOp -PFN_vkCmdSetDepthCompareOpEXT -PFN_vkCmdSetDepthTestEnable -PFN_vkCmdSetDepthTestEnableEXT -PFN_vkCmdSetDepthWriteEnable -PFN_vkCmdSetDepthWriteEnableEXT -PFN_vkCmdSetDescriptorBufferOffsetsEXT -PFN_vkCmdSetDeviceMask -PFN_vkCmdSetDeviceMaskKHR -PFN_vkCmdSetDiscardRectangleEXT -PFN_vkCmdSetEvent -PFN_vkCmdSetEvent2 -PFN_vkCmdSetEvent2KHR -PFN_vkCmdSetExclusiveScissorNV -PFN_vkCmdSetExtraPrimitiveOverestimationSizeEXT -PFN_vkCmdSetFragmentShadingRateEnumNV -PFN_vkCmdSetFragmentShadingRateKHR -PFN_vkCmdSetFrontFace -PFN_vkCmdSetFrontFaceEXT -PFN_vkCmdSetLineRasterizationModeEXT -PFN_vkCmdSetLineStippleEXT -PFN_vkCmdSetLineStippleEnableEXT -PFN_vkCmdSetLineWidth -PFN_vkCmdSetLogicOpEXT -PFN_vkCmdSetLogicOpEnableEXT -PFN_vkCmdSetPatchControlPointsEXT -PFN_vkCmdSetPerformanceMarkerINTEL -PFN_vkCmdSetPerformanceOverrideINTEL -PFN_vkCmdSetPerformanceStreamMarkerINTEL -PFN_vkCmdSetPolygonModeEXT -PFN_vkCmdSetPrimitiveRestartEnable -PFN_vkCmdSetPrimitiveRestartEnableEXT -PFN_vkCmdSetPrimitiveTopology -PFN_vkCmdSetPrimitiveTopologyEXT -PFN_vkCmdSetProvokingVertexModeEXT -PFN_vkCmdSetRasterizationSamplesEXT -PFN_vkCmdSetRasterizationStreamEXT -PFN_vkCmdSetRasterizerDiscardEnable -PFN_vkCmdSetRasterizerDiscardEnableEXT -PFN_vkCmdSetRayTracingPipelineStackSizeKHR -PFN_vkCmdSetRepresentativeFragmentTestEnableNV -PFN_vkCmdSetSampleLocationsEXT -PFN_vkCmdSetSampleLocationsEnableEXT -PFN_vkCmdSetSampleMaskEXT -PFN_vkCmdSetScissor -PFN_vkCmdSetScissorWithCount -PFN_vkCmdSetScissorWithCountEXT -PFN_vkCmdSetShadingRateImageEnableNV -PFN_vkCmdSetStencilCompareMask -PFN_vkCmdSetStencilOp -PFN_vkCmdSetStencilOpEXT -PFN_vkCmdSetStencilReference -PFN_vkCmdSetStencilTestEnable -PFN_vkCmdSetStencilTestEnableEXT -PFN_vkCmdSetStencilWriteMask -PFN_vkCmdSetTessellationDomainOriginEXT -PFN_vkCmdSetVertexInputEXT -PFN_vkCmdSetViewport -PFN_vkCmdSetViewportShadingRatePaletteNV -PFN_vkCmdSetViewportSwizzleNV -PFN_vkCmdSetViewportWScalingEnableNV -PFN_vkCmdSetViewportWScalingNV -PFN_vkCmdSetViewportWithCount -PFN_vkCmdSetViewportWithCountEXT -PFN_vkCmdSubpassShadingHUAWEI -PFN_vkCmdTraceRaysIndirect2KHR -PFN_vkCmdTraceRaysIndirectKHR -PFN_vkCmdTraceRaysKHR -PFN_vkCmdTraceRaysNV -PFN_vkCmdUpdateBuffer -PFN_vkCmdWaitEvents -PFN_vkCmdWaitEvents2 -PFN_vkCmdWaitEvents2KHR -PFN_vkCmdWriteAccelerationStructuresPropertiesKHR -PFN_vkCmdWriteAccelerationStructuresPropertiesNV -PFN_vkCmdWriteBufferMarker2AMD -PFN_vkCmdWriteBufferMarkerAMD -PFN_vkCmdWriteMicromapsPropertiesEXT -PFN_vkCmdWriteTimestamp -PFN_vkCmdWriteTimestamp2 -PFN_vkCmdWriteTimestamp2KHR -PFN_vkCompileDeferredNV -PFN_vkCopyAccelerationStructureKHR -PFN_vkCopyAccelerationStructureToMemoryKHR -PFN_vkCopyMemoryToAccelerationStructureKHR -PFN_vkCopyMemoryToMicromapEXT -PFN_vkCopyMicromapEXT -PFN_vkCopyMicromapToMemoryEXT -PFN_vkCreateAccelerationStructureKHR -PFN_vkCreateAccelerationStructureNV -PFN_vkCreateBuffer -PFN_vkCreateBufferView -PFN_vkCreateCommandPool -PFN_vkCreateComputePipelines -PFN_vkCreateCuFunctionNVX -PFN_vkCreateCuModuleNVX -PFN_vkCreateDebugReportCallbackEXT -PFN_vkCreateDebugUtilsMessengerEXT -PFN_vkCreateDeferredOperationKHR -PFN_vkCreateDescriptorPool -PFN_vkCreateDescriptorSetLayout -PFN_vkCreateDescriptorUpdateTemplate -PFN_vkCreateDescriptorUpdateTemplateKHR -PFN_vkCreateDevice -PFN_vkCreateDisplayModeKHR -PFN_vkCreateDisplayPlaneSurfaceKHR -PFN_vkCreateEvent -PFN_vkCreateFence -PFN_vkCreateFramebuffer -PFN_vkCreateGraphicsPipelines -PFN_vkCreateHeadlessSurfaceEXT -PFN_vkCreateImage -PFN_vkCreateImageView -PFN_vkCreateIndirectCommandsLayoutNV -PFN_vkCreateInstance -PFN_vkCreateMicromapEXT -PFN_vkCreateOpticalFlowSessionNV -PFN_vkCreatePipelineCache -PFN_vkCreatePipelineLayout -PFN_vkCreatePrivateDataSlot -PFN_vkCreatePrivateDataSlotEXT -PFN_vkCreateQueryPool -PFN_vkCreateRayTracingPipelinesKHR -PFN_vkCreateRayTracingPipelinesNV -PFN_vkCreateRenderPass -PFN_vkCreateRenderPass2 -PFN_vkCreateRenderPass2KHR -PFN_vkCreateSampler -PFN_vkCreateSamplerYcbcrConversion -PFN_vkCreateSamplerYcbcrConversionKHR -PFN_vkCreateSemaphore -PFN_vkCreateShaderModule -PFN_vkCreateSharedSwapchainsKHR -PFN_vkCreateSwapchainKHR -PFN_vkCreateValidationCacheEXT -PFN_vkDebugMarkerSetObjectNameEXT -PFN_vkDebugMarkerSetObjectTagEXT -PFN_vkDebugReportCallbackEXT -PFN_vkDebugReportMessageEXT -PFN_vkDebugUtilsMessengerCallbackEXT -PFN_vkDeferredOperationJoinKHR -PFN_vkDestroyAccelerationStructureKHR -PFN_vkDestroyAccelerationStructureNV -PFN_vkDestroyBuffer -PFN_vkDestroyBufferView -PFN_vkDestroyCommandPool -PFN_vkDestroyCuFunctionNVX -PFN_vkDestroyCuModuleNVX -PFN_vkDestroyDebugReportCallbackEXT -PFN_vkDestroyDebugUtilsMessengerEXT -PFN_vkDestroyDeferredOperationKHR -PFN_vkDestroyDescriptorPool -PFN_vkDestroyDescriptorSetLayout -PFN_vkDestroyDescriptorUpdateTemplate -PFN_vkDestroyDescriptorUpdateTemplateKHR -PFN_vkDestroyDevice -PFN_vkDestroyEvent -PFN_vkDestroyFence -PFN_vkDestroyFramebuffer -PFN_vkDestroyImage -PFN_vkDestroyImageView -PFN_vkDestroyIndirectCommandsLayoutNV -PFN_vkDestroyInstance -PFN_vkDestroyMicromapEXT -PFN_vkDestroyOpticalFlowSessionNV -PFN_vkDestroyPipeline -PFN_vkDestroyPipelineCache -PFN_vkDestroyPipelineLayout -PFN_vkDestroyPrivateDataSlot -PFN_vkDestroyPrivateDataSlotEXT -PFN_vkDestroyQueryPool -PFN_vkDestroyRenderPass -PFN_vkDestroySampler -PFN_vkDestroySamplerYcbcrConversion -PFN_vkDestroySamplerYcbcrConversionKHR -PFN_vkDestroySemaphore -PFN_vkDestroyShaderModule -PFN_vkDestroySurfaceKHR -PFN_vkDestroySwapchainKHR -PFN_vkDestroyValidationCacheEXT -PFN_vkDeviceMemoryReportCallbackEXT -PFN_vkDeviceWaitIdle -PFN_vkDisplayPowerControlEXT -PFN_vkEndCommandBuffer -PFN_vkEnumerateDeviceExtensionProperties -PFN_vkEnumerateDeviceLayerProperties -PFN_vkEnumerateInstanceExtensionProperties -PFN_vkEnumerateInstanceLayerProperties -PFN_vkEnumerateInstanceVersion -PFN_vkEnumeratePhysicalDeviceGroups -PFN_vkEnumeratePhysicalDeviceGroupsKHR -PFN_vkEnumeratePhysicalDeviceQueueFamilyPerformanceQueryCountersKHR -PFN_vkEnumeratePhysicalDevices -PFN_vkFlushMappedMemoryRanges -PFN_vkFreeCommandBuffers -PFN_vkFreeDescriptorSets -PFN_vkFreeFunction -PFN_vkFreeMemory -PFN_vkGetAccelerationStructureBuildSizesKHR -PFN_vkGetAccelerationStructureDeviceAddressKHR -PFN_vkGetAccelerationStructureHandleNV -PFN_vkGetAccelerationStructureMemoryRequirementsNV -PFN_vkGetAccelerationStructureOpaqueCaptureDescriptorDataEXT -PFN_vkGetBufferDeviceAddress -PFN_vkGetBufferDeviceAddressEXT -PFN_vkGetBufferDeviceAddressKHR -PFN_vkGetBufferMemoryRequirements -PFN_vkGetBufferMemoryRequirements2 -PFN_vkGetBufferMemoryRequirements2KHR -PFN_vkGetBufferOpaqueCaptureAddress -PFN_vkGetBufferOpaqueCaptureAddressKHR -PFN_vkGetBufferOpaqueCaptureDescriptorDataEXT -PFN_vkGetCalibratedTimestampsEXT -PFN_vkGetDeferredOperationMaxConcurrencyKHR -PFN_vkGetDeferredOperationResultKHR -PFN_vkGetDescriptorEXT -PFN_vkGetDescriptorSetHostMappingVALVE -PFN_vkGetDescriptorSetLayoutBindingOffsetEXT -PFN_vkGetDescriptorSetLayoutHostMappingInfoVALVE -PFN_vkGetDescriptorSetLayoutSizeEXT -PFN_vkGetDescriptorSetLayoutSupport -PFN_vkGetDescriptorSetLayoutSupportKHR -PFN_vkGetDeviceAccelerationStructureCompatibilityKHR -PFN_vkGetDeviceBufferMemoryRequirements -PFN_vkGetDeviceBufferMemoryRequirementsKHR -PFN_vkGetDeviceFaultInfoEXT -PFN_vkGetDeviceGroupPeerMemoryFeatures -PFN_vkGetDeviceGroupPeerMemoryFeaturesKHR -PFN_vkGetDeviceGroupPresentCapabilitiesKHR -PFN_vkGetDeviceGroupSurfacePresentModesKHR -PFN_vkGetDeviceImageMemoryRequirements -PFN_vkGetDeviceImageMemoryRequirementsKHR -PFN_vkGetDeviceImageSparseMemoryRequirements -PFN_vkGetDeviceImageSparseMemoryRequirementsKHR -PFN_vkGetDeviceMemoryCommitment -PFN_vkGetDeviceMemoryOpaqueCaptureAddress -PFN_vkGetDeviceMemoryOpaqueCaptureAddressKHR -PFN_vkGetDeviceMicromapCompatibilityEXT -PFN_vkGetDeviceProcAddr -PFN_vkGetDeviceQueue -PFN_vkGetDeviceQueue2 -PFN_vkGetDeviceSubpassShadingMaxWorkgroupSizeHUAWEI -PFN_vkGetDisplayModeProperties2KHR -PFN_vkGetDisplayModePropertiesKHR -PFN_vkGetDisplayPlaneCapabilities2KHR -PFN_vkGetDisplayPlaneCapabilitiesKHR -PFN_vkGetDisplayPlaneSupportedDisplaysKHR -PFN_vkGetDrmDisplayEXT -PFN_vkGetDynamicRenderingTilePropertiesQCOM -PFN_vkGetEventStatus -PFN_vkGetFenceFdKHR -PFN_vkGetFenceStatus -PFN_vkGetFramebufferTilePropertiesQCOM -PFN_vkGetGeneratedCommandsMemoryRequirementsNV -PFN_vkGetImageDrmFormatModifierPropertiesEXT -PFN_vkGetImageMemoryRequirements -PFN_vkGetImageMemoryRequirements2 -PFN_vkGetImageMemoryRequirements2KHR -PFN_vkGetImageOpaqueCaptureDescriptorDataEXT -PFN_vkGetImageSparseMemoryRequirements -PFN_vkGetImageSparseMemoryRequirements2 -PFN_vkGetImageSparseMemoryRequirements2KHR -PFN_vkGetImageSubresourceLayout -PFN_vkGetImageSubresourceLayout2EXT -PFN_vkGetImageViewAddressNVX -PFN_vkGetImageViewHandleNVX -PFN_vkGetImageViewOpaqueCaptureDescriptorDataEXT -PFN_vkGetInstanceProcAddr -PFN_vkGetMemoryFdKHR -PFN_vkGetMemoryFdPropertiesKHR -PFN_vkGetMemoryHostPointerPropertiesEXT -PFN_vkGetMemoryRemoteAddressNV -PFN_vkGetMicromapBuildSizesEXT -PFN_vkGetPastPresentationTimingGOOGLE -PFN_vkGetPerformanceParameterINTEL -PFN_vkGetPhysicalDeviceCalibrateableTimeDomainsEXT -PFN_vkGetPhysicalDeviceCooperativeMatrixPropertiesNV -PFN_vkGetPhysicalDeviceDisplayPlaneProperties2KHR -PFN_vkGetPhysicalDeviceDisplayPlanePropertiesKHR -PFN_vkGetPhysicalDeviceDisplayProperties2KHR -PFN_vkGetPhysicalDeviceDisplayPropertiesKHR -PFN_vkGetPhysicalDeviceExternalBufferProperties -PFN_vkGetPhysicalDeviceExternalBufferPropertiesKHR -PFN_vkGetPhysicalDeviceExternalFenceProperties -PFN_vkGetPhysicalDeviceExternalFencePropertiesKHR -PFN_vkGetPhysicalDeviceExternalImageFormatPropertiesNV -PFN_vkGetPhysicalDeviceExternalSemaphoreProperties -PFN_vkGetPhysicalDeviceExternalSemaphorePropertiesKHR -PFN_vkGetPhysicalDeviceFeatures -PFN_vkGetPhysicalDeviceFeatures2 -PFN_vkGetPhysicalDeviceFeatures2KHR -PFN_vkGetPhysicalDeviceFormatProperties -PFN_vkGetPhysicalDeviceFormatProperties2 -PFN_vkGetPhysicalDeviceFormatProperties2KHR -PFN_vkGetPhysicalDeviceFragmentShadingRatesKHR -PFN_vkGetPhysicalDeviceImageFormatProperties -PFN_vkGetPhysicalDeviceImageFormatProperties2 -PFN_vkGetPhysicalDeviceImageFormatProperties2KHR -PFN_vkGetPhysicalDeviceMemoryProperties -PFN_vkGetPhysicalDeviceMemoryProperties2 -PFN_vkGetPhysicalDeviceMemoryProperties2KHR -PFN_vkGetPhysicalDeviceMultisamplePropertiesEXT -PFN_vkGetPhysicalDeviceOpticalFlowImageFormatsNV -PFN_vkGetPhysicalDevicePresentRectanglesKHR -PFN_vkGetPhysicalDeviceProperties -PFN_vkGetPhysicalDeviceProperties2 -PFN_vkGetPhysicalDeviceProperties2KHR -PFN_vkGetPhysicalDeviceQueueFamilyPerformanceQueryPassesKHR -PFN_vkGetPhysicalDeviceQueueFamilyProperties -PFN_vkGetPhysicalDeviceQueueFamilyProperties2 -PFN_vkGetPhysicalDeviceQueueFamilyProperties2KHR -PFN_vkGetPhysicalDeviceSparseImageFormatProperties -PFN_vkGetPhysicalDeviceSparseImageFormatProperties2 -PFN_vkGetPhysicalDeviceSparseImageFormatProperties2KHR -PFN_vkGetPhysicalDeviceSupportedFramebufferMixedSamplesCombinationsNV -PFN_vkGetPhysicalDeviceSurfaceCapabilities2EXT -PFN_vkGetPhysicalDeviceSurfaceCapabilities2KHR -PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR -PFN_vkGetPhysicalDeviceSurfaceFormats2KHR -PFN_vkGetPhysicalDeviceSurfaceFormatsKHR -PFN_vkGetPhysicalDeviceSurfacePresentModesKHR -PFN_vkGetPhysicalDeviceSurfaceSupportKHR -PFN_vkGetPhysicalDeviceToolProperties -PFN_vkGetPhysicalDeviceToolPropertiesEXT -PFN_vkGetPipelineCacheData -PFN_vkGetPipelineExecutableInternalRepresentationsKHR -PFN_vkGetPipelineExecutablePropertiesKHR -PFN_vkGetPipelineExecutableStatisticsKHR -PFN_vkGetPipelinePropertiesEXT -PFN_vkGetPrivateData -PFN_vkGetPrivateDataEXT -PFN_vkGetQueryPoolResults -PFN_vkGetQueueCheckpointData2NV -PFN_vkGetQueueCheckpointDataNV -PFN_vkGetRayTracingCaptureReplayShaderGroupHandlesKHR -PFN_vkGetRayTracingShaderGroupHandlesKHR -PFN_vkGetRayTracingShaderGroupHandlesNV -PFN_vkGetRayTracingShaderGroupStackSizeKHR -PFN_vkGetRefreshCycleDurationGOOGLE -PFN_vkGetRenderAreaGranularity -PFN_vkGetSamplerOpaqueCaptureDescriptorDataEXT -PFN_vkGetSemaphoreCounterValue -PFN_vkGetSemaphoreCounterValueKHR -PFN_vkGetSemaphoreFdKHR -PFN_vkGetShaderInfoAMD -PFN_vkGetShaderModuleCreateInfoIdentifierEXT -PFN_vkGetShaderModuleIdentifierEXT -PFN_vkGetSwapchainCounterEXT -PFN_vkGetSwapchainImagesKHR -PFN_vkGetSwapchainStatusKHR -PFN_vkGetValidationCacheDataEXT -PFN_vkGetWinrtDisplayNV -PFN_vkImportFenceFdKHR -PFN_vkImportSemaphoreFdKHR -PFN_vkInitializePerformanceApiINTEL -PFN_vkInternalAllocationNotification -PFN_vkInternalFreeNotification -PFN_vkInvalidateMappedMemoryRanges -PFN_vkMapMemory -PFN_vkMergePipelineCaches -PFN_vkMergeValidationCachesEXT -PFN_vkQueueBeginDebugUtilsLabelEXT -PFN_vkQueueBindSparse -PFN_vkQueueEndDebugUtilsLabelEXT -PFN_vkQueueInsertDebugUtilsLabelEXT -PFN_vkQueuePresentKHR -PFN_vkQueueSetPerformanceConfigurationINTEL -PFN_vkQueueSubmit -PFN_vkQueueSubmit2 -PFN_vkQueueSubmit2KHR -PFN_vkQueueWaitIdle -PFN_vkReallocationFunction -PFN_vkRegisterDeviceEventEXT -PFN_vkRegisterDisplayEventEXT -PFN_vkReleaseDisplayEXT -PFN_vkReleasePerformanceConfigurationINTEL -PFN_vkReleaseProfilingLockKHR -PFN_vkResetCommandBuffer -PFN_vkResetCommandPool -PFN_vkResetDescriptorPool -PFN_vkResetEvent -PFN_vkResetFences -PFN_vkResetQueryPool -PFN_vkResetQueryPoolEXT -PFN_vkSetDebugUtilsObjectNameEXT -PFN_vkSetDebugUtilsObjectTagEXT -PFN_vkSetDeviceMemoryPriorityEXT -PFN_vkSetEvent -PFN_vkSetHdrMetadataEXT -PFN_vkSetLocalDimmingAMD -PFN_vkSetPrivateData -PFN_vkSetPrivateDataEXT -PFN_vkSignalSemaphore -PFN_vkSignalSemaphoreKHR -PFN_vkSubmitDebugUtilsMessageEXT -PFN_vkTrimCommandPool -PFN_vkTrimCommandPoolKHR -PFN_vkUninitializePerformanceApiINTEL -PFN_vkUnmapMemory -PFN_vkUpdateDescriptorSetWithTemplate -PFN_vkUpdateDescriptorSetWithTemplateKHR -PFN_vkUpdateDescriptorSets -PFN_vkVoidFunction -PFN_vkWaitForFences -PFN_vkWaitForPresentKHR -PFN_vkWaitSemaphores -PFN_vkWaitSemaphoresKHR -PFN_vkWriteAccelerationStructuresPropertiesKHR -PFN_vkWriteMicromapsPropertiesEXT -VkAabbPositionsKHR -VkAabbPositionsNV -VkAccelerationStructureBuildGeometryInfoKHR -VkAccelerationStructureBuildRangeInfoKHR -VkAccelerationStructureBuildSizesInfoKHR -VkAccelerationStructureBuildTypeKHR -VkAccelerationStructureCaptureDescriptorDataInfoEXT -VkAccelerationStructureCompatibilityKHR -VkAccelerationStructureCreateFlagBitsKHR -VkAccelerationStructureCreateFlagsKHR -VkAccelerationStructureCreateInfoKHR -VkAccelerationStructureCreateInfoNV -VkAccelerationStructureDeviceAddressInfoKHR -VkAccelerationStructureGeometryAabbsDataKHR -VkAccelerationStructureGeometryDataKHR -VkAccelerationStructureGeometryInstancesDataKHR -VkAccelerationStructureGeometryKHR -VkAccelerationStructureGeometryMotionTrianglesDataNV -VkAccelerationStructureGeometryTrianglesDataKHR -VkAccelerationStructureInfoNV -VkAccelerationStructureInstanceKHR -VkAccelerationStructureInstanceNV -VkAccelerationStructureMatrixMotionInstanceNV -VkAccelerationStructureMemoryRequirementsInfoNV -VkAccelerationStructureMemoryRequirementsTypeNV -VkAccelerationStructureMotionInfoFlagsNV -VkAccelerationStructureMotionInfoNV -VkAccelerationStructureMotionInstanceDataNV -VkAccelerationStructureMotionInstanceFlagsNV -VkAccelerationStructureMotionInstanceNV -VkAccelerationStructureMotionInstanceTypeNV -VkAccelerationStructureSRTMotionInstanceNV -VkAccelerationStructureTrianglesOpacityMicromapEXT -VkAccelerationStructureTypeKHR -VkAccelerationStructureTypeNV -VkAccelerationStructureVersionInfoKHR -VkAccessFlagBits -VkAccessFlagBits2 -VkAccessFlagBits2KHR -VkAccessFlags -VkAccessFlags2 -VkAccessFlags2KHR -VkAcquireNextImageInfoKHR -VkAcquireProfilingLockFlagBitsKHR -VkAcquireProfilingLockFlagsKHR -VkAcquireProfilingLockInfoKHR -VkAllocationCallbacks -VkAmigoProfilingSubmitInfoSEC -VkApplicationInfo -VkAttachmentDescription -VkAttachmentDescription2 -VkAttachmentDescription2KHR -VkAttachmentDescriptionFlagBits -VkAttachmentDescriptionFlags -VkAttachmentDescriptionStencilLayout -VkAttachmentDescriptionStencilLayoutKHR -VkAttachmentLoadOp -VkAttachmentReference -VkAttachmentReference2 -VkAttachmentReference2KHR -VkAttachmentReferenceStencilLayout -VkAttachmentReferenceStencilLayoutKHR -VkAttachmentSampleCountInfoAMD -VkAttachmentSampleCountInfoNV -VkAttachmentSampleLocationsEXT -VkAttachmentStoreOp -VkBaseInStructure -VkBaseOutStructure -VkBindAccelerationStructureMemoryInfoNV -VkBindBufferMemoryDeviceGroupInfo -VkBindBufferMemoryDeviceGroupInfoKHR -VkBindBufferMemoryInfo -VkBindBufferMemoryInfoKHR -VkBindImageMemoryDeviceGroupInfo -VkBindImageMemoryDeviceGroupInfoKHR -VkBindImageMemoryInfo -VkBindImageMemoryInfoKHR -VkBindImageMemorySwapchainInfoKHR -VkBindImagePlaneMemoryInfo -VkBindImagePlaneMemoryInfoKHR -VkBindIndexBufferIndirectCommandNV -VkBindShaderGroupIndirectCommandNV -VkBindSparseInfo -VkBindVertexBufferIndirectCommandNV -VkBlendFactor -VkBlendOp -VkBlendOverlapEXT -VkBlitImageInfo2 -VkBlitImageInfo2KHR -VkBool32 -VkBorderColor -VkBufferCaptureDescriptorDataInfoEXT -VkBufferCopy -VkBufferCopy2 -VkBufferCopy2KHR -VkBufferCreateFlagBits -VkBufferCreateFlags -VkBufferCreateInfo -VkBufferDeviceAddressCreateInfoEXT -VkBufferDeviceAddressInfo -VkBufferDeviceAddressInfoEXT -VkBufferDeviceAddressInfoKHR -VkBufferImageCopy -VkBufferImageCopy2 -VkBufferImageCopy2KHR -VkBufferMemoryBarrier -VkBufferMemoryBarrier2 -VkBufferMemoryBarrier2KHR -VkBufferMemoryRequirementsInfo2 -VkBufferMemoryRequirementsInfo2KHR -VkBufferOpaqueCaptureAddressCreateInfo -VkBufferOpaqueCaptureAddressCreateInfoKHR -VkBufferUsageFlagBits -VkBufferUsageFlags -VkBufferViewCreateFlags -VkBufferViewCreateInfo -VkBuildAccelerationStructureFlagBitsKHR -VkBuildAccelerationStructureFlagBitsNV -VkBuildAccelerationStructureFlagsKHR -VkBuildAccelerationStructureFlagsNV -VkBuildAccelerationStructureModeKHR -VkBuildMicromapFlagBitsEXT -VkBuildMicromapFlagsEXT -VkBuildMicromapModeEXT -VkCalibratedTimestampInfoEXT -VkCheckpointData2NV -VkCheckpointDataNV -VkChromaLocation -VkChromaLocationKHR -VkClearAttachment -VkClearColorValue -VkClearDepthStencilValue -VkClearRect -VkClearValue -VkCoarseSampleLocationNV -VkCoarseSampleOrderCustomNV -VkCoarseSampleOrderTypeNV -VkColorBlendAdvancedEXT -VkColorBlendEquationEXT -VkColorComponentFlagBits -VkColorComponentFlags -VkColorSpaceKHR -VkCommandBufferAllocateInfo -VkCommandBufferBeginInfo -VkCommandBufferInheritanceConditionalRenderingInfoEXT -VkCommandBufferInheritanceInfo -VkCommandBufferInheritanceRenderPassTransformInfoQCOM -VkCommandBufferInheritanceRenderingInfo -VkCommandBufferInheritanceRenderingInfoKHR -VkCommandBufferInheritanceViewportScissorInfoNV -VkCommandBufferLevel -VkCommandBufferResetFlagBits -VkCommandBufferResetFlags -VkCommandBufferSubmitInfo -VkCommandBufferSubmitInfoKHR -VkCommandBufferUsageFlagBits -VkCommandBufferUsageFlags -VkCommandPoolCreateFlagBits -VkCommandPoolCreateFlags -VkCommandPoolCreateInfo -VkCommandPoolResetFlagBits -VkCommandPoolResetFlags -VkCommandPoolTrimFlags -VkCommandPoolTrimFlagsKHR -VkCompareOp -VkComponentMapping -VkComponentSwizzle -VkComponentTypeNV -VkCompositeAlphaFlagBitsKHR -VkCompositeAlphaFlagsKHR -VkComputePipelineCreateInfo -VkConditionalRenderingBeginInfoEXT -VkConditionalRenderingFlagBitsEXT -VkConditionalRenderingFlagsEXT -VkConformanceVersion -VkConformanceVersionKHR -VkConservativeRasterizationModeEXT -VkCooperativeMatrixPropertiesNV -VkCopyAccelerationStructureInfoKHR -VkCopyAccelerationStructureModeKHR -VkCopyAccelerationStructureModeNV -VkCopyAccelerationStructureToMemoryInfoKHR -VkCopyBufferInfo2 -VkCopyBufferInfo2KHR -VkCopyBufferToImageInfo2 -VkCopyBufferToImageInfo2KHR -VkCopyCommandTransformInfoQCOM -VkCopyDescriptorSet -VkCopyImageInfo2 -VkCopyImageInfo2KHR -VkCopyImageToBufferInfo2 -VkCopyImageToBufferInfo2KHR -VkCopyMemoryIndirectCommandNV -VkCopyMemoryToAccelerationStructureInfoKHR -VkCopyMemoryToImageIndirectCommandNV -VkCopyMemoryToMicromapInfoEXT -VkCopyMicromapInfoEXT -VkCopyMicromapModeEXT -VkCopyMicromapToMemoryInfoEXT -VkCoverageModulationModeNV -VkCoverageReductionModeNV -VkCuFunctionCreateInfoNVX -VkCuLaunchInfoNVX -VkCuModuleCreateInfoNVX -VkCullModeFlagBits -VkCullModeFlags -VkDebugMarkerMarkerInfoEXT -VkDebugMarkerObjectNameInfoEXT -VkDebugMarkerObjectTagInfoEXT -VkDebugReportCallbackCreateInfoEXT -VkDebugReportFlagBitsEXT -VkDebugReportFlagsEXT -VkDebugReportObjectTypeEXT -VkDebugUtilsLabelEXT -VkDebugUtilsMessageSeverityFlagBitsEXT -VkDebugUtilsMessageSeverityFlagsEXT -VkDebugUtilsMessageTypeFlagBitsEXT -VkDebugUtilsMessageTypeFlagsEXT -VkDebugUtilsMessengerCallbackDataEXT -VkDebugUtilsMessengerCallbackDataFlagsEXT -VkDebugUtilsMessengerCreateFlagsEXT -VkDebugUtilsMessengerCreateInfoEXT -VkDebugUtilsObjectNameInfoEXT -VkDebugUtilsObjectTagInfoEXT -VkDecompressMemoryRegionNV -VkDedicatedAllocationBufferCreateInfoNV -VkDedicatedAllocationImageCreateInfoNV -VkDedicatedAllocationMemoryAllocateInfoNV -VkDependencyFlagBits -VkDependencyFlags -VkDependencyInfo -VkDependencyInfoKHR -VkDescriptorAddressInfoEXT -VkDescriptorBindingFlagBits -VkDescriptorBindingFlagBitsEXT -VkDescriptorBindingFlags -VkDescriptorBindingFlagsEXT -VkDescriptorBufferBindingInfoEXT -VkDescriptorBufferBindingPushDescriptorBufferHandleEXT -VkDescriptorBufferInfo -VkDescriptorDataEXT -VkDescriptorGetInfoEXT -VkDescriptorImageInfo -VkDescriptorPoolCreateFlagBits -VkDescriptorPoolCreateFlags -VkDescriptorPoolCreateInfo -VkDescriptorPoolInlineUniformBlockCreateInfo -VkDescriptorPoolInlineUniformBlockCreateInfoEXT -VkDescriptorPoolResetFlags -VkDescriptorPoolSize -VkDescriptorSetAllocateInfo -VkDescriptorSetBindingReferenceVALVE -VkDescriptorSetLayoutBinding -VkDescriptorSetLayoutBindingFlagsCreateInfo -VkDescriptorSetLayoutBindingFlagsCreateInfoEXT -VkDescriptorSetLayoutCreateFlagBits -VkDescriptorSetLayoutCreateFlags -VkDescriptorSetLayoutCreateInfo -VkDescriptorSetLayoutHostMappingInfoVALVE -VkDescriptorSetLayoutSupport -VkDescriptorSetLayoutSupportKHR -VkDescriptorSetVariableDescriptorCountAllocateInfo -VkDescriptorSetVariableDescriptorCountAllocateInfoEXT -VkDescriptorSetVariableDescriptorCountLayoutSupport -VkDescriptorSetVariableDescriptorCountLayoutSupportEXT -VkDescriptorType -VkDescriptorUpdateTemplateCreateFlags -VkDescriptorUpdateTemplateCreateFlagsKHR -VkDescriptorUpdateTemplateCreateInfo -VkDescriptorUpdateTemplateCreateInfoKHR -VkDescriptorUpdateTemplateEntry -VkDescriptorUpdateTemplateEntryKHR -VkDescriptorUpdateTemplateKHR -VkDescriptorUpdateTemplateType -VkDescriptorUpdateTemplateTypeKHR -VkDeviceAddress -VkDeviceAddressBindingCallbackDataEXT -VkDeviceAddressBindingFlagBitsEXT -VkDeviceAddressBindingFlagsEXT -VkDeviceAddressBindingTypeEXT -VkDeviceBufferMemoryRequirements -VkDeviceBufferMemoryRequirementsKHR -VkDeviceCreateFlags -VkDeviceCreateInfo -VkDeviceDeviceMemoryReportCreateInfoEXT -VkDeviceDiagnosticsConfigCreateInfoNV -VkDeviceDiagnosticsConfigFlagBitsNV -VkDeviceDiagnosticsConfigFlagsNV -VkDeviceEventInfoEXT -VkDeviceEventTypeEXT -VkDeviceFaultAddressInfoEXT -VkDeviceFaultAddressTypeEXT -VkDeviceFaultCountsEXT -VkDeviceFaultInfoEXT -VkDeviceFaultVendorBinaryHeaderVersionEXT -VkDeviceFaultVendorBinaryHeaderVersionOneEXT -VkDeviceFaultVendorInfoEXT -VkDeviceGroupBindSparseInfo -VkDeviceGroupBindSparseInfoKHR -VkDeviceGroupCommandBufferBeginInfo -VkDeviceGroupCommandBufferBeginInfoKHR -VkDeviceGroupDeviceCreateInfo -VkDeviceGroupDeviceCreateInfoKHR -VkDeviceGroupPresentCapabilitiesKHR -VkDeviceGroupPresentInfoKHR -VkDeviceGroupPresentModeFlagBitsKHR -VkDeviceGroupPresentModeFlagsKHR -VkDeviceGroupRenderPassBeginInfo -VkDeviceGroupRenderPassBeginInfoKHR -VkDeviceGroupSubmitInfo -VkDeviceGroupSubmitInfoKHR -VkDeviceGroupSwapchainCreateInfoKHR -VkDeviceImageMemoryRequirements -VkDeviceImageMemoryRequirementsKHR -VkDeviceMemoryOpaqueCaptureAddressInfo -VkDeviceMemoryOpaqueCaptureAddressInfoKHR -VkDeviceMemoryOverallocationCreateInfoAMD -VkDeviceMemoryReportCallbackDataEXT -VkDeviceMemoryReportEventTypeEXT -VkDeviceMemoryReportFlagsEXT -VkDeviceOrHostAddressConstKHR -VkDeviceOrHostAddressKHR -VkDevicePrivateDataCreateInfo -VkDevicePrivateDataCreateInfoEXT -VkDeviceQueueCreateFlagBits -VkDeviceQueueCreateFlags -VkDeviceQueueCreateInfo -VkDeviceQueueGlobalPriorityCreateInfoEXT -VkDeviceQueueGlobalPriorityCreateInfoKHR -VkDeviceQueueInfo2 -VkDeviceSize -VkDiscardRectangleModeEXT -VkDispatchIndirectCommand -VkDisplayEventInfoEXT -VkDisplayEventTypeEXT -VkDisplayModeCreateFlagsKHR -VkDisplayModeCreateInfoKHR -VkDisplayModeParametersKHR -VkDisplayModeProperties2KHR -VkDisplayModePropertiesKHR -VkDisplayNativeHdrSurfaceCapabilitiesAMD -VkDisplayPlaneAlphaFlagBitsKHR -VkDisplayPlaneAlphaFlagsKHR -VkDisplayPlaneCapabilities2KHR -VkDisplayPlaneCapabilitiesKHR -VkDisplayPlaneInfo2KHR -VkDisplayPlaneProperties2KHR -VkDisplayPlanePropertiesKHR -VkDisplayPowerInfoEXT -VkDisplayPowerStateEXT -VkDisplayPresentInfoKHR -VkDisplayProperties2KHR -VkDisplayPropertiesKHR -VkDisplaySurfaceCreateFlagsKHR -VkDisplaySurfaceCreateInfoKHR -VkDrawIndexedIndirectCommand -VkDrawIndirectCommand -VkDrawMeshTasksIndirectCommandEXT -VkDrawMeshTasksIndirectCommandNV -VkDriverId -VkDriverIdKHR -VkDrmFormatModifierProperties2EXT -VkDrmFormatModifierPropertiesEXT -VkDrmFormatModifierPropertiesList2EXT -VkDrmFormatModifierPropertiesListEXT -VkDynamicState -VkEventCreateFlagBits -VkEventCreateFlags -VkEventCreateInfo -VkExportFenceCreateInfo -VkExportFenceCreateInfoKHR -VkExportMemoryAllocateInfo -VkExportMemoryAllocateInfoKHR -VkExportMemoryAllocateInfoNV -VkExportSemaphoreCreateInfo -VkExportSemaphoreCreateInfoKHR -VkExtensionProperties -VkExtent2D -VkExtent3D -VkExternalBufferProperties -VkExternalBufferPropertiesKHR -VkExternalFenceFeatureFlagBits -VkExternalFenceFeatureFlagBitsKHR -VkExternalFenceFeatureFlags -VkExternalFenceFeatureFlagsKHR -VkExternalFenceHandleTypeFlagBits -VkExternalFenceHandleTypeFlagBitsKHR -VkExternalFenceHandleTypeFlags -VkExternalFenceHandleTypeFlagsKHR -VkExternalFenceProperties -VkExternalFencePropertiesKHR -VkExternalImageFormatProperties -VkExternalImageFormatPropertiesKHR -VkExternalImageFormatPropertiesNV -VkExternalMemoryBufferCreateInfo -VkExternalMemoryBufferCreateInfoKHR -VkExternalMemoryFeatureFlagBits -VkExternalMemoryFeatureFlagBitsKHR -VkExternalMemoryFeatureFlagBitsNV -VkExternalMemoryFeatureFlags -VkExternalMemoryFeatureFlagsKHR -VkExternalMemoryFeatureFlagsNV -VkExternalMemoryHandleTypeFlagBits -VkExternalMemoryHandleTypeFlagBitsKHR -VkExternalMemoryHandleTypeFlagBitsNV -VkExternalMemoryHandleTypeFlags -VkExternalMemoryHandleTypeFlagsKHR -VkExternalMemoryHandleTypeFlagsNV -VkExternalMemoryImageCreateInfo -VkExternalMemoryImageCreateInfoKHR -VkExternalMemoryImageCreateInfoNV -VkExternalMemoryProperties -VkExternalMemoryPropertiesKHR -VkExternalSemaphoreFeatureFlagBits -VkExternalSemaphoreFeatureFlagBitsKHR -VkExternalSemaphoreFeatureFlags -VkExternalSemaphoreFeatureFlagsKHR -VkExternalSemaphoreHandleTypeFlagBits -VkExternalSemaphoreHandleTypeFlagBitsKHR -VkExternalSemaphoreHandleTypeFlags -VkExternalSemaphoreHandleTypeFlagsKHR -VkExternalSemaphoreProperties -VkExternalSemaphorePropertiesKHR -VkFenceCreateFlagBits -VkFenceCreateFlags -VkFenceCreateInfo -VkFenceGetFdInfoKHR -VkFenceImportFlagBits -VkFenceImportFlagBitsKHR -VkFenceImportFlags -VkFenceImportFlagsKHR -VkFilter -VkFilterCubicImageViewImageFormatPropertiesEXT -VkFlags -VkFlags64 -VkFormat -VkFormatFeatureFlagBits -VkFormatFeatureFlagBits2 -VkFormatFeatureFlagBits2KHR -VkFormatFeatureFlags -VkFormatFeatureFlags2 -VkFormatFeatureFlags2KHR -VkFormatProperties -VkFormatProperties2 -VkFormatProperties2KHR -VkFormatProperties3 -VkFormatProperties3KHR -VkFragmentShadingRateAttachmentInfoKHR -VkFragmentShadingRateCombinerOpKHR -VkFragmentShadingRateNV -VkFragmentShadingRateTypeNV -VkFramebufferAttachmentImageInfo -VkFramebufferAttachmentImageInfoKHR -VkFramebufferAttachmentsCreateInfo -VkFramebufferAttachmentsCreateInfoKHR -VkFramebufferCreateFlagBits -VkFramebufferCreateFlags -VkFramebufferCreateInfo -VkFramebufferMixedSamplesCombinationNV -VkFrontFace -VkGeneratedCommandsInfoNV -VkGeneratedCommandsMemoryRequirementsInfoNV -VkGeometryAABBNV -VkGeometryDataNV -VkGeometryFlagBitsKHR -VkGeometryFlagBitsNV -VkGeometryFlagsKHR -VkGeometryFlagsNV -VkGeometryInstanceFlagBitsKHR -VkGeometryInstanceFlagBitsNV -VkGeometryInstanceFlagsKHR -VkGeometryInstanceFlagsNV -VkGeometryNV -VkGeometryTrianglesNV -VkGeometryTypeKHR -VkGeometryTypeNV -VkGraphicsPipelineCreateInfo -VkGraphicsPipelineLibraryCreateInfoEXT -VkGraphicsPipelineLibraryFlagBitsEXT -VkGraphicsPipelineLibraryFlagsEXT -VkGraphicsPipelineShaderGroupsCreateInfoNV -VkGraphicsShaderGroupCreateInfoNV -VkHdrMetadataEXT -VkHeadlessSurfaceCreateFlagsEXT -VkHeadlessSurfaceCreateInfoEXT -VkImageAspectFlagBits -VkImageAspectFlags -VkImageBlit -VkImageBlit2 -VkImageBlit2KHR -VkImageCaptureDescriptorDataInfoEXT -VkImageCompressionControlEXT -VkImageCompressionFixedRateFlagBitsEXT -VkImageCompressionFixedRateFlagsEXT -VkImageCompressionFlagBitsEXT -VkImageCompressionFlagsEXT -VkImageCompressionPropertiesEXT -VkImageCopy -VkImageCopy2 -VkImageCopy2KHR -VkImageCreateFlagBits -VkImageCreateFlags -VkImageCreateInfo -VkImageDrmFormatModifierExplicitCreateInfoEXT -VkImageDrmFormatModifierListCreateInfoEXT -VkImageDrmFormatModifierPropertiesEXT -VkImageFormatListCreateInfo -VkImageFormatListCreateInfoKHR -VkImageFormatProperties -VkImageFormatProperties2 -VkImageFormatProperties2KHR -VkImageLayout -VkImageMemoryBarrier -VkImageMemoryBarrier2 -VkImageMemoryBarrier2KHR -VkImageMemoryRequirementsInfo2 -VkImageMemoryRequirementsInfo2KHR -VkImagePlaneMemoryRequirementsInfo -VkImagePlaneMemoryRequirementsInfoKHR -VkImageResolve -VkImageResolve2 -VkImageResolve2KHR -VkImageSparseMemoryRequirementsInfo2 -VkImageSparseMemoryRequirementsInfo2KHR -VkImageStencilUsageCreateInfo -VkImageStencilUsageCreateInfoEXT -VkImageSubresource -VkImageSubresource2EXT -VkImageSubresourceLayers -VkImageSubresourceRange -VkImageSwapchainCreateInfoKHR -VkImageTiling -VkImageType -VkImageUsageFlagBits -VkImageUsageFlags -VkImageViewASTCDecodeModeEXT -VkImageViewAddressPropertiesNVX -VkImageViewCaptureDescriptorDataInfoEXT -VkImageViewCreateFlagBits -VkImageViewCreateFlags -VkImageViewCreateInfo -VkImageViewHandleInfoNVX -VkImageViewMinLodCreateInfoEXT -VkImageViewSampleWeightCreateInfoQCOM -VkImageViewType -VkImageViewUsageCreateInfo -VkImageViewUsageCreateInfoKHR -VkImportFenceFdInfoKHR -VkImportMemoryFdInfoKHR -VkImportMemoryHostPointerInfoEXT -VkImportSemaphoreFdInfoKHR -VkIndexType -VkIndirectCommandsLayoutCreateInfoNV -VkIndirectCommandsLayoutTokenNV -VkIndirectCommandsLayoutUsageFlagBitsNV -VkIndirectCommandsLayoutUsageFlagsNV -VkIndirectCommandsStreamNV -VkIndirectCommandsTokenTypeNV -VkIndirectStateFlagBitsNV -VkIndirectStateFlagsNV -VkInitializePerformanceApiInfoINTEL -VkInputAttachmentAspectReference -VkInputAttachmentAspectReferenceKHR -VkInstanceCreateFlagBits -VkInstanceCreateFlags -VkInstanceCreateInfo -VkInternalAllocationType -VkLayerProperties -VkLineRasterizationModeEXT -VkLogicOp -VkMappedMemoryRange -VkMemoryAllocateFlagBits -VkMemoryAllocateFlagBitsKHR -VkMemoryAllocateFlags -VkMemoryAllocateFlagsInfo -VkMemoryAllocateFlagsInfoKHR -VkMemoryAllocateFlagsKHR -VkMemoryAllocateInfo -VkMemoryBarrier -VkMemoryBarrier2 -VkMemoryBarrier2KHR -VkMemoryDecompressionMethodFlagBitsNV -VkMemoryDecompressionMethodFlagsNV -VkMemoryDedicatedAllocateInfo -VkMemoryDedicatedAllocateInfoKHR -VkMemoryDedicatedRequirements -VkMemoryDedicatedRequirementsKHR -VkMemoryFdPropertiesKHR -VkMemoryGetFdInfoKHR -VkMemoryGetRemoteAddressInfoNV -VkMemoryHeap -VkMemoryHeapFlagBits -VkMemoryHeapFlags -VkMemoryHostPointerPropertiesEXT -VkMemoryMapFlags -VkMemoryOpaqueCaptureAddressAllocateInfo -VkMemoryOpaqueCaptureAddressAllocateInfoKHR -VkMemoryOverallocationBehaviorAMD -VkMemoryPriorityAllocateInfoEXT -VkMemoryPropertyFlagBits -VkMemoryPropertyFlags -VkMemoryRequirements -VkMemoryRequirements2 -VkMemoryRequirements2KHR -VkMemoryType -VkMicromapBuildInfoEXT -VkMicromapBuildSizesInfoEXT -VkMicromapCreateFlagBitsEXT -VkMicromapCreateFlagsEXT -VkMicromapCreateInfoEXT -VkMicromapTriangleEXT -VkMicromapTypeEXT -VkMicromapUsageEXT -VkMicromapVersionInfoEXT -VkMultiDrawIndexedInfoEXT -VkMultiDrawInfoEXT -VkMultisamplePropertiesEXT -VkMultisampledRenderToSingleSampledInfoEXT -VkMultiviewPerViewAttributesInfoNVX -VkMutableDescriptorTypeCreateInfoEXT -VkMutableDescriptorTypeCreateInfoVALVE -VkMutableDescriptorTypeListEXT -VkMutableDescriptorTypeListVALVE -VkObjectType -VkOffset2D -VkOffset3D -VkOpacityMicromapFormatEXT -VkOpacityMicromapSpecialIndexEXT -VkOpaqueCaptureDescriptorDataCreateInfoEXT -VkOpticalFlowExecuteFlagBitsNV -VkOpticalFlowExecuteFlagsNV -VkOpticalFlowExecuteInfoNV -VkOpticalFlowGridSizeFlagBitsNV -VkOpticalFlowGridSizeFlagsNV -VkOpticalFlowImageFormatInfoNV -VkOpticalFlowImageFormatPropertiesNV -VkOpticalFlowPerformanceLevelNV -VkOpticalFlowSessionBindingPointNV -VkOpticalFlowSessionCreateFlagBitsNV -VkOpticalFlowSessionCreateFlagsNV -VkOpticalFlowSessionCreateInfoNV -VkOpticalFlowSessionCreatePrivateDataInfoNV -VkOpticalFlowUsageFlagBitsNV -VkOpticalFlowUsageFlagsNV -VkPastPresentationTimingGOOGLE -VkPeerMemoryFeatureFlagBits -VkPeerMemoryFeatureFlagBitsKHR -VkPeerMemoryFeatureFlags -VkPeerMemoryFeatureFlagsKHR -VkPerformanceConfigurationAcquireInfoINTEL -VkPerformanceConfigurationTypeINTEL -VkPerformanceCounterDescriptionFlagBitsKHR -VkPerformanceCounterDescriptionFlagsKHR -VkPerformanceCounterDescriptionKHR -VkPerformanceCounterKHR -VkPerformanceCounterResultKHR -VkPerformanceCounterScopeKHR -VkPerformanceCounterStorageKHR -VkPerformanceCounterUnitKHR -VkPerformanceMarkerInfoINTEL -VkPerformanceOverrideInfoINTEL -VkPerformanceOverrideTypeINTEL -VkPerformanceParameterTypeINTEL -VkPerformanceQuerySubmitInfoKHR -VkPerformanceStreamMarkerInfoINTEL -VkPerformanceValueDataINTEL -VkPerformanceValueINTEL -VkPerformanceValueTypeINTEL -VkPhysicalDevice16BitStorageFeatures -VkPhysicalDevice16BitStorageFeaturesKHR -VkPhysicalDevice4444FormatsFeaturesEXT -VkPhysicalDevice8BitStorageFeatures -VkPhysicalDevice8BitStorageFeaturesKHR -VkPhysicalDeviceASTCDecodeFeaturesEXT -VkPhysicalDeviceAccelerationStructureFeaturesKHR -VkPhysicalDeviceAccelerationStructurePropertiesKHR -VkPhysicalDeviceAddressBindingReportFeaturesEXT -VkPhysicalDeviceAmigoProfilingFeaturesSEC -VkPhysicalDeviceAttachmentFeedbackLoopLayoutFeaturesEXT -VkPhysicalDeviceBlendOperationAdvancedFeaturesEXT -VkPhysicalDeviceBlendOperationAdvancedPropertiesEXT -VkPhysicalDeviceBorderColorSwizzleFeaturesEXT -VkPhysicalDeviceBufferAddressFeaturesEXT -VkPhysicalDeviceBufferDeviceAddressFeatures -VkPhysicalDeviceBufferDeviceAddressFeaturesEXT -VkPhysicalDeviceBufferDeviceAddressFeaturesKHR -VkPhysicalDeviceCoherentMemoryFeaturesAMD -VkPhysicalDeviceColorWriteEnableFeaturesEXT -VkPhysicalDeviceComputeShaderDerivativesFeaturesNV -VkPhysicalDeviceConditionalRenderingFeaturesEXT -VkPhysicalDeviceConservativeRasterizationPropertiesEXT -VkPhysicalDeviceCooperativeMatrixFeaturesNV -VkPhysicalDeviceCooperativeMatrixPropertiesNV -VkPhysicalDeviceCopyMemoryIndirectFeaturesNV -VkPhysicalDeviceCopyMemoryIndirectPropertiesNV -VkPhysicalDeviceCornerSampledImageFeaturesNV -VkPhysicalDeviceCoverageReductionModeFeaturesNV -VkPhysicalDeviceCustomBorderColorFeaturesEXT -VkPhysicalDeviceCustomBorderColorPropertiesEXT -VkPhysicalDeviceDedicatedAllocationImageAliasingFeaturesNV -VkPhysicalDeviceDepthClampZeroOneFeaturesEXT -VkPhysicalDeviceDepthClipControlFeaturesEXT -VkPhysicalDeviceDepthClipEnableFeaturesEXT -VkPhysicalDeviceDepthStencilResolveProperties -VkPhysicalDeviceDepthStencilResolvePropertiesKHR -VkPhysicalDeviceDescriptorBufferDensityMapPropertiesEXT -VkPhysicalDeviceDescriptorBufferFeaturesEXT -VkPhysicalDeviceDescriptorBufferPropertiesEXT -VkPhysicalDeviceDescriptorIndexingFeatures -VkPhysicalDeviceDescriptorIndexingFeaturesEXT -VkPhysicalDeviceDescriptorIndexingProperties -VkPhysicalDeviceDescriptorIndexingPropertiesEXT -VkPhysicalDeviceDescriptorSetHostMappingFeaturesVALVE -VkPhysicalDeviceDeviceGeneratedCommandsFeaturesNV -VkPhysicalDeviceDeviceGeneratedCommandsPropertiesNV -VkPhysicalDeviceDeviceMemoryReportFeaturesEXT -VkPhysicalDeviceDiagnosticsConfigFeaturesNV -VkPhysicalDeviceDiscardRectanglePropertiesEXT -VkPhysicalDeviceDriverProperties -VkPhysicalDeviceDriverPropertiesKHR -VkPhysicalDeviceDrmPropertiesEXT -VkPhysicalDeviceDynamicRenderingFeatures -VkPhysicalDeviceDynamicRenderingFeaturesKHR -VkPhysicalDeviceExclusiveScissorFeaturesNV -VkPhysicalDeviceExtendedDynamicState2FeaturesEXT -VkPhysicalDeviceExtendedDynamicState3FeaturesEXT -VkPhysicalDeviceExtendedDynamicState3PropertiesEXT -VkPhysicalDeviceExtendedDynamicStateFeaturesEXT -VkPhysicalDeviceExternalBufferInfo -VkPhysicalDeviceExternalBufferInfoKHR -VkPhysicalDeviceExternalFenceInfo -VkPhysicalDeviceExternalFenceInfoKHR -VkPhysicalDeviceExternalImageFormatInfo -VkPhysicalDeviceExternalImageFormatInfoKHR -VkPhysicalDeviceExternalMemoryHostPropertiesEXT -VkPhysicalDeviceExternalMemoryRDMAFeaturesNV -VkPhysicalDeviceExternalSemaphoreInfo -VkPhysicalDeviceExternalSemaphoreInfoKHR -VkPhysicalDeviceFaultFeaturesEXT -VkPhysicalDeviceFeatures -VkPhysicalDeviceFeatures2 -VkPhysicalDeviceFeatures2KHR -VkPhysicalDeviceFloat16Int8FeaturesKHR -VkPhysicalDeviceFloatControlsProperties -VkPhysicalDeviceFloatControlsPropertiesKHR -VkPhysicalDeviceFragmentDensityMap2FeaturesEXT -VkPhysicalDeviceFragmentDensityMap2PropertiesEXT -VkPhysicalDeviceFragmentDensityMapFeaturesEXT -VkPhysicalDeviceFragmentDensityMapOffsetFeaturesQCOM -VkPhysicalDeviceFragmentDensityMapOffsetPropertiesQCOM -VkPhysicalDeviceFragmentDensityMapPropertiesEXT -VkPhysicalDeviceFragmentShaderBarycentricFeaturesKHR -VkPhysicalDeviceFragmentShaderBarycentricFeaturesNV -VkPhysicalDeviceFragmentShaderBarycentricPropertiesKHR -VkPhysicalDeviceFragmentShaderInterlockFeaturesEXT -VkPhysicalDeviceFragmentShadingRateEnumsFeaturesNV -VkPhysicalDeviceFragmentShadingRateEnumsPropertiesNV -VkPhysicalDeviceFragmentShadingRateFeaturesKHR -VkPhysicalDeviceFragmentShadingRateKHR -VkPhysicalDeviceFragmentShadingRatePropertiesKHR -VkPhysicalDeviceGlobalPriorityQueryFeaturesEXT -VkPhysicalDeviceGlobalPriorityQueryFeaturesKHR -VkPhysicalDeviceGraphicsPipelineLibraryFeaturesEXT -VkPhysicalDeviceGraphicsPipelineLibraryPropertiesEXT -VkPhysicalDeviceGroupProperties -VkPhysicalDeviceGroupPropertiesKHR -VkPhysicalDeviceHostQueryResetFeatures -VkPhysicalDeviceHostQueryResetFeaturesEXT -VkPhysicalDeviceIDProperties -VkPhysicalDeviceIDPropertiesKHR -VkPhysicalDeviceImage2DViewOf3DFeaturesEXT -VkPhysicalDeviceImageCompressionControlFeaturesEXT -VkPhysicalDeviceImageCompressionControlSwapchainFeaturesEXT -VkPhysicalDeviceImageDrmFormatModifierInfoEXT -VkPhysicalDeviceImageFormatInfo2 -VkPhysicalDeviceImageFormatInfo2KHR -VkPhysicalDeviceImageProcessingFeaturesQCOM -VkPhysicalDeviceImageProcessingPropertiesQCOM -VkPhysicalDeviceImageRobustnessFeatures -VkPhysicalDeviceImageRobustnessFeaturesEXT -VkPhysicalDeviceImageViewImageFormatInfoEXT -VkPhysicalDeviceImageViewMinLodFeaturesEXT -VkPhysicalDeviceImagelessFramebufferFeatures -VkPhysicalDeviceImagelessFramebufferFeaturesKHR -VkPhysicalDeviceIndexTypeUint8FeaturesEXT -VkPhysicalDeviceInheritedViewportScissorFeaturesNV -VkPhysicalDeviceInlineUniformBlockFeatures -VkPhysicalDeviceInlineUniformBlockFeaturesEXT -VkPhysicalDeviceInlineUniformBlockProperties -VkPhysicalDeviceInlineUniformBlockPropertiesEXT -VkPhysicalDeviceInvocationMaskFeaturesHUAWEI -VkPhysicalDeviceLegacyDitheringFeaturesEXT -VkPhysicalDeviceLimits -VkPhysicalDeviceLineRasterizationFeaturesEXT -VkPhysicalDeviceLineRasterizationPropertiesEXT -VkPhysicalDeviceLinearColorAttachmentFeaturesNV -VkPhysicalDeviceMaintenance3Properties -VkPhysicalDeviceMaintenance3PropertiesKHR -VkPhysicalDeviceMaintenance4Features -VkPhysicalDeviceMaintenance4FeaturesKHR -VkPhysicalDeviceMaintenance4Properties -VkPhysicalDeviceMaintenance4PropertiesKHR -VkPhysicalDeviceMemoryBudgetPropertiesEXT -VkPhysicalDeviceMemoryDecompressionFeaturesNV -VkPhysicalDeviceMemoryDecompressionPropertiesNV -VkPhysicalDeviceMemoryPriorityFeaturesEXT -VkPhysicalDeviceMemoryProperties -VkPhysicalDeviceMemoryProperties2 -VkPhysicalDeviceMemoryProperties2KHR -VkPhysicalDeviceMeshShaderFeaturesEXT -VkPhysicalDeviceMeshShaderFeaturesNV -VkPhysicalDeviceMeshShaderPropertiesEXT -VkPhysicalDeviceMeshShaderPropertiesNV -VkPhysicalDeviceMultiDrawFeaturesEXT -VkPhysicalDeviceMultiDrawPropertiesEXT -VkPhysicalDeviceMultisampledRenderToSingleSampledFeaturesEXT -VkPhysicalDeviceMultiviewFeatures -VkPhysicalDeviceMultiviewFeaturesKHR -VkPhysicalDeviceMultiviewPerViewAttributesPropertiesNVX -VkPhysicalDeviceMultiviewProperties -VkPhysicalDeviceMultiviewPropertiesKHR -VkPhysicalDeviceMutableDescriptorTypeFeaturesEXT -VkPhysicalDeviceMutableDescriptorTypeFeaturesVALVE -VkPhysicalDeviceNonSeamlessCubeMapFeaturesEXT -VkPhysicalDeviceOpacityMicromapFeaturesEXT -VkPhysicalDeviceOpacityMicromapPropertiesEXT -VkPhysicalDeviceOpticalFlowFeaturesNV -VkPhysicalDeviceOpticalFlowPropertiesNV -VkPhysicalDevicePCIBusInfoPropertiesEXT -VkPhysicalDevicePageableDeviceLocalMemoryFeaturesEXT -VkPhysicalDevicePerformanceQueryFeaturesKHR -VkPhysicalDevicePerformanceQueryPropertiesKHR -VkPhysicalDevicePipelineCreationCacheControlFeatures -VkPhysicalDevicePipelineCreationCacheControlFeaturesEXT -VkPhysicalDevicePipelineExecutablePropertiesFeaturesKHR -VkPhysicalDevicePipelinePropertiesFeaturesEXT -VkPhysicalDevicePipelineProtectedAccessFeaturesEXT -VkPhysicalDevicePipelineRobustnessFeaturesEXT -VkPhysicalDevicePipelineRobustnessPropertiesEXT -VkPhysicalDevicePointClippingProperties -VkPhysicalDevicePointClippingPropertiesKHR -VkPhysicalDevicePresentBarrierFeaturesNV -VkPhysicalDevicePresentIdFeaturesKHR -VkPhysicalDevicePresentWaitFeaturesKHR -VkPhysicalDevicePrimitiveTopologyListRestartFeaturesEXT -VkPhysicalDevicePrimitivesGeneratedQueryFeaturesEXT -VkPhysicalDevicePrivateDataFeatures -VkPhysicalDevicePrivateDataFeaturesEXT -VkPhysicalDeviceProperties -VkPhysicalDeviceProperties2 -VkPhysicalDeviceProperties2KHR -VkPhysicalDeviceProtectedMemoryFeatures -VkPhysicalDeviceProtectedMemoryProperties -VkPhysicalDeviceProvokingVertexFeaturesEXT -VkPhysicalDeviceProvokingVertexPropertiesEXT -VkPhysicalDevicePushDescriptorPropertiesKHR -VkPhysicalDeviceRGBA10X6FormatsFeaturesEXT -VkPhysicalDeviceRasterizationOrderAttachmentAccessFeaturesARM -VkPhysicalDeviceRasterizationOrderAttachmentAccessFeaturesEXT -VkPhysicalDeviceRayQueryFeaturesKHR -VkPhysicalDeviceRayTracingInvocationReorderFeaturesNV -VkPhysicalDeviceRayTracingInvocationReorderPropertiesNV -VkPhysicalDeviceRayTracingMaintenance1FeaturesKHR -VkPhysicalDeviceRayTracingMotionBlurFeaturesNV -VkPhysicalDeviceRayTracingPipelineFeaturesKHR -VkPhysicalDeviceRayTracingPipelinePropertiesKHR -VkPhysicalDeviceRayTracingPropertiesNV -VkPhysicalDeviceRepresentativeFragmentTestFeaturesNV -VkPhysicalDeviceRobustness2FeaturesEXT -VkPhysicalDeviceRobustness2PropertiesEXT -VkPhysicalDeviceSampleLocationsPropertiesEXT -VkPhysicalDeviceSamplerFilterMinmaxProperties -VkPhysicalDeviceSamplerFilterMinmaxPropertiesEXT -VkPhysicalDeviceSamplerYcbcrConversionFeatures -VkPhysicalDeviceSamplerYcbcrConversionFeaturesKHR -VkPhysicalDeviceScalarBlockLayoutFeatures -VkPhysicalDeviceScalarBlockLayoutFeaturesEXT -VkPhysicalDeviceSeparateDepthStencilLayoutsFeatures -VkPhysicalDeviceSeparateDepthStencilLayoutsFeaturesKHR -VkPhysicalDeviceShaderAtomicFloat2FeaturesEXT -VkPhysicalDeviceShaderAtomicFloatFeaturesEXT -VkPhysicalDeviceShaderAtomicInt64Features -VkPhysicalDeviceShaderAtomicInt64FeaturesKHR -VkPhysicalDeviceShaderClockFeaturesKHR -VkPhysicalDeviceShaderCoreBuiltinsFeaturesARM -VkPhysicalDeviceShaderCoreBuiltinsPropertiesARM -VkPhysicalDeviceShaderCoreProperties2AMD -VkPhysicalDeviceShaderCorePropertiesAMD -VkPhysicalDeviceShaderDemoteToHelperInvocationFeatures -VkPhysicalDeviceShaderDemoteToHelperInvocationFeaturesEXT -VkPhysicalDeviceShaderDrawParameterFeatures -VkPhysicalDeviceShaderDrawParametersFeatures -VkPhysicalDeviceShaderEarlyAndLateFragmentTestsFeaturesAMD -VkPhysicalDeviceShaderFloat16Int8Features -VkPhysicalDeviceShaderFloat16Int8FeaturesKHR -VkPhysicalDeviceShaderImageAtomicInt64FeaturesEXT -VkPhysicalDeviceShaderImageFootprintFeaturesNV -VkPhysicalDeviceShaderIntegerDotProductFeatures -VkPhysicalDeviceShaderIntegerDotProductFeaturesKHR -VkPhysicalDeviceShaderIntegerDotProductProperties -VkPhysicalDeviceShaderIntegerDotProductPropertiesKHR -VkPhysicalDeviceShaderIntegerFunctions2FeaturesINTEL -VkPhysicalDeviceShaderModuleIdentifierFeaturesEXT -VkPhysicalDeviceShaderModuleIdentifierPropertiesEXT -VkPhysicalDeviceShaderSMBuiltinsFeaturesNV -VkPhysicalDeviceShaderSMBuiltinsPropertiesNV -VkPhysicalDeviceShaderSubgroupExtendedTypesFeatures -VkPhysicalDeviceShaderSubgroupExtendedTypesFeaturesKHR -VkPhysicalDeviceShaderSubgroupUniformControlFlowFeaturesKHR -VkPhysicalDeviceShaderTerminateInvocationFeatures -VkPhysicalDeviceShaderTerminateInvocationFeaturesKHR -VkPhysicalDeviceShadingRateImageFeaturesNV -VkPhysicalDeviceShadingRateImagePropertiesNV -VkPhysicalDeviceSparseImageFormatInfo2 -VkPhysicalDeviceSparseImageFormatInfo2KHR -VkPhysicalDeviceSparseProperties -VkPhysicalDeviceSubgroupProperties -VkPhysicalDeviceSubgroupSizeControlFeatures -VkPhysicalDeviceSubgroupSizeControlFeaturesEXT -VkPhysicalDeviceSubgroupSizeControlProperties -VkPhysicalDeviceSubgroupSizeControlPropertiesEXT -VkPhysicalDeviceSubpassMergeFeedbackFeaturesEXT -VkPhysicalDeviceSubpassShadingFeaturesHUAWEI -VkPhysicalDeviceSubpassShadingPropertiesHUAWEI -VkPhysicalDeviceSurfaceInfo2KHR -VkPhysicalDeviceSynchronization2Features -VkPhysicalDeviceSynchronization2FeaturesKHR -VkPhysicalDeviceTexelBufferAlignmentFeaturesEXT -VkPhysicalDeviceTexelBufferAlignmentProperties -VkPhysicalDeviceTexelBufferAlignmentPropertiesEXT -VkPhysicalDeviceTextureCompressionASTCHDRFeatures -VkPhysicalDeviceTextureCompressionASTCHDRFeaturesEXT -VkPhysicalDeviceTilePropertiesFeaturesQCOM -VkPhysicalDeviceTimelineSemaphoreFeatures -VkPhysicalDeviceTimelineSemaphoreFeaturesKHR -VkPhysicalDeviceTimelineSemaphoreProperties -VkPhysicalDeviceTimelineSemaphorePropertiesKHR -VkPhysicalDeviceToolProperties -VkPhysicalDeviceToolPropertiesEXT -VkPhysicalDeviceTransformFeedbackFeaturesEXT -VkPhysicalDeviceTransformFeedbackPropertiesEXT -VkPhysicalDeviceType -VkPhysicalDeviceUniformBufferStandardLayoutFeatures -VkPhysicalDeviceUniformBufferStandardLayoutFeaturesKHR -VkPhysicalDeviceVariablePointerFeatures -VkPhysicalDeviceVariablePointerFeaturesKHR -VkPhysicalDeviceVariablePointersFeatures -VkPhysicalDeviceVariablePointersFeaturesKHR -VkPhysicalDeviceVertexAttributeDivisorFeaturesEXT -VkPhysicalDeviceVertexAttributeDivisorPropertiesEXT -VkPhysicalDeviceVertexInputDynamicStateFeaturesEXT -VkPhysicalDeviceVulkan11Features -VkPhysicalDeviceVulkan11Properties -VkPhysicalDeviceVulkan12Features -VkPhysicalDeviceVulkan12Properties -VkPhysicalDeviceVulkan13Features -VkPhysicalDeviceVulkan13Properties -VkPhysicalDeviceVulkanMemoryModelFeatures -VkPhysicalDeviceVulkanMemoryModelFeaturesKHR -VkPhysicalDeviceWorkgroupMemoryExplicitLayoutFeaturesKHR -VkPhysicalDeviceYcbcr2Plane444FormatsFeaturesEXT -VkPhysicalDeviceYcbcrImageArraysFeaturesEXT -VkPhysicalDeviceZeroInitializeWorkgroupMemoryFeatures -VkPhysicalDeviceZeroInitializeWorkgroupMemoryFeaturesKHR -VkPipelineBindPoint -VkPipelineCacheCreateFlagBits -VkPipelineCacheCreateFlags -VkPipelineCacheCreateInfo -VkPipelineCacheHeaderVersion -VkPipelineCacheHeaderVersionOne -VkPipelineColorBlendAdvancedStateCreateInfoEXT -VkPipelineColorBlendAttachmentState -VkPipelineColorBlendStateCreateFlagBits -VkPipelineColorBlendStateCreateFlags -VkPipelineColorBlendStateCreateInfo -VkPipelineColorWriteCreateInfoEXT -VkPipelineCompilerControlCreateInfoAMD -VkPipelineCompilerControlFlagBitsAMD -VkPipelineCompilerControlFlagsAMD -VkPipelineCoverageModulationStateCreateFlagsNV -VkPipelineCoverageModulationStateCreateInfoNV -VkPipelineCoverageReductionStateCreateFlagsNV -VkPipelineCoverageReductionStateCreateInfoNV -VkPipelineCoverageToColorStateCreateFlagsNV -VkPipelineCoverageToColorStateCreateInfoNV -VkPipelineCreateFlagBits -VkPipelineCreateFlags -VkPipelineCreationFeedback -VkPipelineCreationFeedbackCreateInfo -VkPipelineCreationFeedbackCreateInfoEXT -VkPipelineCreationFeedbackEXT -VkPipelineCreationFeedbackFlagBits -VkPipelineCreationFeedbackFlagBitsEXT -VkPipelineCreationFeedbackFlags -VkPipelineCreationFeedbackFlagsEXT -VkPipelineDepthStencilStateCreateFlagBits -VkPipelineDepthStencilStateCreateFlags -VkPipelineDepthStencilStateCreateInfo -VkPipelineDiscardRectangleStateCreateFlagsEXT -VkPipelineDiscardRectangleStateCreateInfoEXT -VkPipelineDynamicStateCreateFlags -VkPipelineDynamicStateCreateInfo -VkPipelineExecutableInfoKHR -VkPipelineExecutableInternalRepresentationKHR -VkPipelineExecutablePropertiesKHR -VkPipelineExecutableStatisticFormatKHR -VkPipelineExecutableStatisticKHR -VkPipelineExecutableStatisticValueKHR -VkPipelineFragmentShadingRateEnumStateCreateInfoNV -VkPipelineFragmentShadingRateStateCreateInfoKHR -VkPipelineInfoEXT -VkPipelineInfoKHR -VkPipelineInputAssemblyStateCreateFlags -VkPipelineInputAssemblyStateCreateInfo -VkPipelineLayoutCreateFlagBits -VkPipelineLayoutCreateFlags -VkPipelineLayoutCreateInfo -VkPipelineLibraryCreateInfoKHR -VkPipelineMultisampleStateCreateFlags -VkPipelineMultisampleStateCreateInfo -VkPipelinePropertiesIdentifierEXT -VkPipelineRasterizationConservativeStateCreateFlagsEXT -VkPipelineRasterizationConservativeStateCreateInfoEXT -VkPipelineRasterizationDepthClipStateCreateFlagsEXT -VkPipelineRasterizationDepthClipStateCreateInfoEXT -VkPipelineRasterizationLineStateCreateInfoEXT -VkPipelineRasterizationProvokingVertexStateCreateInfoEXT -VkPipelineRasterizationStateCreateFlags -VkPipelineRasterizationStateCreateInfo -VkPipelineRasterizationStateRasterizationOrderAMD -VkPipelineRasterizationStateStreamCreateFlagsEXT -VkPipelineRasterizationStateStreamCreateInfoEXT -VkPipelineRenderingCreateInfo -VkPipelineRenderingCreateInfoKHR -VkPipelineRepresentativeFragmentTestStateCreateInfoNV -VkPipelineRobustnessBufferBehaviorEXT -VkPipelineRobustnessCreateInfoEXT -VkPipelineRobustnessImageBehaviorEXT -VkPipelineSampleLocationsStateCreateInfoEXT -VkPipelineShaderStageCreateFlagBits -VkPipelineShaderStageCreateFlags -VkPipelineShaderStageCreateInfo -VkPipelineShaderStageModuleIdentifierCreateInfoEXT -VkPipelineShaderStageRequiredSubgroupSizeCreateInfo -VkPipelineShaderStageRequiredSubgroupSizeCreateInfoEXT -VkPipelineStageFlagBits -VkPipelineStageFlagBits2 -VkPipelineStageFlagBits2KHR -VkPipelineStageFlags -VkPipelineStageFlags2 -VkPipelineStageFlags2KHR -VkPipelineTessellationDomainOriginStateCreateInfo -VkPipelineTessellationDomainOriginStateCreateInfoKHR -VkPipelineTessellationStateCreateFlags -VkPipelineTessellationStateCreateInfo -VkPipelineVertexInputDivisorStateCreateInfoEXT -VkPipelineVertexInputStateCreateFlags -VkPipelineVertexInputStateCreateInfo -VkPipelineViewportCoarseSampleOrderStateCreateInfoNV -VkPipelineViewportDepthClipControlCreateInfoEXT -VkPipelineViewportExclusiveScissorStateCreateInfoNV -VkPipelineViewportShadingRateImageStateCreateInfoNV -VkPipelineViewportStateCreateFlags -VkPipelineViewportStateCreateInfo -VkPipelineViewportSwizzleStateCreateFlagsNV -VkPipelineViewportSwizzleStateCreateInfoNV -VkPipelineViewportWScalingStateCreateInfoNV -VkPointClippingBehavior -VkPointClippingBehaviorKHR -VkPolygonMode -VkPresentIdKHR -VkPresentInfoKHR -VkPresentModeKHR -VkPresentRegionKHR -VkPresentRegionsKHR -VkPresentTimeGOOGLE -VkPresentTimesInfoGOOGLE -VkPrimitiveTopology -VkPrivateDataSlotCreateFlags -VkPrivateDataSlotCreateFlagsEXT -VkPrivateDataSlotCreateInfo -VkPrivateDataSlotCreateInfoEXT -VkPrivateDataSlotEXT -VkProtectedSubmitInfo -VkProvokingVertexModeEXT -VkPushConstantRange -VkQueryControlFlagBits -VkQueryControlFlags -VkQueryPipelineStatisticFlagBits -VkQueryPipelineStatisticFlags -VkQueryPoolCreateFlags -VkQueryPoolCreateInfo -VkQueryPoolCreateInfoINTEL -VkQueryPoolPerformanceCreateInfoKHR -VkQueryPoolPerformanceQueryCreateInfoINTEL -VkQueryPoolSamplingModeINTEL -VkQueryResultFlagBits -VkQueryResultFlags -VkQueryType -VkQueueFamilyCheckpointProperties2NV -VkQueueFamilyCheckpointPropertiesNV -VkQueueFamilyGlobalPriorityPropertiesEXT -VkQueueFamilyGlobalPriorityPropertiesKHR -VkQueueFamilyProperties -VkQueueFamilyProperties2 -VkQueueFamilyProperties2KHR -VkQueueFlagBits -VkQueueFlags -VkQueueGlobalPriorityEXT -VkQueueGlobalPriorityKHR -VkRasterizationOrderAMD -VkRayTracingInvocationReorderModeNV -VkRayTracingPipelineCreateInfoKHR -VkRayTracingPipelineCreateInfoNV -VkRayTracingPipelineInterfaceCreateInfoKHR -VkRayTracingShaderGroupCreateInfoKHR -VkRayTracingShaderGroupCreateInfoNV -VkRayTracingShaderGroupTypeKHR -VkRayTracingShaderGroupTypeNV -VkRect2D -VkRectLayerKHR -VkRefreshCycleDurationGOOGLE -VkRemoteAddressNV -VkRenderPassAttachmentBeginInfo -VkRenderPassAttachmentBeginInfoKHR -VkRenderPassBeginInfo -VkRenderPassCreateFlagBits -VkRenderPassCreateFlags -VkRenderPassCreateInfo -VkRenderPassCreateInfo2 -VkRenderPassCreateInfo2KHR -VkRenderPassCreationControlEXT -VkRenderPassCreationFeedbackCreateInfoEXT -VkRenderPassCreationFeedbackInfoEXT -VkRenderPassFragmentDensityMapCreateInfoEXT -VkRenderPassInputAttachmentAspectCreateInfo -VkRenderPassInputAttachmentAspectCreateInfoKHR -VkRenderPassMultiviewCreateInfo -VkRenderPassMultiviewCreateInfoKHR -VkRenderPassSampleLocationsBeginInfoEXT -VkRenderPassSubpassFeedbackCreateInfoEXT -VkRenderPassSubpassFeedbackInfoEXT -VkRenderPassTransformBeginInfoQCOM -VkRenderingAttachmentInfo -VkRenderingAttachmentInfoKHR -VkRenderingFlagBits -VkRenderingFlagBitsKHR -VkRenderingFlags -VkRenderingFlagsKHR -VkRenderingFragmentDensityMapAttachmentInfoEXT -VkRenderingFragmentShadingRateAttachmentInfoKHR -VkRenderingInfo -VkRenderingInfoKHR -VkResolveImageInfo2 -VkResolveImageInfo2KHR -VkResolveModeFlagBits -VkResolveModeFlagBitsKHR -VkResolveModeFlags -VkResolveModeFlagsKHR -VkResult -VkSRTDataNV -VkSampleCountFlagBits -VkSampleCountFlags -VkSampleLocationEXT -VkSampleLocationsInfoEXT -VkSampleMask -VkSamplerAddressMode -VkSamplerBorderColorComponentMappingCreateInfoEXT -VkSamplerCaptureDescriptorDataInfoEXT -VkSamplerCreateFlagBits -VkSamplerCreateFlags -VkSamplerCreateInfo -VkSamplerCustomBorderColorCreateInfoEXT -VkSamplerMipmapMode -VkSamplerReductionMode -VkSamplerReductionModeCreateInfo -VkSamplerReductionModeCreateInfoEXT -VkSamplerReductionModeEXT -VkSamplerYcbcrConversionCreateInfo -VkSamplerYcbcrConversionCreateInfoKHR -VkSamplerYcbcrConversionImageFormatProperties -VkSamplerYcbcrConversionImageFormatPropertiesKHR -VkSamplerYcbcrConversionInfo -VkSamplerYcbcrConversionInfoKHR -VkSamplerYcbcrConversionKHR -VkSamplerYcbcrModelConversion -VkSamplerYcbcrModelConversionKHR -VkSamplerYcbcrRange -VkSamplerYcbcrRangeKHR -VkScopeNV -VkSemaphoreCreateFlags -VkSemaphoreCreateInfo -VkSemaphoreGetFdInfoKHR -VkSemaphoreImportFlagBits -VkSemaphoreImportFlagBitsKHR -VkSemaphoreImportFlags -VkSemaphoreImportFlagsKHR -VkSemaphoreSignalInfo -VkSemaphoreSignalInfoKHR -VkSemaphoreSubmitInfo -VkSemaphoreSubmitInfoKHR -VkSemaphoreType -VkSemaphoreTypeCreateInfo -VkSemaphoreTypeCreateInfoKHR -VkSemaphoreTypeKHR -VkSemaphoreWaitFlagBits -VkSemaphoreWaitFlagBitsKHR -VkSemaphoreWaitFlags -VkSemaphoreWaitFlagsKHR -VkSemaphoreWaitInfo -VkSemaphoreWaitInfoKHR -VkSetStateFlagsIndirectCommandNV -VkShaderCorePropertiesFlagBitsAMD -VkShaderCorePropertiesFlagsAMD -VkShaderFloatControlsIndependence -VkShaderFloatControlsIndependenceKHR -VkShaderGroupShaderKHR -VkShaderInfoTypeAMD -VkShaderModuleCreateFlags -VkShaderModuleCreateInfo -VkShaderModuleIdentifierEXT -VkShaderModuleValidationCacheCreateInfoEXT -VkShaderResourceUsageAMD -VkShaderStageFlagBits -VkShaderStageFlags -VkShaderStatisticsInfoAMD -VkShadingRatePaletteEntryNV -VkShadingRatePaletteNV -VkSharedPresentSurfaceCapabilitiesKHR -VkSharingMode -VkSparseBufferMemoryBindInfo -VkSparseImageFormatFlagBits -VkSparseImageFormatFlags -VkSparseImageFormatProperties -VkSparseImageFormatProperties2 -VkSparseImageFormatProperties2KHR -VkSparseImageMemoryBind -VkSparseImageMemoryBindInfo -VkSparseImageMemoryRequirements -VkSparseImageMemoryRequirements2 -VkSparseImageMemoryRequirements2KHR -VkSparseImageOpaqueMemoryBindInfo -VkSparseMemoryBind -VkSparseMemoryBindFlagBits -VkSparseMemoryBindFlags -VkSpecializationInfo -VkSpecializationMapEntry -VkStencilFaceFlagBits -VkStencilFaceFlags -VkStencilOp -VkStencilOpState -VkStridedDeviceAddressRegionKHR -VkStructureType -VkSubgroupFeatureFlagBits -VkSubgroupFeatureFlags -VkSubmitFlagBits -VkSubmitFlagBitsKHR -VkSubmitFlags -VkSubmitFlagsKHR -VkSubmitInfo -VkSubmitInfo2 -VkSubmitInfo2KHR -VkSubpassBeginInfo -VkSubpassBeginInfoKHR -VkSubpassContents -VkSubpassDependency -VkSubpassDependency2 -VkSubpassDependency2KHR -VkSubpassDescription -VkSubpassDescription2 -VkSubpassDescription2KHR -VkSubpassDescriptionDepthStencilResolve -VkSubpassDescriptionDepthStencilResolveKHR -VkSubpassDescriptionFlagBits -VkSubpassDescriptionFlags -VkSubpassEndInfo -VkSubpassEndInfoKHR -VkSubpassFragmentDensityMapOffsetEndInfoQCOM -VkSubpassMergeStatusEXT -VkSubpassResolvePerformanceQueryEXT -VkSubpassSampleLocationsEXT -VkSubpassShadingPipelineCreateInfoHUAWEI -VkSubresourceLayout -VkSubresourceLayout2EXT -VkSurfaceCapabilities2EXT -VkSurfaceCapabilities2KHR -VkSurfaceCapabilitiesKHR -VkSurfaceCapabilitiesPresentBarrierNV -VkSurfaceCounterFlagBitsEXT -VkSurfaceCounterFlagsEXT -VkSurfaceFormat2KHR -VkSurfaceFormatKHR -VkSurfaceProtectedCapabilitiesKHR -VkSurfaceTransformFlagBitsKHR -VkSurfaceTransformFlagsKHR -VkSwapchainCounterCreateInfoEXT -VkSwapchainCreateFlagBitsKHR -VkSwapchainCreateFlagsKHR -VkSwapchainCreateInfoKHR -VkSwapchainDisplayNativeHdrCreateInfoAMD -VkSwapchainPresentBarrierCreateInfoNV -VkSystemAllocationScope -VkTessellationDomainOrigin -VkTessellationDomainOriginKHR -VkTextureLODGatherFormatPropertiesAMD -VkTilePropertiesQCOM -VkTimeDomainEXT -VkTimelineSemaphoreSubmitInfo -VkTimelineSemaphoreSubmitInfoKHR -VkToolPurposeFlagBits -VkToolPurposeFlagBitsEXT -VkToolPurposeFlags -VkToolPurposeFlagsEXT -VkTraceRaysIndirectCommand2KHR -VkTraceRaysIndirectCommandKHR -VkTransformMatrixKHR -VkTransformMatrixNV -VkValidationCacheCreateFlagsEXT -VkValidationCacheCreateInfoEXT -VkValidationCacheHeaderVersionEXT -VkValidationCheckEXT -VkValidationFeatureDisableEXT -VkValidationFeatureEnableEXT -VkValidationFeaturesEXT -VkValidationFlagsEXT -VkVendorId -VkVertexInputAttributeDescription -VkVertexInputAttributeDescription2EXT -VkVertexInputBindingDescription -VkVertexInputBindingDescription2EXT -VkVertexInputBindingDivisorDescriptionEXT -VkVertexInputRate -VkViewport -VkViewportCoordinateSwizzleNV -VkViewportSwizzleNV -VkViewportWScalingNV -VkWriteDescriptorSet -VkWriteDescriptorSetAccelerationStructureKHR -VkWriteDescriptorSetAccelerationStructureNV -VkWriteDescriptorSetInlineUniformBlock -VkWriteDescriptorSetInlineUniformBlockEXT -VkXYColorEXT diff --git a/vulkan_symbols/vulkan_wayland_types b/vulkan_symbols/vulkan_wayland_types deleted file mode 100644 index 719c866..0000000 --- a/vulkan_symbols/vulkan_wayland_types +++ /dev/null @@ -1,4 +0,0 @@ -PFN_vkCreateWaylandSurfaceKHR -PFN_vkGetPhysicalDeviceWaylandPresentationSupportKHR -VkWaylandSurfaceCreateFlagsKHR -VkWaylandSurfaceCreateInfoKHR diff --git a/vulkan_symbols/vulkan_xcb_types b/vulkan_symbols/vulkan_xcb_types deleted file mode 100644 index 113d212..0000000 --- a/vulkan_symbols/vulkan_xcb_types +++ /dev/null @@ -1,4 +0,0 @@ -PFN_vkCreateXcbSurfaceKHR -PFN_vkGetPhysicalDeviceXcbPresentationSupportKHR -VkXcbSurfaceCreateFlagsKHR -VkXcbSurfaceCreateInfoKHR diff --git a/vulkan_symbols/vulkan_xlib_types b/vulkan_symbols/vulkan_xlib_types deleted file mode 100644 index 9171a78..0000000 --- a/vulkan_symbols/vulkan_xlib_types +++ /dev/null @@ -1,4 +0,0 @@ -PFN_vkCreateXlibSurfaceKHR -PFN_vkGetPhysicalDeviceXlibPresentationSupportKHR -VkXlibSurfaceCreateFlagsKHR -VkXlibSurfaceCreateInfoKHR From a89a48dfb44caa759d97e0c5843a52f69c85b062 Mon Sep 17 00:00:00 2001 From: hodasemi Date: Wed, 11 Jan 2023 21:46:17 +0100 Subject: [PATCH 04/85] Use vk rs --- Cargo.toml | 5 +++-- src/enums.rs | 2 +- src/lib.rs | 2 +- src/structs.rs | 2 +- src/vk_handles.rs | 27 +++++---------------------- 5 files changed, 11 insertions(+), 27 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 97dd5e9..671d3f4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,5 +9,6 @@ edition = "2021" crate-type = ["cdylib"] [dependencies] -vulkan-sys = { git = "https://gavania.de/Gavania/Gavania.git", branch = "0.2.0_dev" } -anyhow = { version = "1.0.68", features = ["backtrace"] } \ No newline at end of file +# vulkan-rs = { git = "ssh://gitea@gavania.de:23/Gavania/Gavania.git", branch = "0.2.0_dev" } +vulkan-rs = { path = "/home/michael/Dokumente/Workspace/Gavania/vulkan-rs" } +anyhow = { version = "1.0.68", features = ["backtrace"] } diff --git a/src/enums.rs b/src/enums.rs index 592a93c..1f30d8c 100644 --- a/src/enums.rs +++ b/src/enums.rs @@ -1,7 +1,7 @@ #![allow(non_camel_case_types)] use std::{mem, ptr}; -use vulkan_sys::prelude::*; +use vulkan_rs::prelude::*; pub use VkLayerFunction::*; pub use VkNegotiateLayerStructType::*; diff --git a/src/lib.rs b/src/lib.rs index 411de48..61bdfd0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -14,7 +14,7 @@ use std::{ use enums::*; use structs::*; use vk_handles::*; -use vulkan_sys::prelude::*; +use vulkan_rs::prelude::*; const LOG_FILE: &'static str = "/home/michael/rf2_vk_hud.log"; diff --git a/src/structs.rs b/src/structs.rs index 4262bc7..f31a301 100644 --- a/src/structs.rs +++ b/src/structs.rs @@ -2,7 +2,7 @@ use crate::enums::*; -use vulkan_sys::prelude::*; +use vulkan_rs::prelude::*; use std::{ mem::{self, ManuallyDrop}, diff --git a/src/vk_handles.rs b/src/vk_handles.rs index 28b0afd..0f171be 100644 --- a/src/vk_handles.rs +++ b/src/vk_handles.rs @@ -1,6 +1,6 @@ use anyhow::Result; use std::{collections::HashMap, ffi::c_void, mem, ptr}; -use vulkan_sys::prelude::*; +use vulkan_rs::prelude::*; static mut FN_HANDLES: Option = None; @@ -19,21 +19,16 @@ pub fn set_vk_handles(handles: VkTypedefHandles) { pub struct VkTypedefHandles { typedefs: Vec, functions: HashMap, - instance: VkInstance, - device: VkDevice, } impl VkTypedefHandles { pub fn new() -> Result { - let fns = include_str!("../vk_functions"); - let symbols = fns.lines().map(|s| s.to_string()).collect(); - Ok(Self { - typedefs: symbols, + typedefs: include_str!("../vk_functions") + .lines() + .map(|s| s.to_string()) + .collect(), functions: HashMap::new(), - - instance: VkInstance::NULL_HANDLE, - device: VkDevice::NULL_HANDLE, }) } @@ -42,8 +37,6 @@ impl VkTypedefHandles { instance: VkInstance, proc_addr: PFN_vkGetInstanceProcAddr, ) { - self.instance = instance; - unsafe { for symbol in &self.typedefs { let name = VkString::new(symbol); @@ -57,8 +50,6 @@ impl VkTypedefHandles { } pub fn load_device_functions(&mut self, device: VkDevice, proc_addr: PFN_vkGetDeviceProcAddr) { - self.device = device; - unsafe { for symbol in &self.typedefs { let name = VkString::new(symbol); @@ -74,12 +65,4 @@ impl VkTypedefHandles { pub fn handle(&self, symbol_name: impl ToString) -> Option { self.functions.get(&symbol_name.to_string()).cloned() } - - pub fn instance(&self) -> VkInstance { - self.instance - } - - pub fn device(&self) -> VkDevice { - self.device - } } From c1add2e953f208a72faad643ab0f1dc4f7883ce7 Mon Sep 17 00:00:00 2001 From: hodasemi Date: Thu, 12 Jan 2023 09:17:24 +0100 Subject: [PATCH 05/85] Start adding function entries --- src/enums.rs | 4 ++ src/lib.rs | 129 +++++++++++++++++++++++++++++++++++++++++++-------- 2 files changed, 113 insertions(+), 20 deletions(-) diff --git a/src/enums.rs b/src/enums.rs index 1f30d8c..5eb56ae 100644 --- a/src/enums.rs +++ b/src/enums.rs @@ -25,6 +25,8 @@ pub enum Functions { DestroyInstance(PFN_vkDestroyInstance), CreateDevice(PFN_vkCreateDevice), DestroyDevice(PFN_vkDestroyDevice), + CreateSwapchain(PFN_vkCreateSwapchainKHR), + QueueSubmit(PFN_vkQueueSubmit), } impl Functions { @@ -37,6 +39,8 @@ impl Functions { Functions::DestroyInstance(func) => unsafe { mem::transmute(func) }, Functions::CreateDevice(func) => unsafe { mem::transmute(func) }, Functions::DestroyDevice(func) => unsafe { mem::transmute(func) }, + Functions::CreateSwapchain(func) => unsafe { mem::transmute(func) }, + Functions::QueueSubmit(func) => unsafe { mem::transmute(func) }, } } } diff --git a/src/lib.rs b/src/lib.rs index 61bdfd0..646c3a8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,11 +4,13 @@ pub mod structs; mod vk_handles; use std::{ + ffi::c_void, fs::{File, OpenOptions}, io::Write, mem, os::raw::c_char, ptr, + sync::Arc, }; use enums::*; @@ -18,6 +20,20 @@ use vulkan_rs::prelude::*; const LOG_FILE: &'static str = "/home/michael/rf2_vk_hud.log"; +static mut INSTANCE: Option> = None; +static mut DEVICE: Option> = None; +static mut SWAPCHAIN: Option> = None; +static mut QUEUE_SUBMIT: PFN_vkQueueSubmit = + unsafe { mem::transmute(vkVoidFunction as *const c_void) }; + +pub fn instance() -> Arc { + unsafe { INSTANCE.as_ref().unwrap().clone() } +} + +pub fn device() -> Arc { + unsafe { DEVICE.as_ref().unwrap().clone() } +} + #[no_mangle] #[allow(non_snake_case)] pub extern "C" fn vkNegotiateLoaderLayerInterfaceVersion( @@ -60,21 +76,16 @@ extern "system" fn get_device_proc_addr( let s = func_string.as_str(); - match s { - "vkCreateDevice" => return Functions::CreateDevice(create_device).convert(), - "vkDestroyDevice" => return Functions::DestroyDevice(destroy_device).convert(), - "vkCreateInstance" => return Functions::CreateInstance(create_instance).convert(), - "vkDestroyInstance" => return Functions::DestroyInstance(destroy_instance).convert(), - - _ => (), - }; + if let Some(func) = get_vk_func(s) { + return func.convert(); + } if let Some(func) = vk_handles().handle(s) { return func; } - write_log(format!("\trequested fn: {} in device proc addr", s)); - write_log(format!("\t-> not found")); + // write_log(format!("\trequested fn: {} in device proc addr", s)); + // write_log(format!("\t-> not found")); Functions::Null.convert() } @@ -93,21 +104,16 @@ extern "system" fn get_instance_proc_addr( let s = func_string.as_str(); - match s { - "vkCreateDevice" => return Functions::CreateDevice(create_device).convert(), - "vkDestroyDevice" => return Functions::DestroyDevice(destroy_device).convert(), - "vkCreateInstance" => return Functions::CreateInstance(create_instance).convert(), - "vkDestroyInstance" => return Functions::DestroyInstance(destroy_instance).convert(), - - _ => (), - }; + if let Some(func) = get_vk_func(s) { + return func.convert(); + } if let Some(func) = vk_handles().handle(s) { return func; } - write_log(format!("\trequested fn: {} in instance proc addr", s)); - write_log(format!("\t-> not found")); + // write_log(format!("\trequested fn: {} in instance proc addr", s)); + // write_log(format!("\t-> not found")); Functions::Null.convert() } @@ -147,6 +153,32 @@ extern "system" fn create_instance( write_log("-> successfully created instance."); + let ext_names = unsafe { (*create_info).extension_names() }; + + write_log(format!("{:?}", ext_names)); + + // DXVK workaround, it creates the instance twice with different properties + if ext_names.contains(&VkString::new("VK_KHR_surface")) { + unsafe { + let ins = match Instance::preinitialized( + *instance, + proc_addr, + &ext_names, + (*(*create_info).pApplicationInfo).apiVersion, + ) { + Ok(ins) => ins, + Err(err) => { + write_log(format!("-> local instance creation failed: {:?}", err)); + return VK_ERROR_INITIALIZATION_FAILED; + } + }; + + INSTANCE = Some(ins); + } + + write_log("-> created local instance handle"); + } + VK_SUCCESS } @@ -195,6 +227,25 @@ extern "system" fn create_device( } vk_handles_mut().load_device_functions(unsafe { *device }, proc_addr); + unsafe { QUEUE_SUBMIT = mem::transmute(vk_handles().handle("vkQueueSubmit").unwrap()) }; + + let pdev = PhysicalDevice::from_raw(instance(), physical_device).unwrap(); + + let ext_names = unsafe { (*create_info).extension_names() }; + + write_log(format!("{:?}", ext_names)); + + unsafe { + DEVICE = Some( + match Device::preinitialized(*device, proc_addr, pdev, &ext_names) { + Ok(dev) => dev, + Err(err) => { + write_log(format!("-> local device creation failed: {:?}", err)); + return VK_ERROR_INITIALIZATION_FAILED; + } + }, + ); + } VK_SUCCESS } @@ -210,6 +261,31 @@ extern "system" fn destroy_device(device: VkDevice, allocator: *const VkAllocati } } +extern "system" fn create_swapchain( + _device: VkDevice, + create_info: *const VkSwapchainCreateInfoKHR, + _allocator: *const VkAllocationCallbacks, + p_swapchain: *mut VkSwapchainKHR, +) -> VkResult { + write_log(" ================== vulkan layer create swapchain =================="); + + let swapchain = Swapchain::from_ci(device(), unsafe { &*create_info }).unwrap(); + + unsafe { *p_swapchain = swapchain.vk_handle() }; + unsafe { SWAPCHAIN = Some(swapchain) }; + + VK_SUCCESS +} + +extern "system" fn submit_queue( + queue: VkQueue, + submit_count: u32, + submits: *const VkSubmitInfo, + fence: VkFence, +) -> VkResult { + unsafe { QUEUE_SUBMIT(queue, submit_count, submits, fence) } +} + pub fn write_log(msg: impl ToString) { let mut file = OpenOptions::new() .append(true) @@ -220,3 +296,16 @@ pub fn write_log(msg: impl ToString) { file.write_all(format!("{}\n", msg.to_string()).as_bytes()) .unwrap(); } + +pub fn get_vk_func(s: &str) -> Option { + match s { + "vkCreateDevice" => Some(Functions::CreateDevice(create_device)), + "vkDestroyDevice" => Some(Functions::DestroyDevice(destroy_device)), + "vkCreateInstance" => Some(Functions::CreateInstance(create_instance)), + "vkDestroyInstance" => Some(Functions::DestroyInstance(destroy_instance)), + "vkCreateSwapchainKHR" => Some(Functions::CreateSwapchain(create_swapchain)), + "vkQueueSubmit" => Some(Functions::QueueSubmit(submit_queue)), + + _ => None, + } +} From 5b8be0eb4591f177e6840496c3825c1708735c11 Mon Sep 17 00:00:00 2001 From: hodasemi Date: Thu, 12 Jan 2023 10:10:09 +0100 Subject: [PATCH 06/85] Create project structure for rendering --- src/lib.rs | 128 +++++++++++++++++++++++++-------------- src/overlay/mod.rs | 50 +++++++++++++++ src/overlay/rendering.rs | 18 ++++++ 3 files changed, 151 insertions(+), 45 deletions(-) create mode 100644 src/overlay/mod.rs create mode 100644 src/overlay/rendering.rs diff --git a/src/lib.rs b/src/lib.rs index 646c3a8..4c19f1f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,7 @@ pub mod enums; pub mod structs; +mod overlay; mod vk_handles; use std::{ @@ -9,37 +10,27 @@ use std::{ io::Write, mem, os::raw::c_char, - ptr, - sync::Arc, + ptr, slice, }; use enums::*; +use overlay::Overlay; use structs::*; use vk_handles::*; use vulkan_rs::prelude::*; const LOG_FILE: &'static str = "/home/michael/rf2_vk_hud.log"; -static mut INSTANCE: Option> = None; -static mut DEVICE: Option> = None; -static mut SWAPCHAIN: Option> = None; +static mut OVERLAY: Overlay = Overlay::new(); static mut QUEUE_SUBMIT: PFN_vkQueueSubmit = unsafe { mem::transmute(vkVoidFunction as *const c_void) }; -pub fn instance() -> Arc { - unsafe { INSTANCE.as_ref().unwrap().clone() } -} - -pub fn device() -> Arc { - unsafe { DEVICE.as_ref().unwrap().clone() } -} - #[no_mangle] #[allow(non_snake_case)] pub extern "C" fn vkNegotiateLoaderLayerInterfaceVersion( pVersionStruct: *mut VkNegotiateLayerInterface, ) -> VkResult { - File::create(LOG_FILE).unwrap(); + if let Err(_) = File::create(LOG_FILE) {} write_log(" =================================================================="); write_log(" ======================= New Negotiation =========================="); @@ -56,7 +47,13 @@ pub extern "C" fn vkNegotiateLoaderLayerInterfaceVersion( } }; - set_vk_handles(VkTypedefHandles::new().unwrap()); + set_vk_handles(match VkTypedefHandles::new() { + Ok(handles) => handles, + Err(err) => { + write_log(format!("failed to load typedef handles {:?}", err)); + return VK_ERROR_INITIALIZATION_FAILED; + } + }); VK_SUCCESS } @@ -84,8 +81,6 @@ extern "system" fn get_device_proc_addr( return func; } - // write_log(format!("\trequested fn: {} in device proc addr", s)); - // write_log(format!("\t-> not found")); Functions::Null.convert() } @@ -112,8 +107,6 @@ extern "system" fn get_instance_proc_addr( return func; } - // write_log(format!("\trequested fn: {} in instance proc addr", s)); - // write_log(format!("\t-> not found")); Functions::Null.convert() } @@ -173,7 +166,7 @@ extern "system" fn create_instance( } }; - INSTANCE = Some(ins); + OVERLAY.set_instance(ins); } write_log("-> created local instance handle"); @@ -186,10 +179,11 @@ extern "system" fn destroy_instance(instance: VkInstance, allocator: *const VkAl write_log(" ================== vulkan layer destroy instance =================="); unsafe { - let destroy_instance: PFN_vkDestroyInstance = - mem::transmute(vk_handles().handle("vkDestroyInstance").unwrap()); + if let Some(vk_fn) = vk_handles().handle("vkDestroyInstance") { + let destroy_instance: PFN_vkDestroyInstance = mem::transmute(vk_fn); - destroy_instance(instance, allocator); + destroy_instance(instance, allocator); + } } } @@ -216,10 +210,16 @@ extern "system" fn create_device( chain_info.advance_layer_info(); let result = unsafe { - let create_device: PFN_vkCreateDevice = - mem::transmute(vk_handles().handle("vkCreateDevice").unwrap()); - - create_device(physical_device, create_info, allocator, device) + match vk_handles().handle("vkCreateDevice") { + Some(vk_fn) => { + let create_device: PFN_vkCreateDevice = mem::transmute(vk_fn); + create_device(physical_device, create_info, allocator, device) + } + None => { + write_log("failed creating device"); + return VK_ERROR_INITIALIZATION_FAILED; + } + } }; if result != VK_SUCCESS { @@ -227,16 +227,29 @@ extern "system" fn create_device( } vk_handles_mut().load_device_functions(unsafe { *device }, proc_addr); - unsafe { QUEUE_SUBMIT = mem::transmute(vk_handles().handle("vkQueueSubmit").unwrap()) }; - - let pdev = PhysicalDevice::from_raw(instance(), physical_device).unwrap(); + unsafe { + QUEUE_SUBMIT = match vk_handles().handle("vkQueueSubmit") { + Some(submit) => mem::transmute(submit), + None => { + write_log("failed querying vkQueueSubmit"); + return VK_ERROR_INITIALIZATION_FAILED; + } + } + }; + let pdev = match PhysicalDevice::from_raw(unsafe { OVERLAY.instance() }, physical_device) { + Ok(pdev) => pdev, + Err(err) => { + write_log(format!("failed creating physical device: {:?}", err)); + return VK_ERROR_INITIALIZATION_FAILED; + } + }; let ext_names = unsafe { (*create_info).extension_names() }; write_log(format!("{:?}", ext_names)); unsafe { - DEVICE = Some( + OVERLAY.set_device( match Device::preinitialized(*device, proc_addr, pdev, &ext_names) { Ok(dev) => dev, Err(err) => { @@ -254,10 +267,11 @@ extern "system" fn destroy_device(device: VkDevice, allocator: *const VkAllocati write_log(" ================== vulkan layer destroy device =================="); unsafe { - let destroy_device: PFN_vkDestroyDevice = - mem::transmute(vk_handles().handle("vkDestroyDevice").unwrap()); + if let Some(vk_fn) = vk_handles().handle("vkDestroyDevice") { + let destroy_device: PFN_vkDestroyDevice = mem::transmute(vk_fn); - destroy_device(device, allocator); + destroy_device(device, allocator); + } } } @@ -269,10 +283,19 @@ extern "system" fn create_swapchain( ) -> VkResult { write_log(" ================== vulkan layer create swapchain =================="); - let swapchain = Swapchain::from_ci(device(), unsafe { &*create_info }).unwrap(); + let swapchain = match unsafe { Swapchain::from_ci(OVERLAY.device(), &*create_info) } { + Ok(swapchain) => swapchain, + Err(err) => { + write_log(format!("create swapchain failed: {:?}", err)); + return VK_ERROR_INITIALIZATION_FAILED; + } + }; unsafe { *p_swapchain = swapchain.vk_handle() }; - unsafe { SWAPCHAIN = Some(swapchain) }; + if let Err(err) = unsafe { OVERLAY.create_rendering(swapchain) } { + write_log(format!("create overlay rendering struct failed: {:?}", err)); + return VK_ERROR_INITIALIZATION_FAILED; + } VK_SUCCESS } @@ -283,18 +306,33 @@ extern "system" fn submit_queue( submits: *const VkSubmitInfo, fence: VkFence, ) -> VkResult { - unsafe { QUEUE_SUBMIT(queue, submit_count, submits, fence) } + // unsafe { return QUEUE_SUBMIT(queue, submit_count, submits, fence): } + + unsafe { + let input_submit_info = slice::from_raw_parts(submits, submit_count as usize); + let overlay_submit = match OVERLAY.render() { + Ok(submit) => submit, + Err(err) => { + write_log(format!("overlay rendering failed: {:?}", err)); + return VK_ERROR_DEVICE_LOST; + } + }; + + let complete_submits = [input_submit_info, &[overlay_submit]].concat(); + + QUEUE_SUBMIT( + queue, + complete_submits.len() as u32, + complete_submits.as_ptr(), + fence, + ) + } } pub fn write_log(msg: impl ToString) { - let mut file = OpenOptions::new() - .append(true) - .create(true) - .open(LOG_FILE) - .unwrap(); - - file.write_all(format!("{}\n", msg.to_string()).as_bytes()) - .unwrap(); + if let Ok(mut file) = OpenOptions::new().append(true).create(true).open(LOG_FILE) { + if let Err(_) = file.write_all(format!("{}\n", msg.to_string()).as_bytes()) {} + } } pub fn get_vk_func(s: &str) -> Option { diff --git a/src/overlay/mod.rs b/src/overlay/mod.rs new file mode 100644 index 0000000..0390178 --- /dev/null +++ b/src/overlay/mod.rs @@ -0,0 +1,50 @@ +use self::rendering::Rendering; + +mod rendering; + +use anyhow::Result; +use std::sync::Arc; +use vulkan_rs::prelude::*; + +#[derive(Default)] +pub struct Overlay { + instance: Option>, + device: Option>, + rendering: Option, +} + +impl Overlay { + pub const fn new() -> Self { + Self { + instance: None, + device: None, + rendering: None, + } + } + + pub fn set_instance(&mut self, instance: Arc) { + self.instance = Some(instance); + } + + pub fn instance(&self) -> Arc { + self.instance.as_ref().unwrap().clone() + } + + pub fn set_device(&mut self, device: Arc) { + self.device = Some(device); + } + + pub fn device(&self) -> Arc { + self.device.as_ref().unwrap().clone() + } + + pub fn create_rendering(&mut self, swapchain: Arc) -> Result<()> { + self.rendering = Some(Rendering::new(swapchain)); + + Ok(()) + } + + pub fn render(&mut self) -> Result { + self.rendering.as_mut().unwrap().render() + } +} diff --git a/src/overlay/rendering.rs b/src/overlay/rendering.rs new file mode 100644 index 0000000..cc369f7 --- /dev/null +++ b/src/overlay/rendering.rs @@ -0,0 +1,18 @@ +use anyhow::Result; +use vulkan_rs::prelude::*; + +use std::sync::Arc; + +pub struct Rendering { + swapchain: Arc, +} + +impl Rendering { + pub fn new(swapchain: Arc) -> Self { + Self { swapchain } + } + + pub fn render(&mut self) -> Result { + todo!() + } +} From ef720708aa73593be6035e4ecf09914ec80c7a81 Mon Sep 17 00:00:00 2001 From: hodasemi Date: Thu, 12 Jan 2023 13:52:44 +0100 Subject: [PATCH 07/85] Start rendering work --- .gitignore | 2 + build.rs | 30 +++++++- src/enums.rs | 4 + src/lib.rs | 107 +++++++++++++++++++++++---- src/overlay/mod.rs | 48 ++++++++++-- src/overlay/pipeline.rs | 65 ++++++++++++++++ src/overlay/rendering.rs | 34 ++++++++- src/overlay/shader/single_color.frag | 12 +++ src/overlay/shader/single_color.vert | 8 ++ 9 files changed, 284 insertions(+), 26 deletions(-) create mode 100644 src/overlay/pipeline.rs create mode 100644 src/overlay/shader/single_color.frag create mode 100644 src/overlay/shader/single_color.vert diff --git a/.gitignore b/.gitignore index 7aeb431..cb54759 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,6 @@ /target /Cargo.lock +*.spv + vk_functions \ No newline at end of file diff --git a/build.rs b/build.rs index cae31dc..1ff51e6 100644 --- a/build.rs +++ b/build.rs @@ -9,7 +9,7 @@ const VK_HEADER: &[&str] = &[ ]; const FN_PREFIX: &str = "PFN_"; -fn main() { +fn query_vulkan_function_typedefs() { let mut fns = Vec::new(); for header in VK_HEADER { @@ -41,3 +41,31 @@ fn main() { file.write_all(format!("{}\n", func).as_bytes()).unwrap(); } } + +fn compile_shader() { + Command::new("glslangValidator") + .arg("--help") + .output() + .expect("Failed to execute glslangValidator. Maybe you need to install it first?"); + + Command::new("glslangValidator") + .arg("-V") + .arg("src/overlay/shader/single_color.vert") + .arg("-o") + .arg("src/overlay/shader/single_color.vert.spv") + .output() + .expect("Failed to compile single_color.vert"); + + Command::new("glslangValidator") + .arg("-V") + .arg("src/overlay/shader/single_color.frag") + .arg("-o") + .arg("src/overlay/shader/single_color.frag.spv") + .output() + .expect("Failed to compile single_color.frag"); +} + +fn main() { + query_vulkan_function_typedefs(); + compile_shader(); +} diff --git a/src/enums.rs b/src/enums.rs index 5eb56ae..f470913 100644 --- a/src/enums.rs +++ b/src/enums.rs @@ -27,6 +27,8 @@ pub enum Functions { DestroyDevice(PFN_vkDestroyDevice), CreateSwapchain(PFN_vkCreateSwapchainKHR), QueueSubmit(PFN_vkQueueSubmit), + GetDeviceQueue(PFN_vkGetDeviceQueue), + AcquireNextImageKHR(PFN_vkAcquireNextImageKHR), } impl Functions { @@ -41,6 +43,8 @@ impl Functions { Functions::DestroyDevice(func) => unsafe { mem::transmute(func) }, Functions::CreateSwapchain(func) => unsafe { mem::transmute(func) }, Functions::QueueSubmit(func) => unsafe { mem::transmute(func) }, + Functions::GetDeviceQueue(func) => unsafe { mem::transmute(func) }, + Functions::AcquireNextImageKHR(func) => unsafe { mem::transmute(func) }, } } } diff --git a/src/lib.rs b/src/lib.rs index 4c19f1f..fe00ece 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -283,19 +283,30 @@ extern "system" fn create_swapchain( ) -> VkResult { write_log(" ================== vulkan layer create swapchain =================="); - let swapchain = match unsafe { Swapchain::from_ci(OVERLAY.device(), &*create_info) } { - Ok(swapchain) => swapchain, - Err(err) => { - write_log(format!("create swapchain failed: {:?}", err)); - return VK_ERROR_INITIALIZATION_FAILED; - } - }; + let create_swapchain: PFN_vkCreateSwapchainKHR = + match vk_handles().handle("vkCreateSwapchainKHR") { + Some(create_swapchain) => unsafe { mem::transmute(create_swapchain) }, + None => { + write_log("failed querying vkCreateSwapchainKHR"); + return VK_ERROR_INITIALIZATION_FAILED; + } + }; - unsafe { *p_swapchain = swapchain.vk_handle() }; - if let Err(err) = unsafe { OVERLAY.create_rendering(swapchain) } { - write_log(format!("create overlay rendering struct failed: {:?}", err)); - return VK_ERROR_INITIALIZATION_FAILED; - } + create_swapchain(_device, create_info, _allocator, p_swapchain); + + let swapchain = + match unsafe { Swapchain::from_raw(OVERLAY.device(), &*create_info, *p_swapchain) } { + Ok(swapchain) => swapchain, + Err(err) => { + write_log(format!("create swapchain failed: {:?}", err)); + return VK_ERROR_INITIALIZATION_FAILED; + } + }; + + // if let Err(err) = unsafe { OVERLAY.create_rendering(swapchain) } { + // write_log(format!("create overlay rendering struct failed: {:?}", err)); + // return VK_ERROR_INITIALIZATION_FAILED; + // } VK_SUCCESS } @@ -306,11 +317,15 @@ extern "system" fn submit_queue( submits: *const VkSubmitInfo, fence: VkFence, ) -> VkResult { - // unsafe { return QUEUE_SUBMIT(queue, submit_count, submits, fence): } + write_log(" ================== vulkan layer submit queue =================="); + + unsafe { + return QUEUE_SUBMIT(queue, submit_count, submits, fence); + } unsafe { let input_submit_info = slice::from_raw_parts(submits, submit_count as usize); - let overlay_submit = match OVERLAY.render() { + let overlay_submit = match OVERLAY.render(queue) { Ok(submit) => submit, Err(err) => { write_log(format!("overlay rendering failed: {:?}", err)); @@ -329,6 +344,67 @@ extern "system" fn submit_queue( } } +extern "system" fn get_device_queue( + device: VkDevice, + queue_family_index: u32, + queue_index: u32, + p_queue: *mut VkQueue, +) { + write_log(" ================== vulkan layer get device queue =================="); + + let get_device_queue: PFN_vkGetDeviceQueue = match vk_handles().handle("vkGetDeviceQueue") { + Some(get_queue) => unsafe { mem::transmute(get_queue) }, + None => { + write_log("failed querying vkGetDeviceQueue"); + panic!("failed querying vkGetDeviceQueue"); + } + }; + + get_device_queue(device, queue_family_index, queue_index, p_queue); + + unsafe { + let arc_device = OVERLAY.device(); + OVERLAY.add_queue(Queue::new( + arc_device, + *p_queue, + queue_family_index, + queue_index, + )); + } +} + +extern "system" fn acquire_next_image( + _device: VkDevice, + swapchain: VkSwapchainKHR, + timeout: u64, + semaphore: VkSemaphore, + fence: VkFence, + image_index: *mut u32, +) -> VkResult { + write_log(" ================== vulkan layer acquire next image =================="); + + unsafe { + *image_index = match OVERLAY.device().acquire_next_image( + swapchain, + timeout, + Some(semaphore), + Some(fence), + ) { + Ok(res) => match res { + OutOfDate::Ok(index) => index, + OutOfDate::OutOfDate => return VK_ERROR_OUT_OF_DATE_KHR, + OutOfDate::TimeOut => return VK_TIMEOUT, + }, + Err(err) => { + write_log(format!("Failed acquiring next image: {:?}", err)); + return VK_ERROR_DEVICE_LOST; + } + } + } + + VK_SUCCESS +} + pub fn write_log(msg: impl ToString) { if let Ok(mut file) = OpenOptions::new().append(true).create(true).open(LOG_FILE) { if let Err(_) = file.write_all(format!("{}\n", msg.to_string()).as_bytes()) {} @@ -343,7 +419,8 @@ pub fn get_vk_func(s: &str) -> Option { "vkDestroyInstance" => Some(Functions::DestroyInstance(destroy_instance)), "vkCreateSwapchainKHR" => Some(Functions::CreateSwapchain(create_swapchain)), "vkQueueSubmit" => Some(Functions::QueueSubmit(submit_queue)), - + "vkGetDeviceQueue" => Some(Functions::GetDeviceQueue(get_device_queue)), + // "vkAcquireNextImageKHR" => Some(Functions::AcquireNextImageKHR(acquire_next_image)), _ => None, } } diff --git a/src/overlay/mod.rs b/src/overlay/mod.rs index 0390178..4c33604 100644 --- a/src/overlay/mod.rs +++ b/src/overlay/mod.rs @@ -1,16 +1,18 @@ use self::rendering::Rendering; +mod pipeline; mod rendering; use anyhow::Result; -use std::sync::Arc; +use std::sync::{Arc, Mutex}; use vulkan_rs::prelude::*; #[derive(Default)] pub struct Overlay { instance: Option>, device: Option>, - rendering: Option, + queues: Vec>>, + renderings: Vec, } impl Overlay { @@ -18,7 +20,8 @@ impl Overlay { Self { instance: None, device: None, - rendering: None, + queues: Vec::new(), + renderings: Vec::new(), } } @@ -38,13 +41,44 @@ impl Overlay { self.device.as_ref().unwrap().clone() } - pub fn create_rendering(&mut self, swapchain: Arc) -> Result<()> { - self.rendering = Some(Rendering::new(swapchain)); + pub fn add_queue(&mut self, queue: Arc>) { + self.queues.push(queue); + } + + pub fn queue(&self, queue: VkQueue) -> Arc> { + match self + .queues + .iter() + .find(|q| q.lock().unwrap().vk_handle() == queue) + { + Some(q) => q.clone(), + None => panic!(), + } + } + + pub fn swapchain(&self, swapchain: VkSwapchainKHR) -> &Arc { + match self + .renderings + .iter() + .find(|r| r.swapchain().vk_handle() == swapchain) + { + Some(s) => s.swapchain(), + None => panic!(), + } + } + + pub fn add_rendering(&mut self, swapchain: Arc) -> Result<()> { + // self.renderings + // .push(Rendering::new(self.device(), self.queue(), swapchain)?); Ok(()) } - pub fn render(&mut self) -> Result { - self.rendering.as_mut().unwrap().render() + pub fn render(&mut self, queue: VkQueue) -> Result { + // for rendering in self.renderings { + // rendering.render() + // } + + todo!() } } diff --git a/src/overlay/pipeline.rs b/src/overlay/pipeline.rs new file mode 100644 index 0000000..3030bc3 --- /dev/null +++ b/src/overlay/pipeline.rs @@ -0,0 +1,65 @@ +use anyhow::Result; +use vulkan_rs::prelude::*; + +use std::{mem, sync::Arc}; + +pub struct SingleColorPipeline { + pipeline: Arc, + pipeline_layout: Arc, + + vertex_shader: Arc, + fragment_shader: Arc, +} + +impl SingleColorPipeline { + pub fn new(device: Arc, renderpass: &Arc) -> Result { + let vertex_shader = ShaderModule::from_slice( + device.clone(), + include_bytes!("shader/single_color.vert.spv"), + ShaderType::Vertex, + )?; + let fragment_shader = ShaderModule::from_slice( + device.clone(), + include_bytes!("shader/single_color.frag.spv"), + ShaderType::Fragment, + )?; + + let pipeline_layout = PipelineLayout::builder().build(device.clone())?; + + let pipeline = Pipeline::new_graphics() + .set_vertex_shader( + vertex_shader.clone(), + vec![VkVertexInputBindingDescription { + binding: 0, + stride: mem::size_of::<[f32; 4]>() as u32, + inputRate: VK_VERTEX_INPUT_RATE_VERTEX, + }], + vec![ + // position + VkVertexInputAttributeDescription { + location: 0, + binding: 0, + format: VK_FORMAT_R32G32B32A32_SFLOAT, + offset: 0, + }, + ], + ) + .set_fragment_shader(fragment_shader.clone()) + .input_assembly(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, false) + .default_depth_stencil(false, false) + .default_rasterization(VK_CULL_MODE_NONE, VK_FRONT_FACE_COUNTER_CLOCKWISE) + .default_multisample(VK_SAMPLE_COUNT_1_BIT) + .build(device, &pipeline_layout, &renderpass, 0)?; + + Ok(Self { + vertex_shader, + fragment_shader, + pipeline, + pipeline_layout, + }) + } + + pub fn pipeline(&self) -> &Arc { + &self.pipeline + } +} diff --git a/src/overlay/rendering.rs b/src/overlay/rendering.rs index cc369f7..cf635ee 100644 --- a/src/overlay/rendering.rs +++ b/src/overlay/rendering.rs @@ -1,15 +1,43 @@ use anyhow::Result; use vulkan_rs::prelude::*; -use std::sync::Arc; +use std::sync::{Arc, Mutex}; + +use super::pipeline::SingleColorPipeline; pub struct Rendering { swapchain: Arc, + pipeline: SingleColorPipeline, + render_target: RenderTarget, + images: Vec>, } impl Rendering { - pub fn new(swapchain: Arc) -> Self { - Self { swapchain } + pub fn new( + device: Arc, + queue: Arc>, + swapchain: Arc, + ) -> Result { + let images = swapchain.wrap_images(&swapchain.vk_images()?, &queue)?; + + let render_target = RenderTarget::builder() + .add_sub_pass( + SubPass::builder(swapchain.width(), swapchain.height()) + .set_prepared_targets(&images, 0, [0.0, 0.0, 0.0, 1.0], false) + .build(&device, &queue)?, + ) + .build(&device)?; + + Ok(Self { + swapchain, + pipeline: SingleColorPipeline::new(device, render_target.render_pass())?, + render_target, + images, + }) + } + + pub fn swapchain(&self) -> &Arc { + &self.swapchain } pub fn render(&mut self) -> Result { diff --git a/src/overlay/shader/single_color.frag b/src/overlay/shader/single_color.frag new file mode 100644 index 0000000..d29bd8a --- /dev/null +++ b/src/overlay/shader/single_color.frag @@ -0,0 +1,12 @@ +#version 450 + +layout (set = 0, binding = 0) uniform Color { + vec4 val; +} color; + +layout (location = 0) out vec4 out_color; + +void main() +{ + out_color = vec4(color.val); +} diff --git a/src/overlay/shader/single_color.vert b/src/overlay/shader/single_color.vert new file mode 100644 index 0000000..126ef86 --- /dev/null +++ b/src/overlay/shader/single_color.vert @@ -0,0 +1,8 @@ +#version 450 + +layout (location = 0) in vec4 position; + +void main() +{ + gl_Position = position; +} \ No newline at end of file From 03c4194d292e578374579d50d420f81a30b5431b Mon Sep 17 00:00:00 2001 From: hodasemi Date: Thu, 12 Jan 2023 17:45:06 +0100 Subject: [PATCH 08/85] Implement working empty renderer --- src/enums.rs | 2 - src/lib.rs | 68 ++++++++++--------------------- src/overlay/mod.rs | 76 ++++++++++++++++++++--------------- src/overlay/pipeline.rs | 1 + src/overlay/rendering.rs | 80 +++++++++++++++++++++++++++++++++++-- src/overlay/rfactor_data.rs | 15 +++++++ 6 files changed, 156 insertions(+), 86 deletions(-) create mode 100644 src/overlay/rfactor_data.rs diff --git a/src/enums.rs b/src/enums.rs index f470913..811b20d 100644 --- a/src/enums.rs +++ b/src/enums.rs @@ -28,7 +28,6 @@ pub enum Functions { CreateSwapchain(PFN_vkCreateSwapchainKHR), QueueSubmit(PFN_vkQueueSubmit), GetDeviceQueue(PFN_vkGetDeviceQueue), - AcquireNextImageKHR(PFN_vkAcquireNextImageKHR), } impl Functions { @@ -44,7 +43,6 @@ impl Functions { Functions::CreateSwapchain(func) => unsafe { mem::transmute(func) }, Functions::QueueSubmit(func) => unsafe { mem::transmute(func) }, Functions::GetDeviceQueue(func) => unsafe { mem::transmute(func) }, - Functions::AcquireNextImageKHR(func) => unsafe { mem::transmute(func) }, } } } diff --git a/src/lib.rs b/src/lib.rs index fe00ece..44e39f2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -294,6 +294,9 @@ extern "system" fn create_swapchain( create_swapchain(_device, create_info, _allocator, p_swapchain); + write_log("-> created swapchain vk handle"); + + // if unsafe { !OVERLAY.has_rendering() } { let swapchain = match unsafe { Swapchain::from_raw(OVERLAY.device(), &*create_info, *p_swapchain) } { Ok(swapchain) => swapchain, @@ -303,9 +306,14 @@ extern "system" fn create_swapchain( } }; - // if let Err(err) = unsafe { OVERLAY.create_rendering(swapchain) } { - // write_log(format!("create overlay rendering struct failed: {:?}", err)); - // return VK_ERROR_INITIALIZATION_FAILED; + write_log("-> created Arc"); + + if let Err(err) = unsafe { OVERLAY.create_rendering(swapchain) } { + write_log(format!("create overlay rendering struct failed: {:?}", err)); + return VK_ERROR_INITIALIZATION_FAILED; + } + + write_log("-> created renderer"); // } VK_SUCCESS @@ -319,13 +327,9 @@ extern "system" fn submit_queue( ) -> VkResult { write_log(" ================== vulkan layer submit queue =================="); - unsafe { - return QUEUE_SUBMIT(queue, submit_count, submits, fence); - } - unsafe { let input_submit_info = slice::from_raw_parts(submits, submit_count as usize); - let overlay_submit = match OVERLAY.render(queue) { + let overlay_submit = match OVERLAY.render() { Ok(submit) => submit, Err(err) => { write_log(format!("overlay rendering failed: {:?}", err)); @@ -363,46 +367,16 @@ extern "system" fn get_device_queue( get_device_queue(device, queue_family_index, queue_index, p_queue); unsafe { - let arc_device = OVERLAY.device(); - OVERLAY.add_queue(Queue::new( - arc_device, - *p_queue, - queue_family_index, - queue_index, - )); - } -} - -extern "system" fn acquire_next_image( - _device: VkDevice, - swapchain: VkSwapchainKHR, - timeout: u64, - semaphore: VkSemaphore, - fence: VkFence, - image_index: *mut u32, -) -> VkResult { - write_log(" ================== vulkan layer acquire next image =================="); - - unsafe { - *image_index = match OVERLAY.device().acquire_next_image( - swapchain, - timeout, - Some(semaphore), - Some(fence), - ) { - Ok(res) => match res { - OutOfDate::Ok(index) => index, - OutOfDate::OutOfDate => return VK_ERROR_OUT_OF_DATE_KHR, - OutOfDate::TimeOut => return VK_TIMEOUT, - }, - Err(err) => { - write_log(format!("Failed acquiring next image: {:?}", err)); - return VK_ERROR_DEVICE_LOST; - } + if !OVERLAY.has_queue() { + let arc_device = OVERLAY.device(); + OVERLAY.add_queue(Queue::new( + arc_device, + *p_queue, + queue_family_index, + queue_index, + )); } } - - VK_SUCCESS } pub fn write_log(msg: impl ToString) { @@ -420,7 +394,7 @@ pub fn get_vk_func(s: &str) -> Option { "vkCreateSwapchainKHR" => Some(Functions::CreateSwapchain(create_swapchain)), "vkQueueSubmit" => Some(Functions::QueueSubmit(submit_queue)), "vkGetDeviceQueue" => Some(Functions::GetDeviceQueue(get_device_queue)), - // "vkAcquireNextImageKHR" => Some(Functions::AcquireNextImageKHR(acquire_next_image)), + _ => None, } } diff --git a/src/overlay/mod.rs b/src/overlay/mod.rs index 4c33604..a8c9b1b 100644 --- a/src/overlay/mod.rs +++ b/src/overlay/mod.rs @@ -1,18 +1,22 @@ -use self::rendering::Rendering; +use crate::write_log; + +use self::{rendering::Rendering, rfactor_data::RFactorData}; mod pipeline; mod rendering; +mod rfactor_data; use anyhow::Result; use std::sync::{Arc, Mutex}; use vulkan_rs::prelude::*; -#[derive(Default)] pub struct Overlay { instance: Option>, device: Option>, - queues: Vec>>, - renderings: Vec, + queue: Option>>, + rendering: Option, + + rfactor_data: RFactorData, } impl Overlay { @@ -20,8 +24,10 @@ impl Overlay { Self { instance: None, device: None, - queues: Vec::new(), - renderings: Vec::new(), + queue: None, + rendering: None, + + rfactor_data: RFactorData::default(), } } @@ -41,44 +47,48 @@ impl Overlay { self.device.as_ref().unwrap().clone() } + pub fn has_queue(&self) -> bool { + self.queue.is_some() + } + pub fn add_queue(&mut self, queue: Arc>) { - self.queues.push(queue); + self.queue = Some(queue); } - pub fn queue(&self, queue: VkQueue) -> Arc> { - match self - .queues - .iter() - .find(|q| q.lock().unwrap().vk_handle() == queue) - { - Some(q) => q.clone(), - None => panic!(), - } + pub fn queue(&self) -> Arc> { + self.queue.as_ref().unwrap().clone() } - pub fn swapchain(&self, swapchain: VkSwapchainKHR) -> &Arc { - match self - .renderings - .iter() - .find(|r| r.swapchain().vk_handle() == swapchain) - { - Some(s) => s.swapchain(), - None => panic!(), - } + pub fn swapchain(&self) -> &Arc { + self.rendering.as_ref().unwrap().swapchain() } - pub fn add_rendering(&mut self, swapchain: Arc) -> Result<()> { - // self.renderings - // .push(Rendering::new(self.device(), self.queue(), swapchain)?); + pub fn create_rendering(&mut self, swapchain: Arc) -> Result<()> { + write_log("-> create rendering: start"); + + self.rendering = None; + + write_log("-> create rendering: old cleared"); + + self.rendering = Some(Rendering::new(self.device(), self.queue(), swapchain)?); + + write_log("-> create rendering: new created"); + + write_log("-> create rendering: end"); Ok(()) } - pub fn render(&mut self, queue: VkQueue) -> Result { - // for rendering in self.renderings { - // rendering.render() - // } + pub fn render(&mut self) -> Result { + self.rfactor_data.update()?; - todo!() + let device = self.device(); + let queue = self.queue(); + let swapchain = self.swapchain().clone(); + + self.rendering + .as_mut() + .unwrap() + .render(device, queue, swapchain, &self.rfactor_data) } } diff --git a/src/overlay/pipeline.rs b/src/overlay/pipeline.rs index 3030bc3..82a0ed2 100644 --- a/src/overlay/pipeline.rs +++ b/src/overlay/pipeline.rs @@ -47,6 +47,7 @@ impl SingleColorPipeline { .set_fragment_shader(fragment_shader.clone()) .input_assembly(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, false) .default_depth_stencil(false, false) + .default_color_blend(vec![VkPipelineColorBlendAttachmentState::default()]) .default_rasterization(VK_CULL_MODE_NONE, VK_FRONT_FACE_COUNTER_CLOCKWISE) .default_multisample(VK_SAMPLE_COUNT_1_BIT) .build(device, &pipeline_layout, &renderpass, 0)?; diff --git a/src/overlay/rendering.rs b/src/overlay/rendering.rs index cf635ee..f833453 100644 --- a/src/overlay/rendering.rs +++ b/src/overlay/rendering.rs @@ -3,13 +3,15 @@ use vulkan_rs::prelude::*; use std::sync::{Arc, Mutex}; -use super::pipeline::SingleColorPipeline; +use super::{pipeline::SingleColorPipeline, rfactor_data::RFactorData}; +use crate::write_log; pub struct Rendering { swapchain: Arc, pipeline: SingleColorPipeline, render_target: RenderTarget, images: Vec>, + submit_info: SubmitInfo, } impl Rendering { @@ -18,7 +20,20 @@ impl Rendering { queue: Arc>, swapchain: Arc, ) -> Result { - let images = swapchain.wrap_images(&swapchain.vk_images()?, &queue)?; + write_log("-> Rendering ctor: begin"); + let vk_images = swapchain.vk_images()?; + write_log(format!( + "-> Rendering ctor: vk images ({})", + vk_images.len() + )); + let images = match swapchain.wrap_images(&vk_images, &queue) { + Ok(images) => images, + Err(err) => { + write_log(format!("-> Rendering ctor: failed wrapper: {:?}", err)); + return Err(err); + } + }; + write_log("-> Rendering ctor: wrapped images"); let render_target = RenderTarget::builder() .add_sub_pass( @@ -28,11 +43,14 @@ impl Rendering { ) .build(&device)?; + write_log("-> Rendering ctor: created render_target"); + Ok(Self { swapchain, pipeline: SingleColorPipeline::new(device, render_target.render_pass())?, render_target, images, + submit_info: SubmitInfo::default(), }) } @@ -40,7 +58,61 @@ impl Rendering { &self.swapchain } - pub fn render(&mut self) -> Result { - todo!() + pub fn render( + &mut self, + device: Arc, + queue: Arc>, + swapchain: Arc, + rfactor_data: &RFactorData, + ) -> Result { + write_log(" ================== vulkan layer enter rendering =================="); + + let image_index = self.swapchain.current_index(); + + let viewport = [VkViewport { + x: 0.0, + y: 0.0, + width: swapchain.width() as f32, + height: swapchain.height() as f32, + minDepth: 0.0, + maxDepth: 1.0, + }]; + + let scissor = [VkRect2D { + offset: VkOffset2D { x: 0, y: 0 }, + extent: VkExtent2D { + width: swapchain.width(), + height: swapchain.height(), + }, + }]; + + let command_buffer = CommandBuffer::new_primary().build(device, queue)?; + + write_log(" ================== vulkan layer created command buffer =================="); + + { + let mut recorder = command_buffer.begin(VkCommandBufferBeginInfo::new( + VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT, + ))?; + + write_log(" ================== vulkan layer begin command buffer =================="); + + self.render_target + .begin(&recorder, VK_SUBPASS_CONTENTS_INLINE, image_index as usize); + + recorder.bind_pipeline(self.pipeline.pipeline())?; + recorder.set_scissor(&scissor); + recorder.set_viewport(&viewport); + + // // recorder.bind_vertex_buffer(); + + self.render_target.end(&recorder); + } + + self.submit_info = SubmitInfo::default() + .add_wait_stage(VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT) + .add_command_buffer(command_buffer); + + Ok(self.submit_info.as_vk_submit()) } } diff --git a/src/overlay/rfactor_data.rs b/src/overlay/rfactor_data.rs new file mode 100644 index 0000000..c46c244 --- /dev/null +++ b/src/overlay/rfactor_data.rs @@ -0,0 +1,15 @@ +use anyhow::Result; + +pub struct RFactorData { + // TODO +} + +impl RFactorData { + pub const fn default() -> Self { + Self {} + } + + pub fn update(&mut self) -> Result<()> { + Ok(()) + } +} From b3c83dc3290318c71a34b761615189561447343a Mon Sep 17 00:00:00 2001 From: hodasemi Date: Thu, 12 Jan 2023 17:50:50 +0100 Subject: [PATCH 09/85] Add rfactor shared memory reader dependency --- Cargo.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/Cargo.toml b/Cargo.toml index 671d3f4..9b3f851 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,3 +12,4 @@ crate-type = ["cdylib"] # vulkan-rs = { git = "ssh://gitea@gavania.de:23/Gavania/Gavania.git", branch = "0.2.0_dev" } vulkan-rs = { path = "/home/michael/Dokumente/Workspace/Gavania/vulkan-rs" } anyhow = { version = "1.0.68", features = ["backtrace"] } +rfactor_sm_reader = { path = "../rfactor_sm_reader" } From c9a65898ee9bab1452e30fa87aa967f95a66c7d5 Mon Sep 17 00:00:00 2001 From: hodasemi Date: Fri, 13 Jan 2023 08:11:53 +0100 Subject: [PATCH 10/85] Start implement data reader from rfactor --- Cargo.toml | 1 + build.rs | 29 ++++---- src/lib.rs | 2 - src/overlay/mod.rs | 28 ++++++-- src/overlay/pipeline.rs | 23 ++++++- src/overlay/rendering.rs | 61 +++++++++++++---- src/overlay/rfactor_data.rs | 128 +++++++++++++++++++++++++++++++++++- 7 files changed, 235 insertions(+), 37 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 9b3f851..9c9ca12 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,3 +13,4 @@ crate-type = ["cdylib"] vulkan-rs = { path = "/home/michael/Dokumente/Workspace/Gavania/vulkan-rs" } anyhow = { version = "1.0.68", features = ["backtrace"] } rfactor_sm_reader = { path = "../rfactor_sm_reader" } +cgmath = "0.18.0" diff --git a/build.rs b/build.rs index 1ff51e6..d0fb369 100644 --- a/build.rs +++ b/build.rs @@ -9,6 +9,11 @@ const VK_HEADER: &[&str] = &[ ]; const FN_PREFIX: &str = "PFN_"; +const SHADER: &[&str] = &[ + "src/overlay/shader/single_color.vert", + "src/overlay/shader/single_color.frag", +]; + fn query_vulkan_function_typedefs() { let mut fns = Vec::new(); @@ -48,21 +53,15 @@ fn compile_shader() { .output() .expect("Failed to execute glslangValidator. Maybe you need to install it first?"); - Command::new("glslangValidator") - .arg("-V") - .arg("src/overlay/shader/single_color.vert") - .arg("-o") - .arg("src/overlay/shader/single_color.vert.spv") - .output() - .expect("Failed to compile single_color.vert"); - - Command::new("glslangValidator") - .arg("-V") - .arg("src/overlay/shader/single_color.frag") - .arg("-o") - .arg("src/overlay/shader/single_color.frag.spv") - .output() - .expect("Failed to compile single_color.frag"); + for shader in SHADER { + Command::new("glslangValidator") + .arg("-V") + .arg(shader) + .arg("-o") + .arg(&format!("{}.spv", shader)) + .output() + .expect(&format!("Failed to compile {}", shader)); + } } fn main() { diff --git a/src/lib.rs b/src/lib.rs index 44e39f2..61b7b13 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -325,8 +325,6 @@ extern "system" fn submit_queue( submits: *const VkSubmitInfo, fence: VkFence, ) -> VkResult { - write_log(" ================== vulkan layer submit queue =================="); - unsafe { let input_submit_info = slice::from_raw_parts(submits, submit_count as usize); let overlay_submit = match OVERLAY.render() { diff --git a/src/overlay/mod.rs b/src/overlay/mod.rs index a8c9b1b..91376b5 100644 --- a/src/overlay/mod.rs +++ b/src/overlay/mod.rs @@ -16,7 +16,7 @@ pub struct Overlay { queue: Option>>, rendering: Option, - rfactor_data: RFactorData, + rfactor_data: Option, } impl Overlay { @@ -27,7 +27,7 @@ impl Overlay { queue: None, rendering: None, - rfactor_data: RFactorData::default(), + rfactor_data: None, } } @@ -80,7 +80,27 @@ impl Overlay { } pub fn render(&mut self) -> Result { - self.rfactor_data.update()?; + if self.rfactor_data.is_none() { + self.rfactor_data = RFactorData::new( + self.device(), + self.rendering + .as_mut() + .unwrap() + .single_color_pipeline() + .descriptor_layout(), + ) + .ok(); + } + + // check twice for rfactor data, because of borrowing rules + if let Some(rfactor) = &mut self.rfactor_data { + rfactor.update()?; + } + + let objects = match &self.rfactor_data { + Some(rfactor) => rfactor.objects(), + None => Vec::new(), + }; let device = self.device(); let queue = self.queue(); @@ -89,6 +109,6 @@ impl Overlay { self.rendering .as_mut() .unwrap() - .render(device, queue, swapchain, &self.rfactor_data) + .render(device, queue, swapchain, &objects) } } diff --git a/src/overlay/pipeline.rs b/src/overlay/pipeline.rs index 82a0ed2..97de372 100644 --- a/src/overlay/pipeline.rs +++ b/src/overlay/pipeline.rs @@ -3,9 +3,12 @@ use vulkan_rs::prelude::*; use std::{mem, sync::Arc}; +use super::rendering::PositionOnlyVertex; + pub struct SingleColorPipeline { pipeline: Arc, pipeline_layout: Arc, + descriptor_layout: Arc, vertex_shader: Arc, fragment_shader: Arc, @@ -24,14 +27,25 @@ impl SingleColorPipeline { ShaderType::Fragment, )?; - let pipeline_layout = PipelineLayout::builder().build(device.clone())?; + let descriptor_layout = DescriptorSetLayout::builder() + .add_layout_binding( + 0, + VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, + VK_SHADER_STAGE_FRAGMENT_BIT, + 0, + ) + .build(device.clone())?; + + let pipeline_layout = PipelineLayout::builder() + .add_descriptor_set_layout(&descriptor_layout) + .build(device.clone())?; let pipeline = Pipeline::new_graphics() .set_vertex_shader( vertex_shader.clone(), vec![VkVertexInputBindingDescription { binding: 0, - stride: mem::size_of::<[f32; 4]>() as u32, + stride: mem::size_of::() as u32, inputRate: VK_VERTEX_INPUT_RATE_VERTEX, }], vec![ @@ -55,6 +69,7 @@ impl SingleColorPipeline { Ok(Self { vertex_shader, fragment_shader, + descriptor_layout, pipeline, pipeline_layout, }) @@ -63,4 +78,8 @@ impl SingleColorPipeline { pub fn pipeline(&self) -> &Arc { &self.pipeline } + + pub fn descriptor_layout(&self) -> &Arc { + &self.descriptor_layout + } } diff --git a/src/overlay/rendering.rs b/src/overlay/rendering.rs index f833453..85bf1e2 100644 --- a/src/overlay/rendering.rs +++ b/src/overlay/rendering.rs @@ -1,16 +1,52 @@ use anyhow::Result; +use cgmath::{Vector2, Vector4}; use vulkan_rs::prelude::*; use std::sync::{Arc, Mutex}; -use super::{pipeline::SingleColorPipeline, rfactor_data::RFactorData}; +use super::{pipeline::SingleColorPipeline, rfactor_data::RenderObject}; use crate::write_log; +#[derive(Clone)] +pub struct PositionOnlyVertex { + pub position: Vector4, +} + +impl PositionOnlyVertex { + /// + /// corners[0] - bottom left + /// corners[1] - top left + /// corners[2] - top right + /// corners[3] - bottom right + /// + pub fn from_2d_corners(corners: [Vector2; 4]) -> [Self; 6] { + [ + Self { + position: corners[0].extend(0.0).extend(1.0), + }, + Self { + position: corners[1].extend(0.0).extend(1.0), + }, + Self { + position: corners[2].extend(0.0).extend(1.0), + }, + Self { + position: corners[2].extend(0.0).extend(1.0), + }, + Self { + position: corners[3].extend(0.0).extend(1.0), + }, + Self { + position: corners[0].extend(0.0).extend(1.0), + }, + ] + } +} + pub struct Rendering { swapchain: Arc, pipeline: SingleColorPipeline, render_target: RenderTarget, - images: Vec>, submit_info: SubmitInfo, } @@ -49,7 +85,6 @@ impl Rendering { swapchain, pipeline: SingleColorPipeline::new(device, render_target.render_pass())?, render_target, - images, submit_info: SubmitInfo::default(), }) } @@ -58,15 +93,17 @@ impl Rendering { &self.swapchain } + pub fn single_color_pipeline(&self) -> &SingleColorPipeline { + &self.pipeline + } + pub fn render( &mut self, device: Arc, queue: Arc>, swapchain: Arc, - rfactor_data: &RFactorData, + objects: &[&dyn RenderObject], ) -> Result { - write_log(" ================== vulkan layer enter rendering =================="); - let image_index = self.swapchain.current_index(); let viewport = [VkViewport { @@ -88,15 +125,11 @@ impl Rendering { let command_buffer = CommandBuffer::new_primary().build(device, queue)?; - write_log(" ================== vulkan layer created command buffer =================="); - { let mut recorder = command_buffer.begin(VkCommandBufferBeginInfo::new( VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT, ))?; - write_log(" ================== vulkan layer begin command buffer =================="); - self.render_target .begin(&recorder, VK_SUBPASS_CONTENTS_INLINE, image_index as usize); @@ -104,7 +137,13 @@ impl Rendering { recorder.set_scissor(&scissor); recorder.set_viewport(&viewport); - // // recorder.bind_vertex_buffer(); + for object in objects { + let buffer = object.buffer(); + + recorder.bind_descriptor_sets_minimal(&[object.descriptor()]); + recorder.bind_vertex_buffer(buffer); + recorder.draw_complete_single_instance(buffer.size() as u32); + } self.render_target.end(&recorder); } diff --git a/src/overlay/rfactor_data.rs b/src/overlay/rfactor_data.rs index c46c244..f47de8d 100644 --- a/src/overlay/rfactor_data.rs +++ b/src/overlay/rfactor_data.rs @@ -1,15 +1,137 @@ use anyhow::Result; +use cgmath::vec2; +use rfactor_sm_reader::*; +use vulkan_rs::prelude::*; + +use std::sync::Arc; + +use super::rendering::PositionOnlyVertex; + +pub trait RenderObject { + fn descriptor(&self) -> &Arc; + fn buffer(&self) -> &Arc>; +} pub struct RFactorData { - // TODO + // rf2 memory mapped data + telemetry_reader: TelemetryReader, + scoring_reader: ScoringReader, + + // radar objects + background: RadarObject, + player_car: RadarObject, + cars: Vec, } impl RFactorData { - pub const fn default() -> Self { - Self {} + pub fn new(device: Arc, descriptor_layout: &Arc) -> Result { + let radar_extent = 0.2; + let car_height = 0.05; + let car_width = 0.025; + + Ok(Self { + telemetry_reader: TelemetryReader::new()?, + scoring_reader: ScoringReader::new()?, + + background: RadarObject::new( + device.clone(), + descriptor_layout, + PositionOnlyVertex::from_2d_corners([ + vec2(-radar_extent, -radar_extent), + vec2(-radar_extent, radar_extent), + vec2(radar_extent, radar_extent), + vec2(radar_extent, -radar_extent), + ]), + [0.5, 0.5, 0.5, 0.5], + )?, + player_car: RadarObject::new( + device.clone(), + descriptor_layout, + PositionOnlyVertex::from_2d_corners([ + vec2(-car_width, -car_height), + vec2(-car_width, car_height), + vec2(car_width, car_height), + vec2(car_width, -car_height), + ]), + [0.9, 0.9, 0.0, 0.9], + )?, + cars: Vec::new(), + }) } pub fn update(&mut self) -> Result<()> { Ok(()) } + + pub fn objects(&self) -> Vec<&dyn RenderObject> { + let mut objects: Vec<&dyn RenderObject> = Vec::new(); + + objects.push(&self.background); + + for other_player_cars in &self.cars { + objects.push(other_player_cars); + } + + objects.push(&self.player_car); + + objects + } +} + +struct RadarObject { + descriptor_set: Arc, + + // uniform buffer + color_buffer: Arc>, + + // vertex buffer + position_buffer: Arc>, +} + +impl RadarObject { + fn new( + device: Arc, + descriptor_layout: &Arc, + positions: [PositionOnlyVertex; 6], + color: [f32; 4], + ) -> Result { + let color_buffer = Buffer::builder() + .set_usage(VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT) + .set_memory_usage(MemoryUsage::CpuOnly) + .set_data(&color) + .build(device.clone())?; + + let position_buffer = Buffer::builder() + .set_usage(VK_BUFFER_USAGE_VERTEX_BUFFER_BIT) + .set_memory_usage(MemoryUsage::CpuOnly) + .set_data(&positions) + .build(device.clone())?; + + let descriptor_pool = DescriptorPool::builder() + .set_layout(descriptor_layout.clone()) + .build(device.clone())?; + + let descriptor_set = descriptor_pool.prepare_set().allocate()?; + descriptor_set.update(&[DescriptorWrite::uniform_buffers(0, &[&color_buffer])])?; + + Ok(Self { + descriptor_set, + color_buffer, + position_buffer, + }) + } + + pub fn update_color(&self, color: [f32; 4]) -> Result<()> { + self.color_buffer.fill(&color) + } +} + +impl RenderObject for RadarObject { + fn descriptor(&self) -> &Arc { + &self.descriptor_set + } + + fn buffer(&self) -> &Arc> { + &self.position_buffer + } } From 0dfc8a9ff070ca5031f3580521a3bdd426520879 Mon Sep 17 00:00:00 2001 From: hodasemi Date: Fri, 13 Jan 2023 08:47:30 +0100 Subject: [PATCH 11/85] Apply swapchain changes --- src/overlay/rendering.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/overlay/rendering.rs b/src/overlay/rendering.rs index 85bf1e2..a1512cf 100644 --- a/src/overlay/rendering.rs +++ b/src/overlay/rendering.rs @@ -62,7 +62,7 @@ impl Rendering { "-> Rendering ctor: vk images ({})", vk_images.len() )); - let images = match swapchain.wrap_images(&vk_images, &queue) { + let images = match swapchain.wrap_images(&vk_images, &queue, true) { Ok(images) => images, Err(err) => { write_log(format!("-> Rendering ctor: failed wrapper: {:?}", err)); From 2c8a979b1ed2e9f3c88d5bff39e3afdedc79b9bb Mon Sep 17 00:00:00 2001 From: hodasemi Date: Fri, 13 Jan 2023 09:27:58 +0100 Subject: [PATCH 12/85] Debug why there is nothing rendered --- Cargo.toml | 1 + src/enums.rs | 72 ++++++++++++++++++++++++++-------------- src/lib.rs | 46 ++++++++++++------------- src/overlay/rendering.rs | 8 +++++ 4 files changed, 79 insertions(+), 48 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 9c9ca12..6214877 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,3 +14,4 @@ vulkan-rs = { path = "/home/michael/Dokumente/Workspace/Gavania/vulkan-rs" } anyhow = { version = "1.0.68", features = ["backtrace"] } rfactor_sm_reader = { path = "../rfactor_sm_reader" } cgmath = "0.18.0" +paste = "1.0.11" diff --git a/src/enums.rs b/src/enums.rs index 811b20d..ce26d3a 100644 --- a/src/enums.rs +++ b/src/enums.rs @@ -3,6 +3,8 @@ use std::{mem, ptr}; use vulkan_rs::prelude::*; +use crate::*; + pub use VkLayerFunction::*; pub use VkNegotiateLayerStructType::*; @@ -19,30 +21,50 @@ pub enum VkLayerFunction { VK_LOADER_FEATURES = 3, } -pub enum Functions { - Null, - CreateInstance(PFN_vkCreateInstance), - DestroyInstance(PFN_vkDestroyInstance), - CreateDevice(PFN_vkCreateDevice), - DestroyDevice(PFN_vkDestroyDevice), - CreateSwapchain(PFN_vkCreateSwapchainKHR), - QueueSubmit(PFN_vkQueueSubmit), - GetDeviceQueue(PFN_vkGetDeviceQueue), +macro_rules! create_functions_enum { + ($([$pfn:ident, $func:ident],)+) => { + paste::paste! { + pub enum Functions { + Null, + $( + $pfn ( [] ), + )+ + } + + impl Functions { + pub fn convert(self) -> PFN_vkVoidFunction { + match self { + Functions::Null => unsafe { + mem::transmute::<*const (), PFN_vkVoidFunction>(ptr::null()) + }, + $( + Functions::$pfn(foo) => unsafe { mem::transmute(foo) }, + )+ + } + } + + pub fn get_vk_func(s:&str) -> Option { + match s { + $( + stringify!([]) => Some(Functions::$pfn($func)), + )+ + + _ => None + } + } + } + } + + }; } -impl Functions { - pub fn convert(self) -> PFN_vkVoidFunction { - match self { - Functions::Null => unsafe { - mem::transmute::<*const (), PFN_vkVoidFunction>(ptr::null()) - }, - Functions::CreateInstance(func) => unsafe { mem::transmute(func) }, - Functions::DestroyInstance(func) => unsafe { mem::transmute(func) }, - Functions::CreateDevice(func) => unsafe { mem::transmute(func) }, - Functions::DestroyDevice(func) => unsafe { mem::transmute(func) }, - Functions::CreateSwapchain(func) => unsafe { mem::transmute(func) }, - Functions::QueueSubmit(func) => unsafe { mem::transmute(func) }, - Functions::GetDeviceQueue(func) => unsafe { mem::transmute(func) }, - } - } -} +create_functions_enum!( + [CreateInstance, create_instance], + [DestroyInstance, destroy_instance], + [CreateDevice, create_device], + [DestroyDevice, destroy_device], + [CreateSwapchainKHR, create_swapchain], + [QueueSubmit, submit_queue], + [GetDeviceQueue, get_device_queue], + [AcquireNextImageKHR, acquire_next_image], +); diff --git a/src/lib.rs b/src/lib.rs index 61b7b13..ad12871 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -73,7 +73,7 @@ extern "system" fn get_device_proc_addr( let s = func_string.as_str(); - if let Some(func) = get_vk_func(s) { + if let Some(func) = Functions::get_vk_func(s) { return func.convert(); } @@ -99,7 +99,7 @@ extern "system" fn get_instance_proc_addr( let s = func_string.as_str(); - if let Some(func) = get_vk_func(s) { + if let Some(func) = Functions::get_vk_func(s) { return func.convert(); } @@ -110,7 +110,7 @@ extern "system" fn get_instance_proc_addr( Functions::Null.convert() } -extern "system" fn create_instance( +pub extern "system" fn create_instance( create_info: *const VkInstanceCreateInfo, allocator: *const VkAllocationCallbacks, instance: *mut VkInstance, @@ -175,7 +175,10 @@ extern "system" fn create_instance( VK_SUCCESS } -extern "system" fn destroy_instance(instance: VkInstance, allocator: *const VkAllocationCallbacks) { +pub extern "system" fn destroy_instance( + instance: VkInstance, + allocator: *const VkAllocationCallbacks, +) { write_log(" ================== vulkan layer destroy instance =================="); unsafe { @@ -187,7 +190,7 @@ extern "system" fn destroy_instance(instance: VkInstance, allocator: *const VkAl } } -extern "system" fn create_device( +pub extern "system" fn create_device( physical_device: VkPhysicalDevice, create_info: *const VkDeviceCreateInfo<'_>, allocator: *const VkAllocationCallbacks, @@ -263,7 +266,7 @@ extern "system" fn create_device( VK_SUCCESS } -extern "system" fn destroy_device(device: VkDevice, allocator: *const VkAllocationCallbacks) { +pub extern "system" fn destroy_device(device: VkDevice, allocator: *const VkAllocationCallbacks) { write_log(" ================== vulkan layer destroy device =================="); unsafe { @@ -275,7 +278,7 @@ extern "system" fn destroy_device(device: VkDevice, allocator: *const VkAllocati } } -extern "system" fn create_swapchain( +pub extern "system" fn create_swapchain( _device: VkDevice, create_info: *const VkSwapchainCreateInfoKHR, _allocator: *const VkAllocationCallbacks, @@ -319,7 +322,7 @@ extern "system" fn create_swapchain( VK_SUCCESS } -extern "system" fn submit_queue( +pub extern "system" fn submit_queue( queue: VkQueue, submit_count: u32, submits: *const VkSubmitInfo, @@ -346,7 +349,7 @@ extern "system" fn submit_queue( } } -extern "system" fn get_device_queue( +pub extern "system" fn get_device_queue( device: VkDevice, queue_family_index: u32, queue_index: u32, @@ -377,22 +380,19 @@ extern "system" fn get_device_queue( } } +pub extern "system" fn acquire_next_image( + device: VkDevice, + swapchain: VkSwapchainKHR, + timeout: u64, + semaphore: VkSemaphore, + fence: VkFence, + pImageIndex: *mut u32, +) -> VkResult { + VK_SUCCESS +} + pub fn write_log(msg: impl ToString) { if let Ok(mut file) = OpenOptions::new().append(true).create(true).open(LOG_FILE) { if let Err(_) = file.write_all(format!("{}\n", msg.to_string()).as_bytes()) {} } } - -pub fn get_vk_func(s: &str) -> Option { - match s { - "vkCreateDevice" => Some(Functions::CreateDevice(create_device)), - "vkDestroyDevice" => Some(Functions::DestroyDevice(destroy_device)), - "vkCreateInstance" => Some(Functions::CreateInstance(create_instance)), - "vkDestroyInstance" => Some(Functions::DestroyInstance(destroy_instance)), - "vkCreateSwapchainKHR" => Some(Functions::CreateSwapchain(create_swapchain)), - "vkQueueSubmit" => Some(Functions::QueueSubmit(submit_queue)), - "vkGetDeviceQueue" => Some(Functions::GetDeviceQueue(get_device_queue)), - - _ => None, - } -} diff --git a/src/overlay/rendering.rs b/src/overlay/rendering.rs index a1512cf..03608e6 100644 --- a/src/overlay/rendering.rs +++ b/src/overlay/rendering.rs @@ -81,6 +81,12 @@ impl Rendering { write_log("-> Rendering ctor: created render_target"); + write_log(format!( + "-> Rendering swapchain extents ({}, {})", + swapchain.width(), + swapchain.height(), + )); + Ok(Self { swapchain, pipeline: SingleColorPipeline::new(device, render_target.render_pass())?, @@ -137,6 +143,8 @@ impl Rendering { recorder.set_scissor(&scissor); recorder.set_viewport(&viewport); + write_log(format!("-> Rendering {} objects", objects.len())); + for object in objects { let buffer = object.buffer(); From 9fd1275ce3f95dc6e8e47dd2ed1602336d0b1899 Mon Sep 17 00:00:00 2001 From: hodasemi Date: Fri, 13 Jan 2023 15:00:01 +0100 Subject: [PATCH 13/85] More testing --- Cargo.toml | 3 +- src/enums.rs | 1 + src/lib.rs | 97 ++++++++++++++++++++++++++++++++++------ src/overlay/mod.rs | 39 ++++++++-------- src/overlay/pipeline.rs | 7 --- src/overlay/rendering.rs | 23 ++++------ 6 files changed, 115 insertions(+), 55 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 6214877..948fb30 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,7 +11,8 @@ crate-type = ["cdylib"] [dependencies] # vulkan-rs = { git = "ssh://gitea@gavania.de:23/Gavania/Gavania.git", branch = "0.2.0_dev" } vulkan-rs = { path = "/home/michael/Dokumente/Workspace/Gavania/vulkan-rs" } +rfactor_sm_reader = { git = "https://gavania.de/hodasemi/rfactor_sm_reader.git" } + anyhow = { version = "1.0.68", features = ["backtrace"] } -rfactor_sm_reader = { path = "../rfactor_sm_reader" } cgmath = "0.18.0" paste = "1.0.11" diff --git a/src/enums.rs b/src/enums.rs index ce26d3a..813d5ed 100644 --- a/src/enums.rs +++ b/src/enums.rs @@ -67,4 +67,5 @@ create_functions_enum!( [QueueSubmit, submit_queue], [GetDeviceQueue, get_device_queue], [AcquireNextImageKHR, acquire_next_image], + [QueuePresentKHR, present_queue], ); diff --git a/src/lib.rs b/src/lib.rs index ad12871..f9c5b62 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -24,6 +24,8 @@ const LOG_FILE: &'static str = "/home/michael/rf2_vk_hud.log"; static mut OVERLAY: Overlay = Overlay::new(); static mut QUEUE_SUBMIT: PFN_vkQueueSubmit = unsafe { mem::transmute(vkVoidFunction as *const c_void) }; +static mut ACQUIRE_NEXT_IMAGE: PFN_vkAcquireNextImageKHR = + unsafe { mem::transmute(vkVoidFunction as *const c_void) }; #[no_mangle] #[allow(non_snake_case)] @@ -237,7 +239,15 @@ pub extern "system" fn create_device( write_log("failed querying vkQueueSubmit"); return VK_ERROR_INITIALIZATION_FAILED; } - } + }; + + ACQUIRE_NEXT_IMAGE = match vk_handles().handle("vkAcquireNextImageKHR") { + Some(acquire_next_image) => mem::transmute(acquire_next_image), + None => { + write_log("failed querying vkAcquireNextImageKHR"); + return VK_ERROR_INITIALIZATION_FAILED; + } + }; }; let pdev = match PhysicalDevice::from_raw(unsafe { OVERLAY.instance() }, physical_device) { @@ -328,24 +338,30 @@ pub extern "system" fn submit_queue( submits: *const VkSubmitInfo, fence: VkFence, ) -> VkResult { + write_log(" ================== vulkan layer submit queue =================="); + unsafe { - let input_submit_info = slice::from_raw_parts(submits, submit_count as usize); - let overlay_submit = match OVERLAY.render() { - Ok(submit) => submit, + let command_buffers = slice::from_raw_parts( + (*submits).pCommandBuffers, + (*submits).commandBufferCount as usize, + ); + + let cb = match OVERLAY.render() { + Ok(cb) => cb, Err(err) => { write_log(format!("overlay rendering failed: {:?}", err)); return VK_ERROR_DEVICE_LOST; } }; - let complete_submits = [input_submit_info, &[overlay_submit]].concat(); + let cb = [command_buffers, &[cb]].concat(); - QUEUE_SUBMIT( - queue, - complete_submits.len() as u32, - complete_submits.as_ptr(), - fence, - ) + let mut_ptr = submits as *mut VkSubmitInfo; + + (*mut_ptr).commandBufferCount = cb.len() as u32; + (*mut_ptr).pCommandBuffers = cb.as_ptr(); + + QUEUE_SUBMIT(queue, submit_count, mut_ptr, fence) } } @@ -366,6 +382,7 @@ pub extern "system" fn get_device_queue( }; get_device_queue(device, queue_family_index, queue_index, p_queue); + write_log(format!("new queue: {:?}", unsafe { *p_queue })); unsafe { if !OVERLAY.has_queue() { @@ -386,9 +403,63 @@ pub extern "system" fn acquire_next_image( timeout: u64, semaphore: VkSemaphore, fence: VkFence, - pImageIndex: *mut u32, + image_index: *mut u32, ) -> VkResult { - VK_SUCCESS + unsafe { + match OVERLAY.swapchain(swapchain) { + Some(sc) => { + let device = OVERLAY.device(); + + let res = device.acquire_next_image( + sc.vk_handle(), + timeout, + Some(semaphore), + Some(fence), + ); + + write_log(format!("acquire res: {:?}", res)); + + match res { + Ok(res) => match res { + OutOfDate::Ok(index) => { + sc.set_image_index(index); + VK_SUCCESS + } + OutOfDate::OutOfDate => VK_ERROR_OUT_OF_DATE_KHR, + OutOfDate::TimeOut => VK_TIMEOUT, + }, + Err(err) => { + write_log(format!("failed acquiring next image {:?}", err)); + VK_ERROR_DEVICE_LOST + } + } + } + None => { + write_log("acquired other swapchain image"); + ACQUIRE_NEXT_IMAGE(device, swapchain, timeout, semaphore, fence, image_index) + } + } + } +} + +pub extern "system" fn present_queue( + queue: VkQueue, + present_info: *const VkPresentInfoKHR, +) -> VkResult { + write_log(" ================== vulkan layer queue present =================="); + write_log(format!("iq: {:?}, cq: {:?}", queue, unsafe { + OVERLAY.queue().lock().unwrap().vk_handle() + })); + + let pfn: PFN_vkQueuePresentKHR = match vk_handles().handle("vkQueuePresentKHR") { + Some(pfn) => unsafe { mem::transmute(pfn) }, + None => { + write_log("failed querying vkQueuePresentKHR"); + return VK_ERROR_DEVICE_LOST; + } + }; + + pfn(queue, present_info) } pub fn write_log(msg: impl ToString) { diff --git a/src/overlay/mod.rs b/src/overlay/mod.rs index 91376b5..c6a4623 100644 --- a/src/overlay/mod.rs +++ b/src/overlay/mod.rs @@ -59,8 +59,14 @@ impl Overlay { self.queue.as_ref().unwrap().clone() } - pub fn swapchain(&self) -> &Arc { - self.rendering.as_ref().unwrap().swapchain() + pub fn swapchain(&self, swapchain: VkSwapchainKHR) -> Option<&Arc> { + let sc = self.rendering.as_ref().unwrap().swapchain(); + + if sc.vk_handle() == swapchain { + Some(sc) + } else { + None + } } pub fn create_rendering(&mut self, swapchain: Arc) -> Result<()> { @@ -79,17 +85,17 @@ impl Overlay { Ok(()) } - pub fn render(&mut self) -> Result { + pub fn render(&mut self) -> Result { if self.rfactor_data.is_none() { - self.rfactor_data = RFactorData::new( - self.device(), - self.rendering - .as_mut() - .unwrap() - .single_color_pipeline() - .descriptor_layout(), - ) - .ok(); + // self.rfactor_data = RFactorData::new( + // self.device(), + // self.rendering + // .as_mut() + // .unwrap() + // .single_color_pipeline() + // .descriptor_layout(), + // ) + // .ok(); } // check twice for rfactor data, because of borrowing rules @@ -102,13 +108,8 @@ impl Overlay { None => Vec::new(), }; - let device = self.device(); - let queue = self.queue(); - let swapchain = self.swapchain().clone(); + let swapchain = self.rendering.as_ref().unwrap().swapchain().clone(); - self.rendering - .as_mut() - .unwrap() - .render(device, queue, swapchain, &objects) + self.rendering.as_mut().unwrap().render(swapchain, &objects) } } diff --git a/src/overlay/pipeline.rs b/src/overlay/pipeline.rs index 97de372..fca50bf 100644 --- a/src/overlay/pipeline.rs +++ b/src/overlay/pipeline.rs @@ -7,11 +7,7 @@ use super::rendering::PositionOnlyVertex; pub struct SingleColorPipeline { pipeline: Arc, - pipeline_layout: Arc, descriptor_layout: Arc, - - vertex_shader: Arc, - fragment_shader: Arc, } impl SingleColorPipeline { @@ -67,11 +63,8 @@ impl SingleColorPipeline { .build(device, &pipeline_layout, &renderpass, 0)?; Ok(Self { - vertex_shader, - fragment_shader, descriptor_layout, pipeline, - pipeline_layout, }) } diff --git a/src/overlay/rendering.rs b/src/overlay/rendering.rs index 03608e6..70a7182 100644 --- a/src/overlay/rendering.rs +++ b/src/overlay/rendering.rs @@ -47,7 +47,7 @@ pub struct Rendering { swapchain: Arc, pipeline: SingleColorPipeline, render_target: RenderTarget, - submit_info: SubmitInfo, + command_buffer: Arc, } impl Rendering { @@ -89,9 +89,9 @@ impl Rendering { Ok(Self { swapchain, - pipeline: SingleColorPipeline::new(device, render_target.render_pass())?, + pipeline: SingleColorPipeline::new(device.clone(), render_target.render_pass())?, render_target, - submit_info: SubmitInfo::default(), + command_buffer: CommandBuffer::new_primary().build(device, queue)?, }) } @@ -105,11 +105,9 @@ impl Rendering { pub fn render( &mut self, - device: Arc, - queue: Arc>, swapchain: Arc, objects: &[&dyn RenderObject], - ) -> Result { + ) -> Result { let image_index = self.swapchain.current_index(); let viewport = [VkViewport { @@ -129,11 +127,10 @@ impl Rendering { }, }]; - let command_buffer = CommandBuffer::new_primary().build(device, queue)?; - { - let mut recorder = command_buffer.begin(VkCommandBufferBeginInfo::new( - VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT, + let mut recorder = self.command_buffer.begin(VkCommandBufferBeginInfo::new( + VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT + | VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT, ))?; self.render_target @@ -156,10 +153,6 @@ impl Rendering { self.render_target.end(&recorder); } - self.submit_info = SubmitInfo::default() - .add_wait_stage(VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT) - .add_command_buffer(command_buffer); - - Ok(self.submit_info.as_vk_submit()) + Ok(self.command_buffer.vk_handle()) } } From 90df19cbd74f635c0dcf35edcf1350e318783321 Mon Sep 17 00:00:00 2001 From: hodasemi Date: Sat, 14 Jan 2023 08:08:40 +0100 Subject: [PATCH 14/85] Add a lot of debugging noise --- Cargo.toml | 4 +- src/lib.rs | 193 ++++++++++++++++++++++++++---------- src/overlay/mod.rs | 22 ++-- src/overlay/rendering.rs | 20 +++- src/overlay/rfactor_data.rs | 9 +- 5 files changed, 173 insertions(+), 75 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 948fb30..4792f35 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,8 +9,8 @@ edition = "2021" crate-type = ["cdylib"] [dependencies] -# vulkan-rs = { git = "ssh://gitea@gavania.de:23/Gavania/Gavania.git", branch = "0.2.0_dev" } -vulkan-rs = { path = "/home/michael/Dokumente/Workspace/Gavania/vulkan-rs" } +vulkan-rs = { git = "https://gavania.de/Gavania/Gavania.git", branch = "0.2.0_dev" } +# vulkan-rs = { path = "/home/michael/Dokumente/Workspace/Gavania/vulkan-rs" } rfactor_sm_reader = { git = "https://gavania.de/hodasemi/rfactor_sm_reader.git" } anyhow = { version = "1.0.68", features = ["backtrace"] } diff --git a/src/lib.rs b/src/lib.rs index f9c5b62..300579f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -10,7 +10,7 @@ use std::{ io::Write, mem, os::raw::c_char, - ptr, slice, + ptr, }; use enums::*; @@ -19,7 +19,7 @@ use structs::*; use vk_handles::*; use vulkan_rs::prelude::*; -const LOG_FILE: &'static str = "/home/michael/rf2_vk_hud.log"; +static mut LOG_FILE: String = String::new(); static mut OVERLAY: Overlay = Overlay::new(); static mut QUEUE_SUBMIT: PFN_vkQueueSubmit = @@ -29,10 +29,15 @@ static mut ACQUIRE_NEXT_IMAGE: PFN_vkAcquireNextImageKHR = #[no_mangle] #[allow(non_snake_case)] -pub extern "C" fn vkNegotiateLoaderLayerInterfaceVersion( +pub(crate) extern "C" fn vkNegotiateLoaderLayerInterfaceVersion( pVersionStruct: *mut VkNegotiateLayerInterface, ) -> VkResult { - if let Err(_) = File::create(LOG_FILE) {} + let home = std::env::var("HOME").unwrap(); + unsafe { + LOG_FILE = format!("{}/rf2_vk_hud.log", home); + } + + if let Err(_) = File::create(unsafe { &LOG_FILE }) {} write_log(" =================================================================="); write_log(" ======================= New Negotiation =========================="); @@ -112,7 +117,7 @@ extern "system" fn get_instance_proc_addr( Functions::Null.convert() } -pub extern "system" fn create_instance( +pub(crate) extern "system" fn create_instance( create_info: *const VkInstanceCreateInfo, allocator: *const VkAllocationCallbacks, instance: *mut VkInstance, @@ -177,7 +182,7 @@ pub extern "system" fn create_instance( VK_SUCCESS } -pub extern "system" fn destroy_instance( +pub(crate) extern "system" fn destroy_instance( instance: VkInstance, allocator: *const VkAllocationCallbacks, ) { @@ -192,7 +197,7 @@ pub extern "system" fn destroy_instance( } } -pub extern "system" fn create_device( +pub(crate) extern "system" fn create_device( physical_device: VkPhysicalDevice, create_info: *const VkDeviceCreateInfo<'_>, allocator: *const VkAllocationCallbacks, @@ -257,26 +262,65 @@ pub extern "system" fn create_device( return VK_ERROR_INITIALIZATION_FAILED; } }; + + let queue_info = match Queue::create_non_presentable_request_info(&pdev, VK_QUEUE_GRAPHICS_BIT) + { + Ok(qi) => qi, + Err(err) => { + write_log(format!("failed getting queue info: {:?}", err)); + return VK_ERROR_INITIALIZATION_FAILED; + } + }; + + unsafe { + let create_queue_info = std::slice::from_raw_parts( + (*create_info).pQueueCreateInfos, + (*create_info).queueCreateInfoCount as usize, + ); + + for info in create_queue_info { + write_log(format!( + "pCreateDeviceInfo; queue fam: {}, queue count: {}", + info.queueFamilyIndex, info.queueCount + )); + } + + write_log(format!( + "Queue: queue fam: {}, queue count: {}", + queue_info.queue_create_info.queueFamilyIndex, queue_info.queue_create_info.queueCount + )); + } + let ext_names = unsafe { (*create_info).extension_names() }; write_log(format!("{:?}", ext_names)); + let device = match Device::preinitialized(unsafe { *device }, proc_addr, pdev, &ext_names) { + Ok(dev) => dev, + Err(err) => { + write_log(format!("-> local device creation failed: {:?}", err)); + return VK_ERROR_INITIALIZATION_FAILED; + } + }; + + write_log("created device"); + + let queue = device.get_queue(queue_info.queue_family_index, queue_info.queue_index); + + write_log("got queue from device"); + unsafe { - OVERLAY.set_device( - match Device::preinitialized(*device, proc_addr, pdev, &ext_names) { - Ok(dev) => dev, - Err(err) => { - write_log(format!("-> local device creation failed: {:?}", err)); - return VK_ERROR_INITIALIZATION_FAILED; - } - }, - ); + OVERLAY.set_device(device); + OVERLAY.set_queue(queue); } VK_SUCCESS } -pub extern "system" fn destroy_device(device: VkDevice, allocator: *const VkAllocationCallbacks) { +pub(crate) extern "system" fn destroy_device( + device: VkDevice, + allocator: *const VkAllocationCallbacks, +) { write_log(" ================== vulkan layer destroy device =================="); unsafe { @@ -288,7 +332,7 @@ pub extern "system" fn destroy_device(device: VkDevice, allocator: *const VkAllo } } -pub extern "system" fn create_swapchain( +pub(crate) extern "system" fn create_swapchain( _device: VkDevice, create_info: *const VkSwapchainCreateInfoKHR, _allocator: *const VkAllocationCallbacks, @@ -307,9 +351,10 @@ pub extern "system" fn create_swapchain( create_swapchain(_device, create_info, _allocator, p_swapchain); - write_log("-> created swapchain vk handle"); + write_log(format!("-> created swapchain vk handle {:?}", unsafe { + *p_swapchain + })); - // if unsafe { !OVERLAY.has_rendering() } { let swapchain = match unsafe { Swapchain::from_raw(OVERLAY.device(), &*create_info, *p_swapchain) } { Ok(swapchain) => swapchain, @@ -327,12 +372,11 @@ pub extern "system" fn create_swapchain( } write_log("-> created renderer"); - // } VK_SUCCESS } -pub extern "system" fn submit_queue( +pub(crate) extern "system" fn submit_queue( queue: VkQueue, submit_count: u32, submits: *const VkSubmitInfo, @@ -341,31 +385,37 @@ pub extern "system" fn submit_queue( write_log(" ================== vulkan layer submit queue =================="); unsafe { - let command_buffers = slice::from_raw_parts( - (*submits).pCommandBuffers, - (*submits).commandBufferCount as usize, - ); + write_log(format!( + "submit queue ({:?}), internal queue: ({:?})", + queue, + OVERLAY.queue().lock().unwrap().vk_handle() + )); - let cb = match OVERLAY.render() { - Ok(cb) => cb, - Err(err) => { - write_log(format!("overlay rendering failed: {:?}", err)); - return VK_ERROR_DEVICE_LOST; - } - }; + // let command_buffers = slice::from_raw_parts( + // (*submits).pCommandBuffers, + // (*submits).commandBufferCount as usize, + // ); - let cb = [command_buffers, &[cb]].concat(); + // let cb = match OVERLAY.render() { + // Ok(cb) => cb, + // Err(err) => { + // write_log(format!("overlay rendering failed: {:?}", err)); + // return VK_ERROR_DEVICE_LOST; + // } + // }; - let mut_ptr = submits as *mut VkSubmitInfo; + // let cb = [command_buffers, &[cb]].concat(); - (*mut_ptr).commandBufferCount = cb.len() as u32; - (*mut_ptr).pCommandBuffers = cb.as_ptr(); + // let mut_ptr = submits as *mut VkSubmitInfo; - QUEUE_SUBMIT(queue, submit_count, mut_ptr, fence) + // (*mut_ptr).commandBufferCount = cb.len() as u32; + // (*mut_ptr).pCommandBuffers = cb.as_ptr(); + + QUEUE_SUBMIT(queue, submit_count, submits, fence) } } -pub extern "system" fn get_device_queue( +pub(crate) extern "system" fn get_device_queue( device: VkDevice, queue_family_index: u32, queue_index: u32, @@ -383,21 +433,26 @@ pub extern "system" fn get_device_queue( get_device_queue(device, queue_family_index, queue_index, p_queue); write_log(format!("new queue: {:?}", unsafe { *p_queue })); - unsafe { - if !OVERLAY.has_queue() { - let arc_device = OVERLAY.device(); - OVERLAY.add_queue(Queue::new( - arc_device, - *p_queue, - queue_family_index, - queue_index, - )); - } + write_log(format!( + "internal queue: ({:?})", + OVERLAY.queue().lock().unwrap().vk_handle() + )); } + + // unsafe { + // let arc_device = OVERLAY.device(); + + // arc_device.physical_device().OVERLAY.add_queue(Queue::new( + // arc_device, + // *p_queue, + // queue_family_index, + // queue_index, + // )); + // } } -pub extern "system" fn acquire_next_image( +pub(crate) extern "system" fn acquire_next_image( device: VkDevice, swapchain: VkSwapchainKHR, timeout: u64, @@ -417,7 +472,11 @@ pub extern "system" fn acquire_next_image( Some(fence), ); - write_log(format!("acquire res: {:?}", res)); + write_log(format!( + "acquire (swapchain: {:?}) res: {:?}", + sc.vk_handle(), + res + )); match res { Ok(res) => match res { @@ -442,7 +501,7 @@ pub extern "system" fn acquire_next_image( } } -pub extern "system" fn present_queue( +pub(crate) extern "system" fn present_queue( queue: VkQueue, present_info: *const VkPresentInfoKHR, ) -> VkResult { @@ -450,6 +509,30 @@ pub extern "system" fn present_queue( write_log(format!("iq: {:?}, cq: {:?}", queue, unsafe { OVERLAY.queue().lock().unwrap().vk_handle() })); + unsafe { + let swapchains = std::slice::from_raw_parts( + (*present_info).pSwapchains, + (*present_info).swapchainCount as usize, + ); + + write_log(format!("present {} swapchain(s)", swapchains.len())); + + for swapchain in swapchains { + write_log(format!("present swapchain: {:?}", swapchain)); + + if let Some(swch) = OVERLAY.swapchain(*swapchain) { + write_log(" -> internal swapchain found!"); + } + } + } + + match unsafe { OVERLAY.render() } { + Ok(_) => (), + Err(err) => { + write_log(format!("overlay rendering failed: {:?}", err)); + return VK_ERROR_DEVICE_LOST; + } + }; let pfn: PFN_vkQueuePresentKHR = match vk_handles().handle("vkQueuePresentKHR") { Some(pfn) => unsafe { mem::transmute(pfn) }, @@ -463,7 +546,11 @@ pub extern "system" fn present_queue( } pub fn write_log(msg: impl ToString) { - if let Ok(mut file) = OpenOptions::new().append(true).create(true).open(LOG_FILE) { + if let Ok(mut file) = OpenOptions::new() + .append(true) + .create(true) + .open(unsafe { &LOG_FILE }) + { if let Err(_) = file.write_all(format!("{}\n", msg.to_string()).as_bytes()) {} } } diff --git a/src/overlay/mod.rs b/src/overlay/mod.rs index c6a4623..d35c22f 100644 --- a/src/overlay/mod.rs +++ b/src/overlay/mod.rs @@ -51,7 +51,7 @@ impl Overlay { self.queue.is_some() } - pub fn add_queue(&mut self, queue: Arc>) { + pub fn set_queue(&mut self, queue: Arc>) { self.queue = Some(queue); } @@ -85,17 +85,17 @@ impl Overlay { Ok(()) } - pub fn render(&mut self) -> Result { + pub fn render(&mut self) -> Result<()> { if self.rfactor_data.is_none() { - // self.rfactor_data = RFactorData::new( - // self.device(), - // self.rendering - // .as_mut() - // .unwrap() - // .single_color_pipeline() - // .descriptor_layout(), - // ) - // .ok(); + self.rfactor_data = RFactorData::new( + self.device(), + self.rendering + .as_mut() + .unwrap() + .single_color_pipeline() + .descriptor_layout(), + ) + .ok(); } // check twice for rfactor data, because of borrowing rules diff --git a/src/overlay/rendering.rs b/src/overlay/rendering.rs index 70a7182..55d9d5c 100644 --- a/src/overlay/rendering.rs +++ b/src/overlay/rendering.rs @@ -2,7 +2,10 @@ use anyhow::Result; use cgmath::{Vector2, Vector4}; use vulkan_rs::prelude::*; -use std::sync::{Arc, Mutex}; +use std::{ + sync::{Arc, Mutex}, + time::Duration, +}; use super::{pipeline::SingleColorPipeline, rfactor_data::RenderObject}; use crate::write_log; @@ -48,6 +51,9 @@ pub struct Rendering { pipeline: SingleColorPipeline, render_target: RenderTarget, command_buffer: Arc, + + device: Arc, + queue: Arc>, } impl Rendering { @@ -91,7 +97,10 @@ impl Rendering { swapchain, pipeline: SingleColorPipeline::new(device.clone(), render_target.render_pass())?, render_target, - command_buffer: CommandBuffer::new_primary().build(device, queue)?, + command_buffer: CommandBuffer::new_primary().build(device.clone(), queue.clone())?, + + device, + queue, }) } @@ -107,7 +116,7 @@ impl Rendering { &mut self, swapchain: Arc, objects: &[&dyn RenderObject], - ) -> Result { + ) -> Result<()> { let image_index = self.swapchain.current_index(); let viewport = [VkViewport { @@ -153,6 +162,9 @@ impl Rendering { self.render_target.end(&recorder); } - Ok(self.command_buffer.vk_handle()) + let queue = self.queue.lock().unwrap(); + queue.minimal_submit(Duration::from_secs(10), &[self.command_buffer.clone()])?; + + Ok(()) } } diff --git a/src/overlay/rfactor_data.rs b/src/overlay/rfactor_data.rs index f47de8d..576ac00 100644 --- a/src/overlay/rfactor_data.rs +++ b/src/overlay/rfactor_data.rs @@ -14,8 +14,8 @@ pub trait RenderObject { pub struct RFactorData { // rf2 memory mapped data - telemetry_reader: TelemetryReader, - scoring_reader: ScoringReader, + // telemetry_reader: TelemetryReader, + // scoring_reader: ScoringReader, // radar objects background: RadarObject, @@ -30,9 +30,8 @@ impl RFactorData { let car_width = 0.025; Ok(Self { - telemetry_reader: TelemetryReader::new()?, - scoring_reader: ScoringReader::new()?, - + // telemetry_reader: TelemetryReader::new()?, + // scoring_reader: ScoringReader::new()?, background: RadarObject::new( device.clone(), descriptor_layout, From 43a36c0b75e8be2cfaec2f84aa0e0819f0267dd4 Mon Sep 17 00:00:00 2001 From: hodasemi Date: Sat, 14 Jan 2023 14:27:23 +0100 Subject: [PATCH 15/85] Update dependency link --- Cargo.toml | 3 +-- src/overlay/mod.rs | 4 ---- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 4792f35..0a7c9ad 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,8 +9,7 @@ edition = "2021" crate-type = ["cdylib"] [dependencies] -vulkan-rs = { git = "https://gavania.de/Gavania/Gavania.git", branch = "0.2.0_dev" } -# vulkan-rs = { path = "/home/michael/Dokumente/Workspace/Gavania/vulkan-rs" } +vulkan-rs = { git = "https://gavania.de/hodasemi/vulkan_lib.git" } rfactor_sm_reader = { git = "https://gavania.de/hodasemi/rfactor_sm_reader.git" } anyhow = { version = "1.0.68", features = ["backtrace"] } diff --git a/src/overlay/mod.rs b/src/overlay/mod.rs index d35c22f..2050d7e 100644 --- a/src/overlay/mod.rs +++ b/src/overlay/mod.rs @@ -47,10 +47,6 @@ impl Overlay { self.device.as_ref().unwrap().clone() } - pub fn has_queue(&self) -> bool { - self.queue.is_some() - } - pub fn set_queue(&mut self, queue: Arc>) { self.queue = Some(queue); } From 88ba6d5e138d53a28fe4c259185d63282a05dd64 Mon Sep 17 00:00:00 2001 From: hodasemi Date: Sat, 14 Jan 2023 20:15:43 +0100 Subject: [PATCH 16/85] Start implementing radar data backend --- .vscode/tasks.json | 11 +- src/enums.rs | 2 - src/lib.rs | 240 ++++++++++++++---------------------- src/overlay/mod.rs | 26 ++-- src/overlay/rendering.rs | 30 ++--- src/overlay/rfactor_data.rs | 218 ++++++++++++++++++++++++++++---- 6 files changed, 330 insertions(+), 197 deletions(-) diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 88381fe..07abeed 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -9,6 +9,15 @@ ], "group": "build", "label": "rust: cargo build" - } + }, + { + "type": "shell", + "command": "steam steam://rungameid/365960", + "group": "build", + "label": "Run rFactor2", + "dependsOn": [ + "rust: cargo build" + ] + }, ] } \ No newline at end of file diff --git a/src/enums.rs b/src/enums.rs index 813d5ed..5f701e6 100644 --- a/src/enums.rs +++ b/src/enums.rs @@ -64,8 +64,6 @@ create_functions_enum!( [CreateDevice, create_device], [DestroyDevice, destroy_device], [CreateSwapchainKHR, create_swapchain], - [QueueSubmit, submit_queue], - [GetDeviceQueue, get_device_queue], [AcquireNextImageKHR, acquire_next_image], [QueuePresentKHR, present_queue], ); diff --git a/src/lib.rs b/src/lib.rs index 300579f..2700f58 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -19,30 +19,55 @@ use structs::*; use vk_handles::*; use vulkan_rs::prelude::*; +static mut LOG_ENABLED: bool = true; static mut LOG_FILE: String = String::new(); - static mut OVERLAY: Overlay = Overlay::new(); -static mut QUEUE_SUBMIT: PFN_vkQueueSubmit = - unsafe { mem::transmute(vkVoidFunction as *const c_void) }; static mut ACQUIRE_NEXT_IMAGE: PFN_vkAcquireNextImageKHR = unsafe { mem::transmute(vkVoidFunction as *const c_void) }; +pub(crate) fn logging() -> bool { + unsafe { LOG_ENABLED } +} + +macro_rules! write_log { + ($msg:expr) => { + if crate::logging() { + crate::log($msg); + } + }; +} + +pub(crate) use write_log; + #[no_mangle] #[allow(non_snake_case)] pub(crate) extern "C" fn vkNegotiateLoaderLayerInterfaceVersion( pVersionStruct: *mut VkNegotiateLayerInterface, ) -> VkResult { - let home = std::env::var("HOME").unwrap(); - unsafe { - LOG_FILE = format!("{}/rf2_vk_hud.log", home); + let log_enabled = match std::env::var("RFACTOR_HUD_LOG") { + Ok(var) => { + let i: u32 = var.parse().unwrap_or(0); + + i == 1 + } + Err(_) => false, + }; + + unsafe { LOG_ENABLED = log_enabled }; + + if logging() { + let home = std::env::var("HOME").unwrap(); + unsafe { + LOG_FILE = format!("{}/rf2_vk_hud.log", home); + } + + if let Err(_) = File::create(unsafe { &LOG_FILE }) {} + + write_log!(" =================================================================="); + write_log!(" ======================= New Negotiation =========================="); + write_log!(" =================================================================="); } - if let Err(_) = File::create(unsafe { &LOG_FILE }) {} - - write_log(" =================================================================="); - write_log(" ======================= New Negotiation =========================="); - write_log(" =================================================================="); - unsafe { *pVersionStruct = VkNegotiateLayerInterface { sType: enums::VkNegotiateLayerStructType::LAYER_NEGOTIATE_INTERFACE_STRUCT, @@ -57,7 +82,7 @@ pub(crate) extern "C" fn vkNegotiateLoaderLayerInterfaceVersion( set_vk_handles(match VkTypedefHandles::new() { Ok(handles) => handles, Err(err) => { - write_log(format!("failed to load typedef handles {:?}", err)); + write_log!(format!("failed to load typedef handles {:?}", err)); return VK_ERROR_INITIALIZATION_FAILED; } }); @@ -73,7 +98,7 @@ extern "system" fn get_device_proc_addr( let func_string = match VkString::try_from(function_name) { Ok(func) => func, Err(_) => { - write_log("Err: failed creating string"); + write_log!("Err: failed creating string"); return Functions::Null.convert(); } }; @@ -99,7 +124,7 @@ extern "system" fn get_instance_proc_addr( let func_string = match VkString::try_from(function_name) { Ok(func) => func, Err(_) => { - write_log("Err: failed creating string"); + write_log!("Err: failed creating string"); return Functions::Null.convert(); } }; @@ -122,7 +147,7 @@ pub(crate) extern "system" fn create_instance( allocator: *const VkAllocationCallbacks, instance: *mut VkInstance, ) -> VkResult { - write_log(" ================== vulkan layer create instance =================="); + write_log!(" ================== vulkan layer create instance =================="); let chain_info = match VkLayerInstanceCreateInfo::get_chain_info( unsafe { &*create_info }, @@ -130,7 +155,7 @@ pub(crate) extern "system" fn create_instance( ) { Some(info) => info, None => { - write_log("instance chain info not found."); + write_log!("instance chain info not found."); return VK_ERROR_LAYER_NOT_PRESENT; } }; @@ -151,11 +176,11 @@ pub(crate) extern "system" fn create_instance( vk_handles_mut().load_instance_functions(unsafe { *instance }, proc_addr); - write_log("-> successfully created instance."); + write_log!("-> successfully created instance."); let ext_names = unsafe { (*create_info).extension_names() }; - write_log(format!("{:?}", ext_names)); + write_log!(format!("{:?}", ext_names)); // DXVK workaround, it creates the instance twice with different properties if ext_names.contains(&VkString::new("VK_KHR_surface")) { @@ -168,7 +193,7 @@ pub(crate) extern "system" fn create_instance( ) { Ok(ins) => ins, Err(err) => { - write_log(format!("-> local instance creation failed: {:?}", err)); + write_log!(format!("-> local instance creation failed: {:?}", err)); return VK_ERROR_INITIALIZATION_FAILED; } }; @@ -176,7 +201,7 @@ pub(crate) extern "system" fn create_instance( OVERLAY.set_instance(ins); } - write_log("-> created local instance handle"); + write_log!("-> created local instance handle"); } VK_SUCCESS @@ -186,7 +211,7 @@ pub(crate) extern "system" fn destroy_instance( instance: VkInstance, allocator: *const VkAllocationCallbacks, ) { - write_log(" ================== vulkan layer destroy instance =================="); + write_log!(" ================== vulkan layer destroy instance =================="); unsafe { if let Some(vk_fn) = vk_handles().handle("vkDestroyInstance") { @@ -203,14 +228,14 @@ pub(crate) extern "system" fn create_device( allocator: *const VkAllocationCallbacks, device: *mut VkDevice, ) -> VkResult { - write_log(" ================== vulkan layer create device =================="); + write_log!(" ================== vulkan layer create device =================="); let chain_info = match VkLayerDeviceCreateInfo::get_chain_info(unsafe { &*create_info }, VK_LAYER_LINK_INFO) { Some(info) => info, None => { - write_log("device chain info not found."); + write_log!("device chain info not found."); return VK_ERROR_LAYER_NOT_PRESENT; } }; @@ -226,7 +251,7 @@ pub(crate) extern "system" fn create_device( create_device(physical_device, create_info, allocator, device) } None => { - write_log("failed creating device"); + write_log!("failed creating device"); return VK_ERROR_INITIALIZATION_FAILED; } } @@ -238,18 +263,10 @@ pub(crate) extern "system" fn create_device( vk_handles_mut().load_device_functions(unsafe { *device }, proc_addr); unsafe { - QUEUE_SUBMIT = match vk_handles().handle("vkQueueSubmit") { - Some(submit) => mem::transmute(submit), - None => { - write_log("failed querying vkQueueSubmit"); - return VK_ERROR_INITIALIZATION_FAILED; - } - }; - ACQUIRE_NEXT_IMAGE = match vk_handles().handle("vkAcquireNextImageKHR") { Some(acquire_next_image) => mem::transmute(acquire_next_image), None => { - write_log("failed querying vkAcquireNextImageKHR"); + write_log!("failed querying vkAcquireNextImageKHR"); return VK_ERROR_INITIALIZATION_FAILED; } }; @@ -258,7 +275,7 @@ pub(crate) extern "system" fn create_device( let pdev = match PhysicalDevice::from_raw(unsafe { OVERLAY.instance() }, physical_device) { Ok(pdev) => pdev, Err(err) => { - write_log(format!("failed creating physical device: {:?}", err)); + write_log!(format!("failed creating physical device: {:?}", err)); return VK_ERROR_INITIALIZATION_FAILED; } }; @@ -267,7 +284,7 @@ pub(crate) extern "system" fn create_device( { Ok(qi) => qi, Err(err) => { - write_log(format!("failed getting queue info: {:?}", err)); + write_log!(format!("failed getting queue info: {:?}", err)); return VK_ERROR_INITIALIZATION_FAILED; } }; @@ -279,13 +296,13 @@ pub(crate) extern "system" fn create_device( ); for info in create_queue_info { - write_log(format!( + write_log!(format!( "pCreateDeviceInfo; queue fam: {}, queue count: {}", info.queueFamilyIndex, info.queueCount )); } - write_log(format!( + write_log!(format!( "Queue: queue fam: {}, queue count: {}", queue_info.queue_create_info.queueFamilyIndex, queue_info.queue_create_info.queueCount )); @@ -293,21 +310,21 @@ pub(crate) extern "system" fn create_device( let ext_names = unsafe { (*create_info).extension_names() }; - write_log(format!("{:?}", ext_names)); + write_log!(format!("{:?}", ext_names)); let device = match Device::preinitialized(unsafe { *device }, proc_addr, pdev, &ext_names) { Ok(dev) => dev, Err(err) => { - write_log(format!("-> local device creation failed: {:?}", err)); + write_log!(format!("-> local device creation failed: {:?}", err)); return VK_ERROR_INITIALIZATION_FAILED; } }; - write_log("created device"); + write_log!("created device"); let queue = device.get_queue(queue_info.queue_family_index, queue_info.queue_index); - write_log("got queue from device"); + write_log!("got queue from device"); unsafe { OVERLAY.set_device(device); @@ -321,7 +338,7 @@ pub(crate) extern "system" fn destroy_device( device: VkDevice, allocator: *const VkAllocationCallbacks, ) { - write_log(" ================== vulkan layer destroy device =================="); + write_log!(" ================== vulkan layer destroy device =================="); unsafe { if let Some(vk_fn) = vk_handles().handle("vkDestroyDevice") { @@ -338,20 +355,20 @@ pub(crate) extern "system" fn create_swapchain( _allocator: *const VkAllocationCallbacks, p_swapchain: *mut VkSwapchainKHR, ) -> VkResult { - write_log(" ================== vulkan layer create swapchain =================="); + write_log!(" ================== vulkan layer create swapchain =================="); let create_swapchain: PFN_vkCreateSwapchainKHR = match vk_handles().handle("vkCreateSwapchainKHR") { Some(create_swapchain) => unsafe { mem::transmute(create_swapchain) }, None => { - write_log("failed querying vkCreateSwapchainKHR"); + write_log!("failed querying vkCreateSwapchainKHR"); return VK_ERROR_INITIALIZATION_FAILED; } }; create_swapchain(_device, create_info, _allocator, p_swapchain); - write_log(format!("-> created swapchain vk handle {:?}", unsafe { + write_log!(format!("-> created swapchain vk handle {:?}", unsafe { *p_swapchain })); @@ -359,99 +376,23 @@ pub(crate) extern "system" fn create_swapchain( match unsafe { Swapchain::from_raw(OVERLAY.device(), &*create_info, *p_swapchain) } { Ok(swapchain) => swapchain, Err(err) => { - write_log(format!("create swapchain failed: {:?}", err)); + write_log!(format!("create swapchain failed: {:?}", err)); return VK_ERROR_INITIALIZATION_FAILED; } }; - write_log("-> created Arc"); + write_log!("-> created Arc"); if let Err(err) = unsafe { OVERLAY.create_rendering(swapchain) } { - write_log(format!("create overlay rendering struct failed: {:?}", err)); + write_log!(format!("create overlay rendering struct failed: {:?}", err)); return VK_ERROR_INITIALIZATION_FAILED; } - write_log("-> created renderer"); + write_log!("-> created renderer"); VK_SUCCESS } -pub(crate) extern "system" fn submit_queue( - queue: VkQueue, - submit_count: u32, - submits: *const VkSubmitInfo, - fence: VkFence, -) -> VkResult { - write_log(" ================== vulkan layer submit queue =================="); - - unsafe { - write_log(format!( - "submit queue ({:?}), internal queue: ({:?})", - queue, - OVERLAY.queue().lock().unwrap().vk_handle() - )); - - // let command_buffers = slice::from_raw_parts( - // (*submits).pCommandBuffers, - // (*submits).commandBufferCount as usize, - // ); - - // let cb = match OVERLAY.render() { - // Ok(cb) => cb, - // Err(err) => { - // write_log(format!("overlay rendering failed: {:?}", err)); - // return VK_ERROR_DEVICE_LOST; - // } - // }; - - // let cb = [command_buffers, &[cb]].concat(); - - // let mut_ptr = submits as *mut VkSubmitInfo; - - // (*mut_ptr).commandBufferCount = cb.len() as u32; - // (*mut_ptr).pCommandBuffers = cb.as_ptr(); - - QUEUE_SUBMIT(queue, submit_count, submits, fence) - } -} - -pub(crate) extern "system" fn get_device_queue( - device: VkDevice, - queue_family_index: u32, - queue_index: u32, - p_queue: *mut VkQueue, -) { - write_log(" ================== vulkan layer get device queue =================="); - - let get_device_queue: PFN_vkGetDeviceQueue = match vk_handles().handle("vkGetDeviceQueue") { - Some(get_queue) => unsafe { mem::transmute(get_queue) }, - None => { - write_log("failed querying vkGetDeviceQueue"); - panic!("failed querying vkGetDeviceQueue"); - } - }; - - get_device_queue(device, queue_family_index, queue_index, p_queue); - write_log(format!("new queue: {:?}", unsafe { *p_queue })); - unsafe { - write_log(format!( - "internal queue: ({:?})", - OVERLAY.queue().lock().unwrap().vk_handle() - )); - } - - // unsafe { - // let arc_device = OVERLAY.device(); - - // arc_device.physical_device().OVERLAY.add_queue(Queue::new( - // arc_device, - // *p_queue, - // queue_family_index, - // queue_index, - // )); - // } -} - pub(crate) extern "system" fn acquire_next_image( device: VkDevice, swapchain: VkSwapchainKHR, @@ -472,7 +413,7 @@ pub(crate) extern "system" fn acquire_next_image( Some(fence), ); - write_log(format!( + write_log!(format!( "acquire (swapchain: {:?}) res: {:?}", sc.vk_handle(), res @@ -488,13 +429,13 @@ pub(crate) extern "system" fn acquire_next_image( OutOfDate::TimeOut => VK_TIMEOUT, }, Err(err) => { - write_log(format!("failed acquiring next image {:?}", err)); + write_log!(format!("failed acquiring next image {:?}", err)); VK_ERROR_DEVICE_LOST } } } None => { - write_log("acquired other swapchain image"); + write_log!("acquired other swapchain image"); ACQUIRE_NEXT_IMAGE(device, swapchain, timeout, semaphore, fence, image_index) } } @@ -505,23 +446,28 @@ pub(crate) extern "system" fn present_queue( queue: VkQueue, present_info: *const VkPresentInfoKHR, ) -> VkResult { - write_log(" ================== vulkan layer queue present =================="); - write_log(format!("iq: {:?}, cq: {:?}", queue, unsafe { - OVERLAY.queue().lock().unwrap().vk_handle() - })); - unsafe { - let swapchains = std::slice::from_raw_parts( - (*present_info).pSwapchains, - (*present_info).swapchainCount as usize, - ); + if logging() { + write_log!(" ================== vulkan layer queue present =================="); + write_log!(format!("iq: {:?}, cq: {:?}", queue, unsafe { + OVERLAY.queue().lock().unwrap().vk_handle() + })); + unsafe { + let swapchains = std::slice::from_raw_parts( + (*present_info).pSwapchains, + (*present_info).swapchainCount as usize, + ); - write_log(format!("present {} swapchain(s)", swapchains.len())); + write_log!(format!("present {} swapchain(s)", swapchains.len())); - for swapchain in swapchains { - write_log(format!("present swapchain: {:?}", swapchain)); + for swapchain in swapchains { + write_log!(format!("present swapchain: {:?}", swapchain)); - if let Some(swch) = OVERLAY.swapchain(*swapchain) { - write_log(" -> internal swapchain found!"); + if let Some(swch) = OVERLAY.swapchain(*swapchain) { + write_log!(format!( + " -> internal swapchain found! ({:?})", + swch.vk_handle() + )); + } } } } @@ -529,7 +475,7 @@ pub(crate) extern "system" fn present_queue( match unsafe { OVERLAY.render() } { Ok(_) => (), Err(err) => { - write_log(format!("overlay rendering failed: {:?}", err)); + write_log!(format!("overlay rendering failed: {:?}", err)); return VK_ERROR_DEVICE_LOST; } }; @@ -537,7 +483,7 @@ pub(crate) extern "system" fn present_queue( let pfn: PFN_vkQueuePresentKHR = match vk_handles().handle("vkQueuePresentKHR") { Some(pfn) => unsafe { mem::transmute(pfn) }, None => { - write_log("failed querying vkQueuePresentKHR"); + write_log!("failed querying vkQueuePresentKHR"); return VK_ERROR_DEVICE_LOST; } }; @@ -545,7 +491,9 @@ pub(crate) extern "system" fn present_queue( pfn(queue, present_info) } -pub fn write_log(msg: impl ToString) { +pub fn log(msg: impl ToString) { + assert!(logging()); + if let Ok(mut file) = OpenOptions::new() .append(true) .create(true) diff --git a/src/overlay/mod.rs b/src/overlay/mod.rs index 2050d7e..df9ae54 100644 --- a/src/overlay/mod.rs +++ b/src/overlay/mod.rs @@ -1,12 +1,16 @@ use crate::write_log; -use self::{rendering::Rendering, rfactor_data::RFactorData}; +use self::{ + rendering::Rendering, + rfactor_data::{DataConfig, RFactorData}, +}; mod pipeline; mod rendering; mod rfactor_data; use anyhow::Result; +use cgmath::vec3; use std::sync::{Arc, Mutex}; use vulkan_rs::prelude::*; @@ -66,30 +70,40 @@ impl Overlay { } pub fn create_rendering(&mut self, swapchain: Arc) -> Result<()> { - write_log("-> create rendering: start"); + write_log!("-> create rendering: start"); self.rendering = None; - write_log("-> create rendering: old cleared"); + write_log!("-> create rendering: old cleared"); self.rendering = Some(Rendering::new(self.device(), self.queue(), swapchain)?); - write_log("-> create rendering: new created"); + write_log!("-> create rendering: new created"); - write_log("-> create rendering: end"); + write_log!("-> create rendering: end"); Ok(()) } pub fn render(&mut self) -> Result<()> { + let swapchain = self.rendering.as_ref().unwrap().swapchain().clone(); + if self.rfactor_data.is_none() { self.rfactor_data = RFactorData::new( + DataConfig { + radar_scale: 1.0, + radar_car_distance: 20.0, + safe_color: vec3(0.0, 0.75, 0.0), + danger_color: vec3(0.75, 0.0, 0.0), + }, self.device(), self.rendering .as_mut() .unwrap() .single_color_pipeline() .descriptor_layout(), + swapchain.width(), + swapchain.height(), ) .ok(); } @@ -104,8 +118,6 @@ impl Overlay { None => Vec::new(), }; - let swapchain = self.rendering.as_ref().unwrap().swapchain().clone(); - self.rendering.as_mut().unwrap().render(swapchain, &objects) } } diff --git a/src/overlay/rendering.rs b/src/overlay/rendering.rs index 55d9d5c..d642723 100644 --- a/src/overlay/rendering.rs +++ b/src/overlay/rendering.rs @@ -1,5 +1,5 @@ use anyhow::Result; -use cgmath::{Vector2, Vector4}; +use cgmath::{Matrix4, Vector2, Vector4}; use vulkan_rs::prelude::*; use std::{ @@ -22,25 +22,25 @@ impl PositionOnlyVertex { /// corners[2] - top right /// corners[3] - bottom right /// - pub fn from_2d_corners(corners: [Vector2; 4]) -> [Self; 6] { + pub fn from_2d_corners(ortho: Matrix4, corners: [Vector2; 4]) -> [Self; 6] { [ Self { - position: corners[0].extend(0.0).extend(1.0), + position: ortho * corners[0].extend(0.0).extend(1.0), }, Self { - position: corners[1].extend(0.0).extend(1.0), + position: ortho * corners[1].extend(0.0).extend(1.0), }, Self { - position: corners[2].extend(0.0).extend(1.0), + position: ortho * corners[2].extend(0.0).extend(1.0), }, Self { - position: corners[2].extend(0.0).extend(1.0), + position: ortho * corners[2].extend(0.0).extend(1.0), }, Self { - position: corners[3].extend(0.0).extend(1.0), + position: ortho * corners[3].extend(0.0).extend(1.0), }, Self { - position: corners[0].extend(0.0).extend(1.0), + position: ortho * corners[0].extend(0.0).extend(1.0), }, ] } @@ -62,20 +62,20 @@ impl Rendering { queue: Arc>, swapchain: Arc, ) -> Result { - write_log("-> Rendering ctor: begin"); + crate::write_log!("-> Rendering ctor: begin"); let vk_images = swapchain.vk_images()?; - write_log(format!( + write_log!(format!( "-> Rendering ctor: vk images ({})", vk_images.len() )); let images = match swapchain.wrap_images(&vk_images, &queue, true) { Ok(images) => images, Err(err) => { - write_log(format!("-> Rendering ctor: failed wrapper: {:?}", err)); + write_log!(format!("-> Rendering ctor: failed wrapper: {:?}", err)); return Err(err); } }; - write_log("-> Rendering ctor: wrapped images"); + write_log!("-> Rendering ctor: wrapped images"); let render_target = RenderTarget::builder() .add_sub_pass( @@ -85,9 +85,9 @@ impl Rendering { ) .build(&device)?; - write_log("-> Rendering ctor: created render_target"); + write_log!("-> Rendering ctor: created render_target"); - write_log(format!( + write_log!(format!( "-> Rendering swapchain extents ({}, {})", swapchain.width(), swapchain.height(), @@ -149,7 +149,7 @@ impl Rendering { recorder.set_scissor(&scissor); recorder.set_viewport(&viewport); - write_log(format!("-> Rendering {} objects", objects.len())); + write_log!(format!("-> Rendering {} objects", objects.len())); for object in objects { let buffer = object.buffer(); diff --git a/src/overlay/rfactor_data.rs b/src/overlay/rfactor_data.rs index 576ac00..62fbc9f 100644 --- a/src/overlay/rfactor_data.rs +++ b/src/overlay/rfactor_data.rs @@ -1,5 +1,5 @@ use anyhow::Result; -use cgmath::vec2; +use cgmath::{ortho, vec2, vec3, InnerSpace, Matrix4, Vector2, Vector3, VectorSpace}; use rfactor_sm_reader::*; use vulkan_rs::prelude::*; @@ -7,72 +7,224 @@ use std::sync::Arc; use super::rendering::PositionOnlyVertex; +fn convert_vec(v: rF2Vec3) -> Vector3 { + vec3(v.x as f32, v.y as f32, v.z as f32) +} + pub trait RenderObject { fn descriptor(&self) -> &Arc; fn buffer(&self) -> &Arc>; } +pub struct DataConfig { + pub radar_scale: f32, + pub radar_car_distance: f32, + pub safe_color: Vector3, + pub danger_color: Vector3, +} + pub struct RFactorData { + // config + config: DataConfig, + // rf2 memory mapped data - // telemetry_reader: TelemetryReader, - // scoring_reader: ScoringReader, + telemetry_reader: TelemetryReader, + scoring_reader: ScoringReader, // radar objects background: RadarObject, player_car: RadarObject, cars: Vec, + + // game info + player_id: Option, + + // math objects + radar_center: Vector2, + ortho: Matrix4, + window_width: u32, + window_height: u32, + car_width: f32, + car_height: f32, + + device: Arc, + descriptor_layout: Arc, } impl RFactorData { - pub fn new(device: Arc, descriptor_layout: &Arc) -> Result { - let radar_extent = 0.2; - let car_height = 0.05; - let car_width = 0.025; + pub fn new( + config: DataConfig, + device: Arc, + descriptor_layout: &Arc, + width: u32, + height: u32, + ) -> Result { + let radar_extent = width as f32 * 0.075 * config.radar_scale; + let car_height = radar_extent * 0.2; + let car_width = car_height * 0.5; + let radar_center = vec2( + width as f32 / 2.0, + height as f32 / 2.0 + height as f32 * 0.25, + ); + + let ortho = ortho(0.0, width as f32, 0.0, height as f32, -1.0, 1.0); Ok(Self { - // telemetry_reader: TelemetryReader::new()?, - // scoring_reader: ScoringReader::new()?, + config, + + telemetry_reader: TelemetryReader::new()?, + scoring_reader: ScoringReader::new()?, + background: RadarObject::new( device.clone(), descriptor_layout, - PositionOnlyVertex::from_2d_corners([ - vec2(-radar_extent, -radar_extent), - vec2(-radar_extent, radar_extent), - vec2(radar_extent, radar_extent), - vec2(radar_extent, -radar_extent), - ]), + PositionOnlyVertex::from_2d_corners( + ortho, + [ + vec2(radar_center.x - radar_extent, radar_center.y - radar_extent), + vec2(radar_center.x - radar_extent, radar_center.y + radar_extent), + vec2(radar_center.x + radar_extent, radar_center.y + radar_extent), + vec2(radar_center.x + radar_extent, radar_center.y - radar_extent), + ], + ), [0.5, 0.5, 0.5, 0.5], )?, player_car: RadarObject::new( device.clone(), descriptor_layout, - PositionOnlyVertex::from_2d_corners([ - vec2(-car_width, -car_height), - vec2(-car_width, car_height), - vec2(car_width, car_height), - vec2(car_width, -car_height), - ]), + PositionOnlyVertex::from_2d_corners( + ortho, + [ + vec2(radar_center.x - car_width, radar_center.y - car_height), + vec2(radar_center.x - car_width, radar_center.y + car_height), + vec2(radar_center.x + car_width, radar_center.y + car_height), + vec2(radar_center.x + car_width, radar_center.y - car_height), + ], + ), [0.9, 0.9, 0.0, 0.9], )?, cars: Vec::new(), + + player_id: None, + + radar_center, + ortho, + window_width: width, + window_height: height, + car_width, + car_height, + + device, + descriptor_layout: descriptor_layout.clone(), }) } + fn create_car_object(&self, offset: Vector2, color: [f32; 4]) -> Result { + RadarObject::new( + self.device.clone(), + &self.descriptor_layout, + PositionOnlyVertex::from_2d_corners( + self.ortho, + [ + vec2( + self.radar_center.x - self.car_width + offset.x, + self.radar_center.y - self.car_height + offset.y, + ), + vec2( + self.radar_center.x - self.car_width + offset.x, + self.radar_center.y + self.car_height + offset.y, + ), + vec2( + self.radar_center.x + self.car_width + offset.x, + self.radar_center.y + self.car_height + offset.y, + ), + vec2( + self.radar_center.x + self.car_width + offset.x, + self.radar_center.y - self.car_height + offset.y, + ), + ], + ), + color, + ) + } + pub fn update(&mut self) -> Result<()> { + // get scoring info + let (scoring_info, vehicle_scorings) = self.scoring_reader.vehicle_scoring(); + + // check for player id + if scoring_info.mNumVehicles == 0 { + self.player_id = None; + } else if self.player_id.is_none() { + for vehicle_scoring in vehicle_scorings.iter() { + if vehicle_scoring.mIsPlayer != 0 { + self.player_id = Some(vehicle_scoring.mID); + } + } + } + + // if player id is set (a map is loaded), check telemetry data + if let Some(player_id) = &self.player_id { + let telemetries = self.telemetry_reader.query_telemetry(); + + let mut player_position = CarPosition::default(); + let mut other_positions = Vec::new(); + + for telemetry in telemetries { + if telemetry.id == *player_id { + player_position.position = convert_vec(telemetry.position); + player_position.local_rotation = convert_vec(telemetry.local_rotation); + } else { + other_positions.push(CarPosition { + position: convert_vec(telemetry.position), + local_rotation: convert_vec(telemetry.local_rotation), + }); + } + } + + // update radar objects + + // naive way: clear cars and create them new if near enough + self.cars.clear(); + + for other_position in other_positions { + let diff = player_position.position - other_position.position; + let distance = diff.magnitude(); + + // check if car is close enough the players car + if distance < self.config.radar_car_distance { + let offset = diff.truncate(); + let color = self.config.danger_color.lerp( + self.config.safe_color, + distance / self.config.radar_car_distance, + ); + + self.cars + .push(self.create_car_object(offset, [color.x, color.y, color.z, 0.9])?); + } + } + } + Ok(()) } pub fn objects(&self) -> Vec<&dyn RenderObject> { let mut objects: Vec<&dyn RenderObject> = Vec::new(); - objects.push(&self.background); + // only draw radar when player is loaded into a map + if let Some(player_id) = &self.player_id { + // only draw radar when any car is near enough + if !self.cars.is_empty() { + objects.push(&self.background); - for other_player_cars in &self.cars { - objects.push(other_player_cars); + for other_player_cars in &self.cars { + objects.push(other_player_cars); + } + + objects.push(&self.player_car); + } } - objects.push(&self.player_car); - objects } } @@ -134,3 +286,17 @@ impl RenderObject for RadarObject { &self.position_buffer } } + +struct CarPosition { + pub position: Vector3, + pub local_rotation: Vector3, +} + +impl Default for CarPosition { + fn default() -> Self { + Self { + position: vec3(0.0, 0.0, 0.0), + local_rotation: vec3(0.0, 0.0, 0.0), + } + } +} From d1c711586b8058283947f836f2fd02b5e98e9c33 Mon Sep 17 00:00:00 2001 From: hodasemi Date: Sun, 15 Jan 2023 06:58:23 +0100 Subject: [PATCH 17/85] Get something on the screen --- src/overlay/mod.rs | 2 + src/overlay/rendering.rs | 2 - src/overlay/rfactor_data.rs | 118 +++++++++++++++++++++++------------- 3 files changed, 79 insertions(+), 43 deletions(-) diff --git a/src/overlay/mod.rs b/src/overlay/mod.rs index df9ae54..4a74310 100644 --- a/src/overlay/mod.rs +++ b/src/overlay/mod.rs @@ -106,6 +106,8 @@ impl Overlay { swapchain.height(), ) .ok(); + + write_log!("created RFactorData"); } // check twice for rfactor data, because of borrowing rules diff --git a/src/overlay/rendering.rs b/src/overlay/rendering.rs index d642723..d36ba66 100644 --- a/src/overlay/rendering.rs +++ b/src/overlay/rendering.rs @@ -52,7 +52,6 @@ pub struct Rendering { render_target: RenderTarget, command_buffer: Arc, - device: Arc, queue: Arc>, } @@ -99,7 +98,6 @@ impl Rendering { render_target, command_buffer: CommandBuffer::new_primary().build(device.clone(), queue.clone())?, - device, queue, }) } diff --git a/src/overlay/rfactor_data.rs b/src/overlay/rfactor_data.rs index 62fbc9f..fba4836 100644 --- a/src/overlay/rfactor_data.rs +++ b/src/overlay/rfactor_data.rs @@ -3,9 +3,10 @@ use cgmath::{ortho, vec2, vec3, InnerSpace, Matrix4, Vector2, Vector3, VectorSpa use rfactor_sm_reader::*; use vulkan_rs::prelude::*; -use std::sync::Arc; +use std::{sync::Arc, time::Instant}; use super::rendering::PositionOnlyVertex; +use crate::write_log; fn convert_vec(v: rF2Vec3) -> Vector3 { vec3(v.x as f32, v.y as f32, v.z as f32) @@ -44,9 +45,12 @@ pub struct RFactorData { ortho: Matrix4, window_width: u32, window_height: u32, + radar_extent: f32, car_width: f32, car_height: f32, + start_time: Instant, + device: Arc, descriptor_layout: Arc, } @@ -59,6 +63,8 @@ impl RFactorData { width: u32, height: u32, ) -> Result { + write_log!(" =================== create RFactorData ==================="); + let radar_extent = width as f32 * 0.075 * config.radar_scale; let car_height = radar_extent * 0.2; let car_width = car_height * 0.5; @@ -68,12 +74,13 @@ impl RFactorData { ); let ortho = ortho(0.0, width as f32, 0.0, height as f32, -1.0, 1.0); + let start_time = Instant::now(); Ok(Self { config, - telemetry_reader: TelemetryReader::new()?, - scoring_reader: ScoringReader::new()?, + telemetry_reader: TelemetryReader::new(start_time.elapsed().as_secs_f32())?, + scoring_reader: ScoringReader::new(start_time.elapsed().as_secs_f32())?, background: RadarObject::new( device.clone(), @@ -111,15 +118,20 @@ impl RFactorData { ortho, window_width: width, window_height: height, + radar_extent, car_width, car_height, + start_time, + device, descriptor_layout: descriptor_layout.clone(), }) } fn create_car_object(&self, offset: Vector2, color: [f32; 4]) -> Result { + write_log!(" =================== create car object ==================="); + RadarObject::new( self.device.clone(), &self.descriptor_layout, @@ -148,59 +160,81 @@ impl RFactorData { ) } - pub fn update(&mut self) -> Result<()> { - // get scoring info - let (scoring_info, vehicle_scorings) = self.scoring_reader.vehicle_scoring(); + fn now(&self) -> f32 { + self.start_time.elapsed().as_secs_f32() + } - // check for player id - if scoring_info.mNumVehicles == 0 { - self.player_id = None; - } else if self.player_id.is_none() { - for vehicle_scoring in vehicle_scorings.iter() { - if vehicle_scoring.mIsPlayer != 0 { - self.player_id = Some(vehicle_scoring.mID); + pub fn update(&mut self) -> Result<()> { + write_log!(" =================== update RFactorData ==================="); + + // get scoring info + if let Some((scoring_info, vehicle_scorings)) = + self.scoring_reader.vehicle_scoring(self.now()) + { + write_log!(format!( + "new scoring info: vehicles: {}", + scoring_info.mNumVehicles + )); + + // check for player id + if scoring_info.mNumVehicles == 0 { + self.player_id = None; + } else if self.player_id.is_none() { + for vehicle_scoring in vehicle_scorings.iter() { + if vehicle_scoring.mIsPlayer != 0 { + write_log!(format!("player found: {}", vehicle_scoring.mID)); + self.player_id = Some(vehicle_scoring.mID); + break; + } } } } // if player id is set (a map is loaded), check telemetry data if let Some(player_id) = &self.player_id { - let telemetries = self.telemetry_reader.query_telemetry(); + write_log!("before telemetry update"); + if let Some(telemetries) = self.telemetry_reader.query_telemetry(self.now()) { + write_log!("new telemetry update"); - let mut player_position = CarPosition::default(); - let mut other_positions = Vec::new(); + let mut player_position = CarPosition::default(); + let mut other_positions = Vec::new(); - for telemetry in telemetries { - if telemetry.id == *player_id { - player_position.position = convert_vec(telemetry.position); - player_position.local_rotation = convert_vec(telemetry.local_rotation); - } else { - other_positions.push(CarPosition { - position: convert_vec(telemetry.position), - local_rotation: convert_vec(telemetry.local_rotation), - }); + for telemetry in telemetries { + if telemetry.id == *player_id { + player_position.position = convert_vec(telemetry.position); + player_position.local_rotation = convert_vec(telemetry.local_rotation); + } else { + other_positions.push(CarPosition { + position: convert_vec(telemetry.position), + local_rotation: convert_vec(telemetry.local_rotation), + }); + } } - } - // update radar objects + // update radar objects - // naive way: clear cars and create them new if near enough - self.cars.clear(); + // naive way: clear cars and create them new if near enough + self.cars.clear(); - for other_position in other_positions { - let diff = player_position.position - other_position.position; - let distance = diff.magnitude(); + for other_position in other_positions { + let diff = player_position.position - other_position.position; + let distance = diff.magnitude(); - // check if car is close enough the players car - if distance < self.config.radar_car_distance { - let offset = diff.truncate(); - let color = self.config.danger_color.lerp( - self.config.safe_color, - distance / self.config.radar_car_distance, - ); + // check if car is close enough the players car + if distance < self.config.radar_car_distance { + let distance_ratio = distance / self.config.radar_car_distance; - self.cars - .push(self.create_car_object(offset, [color.x, color.y, color.z, 0.9])?); + let offset = + diff.truncate() * (self.radar_extent / self.config.radar_car_distance); + let color = self + .config + .danger_color + .lerp(self.config.safe_color, distance_ratio); + + self.cars.push( + self.create_car_object(offset, [color.x, color.y, color.z, 0.9])?, + ); + } } } } @@ -209,6 +243,8 @@ impl RFactorData { } pub fn objects(&self) -> Vec<&dyn RenderObject> { + write_log!(" =================== get objects of RFactorData ==================="); + let mut objects: Vec<&dyn RenderObject> = Vec::new(); // only draw radar when player is loaded into a map From 7ad2f4eab9d78c5cd46273b404747afe00c0de87 Mon Sep 17 00:00:00 2001 From: hodasemi Date: Sun, 15 Jan 2023 10:46:22 +0100 Subject: [PATCH 18/85] Add readme and few attempts at radar fixes --- Cargo.toml | 2 +- README.md | 26 ++++++ rFactorOverlay.json | 2 +- src/overlay/rfactor_data.rs | 153 ++++++++++++++++++++++++++---------- 4 files changed, 138 insertions(+), 45 deletions(-) create mode 100644 README.md diff --git a/Cargo.toml b/Cargo.toml index 0a7c9ad..557798d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,5 +13,5 @@ vulkan-rs = { git = "https://gavania.de/hodasemi/vulkan_lib.git" } rfactor_sm_reader = { git = "https://gavania.de/hodasemi/rfactor_sm_reader.git" } anyhow = { version = "1.0.68", features = ["backtrace"] } -cgmath = "0.18.0" +cgmath = { version = "0.18.0", features = ["swizzle"] } paste = "1.0.11" diff --git a/README.md b/README.md new file mode 100644 index 0000000..0f8f1d2 --- /dev/null +++ b/README.md @@ -0,0 +1,26 @@ +# Vulkan rFactor2 HUD +This project is an attempt to render custom HUD elements for rFactor2 by being a Vulkan layer. + +# Current state +I just started doing it. That means it isn't very usable right now. I'm working on a radar right now as the first element. + +# How to enable +* Build this repository `cargo build --release` +* Change the path where the `libvk_layer_rs.so` is located +* Put the rFactorOverlay.json into a layer directory ([layer directories](https://vulkan.lunarg.com/doc/view/1.3.216.0/mac/loader_and_layer_interface.html#user-content-linux-layer-discovery)) +* Add `RFACTOR_HUD=1` to steam launch command (example: `RFACTOR_HUD=1 %command%`) + +# Resources + +## Vulkan Layer +* [Sample Layer](https://github.com/baldurk/sample_layer) +* [Brief guide to Vulkan layers (Renderdoc)](https://renderdoc.org/vulkan-layer-guide.html) +* [Mangohud](https://github.com/flightlessmango/MangoHud) +* [Lunarg Guide](https://vulkan.lunarg.com/doc/view/1.3.216.0/mac/loader_and_layer_interface.html#user-content-layer-manifest-file-format) + +## rFactor2 Shared Memory +* [rFactor 2 Modding Resources](https://www.studio-397.com/modding-resources/) +* [rF2SharedMemoryMapPlugin](https://github.com/TheIronWolfModding/rF2SharedMemoryMapPlugin) +* [rF2SharedMemoryMapPlugin_Wine](https://github.com/schlegp/rF2SharedMemoryMapPlugin_Wine) +* [CrewChief](https://github.com/mrbelowski/CrewChiefV4) +* [OpenSimHud](https://github.com/schlegp/OpenSimHud) diff --git a/rFactorOverlay.json b/rFactorOverlay.json index 10ea072..f49349b 100644 --- a/rFactorOverlay.json +++ b/rFactorOverlay.json @@ -4,7 +4,7 @@ "name": "VK_LAYER_rFactor2_overlay", "type": "GLOBAL", "api_version": "1.3.0", - "library_path": "/home/michael/Dokumente/Workspace/vk_layer_rs/target/debug/libvk_layer_rs.so", + "library_path": "$HOME/Dokumente/Workspace/vk_layer_rs/target/debug/libvk_layer_rs.so", "implementation_version": "1", "description": "Vulkan Hud Overlay", "functions": { diff --git a/src/overlay/rfactor_data.rs b/src/overlay/rfactor_data.rs index fba4836..d747228 100644 --- a/src/overlay/rfactor_data.rs +++ b/src/overlay/rfactor_data.rs @@ -1,5 +1,5 @@ use anyhow::Result; -use cgmath::{ortho, vec2, vec3, InnerSpace, Matrix4, Vector2, Vector3, VectorSpace}; +use cgmath::{ortho, vec2, vec3, Deg, InnerSpace, Matrix4, Rad, Vector2, Vector3}; use rfactor_sm_reader::*; use vulkan_rs::prelude::*; @@ -37,14 +37,17 @@ pub struct RFactorData { player_car: RadarObject, cars: Vec, + // buffer car objects, to prevent recreating them every update + car_handles: Vec, + // game info player_id: Option, // math objects radar_center: Vector2, ortho: Matrix4, - window_width: u32, - window_height: u32, + _window_width: u32, + _window_height: u32, radar_extent: f32, car_width: f32, car_height: f32, @@ -108,16 +111,17 @@ impl RFactorData { vec2(radar_center.x + car_width, radar_center.y - car_height), ], ), - [0.9, 0.9, 0.0, 0.9], + [0.0, 0.9, 0.0, 0.9], )?, cars: Vec::new(), + car_handles: Vec::new(), player_id: None, radar_center, ortho, - window_width: width, - window_height: height, + _window_width: width, + _window_height: height, radar_extent, car_width, car_height, @@ -135,31 +139,47 @@ impl RFactorData { RadarObject::new( self.device.clone(), &self.descriptor_layout, - PositionOnlyVertex::from_2d_corners( + Self::create_car_vertices( self.ortho, - [ - vec2( - self.radar_center.x - self.car_width + offset.x, - self.radar_center.y - self.car_height + offset.y, - ), - vec2( - self.radar_center.x - self.car_width + offset.x, - self.radar_center.y + self.car_height + offset.y, - ), - vec2( - self.radar_center.x + self.car_width + offset.x, - self.radar_center.y + self.car_height + offset.y, - ), - vec2( - self.radar_center.x + self.car_width + offset.x, - self.radar_center.y - self.car_height + offset.y, - ), - ], + self.radar_center, + self.car_width, + self.car_height, + offset, ), color, ) } + fn create_car_vertices( + mvp: Matrix4, + radar_center: Vector2, + car_width: f32, + car_height: f32, + offset: Vector2, + ) -> [PositionOnlyVertex; 6] { + PositionOnlyVertex::from_2d_corners( + mvp, + [ + vec2( + radar_center.x - car_width + offset.x, + radar_center.y - car_height + offset.y, + ), + vec2( + radar_center.x - car_width + offset.x, + radar_center.y + car_height + offset.y, + ), + vec2( + radar_center.x + car_width + offset.x, + radar_center.y + car_height + offset.y, + ), + vec2( + radar_center.x + car_width + offset.x, + radar_center.y - car_height + offset.y, + ), + ], + ) + } + fn now(&self) -> f32 { self.start_time.elapsed().as_secs_f32() } @@ -196,25 +216,42 @@ impl RFactorData { if let Some(telemetries) = self.telemetry_reader.query_telemetry(self.now()) { write_log!("new telemetry update"); + // make sure there are enough cars in buffer + if self.car_handles.len() < telemetries.len() { + let size_diff = telemetries.len() - self.car_handles.len(); + + for _ in 0..size_diff { + self.car_handles + .push(self.create_car_object(vec2(0.0, 0.0), [0.0, 0.0, 0.0, 0.0])?); + } + } + let mut player_position = CarPosition::default(); let mut other_positions = Vec::new(); for telemetry in telemetries { if telemetry.id == *player_id { player_position.position = convert_vec(telemetry.position); - player_position.local_rotation = convert_vec(telemetry.local_rotation); + player_position.orientation = [ + convert_vec(telemetry.orientation[0]), + convert_vec(telemetry.orientation[1]), + convert_vec(telemetry.orientation[2]), + ]; } else { other_positions.push(CarPosition { position: convert_vec(telemetry.position), - local_rotation: convert_vec(telemetry.local_rotation), + orientation: [ + convert_vec(telemetry.orientation[0]), + convert_vec(telemetry.orientation[1]), + convert_vec(telemetry.orientation[2]), + ], }); } } // update radar objects - - // naive way: clear cars and create them new if near enough self.cars.clear(); + let mut buffer_car_index = 0; for other_position in other_positions { let diff = player_position.position - other_position.position; @@ -222,18 +259,22 @@ impl RFactorData { // check if car is close enough the players car if distance < self.config.radar_car_distance { - let distance_ratio = distance / self.config.radar_car_distance; - let offset = - diff.truncate() * (self.radar_extent / self.config.radar_car_distance); - let color = self - .config - .danger_color - .lerp(self.config.safe_color, distance_ratio); + diff.xz() * (self.radar_extent / self.config.radar_car_distance); - self.cars.push( - self.create_car_object(offset, [color.x, color.y, color.z, 0.9])?, - ); + let buffered_car = self.car_handles[buffer_car_index].clone(); + buffer_car_index += 1; + buffered_car.update( + self.ortho, + offset, + Self::car_orientation(&other_position), + self.radar_center, + self.car_width, + self.car_height, + [0.9, 0.9, 0.0, 0.9], + )?; + + self.cars.push(buffered_car); } } } @@ -248,7 +289,7 @@ impl RFactorData { let mut objects: Vec<&dyn RenderObject> = Vec::new(); // only draw radar when player is loaded into a map - if let Some(player_id) = &self.player_id { + if let Some(_player_id) = &self.player_id { // only draw radar when any car is near enough if !self.cars.is_empty() { objects.push(&self.background); @@ -263,8 +304,17 @@ impl RFactorData { objects } + + fn car_orientation(car: &CarPosition) -> Rad { + const DEGREES_IN_RADIAN: f32 = 57.2957795; + + let xz_val = car.orientation[2].xz(); + + Rad(xz_val.x.atan2(xz_val.y) * DEGREES_IN_RADIAN) + } } +#[derive(Clone)] struct RadarObject { descriptor_set: Arc, @@ -308,7 +358,24 @@ impl RadarObject { }) } - pub fn update_color(&self, color: [f32; 4]) -> Result<()> { + pub fn update( + &self, + ortho: Matrix4, + offset: Vector2, + rotation: impl Into>, + radar_center: Vector2, + car_width: f32, + car_height: f32, + color: [f32; 4], + ) -> Result<()> { + self.position_buffer + .fill(&RFactorData::create_car_vertices( + ortho * Matrix4::from_angle_z(rotation.into()), + radar_center, + car_width, + car_height, + offset, + ))?; self.color_buffer.fill(&color) } } @@ -325,14 +392,14 @@ impl RenderObject for RadarObject { struct CarPosition { pub position: Vector3, - pub local_rotation: Vector3, + pub orientation: [Vector3; 3], } impl Default for CarPosition { fn default() -> Self { Self { position: vec3(0.0, 0.0, 0.0), - local_rotation: vec3(0.0, 0.0, 0.0), + orientation: [vec3(0.0, 0.0, 0.0); 3], } } } From e225531e6c851be3134ea48c1a176ed431f838c0 Mon Sep 17 00:00:00 2001 From: hodasemi Date: Sun, 15 Jan 2023 10:48:52 +0100 Subject: [PATCH 19/85] Add info what and where to change lib path --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 0f8f1d2..532d5df 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ I just started doing it. That means it isn't very usable right now. I'm working # How to enable * Build this repository `cargo build --release` -* Change the path where the `libvk_layer_rs.so` is located +* Change the path where the `libvk_layer_rs.so` is located (`library_path` parameter in the rFactorOverlay.json file) * Put the rFactorOverlay.json into a layer directory ([layer directories](https://vulkan.lunarg.com/doc/view/1.3.216.0/mac/loader_and_layer_interface.html#user-content-linux-layer-discovery)) * Add `RFACTOR_HUD=1` to steam launch command (example: `RFACTOR_HUD=1 %command%`) From e991cb7af1ee4341404955f515325880c52bbb1a Mon Sep 17 00:00:00 2001 From: hodasemi Date: Sun, 15 Jan 2023 19:58:23 +0100 Subject: [PATCH 20/85] Try to work with car orientation --- src/overlay/rfactor_data.rs | 105 +++++++++++++++++------------------- 1 file changed, 49 insertions(+), 56 deletions(-) diff --git a/src/overlay/rfactor_data.rs b/src/overlay/rfactor_data.rs index d747228..1b865e8 100644 --- a/src/overlay/rfactor_data.rs +++ b/src/overlay/rfactor_data.rs @@ -89,12 +89,12 @@ impl RFactorData { device.clone(), descriptor_layout, PositionOnlyVertex::from_2d_corners( - ortho, + ortho * Matrix4::from_translation(radar_center.extend(0.0)), [ - vec2(radar_center.x - radar_extent, radar_center.y - radar_extent), - vec2(radar_center.x - radar_extent, radar_center.y + radar_extent), - vec2(radar_center.x + radar_extent, radar_center.y + radar_extent), - vec2(radar_center.x + radar_extent, radar_center.y - radar_extent), + vec2(-radar_extent, -radar_extent), + vec2(-radar_extent, radar_extent), + vec2(radar_extent, radar_extent), + vec2(radar_extent, -radar_extent), ], ), [0.5, 0.5, 0.5, 0.5], @@ -103,12 +103,12 @@ impl RFactorData { device.clone(), descriptor_layout, PositionOnlyVertex::from_2d_corners( - ortho, + ortho * Matrix4::from_translation(radar_center.extend(0.0)), [ - vec2(radar_center.x - car_width, radar_center.y - car_height), - vec2(radar_center.x - car_width, radar_center.y + car_height), - vec2(radar_center.x + car_width, radar_center.y + car_height), - vec2(radar_center.x + car_width, radar_center.y - car_height), + vec2(-car_width, -car_height), + vec2(-car_width, car_height), + vec2(car_width, car_height), + vec2(car_width, -car_height), ], ), [0.0, 0.9, 0.0, 0.9], @@ -140,11 +140,11 @@ impl RFactorData { self.device.clone(), &self.descriptor_layout, Self::create_car_vertices( - self.ortho, - self.radar_center, + self.ortho + * Matrix4::from_translation(self.radar_center.extend(0.0)) + * Matrix4::from_translation(offset.extend(0.0)), self.car_width, self.car_height, - offset, ), color, ) @@ -152,30 +152,16 @@ impl RFactorData { fn create_car_vertices( mvp: Matrix4, - radar_center: Vector2, car_width: f32, car_height: f32, - offset: Vector2, ) -> [PositionOnlyVertex; 6] { PositionOnlyVertex::from_2d_corners( mvp, [ - vec2( - radar_center.x - car_width + offset.x, - radar_center.y - car_height + offset.y, - ), - vec2( - radar_center.x - car_width + offset.x, - radar_center.y + car_height + offset.y, - ), - vec2( - radar_center.x + car_width + offset.x, - radar_center.y + car_height + offset.y, - ), - vec2( - radar_center.x + car_width + offset.x, - radar_center.y - car_height + offset.y, - ), + vec2(-car_width, -car_height), + vec2(-car_width, car_height), + vec2(car_width, car_height), + vec2(car_width, -car_height), ], ) } @@ -230,22 +216,19 @@ impl RFactorData { let mut other_positions = Vec::new(); for telemetry in telemetries { - if telemetry.id == *player_id { - player_position.position = convert_vec(telemetry.position); - player_position.orientation = [ + let car = CarPosition::new( + convert_vec(telemetry.position), + [ convert_vec(telemetry.orientation[0]), convert_vec(telemetry.orientation[1]), convert_vec(telemetry.orientation[2]), - ]; + ], + ); + + if telemetry.id == *player_id { + player_position = car } else { - other_positions.push(CarPosition { - position: convert_vec(telemetry.position), - orientation: [ - convert_vec(telemetry.orientation[0]), - convert_vec(telemetry.orientation[1]), - convert_vec(telemetry.orientation[2]), - ], - }); + other_positions.push(car); } } @@ -257,7 +240,7 @@ impl RFactorData { let diff = player_position.position - other_position.position; let distance = diff.magnitude(); - // check if car is close enough the players car + // check if car is close enough to the players car if distance < self.config.radar_car_distance { let offset = diff.xz() * (self.radar_extent / self.config.radar_car_distance); @@ -267,7 +250,7 @@ impl RFactorData { buffered_car.update( self.ortho, offset, - Self::car_orientation(&other_position), + other_position.rotation - player_position.rotation, self.radar_center, self.car_width, self.car_height, @@ -304,14 +287,6 @@ impl RFactorData { objects } - - fn car_orientation(car: &CarPosition) -> Rad { - const DEGREES_IN_RADIAN: f32 = 57.2957795; - - let xz_val = car.orientation[2].xz(); - - Rad(xz_val.x.atan2(xz_val.y) * DEGREES_IN_RADIAN) - } } #[derive(Clone)] @@ -370,11 +345,12 @@ impl RadarObject { ) -> Result<()> { self.position_buffer .fill(&RFactorData::create_car_vertices( - ortho * Matrix4::from_angle_z(rotation.into()), - radar_center, + ortho + * Matrix4::from_translation(radar_center.extend(0.0)) + * Matrix4::from_translation(offset.extend(0.0)) + * Matrix4::from_angle_z(rotation.into()), car_width, car_height, - offset, ))?; self.color_buffer.fill(&color) } @@ -393,6 +369,22 @@ impl RenderObject for RadarObject { struct CarPosition { pub position: Vector3, pub orientation: [Vector3; 3], + pub rotation: Rad, +} + +impl CarPosition { + fn new(position: Vector3, orientation: [Vector3; 3]) -> Self { + Self { + position, + rotation: { + const DEGREES_IN_RADIAN: f32 = 57.2957795; + let xz_val = orientation[2].xz(); + + Rad(xz_val.x.atan2(xz_val.y) * DEGREES_IN_RADIAN) + }, + orientation, + } + } } impl Default for CarPosition { @@ -400,6 +392,7 @@ impl Default for CarPosition { Self { position: vec3(0.0, 0.0, 0.0), orientation: [vec3(0.0, 0.0, 0.0); 3], + rotation: Rad(0.0), } } } From 42eb47836f60ee825ffdf0ba544a8dda1ed63915 Mon Sep 17 00:00:00 2001 From: hodasemi Date: Mon, 16 Jan 2023 03:56:10 +0100 Subject: [PATCH 21/85] Get transforms into the right order --- README.md | 2 +- src/overlay/rfactor_data.rs | 16 ++++++---------- 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 532d5df..39d20e9 100644 --- a/README.md +++ b/README.md @@ -22,5 +22,5 @@ I just started doing it. That means it isn't very usable right now. I'm working * [rFactor 2 Modding Resources](https://www.studio-397.com/modding-resources/) * [rF2SharedMemoryMapPlugin](https://github.com/TheIronWolfModding/rF2SharedMemoryMapPlugin) * [rF2SharedMemoryMapPlugin_Wine](https://github.com/schlegp/rF2SharedMemoryMapPlugin_Wine) -* [CrewChief](https://github.com/mrbelowski/CrewChiefV4) +* [CrewChief](https://gitlab.com/mr_belowski/CrewChiefV4/-/tree/master/CrewChiefV4) * [OpenSimHud](https://github.com/schlegp/OpenSimHud) diff --git a/src/overlay/rfactor_data.rs b/src/overlay/rfactor_data.rs index 1b865e8..59a69f4 100644 --- a/src/overlay/rfactor_data.rs +++ b/src/overlay/rfactor_data.rs @@ -250,7 +250,8 @@ impl RFactorData { buffered_car.update( self.ortho, offset, - other_position.rotation - player_position.rotation, + player_position.rotation, + other_position.rotation, self.radar_center, self.car_width, self.car_height, @@ -335,8 +336,10 @@ impl RadarObject { pub fn update( &self, + ortho: Matrix4, offset: Vector2, + player_rotation: impl Into>, rotation: impl Into>, radar_center: Vector2, car_width: f32, @@ -347,6 +350,7 @@ impl RadarObject { .fill(&RFactorData::create_car_vertices( ortho * Matrix4::from_translation(radar_center.extend(0.0)) + * Matrix4::from_angle_z(-player_rotation.into()) * Matrix4::from_translation(offset.extend(0.0)) * Matrix4::from_angle_z(rotation.into()), car_width, @@ -368,7 +372,6 @@ impl RenderObject for RadarObject { struct CarPosition { pub position: Vector3, - pub orientation: [Vector3; 3], pub rotation: Rad, } @@ -376,13 +379,7 @@ impl CarPosition { fn new(position: Vector3, orientation: [Vector3; 3]) -> Self { Self { position, - rotation: { - const DEGREES_IN_RADIAN: f32 = 57.2957795; - let xz_val = orientation[2].xz(); - - Rad(xz_val.x.atan2(xz_val.y) * DEGREES_IN_RADIAN) - }, - orientation, + rotation: Rad(orientation[2].x.atan2(orientation[2].y)), } } } @@ -391,7 +388,6 @@ impl Default for CarPosition { fn default() -> Self { Self { position: vec3(0.0, 0.0, 0.0), - orientation: [vec3(0.0, 0.0, 0.0); 3], rotation: Rad(0.0), } } From 0e446641c5de26050ca7191f2f3c5b86a3bfdb9f Mon Sep 17 00:00:00 2001 From: hodasemi Date: Mon, 16 Jan 2023 07:32:32 +0100 Subject: [PATCH 22/85] Add hackery y-flip to correct car direction --- src/overlay/rfactor_data.rs | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/src/overlay/rfactor_data.rs b/src/overlay/rfactor_data.rs index 59a69f4..16c61ee 100644 --- a/src/overlay/rfactor_data.rs +++ b/src/overlay/rfactor_data.rs @@ -1,5 +1,5 @@ use anyhow::Result; -use cgmath::{ortho, vec2, vec3, Deg, InnerSpace, Matrix4, Rad, Vector2, Vector3}; +use cgmath::{ortho, vec2, vec3, vec4, Deg, InnerSpace, Matrix4, Rad, Vector2, Vector3}; use rfactor_sm_reader::*; use vulkan_rs::prelude::*; @@ -69,14 +69,15 @@ impl RFactorData { write_log!(" =================== create RFactorData ==================="); let radar_extent = width as f32 * 0.075 * config.radar_scale; - let car_height = radar_extent * 0.2; - let car_width = car_height * 0.5; + let car_height = radar_extent * 0.15; + let car_width = car_height / 2.5; let radar_center = vec2( width as f32 / 2.0, - height as f32 / 2.0 + height as f32 * 0.25, + height as f32 / 2.0 - height as f32 * 0.25, ); - let ortho = ortho(0.0, width as f32, 0.0, height as f32, -1.0, 1.0); + let flip_y = matrix4_from_diagonal(vec3(1.0, -1.0, 1.0)); + let ortho = flip_y * ortho(0.0, width as f32, 0.0, height as f32, -1.0, 1.0); let start_time = Instant::now(); Ok(Self { @@ -379,7 +380,7 @@ impl CarPosition { fn new(position: Vector3, orientation: [Vector3; 3]) -> Self { Self { position, - rotation: Rad(orientation[2].x.atan2(orientation[2].y)), + rotation: Rad(orientation[2].x.atan2(orientation[2].z)), } } } @@ -392,3 +393,12 @@ impl Default for CarPosition { } } } + +const fn matrix4_from_diagonal(diagonal: Vector3) -> Matrix4 { + Matrix4::from_cols( + vec4(diagonal.x, 0.0, 0.0, 0.0), + vec4(0.0, diagonal.y, 0.0, 0.0), + vec4(0.0, 0.0, diagonal.z, 0.0), + vec4(0.0, 0.0, 0.0, 1.0), + ) +} From f70cd3f1c837b8baf3976e7a77ca364a1d5503a4 Mon Sep 17 00:00:00 2001 From: hodasemi Date: Mon, 16 Jan 2023 14:52:58 +0100 Subject: [PATCH 23/85] Add pkgbuild --- pkgbuild/PKGBUILD | 29 +++++++++++++++++++++++++++++ pkgbuild/README.md | 3 +++ rFactorOverlay.json | 2 +- 3 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 pkgbuild/PKGBUILD create mode 100644 pkgbuild/README.md diff --git a/pkgbuild/PKGBUILD b/pkgbuild/PKGBUILD new file mode 100644 index 0000000..890cef1 --- /dev/null +++ b/pkgbuild/PKGBUILD @@ -0,0 +1,29 @@ +# Maintainer: hodasemi +_pkgbase=rFactor2_vk_hud +pkgname="${_pkgbase}" +pkgver=0.1 +pkgrel=1 +pkgdesc="Vulkan overlay layer for rFactor 2" +arch=('x86_64') +url="https://gavania.de/hodasemi/${_pkgbase}" +license=('GPL-3.0') +makedepends=('rust' 'ctags' 'glslang') +depends=() +conflicts=("${_pkgbase}") +source=("git+${url}") +md5sums=(SKIP) + +build() { + cd ${_pkgbase} + + # build binaries + cargo build --release +} + +package() { + # copy lib + install -Dm755 ${_pkgbase}/target/release/libvk_layer_rs.so "${pkgdir}"/usr/lib/libvk_layer_rs.so + + # copy layer discovery info file + install -Dm644 ${_pkgbase}/rFactorOverlay.json "${pkgdir}"/usr/share/vulkan/implicit_layer.d/rFactorOverlay.json +} diff --git a/pkgbuild/README.md b/pkgbuild/README.md new file mode 100644 index 0000000..0194c15 --- /dev/null +++ b/pkgbuild/README.md @@ -0,0 +1,3 @@ +# Build package for arch-based systems +* simply build it: `makepkg` +* also install it directly afterwards: `makepkg -i` \ No newline at end of file diff --git a/rFactorOverlay.json b/rFactorOverlay.json index f49349b..aa6d62e 100644 --- a/rFactorOverlay.json +++ b/rFactorOverlay.json @@ -4,7 +4,7 @@ "name": "VK_LAYER_rFactor2_overlay", "type": "GLOBAL", "api_version": "1.3.0", - "library_path": "$HOME/Dokumente/Workspace/vk_layer_rs/target/debug/libvk_layer_rs.so", + "library_path": "/usr/lib/libvk_layer_rs.so", "implementation_version": "1", "description": "Vulkan Hud Overlay", "functions": { From 4ac8a3ce74bf320fe205fa02aae0d17f54102824 Mon Sep 17 00:00:00 2001 From: hodasemi Date: Mon, 16 Jan 2023 17:07:39 +0100 Subject: [PATCH 24/85] Add config file --- Cargo.toml | 4 ++- src/lib.rs | 52 ++++++++++++++++++++++++++++++++++--- src/overlay/mod.rs | 32 +++++++++++++++++------ src/overlay/rfactor_data.rs | 30 ++++++++++++++++++--- 4 files changed, 102 insertions(+), 16 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 557798d..9e877bf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,5 +13,7 @@ vulkan-rs = { git = "https://gavania.de/hodasemi/vulkan_lib.git" } rfactor_sm_reader = { git = "https://gavania.de/hodasemi/rfactor_sm_reader.git" } anyhow = { version = "1.0.68", features = ["backtrace"] } -cgmath = { version = "0.18.0", features = ["swizzle"] } +cgmath = { version = "0.18.0", features = ["swizzle", "serde"] } paste = "1.0.11" +serde = "1.0.152" +serde_json = "1.0.91" diff --git a/src/lib.rs b/src/lib.rs index 2700f58..dda6b8e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,15 +6,16 @@ mod vk_handles; use std::{ ffi::c_void, - fs::{File, OpenOptions}, + fs::{self, File, OpenOptions}, io::Write, mem, os::raw::c_char, + path::Path, ptr, }; use enums::*; -use overlay::Overlay; +use overlay::{Overlay, OverlayConfig}; use structs::*; use vk_handles::*; use vulkan_rs::prelude::*; @@ -55,8 +56,9 @@ pub(crate) extern "C" fn vkNegotiateLoaderLayerInterfaceVersion( unsafe { LOG_ENABLED = log_enabled }; + let home = std::env::var("HOME").unwrap(); + if logging() { - let home = std::env::var("HOME").unwrap(); unsafe { LOG_FILE = format!("{}/rf2_vk_hud.log", home); } @@ -68,6 +70,50 @@ pub(crate) extern "C" fn vkNegotiateLoaderLayerInterfaceVersion( write_log!(" =================================================================="); } + let config_path = Path::new(&home).join(".config/rFactorHUD/config.json"); + + let config = if config_path.exists() { + fs::read_to_string(&config_path) + .map(|s| { + serde_json::from_str(&s).unwrap_or({ + write_log!("failed to deserialize config file"); + OverlayConfig::new() + }) + }) + .unwrap_or({ + write_log!(format!("failed to open config file: {:?}", config_path)); + OverlayConfig::new() + }) + } else { + if let Err(err) = std::fs::create_dir_all(config_path.parent().unwrap()) { + write_log!(format!("failed to create dirs for config file: {:?}", err)); + } + + let config = OverlayConfig::new(); + + match File::create(config_path) { + Ok(mut file) => match serde_json::to_string(&config) { + Ok(conf_str) => { + if let Err(err) = file.write_all(conf_str.as_bytes()) { + write_log!(format!("failed to write to config file: {:?}", err)); + } + } + Err(err) => { + write_log!(format!("failed to serialize config: {:?}", err)); + } + }, + Err(err) => { + write_log!(format!("failed to create config file: {:?}", err)); + } + } + + config + }; + + unsafe { + OVERLAY.set_config(config); + } + unsafe { *pVersionStruct = VkNegotiateLayerInterface { sType: enums::VkNegotiateLayerStructType::LAYER_NEGOTIATE_INTERFACE_STRUCT, diff --git a/src/overlay/mod.rs b/src/overlay/mod.rs index 4a74310..ab05a89 100644 --- a/src/overlay/mod.rs +++ b/src/overlay/mod.rs @@ -10,11 +10,27 @@ mod rendering; mod rfactor_data; use anyhow::Result; -use cgmath::vec3; use std::sync::{Arc, Mutex}; use vulkan_rs::prelude::*; +use serde::{Deserialize, Serialize}; + +#[derive(Deserialize, Serialize)] +pub struct OverlayConfig { + pub data_config: DataConfig, +} + +impl OverlayConfig { + pub const fn new() -> Self { + Self { + data_config: DataConfig::new(), + } + } +} + pub struct Overlay { + config: OverlayConfig, + instance: Option>, device: Option>, queue: Option>>, @@ -26,6 +42,8 @@ pub struct Overlay { impl Overlay { pub const fn new() -> Self { Self { + config: OverlayConfig::new(), + instance: None, device: None, queue: None, @@ -35,6 +53,10 @@ impl Overlay { } } + pub fn set_config(&mut self, config: OverlayConfig) { + self.config = config; + } + pub fn set_instance(&mut self, instance: Arc) { self.instance = Some(instance); } @@ -79,7 +101,6 @@ impl Overlay { self.rendering = Some(Rendering::new(self.device(), self.queue(), swapchain)?); write_log!("-> create rendering: new created"); - write_log!("-> create rendering: end"); Ok(()) @@ -90,12 +111,7 @@ impl Overlay { if self.rfactor_data.is_none() { self.rfactor_data = RFactorData::new( - DataConfig { - radar_scale: 1.0, - radar_car_distance: 20.0, - safe_color: vec3(0.0, 0.75, 0.0), - danger_color: vec3(0.75, 0.0, 0.0), - }, + self.config.data_config, self.device(), self.rendering .as_mut() diff --git a/src/overlay/rfactor_data.rs b/src/overlay/rfactor_data.rs index 16c61ee..4f0891f 100644 --- a/src/overlay/rfactor_data.rs +++ b/src/overlay/rfactor_data.rs @@ -3,6 +3,8 @@ use cgmath::{ortho, vec2, vec3, vec4, Deg, InnerSpace, Matrix4, Rad, Vector2, Ve use rfactor_sm_reader::*; use vulkan_rs::prelude::*; +use serde::{Deserialize, Serialize}; + use std::{sync::Arc, time::Instant}; use super::rendering::PositionOnlyVertex; @@ -17,13 +19,33 @@ pub trait RenderObject { fn buffer(&self) -> &Arc>; } +#[derive(Deserialize, Serialize, Clone, Copy, Debug)] pub struct DataConfig { pub radar_scale: f32, + pub radar_center_factor: f32, + pub radar_transparency: f32, + pub height_scale: f32, + pub width_scale: f32, pub radar_car_distance: f32, pub safe_color: Vector3, pub danger_color: Vector3, } +impl DataConfig { + pub const fn new() -> Self { + Self { + radar_scale: 1.0, + radar_center_factor: 0.25, + radar_transparency: 0.5, + height_scale: 0.15, + width_scale: 0.4, + radar_car_distance: 20.0, + safe_color: vec3(0.0, 0.75, 0.0), + danger_color: vec3(0.75, 0.0, 0.0), + } + } +} + pub struct RFactorData { // config config: DataConfig, @@ -69,11 +91,11 @@ impl RFactorData { write_log!(" =================== create RFactorData ==================="); let radar_extent = width as f32 * 0.075 * config.radar_scale; - let car_height = radar_extent * 0.15; - let car_width = car_height / 2.5; + let car_height = radar_extent * config.height_scale; + let car_width = car_height * config.width_scale; let radar_center = vec2( width as f32 / 2.0, - height as f32 / 2.0 - height as f32 * 0.25, + height as f32 / 2.0 - height as f32 * config.radar_center_factor, ); let flip_y = matrix4_from_diagonal(vec3(1.0, -1.0, 1.0)); @@ -98,7 +120,7 @@ impl RFactorData { vec2(radar_extent, -radar_extent), ], ), - [0.5, 0.5, 0.5, 0.5], + [0.5, 0.5, 0.5, config.radar_transparency], )?, player_car: RadarObject::new( device.clone(), From badee726767ac31667c7f1f0601622e0857faf5e Mon Sep 17 00:00:00 2001 From: hodasemi Date: Mon, 16 Jan 2023 17:08:57 +0100 Subject: [PATCH 25/85] Add config file info to readme --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 39d20e9..7dced95 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,8 @@ I just started doing it. That means it isn't very usable right now. I'm working * Put the rFactorOverlay.json into a layer directory ([layer directories](https://vulkan.lunarg.com/doc/view/1.3.216.0/mac/loader_and_layer_interface.html#user-content-linux-layer-discovery)) * Add `RFACTOR_HUD=1` to steam launch command (example: `RFACTOR_HUD=1 %command%`) +A config file is generated the first time you start it at: `$HOME/.config/rFactorHUD/config.json` + # Resources ## Vulkan Layer From 0a89dd618bcfb977da325d65f82a02544d2bfbed Mon Sep 17 00:00:00 2001 From: hodasemi Date: Mon, 16 Jan 2023 17:48:12 +0100 Subject: [PATCH 26/85] Use release build from now on --- .vscode/tasks.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 07abeed..9e98ff5 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -8,7 +8,7 @@ "$rustc" ], "group": "build", - "label": "rust: cargo build" + "label": "rust: cargo build --release" }, { "type": "shell", From 20ab9443e23d23220b9c77734bec38550b41a8b7 Mon Sep 17 00:00:00 2001 From: hodasemi Date: Mon, 16 Jan 2023 22:29:23 +0100 Subject: [PATCH 27/85] Attempt at not rendering radar --- src/lib.rs | 95 ++++++++++++---------- src/overlay/rfactor_data.rs | 156 ++++++++++++++++++++---------------- 2 files changed, 142 insertions(+), 109 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index dda6b8e..479f30b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -40,6 +40,48 @@ macro_rules! write_log { pub(crate) use write_log; +fn get_config(home: &str) -> OverlayConfig { + let config_path = Path::new(&home).join(".config/rFactorHUD/config.json"); + + if config_path.exists() { + fs::read_to_string(&config_path) + .map(|s| { + serde_json::from_str(&s).unwrap_or({ + write_log!("failed to deserialize config file"); + OverlayConfig::new() + }) + }) + .unwrap_or({ + write_log!(format!("failed to open config file: {:?}", config_path)); + OverlayConfig::new() + }) + } else { + if let Err(err) = std::fs::create_dir_all(config_path.parent().unwrap()) { + write_log!(format!("failed to create dirs for config file: {:?}", err)); + } + + let config = OverlayConfig::new(); + + match File::create(config_path) { + Ok(mut file) => match serde_json::to_string_pretty(&config) { + Ok(conf_str) => { + if let Err(err) = file.write_all(conf_str.as_bytes()) { + write_log!(format!("failed to write to config file: {:?}", err)); + } + } + Err(err) => { + write_log!(format!("failed to serialize config: {:?}", err)); + } + }, + Err(err) => { + write_log!(format!("failed to create config file: {:?}", err)); + } + } + + config + } +} + #[no_mangle] #[allow(non_snake_case)] pub(crate) extern "C" fn vkNegotiateLoaderLayerInterfaceVersion( @@ -70,48 +112,8 @@ pub(crate) extern "C" fn vkNegotiateLoaderLayerInterfaceVersion( write_log!(" =================================================================="); } - let config_path = Path::new(&home).join(".config/rFactorHUD/config.json"); - - let config = if config_path.exists() { - fs::read_to_string(&config_path) - .map(|s| { - serde_json::from_str(&s).unwrap_or({ - write_log!("failed to deserialize config file"); - OverlayConfig::new() - }) - }) - .unwrap_or({ - write_log!(format!("failed to open config file: {:?}", config_path)); - OverlayConfig::new() - }) - } else { - if let Err(err) = std::fs::create_dir_all(config_path.parent().unwrap()) { - write_log!(format!("failed to create dirs for config file: {:?}", err)); - } - - let config = OverlayConfig::new(); - - match File::create(config_path) { - Ok(mut file) => match serde_json::to_string(&config) { - Ok(conf_str) => { - if let Err(err) = file.write_all(conf_str.as_bytes()) { - write_log!(format!("failed to write to config file: {:?}", err)); - } - } - Err(err) => { - write_log!(format!("failed to serialize config: {:?}", err)); - } - }, - Err(err) => { - write_log!(format!("failed to create config file: {:?}", err)); - } - } - - config - }; - unsafe { - OVERLAY.set_config(config); + OVERLAY.set_config(get_config(&home)); } unsafe { @@ -548,3 +550,14 @@ pub fn log(msg: impl ToString) { if let Err(_) = file.write_all(format!("{}\n", msg.to_string()).as_bytes()) {} } } + +#[cfg(test)] +mod test { + use crate::get_config; + + #[test] + fn config() { + let home = std::env::var("HOME").unwrap(); + get_config(&home); + } +} diff --git a/src/overlay/rfactor_data.rs b/src/overlay/rfactor_data.rs index 4f0891f..919fd49 100644 --- a/src/overlay/rfactor_data.rs +++ b/src/overlay/rfactor_data.rs @@ -55,7 +55,7 @@ pub struct RFactorData { scoring_reader: ScoringReader, // radar objects - background: RadarObject, + background: Option, player_car: RadarObject, cars: Vec, @@ -108,20 +108,24 @@ impl RFactorData { telemetry_reader: TelemetryReader::new(start_time.elapsed().as_secs_f32())?, scoring_reader: ScoringReader::new(start_time.elapsed().as_secs_f32())?, - background: RadarObject::new( - device.clone(), - descriptor_layout, - PositionOnlyVertex::from_2d_corners( - ortho * Matrix4::from_translation(radar_center.extend(0.0)), - [ - vec2(-radar_extent, -radar_extent), - vec2(-radar_extent, radar_extent), - vec2(radar_extent, radar_extent), - vec2(radar_extent, -radar_extent), - ], - ), - [0.5, 0.5, 0.5, config.radar_transparency], - )?, + background: if config.radar_transparency == 0.0 { + None + } else { + Some(RadarObject::new( + device.clone(), + descriptor_layout, + PositionOnlyVertex::from_2d_corners( + ortho * Matrix4::from_translation(radar_center.extend(0.0)), + [ + vec2(-radar_extent, -radar_extent), + vec2(-radar_extent, radar_extent), + vec2(radar_extent, radar_extent), + vec2(radar_extent, -radar_extent), + ], + ), + [0.5, 0.5, 0.5, config.radar_transparency], + )?) + }, player_car: RadarObject::new( device.clone(), descriptor_layout, @@ -196,6 +200,8 @@ impl RFactorData { pub fn update(&mut self) -> Result<()> { write_log!(" =================== update RFactorData ==================="); + let mut should_render = false; + // get scoring info if let Some((scoring_info, vehicle_scorings)) = self.scoring_reader.vehicle_scoring(self.now()) @@ -217,6 +223,14 @@ impl RFactorData { } } } + + if let Some(id) = &self.player_id { + if let Some(vehicle_scoring) = + vehicle_scorings.iter().find(|scoring| scoring.mID == *id) + { + should_render = vehicle_scoring.mInPits != 0; + } + } } // if player id is set (a map is loaded), check telemetry data @@ -225,63 +239,67 @@ impl RFactorData { if let Some(telemetries) = self.telemetry_reader.query_telemetry(self.now()) { write_log!("new telemetry update"); - // make sure there are enough cars in buffer - if self.car_handles.len() < telemetries.len() { - let size_diff = telemetries.len() - self.car_handles.len(); - - for _ in 0..size_diff { - self.car_handles - .push(self.create_car_object(vec2(0.0, 0.0), [0.0, 0.0, 0.0, 0.0])?); - } - } - - let mut player_position = CarPosition::default(); - let mut other_positions = Vec::new(); - - for telemetry in telemetries { - let car = CarPosition::new( - convert_vec(telemetry.position), - [ - convert_vec(telemetry.orientation[0]), - convert_vec(telemetry.orientation[1]), - convert_vec(telemetry.orientation[2]), - ], - ); - - if telemetry.id == *player_id { - player_position = car - } else { - other_positions.push(car); - } - } - - // update radar objects self.cars.clear(); - let mut buffer_car_index = 0; - for other_position in other_positions { - let diff = player_position.position - other_position.position; - let distance = diff.magnitude(); + if should_render { + // make sure there are enough cars in buffer + if self.car_handles.len() < telemetries.len() { + let size_diff = telemetries.len() - self.car_handles.len(); - // check if car is close enough to the players car - if distance < self.config.radar_car_distance { - let offset = - diff.xz() * (self.radar_extent / self.config.radar_car_distance); + for _ in 0..size_diff { + self.car_handles.push( + self.create_car_object(vec2(0.0, 0.0), [0.0, 0.0, 0.0, 0.0])?, + ); + } + } - let buffered_car = self.car_handles[buffer_car_index].clone(); - buffer_car_index += 1; - buffered_car.update( - self.ortho, - offset, - player_position.rotation, - other_position.rotation, - self.radar_center, - self.car_width, - self.car_height, - [0.9, 0.9, 0.0, 0.9], - )?; + let mut player_position = CarPosition::default(); + let mut other_positions = Vec::new(); - self.cars.push(buffered_car); + for telemetry in telemetries { + let car = CarPosition::new( + convert_vec(telemetry.position), + [ + convert_vec(telemetry.orientation[0]), + convert_vec(telemetry.orientation[1]), + convert_vec(telemetry.orientation[2]), + ], + ); + + if telemetry.id == *player_id { + player_position = car + } else { + other_positions.push(car); + } + } + + // update radar objects + let mut buffer_car_index = 0; + + for other_position in other_positions { + let diff = player_position.position - other_position.position; + let distance = diff.magnitude(); + + // check if car is close enough to the players car + if distance < self.config.radar_car_distance { + let offset = + diff.xz() * (self.radar_extent / self.config.radar_car_distance); + + let buffered_car = self.car_handles[buffer_car_index].clone(); + buffer_car_index += 1; + buffered_car.update( + self.ortho, + offset, + player_position.rotation, + other_position.rotation, + self.radar_center, + self.car_width, + self.car_height, + [0.9, 0.9, 0.0, 0.9], + )?; + + self.cars.push(buffered_car); + } } } } @@ -299,7 +317,9 @@ impl RFactorData { if let Some(_player_id) = &self.player_id { // only draw radar when any car is near enough if !self.cars.is_empty() { - objects.push(&self.background); + if let Some(background) = &self.background { + objects.push(background); + } for other_player_cars in &self.cars { objects.push(other_player_cars); From ed5f7e24a72217012e3548ce4c1270f2af8abe98 Mon Sep 17 00:00:00 2001 From: hodasemi Date: Tue, 17 Jan 2023 07:57:56 +0100 Subject: [PATCH 28/85] Create gui handler --- Cargo.toml | 2 + src/overlay/elements/mod.rs | 0 src/overlay/elements/pedals.rs | 0 src/overlay/elements/radar.rs | 0 src/overlay/mod.rs | 93 ++++++++++++++++++++++++++++++++-- src/overlay/rendering.rs | 6 +++ src/overlay/rfactor_data.rs | 8 +-- 7 files changed, 100 insertions(+), 9 deletions(-) create mode 100644 src/overlay/elements/mod.rs create mode 100644 src/overlay/elements/pedals.rs create mode 100644 src/overlay/elements/radar.rs diff --git a/Cargo.toml b/Cargo.toml index 9e877bf..0891f89 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,7 +10,9 @@ crate-type = ["cdylib"] [dependencies] vulkan-rs = { git = "https://gavania.de/hodasemi/vulkan_lib.git" } +assetpath = { git = "https://gavania.de/hodasemi/vulkan_lib.git" } rfactor_sm_reader = { git = "https://gavania.de/hodasemi/rfactor_sm_reader.git" } +ui = { git = "https://gavania.de/hodasemi/ui.git" } anyhow = { version = "1.0.68", features = ["backtrace"] } cgmath = { version = "0.18.0", features = ["swizzle", "serde"] } diff --git a/src/overlay/elements/mod.rs b/src/overlay/elements/mod.rs new file mode 100644 index 0000000..e69de29 diff --git a/src/overlay/elements/pedals.rs b/src/overlay/elements/pedals.rs new file mode 100644 index 0000000..e69de29 diff --git a/src/overlay/elements/radar.rs b/src/overlay/elements/radar.rs new file mode 100644 index 0000000..e69de29 diff --git a/src/overlay/mod.rs b/src/overlay/mod.rs index ab05a89..48460ba 100644 --- a/src/overlay/mod.rs +++ b/src/overlay/mod.rs @@ -2,7 +2,7 @@ use crate::write_log; use self::{ rendering::Rendering, - rfactor_data::{DataConfig, RFactorData}, + rfactor_data::{RFactorData, RadarConfig}, }; mod pipeline; @@ -10,20 +10,24 @@ mod rendering; mod rfactor_data; use anyhow::Result; +use assetpath::AssetPath; use std::sync::{Arc, Mutex}; +use ui::prelude::*; use vulkan_rs::prelude::*; use serde::{Deserialize, Serialize}; #[derive(Deserialize, Serialize)] pub struct OverlayConfig { - pub data_config: DataConfig, + pub radar_config: RadarConfig, + pub font_path: String, } impl OverlayConfig { pub const fn new() -> Self { Self { - data_config: DataConfig::new(), + radar_config: RadarConfig::new(), + font_path: String::new(), } } } @@ -35,6 +39,7 @@ pub struct Overlay { device: Option>, queue: Option>>, rendering: Option, + gui_handler: Option>, rfactor_data: Option, } @@ -48,6 +53,7 @@ impl Overlay { device: None, queue: None, rendering: None, + gui_handler: None, rfactor_data: None, } @@ -98,9 +104,29 @@ impl Overlay { write_log!("-> create rendering: old cleared"); - self.rendering = Some(Rendering::new(self.device(), self.queue(), swapchain)?); + let rendering = Rendering::new(self.device(), self.queue(), swapchain.clone())?; + + // only font is used + let mut create_info = GuiHandlerCreateInfo::default(); + create_info.font_path = AssetPath::from(self.config.font_path.clone()); + create_info.font_path.assume_prefix_free(); + + let ctx = Arc::new(ContextImpl::new( + self.device(), + self.queue(), + swapchain, + rendering.images().clone(), + )); + + self.gui_handler = Some(GuiHandler::new( + create_info, + &(ctx as Arc), + )?); + + self.rendering = Some(rendering); write_log!("-> create rendering: new created"); + write_log!("-> create rendering: end"); Ok(()) @@ -111,7 +137,7 @@ impl Overlay { if self.rfactor_data.is_none() { self.rfactor_data = RFactorData::new( - self.config.data_config, + self.config.radar_config, self.device(), self.rendering .as_mut() @@ -139,3 +165,60 @@ impl Overlay { self.rendering.as_mut().unwrap().render(swapchain, &objects) } } + +struct ContextImpl { + device: Arc, + queue: Arc>, + swapchain: Arc, + images: Vec>, +} + +impl ContextImpl { + fn new( + device: Arc, + queue: Arc>, + swapchain: Arc, + images: Vec>, + ) -> Self { + Self { + device, + queue, + swapchain, + images, + } + } +} + +impl ContextInterface for ContextImpl { + fn device(&self) -> &Arc { + &self.device + } + + fn queue(&self) -> &Arc> { + &self.queue + } + + fn format(&self) -> VkFormat { + self.swapchain.format() + } + + fn image_layout(&self) -> VkImageLayout { + VK_IMAGE_LAYOUT_PRESENT_SRC_KHR + } + + fn image_count(&self) -> usize { + self.images.len() + } + + fn images(&self) -> TargetMode>> { + TargetMode::Mono(self.images.clone()) + } + + fn width(&self) -> u32 { + self.swapchain.width() + } + + fn height(&self) -> u32 { + self.swapchain.height() + } +} diff --git a/src/overlay/rendering.rs b/src/overlay/rendering.rs index d36ba66..1637652 100644 --- a/src/overlay/rendering.rs +++ b/src/overlay/rendering.rs @@ -51,6 +51,7 @@ pub struct Rendering { pipeline: SingleColorPipeline, render_target: RenderTarget, command_buffer: Arc, + images: Vec>, queue: Arc>, } @@ -97,6 +98,7 @@ impl Rendering { pipeline: SingleColorPipeline::new(device.clone(), render_target.render_pass())?, render_target, command_buffer: CommandBuffer::new_primary().build(device.clone(), queue.clone())?, + images, queue, }) @@ -165,4 +167,8 @@ impl Rendering { Ok(()) } + + pub fn images(&self) -> &Vec> { + &self.images + } } diff --git a/src/overlay/rfactor_data.rs b/src/overlay/rfactor_data.rs index 919fd49..05835e7 100644 --- a/src/overlay/rfactor_data.rs +++ b/src/overlay/rfactor_data.rs @@ -20,7 +20,7 @@ pub trait RenderObject { } #[derive(Deserialize, Serialize, Clone, Copy, Debug)] -pub struct DataConfig { +pub struct RadarConfig { pub radar_scale: f32, pub radar_center_factor: f32, pub radar_transparency: f32, @@ -31,7 +31,7 @@ pub struct DataConfig { pub danger_color: Vector3, } -impl DataConfig { +impl RadarConfig { pub const fn new() -> Self { Self { radar_scale: 1.0, @@ -48,7 +48,7 @@ impl DataConfig { pub struct RFactorData { // config - config: DataConfig, + config: RadarConfig, // rf2 memory mapped data telemetry_reader: TelemetryReader, @@ -82,7 +82,7 @@ pub struct RFactorData { impl RFactorData { pub fn new( - config: DataConfig, + config: RadarConfig, device: Arc, descriptor_layout: &Arc, width: u32, From caa87fbc18d06411a3933fa95e63175887cf5d3e Mon Sep 17 00:00:00 2001 From: hodasemi Date: Tue, 17 Jan 2023 12:18:53 +0100 Subject: [PATCH 29/85] Refactor project for easier extension --- build.rs | 4 +- src/overlay/elements/mod.rs | 6 + src/overlay/elements/pedals.rs | 66 +++ src/overlay/{ => elements}/pipeline.rs | 28 +- src/overlay/elements/radar.rs | 495 ++++++++++++++++++ .../{ => elements}/shader/single_color.frag | 0 .../{ => elements}/shader/single_color.vert | 0 src/overlay/elements/ui_files/gui.xsd | 235 +++++++++ src/overlay/elements/ui_files/pedals.xml | 11 + src/overlay/mod.rs | 110 ++-- src/overlay/rendering.rs | 124 +---- src/overlay/rfactor_data.rs | 398 +------------- 12 files changed, 963 insertions(+), 514 deletions(-) rename src/overlay/{ => elements}/pipeline.rs (78%) rename src/overlay/{ => elements}/shader/single_color.frag (100%) rename src/overlay/{ => elements}/shader/single_color.vert (100%) create mode 100644 src/overlay/elements/ui_files/gui.xsd create mode 100644 src/overlay/elements/ui_files/pedals.xml diff --git a/build.rs b/build.rs index d0fb369..d5ffc56 100644 --- a/build.rs +++ b/build.rs @@ -10,8 +10,8 @@ const VK_HEADER: &[&str] = &[ const FN_PREFIX: &str = "PFN_"; const SHADER: &[&str] = &[ - "src/overlay/shader/single_color.vert", - "src/overlay/shader/single_color.frag", + "src/overlay/elements/shader/single_color.vert", + "src/overlay/elements/shader/single_color.frag", ]; fn query_vulkan_function_typedefs() { diff --git a/src/overlay/elements/mod.rs b/src/overlay/elements/mod.rs index e69de29..038032f 100644 --- a/src/overlay/elements/mod.rs +++ b/src/overlay/elements/mod.rs @@ -0,0 +1,6 @@ +mod pedals; +mod pipeline; +mod radar; + +pub use pedals::*; +pub use radar::*; diff --git a/src/overlay/elements/pedals.rs b/src/overlay/elements/pedals.rs index e69de29..12f970a 100644 --- a/src/overlay/elements/pedals.rs +++ b/src/overlay/elements/pedals.rs @@ -0,0 +1,66 @@ +use std::sync::Arc; + +use anyhow::Result; +use rfactor_sm_reader::{rF2VehicleTelemetry, VehicleScoringInfoV01}; +use ui::prelude::*; + +use crate::overlay::{rfactor_data::DataReceiver, UiOverlay}; + +pub struct Pedals { + gui: Arc, + + brake: Arc, + throttle: Arc, +} + +impl Pedals { + pub fn new(gui_handler: &Arc) -> Result { + const DESC: &str = include_str!("ui_files/pedals.xml"); + + let gui = GuiBuilder::from_str(gui_handler, DESC)?; + + let brake = gui.element("brake")?; + let throttle = gui.element("throttle")?; + + Ok(Self { + gui, + brake, + throttle, + }) + } +} + +impl Drop for Pedals { + fn drop(&mut self) { + self.gui.disable().unwrap(); + } +} + +impl UiOverlay for Pedals { + fn enable_ui(&mut self) -> Result<()> { + self.gui.enable() + } +} + +impl DataReceiver for Pedals { + fn scoring_update(&mut self, _vehicle_scoring: &[VehicleScoringInfoV01]) -> Result<()> { + Ok(()) + } + + fn telemetry_update( + &mut self, + player_id: Option, + telemetries: &[rF2VehicleTelemetry], + ) -> Result<()> { + if let Some(id) = player_id { + if let Some(telemetry) = telemetries.iter().find(|telemetry| telemetry.id == id) { + self.brake + .set_progress(telemetry.unfiltered_throttle as f32)?; + self.throttle + .set_progress(telemetry.unfiltered_throttle as f32)?; + } + } + + Ok(()) + } +} diff --git a/src/overlay/pipeline.rs b/src/overlay/elements/pipeline.rs similarity index 78% rename from src/overlay/pipeline.rs rename to src/overlay/elements/pipeline.rs index fca50bf..ee729a2 100644 --- a/src/overlay/pipeline.rs +++ b/src/overlay/elements/pipeline.rs @@ -3,7 +3,7 @@ use vulkan_rs::prelude::*; use std::{mem, sync::Arc}; -use super::rendering::PositionOnlyVertex; +use super::radar::PositionOnlyVertex; pub struct SingleColorPipeline { pipeline: Arc, @@ -11,7 +11,12 @@ pub struct SingleColorPipeline { } impl SingleColorPipeline { - pub fn new(device: Arc, renderpass: &Arc) -> Result { + pub fn new( + device: Arc, + renderpass: &Arc, + width: u32, + height: u32, + ) -> Result { let vertex_shader = ShaderModule::from_slice( device.clone(), include_bytes!("shader/single_color.vert.spv"), @@ -36,6 +41,23 @@ impl SingleColorPipeline { .add_descriptor_set_layout(&descriptor_layout) .build(device.clone())?; + let viewport = VkViewport { + x: 0.0, + y: 0.0, + width: width as f32, + height: height as f32, + minDepth: 0.0, + maxDepth: 1.0, + }; + + let scissor = VkRect2D { + offset: VkOffset2D { x: 0, y: 0 }, + extent: VkExtent2D { + width: width, + height: height, + }, + }; + let pipeline = Pipeline::new_graphics() .set_vertex_shader( vertex_shader.clone(), @@ -60,6 +82,8 @@ impl SingleColorPipeline { .default_color_blend(vec![VkPipelineColorBlendAttachmentState::default()]) .default_rasterization(VK_CULL_MODE_NONE, VK_FRONT_FACE_COUNTER_CLOCKWISE) .default_multisample(VK_SAMPLE_COUNT_1_BIT) + .add_viewport(viewport) + .add_scissor(scissor) .build(device, &pipeline_layout, &renderpass, 0)?; Ok(Self { diff --git a/src/overlay/elements/radar.rs b/src/overlay/elements/radar.rs index e69de29..e9216f4 100644 --- a/src/overlay/elements/radar.rs +++ b/src/overlay/elements/radar.rs @@ -0,0 +1,495 @@ +use anyhow::Result; +use cgmath::{ortho, vec2, vec3, vec4, Deg, InnerSpace, Matrix4, Rad, Vector2, Vector3, Vector4}; +use rfactor_sm_reader::*; +use serde::{Deserialize, Serialize}; +use vulkan_rs::prelude::*; + +use std::sync::{Arc, Mutex}; + +use super::pipeline::SingleColorPipeline; + +use crate::{ + overlay::{rendering::Rendering, rfactor_data::DataReceiver, UiOverlay}, + write_log, +}; + +#[derive(Clone)] +pub struct PositionOnlyVertex { + pub position: Vector4, +} + +impl PositionOnlyVertex { + /// + /// corners[0] - bottom left + /// corners[1] - top left + /// corners[2] - top right + /// corners[3] - bottom right + /// + pub fn from_2d_corners(ortho: Matrix4, corners: [Vector2; 4]) -> [Self; 6] { + [ + Self { + position: ortho * corners[0].extend(0.0).extend(1.0), + }, + Self { + position: ortho * corners[1].extend(0.0).extend(1.0), + }, + Self { + position: ortho * corners[2].extend(0.0).extend(1.0), + }, + Self { + position: ortho * corners[2].extend(0.0).extend(1.0), + }, + Self { + position: ortho * corners[3].extend(0.0).extend(1.0), + }, + Self { + position: ortho * corners[0].extend(0.0).extend(1.0), + }, + ] + } +} + +fn convert_vec(v: rF2Vec3) -> Vector3 { + vec3(v.x as f32, v.y as f32, v.z as f32) +} + +#[derive(Deserialize, Serialize, Clone, Copy, Debug)] +pub struct RadarConfig { + pub radar_scale: f32, + pub radar_center_factor: f32, + pub radar_transparency: f32, + pub height_scale: f32, + pub width_scale: f32, + pub radar_car_distance: f32, + pub safe_color: Vector3, + pub danger_color: Vector3, +} + +impl RadarConfig { + pub const fn new() -> Self { + Self { + radar_scale: 1.0, + radar_center_factor: 0.25, + radar_transparency: 0.5, + height_scale: 0.15, + width_scale: 0.4, + radar_car_distance: 20.0, + safe_color: vec3(0.0, 0.75, 0.0), + danger_color: vec3(0.75, 0.0, 0.0), + } + } +} + +pub struct Radar { + // config + config: RadarConfig, + + // radar objects + background: Option, + player_car: RadarObject, + cars: Vec, + + // buffer car objects, to prevent recreating them every update + car_handles: Vec, + + // game info + player_id: Option, + + // math objects + radar_center: Vector2, + ortho: Matrix4, + _window_width: u32, + _window_height: u32, + radar_extent: f32, + car_width: f32, + car_height: f32, + + device: Arc, + queue: Arc>, + + pipeline: SingleColorPipeline, + render_target: RenderTarget, + + enabled: bool, +} + +impl Radar { + pub fn new( + config: RadarConfig, + device: Arc, + queue: Arc>, + rendering: &Rendering, + ) -> Result { + write_log!(" =================== create RFactorData ==================="); + + let radar_extent = rendering.swapchain().width() as f32 * 0.075 * config.radar_scale; + let car_height = radar_extent * config.height_scale; + let car_width = car_height * config.width_scale; + let radar_center = vec2( + rendering.swapchain().width() as f32 / 2.0, + rendering.swapchain().height() as f32 / 2.0 + - rendering.swapchain().height() as f32 * config.radar_center_factor, + ); + + let flip_y = matrix4_from_diagonal(vec3(1.0, -1.0, 1.0)); + let ortho = flip_y + * ortho( + 0.0, + rendering.swapchain().width() as f32, + 0.0, + rendering.swapchain().height() as f32, + -1.0, + 1.0, + ); + + let render_target = RenderTarget::builder() + .add_sub_pass( + SubPass::builder( + rendering.swapchain().width(), + rendering.swapchain().height(), + ) + .set_prepared_targets(&rendering.images(), 0, [0.0, 0.0, 0.0, 1.0], false) + .build(&device, &queue)?, + ) + .build(&device)?; + + let pipeline = SingleColorPipeline::new( + device.clone(), + render_target.render_pass(), + rendering.swapchain().width(), + rendering.swapchain().height(), + )?; + + Ok(Self { + config, + + background: if config.radar_transparency == 0.0 { + None + } else { + Some(RadarObject::new( + device.clone(), + pipeline.descriptor_layout(), + PositionOnlyVertex::from_2d_corners( + ortho * Matrix4::from_translation(radar_center.extend(0.0)), + [ + vec2(-radar_extent, -radar_extent), + vec2(-radar_extent, radar_extent), + vec2(radar_extent, radar_extent), + vec2(radar_extent, -radar_extent), + ], + ), + [0.5, 0.5, 0.5, config.radar_transparency], + )?) + }, + player_car: RadarObject::new( + device.clone(), + pipeline.descriptor_layout(), + PositionOnlyVertex::from_2d_corners( + ortho * Matrix4::from_translation(radar_center.extend(0.0)), + [ + vec2(-car_width, -car_height), + vec2(-car_width, car_height), + vec2(car_width, car_height), + vec2(car_width, -car_height), + ], + ), + [0.0, 0.9, 0.0, 0.9], + )?, + cars: Vec::new(), + car_handles: Vec::new(), + + player_id: None, + + radar_center, + ortho, + _window_width: rendering.swapchain().width(), + _window_height: rendering.swapchain().height(), + radar_extent, + car_width, + car_height, + + device, + queue, + + render_target, + pipeline, + + enabled: false, + }) + } + + fn create_car_object(&self, offset: Vector2, color: [f32; 4]) -> Result { + write_log!(" =================== create car object ==================="); + + RadarObject::new( + self.device.clone(), + &self.pipeline.descriptor_layout(), + Self::create_car_vertices( + self.ortho + * Matrix4::from_translation(self.radar_center.extend(0.0)) + * Matrix4::from_translation(offset.extend(0.0)), + self.car_width, + self.car_height, + ), + color, + ) + } + + fn create_car_vertices( + mvp: Matrix4, + car_width: f32, + car_height: f32, + ) -> [PositionOnlyVertex; 6] { + PositionOnlyVertex::from_2d_corners( + mvp, + [ + vec2(-car_width, -car_height), + vec2(-car_width, car_height), + vec2(car_width, car_height), + vec2(car_width, -car_height), + ], + ) + } + + pub fn render(&self, image_index: u32) -> Result> { + let command_buffer = + CommandBuffer::new_primary().build(self.device.clone(), self.queue.clone())?; + + { + let mut recorder = command_buffer.begin(VkCommandBufferBeginInfo::new( + VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT + | VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT, + ))?; + + self.render_target + .begin(&recorder, VK_SUBPASS_CONTENTS_INLINE, image_index as usize); + + recorder.bind_pipeline(self.pipeline.pipeline())?; + + for object in self.objects() { + let buffer = &object.position_buffer; + + recorder.bind_descriptor_sets_minimal(&[&object.descriptor_set]); + recorder.bind_vertex_buffer(buffer); + recorder.draw_complete_single_instance(buffer.size() as u32); + } + + self.render_target.end(&recorder); + } + + Ok(command_buffer) + } + + fn objects(&self) -> Vec<&RadarObject> { + write_log!(" =================== get objects of radar ==================="); + + let mut objects = Vec::new(); + + // only draw radar when player is loaded into a map + if let Some(_player_id) = &self.player_id { + // only draw radar when any car is near enough + if !self.cars.is_empty() { + if let Some(background) = &self.background { + objects.push(background); + } + + for other_player_cars in &self.cars { + objects.push(other_player_cars); + } + + objects.push(&self.player_car); + } + } + + objects + } +} + +impl UiOverlay for Radar { + fn enable_ui(&mut self) -> Result<()> { + self.enabled = true; + + Ok(()) + } +} + +impl DataReceiver for Radar { + fn scoring_update(&mut self, _vehicle_scoring: &[VehicleScoringInfoV01]) -> Result<()> { + Ok(()) + } + + fn telemetry_update( + &mut self, + player_id: Option, + telemetries: &[rF2VehicleTelemetry], + ) -> Result<()> { + self.cars.clear(); + + if !self.enabled { + return Ok(()); + } + + if let Some(player_id) = player_id { + // make sure there are enough cars in buffer + if self.car_handles.len() < telemetries.len() { + let size_diff = telemetries.len() - self.car_handles.len(); + + for _ in 0..size_diff { + self.car_handles + .push(self.create_car_object(vec2(0.0, 0.0), [0.0, 0.0, 0.0, 0.0])?); + } + } + + let mut player_position = CarPosition::default(); + let mut other_positions = Vec::new(); + + for telemetry in telemetries { + let car = CarPosition::new( + convert_vec(telemetry.position), + [ + convert_vec(telemetry.orientation[0]), + convert_vec(telemetry.orientation[1]), + convert_vec(telemetry.orientation[2]), + ], + ); + + if telemetry.id == player_id { + player_position = car + } else { + other_positions.push(car); + } + } + + // update radar objects + let mut buffer_car_index = 0; + + for other_position in other_positions { + let diff = player_position.position - other_position.position; + let distance = diff.magnitude(); + + // check if car is close enough to the players car + if distance < self.config.radar_car_distance { + let offset = diff.xz() * (self.radar_extent / self.config.radar_car_distance); + + let buffered_car = self.car_handles[buffer_car_index].clone(); + buffer_car_index += 1; + buffered_car.update( + self.ortho, + offset, + player_position.rotation, + other_position.rotation, + self.radar_center, + self.car_width, + self.car_height, + [0.9, 0.9, 0.0, 0.9], + )?; + + self.cars.push(buffered_car); + } + } + } + + Ok(()) + } +} + +#[derive(Clone)] +struct RadarObject { + descriptor_set: Arc, + + // uniform buffer + color_buffer: Arc>, + + // vertex buffer + position_buffer: Arc>, +} + +impl RadarObject { + fn new( + device: Arc, + descriptor_layout: &Arc, + positions: [PositionOnlyVertex; 6], + color: [f32; 4], + ) -> Result { + let color_buffer = Buffer::builder() + .set_usage(VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT) + .set_memory_usage(MemoryUsage::CpuOnly) + .set_data(&color) + .build(device.clone())?; + + let position_buffer = Buffer::builder() + .set_usage(VK_BUFFER_USAGE_VERTEX_BUFFER_BIT) + .set_memory_usage(MemoryUsage::CpuOnly) + .set_data(&positions) + .build(device.clone())?; + + let descriptor_pool = DescriptorPool::builder() + .set_layout(descriptor_layout.clone()) + .build(device.clone())?; + + let descriptor_set = descriptor_pool.prepare_set().allocate()?; + descriptor_set.update(&[DescriptorWrite::uniform_buffers(0, &[&color_buffer])])?; + + Ok(Self { + descriptor_set, + color_buffer, + position_buffer, + }) + } + + pub fn update( + &self, + + ortho: Matrix4, + offset: Vector2, + player_rotation: impl Into>, + rotation: impl Into>, + radar_center: Vector2, + car_width: f32, + car_height: f32, + color: [f32; 4], + ) -> Result<()> { + self.position_buffer.fill(&Radar::create_car_vertices( + ortho + * Matrix4::from_translation(radar_center.extend(0.0)) + * Matrix4::from_angle_z(-player_rotation.into()) + * Matrix4::from_translation(offset.extend(0.0)) + * Matrix4::from_angle_z(rotation.into()), + car_width, + car_height, + ))?; + self.color_buffer.fill(&color) + } +} + +struct CarPosition { + pub position: Vector3, + pub rotation: Rad, +} + +impl CarPosition { + fn new(position: Vector3, orientation: [Vector3; 3]) -> Self { + Self { + position, + rotation: Rad(orientation[2].x.atan2(orientation[2].z)), + } + } +} + +impl Default for CarPosition { + fn default() -> Self { + Self { + position: vec3(0.0, 0.0, 0.0), + rotation: Rad(0.0), + } + } +} + +const fn matrix4_from_diagonal(diagonal: Vector3) -> Matrix4 { + Matrix4::from_cols( + vec4(diagonal.x, 0.0, 0.0, 0.0), + vec4(0.0, diagonal.y, 0.0, 0.0), + vec4(0.0, 0.0, diagonal.z, 0.0), + vec4(0.0, 0.0, 0.0, 1.0), + ) +} diff --git a/src/overlay/shader/single_color.frag b/src/overlay/elements/shader/single_color.frag similarity index 100% rename from src/overlay/shader/single_color.frag rename to src/overlay/elements/shader/single_color.frag diff --git a/src/overlay/shader/single_color.vert b/src/overlay/elements/shader/single_color.vert similarity index 100% rename from src/overlay/shader/single_color.vert rename to src/overlay/elements/shader/single_color.vert diff --git a/src/overlay/elements/ui_files/gui.xsd b/src/overlay/elements/ui_files/gui.xsd new file mode 100644 index 0000000..7e0042c --- /dev/null +++ b/src/overlay/elements/ui_files/gui.xsd @@ -0,0 +1,235 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/overlay/elements/ui_files/pedals.xml b/src/overlay/elements/ui_files/pedals.xml new file mode 100644 index 0000000..b3d093b --- /dev/null +++ b/src/overlay/elements/ui_files/pedals.xml @@ -0,0 +1,11 @@ + + + + + + + \ No newline at end of file diff --git a/src/overlay/mod.rs b/src/overlay/mod.rs index 48460ba..aa90311 100644 --- a/src/overlay/mod.rs +++ b/src/overlay/mod.rs @@ -2,21 +2,31 @@ use crate::write_log; use self::{ rendering::Rendering, - rfactor_data::{RFactorData, RadarConfig}, + rfactor_data::{DataReceiver, RFactorData}, }; -mod pipeline; +mod elements; mod rendering; mod rfactor_data; use anyhow::Result; use assetpath::AssetPath; -use std::sync::{Arc, Mutex}; +use std::{ + cell::RefCell, + rc::Rc, + sync::{Arc, Mutex}, +}; use ui::prelude::*; use vulkan_rs::prelude::*; +use elements::*; + use serde::{Deserialize, Serialize}; +pub trait UiOverlay: DataReceiver { + fn enable_ui(&mut self) -> Result<()>; +} + #[derive(Deserialize, Serialize)] pub struct OverlayConfig { pub radar_config: RadarConfig, @@ -41,6 +51,8 @@ pub struct Overlay { rendering: Option, gui_handler: Option>, + ui_elements: Vec>>, + rfactor_data: Option, } @@ -54,6 +66,7 @@ impl Overlay { queue: None, rendering: None, gui_handler: None, + ui_elements: Vec::new(), rfactor_data: None, } @@ -104,13 +117,16 @@ impl Overlay { write_log!("-> create rendering: old cleared"); - let rendering = Rendering::new(self.device(), self.queue(), swapchain.clone())?; + let mut rendering = Rendering::new(self.queue(), swapchain.clone())?; + + write_log!("-> create rendering: new created"); // only font is used let mut create_info = GuiHandlerCreateInfo::default(); create_info.font_path = AssetPath::from(self.config.font_path.clone()); create_info.font_path.assume_prefix_free(); + // provide trait required by GuiHandler let ctx = Arc::new(ContextImpl::new( self.device(), self.queue(), @@ -118,38 +134,75 @@ impl Overlay { rendering.images().clone(), )); - self.gui_handler = Some(GuiHandler::new( - create_info, - &(ctx as Arc), - )?); + // create GuiHandler + let gui_handler = GuiHandler::new(create_info, &(ctx as Arc))?; - self.rendering = Some(rendering); + // create ui elements - write_log!("-> create rendering: new created"); + // create radar + let radar = Rc::new(RefCell::new(Radar::new( + self.config.radar_config, + self.device(), + self.queue(), + &rendering, + )?)); + + // create pedals + let pedals = Rc::new(RefCell::new(Pedals::new(&gui_handler)?)); + + // add rendering callbacks + rendering.add_render_callback({ + let radar = radar.clone(); + + move |index| radar.borrow().render(index) + }); + + rendering.add_render_callback({ + let gui_handler = gui_handler.clone(); + let device = self.device(); + let queue = self.queue(); + + move |index| { + let command_buffer = + CommandBuffer::new_primary().build(device.clone(), queue.clone())?; + + { + let mut recorder = command_buffer.begin(VkCommandBufferBeginInfo::new( + VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT + | VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT, + ))?; + + gui_handler.process(&mut recorder, &TargetMode::Mono(index as usize))?; + } + + Ok(command_buffer) + } + }); + + // add ui elements to local list + self.ui_elements.push(radar); + self.ui_elements.push(pedals); write_log!("-> create rendering: end"); + self.rendering = Some(rendering); + self.gui_handler = Some(gui_handler); + Ok(()) } pub fn render(&mut self) -> Result<()> { - let swapchain = self.rendering.as_ref().unwrap().swapchain().clone(); - if self.rfactor_data.is_none() { - self.rfactor_data = RFactorData::new( - self.config.radar_config, - self.device(), - self.rendering - .as_mut() - .unwrap() - .single_color_pipeline() - .descriptor_layout(), - swapchain.width(), - swapchain.height(), - ) - .ok(); + self.rfactor_data = RFactorData::new().ok(); - write_log!("created RFactorData"); + if let Some(data) = &mut self.rfactor_data { + write_log!("created RFactorData"); + + for receiver in self.ui_elements.iter() { + receiver.borrow_mut().enable_ui()?; + data.add_receiver(receiver.clone()); + } + } } // check twice for rfactor data, because of borrowing rules @@ -157,12 +210,7 @@ impl Overlay { rfactor.update()?; } - let objects = match &self.rfactor_data { - Some(rfactor) => rfactor.objects(), - None => Vec::new(), - }; - - self.rendering.as_mut().unwrap().render(swapchain, &objects) + self.rendering.as_ref().unwrap().render() } } diff --git a/src/overlay/rendering.rs b/src/overlay/rendering.rs index 1637652..ba16122 100644 --- a/src/overlay/rendering.rs +++ b/src/overlay/rendering.rs @@ -1,5 +1,4 @@ use anyhow::Result; -use cgmath::{Matrix4, Vector2, Vector4}; use vulkan_rs::prelude::*; use std::{ @@ -7,61 +6,19 @@ use std::{ time::Duration, }; -use super::{pipeline::SingleColorPipeline, rfactor_data::RenderObject}; use crate::write_log; -#[derive(Clone)] -pub struct PositionOnlyVertex { - pub position: Vector4, -} - -impl PositionOnlyVertex { - /// - /// corners[0] - bottom left - /// corners[1] - top left - /// corners[2] - top right - /// corners[3] - bottom right - /// - pub fn from_2d_corners(ortho: Matrix4, corners: [Vector2; 4]) -> [Self; 6] { - [ - Self { - position: ortho * corners[0].extend(0.0).extend(1.0), - }, - Self { - position: ortho * corners[1].extend(0.0).extend(1.0), - }, - Self { - position: ortho * corners[2].extend(0.0).extend(1.0), - }, - Self { - position: ortho * corners[2].extend(0.0).extend(1.0), - }, - Self { - position: ortho * corners[3].extend(0.0).extend(1.0), - }, - Self { - position: ortho * corners[0].extend(0.0).extend(1.0), - }, - ] - } -} - pub struct Rendering { swapchain: Arc, - pipeline: SingleColorPipeline, - render_target: RenderTarget, - command_buffer: Arc, images: Vec>, queue: Arc>, + + render_callbacks: Vec Result>>>, } impl Rendering { - pub fn new( - device: Arc, - queue: Arc>, - swapchain: Arc, - ) -> Result { + pub fn new(queue: Arc>, swapchain: Arc) -> Result { crate::write_log!("-> Rendering ctor: begin"); let vk_images = swapchain.vk_images()?; write_log!(format!( @@ -77,14 +34,6 @@ impl Rendering { }; write_log!("-> Rendering ctor: wrapped images"); - let render_target = RenderTarget::builder() - .add_sub_pass( - SubPass::builder(swapchain.width(), swapchain.height()) - .set_prepared_targets(&images, 0, [0.0, 0.0, 0.0, 1.0], false) - .build(&device, &queue)?, - ) - .build(&device)?; - write_log!("-> Rendering ctor: created render_target"); write_log!(format!( @@ -95,12 +44,11 @@ impl Rendering { Ok(Self { swapchain, - pipeline: SingleColorPipeline::new(device.clone(), render_target.render_pass())?, - render_target, - command_buffer: CommandBuffer::new_primary().build(device.clone(), queue.clone())?, images, queue, + + render_callbacks: Vec::new(), }) } @@ -108,62 +56,24 @@ impl Rendering { &self.swapchain } - pub fn single_color_pipeline(&self) -> &SingleColorPipeline { - &self.pipeline + pub fn add_render_callback(&mut self, f: F) + where + F: Fn(u32) -> Result> + 'static, + { + self.render_callbacks.push(Box::new(f)); } - pub fn render( - &mut self, - swapchain: Arc, - objects: &[&dyn RenderObject], - ) -> Result<()> { + pub fn render(&self) -> Result<()> { let image_index = self.swapchain.current_index(); - let viewport = [VkViewport { - x: 0.0, - y: 0.0, - width: swapchain.width() as f32, - height: swapchain.height() as f32, - minDepth: 0.0, - maxDepth: 1.0, - }]; - - let scissor = [VkRect2D { - offset: VkOffset2D { x: 0, y: 0 }, - extent: VkExtent2D { - width: swapchain.width(), - height: swapchain.height(), - }, - }]; - - { - let mut recorder = self.command_buffer.begin(VkCommandBufferBeginInfo::new( - VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT - | VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT, - ))?; - - self.render_target - .begin(&recorder, VK_SUBPASS_CONTENTS_INLINE, image_index as usize); - - recorder.bind_pipeline(self.pipeline.pipeline())?; - recorder.set_scissor(&scissor); - recorder.set_viewport(&viewport); - - write_log!(format!("-> Rendering {} objects", objects.len())); - - for object in objects { - let buffer = object.buffer(); - - recorder.bind_descriptor_sets_minimal(&[object.descriptor()]); - recorder.bind_vertex_buffer(buffer); - recorder.draw_complete_single_instance(buffer.size() as u32); - } - - self.render_target.end(&recorder); - } + let command_buffers: Vec> = self + .render_callbacks + .iter() + .map(|c| c(image_index)) + .collect::>>>()?; let queue = self.queue.lock().unwrap(); - queue.minimal_submit(Duration::from_secs(10), &[self.command_buffer.clone()])?; + queue.minimal_submit(Duration::from_secs(10), &command_buffers)?; Ok(()) } diff --git a/src/overlay/rfactor_data.rs b/src/overlay/rfactor_data.rs index 05835e7..dbb974d 100644 --- a/src/overlay/rfactor_data.rs +++ b/src/overlay/rfactor_data.rs @@ -1,196 +1,52 @@ use anyhow::Result; -use cgmath::{ortho, vec2, vec3, vec4, Deg, InnerSpace, Matrix4, Rad, Vector2, Vector3}; use rfactor_sm_reader::*; -use vulkan_rs::prelude::*; -use serde::{Deserialize, Serialize}; +use std::{cell::RefCell, rc::Rc, time::Instant}; -use std::{sync::Arc, time::Instant}; - -use super::rendering::PositionOnlyVertex; use crate::write_log; -fn convert_vec(v: rF2Vec3) -> Vector3 { - vec3(v.x as f32, v.y as f32, v.z as f32) -} +use super::UiOverlay; -pub trait RenderObject { - fn descriptor(&self) -> &Arc; - fn buffer(&self) -> &Arc>; -} +pub trait DataReceiver { + fn scoring_update(&mut self, vehicle_scoring: &[VehicleScoringInfoV01]) -> Result<()>; -#[derive(Deserialize, Serialize, Clone, Copy, Debug)] -pub struct RadarConfig { - pub radar_scale: f32, - pub radar_center_factor: f32, - pub radar_transparency: f32, - pub height_scale: f32, - pub width_scale: f32, - pub radar_car_distance: f32, - pub safe_color: Vector3, - pub danger_color: Vector3, -} - -impl RadarConfig { - pub const fn new() -> Self { - Self { - radar_scale: 1.0, - radar_center_factor: 0.25, - radar_transparency: 0.5, - height_scale: 0.15, - width_scale: 0.4, - radar_car_distance: 20.0, - safe_color: vec3(0.0, 0.75, 0.0), - danger_color: vec3(0.75, 0.0, 0.0), - } - } + fn telemetry_update( + &mut self, + player_id: Option, + telemetries: &[rF2VehicleTelemetry], + ) -> Result<()>; } pub struct RFactorData { - // config - config: RadarConfig, - // rf2 memory mapped data telemetry_reader: TelemetryReader, scoring_reader: ScoringReader, - // radar objects - background: Option, - player_car: RadarObject, - cars: Vec, - - // buffer car objects, to prevent recreating them every update - car_handles: Vec, - - // game info + start_time: Instant, player_id: Option, - // math objects - radar_center: Vector2, - ortho: Matrix4, - _window_width: u32, - _window_height: u32, - radar_extent: f32, - car_width: f32, - car_height: f32, - - start_time: Instant, - - device: Arc, - descriptor_layout: Arc, + receivers: Vec>>, } impl RFactorData { - pub fn new( - config: RadarConfig, - device: Arc, - descriptor_layout: &Arc, - width: u32, - height: u32, - ) -> Result { + pub fn new() -> Result { write_log!(" =================== create RFactorData ==================="); - let radar_extent = width as f32 * 0.075 * config.radar_scale; - let car_height = radar_extent * config.height_scale; - let car_width = car_height * config.width_scale; - let radar_center = vec2( - width as f32 / 2.0, - height as f32 / 2.0 - height as f32 * config.radar_center_factor, - ); - - let flip_y = matrix4_from_diagonal(vec3(1.0, -1.0, 1.0)); - let ortho = flip_y * ortho(0.0, width as f32, 0.0, height as f32, -1.0, 1.0); let start_time = Instant::now(); Ok(Self { - config, - telemetry_reader: TelemetryReader::new(start_time.elapsed().as_secs_f32())?, scoring_reader: ScoringReader::new(start_time.elapsed().as_secs_f32())?, - background: if config.radar_transparency == 0.0 { - None - } else { - Some(RadarObject::new( - device.clone(), - descriptor_layout, - PositionOnlyVertex::from_2d_corners( - ortho * Matrix4::from_translation(radar_center.extend(0.0)), - [ - vec2(-radar_extent, -radar_extent), - vec2(-radar_extent, radar_extent), - vec2(radar_extent, radar_extent), - vec2(radar_extent, -radar_extent), - ], - ), - [0.5, 0.5, 0.5, config.radar_transparency], - )?) - }, - player_car: RadarObject::new( - device.clone(), - descriptor_layout, - PositionOnlyVertex::from_2d_corners( - ortho * Matrix4::from_translation(radar_center.extend(0.0)), - [ - vec2(-car_width, -car_height), - vec2(-car_width, car_height), - vec2(car_width, car_height), - vec2(car_width, -car_height), - ], - ), - [0.0, 0.9, 0.0, 0.9], - )?, - cars: Vec::new(), - car_handles: Vec::new(), - + start_time, player_id: None, - radar_center, - ortho, - _window_width: width, - _window_height: height, - radar_extent, - car_width, - car_height, - - start_time, - - device, - descriptor_layout: descriptor_layout.clone(), + receivers: Vec::new(), }) } - fn create_car_object(&self, offset: Vector2, color: [f32; 4]) -> Result { - write_log!(" =================== create car object ==================="); - - RadarObject::new( - self.device.clone(), - &self.descriptor_layout, - Self::create_car_vertices( - self.ortho - * Matrix4::from_translation(self.radar_center.extend(0.0)) - * Matrix4::from_translation(offset.extend(0.0)), - self.car_width, - self.car_height, - ), - color, - ) - } - - fn create_car_vertices( - mvp: Matrix4, - car_width: f32, - car_height: f32, - ) -> [PositionOnlyVertex; 6] { - PositionOnlyVertex::from_2d_corners( - mvp, - [ - vec2(-car_width, -car_height), - vec2(-car_width, car_height), - vec2(car_width, car_height), - vec2(car_width, -car_height), - ], - ) + pub fn add_receiver(&mut self, receiver: Rc>) { + self.receivers.push(receiver); } fn now(&self) -> f32 { @@ -200,8 +56,6 @@ impl RFactorData { pub fn update(&mut self) -> Result<()> { write_log!(" =================== update RFactorData ==================="); - let mut should_render = false; - // get scoring info if let Some((scoring_info, vehicle_scorings)) = self.scoring_reader.vehicle_scoring(self.now()) @@ -224,223 +78,23 @@ impl RFactorData { } } - if let Some(id) = &self.player_id { - if let Some(vehicle_scoring) = - vehicle_scorings.iter().find(|scoring| scoring.mID == *id) - { - should_render = vehicle_scoring.mInPits != 0; - } + for receiver in self.receivers.iter() { + receiver.borrow_mut().scoring_update(&vehicle_scorings)?; } } - // if player id is set (a map is loaded), check telemetry data - if let Some(player_id) = &self.player_id { - write_log!("before telemetry update"); - if let Some(telemetries) = self.telemetry_reader.query_telemetry(self.now()) { - write_log!("new telemetry update"); + // check telemetry data + write_log!("before telemetry update"); + if let Some(telemetries) = self.telemetry_reader.query_telemetry(self.now()) { + write_log!("new telemetry update"); - self.cars.clear(); - - if should_render { - // make sure there are enough cars in buffer - if self.car_handles.len() < telemetries.len() { - let size_diff = telemetries.len() - self.car_handles.len(); - - for _ in 0..size_diff { - self.car_handles.push( - self.create_car_object(vec2(0.0, 0.0), [0.0, 0.0, 0.0, 0.0])?, - ); - } - } - - let mut player_position = CarPosition::default(); - let mut other_positions = Vec::new(); - - for telemetry in telemetries { - let car = CarPosition::new( - convert_vec(telemetry.position), - [ - convert_vec(telemetry.orientation[0]), - convert_vec(telemetry.orientation[1]), - convert_vec(telemetry.orientation[2]), - ], - ); - - if telemetry.id == *player_id { - player_position = car - } else { - other_positions.push(car); - } - } - - // update radar objects - let mut buffer_car_index = 0; - - for other_position in other_positions { - let diff = player_position.position - other_position.position; - let distance = diff.magnitude(); - - // check if car is close enough to the players car - if distance < self.config.radar_car_distance { - let offset = - diff.xz() * (self.radar_extent / self.config.radar_car_distance); - - let buffered_car = self.car_handles[buffer_car_index].clone(); - buffer_car_index += 1; - buffered_car.update( - self.ortho, - offset, - player_position.rotation, - other_position.rotation, - self.radar_center, - self.car_width, - self.car_height, - [0.9, 0.9, 0.0, 0.9], - )?; - - self.cars.push(buffered_car); - } - } - } + for receiver in self.receivers.iter() { + receiver + .borrow_mut() + .telemetry_update(self.player_id, &telemetries)?; } } Ok(()) } - - pub fn objects(&self) -> Vec<&dyn RenderObject> { - write_log!(" =================== get objects of RFactorData ==================="); - - let mut objects: Vec<&dyn RenderObject> = Vec::new(); - - // only draw radar when player is loaded into a map - if let Some(_player_id) = &self.player_id { - // only draw radar when any car is near enough - if !self.cars.is_empty() { - if let Some(background) = &self.background { - objects.push(background); - } - - for other_player_cars in &self.cars { - objects.push(other_player_cars); - } - - objects.push(&self.player_car); - } - } - - objects - } -} - -#[derive(Clone)] -struct RadarObject { - descriptor_set: Arc, - - // uniform buffer - color_buffer: Arc>, - - // vertex buffer - position_buffer: Arc>, -} - -impl RadarObject { - fn new( - device: Arc, - descriptor_layout: &Arc, - positions: [PositionOnlyVertex; 6], - color: [f32; 4], - ) -> Result { - let color_buffer = Buffer::builder() - .set_usage(VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT) - .set_memory_usage(MemoryUsage::CpuOnly) - .set_data(&color) - .build(device.clone())?; - - let position_buffer = Buffer::builder() - .set_usage(VK_BUFFER_USAGE_VERTEX_BUFFER_BIT) - .set_memory_usage(MemoryUsage::CpuOnly) - .set_data(&positions) - .build(device.clone())?; - - let descriptor_pool = DescriptorPool::builder() - .set_layout(descriptor_layout.clone()) - .build(device.clone())?; - - let descriptor_set = descriptor_pool.prepare_set().allocate()?; - descriptor_set.update(&[DescriptorWrite::uniform_buffers(0, &[&color_buffer])])?; - - Ok(Self { - descriptor_set, - color_buffer, - position_buffer, - }) - } - - pub fn update( - &self, - - ortho: Matrix4, - offset: Vector2, - player_rotation: impl Into>, - rotation: impl Into>, - radar_center: Vector2, - car_width: f32, - car_height: f32, - color: [f32; 4], - ) -> Result<()> { - self.position_buffer - .fill(&RFactorData::create_car_vertices( - ortho - * Matrix4::from_translation(radar_center.extend(0.0)) - * Matrix4::from_angle_z(-player_rotation.into()) - * Matrix4::from_translation(offset.extend(0.0)) - * Matrix4::from_angle_z(rotation.into()), - car_width, - car_height, - ))?; - self.color_buffer.fill(&color) - } -} - -impl RenderObject for RadarObject { - fn descriptor(&self) -> &Arc { - &self.descriptor_set - } - - fn buffer(&self) -> &Arc> { - &self.position_buffer - } -} - -struct CarPosition { - pub position: Vector3, - pub rotation: Rad, -} - -impl CarPosition { - fn new(position: Vector3, orientation: [Vector3; 3]) -> Self { - Self { - position, - rotation: Rad(orientation[2].x.atan2(orientation[2].z)), - } - } -} - -impl Default for CarPosition { - fn default() -> Self { - Self { - position: vec3(0.0, 0.0, 0.0), - rotation: Rad(0.0), - } - } -} - -const fn matrix4_from_diagonal(diagonal: Vector3) -> Matrix4 { - Matrix4::from_cols( - vec4(diagonal.x, 0.0, 0.0, 0.0), - vec4(0.0, diagonal.y, 0.0, 0.0), - vec4(0.0, 0.0, diagonal.z, 0.0), - vec4(0.0, 0.0, 0.0, 1.0), - ) } From cab4701289957fe0bf29bbf637d2d98a2f1e0780 Mon Sep 17 00:00:00 2001 From: hodasemi Date: Tue, 17 Jan 2023 15:44:11 +0100 Subject: [PATCH 30/85] Add watermark --- .vscode/tasks.json | 5 +- font.png | Bin 0 -> 62608 bytes src/overlay/elements/mod.rs | 2 + src/overlay/elements/pedals.rs | 34 +++++--- src/overlay/elements/radar.rs | 53 ++++-------- src/overlay/elements/ui_files/gui.xsd | 9 ++ src/overlay/elements/ui_files/pedals.xml | 15 ++-- src/overlay/elements/ui_files/watermark.xml | 8 ++ src/overlay/elements/watermark.rs | 45 ++++++++++ src/overlay/mod.rs | 90 +++++++++++++++----- src/overlay/rendering.rs | 5 ++ 11 files changed, 191 insertions(+), 75 deletions(-) create mode 100644 font.png create mode 100644 src/overlay/elements/ui_files/watermark.xml create mode 100644 src/overlay/elements/watermark.rs diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 9e98ff5..ea777d8 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -4,11 +4,14 @@ { "type": "cargo", "command": "build", + "args": [ + "--release" + ], "problemMatcher": [ "$rustc" ], "group": "build", - "label": "rust: cargo build --release" + "label": "rust: cargo build" }, { "type": "shell", diff --git a/font.png b/font.png new file mode 100644 index 0000000000000000000000000000000000000000..d10f072d855c273bd7e762ea5475857df4f2179f GIT binary patch literal 62608 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

RSaj|#i^nGocW5wGUFB| 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 literal 0 HcmV?d00001 diff --git a/src/overlay/elements/mod.rs b/src/overlay/elements/mod.rs index 038032f..91e69c3 100644 --- a/src/overlay/elements/mod.rs +++ b/src/overlay/elements/mod.rs @@ -1,6 +1,8 @@ mod pedals; mod pipeline; mod radar; +mod watermark; pub use pedals::*; pub use radar::*; +pub use watermark::*; diff --git a/src/overlay/elements/pedals.rs b/src/overlay/elements/pedals.rs index 12f970a..1e326ac 100644 --- a/src/overlay/elements/pedals.rs +++ b/src/overlay/elements/pedals.rs @@ -11,6 +11,8 @@ pub struct Pedals { brake: Arc, throttle: Arc, + fuel: Arc