Merge pull request 'Use rFactor typical angled ui elements' (#6) from dev into master
Reviewed-on: #6
This commit is contained in:
commit
c133a2cccc
18 changed files with 1029 additions and 582 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -2,5 +2,6 @@
|
||||||
/Cargo.lock
|
/Cargo.lock
|
||||||
|
|
||||||
*.spv
|
*.spv
|
||||||
|
image_to_file_*.png
|
||||||
|
|
||||||
vk_functions
|
vk_functions
|
2
build.rs
2
build.rs
|
@ -14,6 +14,8 @@ const SHADER: &[&str] = &[
|
||||||
"src/overlay/elements/radar/single_color.frag",
|
"src/overlay/elements/radar/single_color.frag",
|
||||||
"src/overlay/elements/pedals/history.vert",
|
"src/overlay/elements/pedals/history.vert",
|
||||||
"src/overlay/elements/pedals/history.frag",
|
"src/overlay/elements/pedals/history.frag",
|
||||||
|
"src/overlay/elements/leaderboard/generator.frag",
|
||||||
|
"src/overlay/elements/leaderboard/generator.vert",
|
||||||
];
|
];
|
||||||
|
|
||||||
fn query_vulkan_function_typedefs() {
|
fn query_vulkan_function_typedefs() {
|
||||||
|
|
472
src/lib.rs
472
src/lib.rs
|
@ -1,30 +1,17 @@
|
||||||
pub mod enums;
|
|
||||||
pub mod structs;
|
|
||||||
|
|
||||||
mod overlay;
|
mod overlay;
|
||||||
mod vk_handles;
|
mod vk_layer;
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
ffi::c_void,
|
|
||||||
fs::{self, File, OpenOptions},
|
fs::{self, File, OpenOptions},
|
||||||
io::Write,
|
io::Write,
|
||||||
mem,
|
|
||||||
os::raw::c_char,
|
|
||||||
path::Path,
|
path::Path,
|
||||||
ptr,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use enums::*;
|
|
||||||
use overlay::{Overlay, OverlayConfig};
|
use overlay::{Overlay, OverlayConfig};
|
||||||
use structs::*;
|
|
||||||
use vk_handles::*;
|
|
||||||
use vulkan_rs::prelude::*;
|
|
||||||
|
|
||||||
static mut LOG_ENABLED: bool = true;
|
static mut LOG_ENABLED: bool = true;
|
||||||
static mut LOG_FILE: String = String::new();
|
static mut LOG_FILE: String = String::new();
|
||||||
static mut OVERLAY: Overlay = Overlay::new();
|
static mut OVERLAY: Overlay = Overlay::new();
|
||||||
static mut ACQUIRE_NEXT_IMAGE: PFN_vkAcquireNextImageKHR =
|
|
||||||
unsafe { mem::transmute(vkVoidFunction as *const c_void) };
|
|
||||||
|
|
||||||
pub(crate) fn logging() -> bool {
|
pub(crate) fn logging() -> bool {
|
||||||
unsafe { LOG_ENABLED }
|
unsafe { LOG_ENABLED }
|
||||||
|
@ -82,463 +69,6 @@ fn get_config(home: &str) -> OverlayConfig {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
#[allow(non_snake_case)]
|
|
||||||
pub(crate) extern "C" fn vkNegotiateLoaderLayerInterfaceVersion(
|
|
||||||
pVersionStruct: *mut VkNegotiateLayerInterface,
|
|
||||||
) -> VkResult {
|
|
||||||
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 };
|
|
||||||
|
|
||||||
let home = std::env::var("HOME").unwrap();
|
|
||||||
|
|
||||||
if logging() {
|
|
||||||
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!(" ==================================================================");
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
OVERLAY.set_config(get_config(&home));
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
*pVersionStruct = VkNegotiateLayerInterface {
|
|
||||||
sType: enums::VkNegotiateLayerStructType::LAYER_NEGOTIATE_INTERFACE_STRUCT,
|
|
||||||
pNext: ptr::null_mut(),
|
|
||||||
loaderLayerInterfaceVersion: 2,
|
|
||||||
pfnGetInstanceProcAddr: Some(get_instance_proc_addr),
|
|
||||||
pfnGetDeviceProcAddr: Some(get_device_proc_addr),
|
|
||||||
pfnGetPhysicalDeviceProcAddr: None,
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
extern "system" fn get_device_proc_addr(
|
|
||||||
_device: VkDevice,
|
|
||||||
function_name: *const c_char,
|
|
||||||
) -> PFN_vkVoidFunction {
|
|
||||||
let func_string = match VkString::try_from(function_name) {
|
|
||||||
Ok(func) => func,
|
|
||||||
Err(_) => {
|
|
||||||
write_log!("Err: failed creating string");
|
|
||||||
return Functions::Null.convert();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let s = func_string.as_str();
|
|
||||||
|
|
||||||
if let Some(func) = Functions::get_vk_func(s) {
|
|
||||||
return func.convert();
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(func) = vk_handles().handle(s) {
|
|
||||||
return func;
|
|
||||||
}
|
|
||||||
|
|
||||||
Functions::Null.convert()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
extern "system" fn get_instance_proc_addr(
|
|
||||||
_instance: VkInstance,
|
|
||||||
function_name: *const c_char,
|
|
||||||
) -> PFN_vkVoidFunction {
|
|
||||||
let func_string = match VkString::try_from(function_name) {
|
|
||||||
Ok(func) => func,
|
|
||||||
Err(_) => {
|
|
||||||
write_log!("Err: failed creating string");
|
|
||||||
return Functions::Null.convert();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let s = func_string.as_str();
|
|
||||||
|
|
||||||
if let Some(func) = Functions::get_vk_func(s) {
|
|
||||||
return func.convert();
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(func) = vk_handles().handle(s) {
|
|
||||||
return func;
|
|
||||||
}
|
|
||||||
|
|
||||||
Functions::Null.convert()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) extern "system" fn create_instance(
|
|
||||||
create_info: *const VkInstanceCreateInfo,
|
|
||||||
allocator: *const VkAllocationCallbacks,
|
|
||||||
instance: *mut VkInstance,
|
|
||||||
) -> VkResult {
|
|
||||||
write_log!(" ================== vulkan layer create instance ==================");
|
|
||||||
|
|
||||||
let chain_info = match VkLayerInstanceCreateInfo::get_chain_info(
|
|
||||||
unsafe { &*create_info },
|
|
||||||
VK_LAYER_LINK_INFO,
|
|
||||||
) {
|
|
||||||
Some(info) => info,
|
|
||||||
None => {
|
|
||||||
write_log!("instance chain info not found.");
|
|
||||||
return VK_ERROR_LAYER_NOT_PRESENT;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let proc_addr = chain_info.layer_info().next_instance_proc_addr;
|
|
||||||
let create_instance: PFN_vkCreateInstance = unsafe {
|
|
||||||
mem::transmute(proc_addr(
|
|
||||||
VkInstance::NULL_HANDLE,
|
|
||||||
VkString::new("vkCreateInstance").as_ptr(),
|
|
||||||
))
|
|
||||||
};
|
|
||||||
|
|
||||||
chain_info.advance_layer_info();
|
|
||||||
let result = create_instance(create_info, allocator, instance);
|
|
||||||
if result != VK_SUCCESS {
|
|
||||||
return result;
|
|
||||||
};
|
|
||||||
|
|
||||||
vk_handles_mut().load_instance_functions(unsafe { *instance }, proc_addr);
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
OVERLAY.set_instance(ins);
|
|
||||||
}
|
|
||||||
|
|
||||||
write_log!("-> created local instance handle");
|
|
||||||
}
|
|
||||||
|
|
||||||
VK_SUCCESS
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) extern "system" fn destroy_instance(
|
|
||||||
instance: VkInstance,
|
|
||||||
allocator: *const VkAllocationCallbacks,
|
|
||||||
) {
|
|
||||||
write_log!(" ================== vulkan layer destroy instance ==================");
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
if let Some(vk_fn) = vk_handles().handle("vkDestroyInstance") {
|
|
||||||
let destroy_instance: PFN_vkDestroyInstance = mem::transmute(vk_fn);
|
|
||||||
|
|
||||||
destroy_instance(instance, allocator);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) extern "system" fn create_device(
|
|
||||||
physical_device: VkPhysicalDevice,
|
|
||||||
create_info: *const VkDeviceCreateInfo<'_>,
|
|
||||||
allocator: *const VkAllocationCallbacks,
|
|
||||||
device: *mut VkDevice,
|
|
||||||
) -> VkResult {
|
|
||||||
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.");
|
|
||||||
return VK_ERROR_LAYER_NOT_PRESENT;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let proc_addr = chain_info.layer_info().next_device_proc_addr;
|
|
||||||
|
|
||||||
chain_info.advance_layer_info();
|
|
||||||
|
|
||||||
let result = unsafe {
|
|
||||||
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 {
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
vk_handles_mut().load_device_functions(unsafe { *device }, proc_addr);
|
|
||||||
unsafe {
|
|
||||||
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) {
|
|
||||||
Ok(pdev) => pdev,
|
|
||||||
Err(err) => {
|
|
||||||
write_log!(format!("failed creating physical device: {:?}", err));
|
|
||||||
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(device);
|
|
||||||
OVERLAY.set_queue(queue);
|
|
||||||
}
|
|
||||||
|
|
||||||
VK_SUCCESS
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) extern "system" fn destroy_device(
|
|
||||||
device: VkDevice,
|
|
||||||
allocator: *const VkAllocationCallbacks,
|
|
||||||
) {
|
|
||||||
write_log!(" ================== vulkan layer destroy device ==================");
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
if let Some(vk_fn) = vk_handles().handle("vkDestroyDevice") {
|
|
||||||
let destroy_device: PFN_vkDestroyDevice = mem::transmute(vk_fn);
|
|
||||||
|
|
||||||
destroy_device(device, allocator);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) 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 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;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
create_swapchain(_device, create_info, _allocator, p_swapchain);
|
|
||||||
|
|
||||||
write_log!(format!("-> created swapchain vk handle {:?}", unsafe {
|
|
||||||
*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;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
write_log!("-> created Arc<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 renderer");
|
|
||||||
|
|
||||||
VK_SUCCESS
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) extern "system" fn acquire_next_image(
|
|
||||||
device: VkDevice,
|
|
||||||
swapchain: VkSwapchainKHR,
|
|
||||||
timeout: u64,
|
|
||||||
semaphore: VkSemaphore,
|
|
||||||
fence: VkFence,
|
|
||||||
image_index: *mut u32,
|
|
||||||
) -> VkResult {
|
|
||||||
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 (swapchain: {:?}) res: {:?}",
|
|
||||||
sc.vk_handle(),
|
|
||||||
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(crate) extern "system" fn present_queue(
|
|
||||||
queue: VkQueue,
|
|
||||||
present_info: *const VkPresentInfoKHR,
|
|
||||||
) -> VkResult {
|
|
||||||
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()));
|
|
||||||
|
|
||||||
for swapchain in swapchains {
|
|
||||||
write_log!(format!("present swapchain: {:?}", swapchain));
|
|
||||||
|
|
||||||
if let Some(swch) = OVERLAY.swapchain(*swapchain) {
|
|
||||||
write_log!(format!(
|
|
||||||
" -> internal swapchain found! ({:?})",
|
|
||||||
swch.vk_handle()
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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) },
|
|
||||||
None => {
|
|
||||||
write_log!("failed querying vkQueuePresentKHR");
|
|
||||||
return VK_ERROR_DEVICE_LOST;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
pfn(queue, present_info)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn log(msg: impl ToString) {
|
pub fn log(msg: impl ToString) {
|
||||||
assert!(logging());
|
assert!(logging());
|
||||||
|
|
||||||
|
|
246
src/overlay/elements/leaderboard/bg_generator.rs
Normal file
246
src/overlay/elements/leaderboard/bg_generator.rs
Normal file
|
@ -0,0 +1,246 @@
|
||||||
|
use anyhow::Result;
|
||||||
|
use cgmath::{ortho, vec2, Deg};
|
||||||
|
use vulkan_rs::prelude::*;
|
||||||
|
|
||||||
|
use std::{
|
||||||
|
mem,
|
||||||
|
sync::{Arc, Mutex},
|
||||||
|
time::Duration,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::overlay::elements::PositionOnlyVertex;
|
||||||
|
|
||||||
|
pub struct BackgroundGenerator;
|
||||||
|
|
||||||
|
impl BackgroundGenerator {
|
||||||
|
pub fn generate<const N: usize>(
|
||||||
|
device: &Arc<Device>,
|
||||||
|
queue: &Arc<Mutex<Queue>>,
|
||||||
|
color: [f32; 4],
|
||||||
|
image_infos: [(u32, u32); N],
|
||||||
|
) -> Result<[Arc<Image>; N]> {
|
||||||
|
let vertex_shader = ShaderModule::from_slice(
|
||||||
|
device.clone(),
|
||||||
|
include_bytes!("generator.vert.spv"),
|
||||||
|
ShaderType::Vertex,
|
||||||
|
)?;
|
||||||
|
let fragment_shader = ShaderModule::from_slice(
|
||||||
|
device.clone(),
|
||||||
|
include_bytes!("generator.frag.spv"),
|
||||||
|
ShaderType::Fragment,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
Ok(image_infos
|
||||||
|
.iter()
|
||||||
|
.map(|(width, height)| {
|
||||||
|
let image = Image::empty(
|
||||||
|
*width,
|
||||||
|
*height,
|
||||||
|
VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT,
|
||||||
|
VK_SAMPLE_COUNT_1_BIT,
|
||||||
|
)
|
||||||
|
.format(VK_FORMAT_R8G8B8A8_UNORM)
|
||||||
|
.attach_sampler(Sampler::nearest_sampler().build(&device)?)
|
||||||
|
.build(&device, &queue)?;
|
||||||
|
|
||||||
|
image.convert_layout(VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL)?;
|
||||||
|
|
||||||
|
let render_target = RenderTarget::builder()
|
||||||
|
.add_sub_pass(
|
||||||
|
SubPass::builder(image.width(), image.height())
|
||||||
|
.set_prepared_targets(&[image.clone()], 0, [0.0, 0.0, 0.0, 0.0], true)
|
||||||
|
.build(&device, &queue)?,
|
||||||
|
)
|
||||||
|
.build(&device)?;
|
||||||
|
|
||||||
|
let viewport = VkViewport {
|
||||||
|
x: 0.0,
|
||||||
|
y: 0.0,
|
||||||
|
width: image.width() as f32,
|
||||||
|
height: image.height() as f32,
|
||||||
|
minDepth: 0.0,
|
||||||
|
maxDepth: 1.0,
|
||||||
|
};
|
||||||
|
|
||||||
|
let scissor = VkRect2D {
|
||||||
|
offset: VkOffset2D { x: 0, y: 0 },
|
||||||
|
extent: VkExtent2D {
|
||||||
|
width: image.width(),
|
||||||
|
height: image.height(),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
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 = Pipeline::new_graphics()
|
||||||
|
.set_vertex_shader(
|
||||||
|
vertex_shader.clone(),
|
||||||
|
vec![VkVertexInputBindingDescription {
|
||||||
|
binding: 0,
|
||||||
|
stride: mem::size_of::<PositionOnlyVertex>() 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_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.clone(),
|
||||||
|
&PipelineLayout::builder()
|
||||||
|
.add_descriptor_set_layout(&descriptor_layout)
|
||||||
|
.build(device.clone())?,
|
||||||
|
render_target.render_pass(),
|
||||||
|
0,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
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 desc_pool = DescriptorPool::builder()
|
||||||
|
.set_layout(descriptor_layout)
|
||||||
|
.build(device.clone())?;
|
||||||
|
|
||||||
|
let descriptor_set = desc_pool.prepare_set().allocate()?;
|
||||||
|
descriptor_set.update(&[DescriptorWrite::uniform_buffers(0, &[&color_buffer])])?;
|
||||||
|
|
||||||
|
let ortho = ortho(
|
||||||
|
0.0,
|
||||||
|
image.width() as f32,
|
||||||
|
0.00,
|
||||||
|
image.height() as f32,
|
||||||
|
-1.0,
|
||||||
|
1.0,
|
||||||
|
);
|
||||||
|
|
||||||
|
let command_buffer =
|
||||||
|
CommandBuffer::new_primary().build(device.clone(), queue.clone())?;
|
||||||
|
|
||||||
|
// angle 70°
|
||||||
|
let angle: Deg<f32> = Deg(70.0);
|
||||||
|
let tan = angle.0.atan();
|
||||||
|
|
||||||
|
let vertices = PositionOnlyVertex::from_2d_corners(
|
||||||
|
ortho,
|
||||||
|
[
|
||||||
|
vec2(0.0, image.height() as f32),
|
||||||
|
vec2((image.height() as f32) / tan, 0.0),
|
||||||
|
vec2(image.width() as f32, 0.0),
|
||||||
|
vec2(
|
||||||
|
image.width() as f32 - (image.height() as f32) / tan,
|
||||||
|
image.height() as f32,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
let vertex_buffer = Buffer::builder()
|
||||||
|
.set_usage(VK_BUFFER_USAGE_VERTEX_BUFFER_BIT)
|
||||||
|
.set_memory_usage(MemoryUsage::CpuOnly)
|
||||||
|
.set_data(&vertices)
|
||||||
|
.build(device.clone())?;
|
||||||
|
|
||||||
|
SingleSubmit::builder(&command_buffer, queue, |recorder| {
|
||||||
|
render_target.begin(recorder, VK_SUBPASS_CONTENTS_INLINE, 0);
|
||||||
|
|
||||||
|
recorder.bind_pipeline(&pipeline)?;
|
||||||
|
|
||||||
|
recorder.bind_descriptor_sets_minimal(&[&descriptor_set]);
|
||||||
|
recorder.bind_vertex_buffer(&vertex_buffer);
|
||||||
|
recorder.draw_complete_single_instance(vertex_buffer.size() as u32);
|
||||||
|
|
||||||
|
render_target.end(recorder);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
})
|
||||||
|
.wait_for_timeout(Duration::from_secs(1))
|
||||||
|
.submit()?;
|
||||||
|
|
||||||
|
Ok(image)
|
||||||
|
})
|
||||||
|
.collect::<Result<Vec<Arc<Image>>>>()?
|
||||||
|
.try_into()
|
||||||
|
.unwrap_or_else(|_: Vec<Arc<Image>>| {
|
||||||
|
unreachable!("create array from vec from an array")
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use anyhow::Result;
|
||||||
|
use vulkan_rs::prelude::*;
|
||||||
|
|
||||||
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
|
use super::BackgroundGenerator;
|
||||||
|
|
||||||
|
fn create_vk_handles() -> Result<(Arc<Device>, Arc<Mutex<Queue>>)> {
|
||||||
|
let instance = Instance::new(
|
||||||
|
VkApplicationInfo::new(
|
||||||
|
&VkString::new("Test: image::to_file"),
|
||||||
|
1,
|
||||||
|
&VkString::new("no name"),
|
||||||
|
1,
|
||||||
|
VK_MAKE_VERSION(1, 3, 0),
|
||||||
|
),
|
||||||
|
VulkanDebugInfo::default(),
|
||||||
|
InstanceExtensions::default(),
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let physical_device = PhysicalDevice::new(instance)?;
|
||||||
|
|
||||||
|
let queue_info = Queue::create_non_presentable_request_info(
|
||||||
|
&physical_device,
|
||||||
|
VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_TRANSFER_BIT,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let device = Device::new(
|
||||||
|
physical_device,
|
||||||
|
DeviceExtensions::default(),
|
||||||
|
&[queue_info.queue_create_info],
|
||||||
|
DeviceFeatures::default(),
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let queue = device.get_queue(queue_info.queue_family_index, queue_info.queue_index);
|
||||||
|
|
||||||
|
Ok((device, queue))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn generate_image_test() {
|
||||||
|
let (device, queue) = create_vk_handles().unwrap();
|
||||||
|
|
||||||
|
let images =
|
||||||
|
BackgroundGenerator::generate(&device, &queue, [1.0, 0.0, 0.0, 1.0], [(120, 40)])
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
for (index, image) in images.iter().enumerate() {
|
||||||
|
image
|
||||||
|
.to_file(&format!("image_to_file_{}.png", index))
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
<?xml-model href="../gui.xsd" type="application/xml" schematypes="http://www.w3.org/2001/XMLSchema"?>
|
<?xml-model href="../gui.xsd" type="application/xml" schematypes="http://www.w3.org/2001/XMLSchema"?>
|
||||||
<root reference_width="2560" reference_height="1440">
|
<root reference_width="2560" reference_height="1440">
|
||||||
<!-- max 5 entries, each entry gets 39 pixel (at 1440p) -->
|
<!-- max 5 entries, each entry gets 44 pixel (at 1440p) -->
|
||||||
<grid id="main_grid" x_dim="1" y_dim="5" x_offset="-410" y_offset="10" width="400" height="195"
|
<grid id="main_grid" x_dim="1" y_dim="5" x_offset="-510" y_offset="10" width="500" height="220"
|
||||||
vert_align="top" hori_align="right" margin="0" padding="0"> </grid>
|
vert_align="top" hori_align="right" margin="0" padding="0"> </grid>
|
||||||
</root>
|
</root>
|
12
src/overlay/elements/leaderboard/generator.frag
Normal file
12
src/overlay/elements/leaderboard/generator.frag
Normal file
|
@ -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 = color.val;
|
||||||
|
}
|
8
src/overlay/elements/leaderboard/generator.vert
Normal file
8
src/overlay/elements/leaderboard/generator.vert
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
#version 450
|
||||||
|
|
||||||
|
layout (location = 0) in vec4 position;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
gl_Position = position;
|
||||||
|
}
|
|
@ -3,12 +3,15 @@ use std::sync::Arc;
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
|
||||||
use ui::prelude::*;
|
use ui::prelude::*;
|
||||||
use utilities::prelude::Color;
|
use vulkan_rs::prelude::*;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
|
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
|
||||||
pub enum BehindLeader {
|
pub enum BehindLeader {
|
||||||
Time(f64),
|
Time(f64),
|
||||||
Laps(i32),
|
Laps(i32),
|
||||||
|
DNF,
|
||||||
|
DSQ,
|
||||||
|
PITS,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct LeaderBoardEntry {
|
pub struct LeaderBoardEntry {
|
||||||
|
@ -22,9 +25,9 @@ pub struct LeaderBoardEntry {
|
||||||
|
|
||||||
snippet: Arc<GuiSnippet>,
|
snippet: Arc<GuiSnippet>,
|
||||||
|
|
||||||
name_label: Arc<Label>,
|
name_label: Arc<Icon>,
|
||||||
place_label: Arc<Label>,
|
place_label: Arc<Icon>,
|
||||||
time_label: Arc<Label>,
|
time_label: Arc<Icon>,
|
||||||
|
|
||||||
place_updated: bool,
|
place_updated: bool,
|
||||||
}
|
}
|
||||||
|
@ -32,6 +35,10 @@ pub struct LeaderBoardEntry {
|
||||||
impl LeaderBoardEntry {
|
impl LeaderBoardEntry {
|
||||||
const ENTRY: &str = include_str!("leaderboard_entry.xml");
|
const ENTRY: &str = include_str!("leaderboard_entry.xml");
|
||||||
|
|
||||||
|
pub fn create_snippet(gui_handler: &Arc<GuiHandler>) -> Result<Arc<GuiSnippet>> {
|
||||||
|
GuiSnippet::from_str(gui_handler, Self::ENTRY)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn empty(gui_handler: &Arc<GuiHandler>) -> Result<Self> {
|
pub fn empty(gui_handler: &Arc<GuiHandler>) -> Result<Self> {
|
||||||
Self::new(
|
Self::new(
|
||||||
gui_handler,
|
gui_handler,
|
||||||
|
@ -53,11 +60,11 @@ impl LeaderBoardEntry {
|
||||||
time_behind_next: f64,
|
time_behind_next: f64,
|
||||||
best_lap: f64,
|
best_lap: f64,
|
||||||
) -> Result<Self> {
|
) -> Result<Self> {
|
||||||
let snippet = GuiSnippet::from_str(gui_handler, Self::ENTRY)?;
|
let snippet = Self::create_snippet(gui_handler)?;
|
||||||
|
|
||||||
let name_label: Arc<Label> = snippet.element("name")?;
|
let name_label: Arc<Icon> = snippet.element("name")?;
|
||||||
let place_label: Arc<Label> = snippet.element("place")?;
|
let place_label: Arc<Icon> = snippet.element("place")?;
|
||||||
let time_label: Arc<Label> = snippet.element("time")?;
|
let time_label: Arc<Icon> = snippet.element("time")?;
|
||||||
|
|
||||||
name_label.set_text(&name)?;
|
name_label.set_text(&name)?;
|
||||||
place_label.set_text(place)?;
|
place_label.set_text(place)?;
|
||||||
|
@ -108,10 +115,13 @@ impl LeaderBoardEntry {
|
||||||
self.snippet.clone()
|
self.snippet.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn change_background_color(&self, color: Color) -> Result<()> {
|
pub fn change_background_color(
|
||||||
self.time_label.set_background(color)?;
|
&self,
|
||||||
self.name_label.set_background(color)?;
|
(place_image, name_image, time_image): &(Arc<Image>, Arc<Image>, Arc<Image>),
|
||||||
self.place_label.set_background(color)?;
|
) -> Result<()> {
|
||||||
|
self.time_label.set_icon(&time_image)?;
|
||||||
|
self.name_label.set_icon(&name_image)?;
|
||||||
|
self.place_label.set_icon(&place_image)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -119,17 +129,23 @@ impl LeaderBoardEntry {
|
||||||
pub fn update_place(&mut self, place: u8) -> Result<()> {
|
pub fn update_place(&mut self, place: u8) -> Result<()> {
|
||||||
if self.place != place {
|
if self.place != place {
|
||||||
self.place_updated = true;
|
self.place_updated = true;
|
||||||
}
|
|
||||||
|
|
||||||
self.place = place;
|
self.place = place;
|
||||||
|
self.place_label.set_text(self.place)?;
|
||||||
self.place_label.set_text(self.place)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_time_behind_leader(&mut self, behind: BehindLeader) -> Result<()> {
|
Ok(())
|
||||||
if self.behind != behind {
|
}
|
||||||
self.behind = behind;
|
|
||||||
|
|
||||||
|
pub fn reset_time(&mut self) -> Result<()> {
|
||||||
|
self.behind = BehindLeader::Time(f64::MIN);
|
||||||
|
self.best_lap = f64::MIN;
|
||||||
|
self.time_behind_next = f64::MIN;
|
||||||
|
|
||||||
|
self.time_label.set_text("---")
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn force_display_behind_leader(&mut self) -> Result<()> {
|
||||||
match self.behind {
|
match self.behind {
|
||||||
BehindLeader::Time(time_behind) => {
|
BehindLeader::Time(time_behind) => {
|
||||||
// check if we are leader
|
// check if we are leader
|
||||||
|
@ -149,25 +165,34 @@ impl LeaderBoardEntry {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
BehindLeader::Laps(laps_behind) => {
|
BehindLeader::Laps(laps_behind) => {
|
||||||
let text = if laps_behind == 1 {
|
self.time_label.set_text(format!("+{}", laps_behind))?;
|
||||||
format!("+{} Lap", laps_behind)
|
|
||||||
} else {
|
|
||||||
format!("+{} Laps", laps_behind)
|
|
||||||
};
|
|
||||||
|
|
||||||
self.time_label.set_text(text)?;
|
|
||||||
}
|
}
|
||||||
|
BehindLeader::DNF => {
|
||||||
|
self.time_label.set_text("DNF")?;
|
||||||
|
}
|
||||||
|
BehindLeader::DSQ => {
|
||||||
|
self.time_label.set_text("DSQ")?;
|
||||||
|
}
|
||||||
|
BehindLeader::PITS => {
|
||||||
|
self.time_label.set_text("PIT")?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_best_lap(&mut self, time: f64) -> Result<()> {
|
pub fn update_time_behind_leader(&mut self, behind: BehindLeader) -> Result<()> {
|
||||||
if self.best_lap != time {
|
if self.behind != behind {
|
||||||
self.best_lap = time;
|
self.behind = behind;
|
||||||
|
|
||||||
if self.best_lap < 0.0 {
|
self.force_display_behind_leader()?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn force_display_best_lap(&mut self) -> Result<()> {
|
||||||
|
if self.best_lap <= 0.0 {
|
||||||
self.time_label.set_text("---")?;
|
self.time_label.set_text("---")?;
|
||||||
} else {
|
} else {
|
||||||
let text = if self.best_lap > 60.0 {
|
let text = if self.best_lap > 60.0 {
|
||||||
|
@ -181,14 +206,21 @@ impl LeaderBoardEntry {
|
||||||
|
|
||||||
self.time_label.set_text(text)?;
|
self.time_label.set_text(text)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update_best_lap(&mut self, time: f64) -> Result<()> {
|
||||||
|
if self.best_lap != time {
|
||||||
|
self.best_lap = time;
|
||||||
|
|
||||||
|
self.force_display_best_lap()?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_time_behind_next(&mut self, time: f64) -> Result<()> {
|
pub fn force_display_behind_next(&mut self) -> Result<()> {
|
||||||
self.time_behind_next = time;
|
|
||||||
|
|
||||||
let text = if self.time_behind_next > 60.0 {
|
let text = if self.time_behind_next > 60.0 {
|
||||||
let full_minutes = (self.time_behind_next / 60.0).floor();
|
let full_minutes = (self.time_behind_next / 60.0).floor();
|
||||||
let remainder = self.time_behind_next - (full_minutes * 60.0);
|
let remainder = self.time_behind_next - (full_minutes * 60.0);
|
||||||
|
@ -201,6 +233,16 @@ impl LeaderBoardEntry {
|
||||||
self.time_label.set_text(text)
|
self.time_label.set_text(text)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn update_time_behind_next(&mut self, time: f64) -> Result<()> {
|
||||||
|
if self.time_behind_next != time {
|
||||||
|
self.time_behind_next = time;
|
||||||
|
|
||||||
|
self.force_display_behind_next()?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
pub fn needs_resorting(&self) -> bool {
|
pub fn needs_resorting(&self) -> bool {
|
||||||
self.place_updated
|
self.place_updated
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,11 @@
|
||||||
<?xml-model href="../gui.xsd" type="application/xml" schematypes="http://www.w3.org/2001/XMLSchema"?>
|
<?xml-model href="../gui.xsd" type="application/xml" schematypes="http://www.w3.org/2001/XMLSchema"?>
|
||||||
<root>
|
<root>
|
||||||
<grid x_dim="11" y_dim="1" padding="2" margin="2">
|
<grid x_dim="12" y_dim="1" padding="3" margin="0">
|
||||||
<label id="place" x_slot="0" y_slot="0" text_color="black" text_alignment="right"></label>
|
<icon id="place" x_slot="0" y_slot="0" x_size="2" text_color="black"></icon>
|
||||||
<label
|
<icon id="name"
|
||||||
id="name"
|
x_slot="2"
|
||||||
x_slot="1" y_slot="0" x_size="7" text_color="black"></label>
|
y_slot="0" x_size="7" text_color="black"></icon>
|
||||||
<label
|
<icon id="time" x_slot="9" y_slot="0"
|
||||||
id="time"
|
x_size="3" text_color="black"></icon>
|
||||||
x_slot="8" y_slot="0" x_size="3" text_color="black" text_alignment="right"></label>
|
|
||||||
</grid>
|
</grid>
|
||||||
</root>
|
</root>
|
|
@ -1,6 +1,6 @@
|
||||||
<?xml-model href="../gui.xsd" type="application/xml" schematypes="http://www.w3.org/2001/XMLSchema"?>
|
<?xml-model href="../gui.xsd" type="application/xml" schematypes="http://www.w3.org/2001/XMLSchema"?>
|
||||||
<root reference_width="2560" reference_height="1440">
|
<root reference_width="2560" reference_height="1440">
|
||||||
<!-- max 25 entries, each entry gets 39 pixel (at 1440p) -->
|
<!-- max 20 entries, each entry gets 44 pixel (at 1440p) -->
|
||||||
<grid id="main_grid" x_dim="1" y_dim="25" x_offset="10" y_offset="10" width="400" height="975"
|
<grid id="main_grid" x_dim="1" y_dim="20" x_offset="10" y_offset="10" width="500" height="880"
|
||||||
vert_align="top" hori_align="left" margin="0" padding="0"> </grid>
|
vert_align="top" hori_align="left" margin="0" padding="0"> </grid>
|
||||||
</root>
|
</root>
|
|
@ -1,6 +1,8 @@
|
||||||
|
mod bg_generator;
|
||||||
mod leaderboard_entry;
|
mod leaderboard_entry;
|
||||||
|
|
||||||
use leaderboard_entry::*;
|
use leaderboard_entry::*;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
ffi::{c_char, CStr},
|
ffi::{c_char, CStr},
|
||||||
|
@ -10,7 +12,7 @@ use std::{
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use rfactor_sm_reader::{rF2VehicleTelemetry, VehicleScoringInfoV01};
|
use rfactor_sm_reader::{rF2VehicleTelemetry, VehicleScoringInfoV01};
|
||||||
use ui::prelude::*;
|
use ui::prelude::*;
|
||||||
use utilities::prelude::Color;
|
use vulkan_rs::prelude::*;
|
||||||
|
|
||||||
use crate::overlay::{
|
use crate::overlay::{
|
||||||
rfactor_data::{DataReceiver, GamePhase},
|
rfactor_data::{DataReceiver, GamePhase},
|
||||||
|
@ -18,6 +20,25 @@ use crate::overlay::{
|
||||||
};
|
};
|
||||||
use crate::write_log;
|
use crate::write_log;
|
||||||
|
|
||||||
|
use bg_generator::BackgroundGenerator;
|
||||||
|
|
||||||
|
#[derive(Default, Deserialize, Serialize, Clone, Copy)]
|
||||||
|
pub struct LeaderBoardConfig {
|
||||||
|
first_board_color: [f32; 3],
|
||||||
|
second_board_color: [f32; 3],
|
||||||
|
player_board_color: [f32; 3],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LeaderBoardConfig {
|
||||||
|
pub const fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
first_board_color: [0.33; 3],
|
||||||
|
second_board_color: [0.51; 3],
|
||||||
|
player_board_color: [0.7, 0.75, 0.15],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct LeaderBoard {
|
pub struct LeaderBoard {
|
||||||
gui_handler: Arc<GuiHandler>,
|
gui_handler: Arc<GuiHandler>,
|
||||||
leaderboard: Arc<GuiBuilder>,
|
leaderboard: Arc<GuiBuilder>,
|
||||||
|
@ -32,25 +53,78 @@ pub struct LeaderBoard {
|
||||||
leaderboard_redraw: bool,
|
leaderboard_redraw: bool,
|
||||||
last_player_id: i32,
|
last_player_id: i32,
|
||||||
|
|
||||||
entry_backgrounds: [Color; 2],
|
entry_backgrounds: [(Arc<Image>, Arc<Image>, Arc<Image>); 2],
|
||||||
player_background: Color,
|
player_background: (Arc<Image>, Arc<Image>, Arc<Image>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LeaderBoard {
|
impl LeaderBoard {
|
||||||
const LEADERBOARD: &str = include_str!("leaderboard_grid.xml");
|
const LEADERBOARD: &str = include_str!("leaderboard_grid.xml");
|
||||||
const DELTABOARD: &str = include_str!("deltaboard_grid.xml");
|
const DELTABOARD: &str = include_str!("deltaboard_grid.xml");
|
||||||
|
|
||||||
pub fn new(gui_handler: &Arc<GuiHandler>) -> Result<Self> {
|
pub fn new(
|
||||||
|
gui_handler: &Arc<GuiHandler>,
|
||||||
|
leader_board_config: LeaderBoardConfig,
|
||||||
|
) -> Result<Self> {
|
||||||
let leaderboard = GuiBuilder::from_str(gui_handler, Self::LEADERBOARD)?;
|
let leaderboard = GuiBuilder::from_str(gui_handler, Self::LEADERBOARD)?;
|
||||||
let deltaboard = GuiBuilder::from_str(gui_handler, Self::DELTABOARD)?;
|
let deltaboard = GuiBuilder::from_str(gui_handler, Self::DELTABOARD)?;
|
||||||
|
|
||||||
let leaderboard_grid = leaderboard.element("main_grid")?;
|
let leaderboard_grid: Arc<Grid> = leaderboard.element("main_grid")?;
|
||||||
let deltaboard_grid = deltaboard.element("main_grid")?;
|
let deltaboard_grid = deltaboard.element("main_grid")?;
|
||||||
|
|
||||||
|
let images = {
|
||||||
|
// attach snippet to leader board to let it resize its child element correctly
|
||||||
|
// then use these sizes to create actual UI element background
|
||||||
|
let dummy_snippet = LeaderBoardEntry::create_snippet(gui_handler)?;
|
||||||
|
|
||||||
|
let place: Arc<Icon> = dummy_snippet.element("place")?;
|
||||||
|
let name: Arc<Icon> = dummy_snippet.element("name")?;
|
||||||
|
let time: Arc<Icon> = dummy_snippet.element("time")?;
|
||||||
|
|
||||||
|
leaderboard_grid.attach(dummy_snippet, 0, 0, 1, 1)?;
|
||||||
|
|
||||||
|
let colors = [
|
||||||
|
{
|
||||||
|
let a = leader_board_config.first_board_color;
|
||||||
|
[a[0], a[1], a[2], 1.0]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
let a = leader_board_config.second_board_color;
|
||||||
|
[a[0], a[1], a[2], 1.0]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
let a = leader_board_config.player_board_color;
|
||||||
|
[a[0], a[1], a[2], 1.0]
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
let images = colors
|
||||||
|
.iter()
|
||||||
|
.map(|color| {
|
||||||
|
let images = BackgroundGenerator::generate(
|
||||||
|
gui_handler.device(),
|
||||||
|
gui_handler.queue(),
|
||||||
|
*color,
|
||||||
|
[
|
||||||
|
Self::extent_i_to_u(place.extent()),
|
||||||
|
Self::extent_i_to_u(name.extent()),
|
||||||
|
Self::extent_i_to_u(time.extent()),
|
||||||
|
],
|
||||||
|
)?;
|
||||||
|
|
||||||
|
Ok((images[0].clone(), images[1].clone(), images[2].clone()))
|
||||||
|
})
|
||||||
|
.collect::<Result<Vec<(Arc<Image>, Arc<Image>, Arc<Image>)>>>()?;
|
||||||
|
|
||||||
|
leaderboard_grid.detach(0, 0)?;
|
||||||
|
|
||||||
|
images
|
||||||
|
};
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
gui_handler: gui_handler.clone(),
|
gui_handler: gui_handler.clone(),
|
||||||
leaderboard,
|
leaderboard,
|
||||||
deltaboard,
|
deltaboard,
|
||||||
|
|
||||||
leaderboard_grid,
|
leaderboard_grid,
|
||||||
deltaboard_grid,
|
deltaboard_grid,
|
||||||
|
|
||||||
|
@ -66,11 +140,15 @@ impl LeaderBoard {
|
||||||
leaderboard_redraw: false,
|
leaderboard_redraw: false,
|
||||||
last_player_id: -1,
|
last_player_id: -1,
|
||||||
|
|
||||||
entry_backgrounds: [Color::try_from("#838383")?, Color::try_from("#545454")?],
|
entry_backgrounds: [images[0].clone(), images[1].clone()],
|
||||||
player_background: Color::try_from("#b4bf26")?,
|
player_background: images[2].clone(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn extent_i_to_u((x, y): (i32, i32)) -> (u32, u32) {
|
||||||
|
(x as u32, y as u32)
|
||||||
|
}
|
||||||
|
|
||||||
fn c_char_to_string(c: [c_char; 32usize]) -> String {
|
fn c_char_to_string(c: [c_char; 32usize]) -> String {
|
||||||
unsafe { CStr::from_ptr(&c as *const c_char) }
|
unsafe { CStr::from_ptr(&c as *const c_char) }
|
||||||
.to_str()
|
.to_str()
|
||||||
|
@ -78,13 +156,15 @@ impl LeaderBoard {
|
||||||
.to_string()
|
.to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_leaderboard<F>(
|
fn update_leaderboard<F, D>(
|
||||||
&mut self,
|
&mut self,
|
||||||
vehicle_scorings: &[VehicleScoringInfoV01],
|
vehicle_scorings: &[VehicleScoringInfoV01],
|
||||||
f: F,
|
f: F,
|
||||||
|
d: D,
|
||||||
) -> Result<()>
|
) -> Result<()>
|
||||||
where
|
where
|
||||||
F: Fn(&mut LeaderBoardEntry, &VehicleScoringInfoV01) -> Result<()>,
|
F: Fn(&mut LeaderBoardEntry, &VehicleScoringInfoV01) -> Result<()>,
|
||||||
|
D: Fn(&mut LeaderBoardEntry) -> Result<()>,
|
||||||
{
|
{
|
||||||
for vehicle_scoring in vehicle_scorings {
|
for vehicle_scoring in vehicle_scorings {
|
||||||
let driver_name = Self::c_char_to_string(vehicle_scoring.mDriverName);
|
let driver_name = Self::c_char_to_string(vehicle_scoring.mDriverName);
|
||||||
|
@ -95,6 +175,7 @@ impl LeaderBoard {
|
||||||
.iter_mut()
|
.iter_mut()
|
||||||
.find(|entry| vehicle_scoring.mID == entry.id())
|
.find(|entry| vehicle_scoring.mID == entry.id())
|
||||||
{
|
{
|
||||||
|
// update existing entry
|
||||||
Some(entry) => {
|
Some(entry) => {
|
||||||
if entry.name() != driver_name {
|
if entry.name() != driver_name {
|
||||||
entry.change_name(driver_name)?;
|
entry.change_name(driver_name)?;
|
||||||
|
@ -104,25 +185,20 @@ impl LeaderBoard {
|
||||||
|
|
||||||
f(entry, vehicle_scoring)?;
|
f(entry, vehicle_scoring)?;
|
||||||
}
|
}
|
||||||
|
// add new entry if not found
|
||||||
None => {
|
None => {
|
||||||
let entry = LeaderBoardEntry::new(
|
let mut entry = LeaderBoardEntry::new(
|
||||||
&self.gui_handler,
|
&self.gui_handler,
|
||||||
vehicle_scoring.mID,
|
vehicle_scoring.mID,
|
||||||
driver_name,
|
driver_name,
|
||||||
vehicle_scoring.mPlace,
|
vehicle_scoring.mPlace,
|
||||||
{
|
Self::query_behind_leader(vehicle_scoring),
|
||||||
let laps_behind = vehicle_scoring.mLapsBehindLeader;
|
|
||||||
|
|
||||||
if laps_behind != 0 {
|
|
||||||
BehindLeader::Laps(laps_behind)
|
|
||||||
} else {
|
|
||||||
BehindLeader::Time(vehicle_scoring.mTimeBehindLeader)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
vehicle_scoring.mTimeBehindNext,
|
vehicle_scoring.mTimeBehindNext,
|
||||||
vehicle_scoring.mBestLapTime,
|
vehicle_scoring.mBestLapTime,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
d(&mut entry)?;
|
||||||
|
|
||||||
self.leaderboard_entries.push(entry);
|
self.leaderboard_entries.push(entry);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -177,7 +253,7 @@ impl LeaderBoard {
|
||||||
// don't break here, just skip adding to grid
|
// don't break here, just skip adding to grid
|
||||||
// because resorting_finished should be called for every entry
|
// because resorting_finished should be called for every entry
|
||||||
if i < self.leaderboard_grid.dimensions().1 {
|
if i < self.leaderboard_grid.dimensions().1 {
|
||||||
entry.change_background_color(self.entry_backgrounds[i % 2])?;
|
entry.change_background_color(&self.entry_backgrounds[i % 2])?;
|
||||||
|
|
||||||
self.leaderboard_grid.attach(entry.snippet(), 0, i, 1, 1)?;
|
self.leaderboard_grid.attach(entry.snippet(), 0, i, 1, 1)?;
|
||||||
}
|
}
|
||||||
|
@ -196,7 +272,27 @@ impl LeaderBoard {
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.find(|(_index, entry)| entry.id() == self.last_player_id)
|
.find(|(_index, entry)| entry.id() == self.last_player_id)
|
||||||
{
|
{
|
||||||
let mut start_index = if index >= 2 { index - 2 } else { 0 };
|
let mut start_index = if index >= 2 {
|
||||||
|
if index == self.leaderboard_entries.len() - 2 {
|
||||||
|
if index >= 3 {
|
||||||
|
index - 3
|
||||||
|
} else {
|
||||||
|
index - 2
|
||||||
|
}
|
||||||
|
} else if index == self.leaderboard_entries.len() - 1 {
|
||||||
|
if index >= 4 {
|
||||||
|
index - 4
|
||||||
|
} else if index >= 3 {
|
||||||
|
index - 3
|
||||||
|
} else {
|
||||||
|
index - 2
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
index - 2
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
};
|
||||||
|
|
||||||
let max = self.leaderboard_entries.len().min(5);
|
let max = self.leaderboard_entries.len().min(5);
|
||||||
|
|
||||||
|
@ -222,9 +318,9 @@ impl LeaderBoard {
|
||||||
entry.update_place(leaderboard_entry.place())?;
|
entry.update_place(leaderboard_entry.place())?;
|
||||||
|
|
||||||
if entry.id() == self.last_player_id {
|
if entry.id() == self.last_player_id {
|
||||||
entry.change_background_color(self.player_background)?;
|
entry.change_background_color(&self.player_background)?;
|
||||||
} else {
|
} else {
|
||||||
entry.change_background_color(self.entry_backgrounds[i % 2])?;
|
entry.change_background_color(&self.entry_backgrounds[i % 2])?;
|
||||||
}
|
}
|
||||||
|
|
||||||
if entry.name() != leaderboard_entry.name() {
|
if entry.name() != leaderboard_entry.name() {
|
||||||
|
@ -250,22 +346,49 @@ impl LeaderBoard {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn race_leaderboard(&mut self, vehicle_scorings: &[VehicleScoringInfoV01]) -> Result<()> {
|
fn query_behind_leader(scoring: &VehicleScoringInfoV01) -> BehindLeader {
|
||||||
self.update_leaderboard(vehicle_scorings, |entry, scoring| {
|
match scoring.mFinishStatus {
|
||||||
|
0 | 1 => {
|
||||||
|
if scoring.mInPits != 0 {
|
||||||
|
BehindLeader::PITS
|
||||||
|
} else {
|
||||||
let laps_behind = scoring.mLapsBehindLeader;
|
let laps_behind = scoring.mLapsBehindLeader;
|
||||||
|
|
||||||
if laps_behind != 0 {
|
if laps_behind != 0 {
|
||||||
entry.update_time_behind_leader(BehindLeader::Laps(laps_behind))
|
BehindLeader::Laps(laps_behind)
|
||||||
} else {
|
} else {
|
||||||
entry.update_time_behind_leader(BehindLeader::Time(scoring.mTimeBehindLeader))
|
BehindLeader::Time(scoring.mTimeBehindLeader)
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
|
}
|
||||||
|
2 => BehindLeader::DNF,
|
||||||
|
3 => BehindLeader::DSQ,
|
||||||
|
|
||||||
|
_ => {
|
||||||
|
write_log!(format!(
|
||||||
|
"not allowed finish state: {}",
|
||||||
|
scoring.mFinishStatus
|
||||||
|
));
|
||||||
|
|
||||||
|
BehindLeader::Time(scoring.mTimeBehindLeader)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn race_leaderboard(&mut self, vehicle_scorings: &[VehicleScoringInfoV01]) -> Result<()> {
|
||||||
|
self.update_leaderboard(
|
||||||
|
vehicle_scorings,
|
||||||
|
|entry, scoring| entry.update_time_behind_leader(Self::query_behind_leader(scoring)),
|
||||||
|
|entry| entry.force_display_behind_leader(),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn quali_leaderboard(&mut self, vehicle_scorings: &[VehicleScoringInfoV01]) -> Result<()> {
|
fn quali_leaderboard(&mut self, vehicle_scorings: &[VehicleScoringInfoV01]) -> Result<()> {
|
||||||
self.update_leaderboard(vehicle_scorings, |entry, scoring| {
|
self.update_leaderboard(
|
||||||
entry.update_best_lap(scoring.mBestLapTime)
|
vehicle_scorings,
|
||||||
})
|
|entry, scoring| entry.update_best_lap(scoring.mBestLapTime),
|
||||||
|
|entry| entry.force_display_best_lap(),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -276,7 +399,7 @@ impl DataReceiver for LeaderBoard {
|
||||||
match phase {
|
match phase {
|
||||||
GamePhase::Practice | GamePhase::Qualifying | GamePhase::TestDay => {
|
GamePhase::Practice | GamePhase::Qualifying | GamePhase::TestDay => {
|
||||||
for entry in self.leaderboard_entries.iter_mut() {
|
for entry in self.leaderboard_entries.iter_mut() {
|
||||||
entry.update_best_lap(-1.0)?;
|
entry.reset_time()?;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.leaderboard.enable()?;
|
self.leaderboard.enable()?;
|
||||||
|
@ -284,7 +407,11 @@ impl DataReceiver for LeaderBoard {
|
||||||
|
|
||||||
GamePhase::Race => {
|
GamePhase::Race => {
|
||||||
for entry in self.leaderboard_entries.iter_mut() {
|
for entry in self.leaderboard_entries.iter_mut() {
|
||||||
entry.update_time_behind_leader(BehindLeader::Time(0.0))?;
|
entry.reset_time()?;
|
||||||
|
}
|
||||||
|
|
||||||
|
for entry in self.deltaboard_entries.iter_mut() {
|
||||||
|
entry.reset_time()?;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.leaderboard.enable()?;
|
self.leaderboard.enable()?;
|
||||||
|
@ -351,7 +478,7 @@ impl DataReceiver for LeaderBoard {
|
||||||
"Update player entry background color: {:?}",
|
"Update player entry background color: {:?}",
|
||||||
self.player_background
|
self.player_background
|
||||||
));
|
));
|
||||||
entry.change_background_color(self.player_background)?;
|
entry.change_background_color(&self.player_background)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,7 +42,7 @@ impl RadarConfig {
|
||||||
Self {
|
Self {
|
||||||
radar_scale: 1.0,
|
radar_scale: 1.0,
|
||||||
radar_center_factor: 0.25,
|
radar_center_factor: 0.25,
|
||||||
radar_transparency: 0.5,
|
radar_transparency: 0.3,
|
||||||
height_scale: 0.15,
|
height_scale: 0.15,
|
||||||
width_scale: 0.4,
|
width_scale: 0.4,
|
||||||
radar_car_distance: 20.0,
|
radar_car_distance: 20.0,
|
||||||
|
|
|
@ -49,6 +49,7 @@ impl UiSelectorConfig {
|
||||||
pub struct OverlayConfig {
|
pub struct OverlayConfig {
|
||||||
pub ui_config: UiSelectorConfig,
|
pub ui_config: UiSelectorConfig,
|
||||||
pub radar_config: RadarConfig,
|
pub radar_config: RadarConfig,
|
||||||
|
pub leader_board_config: LeaderBoardConfig,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl OverlayConfig {
|
impl OverlayConfig {
|
||||||
|
@ -56,6 +57,7 @@ impl OverlayConfig {
|
||||||
Self {
|
Self {
|
||||||
ui_config: UiSelectorConfig::new(),
|
ui_config: UiSelectorConfig::new(),
|
||||||
radar_config: RadarConfig::new(),
|
radar_config: RadarConfig::new(),
|
||||||
|
leader_board_config: LeaderBoardConfig::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -208,7 +210,10 @@ impl Overlay {
|
||||||
|
|
||||||
// create leaderboard
|
// create leaderboard
|
||||||
if self.config.ui_config.enable_leaderboard {
|
if self.config.ui_config.enable_leaderboard {
|
||||||
let leaderboard = Rc::new(RefCell::new(LeaderBoard::new(&gui_handler)?));
|
let leaderboard = Rc::new(RefCell::new(LeaderBoard::new(
|
||||||
|
&gui_handler,
|
||||||
|
self.config.leader_board_config,
|
||||||
|
)?));
|
||||||
self.ui_elements.push(leaderboard);
|
self.ui_elements.push(leaderboard);
|
||||||
|
|
||||||
write_log!("Leader Board successfully created");
|
write_log!("Leader Board successfully created");
|
||||||
|
|
|
@ -20,11 +20,14 @@ pub struct Rendering {
|
||||||
impl Rendering {
|
impl Rendering {
|
||||||
pub fn new(queue: Arc<Mutex<Queue>>, swapchain: Arc<Swapchain>) -> Result<Self> {
|
pub fn new(queue: Arc<Mutex<Queue>>, swapchain: Arc<Swapchain>) -> Result<Self> {
|
||||||
crate::write_log!("-> Rendering ctor: begin");
|
crate::write_log!("-> Rendering ctor: begin");
|
||||||
|
|
||||||
let vk_images = swapchain.vk_images()?;
|
let vk_images = swapchain.vk_images()?;
|
||||||
|
|
||||||
write_log!(format!(
|
write_log!(format!(
|
||||||
"-> Rendering ctor: vk images ({})",
|
"-> Rendering ctor: vk images ({})",
|
||||||
vk_images.len()
|
vk_images.len()
|
||||||
));
|
));
|
||||||
|
|
||||||
let images = match swapchain.wrap_images(&vk_images, &queue, true) {
|
let images = match swapchain.wrap_images(&vk_images, &queue, true) {
|
||||||
Ok(images) => images,
|
Ok(images) => images,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
|
@ -32,9 +35,8 @@ impl Rendering {
|
||||||
return Err(err);
|
return Err(err);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
write_log!("-> Rendering ctor: wrapped images");
|
|
||||||
|
|
||||||
write_log!("-> Rendering ctor: created render_target");
|
write_log!("-> Rendering ctor: wrapped images");
|
||||||
|
|
||||||
write_log!(format!(
|
write_log!(format!(
|
||||||
"-> Rendering swapchain extents ({}, {})",
|
"-> Rendering swapchain extents ({}, {})",
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
use std::{mem, ptr};
|
use std::{mem, ptr};
|
||||||
use vulkan_rs::prelude::*;
|
use vulkan_rs::prelude::*;
|
||||||
|
|
||||||
use crate::*;
|
use super::*;
|
||||||
|
|
||||||
pub use VkLayerFunction::*;
|
pub use VkLayerFunction::*;
|
||||||
pub use VkNegotiateLayerStructType::*;
|
pub use VkNegotiateLayerStructType::*;
|
||||||
|
@ -13,6 +13,7 @@ pub enum VkNegotiateLayerStructType {
|
||||||
LAYER_NEGOTIATE_INTERFACE_STRUCT = 1,
|
LAYER_NEGOTIATE_INTERFACE_STRUCT = 1,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(unused)]
|
||||||
#[derive(Clone, Copy, Eq, PartialEq)]
|
#[derive(Clone, Copy, Eq, PartialEq)]
|
||||||
pub enum VkLayerFunction {
|
pub enum VkLayerFunction {
|
||||||
VK_LAYER_LINK_INFO = 0,
|
VK_LAYER_LINK_INFO = 0,
|
472
src/vk_layer/mod.rs
Normal file
472
src/vk_layer/mod.rs
Normal file
|
@ -0,0 +1,472 @@
|
||||||
|
mod enums;
|
||||||
|
mod structs;
|
||||||
|
mod vk_handles;
|
||||||
|
|
||||||
|
use enums::*;
|
||||||
|
use structs::*;
|
||||||
|
use vk_handles::*;
|
||||||
|
use vulkan_rs::prelude::*;
|
||||||
|
|
||||||
|
use std::{ffi::c_void, fs::File, mem, os::raw::c_char, ptr};
|
||||||
|
|
||||||
|
static mut ACQUIRE_NEXT_IMAGE: PFN_vkAcquireNextImageKHR =
|
||||||
|
unsafe { mem::transmute(vkVoidFunction as *const c_void) };
|
||||||
|
|
||||||
|
use crate::{get_config, logging, write_log, LOG_ENABLED, LOG_FILE, OVERLAY};
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
pub(crate) extern "C" fn vkNegotiateLoaderLayerInterfaceVersion(
|
||||||
|
pVersionStruct: *mut VkNegotiateLayerInterface,
|
||||||
|
) -> VkResult {
|
||||||
|
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 };
|
||||||
|
|
||||||
|
let home = std::env::var("HOME").unwrap();
|
||||||
|
|
||||||
|
if logging() {
|
||||||
|
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!(" ==================================================================");
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
OVERLAY.set_config(get_config(&home));
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
*pVersionStruct = VkNegotiateLayerInterface {
|
||||||
|
sType: enums::VkNegotiateLayerStructType::LAYER_NEGOTIATE_INTERFACE_STRUCT,
|
||||||
|
pNext: ptr::null_mut(),
|
||||||
|
loaderLayerInterfaceVersion: 2,
|
||||||
|
pfnGetInstanceProcAddr: Some(get_instance_proc_addr),
|
||||||
|
pfnGetDeviceProcAddr: Some(get_device_proc_addr),
|
||||||
|
pfnGetPhysicalDeviceProcAddr: None,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
extern "system" fn get_device_proc_addr(
|
||||||
|
_device: VkDevice,
|
||||||
|
function_name: *const c_char,
|
||||||
|
) -> PFN_vkVoidFunction {
|
||||||
|
let func_string = match VkString::try_from(function_name) {
|
||||||
|
Ok(func) => func,
|
||||||
|
Err(_) => {
|
||||||
|
write_log!("Err: failed creating string");
|
||||||
|
return Functions::Null.convert();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let s = func_string.as_str();
|
||||||
|
|
||||||
|
if let Some(func) = Functions::get_vk_func(s) {
|
||||||
|
return func.convert();
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(func) = vk_handles().handle(s) {
|
||||||
|
return func;
|
||||||
|
}
|
||||||
|
|
||||||
|
Functions::Null.convert()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
extern "system" fn get_instance_proc_addr(
|
||||||
|
_instance: VkInstance,
|
||||||
|
function_name: *const c_char,
|
||||||
|
) -> PFN_vkVoidFunction {
|
||||||
|
let func_string = match VkString::try_from(function_name) {
|
||||||
|
Ok(func) => func,
|
||||||
|
Err(_) => {
|
||||||
|
write_log!("Err: failed creating string");
|
||||||
|
return Functions::Null.convert();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let s = func_string.as_str();
|
||||||
|
|
||||||
|
if let Some(func) = Functions::get_vk_func(s) {
|
||||||
|
return func.convert();
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(func) = vk_handles().handle(s) {
|
||||||
|
return func;
|
||||||
|
}
|
||||||
|
|
||||||
|
Functions::Null.convert()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) extern "system" fn create_instance(
|
||||||
|
create_info: *const VkInstanceCreateInfo,
|
||||||
|
allocator: *const VkAllocationCallbacks,
|
||||||
|
instance: *mut VkInstance,
|
||||||
|
) -> VkResult {
|
||||||
|
write_log!(" ================== vulkan layer create instance ==================");
|
||||||
|
|
||||||
|
let chain_info = match VkLayerInstanceCreateInfo::get_chain_info(
|
||||||
|
unsafe { &*create_info },
|
||||||
|
VK_LAYER_LINK_INFO,
|
||||||
|
) {
|
||||||
|
Some(info) => info,
|
||||||
|
None => {
|
||||||
|
write_log!("instance chain info not found.");
|
||||||
|
return VK_ERROR_LAYER_NOT_PRESENT;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let proc_addr = chain_info.layer_info().next_instance_proc_addr;
|
||||||
|
let create_instance: PFN_vkCreateInstance = unsafe {
|
||||||
|
mem::transmute(proc_addr(
|
||||||
|
VkInstance::NULL_HANDLE,
|
||||||
|
VkString::new("vkCreateInstance").as_ptr(),
|
||||||
|
))
|
||||||
|
};
|
||||||
|
|
||||||
|
chain_info.advance_layer_info();
|
||||||
|
let result = create_instance(create_info, allocator, instance);
|
||||||
|
if result != VK_SUCCESS {
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
|
vk_handles_mut().load_instance_functions(unsafe { *instance }, proc_addr);
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
OVERLAY.set_instance(ins);
|
||||||
|
}
|
||||||
|
|
||||||
|
write_log!("-> created local instance handle");
|
||||||
|
}
|
||||||
|
|
||||||
|
VK_SUCCESS
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) extern "system" fn destroy_instance(
|
||||||
|
instance: VkInstance,
|
||||||
|
allocator: *const VkAllocationCallbacks,
|
||||||
|
) {
|
||||||
|
write_log!(" ================== vulkan layer destroy instance ==================");
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
if let Some(vk_fn) = vk_handles().handle("vkDestroyInstance") {
|
||||||
|
let destroy_instance: PFN_vkDestroyInstance = mem::transmute(vk_fn);
|
||||||
|
|
||||||
|
destroy_instance(instance, allocator);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) extern "system" fn create_device(
|
||||||
|
physical_device: VkPhysicalDevice,
|
||||||
|
create_info: *const VkDeviceCreateInfo<'_>,
|
||||||
|
allocator: *const VkAllocationCallbacks,
|
||||||
|
device: *mut VkDevice,
|
||||||
|
) -> VkResult {
|
||||||
|
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.");
|
||||||
|
return VK_ERROR_LAYER_NOT_PRESENT;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let proc_addr = chain_info.layer_info().next_device_proc_addr;
|
||||||
|
|
||||||
|
chain_info.advance_layer_info();
|
||||||
|
|
||||||
|
let result = unsafe {
|
||||||
|
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 {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
vk_handles_mut().load_device_functions(unsafe { *device }, proc_addr);
|
||||||
|
unsafe {
|
||||||
|
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) {
|
||||||
|
Ok(pdev) => pdev,
|
||||||
|
Err(err) => {
|
||||||
|
write_log!(format!("failed creating physical device: {:?}", err));
|
||||||
|
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(device);
|
||||||
|
OVERLAY.set_queue(queue);
|
||||||
|
}
|
||||||
|
|
||||||
|
VK_SUCCESS
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) extern "system" fn destroy_device(
|
||||||
|
device: VkDevice,
|
||||||
|
allocator: *const VkAllocationCallbacks,
|
||||||
|
) {
|
||||||
|
write_log!(" ================== vulkan layer destroy device ==================");
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
if let Some(vk_fn) = vk_handles().handle("vkDestroyDevice") {
|
||||||
|
let destroy_device: PFN_vkDestroyDevice = mem::transmute(vk_fn);
|
||||||
|
|
||||||
|
destroy_device(device, allocator);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) 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 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;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
create_swapchain(_device, create_info, _allocator, p_swapchain);
|
||||||
|
|
||||||
|
write_log!(format!("-> created swapchain vk handle {:?}", unsafe {
|
||||||
|
*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;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
write_log!("-> created Arc<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 renderer");
|
||||||
|
|
||||||
|
VK_SUCCESS
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) extern "system" fn acquire_next_image(
|
||||||
|
device: VkDevice,
|
||||||
|
swapchain: VkSwapchainKHR,
|
||||||
|
timeout: u64,
|
||||||
|
semaphore: VkSemaphore,
|
||||||
|
fence: VkFence,
|
||||||
|
image_index: *mut u32,
|
||||||
|
) -> VkResult {
|
||||||
|
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 (swapchain: {:?}) res: {:?}",
|
||||||
|
sc.vk_handle(),
|
||||||
|
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(crate) extern "system" fn present_queue(
|
||||||
|
queue: VkQueue,
|
||||||
|
present_info: *const VkPresentInfoKHR,
|
||||||
|
) -> VkResult {
|
||||||
|
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()));
|
||||||
|
|
||||||
|
for swapchain in swapchains {
|
||||||
|
write_log!(format!("present swapchain: {:?}", swapchain));
|
||||||
|
|
||||||
|
if let Some(swch) = OVERLAY.swapchain(*swapchain) {
|
||||||
|
write_log!(format!(
|
||||||
|
" -> internal swapchain found! ({:?})",
|
||||||
|
swch.vk_handle()
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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) },
|
||||||
|
None => {
|
||||||
|
write_log!("failed querying vkQueuePresentKHR");
|
||||||
|
return VK_ERROR_DEVICE_LOST;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pfn(queue, present_info)
|
||||||
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
#![allow(non_snake_case)]
|
#![allow(non_snake_case)]
|
||||||
|
|
||||||
use crate::enums::*;
|
use super::enums::*;
|
||||||
|
|
||||||
use vulkan_rs::prelude::*;
|
use vulkan_rs::prelude::*;
|
||||||
|
|
|
@ -24,7 +24,7 @@ pub struct VkTypedefHandles {
|
||||||
impl VkTypedefHandles {
|
impl VkTypedefHandles {
|
||||||
pub fn new() -> Result<Self> {
|
pub fn new() -> Result<Self> {
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
typedefs: include_str!("../vk_functions")
|
typedefs: include_str!("../../vk_functions")
|
||||||
.lines()
|
.lines()
|
||||||
.map(|s| s.to_string())
|
.map(|s| s.to_string())
|
||||||
.collect(),
|
.collect(),
|
Loading…
Reference in a new issue