use crate::core::ShaderStage;
use crate::ex::{
PipelineBuilder, PipelineId, RuntimeConfig, RuntimeManager, ShaderId, ShaderManager,
};
use crate::ez::errors::EzError;
use ash::vk;
use std::sync::Arc;
#[derive(Clone)]
pub struct EzConfig {
pub app_name: String,
pub enable_validation: bool,
pub in_flight_frames: usize,
}
impl Default for EzConfig {
fn default() -> Self {
Self {
app_name: "EZ Renderer".to_string(),
enable_validation: cfg!(debug_assertions),
in_flight_frames: 2,
}
}
}
pub struct EzFrame<'a> {
device: Arc<crate::core::Device>,
command_buffer: vk::CommandBuffer,
shader_manager: &'a ShaderManager,
}
impl<'a> EzFrame<'a> {
#[inline]
pub fn bind_pipeline(&self, pipeline_id: PipelineId) -> Result<(), EzError> {
let pipeline = self.shader_manager.get_pipeline(pipeline_id)?;
unsafe {
self.device.handle().cmd_bind_pipeline(
self.command_buffer,
vk::PipelineBindPoint::GRAPHICS,
pipeline.handle(),
);
}
Ok(())
}
#[inline]
pub fn bind_compute_pipeline(&self, pipeline_id: PipelineId) -> Result<(), EzError> {
let pipeline = self.shader_manager.get_pipeline(pipeline_id)?;
unsafe {
self.device.handle().cmd_bind_pipeline(
self.command_buffer,
vk::PipelineBindPoint::COMPUTE,
pipeline.handle(),
);
}
Ok(())
}
#[inline]
pub fn set_viewport(&self, x: f32, y: f32, width: f32, height: f32) {
let viewport = vk::Viewport {
x,
y,
width,
height,
min_depth: 0.0,
max_depth: 1.0,
};
unsafe {
self.device
.handle()
.cmd_set_viewport(self.command_buffer, 0, &[viewport]);
}
}
#[inline]
pub fn set_scissor(&self, x: i32, y: i32, width: u32, height: u32) {
let scissor = vk::Rect2D {
offset: vk::Offset2D { x, y },
extent: vk::Extent2D { width, height },
};
unsafe {
self.device
.handle()
.cmd_set_scissor(self.command_buffer, 0, &[scissor]);
}
}
#[inline]
pub fn draw(
&self,
vertex_count: u32,
instance_count: u32,
first_vertex: u32,
first_instance: u32,
) {
unsafe {
self.device.handle().cmd_draw(
self.command_buffer,
vertex_count,
instance_count,
first_vertex,
first_instance,
);
}
}
#[inline]
pub fn draw_indexed(
&self,
index_count: u32,
instance_count: u32,
first_index: u32,
vertex_offset: i32,
first_instance: u32,
) {
unsafe {
self.device.handle().cmd_draw_indexed(
self.command_buffer,
index_count,
instance_count,
first_index,
vertex_offset,
first_instance,
);
}
}
#[inline]
pub fn bind_vertex_buffers(
&self,
first_binding: u32,
buffers: &[vk::Buffer],
offsets: &[vk::DeviceSize],
) {
unsafe {
self.device.handle().cmd_bind_vertex_buffers(
self.command_buffer,
first_binding,
buffers,
offsets,
);
}
}
#[inline]
pub fn bind_index_buffer(
&self,
buffer: vk::Buffer,
offset: vk::DeviceSize,
index_type: vk::IndexType,
) {
unsafe {
self.device.handle().cmd_bind_index_buffer(
self.command_buffer,
buffer,
offset,
index_type,
);
}
}
#[inline]
pub fn dispatch(&self, group_count_x: u32, group_count_y: u32, group_count_z: u32) {
unsafe {
self.device.handle().cmd_dispatch(
self.command_buffer,
group_count_x,
group_count_y,
group_count_z,
);
}
}
#[inline]
pub fn bind_descriptor_sets(
&self,
pipeline_bind_point: vk::PipelineBindPoint,
layout: vk::PipelineLayout,
first_set: u32,
descriptor_sets: &[vk::DescriptorSet],
) {
unsafe {
self.device.handle().cmd_bind_descriptor_sets(
self.command_buffer,
pipeline_bind_point,
layout,
first_set,
descriptor_sets,
&[],
);
}
}
#[inline]
pub fn push_constants<T: Copy>(
&self,
layout: vk::PipelineLayout,
stage_flags: vk::ShaderStageFlags,
offset: u32,
constants: &T,
) {
let size = std::mem::size_of::<T>() as u32;
let data = unsafe {
std::slice::from_raw_parts(constants as *const T as *const u8, size as usize)
};
unsafe {
self.device.handle().cmd_push_constants(
self.command_buffer,
layout,
stage_flags,
offset,
data,
);
}
}
#[inline]
pub fn begin_rendering(&self, width: u32, height: u32, _format: vk::Format) {
let color_attachment = vk::RenderingAttachmentInfo {
image_view: vk::ImageView::null(), image_layout: vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL,
load_op: vk::AttachmentLoadOp::CLEAR,
store_op: vk::AttachmentStoreOp::STORE,
clear_value: vk::ClearValue {
color: vk::ClearColorValue {
float32: [0.0, 0.0, 0.0, 1.0],
},
},
..Default::default()
};
let rendering_info = vk::RenderingInfo {
render_area: vk::Rect2D {
offset: vk::Offset2D { x: 0, y: 0 },
extent: vk::Extent2D { width, height },
},
layer_count: 1,
color_attachment_count: 1,
p_color_attachments: &color_attachment,
..Default::default()
};
unsafe {
self.device
.handle()
.cmd_begin_rendering(self.command_buffer, &rendering_info);
}
}
#[inline]
pub fn end_rendering(&self) {
unsafe {
self.device.handle().cmd_end_rendering(self.command_buffer);
}
}
#[inline]
pub fn command_buffer(&self) -> vk::CommandBuffer {
self.command_buffer
}
#[inline]
pub fn device(&self) -> &Arc<crate::core::Device> {
&self.device
}
}
pub struct EzRenderer {
shader_manager: ShaderManager,
runtime: RuntimeManager,
}
impl EzRenderer {
pub fn new() -> Result<Self, EzError> {
Self::with_config(EzConfig::default())
}
pub fn with_config(config: EzConfig) -> Result<Self, EzError> {
let runtime = RuntimeManager::new(RuntimeConfig {
app_name: config.app_name,
enable_validation: config.enable_validation,
in_flight_frames: config.in_flight_frames,
..Default::default()
})
.map_err(|e| EzError::InitializationFailed(e.to_string()))?;
let device = runtime.device();
let shader_manager =
ShaderManager::new(device).map_err(|e| EzError::InitializationFailed(e.to_string()))?;
Ok(Self {
runtime,
shader_manager,
})
}
pub fn quick_pipeline(
&mut self,
vertex_glsl: &str,
fragment_glsl: &str,
) -> Result<PipelineId, EzError> {
let vert_id = self.add_shader(vertex_glsl, ShaderStage::Vertex)?;
let frag_id = self.add_shader(fragment_glsl, ShaderStage::Fragment)?;
let pipeline_id = self.shader_manager.build_pipeline(
PipelineBuilder::new()
.vertex_shader(self.shader_manager.get_shader(vert_id)?.handle(), "main")
.fragment_shader(self.shader_manager.get_shader(frag_id)?.handle(), "main")
.color_attachment_formats(vec![vk::Format::R8G8B8A8_UNORM])
.dynamic_viewport()
.dynamic_scissor(),
"ez_pipeline",
)?;
Ok(pipeline_id)
}
pub fn quick_compute(&mut self, compute_glsl: &str) -> Result<PipelineId, EzError> {
let comp_id = self.add_shader(compute_glsl, ShaderStage::Compute)?;
let pipeline_id = self.shader_manager.build_pipeline(
PipelineBuilder::new()
.compute_shader(self.shader_manager.get_shader(comp_id)?.handle(), "main"),
"ez_compute",
)?;
Ok(pipeline_id)
}
pub fn add_shader(&mut self, glsl: &str, stage: ShaderStage) -> Result<ShaderId, EzError> {
let name = format!("ez_shader_{}", self.shader_manager.shader_count());
self.shader_manager
.add_shader(glsl, stage, &name)
.map_err(|e| EzError::ShaderCompilationFailed(e.to_string()))
}
pub fn build_pipeline(&mut self, builder: PipelineBuilder) -> Result<PipelineId, EzError> {
let name = format!("ez_pipeline_{}", self.shader_manager.pipeline_count());
self.shader_manager
.build_pipeline(builder, &name)
.map_err(|e| EzError::PipelineCreationFailed(e.to_string()))
}
pub fn render_frame<F>(&mut self, f: F) -> Result<(), EzError>
where
F: FnOnce(&EzFrame) -> Result<(), EzError>,
{
let frame = self
.runtime
.begin_frame()
.map_err(|e| EzError::FrameFailed(e.to_string()))?;
frame
.command_buffer
.begin(&frame.device, vk::CommandBufferUsageFlags::ONE_TIME_SUBMIT)
.map_err(|e| EzError::FrameFailed(e.to_string()))?;
let ez_frame = EzFrame {
device: frame.device.clone(),
command_buffer: frame.command_buffer.handle(),
shader_manager: &self.shader_manager,
};
f(&ez_frame)?;
frame
.command_buffer
.end(&frame.device)
.map_err(|e| EzError::FrameFailed(e.to_string()))?;
self.runtime
.end_frame(&Default::default())
.map_err(|e| EzError::FrameFailed(e.to_string()))?;
Ok(())
}
#[inline]
pub fn runtime(&self) -> &RuntimeManager {
&self.runtime
}
#[inline]
pub fn runtime_mut(&mut self) -> &mut RuntimeManager {
&mut self.runtime
}
#[inline]
pub fn shader_manager(&self) -> &ShaderManager {
&self.shader_manager
}
#[inline]
pub fn shader_manager_mut(&mut self) -> &mut ShaderManager {
&mut self.shader_manager
}
pub fn wait_idle(&self) -> Result<(), EzError> {
self.runtime
.wait_idle()
.map_err(|e| EzError::RuntimeError(e.to_string()))
}
#[inline]
pub fn shader_count(&self) -> usize {
self.shader_manager.shader_count()
}
#[inline]
pub fn pipeline_count(&self) -> usize {
self.shader_manager.pipeline_count()
}
#[inline]
pub fn create_vertex_buffer<T: Copy>(
&self,
data: &[T],
) -> Result<crate::core::Buffer, EzError> {
crate::ex::helpers::create_vertex_buffer(&self.runtime.device(), data)
.map_err(|e| EzError::RuntimeError(e.to_string()))
}
#[inline]
pub fn create_index_buffer<T: Copy>(&self, data: &[T]) -> Result<crate::core::Buffer, EzError> {
crate::ex::helpers::create_index_buffer(&self.runtime.device(), data)
.map_err(|e| EzError::RuntimeError(e.to_string()))
}
#[inline]
pub fn create_uniform_buffer<T>(&self) -> Result<crate::core::Buffer, EzError> {
crate::ex::helpers::create_uniform_buffer::<T>(&self.runtime.device())
.map_err(|e| EzError::RuntimeError(e.to_string()))
}
#[inline]
pub fn create_storage_buffer(&self, size: u64) -> Result<crate::core::Buffer, EzError> {
crate::ex::helpers::create_storage_buffer(&self.runtime.device(), size)
.map_err(|e| EzError::RuntimeError(e.to_string()))
}
#[inline]
pub fn write_buffer<T: Copy>(
&self,
buffer: &crate::core::Buffer,
data: &[T],
) -> Result<(), EzError> {
crate::ex::helpers::write_buffer(&self.runtime.device(), buffer, data)
.map_err(|e| EzError::RuntimeError(e.to_string()))
}
#[inline]
pub fn create_render_target(
&self,
width: u32,
height: u32,
format: vk::Format,
) -> Result<crate::core::Image, EzError> {
crate::ex::helpers::create_render_target(&self.runtime.device(), width, height, format)
.map_err(|e| EzError::RuntimeError(e.to_string()))
}
#[inline]
pub fn create_depth_stencil(
&self,
width: u32,
height: u32,
format: vk::Format,
) -> Result<crate::core::Image, EzError> {
crate::ex::helpers::create_depth_stencil(&self.runtime.device(), width, height, format)
.map_err(|e| EzError::RuntimeError(e.to_string()))
}
#[inline]
pub fn create_texture(
&self,
width: u32,
height: u32,
format: vk::Format,
) -> Result<crate::core::Image, EzError> {
crate::ex::helpers::create_texture(&self.runtime.device(), width, height, format)
.map_err(|e| EzError::RuntimeError(e.to_string()))
}
#[inline]
pub fn create_storage_image(
&self,
width: u32,
height: u32,
format: vk::Format,
) -> Result<crate::core::Image, EzError> {
crate::ex::helpers::create_storage_image(&self.runtime.device(), width, height, format)
.map_err(|e| EzError::RuntimeError(e.to_string()))
}
}
impl Drop for EzRenderer {
fn drop(&mut self) {
let _ = self.wait_idle();
}
}
impl std::fmt::Debug for EzRenderer {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("EzRenderer")
.field("shaders", &self.shader_manager.shader_count())
.field("pipelines", &self.shader_manager.pipeline_count())
.finish()
}
}