commit
0755b4f807
@ -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…
Reference in new issue