use parking_lot::Mutex;
use std::fmt::{Debug, Formatter};
use std::sync::Arc;
use vulkano::pipeline::graphics::viewport::{Viewport, ViewportState};
use vulkano::pipeline::graphics::GraphicsPipelineCreateInfo;
use vulkano::pipeline::GraphicsPipeline;
use crate::event::{Event, EventHandler};
use crate::render::{EngineDevice, RenderTarget, Renderer, RendererStaleEvent};
pub trait VKGraphicsPipelineSource: Send + Sync {
fn vk_graphics(&self) -> Arc<GraphicsPipeline>;
}
#[derive(Clone)]
pub enum ViewportType {
TopLeft,
BottomLeft,
Custom(Arc<dyn (Fn(&Arc<dyn RenderTarget>) -> Viewport) + Send + Sync + 'static>),
}
impl Debug for ViewportType {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
Self::TopLeft => write!(f, "TopLeft"),
Self::BottomLeft => write!(f, "BottomLeft"),
Self::Custom(_) => write!(f, "Custom"),
}
}
}
impl Default for ViewportType {
#[inline]
fn default() -> Self {
Self::TopLeft
}
}
impl<F> From<F> for ViewportType
where
F: (Fn(&Arc<dyn RenderTarget>) -> Viewport) + Send + Sync + 'static,
{
#[inline]
fn from(value: F) -> Self {
Self::Custom(Arc::new(value))
}
}
impl ViewportType {
pub fn viewport(&self, target: &Arc<dyn RenderTarget>) -> Viewport {
match self {
Self::TopLeft => {
Viewport {
offset: [0.0, 0.0],
extent: target.extent().cast::<f32>().into(),
depth_range: 0.0..=1.0,
}
},
Self::BottomLeft => {
let extent = target.extent().cast::<f32>();
Viewport {
offset: [0.0, extent.y],
extent: [extent.x, -extent.y],
depth_range: 0.0..=1.0,
}
},
Self::Custom(create_viewport) => create_viewport(target),
}
}
}
#[derive(Debug, Default)]
struct EngineGraphicsPipelineState {
pipeline: Option<Arc<GraphicsPipeline>>
}
#[derive(Debug)]
pub struct EngineGraphicsPipeline {
target: Arc<dyn RenderTarget>,
device: Arc<EngineDevice>,
create_info: GraphicsPipelineCreateInfo,
viewport_type: ViewportType,
inner: Mutex<EngineGraphicsPipelineState>,
}
impl EngineGraphicsPipeline {
pub fn new(
renderer: &Arc<Renderer>,
create_info: GraphicsPipelineCreateInfo,
viewport_type: ViewportType,
) -> Arc<Self> {
let graphics = Arc::new(Self {
target: renderer.target().clone(),
device: renderer.device().clone(),
create_info,
viewport_type,
inner: Mutex::new(EngineGraphicsPipelineState::default()),
});
renderer.event_bus().register(Arc::downgrade(&graphics)).unwrap();
graphics
}
}
impl VKGraphicsPipelineSource for EngineGraphicsPipeline {
fn vk_graphics(&self) -> Arc<GraphicsPipeline> {
let mut lock = self.inner.lock();
if let Some(pipeline) = &lock.pipeline {
pipeline.clone()
} else {
let viewport = self.viewport_type.viewport(&self.target);
let pipeline = GraphicsPipeline::new(
self.device.vk_device().clone(),
None,
GraphicsPipelineCreateInfo {
viewport_state: Some(ViewportState {
viewports: [viewport].into_iter().collect(),
..Default::default()
}),
..self.create_info.clone()
}
).unwrap();
lock.pipeline = Some(pipeline.clone());
pipeline
}
}
}
impl EventHandler<RendererStaleEvent> for EngineGraphicsPipeline {
fn handle_event(&self, _event: &mut Event<RendererStaleEvent>) {
self.inner.lock().pipeline = None;
}
}