breaking out the shader

master
mitchellhansen 5 years ago
parent 5a888a4163
commit 1a247e482a

@ -2,7 +2,6 @@ use crate::canvas::canvas_state::{CanvasTextureHandle, CanvasImageHandle};
use vulkano::image::{ImmutableImage, AttachmentImage}; use vulkano::image::{ImmutableImage, AttachmentImage};
use std::sync::Arc; use std::sync::Arc;
use vulkano::format::{Format, R8Unorm}; use vulkano::format::{Format, R8Unorm};
use crate::canvas::canvas_shader::GenericShader;
use vulkano::sampler::Sampler; use vulkano::sampler::Sampler;
use vulkano::descriptor::DescriptorSet; use vulkano::descriptor::DescriptorSet;
use vulkano::descriptor::descriptor_set::PersistentDescriptorSet; use vulkano::descriptor::descriptor_set::PersistentDescriptorSet;

@ -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<ShaderType>) -> 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<CompiledGraphicsPipelineHandle>;
fn get_pipeline(&self) -> Arc<dyn GraphicsPipelineAbstract + Sync + Send>;
fn recompile(self, render_pass: Arc<dyn RenderPassAbstract + Send + Sync>)
-> 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<Arc<dyn GraphicsPipelineAbstract + Sync + Send>>,
handle: Arc<CompiledGraphicsPipelineHandle>,
name: String,
shader_types: HashSet<ShaderType>,
device: Arc<Device>,
}
/// 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<CompiledGraphicsPipelineHandle> {
self.handle.clone()
}
fn get_pipeline(&self) -> Arc<dyn GraphicsPipelineAbstract + Sync + Send> {
self.graphics_pipeline.clone().unwrap()
}
fn recompile(self, render_pass: Arc<dyn RenderPassAbstract + Send + Sync>) -> 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<GraphicsEntryPoint<
ShaderSpecializationConstants,
Input,
Output,
Layout>> {
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<ShaderModule> = 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<Device>,
handle: Arc<CompiledGraphicsPipelineHandle>,
render_pass: Arc<dyn RenderPassAbstract + Send + Sync>) -> GenericShader {
let mut shader_types : HashSet<ShaderType> = 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<GraphicsEntryPoint<ShaderSpecializationConstants,Input,Output,Layout>>
= None;
let mut fragment_entry_point : Option<GraphicsEntryPoint<ShaderSpecializationConstants,Input,Output,Layout>>
= 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<ShaderModule> = 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<GraphicsEntryPoint<
ShaderSpecializationConstants,
Input,
Output,
Layout>> = 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::<Vertex3D>::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
}
}

@ -21,10 +21,12 @@ use vulkano::descriptor::descriptor::DescriptorDescTy::TexelBuffer;
use crate::canvas::canvas_frame::CanvasFrame; use crate::canvas::canvas_frame::CanvasFrame;
use std::hash::Hash; use std::hash::Hash;
use crate::canvas::canvas_text::{CanvasText, CanvasTextHandle}; 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::canvas::canvas_buffer::{CanvasImage, CanvasTexture, CanvasTextCache};
use crate::util::vertex_3d::Vertex3D; use crate::util::vertex_3d::Vertex3D;
use vulkano::pipeline::depth_stencil::{StencilFaceFlags, DynamicStencilValue}; 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 /// 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 /// Very generic implementation. (N % 2 == 0) vertices, ditto for texture coords, and rgba color

@ -1,10 +1,9 @@
pub mod canvas_state; pub mod canvas_state;
pub mod canvas_frame; pub mod canvas_frame;
pub mod canvas_shader; pub mod shader;
pub mod canvas_text; pub mod canvas_text;
pub mod canvas_buffer; pub mod canvas_buffer;
use crate::canvas::canvas_frame::CanvasFrame; use crate::canvas::canvas_frame::CanvasFrame;
use crate::canvas::canvas_shader::GenericShader;

@ -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;

@ -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<ShaderType>) -> 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<CompiledGraphicsPipelineHandle>;
fn get_pipeline(&self) -> Arc<dyn GraphicsPipelineAbstract + Sync + Send>;
fn recompile(self, render_pass: Arc<dyn RenderPassAbstract + Send + Sync>)
-> 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,
}

@ -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<Arc<dyn GraphicsPipelineAbstract + Sync + Send>>,
handle: Arc<CompiledGraphicsPipelineHandle>,
name: String,
shader_types: HashSet<ShaderType>,
device: Arc<Device>,
}
/// 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<CompiledGraphicsPipelineHandle> {
self.handle.clone()
}
fn get_pipeline(&self) -> Arc<dyn GraphicsPipelineAbstract + Sync + Send> {
self.graphics_pipeline.clone().unwrap()
}
fn recompile(self, render_pass: Arc<dyn RenderPassAbstract + Send + Sync>) -> 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<Device>,
handle: Arc<CompiledGraphicsPipelineHandle>,
render_pass: Arc<dyn RenderPassAbstract + Send + Sync>) -> GenericShader {
let mut shader_types : HashSet<ShaderType> = 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<ShaderType, (Entry, Arc<ShaderModule>)> = HashMap::default();
let mut entry_points: HashMap<ShaderType, GraphicsEntryPoint<ShaderSpecializationConstants,Input,Output,Layout>> = 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<GraphicsEntryPoint<
ShaderSpecializationConstants,
Input,
Output,
Layout>> = 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::<Vertex3D>::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(),
}
}
}

@ -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
}
}

@ -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<Arc<dyn GraphicsPipelineAbstract + Sync + Send>>,
handle: Arc<CompiledGraphicsPipelineHandle>,
name: String,
shader_types: HashSet<ShaderType>,
device: Arc<Device>,
}
/// 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<CompiledGraphicsPipelineHandle> {
self.handle.clone()
}
fn get_pipeline(&self) -> Arc<dyn GraphicsPipelineAbstract + Sync + Send> {
self.graphics_pipeline.clone().unwrap()
}
fn recompile(self, render_pass: Arc<dyn RenderPassAbstract + Send + Sync>) -> 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<Device>,
handle: Arc<CompiledGraphicsPipelineHandle>,
render_pass: Arc<dyn RenderPassAbstract + Send + Sync>) -> TextShader {
let mut shader_types: HashSet<ShaderType> = 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<ShaderType, (Entry, Arc<ShaderModule>)> = HashMap::default();
let mut entry_points: HashMap<ShaderType, GraphicsEntryPoint<ShaderSpecializationConstants, Input, Output, Layout>> = 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<GraphicsEntryPoint<
ShaderSpecializationConstants,
Input,
Output,
Layout>> = 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::<Vertex3D>::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(),
}
}
}

@ -16,8 +16,8 @@ use crate::canvas::canvas_frame::CanvasFrame;
use crate::compute::compu_kernel::{CompuKernel, CompuKernelHandle}; use crate::compute::compu_kernel::{CompuKernel, CompuKernelHandle};
use crate::compute::compu_buffer::{CompuBuffers, CompuBufferHandle}; use crate::compute::compu_buffer::{CompuBuffers, CompuBufferHandle};
use std::time::Duration; use std::time::Duration;
use crate::canvas::canvas_shader::{CanvasShaderHandle, CompiledGraphicsPipeline, CompiledGraphicsPipelineHandle, ShaderType};
use vulkano::pipeline::depth_stencil::{DynamicStencilValue, StencilFaceFlags}; 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 /// VKProcessor holds the vulkan instance information, the swapchain, and the compute and canvas states
/// ///

Loading…
Cancel
Save