Shader reflection works for everything but dynamic uniforms

This commit is contained in:
Carter Anderson 2020-02-17 14:12:10 -08:00
parent c29a6f7dd2
commit 2fe9710c04
10 changed files with 289 additions and 163 deletions

8
.vscode/launch.json vendored
View File

@ -30,11 +30,11 @@
"cargo": { "cargo": {
"args": [ "args": [
"build", "build",
"--example=simple_new_graph", "--example=simple_new",
"--package=bevy" "--package=bevy"
], ],
"filter": { "filter": {
"name": "simple_new_graph", "name": "simple_new",
"kind": "example" "kind": "example"
} }
}, },
@ -48,12 +48,12 @@
"cargo": { "cargo": {
"args": [ "args": [
"run", "run",
"--example=simple", "--example=simple_new",
"--package=bevy", "--package=bevy",
"--release" "--release"
], ],
"filter": { "filter": {
"name": "simple", "name": "simple_new",
"kind": "example" "kind": "example"
} }
}, },

View File

@ -14,6 +14,9 @@ Here is the current list of planned features. All items are sorted in approximat
* Add runtime type safety to uniform bindings (and maybe compile time) * Add runtime type safety to uniform bindings (and maybe compile time)
* Dynamic / user defined shaders * Dynamic / user defined shaders
* consider using shaderc-rs. but this introduces compile complexity and requires other C++ build systems * consider using shaderc-rs. but this introduces compile complexity and requires other C++ build systems
* Error Handling
* Custom error type?
* Remove as many panics / unwraps as possible
* Input * Input
* Keyboard and mouse events * Keyboard and mouse events
* Gamepad events * Gamepad events

View File

@ -20,10 +20,16 @@ impl<'a> Into<wgpu::VertexBufferDescriptor<'a>> for &'a VertexBufferDescriptor {
} }
} }
#[derive(Clone, Debug)]
pub enum PipelineLayoutType {
Manual(PipelineLayout),
Reflected(Option<PipelineLayout>),
}
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct PipelineDescriptor { pub struct PipelineDescriptor {
pub draw_targets: Vec<String>, pub draw_targets: Vec<String>,
pub pipeline_layout: PipelineLayout, pub layout: PipelineLayoutType,
pub shader_stages: ShaderStages, pub shader_stages: ShaderStages,
pub rasterization_state: Option<wgpu::RasterizationStateDescriptor>, pub rasterization_state: Option<wgpu::RasterizationStateDescriptor>,
@ -59,7 +65,7 @@ pub struct PipelineDescriptor {
impl PipelineDescriptor { impl PipelineDescriptor {
fn new(vertex_shader: Handle<Shader>) -> Self { fn new(vertex_shader: Handle<Shader>) -> Self {
PipelineDescriptor { PipelineDescriptor {
pipeline_layout: PipelineLayout::new(), layout: PipelineLayoutType::Reflected(None),
color_states: Vec::new(), color_states: Vec::new(),
depth_stencil_state: None, depth_stencil_state: None,
draw_targets: Vec::new(), draw_targets: Vec::new(),
@ -79,6 +85,20 @@ impl PipelineDescriptor {
alpha_to_coverage_enabled: false, alpha_to_coverage_enabled: false,
} }
} }
pub fn get_layout(&self) -> Option<&PipelineLayout> {
match self.layout {
PipelineLayoutType::Reflected(ref layout) => layout.as_ref(),
PipelineLayoutType::Manual(ref layout) => Some(layout),
}
}
pub fn get_layout_mut(&mut self) -> Option<&mut PipelineLayout> {
match self.layout {
PipelineLayoutType::Reflected(ref mut layout) => layout.as_mut(),
PipelineLayoutType::Manual(ref mut layout) => Some(layout),
}
}
} }
impl PipelineDescriptor { impl PipelineDescriptor {
@ -128,7 +148,14 @@ impl<'a> PipelineBuilder<'a> {
} }
pub fn add_bind_group(mut self, bind_group: BindGroup) -> Self { pub fn add_bind_group(mut self, bind_group: BindGroup) -> Self {
self.pipeline.pipeline_layout.bind_groups.push(bind_group); if let PipelineLayoutType::Reflected(_) = self.pipeline.layout {
self.pipeline.layout = PipelineLayoutType::Manual(PipelineLayout::new());
}
if let PipelineLayoutType::Manual(ref mut layout) = self.pipeline.layout {
layout.bind_groups.push(bind_group);
}
self self
} }

View File

@ -1,5 +1,6 @@
use crate::render::shader_reflect::ShaderLayout;
use std::{ use std::{
collections::hash_map::DefaultHasher, collections::{HashMap, hash_map::DefaultHasher, BTreeSet},
hash::{Hash, Hasher}, hash::{Hash, Hasher},
}; };
@ -14,23 +15,61 @@ impl PipelineLayout {
bind_groups: Vec::new(), bind_groups: Vec::new(),
} }
} }
pub fn from_shader_layouts(shader_layouts: &mut [ShaderLayout]) -> Self {
let mut bind_groups = HashMap::<u32, BindGroup>::new();
for shader_layout in shader_layouts {
for shader_bind_group in shader_layout.bind_groups.iter_mut() {
match bind_groups.get_mut(&shader_bind_group.index) {
Some(bind_group) => {
for shader_binding in shader_bind_group.bindings.iter() {
if let Some(binding) = bind_group.bindings.iter().find(|binding| binding.index == shader_binding.index) {
if binding != shader_binding {
panic!("Binding {} in BindGroup {} does not match across all shader types: {:?} {:?}", binding.index, bind_group.index, binding, shader_binding);
}
} else {
bind_group.bindings.insert(shader_binding.clone());
}
}
},
None => {
bind_groups.insert(shader_bind_group.index, shader_bind_group.clone());
}
}
}
}
PipelineLayout {
bind_groups: bind_groups.drain().map(|(_, value)| value).collect()
}
}
} }
#[derive(Hash, Clone, Debug)] #[derive(Clone, Debug)]
pub struct BindGroup { pub struct BindGroup {
pub bindings: Vec<Binding>, pub index: u32,
pub bindings: BTreeSet<Binding>,
hash: Option<u64>, hash: Option<u64>,
} }
impl BindGroup { impl BindGroup {
pub fn new(bindings: Vec<Binding>) -> Self { pub fn new(index: u32, bindings: Vec<Binding>) -> Self {
BindGroup { BindGroup {
bindings, index,
bindings: bindings.iter().cloned().collect(),
hash: None, hash: None,
} }
} }
pub fn get_hash(&self) -> u64 { pub fn get_hash(&self) -> Option<u64> {
self.hash
}
pub fn get_or_update_hash(&mut self) -> u64 {
if self.hash.is_none() {
self.update_hash();
}
self.hash.unwrap() self.hash.unwrap()
} }
@ -41,14 +80,22 @@ impl BindGroup {
} }
} }
#[derive(Hash, Clone, Debug)] impl Hash for BindGroup {
fn hash<H: Hasher>(&self, state: &mut H) {
self.index.hash(state);
self.bindings.hash(state);
}
}
#[derive(Hash, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
pub struct Binding { pub struct Binding {
pub name: String, pub name: String,
pub index: u32,
pub bind_type: BindType, pub bind_type: BindType,
// TODO: ADD SHADER STAGE VISIBILITY // TODO: ADD SHADER STAGE VISIBILITY
} }
#[derive(Hash, Clone, Debug)] #[derive(Hash, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
pub enum BindType { pub enum BindType {
Uniform { Uniform {
dynamic: bool, dynamic: bool,
@ -81,13 +128,13 @@ impl BindType {
} }
} }
#[derive(Hash, Clone, Debug)] #[derive(Hash, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
pub struct UniformProperty { pub struct UniformProperty {
pub name: String, pub name: String,
pub property_type: UniformPropertyType, pub property_type: UniformPropertyType,
} }
#[derive(Hash, Clone, Debug)] #[derive(Hash, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
pub enum UniformPropertyType { pub enum UniformPropertyType {
// TODO: Add all types here // TODO: Add all types here
Int, Int,
@ -95,6 +142,7 @@ pub enum UniformPropertyType {
UVec4, UVec4,
Vec3, Vec3,
Vec4, Vec4,
Mat3,
Mat4, Mat4,
Struct(Vec<UniformProperty>), Struct(Vec<UniformProperty>),
Array(Box<UniformPropertyType>, usize), Array(Box<UniformPropertyType>, usize),
@ -108,6 +156,7 @@ impl UniformPropertyType {
UniformPropertyType::UVec4 => 4 * 4, UniformPropertyType::UVec4 => 4 * 4,
UniformPropertyType::Vec3 => 4 * 3, UniformPropertyType::Vec3 => 4 * 3,
UniformPropertyType::Vec4 => 4 * 4, UniformPropertyType::Vec4 => 4 * 4,
UniformPropertyType::Mat3 => 4 * 4 * 3,
UniformPropertyType::Mat4 => 4 * 4 * 4, UniformPropertyType::Mat4 => 4 * 4 * 4,
UniformPropertyType::Struct(properties) => properties UniformPropertyType::Struct(properties) => properties
.iter() .iter()
@ -118,7 +167,7 @@ impl UniformPropertyType {
} }
} }
#[derive(Copy, Clone, Debug, Hash)] #[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, Ord, PartialOrd)]
pub enum TextureViewDimension { pub enum TextureViewDimension {
D1, D1,
D2, D2,

View File

@ -21,72 +21,76 @@ impl ForwardPipelineBuilder for RenderGraphBuilder {
include_str!("forward.frag"), include_str!("forward.frag"),
ShaderStage::Fragment, ShaderStage::Fragment,
)) ))
.add_bind_group(BindGroup::new(vec![ // .add_bind_group(BindGroup::new(0, vec![
Binding { // Binding {
name: "Camera".to_string(), // index: 0,
bind_type: BindType::Uniform { // name: "Camera".to_string(),
dynamic: false, // bind_type: BindType::Uniform {
properties: vec![UniformProperty { // dynamic: false,
name: "ViewProj".to_string(), // properties: vec![UniformProperty {
property_type: UniformPropertyType::Mat4, // name: "ViewProj".to_string(),
}], // property_type: UniformPropertyType::Mat4,
}, // }],
}, // },
Binding { // },
name: "Lights".to_string(), // Binding {
bind_type: BindType::Uniform { // index: 1,
dynamic: false, // name: "Lights".to_string(),
properties: vec![ // bind_type: BindType::Uniform {
UniformProperty { // dynamic: false,
name: "NumLights".to_string(), // properties: vec![
property_type: UniformPropertyType::UVec4, // UniformProperty {
}, // name: "NumLights".to_string(),
UniformProperty { // property_type: UniformPropertyType::UVec4,
name: "SceneLights".to_string(), // },
property_type: UniformPropertyType::Array( // UniformProperty {
Box::new(UniformPropertyType::Struct(vec![ // name: "SceneLights".to_string(),
UniformProperty { // property_type: UniformPropertyType::Array(
name: "proj".to_string(), // Box::new(UniformPropertyType::Struct(vec![
property_type: UniformPropertyType::Mat4, // UniformProperty {
}, // name: "proj".to_string(),
UniformProperty { // property_type: UniformPropertyType::Mat4,
name: "pos".to_string(), // },
property_type: UniformPropertyType::Vec4, // UniformProperty {
}, // name: "pos".to_string(),
UniformProperty { // property_type: UniformPropertyType::Vec4,
name: "color".to_string(), // },
property_type: UniformPropertyType::Vec4, // UniformProperty {
}, // name: "color".to_string(),
])), // property_type: UniformPropertyType::Vec4,
10, // max lights // },
), // ])),
}, // 10, // max lights
], // ),
}, // },
}, // ],
])) // },
.add_bind_group(BindGroup::new(vec![ // },
Binding { // ]))
name: "Object".to_string(), // .add_bind_group(BindGroup::new(1, vec![
bind_type: BindType::Uniform { // Binding {
dynamic: true, // index: 0,
properties: vec![UniformProperty { // name: "Object".to_string(),
name: "Model".to_string(), // bind_type: BindType::Uniform {
property_type: UniformPropertyType::Mat4, // dynamic: true,
}], // properties: vec![UniformProperty {
}, // name: "Model".to_string(),
}, // property_type: UniformPropertyType::Mat4,
Binding { // }],
name: "StandardMaterial_albedo".to_string(), // },
bind_type: BindType::Uniform { // },
dynamic: true, // Binding {
properties: vec![UniformProperty { // index: 1,
name: "Albedo".to_string(), // name: "StandardMaterial_albedo".to_string(),
property_type: UniformPropertyType::Vec4, // bind_type: BindType::Uniform {
}], // dynamic: true,
}, // properties: vec![UniformProperty {
}, // name: "Albedo".to_string(),
])) // property_type: UniformPropertyType::Vec4,
// }],
// },
// },
// ]))
.with_rasterization_state(wgpu::RasterizationStateDescriptor { .with_rasterization_state(wgpu::RasterizationStateDescriptor {
front_face: wgpu::FrontFace::Ccw, front_face: wgpu::FrontFace::Ccw,
cull_mode: wgpu::CullMode::Back, cull_mode: wgpu::CullMode::Back,

View File

@ -34,7 +34,8 @@ impl ForwardFlatPipelineBuilder for RenderGraphBuilder {
include_str!("forward_flat.frag"), include_str!("forward_flat.frag"),
ShaderStage::Fragment, ShaderStage::Fragment,
)) ))
.add_bind_group(BindGroup::new(vec![Binding { .add_bind_group(BindGroup::new(0, vec![Binding {
index: 0,
name: "Camera".to_string(), name: "Camera".to_string(),
bind_type: BindType::Uniform { bind_type: BindType::Uniform {
dynamic: false, dynamic: false,
@ -44,8 +45,9 @@ impl ForwardFlatPipelineBuilder for RenderGraphBuilder {
}], }],
}, },
}])) }]))
.add_bind_group(BindGroup::new(vec![ .add_bind_group(BindGroup::new(1, vec![
Binding { Binding {
index: 0,
name: "Object".to_string(), name: "Object".to_string(),
bind_type: BindType::Uniform { bind_type: BindType::Uniform {
dynamic: true, dynamic: true,
@ -56,6 +58,7 @@ impl ForwardFlatPipelineBuilder for RenderGraphBuilder {
}, },
}, },
Binding { Binding {
index: 1,
name: "StandardMaterial_albedo".to_string(), name: "StandardMaterial_albedo".to_string(),
bind_type: BindType::Uniform { bind_type: BindType::Uniform {
dynamic: true, dynamic: true,

View File

@ -33,7 +33,8 @@ impl UiPipelineBuilder for RenderGraphBuilder {
include_str!("ui.frag"), include_str!("ui.frag"),
ShaderStage::Fragment, ShaderStage::Fragment,
)) ))
.add_bind_group(BindGroup::new(vec![Binding { .add_bind_group(BindGroup::new(0, vec![Binding {
index: 0,
name: "Camera2d".to_string(), name: "Camera2d".to_string(),
bind_type: BindType::Uniform { bind_type: BindType::Uniform {
dynamic: false, dynamic: false,

View File

@ -4,9 +4,9 @@ use crate::{
render::{ render::{
render_graph_2::{ render_graph_2::{
resource_name, update_shader_assignments, BindGroup, BindType, resource_name, update_shader_assignments, BindGroup, BindType,
DynamicUniformBufferInfo, PassDescriptor, PipelineDescriptor, RenderGraph, RenderPass, DynamicUniformBufferInfo, PassDescriptor, PipelineDescriptor, PipelineLayout,
RenderPassColorAttachmentDescriptor, RenderPassDepthStencilAttachmentDescriptor, PipelineLayoutType, RenderGraph, RenderPass, RenderPassColorAttachmentDescriptor,
Renderer, ResourceInfo, TextureDescriptor, RenderPassDepthStencilAttachmentDescriptor, Renderer, ResourceInfo, TextureDescriptor,
}, },
Shader, Shader,
}, },
@ -76,25 +76,37 @@ impl WgpuRenderer {
vertex_shader: &Shader, vertex_shader: &Shader,
fragment_shader: Option<&Shader>, fragment_shader: Option<&Shader>,
) -> wgpu::RenderPipeline { ) -> wgpu::RenderPipeline {
let vertex_shader_module = Self::create_shader_module(device, vertex_shader, None); let vertex_spirv = vertex_shader.get_spirv_shader(None);
let fragment_spirv = fragment_shader.map(|f| f.get_spirv_shader(None));
let vertex_shader_module = Self::create_shader_module(device, &vertex_spirv, None);
let fragment_shader_module = match fragment_shader { let fragment_shader_module = match fragment_shader {
Some(fragment_shader) => { Some(fragment_spirv) => Some(Self::create_shader_module(device, fragment_spirv, None)),
Some(Self::create_shader_module(device, fragment_shader, None))
}
None => None, None => None,
}; };
if let PipelineLayoutType::Reflected(None) = pipeline_descriptor.layout {
let mut layouts = vec![vertex_spirv.reflect_layout().unwrap()];
if let Some(ref fragment_spirv) = fragment_spirv {
layouts.push(fragment_spirv.reflect_layout().unwrap());
}
pipeline_descriptor.layout =
PipelineLayoutType::Reflected(Some(PipelineLayout::from_shader_layouts(&mut layouts)));
}
let layout = pipeline_descriptor.get_layout_mut().unwrap();
// setup new bind group layouts // setup new bind group layouts
for bind_group in pipeline_descriptor.pipeline_layout.bind_groups.iter_mut() { for bind_group in layout.bind_groups.iter_mut() {
bind_group.update_hash(); let bind_group_id = bind_group.get_or_update_hash();
let bind_group_id = bind_group.get_hash();
if let None = bind_group_layouts.get(&bind_group_id) { if let None = bind_group_layouts.get(&bind_group_id) {
let bind_group_layout_binding = bind_group let bind_group_layout_binding = bind_group
.bindings .bindings
.iter() .iter()
.enumerate() .map(|binding| wgpu::BindGroupLayoutBinding {
.map(|(i, binding)| wgpu::BindGroupLayoutBinding { binding: binding.index,
binding: i as u32,
visibility: wgpu::ShaderStage::VERTEX | wgpu::ShaderStage::FRAGMENT, visibility: wgpu::ShaderStage::VERTEX | wgpu::ShaderStage::FRAGMENT,
ty: (&binding.bind_type).into(), ty: (&binding.bind_type).into(),
}) })
@ -109,12 +121,11 @@ impl WgpuRenderer {
} }
// collect bind group layout references // collect bind group layout references
let bind_group_layouts = pipeline_descriptor let bind_group_layouts = layout
.pipeline_layout
.bind_groups .bind_groups
.iter() .iter()
.map(|bind_group| { .map(|bind_group| {
let bind_group_id = bind_group.get_hash(); let bind_group_id = bind_group.get_hash().unwrap();
bind_group_layouts.get(&bind_group_id).unwrap() bind_group_layouts.get(&bind_group_id).unwrap()
}) })
.collect::<Vec<&wgpu::BindGroupLayout>>(); .collect::<Vec<&wgpu::BindGroupLayout>>();
@ -127,11 +138,11 @@ impl WgpuRenderer {
layout: &pipeline_layout, layout: &pipeline_layout,
vertex_stage: wgpu::ProgrammableStageDescriptor { vertex_stage: wgpu::ProgrammableStageDescriptor {
module: &vertex_shader_module, module: &vertex_shader_module,
entry_point: &vertex_shader.entry_point, entry_point: "main",
}, },
fragment_stage: match fragment_shader { fragment_stage: match fragment_shader {
Some(fragment_shader) => Some(wgpu::ProgrammableStageDescriptor { Some(fragment_shader) => Some(wgpu::ProgrammableStageDescriptor {
entry_point: &fragment_shader.entry_point, entry_point: "main",
module: fragment_shader_module.as_ref().unwrap(), module: fragment_shader_module.as_ref().unwrap(),
}), }),
None => None, None => None,
@ -233,7 +244,7 @@ impl WgpuRenderer {
// TODO: consider moving this to a resource provider // TODO: consider moving this to a resource provider
fn setup_bind_group(&mut self, bind_group: &BindGroup) -> u64 { fn setup_bind_group(&mut self, bind_group: &BindGroup) -> u64 {
let bind_group_id = bind_group.get_hash(); let bind_group_id = bind_group.get_hash().unwrap();
if let None = self.bind_groups.get(&bind_group_id) { if let None = self.bind_groups.get(&bind_group_id) {
let mut unset_uniforms = Vec::new(); let mut unset_uniforms = Vec::new();
@ -260,12 +271,11 @@ impl WgpuRenderer {
let bindings = bind_group let bindings = bind_group
.bindings .bindings
.iter() .iter()
.enumerate() .map(|binding| {
.map(|(i, b)| { let resource_info = self.resource_info.get(&binding.name).unwrap();
let resource_info = self.resource_info.get(&b.name).unwrap();
wgpu::Binding { wgpu::Binding {
binding: i as u32, binding: binding.index,
resource: match &b.bind_type { resource: match &binding.bind_type {
BindType::Uniform { BindType::Uniform {
dynamic: _, dynamic: _,
properties: _, properties: _,
@ -275,7 +285,7 @@ impl WgpuRenderer {
buffer_usage: _, buffer_usage: _,
} = resource_info } = resource_info
{ {
let buffer = self.buffers.get(&b.name).unwrap(); let buffer = self.buffers.get(&binding.name).unwrap();
wgpu::BindingResource::Buffer { wgpu::BindingResource::Buffer {
buffer, buffer,
range: 0..*size, range: 0..*size,
@ -426,7 +436,8 @@ impl Renderer for WgpuRenderer {
} }
// create bind groups // create bind groups
for bind_group in pipeline_descriptor.pipeline_layout.bind_groups.iter() { let pipeline_layout = pipeline_descriptor.get_layout().unwrap();
for bind_group in pipeline_layout.bind_groups.iter() {
self.setup_bind_group(bind_group); self.setup_bind_group(bind_group);
} }
} }
@ -641,14 +652,9 @@ impl<'a, 'b, 'c, 'd> RenderPass for WgpuRenderPass<'a, 'b, 'c, 'd> {
} }
fn setup_bind_groups(&mut self, entity: Option<&Entity>) { fn setup_bind_groups(&mut self, entity: Option<&Entity>) {
for (i, bind_group) in self let pipeline_layout = self.pipeline_descriptor.get_layout().unwrap();
.pipeline_descriptor for bind_group in pipeline_layout.bind_groups.iter() {
.pipeline_layout let bind_group_id = bind_group.get_hash().unwrap();
.bind_groups
.iter()
.enumerate()
{
let bind_group_id = bind_group.get_hash();
let bind_group_info = self.renderer.bind_groups.get(&bind_group_id).unwrap(); let bind_group_info = self.renderer.bind_groups.get(&bind_group_id).unwrap();
let mut dynamic_uniform_indices = Vec::new(); let mut dynamic_uniform_indices = Vec::new();
@ -658,7 +664,7 @@ impl<'a, 'b, 'c, 'd> RenderPass for WgpuRenderPass<'a, 'b, 'c, 'd> {
continue; continue;
} }
// PERF: This hashmap get is pretty expensive (10 fps per 10000 entities) // PERF: This hashmap get is pretty expensive (10 fps for 10000 entities)
if let Some(dynamic_uniform_buffer_info) = if let Some(dynamic_uniform_buffer_info) =
self.renderer.dynamic_uniform_buffer_info.get(&binding.name) self.renderer.dynamic_uniform_buffer_info.get(&binding.name)
{ {
@ -674,7 +680,7 @@ impl<'a, 'b, 'c, 'd> RenderPass for WgpuRenderPass<'a, 'b, 'c, 'd> {
// TODO: check to see if bind group is already set // TODO: check to see if bind group is already set
self.render_pass.set_bind_group( self.render_pass.set_bind_group(
i as u32, bind_group.index,
&bind_group_info.bind_group, &bind_group_info.bind_group,
dynamic_uniform_indices.as_slice(), dynamic_uniform_indices.as_slice(),
); );

View File

@ -1,4 +1,7 @@
use crate::{asset::Handle, render::shader_reflect::get_shader_layout}; use crate::{
asset::Handle,
render::shader_reflect::{get_shader_layout, ShaderLayout},
};
use std::marker::Copy; use std::marker::Copy;
#[derive(Hash, Eq, PartialEq, Copy, Clone, Debug)] #[derive(Hash, Eq, PartialEq, Copy, Clone, Debug)]
@ -50,11 +53,10 @@ pub enum ShaderSource {
Glsl(String), Glsl(String),
} }
#[derive(Clone, Debug, Hash, Eq, PartialEq)] #[derive(Clone, Debug)]
pub struct Shader { pub struct Shader {
pub source: ShaderSource, pub source: ShaderSource,
pub stage: ShaderStage, pub stage: ShaderStage,
pub entry_point: String,
// TODO: add "precompile" flag? // TODO: add "precompile" flag?
} }
@ -62,29 +64,31 @@ impl Shader {
pub fn from_glsl(glsl: &str, stage: ShaderStage) -> Shader { pub fn from_glsl(glsl: &str, stage: ShaderStage) -> Shader {
Shader { Shader {
source: ShaderSource::Glsl(glsl.to_string()), source: ShaderSource::Glsl(glsl.to_string()),
entry_point: "main".to_string(),
stage, stage,
} }
} }
pub fn get_spirv(&self, macros: Option<&[String]>) -> Vec<u32> { pub fn get_spirv(&self, macros: Option<&[String]>) -> Vec<u32> {
let result = match self.source { match self.source {
ShaderSource::Spirv(ref bytes) => bytes.clone(), ShaderSource::Spirv(ref bytes) => bytes.clone(),
ShaderSource::Glsl(ref source) => glsl_to_spirv(&source, self.stage, macros), ShaderSource::Glsl(ref source) => glsl_to_spirv(&source, self.stage, macros),
}; }
get_shader_layout(&result);
result
} }
pub fn get_spirv_shader(&self, macros: Option<&[String]>) -> Shader { pub fn get_spirv_shader(&self, macros: Option<&[String]>) -> Shader {
Shader { Shader {
source: ShaderSource::Spirv(self.get_spirv(macros)), source: ShaderSource::Spirv(self.get_spirv(macros)),
entry_point: self.entry_point.clone(),
stage: self.stage, stage: self.stage,
} }
} }
pub fn reflect_layout(&self) -> Option<ShaderLayout> {
if let ShaderSource::Spirv(ref spirv) = self.source {
Some(get_shader_layout(spirv.as_slice()))
} else {
panic!("Cannot reflect layout of non-SpirV shader. Try compiling this shader to SpirV first using self.get_spirv_shader()");
}
}
} }
#[derive(Clone, Debug)] #[derive(Clone, Debug)]

View File

@ -1,12 +1,17 @@
use crate::render::render_graph_2::{BindGroup, UniformPropertyType, Binding, BindType, UniformProperty}; use crate::render::render_graph_2::{
BindGroup, BindType, Binding, UniformProperty, UniformPropertyType,
};
use spirv_reflect::{ use spirv_reflect::{
types::{ReflectDescriptorSet, ReflectTypeDescription, ReflectDescriptorBinding, ReflectDescriptorType, ReflectTypeFlags}, types::{
ReflectDescriptorBinding, ReflectDescriptorSet, ReflectDescriptorType,
ReflectTypeDescription, ReflectTypeFlags,
},
ShaderModule, ShaderModule,
}; };
use zerocopy::AsBytes; use zerocopy::AsBytes;
// use rspirv::{binary::Parser, dr::Loader, lift::LiftContext}; // use rspirv::{binary::Parser, dr::Loader, lift::LiftContext};
// TODO: pick rspirv vs spirv-reflect // TODO: use rspirv when structured representation is ready. this way we can remove spirv_reflect, which is a non-rust dependency
// pub fn get_shader_layout(spirv_data: &[u32]) { // pub fn get_shader_layout(spirv_data: &[u32]) {
// let mut loader = Loader::new(); // You can use your own consumer here. // let mut loader = Loader::new(); // You can use your own consumer here.
// { // {
@ -18,37 +23,39 @@ use zerocopy::AsBytes;
// println!("{:?}", structured.types); // println!("{:?}", structured.types);
// } // }
pub fn get_shader_layout(spirv_data: &[u32]) { #[derive(Debug, Clone)]
pub struct ShaderLayout {
pub bind_groups: Vec<BindGroup>,
pub entry_point: String,
}
pub fn get_shader_layout(spirv_data: &[u32]) -> ShaderLayout {
match ShaderModule::load_u8_data(spirv_data.as_bytes()) { match ShaderModule::load_u8_data(spirv_data.as_bytes()) {
Ok(ref mut module) => { Ok(ref mut module) => {
let entry_point_name = module.get_entry_point_name(); let entry_point_name = module.get_entry_point_name();
let shader_stage = module.get_shader_stage();
println!("entry point: {}", entry_point_name);
println!("shader stage: {:?}", shader_stage);
let mut bind_groups = Vec::new(); let mut bind_groups = Vec::new();
for descriptor_set in module.enumerate_descriptor_sets(None).unwrap() { for descriptor_set in module.enumerate_descriptor_sets(None).unwrap() {
let bind_group = reflect_bind_group(&descriptor_set); let bind_group = reflect_bind_group(&descriptor_set);
bind_groups.push(bind_group); bind_groups.push(bind_group);
} }
println!(" result {:?}", &bind_groups); ShaderLayout {
bind_groups,
println!(); entry_point: entry_point_name,
}
} }
_ => {} Err(err) => panic!("Failed to reflect shader layout: {:?}", err)
} }
} }
fn reflect_bind_group(descriptor_set: &ReflectDescriptorSet) -> BindGroup { fn reflect_bind_group(descriptor_set: &ReflectDescriptorSet) -> BindGroup {
println!(" set {}", descriptor_set.set);
let mut bindings = Vec::new(); let mut bindings = Vec::new();
for descriptor_binding in descriptor_set.bindings.iter() { for descriptor_binding in descriptor_set.bindings.iter() {
let binding = reflect_binding(descriptor_binding); let binding = reflect_binding(descriptor_binding);
bindings.push(binding); bindings.push(binding);
} }
BindGroup::new(bindings) BindGroup::new(descriptor_set.set, bindings)
} }
fn reflect_binding(binding: &ReflectDescriptorBinding) -> Binding { fn reflect_binding(binding: &ReflectDescriptorBinding) -> Binding {
@ -61,10 +68,10 @@ fn reflect_binding(binding: &ReflectDescriptorBinding) -> Binding {
_ => panic!("unsupported bind type {:?}", binding.descriptor_type), _ => panic!("unsupported bind type {:?}", binding.descriptor_type),
}; };
// println!(" {:?}", binding); Binding {
Binding{ index: binding.binding,
bind_type: bind_type, bind_type,
name: type_description.type_name.to_string() name: type_description.type_name.to_string(),
} }
} }
@ -76,7 +83,10 @@ enum NumberType {
} }
fn reflect_uniform(type_description: &ReflectTypeDescription) -> UniformProperty { fn reflect_uniform(type_description: &ReflectTypeDescription) -> UniformProperty {
let uniform_property_type = if type_description.type_flags.contains(ReflectTypeFlags::STRUCT) { let uniform_property_type = if type_description
.type_flags
.contains(ReflectTypeFlags::STRUCT)
{
reflect_uniform_struct(type_description) reflect_uniform_struct(type_description)
} else { } else {
reflect_uniform_numeric(type_description) reflect_uniform_numeric(type_description)
@ -89,8 +99,7 @@ fn reflect_uniform(type_description: &ReflectTypeDescription) -> UniformProperty
} }
fn reflect_uniform_struct(type_description: &ReflectTypeDescription) -> UniformPropertyType { fn reflect_uniform_struct(type_description: &ReflectTypeDescription) -> UniformPropertyType {
println!("reflecting struct"); let mut properties = Vec::new();
let mut properties = Vec::new();
for member in type_description.members.iter() { for member in type_description.members.iter() {
properties.push(reflect_uniform(member)); properties.push(reflect_uniform(member));
} }
@ -104,9 +113,12 @@ fn reflect_uniform_numeric(type_description: &ReflectTypeDescription) -> Uniform
match traits.numeric.scalar.signedness { match traits.numeric.scalar.signedness {
0 => NumberType::UInt, 0 => NumberType::UInt,
1 => NumberType::Int, 1 => NumberType::Int,
signedness => panic!("unexpected signedness {}", signedness) signedness => panic!("unexpected signedness {}", signedness),
} }
} else if type_description.type_flags.contains(ReflectTypeFlags::FLOAT) { } else if type_description
.type_flags
.contains(ReflectTypeFlags::FLOAT)
{
NumberType::Float NumberType::Float
} else { } else {
panic!("unexpected type flag {:?}", type_description.type_flags); panic!("unexpected type flag {:?}", type_description.type_flags);
@ -114,11 +126,28 @@ fn reflect_uniform_numeric(type_description: &ReflectTypeDescription) -> Uniform
// TODO: handle scalar width here // TODO: handle scalar width here
match (number_type, traits.numeric.vector.component_count) { if type_description
(NumberType::Int, 1) => UniformPropertyType::Int, .type_flags
(NumberType::Float, 3) => UniformPropertyType::Vec3, .contains(ReflectTypeFlags::MATRIX)
(NumberType::Float, 4) => UniformPropertyType::Vec4, {
(NumberType::UInt, 4) => UniformPropertyType::UVec4, match (number_type, traits.numeric.matrix.column_count, traits.numeric.matrix.row_count) {
(number_type, component_count) => panic!("unexpected uniform property format {:?} {}", number_type, component_count), (NumberType::Float, 3, 3) => UniformPropertyType::Mat3,
(NumberType::Float, 4, 4) => UniformPropertyType::Mat4,
(number_type, column_count, row_count) => panic!(
"unexpected uniform property matrix format {:?} {}x{}",
number_type, column_count, row_count
),
}
} else {
match (number_type, traits.numeric.vector.component_count) {
(NumberType::Int, 1) => UniformPropertyType::Int,
(NumberType::Float, 3) => UniformPropertyType::Vec3,
(NumberType::Float, 4) => UniformPropertyType::Vec4,
(NumberType::UInt, 4) => UniformPropertyType::UVec4,
(number_type, component_count) => panic!(
"unexpected uniform property format {:?} {}",
number_type, component_count
),
}
} }
} }