diff --git a/src/canvas/canvas_buffer.rs b/src/canvas/canvas_buffer.rs index eaa62054..0eb3c4c2 100644 --- a/src/canvas/canvas_buffer.rs +++ b/src/canvas/canvas_buffer.rs @@ -2,7 +2,6 @@ use crate::canvas::canvas_state::{CanvasTextureHandle, CanvasImageHandle}; use vulkano::image::{ImmutableImage, AttachmentImage}; use std::sync::Arc; use vulkano::format::{Format, R8Unorm}; -use crate::canvas::canvas_shader::GenericShader; use vulkano::sampler::Sampler; use vulkano::descriptor::DescriptorSet; use vulkano::descriptor::descriptor_set::PersistentDescriptorSet; diff --git a/src/canvas/canvas_shader.rs b/src/canvas/canvas_shader.rs deleted file mode 100644 index 6a13c6be..00000000 --- a/src/canvas/canvas_shader.rs +++ /dev/null @@ -1,355 +0,0 @@ -use vulkano::device::{Device, Queue}; -use vulkano::instance::{PhysicalDevice, QueueFamily, LayerProperties}; -use vulkano::pipeline::{GraphicsPipeline, GraphicsPipelineAbstract, GraphicsPipelineBuilder}; -use std::sync::Arc; -use std::ffi::CStr; -use std::path::PathBuf; -use shade_runner as sr; -use vulkano::framebuffer::{Subpass, RenderPassAbstract, Framebuffer, FramebufferAbstract}; -use vulkano::pipeline::shader::{GraphicsShaderType, ShaderModule, SpecializationConstants, SpecializationMapEntry, GeometryShaderExecutionMode, GraphicsEntryPointAbstract, GraphicsEntryPoint, EmptyEntryPointDummy}; -use vulkano::swapchain::Capabilities; -use vulkano::pipeline::vertex::SingleBufferDefinition; -use crate::util::vertex_3d::Vertex3D; -use vulkano::pipeline::depth_stencil::{DepthStencil, Stencil, StencilOp, Compare, DepthBounds}; -use std::collections::{HashSet, HashMap}; -use shade_runner::{Layout, Output, Input}; -use vulkano::descriptor::pipeline_layout::PipelineLayout; -use std::marker::PhantomData; -use vulkano::pipeline::input_assembly::PrimitiveTopology; -use vulkano::pipeline::blend::{Blend, AttachmentsBlend}; -use vulkano::pipeline::vertex::BufferlessDefinition; - -/// Inheriting this gives private functions to grab resources -trait CompiledGraphicsPipelineResources { - fn get_paths(filename: String, types: HashSet) -> Vec<(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 paths = Vec::new(); - - for shader_type in types { - match shader_type { - ShaderType::VERTEX => { - let mut shader_path = shader_path.clone(); - shader_path.push(PathBuf::from(filename.clone() + ".vert")); - paths.push((shader_type, shader_path)); - } - ShaderType::FRAGMENT => { - let mut shader_path = shader_path.clone(); - shader_path.push(PathBuf::from(filename.clone() + ".frag")); - paths.push((shader_type, shader_path)); - } - ShaderType::GEOMETRY => { - let mut shader_path = shader_path.clone(); - shader_path.push(PathBuf::from(filename.clone() + ".geom")); - paths.push((shader_type, shader_path)); - } - ShaderType::TESSELLATION_CONTROL => { - let mut shader_path = shader_path.clone(); - shader_path.push(PathBuf::from(filename.clone() + ".tesscont")); - paths.push((shader_type, shader_path)); - } - ShaderType::TESSELLATION_EVALUATION => { - let mut shader_path = shader_path.clone(); - shader_path.push(PathBuf::from(filename.clone() + ".tesseval")); - paths.push((shader_type, shader_path)); - } - } - } - - paths - } -} - -/// Typed wrapper for a u32 handle -#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)] -pub struct CompiledGraphicsPipelineHandle { - pub handle: u32 -} - -pub trait CompiledGraphicsPipeline { - fn get_name(&self) -> String; - fn get_handle(&self) -> Arc; - fn get_pipeline(&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, -} - - -/// Typed wrapper for a u32 shader handle (index id) -#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)] -pub struct CanvasShaderHandle { - pub handle: u32 -} - -/// CanvasShader holds the pipeline and render pass for the input shader source -#[derive(Clone)] -pub struct GenericShader { - graphics_pipeline: Option>, - - handle: Arc, - name: String, - shader_types: HashSet, - - device: Arc, -} - -/// Gives CanvasShader the resource functions -impl CompiledGraphicsPipelineResources for GenericShader {} - -/// Convenience interface so we don't have to juggle shader types -impl CompiledGraphicsPipeline for GenericShader { - fn get_name(&self) -> String { - self.name.clone() - } - - fn get_handle(&self) -> Arc { - self.handle.clone() - } - - fn get_pipeline(&self) -> Arc { - self.graphics_pipeline.clone().unwrap() - } - - fn recompile(self, render_pass: Arc) -> GenericShader { - GenericShader::new(self.name, - self.device, - self.handle, - render_pass.clone()) - } -} - -/* - -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 - -*/ - - -impl GenericShader { - - pub fn ret(&self) -> Option> { - - let compiled_shader = sr::load_vertex("dfqwefqwef") - .expect("Shader didn't compile"); - - let vulkano_entry = - sr::parse(&compiled_shader) - .expect("failed to parse"); - - let shader_module: Arc = unsafe { - ShaderModule::from_words(self.device.clone(), &compiled_shader.spriv.clone()) - }.unwrap(); - - let s = ShaderType::VERTEX; - - let shader_type = match s { - ShaderType::VERTEX => { GraphicsShaderType::Vertex } - ShaderType::FRAGMENT => { GraphicsShaderType::Fragment } - ShaderType::GEOMETRY => { GraphicsShaderType::Geometry(GeometryShaderExecutionMode::Triangles) } - ShaderType::TESSELLATION_CONTROL => { GraphicsShaderType::TessellationControl } - ShaderType::TESSELLATION_EVALUATION => { GraphicsShaderType::TessellationEvaluation } - }; - - unsafe { - Some(GraphicsEntryPoint { - module: &shader_module, - name: &CStr::from_bytes_with_nul_unchecked(b"main\0"), - input: vulkano_entry.input.unwrap(), - layout: vulkano_entry.layout, - output: vulkano_entry.output.unwrap(), - ty: shader_type, - marker: PhantomData::default(), - }) - } - - } - /// This will explode when the shader does not want to compile - pub fn new(filename: String, - device: Arc, - handle: Arc, - render_pass: Arc) -> GenericShader { - - let mut shader_types : HashSet = vec![ - ShaderType::VERTEX, - ShaderType::FRAGMENT, - ].iter().cloned().collect(); - - let filenames = GenericShader::get_paths(filename.clone(), shader_types.clone()); - - - // let mut c = Vec::new(); - - let mut vertex_entry_point : Option> - = None; - let mut fragment_entry_point : Option> - = None; - - for shader in filenames { - let compiled_shader = sr::load_vertex(shader.1) - .expect("Shader didn't compile"); - - let vulkano_entry = - sr::parse(&compiled_shader) - .expect("failed to parse"); - - let shader_module: Arc = unsafe { - ShaderModule::from_words(device.clone(), &compiled_shader.spriv.clone()) - }.unwrap(); - - let shader_type = match shader.0 { - ShaderType::VERTEX => { GraphicsShaderType::Vertex } - ShaderType::FRAGMENT => { GraphicsShaderType::Fragment } - ShaderType::GEOMETRY => { GraphicsShaderType::Geometry(GeometryShaderExecutionMode::Triangles) } - ShaderType::TESSELLATION_CONTROL => { GraphicsShaderType::TessellationControl } - ShaderType::TESSELLATION_EVALUATION => { GraphicsShaderType::TessellationEvaluation } - }; - - let entry_point: Option> = unsafe { - Some(GraphicsEntryPoint { - module: &shader_module, - name: &CStr::from_bytes_with_nul_unchecked(b"main\0"), - input: vulkano_entry.input.unwrap(), - layout: vulkano_entry.layout, - output: vulkano_entry.output.unwrap(), - ty: shader_type, - marker: PhantomData::default(), - }) - }; - - let shader_type = match shader.0 { - ShaderType::VERTEX => { vertex_entry_point = Some(entry_point.clone().unwrap()) } - ShaderType::FRAGMENT => { fragment_entry_point = Some(entry_point.clone().unwrap()) } - _ => {} - }; - - // c.push((entry_point.unwrap().clone(), shader_module.clone())); - } - - let stencil = DepthStencil { - depth_compare: Compare::Less, - depth_write: true, - depth_bounds_test: DepthBounds::Disabled, - stencil_front: Stencil { - compare: Compare::Equal, - pass_op: StencilOp::IncrementAndWrap, - fail_op: StencilOp::DecrementAndClamp, - depth_fail_op: StencilOp::Keep, - compare_mask: None, - write_mask: None, - reference: None, - }, - stencil_back: Stencil { - compare: Compare::Equal, - pass_op: StencilOp::Invert, - fail_op: StencilOp::Zero, - depth_fail_op: StencilOp::Zero, - compare_mask: None, - write_mask: None, - reference: None, - }, - }; - - GenericShader { - graphics_pipeline: - Some(Arc::new(GraphicsPipeline::start() - - .vertex_input(SingleBufferDefinition::::new()) - - .vertex_shader(vertex_entry_point.unwrap(), ShaderSpecializationConstants { - first_constant: 0, - second_constant: 0, - third_constant: 0.0, - }) - - .triangle_list() - // Use a resizable viewport set to draw over the entire window - .viewports_dynamic_scissors_irrelevant(1) - - .fragment_shader(fragment_entry_point.unwrap(), ShaderSpecializationConstants { - first_constant: 0, - second_constant: 0, - third_constant: 0.0, - }) - - .depth_stencil(stencil) - - - // We have to indicate which subpass of which render pass this pipeline is going to be used - // in. The pipeline will only be usable from this particular subpass. - .render_pass(Subpass::from(render_pass.clone(), 0).unwrap()) - - .build(device.clone()) - .unwrap())), - - device: device, - handle: handle.clone(), - name: filename.clone(), - shader_types: shader_types.clone(), - } - } -} - -#[repr(C)] -#[derive(Default, Debug, Clone)] -/// Specialization constants which can be passed to the shader. Pretty much placeholder ATM -struct ShaderSpecializationConstants { - first_constant: i32, - second_constant: u32, - third_constant: f32, -} - -unsafe impl SpecializationConstants for ShaderSpecializationConstants { - fn descriptors() -> &'static [SpecializationMapEntry] { - static DESCRIPTORS: [SpecializationMapEntry; 3] = [ - SpecializationMapEntry { - constant_id: 0, - offset: 0, - size: 4, - }, - SpecializationMapEntry { - constant_id: 1, - offset: 4, - size: 4, - }, - SpecializationMapEntry { - constant_id: 2, - offset: 8, - size: 4, - }, - ]; - - &DESCRIPTORS - } -} \ No newline at end of file diff --git a/src/canvas/canvas_state.rs b/src/canvas/canvas_state.rs index 9dfa4a9c..f5295314 100644 --- a/src/canvas/canvas_state.rs +++ b/src/canvas/canvas_state.rs @@ -21,10 +21,12 @@ use vulkano::descriptor::descriptor::DescriptorDescTy::TexelBuffer; use crate::canvas::canvas_frame::CanvasFrame; use std::hash::Hash; use crate::canvas::canvas_text::{CanvasText, CanvasTextHandle}; -use crate::canvas::canvas_shader::{GenericShader, CanvasShaderHandle, CompiledGraphicsPipelineHandle, CompiledGraphicsPipeline, ShaderType}; + use crate::canvas::canvas_buffer::{CanvasImage, CanvasTexture, CanvasTextCache}; use crate::util::vertex_3d::Vertex3D; use vulkano::pipeline::depth_stencil::{StencilFaceFlags, DynamicStencilValue}; +use crate::canvas::shader::common::{CompiledGraphicsPipeline, CompiledGraphicsPipelineHandle}; +use crate::canvas::shader::generic_shader::GenericShader; /// A drawable object can be passed into a CanvasFrame to be rendered /// Very generic implementation. (N % 2 == 0) vertices, ditto for texture coords, and rgba color diff --git a/src/canvas/mod.rs b/src/canvas/mod.rs index 59e5ae2d..2566f124 100644 --- a/src/canvas/mod.rs +++ b/src/canvas/mod.rs @@ -1,10 +1,9 @@ pub mod canvas_state; pub mod canvas_frame; -pub mod canvas_shader; +pub mod shader; pub mod canvas_text; pub mod canvas_buffer; use crate::canvas::canvas_frame::CanvasFrame; -use crate::canvas::canvas_shader::GenericShader; \ No newline at end of file diff --git a/src/canvas/shader/canvas_shader.rs b/src/canvas/shader/canvas_shader.rs new file mode 100644 index 00000000..1147ec00 --- /dev/null +++ b/src/canvas/shader/canvas_shader.rs @@ -0,0 +1,22 @@ +use vulkano::device::{Device, Queue}; +use vulkano::instance::{PhysicalDevice, QueueFamily, LayerProperties}; +use vulkano::pipeline::{GraphicsPipeline, GraphicsPipelineAbstract, GraphicsPipelineBuilder}; +use std::sync::Arc; +use std::ffi::CStr; +use std::path::PathBuf; +use shade_runner as sr; +use vulkano::framebuffer::{Subpass, RenderPassAbstract, Framebuffer, FramebufferAbstract}; +use vulkano::pipeline::shader::{GraphicsShaderType, ShaderModule, SpecializationConstants, SpecializationMapEntry, GeometryShaderExecutionMode, GraphicsEntryPointAbstract, GraphicsEntryPoint, EmptyEntryPointDummy}; +use vulkano::swapchain::Capabilities; +use vulkano::pipeline::vertex::SingleBufferDefinition; +use crate::util::vertex_3d::Vertex3D; +use vulkano::pipeline::depth_stencil::{DepthStencil, Stencil, StencilOp, Compare, DepthBounds}; +use std::collections::{HashSet, HashMap}; +use shade_runner::{Layout, Output, Input, Entry}; +use vulkano::descriptor::pipeline_layout::PipelineLayout; +use std::marker::PhantomData; +use vulkano::pipeline::input_assembly::PrimitiveTopology; +use vulkano::pipeline::blend::{Blend, AttachmentsBlend}; +use vulkano::pipeline::vertex::BufferlessDefinition; + + diff --git a/src/canvas/shader/common.rs b/src/canvas/shader/common.rs new file mode 100644 index 00000000..8a929d9f --- /dev/null +++ b/src/canvas/shader/common.rs @@ -0,0 +1,93 @@ + +use std::collections::HashSet; +use std::path::PathBuf; +use vulkano::pipeline::GraphicsPipelineAbstract; +use vulkano::framebuffer::RenderPassAbstract; +use std::sync::Arc; + +/* + +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(super) trait CompiledGraphicsPipelineResources { + fn get_paths(filename: String, types: HashSet) -> Vec<(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 paths = Vec::new(); + + for shader_type in types { + match shader_type { + ShaderType::VERTEX => { + let mut shader_path = shader_path.clone(); + shader_path.push(PathBuf::from(filename.clone() + ".vert")); + paths.push((shader_type, shader_path)); + } + ShaderType::FRAGMENT => { + let mut shader_path = shader_path.clone(); + shader_path.push(PathBuf::from(filename.clone() + ".frag")); + paths.push((shader_type, shader_path)); + } + ShaderType::GEOMETRY => { + let mut shader_path = shader_path.clone(); + shader_path.push(PathBuf::from(filename.clone() + ".geom")); + paths.push((shader_type, shader_path)); + } + ShaderType::TESSELLATION_CONTROL => { + let mut shader_path = shader_path.clone(); + shader_path.push(PathBuf::from(filename.clone() + ".tesscont")); + paths.push((shader_type, shader_path)); + } + ShaderType::TESSELLATION_EVALUATION => { + let mut shader_path = shader_path.clone(); + shader_path.push(PathBuf::from(filename.clone() + ".tesseval")); + paths.push((shader_type, shader_path)); + } + } + } + + paths + } +} + +/// Typed wrapper for a u32 handle +#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)] +pub struct CompiledGraphicsPipelineHandle { + pub handle: u32 +} + +pub trait CompiledGraphicsPipeline { + fn get_name(&self) -> String; + fn get_handle(&self) -> Arc; + fn get_pipeline(&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, +} \ No newline at end of file diff --git a/src/canvas/shader/generic_shader.rs b/src/canvas/shader/generic_shader.rs new file mode 100644 index 00000000..077b6da7 --- /dev/null +++ b/src/canvas/shader/generic_shader.rs @@ -0,0 +1,183 @@ + +use vulkano::pipeline::GraphicsPipelineAbstract; +use std::sync::Arc; +use crate::canvas::shader::common::{CompiledGraphicsPipelineHandle, ShaderType, CompiledGraphicsPipeline}; +use std::collections::{HashSet, HashMap}; +use vulkano::device::Device; +use vulkano::framebuffer::{RenderPassAbstract, Subpass}; +use vulkano::pipeline::GraphicsPipeline; +use vulkano::pipeline::shader::{GraphicsEntryPoint, ShaderModule, GraphicsShaderType, GeometryShaderExecutionMode}; +use crate::canvas::shader::ShaderSpecializationConstants; +use shade_runner::{Input, Output, Layout, Entry}; +use std::ffi::CStr; +use std::marker::PhantomData; +use vulkano::pipeline::depth_stencil::{DepthStencil, Compare, DepthBounds, Stencil, StencilOp}; +use vulkano::pipeline::vertex::SingleBufferDefinition; +use crate::util::vertex_3d::Vertex3D; +use shade_runner as sr; +use crate::canvas::shader::common::CompiledGraphicsPipelineResources; + +/// CanvasShader holds the pipeline and render pass for the input shader source +#[derive(Clone)] +pub struct GenericShader { + graphics_pipeline: Option>, + + handle: Arc, + name: String, + shader_types: HashSet, + + device: Arc, +} + +/// Gives CanvasShader the resource functions +impl CompiledGraphicsPipelineResources for GenericShader {} + +/// Convenience interface so we don't have to juggle shader types +impl CompiledGraphicsPipeline for GenericShader { + fn get_name(&self) -> String { + self.name.clone() + } + + fn get_handle(&self) -> Arc { + self.handle.clone() + } + + fn get_pipeline(&self) -> Arc { + self.graphics_pipeline.clone().unwrap() + } + + fn recompile(self, render_pass: Arc) -> GenericShader { + GenericShader::new(self.name, + self.device, + self.handle, + render_pass.clone()) + } +} + +impl GenericShader { + + /// This will explode when the shader does not want to compile + pub fn new(filename: String, + device: Arc, + handle: Arc, + render_pass: Arc) -> GenericShader { + + let mut shader_types : HashSet = vec![ + ShaderType::VERTEX, + ShaderType::FRAGMENT, + ].iter().cloned().collect(); + + let filenames = GenericShader::get_paths(filename.clone(), shader_types.clone()); + + // I guess this really herky intermediate store is going to be the most flexible way to + // create these pipelines? + + let mut modules: HashMap)> = HashMap::default(); + + let mut entry_points: HashMap> = HashMap::default(); + + for shader in filenames { + let compiled_shader = sr::load_vertex(shader.1) + .expect("Shader didn't compile"); + + let vulkano_entry = + sr::parse(&compiled_shader) + .expect("failed to parse"); + + modules.insert(shader.0, (vulkano_entry, unsafe { + ShaderModule::from_words(device.clone(), &compiled_shader.spriv.clone()) + }.unwrap())); + } + + for (shader_type, (entry, module)) in modules { + + let graphics_shader_type = 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 } + }; + + let entry_point: Option> = unsafe { + Some(GraphicsEntryPoint { + module: &module, + name: &CStr::from_bytes_with_nul_unchecked(b"main\0"), + input: entry.input.unwrap(), + layout: entry.layout, + output: entry.output.unwrap(), + ty: graphics_shader_type, + marker: PhantomData::default(), + }) + }; + + entry_points.insert(shader_type, entry_point.unwrap().to_owned()); + } + + let stencil = DepthStencil { + depth_compare: Compare::Less, + depth_write: true, + depth_bounds_test: DepthBounds::Disabled, + stencil_front: Stencil { + compare: Compare::Equal, + pass_op: StencilOp::IncrementAndWrap, + fail_op: StencilOp::DecrementAndClamp, + depth_fail_op: StencilOp::Keep, + compare_mask: None, + write_mask: None, + reference: None, + }, + stencil_back: Stencil { + compare: Compare::Equal, + pass_op: StencilOp::Invert, + fail_op: StencilOp::Zero, + depth_fail_op: StencilOp::Zero, + compare_mask: None, + write_mask: None, + reference: None, + }, + }; + + GenericShader { + graphics_pipeline: + Some(Arc::new(GraphicsPipeline::start() + + .vertex_input(SingleBufferDefinition::::new()) + + .vertex_shader(entry_points.get(&ShaderType::VERTEX).unwrap().clone(), ShaderSpecializationConstants { + first_constant: 0, + second_constant: 0, + third_constant: 0.0, + }) + + .triangle_list() + // Use a resizable viewport set to draw over the entire window + .viewports_dynamic_scissors_irrelevant(1) + + .fragment_shader(entry_points.get(&ShaderType::VERTEX).unwrap().clone(), ShaderSpecializationConstants { + first_constant: 0, + second_constant: 0, + third_constant: 0.0, + }) + + .depth_stencil(stencil) + + + // We have to indicate which subpass of which render pass this pipeline is going to be used + // in. The pipeline will only be usable from this particular subpass. + .render_pass(Subpass::from(render_pass.clone(), 0).unwrap()) + + .build(device.clone()) + .unwrap())), + + device: device, + handle: handle.clone(), + name: filename.clone(), + shader_types: shader_types.clone(), + } + } +} \ No newline at end of file diff --git a/src/canvas/shader/mod.rs b/src/canvas/shader/mod.rs new file mode 100644 index 00000000..da635645 --- /dev/null +++ b/src/canvas/shader/mod.rs @@ -0,0 +1,47 @@ +use crate::canvas::shader::common::CompiledGraphicsPipeline; + +pub mod canvas_shader; +pub mod common; +pub mod generic_shader; +pub mod text_shader; + +use crate::canvas::shader::common::*; +use crate::canvas::shader::generic_shader::*; +use crate::canvas::shader::text_shader::*; + + + +use vulkano::pipeline::shader::{SpecializationConstants, SpecializationMapEntry}; + +#[repr(C)] +#[derive(Default, Debug, Clone)] +/// Specialization constants which can be passed to the shader. Pretty much placeholder ATM +struct ShaderSpecializationConstants { + first_constant: i32, + second_constant: u32, + third_constant: f32, +} + +unsafe impl SpecializationConstants for ShaderSpecializationConstants { + fn descriptors() -> &'static [SpecializationMapEntry] { + static DESCRIPTORS: [SpecializationMapEntry; 3] = [ + SpecializationMapEntry { + constant_id: 0, + offset: 0, + size: 4, + }, + SpecializationMapEntry { + constant_id: 1, + offset: 4, + size: 4, + }, + SpecializationMapEntry { + constant_id: 2, + offset: 8, + size: 4, + }, + ]; + + &DESCRIPTORS + } +} \ No newline at end of file diff --git a/src/canvas/shader/text_shader.rs b/src/canvas/shader/text_shader.rs new file mode 100644 index 00000000..bbacaa0c --- /dev/null +++ b/src/canvas/shader/text_shader.rs @@ -0,0 +1,180 @@ +use vulkano::pipeline::GraphicsPipelineAbstract; +use std::sync::Arc; +use crate::canvas::shader::common::{CompiledGraphicsPipelineHandle, ShaderType, CompiledGraphicsPipeline, CompiledGraphicsPipelineResources}; +use std::collections::{HashSet, HashMap}; +use vulkano::device::Device; +use vulkano::framebuffer::{RenderPassAbstract, Subpass}; +use vulkano::pipeline::GraphicsPipeline; +use vulkano::pipeline::shader::{GraphicsEntryPoint, ShaderModule, GraphicsShaderType, GeometryShaderExecutionMode}; +use crate::canvas::shader::ShaderSpecializationConstants; +use shade_runner::{Input, Output, Layout, Entry}; +use std::ffi::CStr; +use std::marker::PhantomData; +use vulkano::pipeline::depth_stencil::{DepthStencil, Compare, DepthBounds, Stencil, StencilOp}; +use vulkano::pipeline::vertex::SingleBufferDefinition; +use crate::util::vertex_3d::Vertex3D; +use crate::canvas::shader::generic_shader::GenericShader; +use shade_runner as sr; + +/// CanvasShader holds the pipeline and render pass for the input shader source +#[derive(Clone)] +pub struct TextShader { + graphics_pipeline: Option>, + + handle: Arc, + name: String, + shader_types: HashSet, + + device: Arc, +} + +/// Gives CanvasShader the resource functions +impl CompiledGraphicsPipelineResources for TextShader {} + +/// Convenience interface so we don't have to juggle shader types +impl CompiledGraphicsPipeline for TextShader { + fn get_name(&self) -> String { + self.name.clone() + } + + fn get_handle(&self) -> Arc { + self.handle.clone() + } + + fn get_pipeline(&self) -> Arc { + self.graphics_pipeline.clone().unwrap() + } + + fn recompile(self, render_pass: Arc) -> TextShader { + TextShader::new(self.name, + self.device, + self.handle, + render_pass.clone()) + } +} + +impl TextShader { + + /// This will explode when the shader does not want to compile + pub fn new(filename: String, + device: Arc, + handle: Arc, + render_pass: Arc) -> TextShader { + let mut shader_types: HashSet = vec![ + ShaderType::VERTEX, + ShaderType::FRAGMENT, + ].iter().cloned().collect(); + + let filenames = GenericShader::get_paths(filename.clone(), shader_types.clone()); + + // I guess this really herky intermediate store is going to be the most flexible way to + // create these pipelines? + + let mut modules: HashMap)> = HashMap::default(); + + let mut entry_points: HashMap> = HashMap::default(); + + for shader in filenames { + let compiled_shader = sr::load_vertex(shader.1) + .expect("Shader didn't compile"); + + let vulkano_entry = + sr::parse(&compiled_shader) + .expect("failed to parse"); + + modules.insert(shader.0, (vulkano_entry, unsafe { + ShaderModule::from_words(device.clone(), &compiled_shader.spriv.clone()) + }.unwrap())); + } + + for (shader_type, (entry, module)) in modules { + let graphics_shader_type = 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 } + }; + + let entry_point: Option> = unsafe { + Some(GraphicsEntryPoint { + module: &module, + name: &CStr::from_bytes_with_nul_unchecked(b"main\0"), + input: entry.input.unwrap(), + layout: entry.layout, + output: entry.output.unwrap(), + ty: graphics_shader_type, + marker: PhantomData::default(), + }) + }; + + entry_points.insert(shader_type, entry_point.unwrap().clone()); + } + + let stencil = DepthStencil { + depth_compare: Compare::Less, + depth_write: true, + depth_bounds_test: DepthBounds::Disabled, + stencil_front: Stencil { + compare: Compare::Equal, + pass_op: StencilOp::IncrementAndWrap, + fail_op: StencilOp::DecrementAndClamp, + depth_fail_op: StencilOp::Keep, + compare_mask: None, + write_mask: None, + reference: None, + }, + stencil_back: Stencil { + compare: Compare::Equal, + pass_op: StencilOp::Invert, + fail_op: StencilOp::Zero, + depth_fail_op: StencilOp::Zero, + compare_mask: None, + write_mask: None, + reference: None, + }, + }; + + TextShader { + graphics_pipeline: + Some(Arc::new(GraphicsPipeline::start() + + .vertex_input(SingleBufferDefinition::::new()) + + .vertex_shader(entry_points.get(&ShaderType::VERTEX).unwrap().clone(), ShaderSpecializationConstants { + first_constant: 0, + second_constant: 0, + third_constant: 0.0, + }) + + .triangle_list() + // Use a resizable viewport set to draw over the entire window + .viewports_dynamic_scissors_irrelevant(1) + + .fragment_shader(entry_points.get(&ShaderType::VERTEX).unwrap().clone(), ShaderSpecializationConstants { + first_constant: 0, + second_constant: 0, + third_constant: 0.0, + }) + + .depth_stencil(stencil) + + + // We have to indicate which subpass of which render pass this pipeline is going to be used + // in. The pipeline will only be usable from this particular subpass. + .render_pass(Subpass::from(render_pass.clone(), 0).unwrap()) + + .build(device.clone()) + .unwrap())), + + device: device, + handle: handle.clone(), + name: filename.clone(), + shader_types: shader_types.clone(), + } + } +} \ No newline at end of file diff --git a/src/vkprocessor.rs b/src/vkprocessor.rs index 3c37efa4..67825776 100644 --- a/src/vkprocessor.rs +++ b/src/vkprocessor.rs @@ -16,8 +16,8 @@ use crate::canvas::canvas_frame::CanvasFrame; use crate::compute::compu_kernel::{CompuKernel, CompuKernelHandle}; use crate::compute::compu_buffer::{CompuBuffers, CompuBufferHandle}; use std::time::Duration; -use crate::canvas::canvas_shader::{CanvasShaderHandle, CompiledGraphicsPipeline, CompiledGraphicsPipelineHandle, ShaderType}; use vulkano::pipeline::depth_stencil::{DynamicStencilValue, StencilFaceFlags}; +use crate::canvas::shader::common::CompiledGraphicsPipelineHandle; /// VKProcessor holds the vulkan instance information, the swapchain, and the compute and canvas states ///