engine/presentation/src/wsi/vulkanwindowrendercore.rs
2025-02-28 20:42:07 +01:00

250 lines
6.9 KiB
Rust

use crate::{RenderCoreCreateInfo, prelude::*, renderbackend::RenderBackend};
use super::windowsystemintegration::WindowSystemIntegration;
use crate::Result;
use ecs::World;
// use ui::prelude::*;
use vulkan_rs::prelude::*;
use std::sync::{
Arc, Mutex, RwLock,
atomic::{AtomicUsize, Ordering::SeqCst},
};
use std::time::Duration;
use std::u64;
pub struct VulkanWindowRenderCore {
device: Arc<Device>,
// driver provided images
swapchain: Arc<Swapchain>,
_surface: Arc<Surface>,
format: VkFormat,
image_available_sem: RwLock<Arc<Semaphore>>,
render_finished_sem: Arc<Semaphore>,
render_fence: Arc<Fence>,
render_backend: RenderBackend,
current_image_index: AtomicUsize,
wsi: Arc<WindowSystemIntegration>,
}
impl VulkanWindowRenderCore {
pub fn new(
wsi: Arc<WindowSystemIntegration>,
device: &Arc<Device>,
queue: &Arc<Mutex<Queue>>,
create_info: RenderCoreCreateInfo,
) -> Result<(Self, TargetMode<()>)> {
// check swapchain extension
if !device.enabled_extensions().swapchain {
return Err(anyhow::Error::msg("Swapchain Extension must be enabled"));
}
let surface = wsi.surface();
if (surface.capabilities(device)?.supportedUsageFlagBits & VK_IMAGE_USAGE_TRANSFER_DST_BIT)
== 0
{
return Err(anyhow::Error::msg(format!(
"Surface capability not met: {:?}",
VK_IMAGE_USAGE_TRANSFER_DST_BIT
)));
}
let usage = create_info.usage | RenderBackend::required_image_usage();
// create swapchain
let swapchain = Swapchain::new(
device.clone(),
&surface,
create_info.vsync,
2,
usage,
create_info.format,
1,
wsi.window_size(),
)?;
let swapchain_images = swapchain.wrap_images(&swapchain.vk_images()?, queue, false)?;
let render_sem = Semaphore::new(device.clone())?;
let image_sem = Semaphore::new(device.clone())?;
let fence = Fence::builder().build(device.clone())?;
let images = TargetMode::Mono(swapchain_images);
let render_backend = RenderBackend::new(device, queue, images)?;
let window_render_core = VulkanWindowRenderCore {
device: device.clone(),
format: swapchain.format(),
swapchain,
_surface: surface,
render_finished_sem: render_sem,
image_available_sem: RwLock::new(image_sem),
render_fence: fence,
render_backend,
current_image_index: AtomicUsize::new(0),
wsi,
};
Ok((window_render_core, TargetMode::Mono(())))
}
fn aquire_next_image_index(&mut self, world: &mut World) -> Result<()> {
// there was a bug that a windows never reacted after it was minimized
// with this timeout, the window has 250ms delay
#[cfg(target_os = "windows")]
const ACQUIRE_IMAGE_TIME_OUT: u64 = 1_000_000_000 / 4;
#[cfg(target_os = "linux")]
const ACQUIRE_IMAGE_TIME_OUT: u64 = u64::MAX;
let resize_mut = unsafe { remove_life_time_mut(self) };
let mut semaphore = self.image_available_sem.write().unwrap();
loop {
match self
.swapchain
.acquire_next_image(ACQUIRE_IMAGE_TIME_OUT, &*semaphore)?
{
OutOfDate::Ok(index) => {
self.current_image_index.store(index as usize, SeqCst);
break;
}
OutOfDate::OutOfDate => {
let (w, h) = self.wsi.window_size();
resize_mut.resize(world, w, h)?;
*semaphore = Semaphore::new(self.device.clone())?;
}
OutOfDate::TimeOut => {
break;
}
}
}
Ok(())
}
}
impl VulkanWindowRenderCore {
pub fn format(&self) -> VkFormat {
self.format
}
pub fn resize(&mut self, world: &mut World, w: u32, h: u32) -> Result<()> {
self.swapchain.recreate((w, h))?;
let swapchain_images = self.swapchain.wrap_images(
&self.swapchain.vk_images()?,
self.render_backend.queue(),
false,
)?;
self.render_backend.resize(
world,
TargetMode::Mono(swapchain_images),
self.swapchain.width(),
self.swapchain.height(),
)?;
Ok(())
}
pub fn next_frame(&mut self, world: &mut World) -> Result<bool> {
self.aquire_next_image_index(world)?;
let command_buffer = self.render_backend.render(
world,
TargetMode::Mono(self.current_image_index.load(SeqCst)),
)?;
let submits = &[SubmitInfo::default()
.add_wait_semaphore(
&*self.image_available_sem.read().unwrap(),
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
)
.add_command_buffer(command_buffer)
.add_signal_semaphore(&self.render_finished_sem)];
if let OutOfDate::OutOfDate = {
let queue_lock = self.render_backend.queue().lock().unwrap();
queue_lock.submit(Some(&self.render_fence), submits)?;
queue_lock.present(
&[&self.swapchain],
&[self.current_image_index.load(SeqCst) as u32],
&[&self.render_finished_sem],
)?
} {
let (w, h) = self.wsi.window_size();
self.resize(world, w, h)?;
self.render_fence.reset();
return Ok(true);
}
// make sure command_buffer is ready
self.render_backend.device().wait_for_fences(
&[&self.render_fence],
true,
Duration::from_secs(10),
)?;
self.render_fence.reset();
Ok(true)
}
pub fn set_clear_color(&self, color: [f32; 4]) {
self.render_backend.set_clear_color(color);
}
// post process handling
pub fn add_render_routine<T: TScene>(&mut self, priority: u32) {
self.render_backend.add_render_routine::<T>(priority);
}
pub fn remove_render_routine<T: TScene>(&mut self) {
self.render_backend.remove_render_routine::<T>();
}
// getter
pub fn image_count(&self) -> usize {
self.render_backend.image_count()
}
pub fn images(&self) -> TargetMode<Vec<Arc<Image>>> {
self.render_backend.images()
}
pub fn width(&self) -> u32 {
self.swapchain.width()
}
pub fn height(&self) -> u32 {
self.swapchain.height()
}
pub fn transformations(&self) -> Option<(VRTransformations, VRTransformations)> {
None
}
}
impl std::fmt::Debug for VulkanWindowRenderCore {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "VulkanWindowRenderCore {{ }}")
}
}