initial commit

master
Tom Gowan 6 years ago
commit 0755b4f807

3
.gitignore vendored

@ -0,0 +1,3 @@
/target
**/*.rs.bk
Cargo.lock

@ -0,0 +1,19 @@
[package]
name = "shade_runner"
version = "0.1.0"
authors = ["Tom Gowan <tomrgowan@gmail.com>"]
edition = "2018"
[dependencies]
notify = "4"
shaderc = "0.3"
spirv-reflect = "0.2"
vulkano = "0.11"
[dev-dependencies]
vulkano = "0.11"
color-backtrace = "0.1"
difference = "2"
[patch.crates-io]
vulkano = { git = "https://github.com/mitchmindtree/vulkano", branch = "nannou_patches" }

@ -0,0 +1,13 @@
use shade_runner as sr;
use std::path::PathBuf;
fn main() {
let project_root = std::env::current_dir().expect("failed to get root directory");
let mut vert_path = project_root.clone();
vert_path.push(PathBuf::from("examples/shaders/vert.glsl"));
let mut frag_path = project_root.clone();
frag_path.push(PathBuf::from("examples/shaders/frag.glsl"));
let shader = sr::load(vert_path, frag_path).expect("Failed to compile");
let vulkano_entry = sr::parse(&shader);
dbg!(vulkano_entry);
}

@ -0,0 +1,7 @@
#version 450
layout(location = 0) out vec4 f_color;
void main() {
f_color = vec4(0.0, 0.5, 1.0, 1.0);
}

@ -0,0 +1,10 @@
#version 450
layout(location = 0) in vec2 position;
void main() {
vec2 p = position;
p.x += 0.2;
gl_Position = vec4(p, 0.0, 1.0);
}

@ -0,0 +1,26 @@
use std::fs::File;
use std::path::Path;
use shaderc::ShaderKind;
use std::io::Read;
pub fn compile<T>(path: T, shader_kind: ShaderKind) -> shaderc::Result<Vec<u32>>
where
T: AsRef<Path>,
{
// TODO Probably shouldn't create this every time.
let mut compiler = shaderc::Compiler::new().expect("failed to create compiler");
let mut f = File::open(&path).expect("failed to open shader src");
let mut src = String::new();
f.read_to_string(&mut src).expect("failed to read src");
let mut options = shaderc::CompileOptions::new().unwrap();
options.add_macro_definition("EP", Some("main"));
let result = compiler.compile_into_spirv(
src.as_str(),
shader_kind,
path.as_ref().to_str().expect("failed to make path string"),
"main",
None,
)?;
let data = result.as_binary();
Ok(data.to_owned())
}

@ -0,0 +1,4 @@
#[derive(Debug)]
pub enum Error {
Compile(shaderc::Error),
}

@ -0,0 +1,125 @@
use crate::vk;
use vk::pipeline::shader::*;
use vk::descriptor::descriptor::*;
use vk::descriptor::pipeline_layout::*;
use crate::reflection::LayoutData;
#[derive(Debug, Clone)]
pub struct Entry {
pub frag_input: FragInput,
pub frag_output: FragOutput,
pub frag_layout: FragLayout,
pub vert_input: VertInput,
pub vert_output: VertOutput,
pub vert_layout: VertLayout,
}
#[derive(Debug, Clone)]
pub struct FragInput {
pub inputs: Vec<ShaderInterfaceDefEntry>,
}
unsafe impl ShaderInterfaceDef for FragInput {
type Iter = FragInputIter;
fn elements(&self) -> FragInputIter {
self.inputs.clone().into_iter()
}
}
pub type FragInputIter = std::vec::IntoIter<ShaderInterfaceDefEntry>;
#[derive(Debug, Clone)]
pub struct FragOutput {
pub outputs: Vec<ShaderInterfaceDefEntry>,
}
unsafe impl ShaderInterfaceDef for FragOutput {
type Iter = FragOutputIter;
fn elements(&self) -> FragOutputIter {
self.outputs.clone().into_iter()
}
}
pub type FragOutputIter = std::vec::IntoIter<ShaderInterfaceDefEntry>;
// Layout same as with vertex shader.
#[derive(Debug, Clone)]
pub struct FragLayout {
pub stages: ShaderStages,
pub layout_data: LayoutData,
}
unsafe impl PipelineLayoutDesc for FragLayout {
fn num_sets(&self) -> usize {
self.layout_data.num_sets
}
fn num_bindings_in_set(&self, set: usize) -> Option<usize> {
self.layout_data.num_bindings.get(&set).map(|&i| i)
}
fn descriptor(&self, _set: usize, _binding: usize) -> Option<DescriptorDesc> {
None
}
fn num_push_constants_ranges(&self) -> usize {
0
}
fn push_constants_range(&self, _num: usize) -> Option<PipelineLayoutDescPcRange> {
None
}
}
#[derive(Debug, Clone)]
pub struct VertInput {
pub inputs: Vec<ShaderInterfaceDefEntry>,
}
unsafe impl ShaderInterfaceDef for VertInput {
type Iter = VertInputIter;
fn elements(&self) -> VertInputIter {
self.inputs.clone().into_iter()
}
}
pub type VertInputIter = std::vec::IntoIter<ShaderInterfaceDefEntry>;
#[derive(Debug, Clone)]
pub struct VertOutput {
pub outputs: Vec<ShaderInterfaceDefEntry>,
}
unsafe impl ShaderInterfaceDef for VertOutput {
type Iter = VertOutputIter;
fn elements(&self) -> VertOutputIter {
self.outputs.clone().into_iter()
}
}
pub type VertOutputIter = std::vec::IntoIter<ShaderInterfaceDefEntry>;
// This structure describes layout of this stage.
#[derive(Debug, Copy, Clone)]
pub struct VertLayout(pub ShaderStages);
unsafe impl PipelineLayoutDesc for VertLayout {
// Number of descriptor sets it takes.
fn num_sets(&self) -> usize {
0
}
// Number of entries (bindings) in each set.
fn num_bindings_in_set(&self, _set: usize) -> Option<usize> {
None
}
// Descriptor descriptions.
fn descriptor(&self, _set: usize, _binding: usize) -> Option<DescriptorDesc> {
None
}
// Number of push constants ranges (think: number of push constants).
fn num_push_constants_ranges(&self) -> usize {
0
}
// Each push constant range in memory.
fn push_constants_range(&self, _num: usize) -> Option<PipelineLayoutDescPcRange> {
None
}
}

@ -0,0 +1,32 @@
mod compiler;
mod error;
mod reflection;
mod srvk;
mod layouts;
pub use layouts::*;
pub use reflection::LayoutData;
use spirv_reflect as sr;
use vulkano as vk;
use std::path::Path;
use error::Error;
use shaderc::ShaderKind;
pub struct CompiledShaders {
pub vertex: Vec<u32>,
pub fragment: Vec<u32>,
}
pub fn load<T>(vertex: T, fragment: T) -> Result<CompiledShaders, Error>
where
T: AsRef<Path>,
{
let vertex = compiler::compile(vertex, ShaderKind::Vertex).map_err(|e| Error::Compile(e))?;
let fragment = compiler::compile(fragment, ShaderKind::Fragment).map_err(|e| Error::Compile(e))?;
Ok(CompiledShaders{ vertex, fragment })
}
pub fn parse(code: &CompiledShaders) -> Entry {
reflection::create_entry(code)
}

@ -0,0 +1,90 @@
use crate::sr;
use crate::srvk::SpirvTy;
use std::borrow::Cow;
use crate::vk::pipeline::shader::ShaderInterfaceDefEntry;
use crate::vk::descriptor::descriptor::ShaderStages;
use std::collections::HashMap;
use crate::CompiledShaders;
use crate::layouts::*;
pub struct ShaderInterfaces {
pub inputs: Vec<ShaderInterfaceDefEntry>,
pub outputs: Vec<ShaderInterfaceDefEntry>,
}
#[derive(Debug, Clone, Default)]
pub struct LayoutData {
pub num_sets: usize,
pub num_bindings: HashMap<usize, usize>,
}
pub fn create_entry(shaders: &CompiledShaders) -> Entry {
let vertex_interfaces = create_interfaces(&shaders.vertex);
let fragment_interfaces = create_interfaces(&shaders.fragment);
let frag_input = FragInput{ inputs: fragment_interfaces.inputs };
let frag_output = FragOutput{ outputs: fragment_interfaces.outputs };
let frag_layout = FragLayout {
stages: ShaderStages {
fragment: true,
..ShaderStages::none()
},
layout_data: Default::default(),
};
let vert_input = VertInput{ inputs: vertex_interfaces.inputs };
let vert_output = VertOutput{ outputs: vertex_interfaces.outputs };
let vert_layout = VertLayout(ShaderStages {
vertex: true,
..ShaderStages::none()
});
Entry {
frag_input,
frag_output,
vert_input,
vert_output,
frag_layout,
vert_layout,
}
}
fn create_interfaces(data: &[u32]) -> ShaderInterfaces {
sr::ShaderModule::load_u32_data(data)
.map(|m| {
let inputs = m
.enumerate_input_variables(None)
.map(|inputs| {
inputs
.iter()
.filter(|i| {
!i.decoration_flags
.contains(sr::types::ReflectDecorationFlags::BUILT_IN)
})
.map(|i| ShaderInterfaceDefEntry {
location: i.location..(i.location + 1),
format: SpirvTy::from(i.format).inner(),
name: Some(Cow::from(i.name.clone())),
})
.collect::<Vec<ShaderInterfaceDefEntry>>()
})
.expect("Failed to pass inputs");
let outputs = m
.enumerate_output_variables(None)
.map(|outputs| {
outputs
.iter()
.filter(|i| {
!i.decoration_flags
.contains(sr::types::ReflectDecorationFlags::BUILT_IN)
})
.map(|i| ShaderInterfaceDefEntry {
location: i.location..(i.location + 1),
format: SpirvTy::from(i.format).inner(),
name: Some(Cow::from(i.name.clone())),
})
.collect::<Vec<ShaderInterfaceDefEntry>>()
})
.expect("Failed to pass outputs");
ShaderInterfaces { inputs, outputs }
})
.expect("failed to load module")
}

@ -0,0 +1,100 @@
use crate::sr;
use crate::vk;
use vk::descriptor::descriptor::*;
use vk::pipeline::shader::ShaderInterfaceDefEntry;
use vk::format::Format;
pub struct SpirvTy<T> {
inner: T,
}
pub struct DescriptorDescInfo {
descriptor_type: sr::types::ReflectDescriptorType,
image: sr::types::ReflectImageTraits,
}
impl<T> SpirvTy<T> {
pub fn inner(self) -> T {
self.inner
}
}
impl From<DescriptorDescInfo> for SpirvTy<DescriptorDescTy> {
fn from(d: DescriptorDescInfo) -> Self {
use sr::types::ReflectDescriptorType as SR;
use DescriptorDescTy as VK;
let t = match d.descriptor_type {
SR::Undefined => unreachable!(),
SR::Sampler => VK::Sampler,
SR::CombinedImageSampler => VK::CombinedImageSampler(SpirvTy::from(d.image).inner()),
SR::SampledImage => unreachable!(),
SR::StorageImage => unreachable!(),
SR::UniformTexelBuffer => unreachable!(),
SR::StorageTexelBuffer => unreachable!(),
SR::UniformBuffer => unreachable!(),
SR::StorageBuffer => unreachable!(),
SR::UniformBufferDynamic => unreachable!(),
SR::StorageBufferDynamic => unreachable!(),
SR::InputAttachment => unreachable!(),
SR::AccelerationStructureNV => unreachable!(),
};
SpirvTy {
inner: t,
}
}
}
impl From<sr::types::ReflectImageTraits> for SpirvTy<DescriptorImageDesc> {
fn from(d: sr::types::ReflectImageTraits) -> Self {
let conv_array_layers = |a, d|{
if a != 0 {
DescriptorImageDescArray::Arrayed{max_layers: Some(d)}
} else {
DescriptorImageDescArray::NonArrayed
}
};
let t = DescriptorImageDesc {
sampled: d.sampled != 0,
dimensions: SpirvTy::from(d.dim).inner(),
format: Some(SpirvTy::from(d.image_format).inner()),
multisampled: d.ms != 0,
array_layers: conv_array_layers(d.arrayed, d.depth),
};
SpirvTy{inner: t}
}
}
impl From<sr::types::variable::ReflectDimension> for SpirvTy<DescriptorImageDescDimensions> {
fn from(d: sr::types::variable::ReflectDimension) -> Self {
unimplemented!()
}
}
impl From<sr::types::image::ReflectImageFormat> for SpirvTy<Format> {
fn from(d: sr::types::image::ReflectImageFormat) -> Self {
unimplemented!()
}
}
impl From<sr::types::ReflectFormat> for SpirvTy<Format> {
fn from(f: sr::types::ReflectFormat) -> Self {
use sr::types::ReflectFormat::*;
use Format::*;
let t = match f {
Undefined => unreachable!(),
R32_UINT => R32Uint,
R32_SINT => R32Sint,
R32_SFLOAT => R32Sfloat,
R32G32_UINT => R32G32Uint,
R32G32_SINT => R32G32Sint,
R32G32_SFLOAT => R32G32Sfloat,
R32G32B32_UINT => R32G32B32Uint,
R32G32B32_SINT => R32G32B32Sint,
R32G32B32_SFLOAT => R32G32B32Sfloat,
R32G32B32A32_UINT => R32G32B32A32Uint,
R32G32B32A32_SINT => R32G32B32A32Sint,
R32G32B32A32_SFLOAT => R32G32B32A32Sfloat,
};
SpirvTy { inner: t }
}
}

@ -0,0 +1,7 @@
#version 450
layout(location = 0) out vec4 f_color;
void main() {
f_color = vec4(0.0, 0.5, 1.0, 1.0);
}

@ -0,0 +1,13 @@
#version 450
layout(location = 0) in vec4 cool;
layout(location = 1) in vec2 yep;
layout(location = 2) in float monkey;
layout(location = 0) out vec4 f_color;
void main() {
vec4 t = cool;
t.yw += yep;
t.x -= monkey;
f_color = t;
}

@ -0,0 +1,10 @@
#version 450
layout(location = 0) in vec2 position;
void main() {
vec2 p = position;
p.x += 0.2;
gl_Position = vec4(p, 0.0, 1.0);
}

@ -0,0 +1,14 @@
#version 450
layout(location = 0) in vec2 position;
layout(location = 0) out vec4 cool;
layout(location = 1) out vec2 yep;
layout(location = 2) out float monkey;
void main() {
cool = vec4(0.0, 0.5, 1.0, 1.0);
yep = position;
monkey = 0.9;
gl_Position = vec4(yep, 0.0, 1.0);
}

@ -0,0 +1,178 @@
use color_backtrace;
use difference::{Changeset, Difference};
use shade_runner::*;
use std::borrow::Cow;
use std::collections::HashMap;
use std::path::{Path, PathBuf};
use vulkano::descriptor::descriptor::ShaderStages;
use vulkano::format::*;
use vulkano::pipeline::shader::ShaderInterfaceDefEntry;
fn setup() {
color_backtrace::install();
}
fn difference(e: &str, t: &str) -> String {
let diffs = Changeset::new(&e, &t, "");
diffs
.diffs
.iter()
.filter(|d| match d {
Difference::Add(_) => true,
Difference::Rem(_) => true,
_ => false,
})
.map(|d| match d {
Difference::Add(a) => format!("add: {}", a),
Difference::Rem(a) => format!("remove: {}", a),
_ => "".to_string(),
})
.collect::<Vec<String>>()
.join("\n")
}
fn parse<T>(vertex: T, fragment: T) -> shade_runner::Entry
where
T: AsRef<Path>,
{
let project_root = std::env::current_dir().expect("failed to get root directory");
let mut path = project_root.clone();
path.push(PathBuf::from("tests/shaders/"));
let mut vertex_path = path.clone();
vertex_path.push(vertex);
let mut fragment_path = path.clone();
fragment_path.push(fragment);
let shader = shade_runner::load(vertex_path, fragment_path).expect("Failed to compile");
shade_runner::parse(&shader)
}
#[test]
fn test_shade1() {
setup();
let target = Entry {
frag_input: FragInput { inputs: Vec::new() },
frag_output: FragOutput {
outputs: vec![ShaderInterfaceDefEntry {
location: 0..1,
format: Format::R32G32B32A32Sfloat,
name: Some(Cow::Borrowed("f_color")),
}],
},
frag_layout: FragLayout {
stages: ShaderStages {
fragment: true,
..ShaderStages::none()
},
layout_data: LayoutData {
num_sets: 0,
num_bindings: HashMap::new(),
},
},
vert_input: VertInput {
inputs: vec![ShaderInterfaceDefEntry {
location: 0..1,
format: Format::R32G32Sfloat,
name: Some(Cow::Borrowed("position")),
}],
},
vert_output: VertOutput {
outputs: Vec::new(),
},
vert_layout: VertLayout(ShaderStages {
vertex: true,
..ShaderStages::none()
}),
};
let entry = parse("vert1.glsl", "frag1.glsl");
let entry = format!("{:?}", entry);
let target = format!("{:?}", target);
assert_eq!(
&entry,
&target,
"\n\nDifference: {}",
difference(&entry, &target)
);
}
#[test]
fn test_shade2() {
setup();
let target = Entry {
frag_input: FragInput {
inputs: vec![
ShaderInterfaceDefEntry {
location: 0..1,
format: Format::R32G32B32A32Sfloat,
name: Some(Cow::Borrowed("cool")),
},
ShaderInterfaceDefEntry {
location: 1..2,
format: Format::R32G32Sfloat,
name: Some(Cow::Borrowed("yep")),
},
ShaderInterfaceDefEntry {
location: 2..3,
format: Format::R32Sfloat,
name: Some(Cow::Borrowed("monkey")),
},
],
},
frag_output: FragOutput {
outputs: vec![ShaderInterfaceDefEntry {
location: 0..1,
format: Format::R32G32B32A32Sfloat,
name: Some(Cow::Borrowed("f_color")),
}],
},
frag_layout: FragLayout {
stages: ShaderStages {
fragment: true,
..ShaderStages::none()
},
layout_data: LayoutData {
num_sets: 0,
num_bindings: HashMap::new(),
},
},
vert_input: VertInput {
inputs: vec![ShaderInterfaceDefEntry {
location: 0..1,
format: Format::R32G32Sfloat,
name: Some(Cow::Borrowed("position")),
}],
},
vert_output: VertOutput {
outputs: vec![
ShaderInterfaceDefEntry {
location: 0..1,
format: Format::R32G32B32A32Sfloat,
name: Some(Cow::Borrowed("cool")),
},
ShaderInterfaceDefEntry {
location: 1..2,
format: Format::R32G32Sfloat,
name: Some(Cow::Borrowed("yep")),
},
ShaderInterfaceDefEntry {
location: 2..3,
format: Format::R32Sfloat,
name: Some(Cow::Borrowed("monkey")),
},
],
},
vert_layout: VertLayout(ShaderStages {
vertex: true,
..ShaderStages::none()
}),
};
let entry = parse("vert2.glsl", "frag2.glsl");
let entry = format!("{:?}", entry);
let target = format!("{:?}", target);
assert_eq!(
&entry,
&target,
"\n\nDifference: {}",
difference(&entry, &target)
);
}
Loading…
Cancel
Save