use std::collections::HashSet; use std::path::PathBuf; use vulkano::pipeline::GraphicsPipelineAbstract; use vulkano::framebuffer::RenderPassAbstract; use std::sync::Arc; use vulkano::pipeline::shader::{ShaderModule, GraphicsShaderType, GeometryShaderExecutionMode}; use vulkano::device::Device; use shade_runner::Entry; use shaderc::ShaderKind; use crate::canvas::managed::handles::CompiledShaderHandle; use vulkano::pipeline::vertex::Vertex; /* Realistically, what should the API for this thing look like... It's going to just generate a pipeline. But that consists of loading and compiling various shaders, and generating a pipeline for those shaders and other customer behaviour. This best works I think if I allow users to A.) impl from a base trait which allows resource lookup B.) Generate 1 of each of the types of shaders C.) Modify specilization constants, whatever that might mean D.) impl from a base trait which defines it's interface */ /// Inheriting this gives private functions to grab resources pub trait CompiledShaderResources { fn get_path(filename: String, shader_type: ShaderType) -> PathBuf { let project_root = std::env::current_dir() .expect("failed to get root directory"); let mut shader_path = project_root.clone(); shader_path.push(PathBuf::from("resources/shaders/")); let mut shader_path = shader_path.clone(); match shader_type { ShaderType::VERTEX => { shader_path.push(PathBuf::from(filename.clone() + ".vert")); } ShaderType::FRAGMENT => { shader_path.push(PathBuf::from(filename.clone() + ".frag")); } ShaderType::GEOMETRY => { shader_path.push(PathBuf::from(filename.clone() + ".geom")); } ShaderType::TESSELLATION_CONTROL => { shader_path.push(PathBuf::from(filename.clone() + ".tesscont")); } ShaderType::TESSELLATION_EVALUATION => { shader_path.push(PathBuf::from(filename.clone() + ".tesseval")); } } shader_path } fn compile(filepath: PathBuf, device: Arc, shader_type: ShaderType) -> (Entry, Arc) { let compiled_shader = shade_runner::load(filepath, Self::convert_sr(shader_type)) .expect("Shader didn't compile"); let vulkano_entry = shade_runner::parse(&compiled_shader) .expect("failed to parse"); (vulkano_entry, unsafe { ShaderModule::from_words(device.clone(), &compiled_shader.spriv.clone()) }.unwrap()) } fn convert_vk(shader_type: ShaderType) -> GraphicsShaderType { match shader_type { ShaderType::VERTEX => { GraphicsShaderType::Vertex } ShaderType::FRAGMENT => { GraphicsShaderType::Fragment } ShaderType::GEOMETRY => { GraphicsShaderType::Geometry(GeometryShaderExecutionMode::Triangles) } ShaderType::TESSELLATION_CONTROL => { GraphicsShaderType::TessellationControl } ShaderType::TESSELLATION_EVALUATION => { GraphicsShaderType::TessellationEvaluation } } } fn convert_sr(shader_type: ShaderType) -> ShaderKind { match shader_type { ShaderType::VERTEX => { ShaderKind::Vertex } ShaderType::FRAGMENT => { ShaderKind::Fragment } ShaderType::GEOMETRY => { ShaderKind::Geometry } ShaderType::TESSELLATION_CONTROL => { ShaderKind::TessControl } ShaderType::TESSELLATION_EVALUATION => { ShaderKind::TessEvaluation } } } } pub trait CompiledShader { fn new(filename: String, device: Arc, handle: Arc, render_pass: Arc) -> Self where Self: Sized, V: Vertex,; fn get_name(&self) -> String; fn get_handle(&self) -> Arc; fn get_pipeline(&self) -> Arc; fn get_renderpass(&self) -> Arc; fn recompile(self, render_pass: Arc) -> Self where Self: Sized; } /// Legacy ShaderType enum for single type shaders. #[derive(PartialEq, Eq, Hash, Clone)] pub enum ShaderType { VERTEX = 0, FRAGMENT = 1, GEOMETRY = 2, TESSELLATION_CONTROL = 3, TESSELLATION_EVALUATION = 4, }