From 073921b9fb7bf8c046366c6eb7bdfcbe0bc46ff2 Mon Sep 17 00:00:00 2001 From: chyyran Date: Thu, 27 Oct 2022 02:22:44 -0400 Subject: [PATCH] reflect: implement shader reflection --- librashader-reflect/src/error.rs | 19 +- librashader-reflect/src/front/naga.rs | 62 +- librashader-reflect/src/front/shaderc.rs | 5 +- librashader-reflect/src/reflect/cross.rs | 599 ++++++++++++++----- librashader-reflect/src/reflect/mod.rs | 68 ++- librashader-reflect/src/reflect/rspirv.rs | 5 +- librashader-reflect/src/reflect/semantics.rs | 55 +- test/basic.slang | 2 +- 8 files changed, 604 insertions(+), 211 deletions(-) diff --git a/librashader-reflect/src/error.rs b/librashader-reflect/src/error.rs index 638dfb3..e4e7295 100644 --- a/librashader-reflect/src/error.rs +++ b/librashader-reflect/src/error.rs @@ -1,5 +1,5 @@ -use thiserror::Error; use crate::reflect::semantics::MemberOffset; +use thiserror::Error; #[derive(Error, Debug)] pub enum ShaderCompileError { @@ -25,7 +25,7 @@ pub enum SemanticsErrorKind { InvalidResourceType, InvalidRange(u32), UnknownSemantics(String), - InvalidTypeForSemantic(String) + InvalidTypeForSemantic(String), } #[derive(Error, Debug)] @@ -45,10 +45,19 @@ pub enum ShaderReflectError { #[error("filter chain is non causal")] NonCausalFilterChain { pass: u32, target: u32 }, #[error("mismatched offset")] - MismatchedOffset { semantic: String, vertex: MemberOffset, fragment: MemberOffset }, + MismatchedOffset { + semantic: String, + vertex: MemberOffset, + fragment: MemberOffset, + }, #[error("mismatched component")] - MismatchedComponent { semantic: String, vertex: u32, fragment: u32 }, - + MismatchedComponent { + semantic: String, + vertex: u32, + fragment: u32, + }, + #[error("the binding is already in use")] + BindingInUse(u32), } impl From> for ShaderCompileError { diff --git a/librashader-reflect/src/front/naga.rs b/librashader-reflect/src/front/naga.rs index e181af4..049ef1b 100644 --- a/librashader-reflect/src/front/naga.rs +++ b/librashader-reflect/src/front/naga.rs @@ -18,14 +18,14 @@ pub fn compile_spirv(source: &ShaderSource) -> Result::parse(&loaded) - .unwrap(); + let mut ast = spirv_cross::spirv::Ast::::parse(&loaded).unwrap(); println!("{:#}", ast.compile().unwrap()); println!("--- naga glsl---"); println!("{:#}", glsl_out); diff --git a/librashader-reflect/src/front/shaderc.rs b/librashader-reflect/src/front/shaderc.rs index a7b44ad..f58ffeb 100644 --- a/librashader-reflect/src/front/shaderc.rs +++ b/librashader-reflect/src/front/shaderc.rs @@ -126,10 +126,7 @@ mod test { use crate::front::shaderc::compile_spirv; #[test] pub fn compile_shader() { - let result = librashader_preprocess::load_shader_source( - "../test/basic.slang", - ) - .unwrap(); + let result = librashader_preprocess::load_shader_source("../test/basic.slang").unwrap(); let spirv = compile_spirv(&result).unwrap(); } } diff --git a/librashader-reflect/src/reflect/cross.rs b/librashader-reflect/src/reflect/cross.rs index f77339b..cbeacd6 100644 --- a/librashader-reflect/src/reflect/cross.rs +++ b/librashader-reflect/src/reflect/cross.rs @@ -1,13 +1,13 @@ - -use crate::error::{ShaderReflectError, SemanticsErrorKind}; +use crate::error::{SemanticsErrorKind, ShaderReflectError}; use crate::front::shaderc::GlslangCompilation; -use crate::reflect::semantics::{BindingStage, UboReflection, MAX_BINDINGS_COUNT, ShaderReflection, PushReflection, MAX_PUSH_BUFFER_SIZE, VariableSemantics, TextureSemantics, ValidateTypeSemantics, MemberOffset, VariableMeta, TypeInfo, TextureMeta}; +use crate::reflect::semantics::{BindingStage, MemberOffset, PushReflection, SemanticMap, ShaderReflection, TextureSizeMeta, TextureSemantics, TypeInfo, UboReflection, ValidateTypeSemantics, VariableMeta, VariableSemantics, MAX_BINDINGS_COUNT, MAX_PUSH_BUFFER_SIZE, TextureImage}; use crate::reflect::{ReflectMeta, ReflectOptions, ReflectShader, UniformSemantic}; -use spirv_cross::spirv::{Ast, Decoration, Module, Resource, ShaderResources, Type}; -use std::fmt::Debug; use rustc_hash::FxHashMap; -use spirv_cross::{ErrorCode, hlsl}; use spirv_cross::hlsl::{CompilerOptions, ShaderModel}; +use spirv_cross::spirv::{Ast, Decoration, Module, Resource, ShaderResources, Type}; +use spirv_cross::{hlsl, ErrorCode}; +use std::fmt::Debug; +use std::str::FromStr; pub struct CrossReflect where @@ -24,21 +24,29 @@ impl ValidateTypeSemantics for VariableSemantics { }; if !array.is_empty() { - return None + return None; } let valid = match self { - VariableSemantics::MVP => matches!(ty, &Type::Float { .. }) && vecsize == 4 && columns == 4, - VariableSemantics::FrameCount => matches!(ty, &Type::UInt { .. }) && vecsize == 1 && columns == 1, - VariableSemantics::FrameDirection => matches!(ty, &Type::Int { .. }) && vecsize == 1 && columns == 1, - VariableSemantics::FloatParameter => matches!(ty, &Type::Float { .. }) && vecsize == 1 && columns == 1, - _ => matches!(ty, Type::Float { .. }) && vecsize == 4 && columns == 1 + VariableSemantics::MVP => { + matches!(ty, &Type::Float { .. }) && vecsize == 4 && columns == 4 + } + VariableSemantics::FrameCount => { + matches!(ty, &Type::UInt { .. }) && vecsize == 1 && columns == 1 + } + VariableSemantics::FrameDirection => { + matches!(ty, &Type::Int { .. }) && vecsize == 1 && columns == 1 + } + VariableSemantics::FloatParameter => { + matches!(ty, &Type::Float { .. }) && vecsize == 1 && columns == 1 + } + _ => matches!(ty, Type::Float { .. }) && vecsize == 4 && columns == 1, }; if valid { Some(TypeInfo { size: vecsize, - columns + columns, }) } else { None @@ -53,13 +61,13 @@ impl ValidateTypeSemantics for TextureSemantics { }; if !array.is_empty() { - return None + return None; } if vecsize == 4 && columns == 1 { Some(TypeInfo { size: vecsize, - columns + columns, }) } else { None @@ -67,8 +75,7 @@ impl ValidateTypeSemantics for TextureSemantics { } } -impl TryFrom for CrossReflect -{ +impl TryFrom for CrossReflect { type Error = ShaderReflectError; fn try_from(value: GlslangCompilation) -> Result { @@ -87,13 +94,17 @@ impl TryFrom for CrossReflect } } -impl CrossReflect - where - T: spirv_cross::spirv::Target, - Ast: spirv_cross::spirv::Compile, - Ast: spirv_cross::spirv::Parse, +impl CrossReflect +where + T: spirv_cross::spirv::Target, + Ast: spirv_cross::spirv::Compile, + Ast: spirv_cross::spirv::Parse, { - fn validate(&self, vertex_res: &ShaderResources, fragment_res: &ShaderResources) -> Result<(), ShaderReflectError> { + fn validate( + &self, + vertex_res: &ShaderResources, + fragment_res: &ShaderResources, + ) -> Result<(), ShaderReflectError> { if !vertex_res.sampled_images.is_empty() || !vertex_res.storage_buffers.is_empty() || !vertex_res.subpass_inputs.is_empty() @@ -129,17 +140,20 @@ impl CrossReflect )); } - let fragment_location = self.fragment.get_decoration(fragment_res.stage_outputs[0].id, Decoration::Location)?; + let fragment_location = self + .fragment + .get_decoration(fragment_res.stage_outputs[0].id, Decoration::Location)?; if fragment_location != 0 { return Err(ShaderReflectError::FragmentSemanticError( SemanticsErrorKind::InvalidLocation(fragment_location), )); } - let mut vert_mask = vertex_res.stage_inputs.iter() - .try_fold(0, |mask, input| { - Ok::(mask | 1 << self.vertex.get_decoration(input.id, Decoration::Location)?) - })?; + let mut vert_mask = vertex_res.stage_inputs.iter().try_fold(0, |mask, input| { + Ok::( + mask | 1 << self.vertex.get_decoration(input.id, Decoration::Location)?, + ) + })?; if vert_mask != 0x3 { return Err(ShaderReflectError::VertexSemanticError( SemanticsErrorKind::InvalidLocation(vert_mask), @@ -154,7 +168,9 @@ impl CrossReflect if vertex_res.push_constant_buffers.len() > 1 { return Err(ShaderReflectError::VertexSemanticError( - SemanticsErrorKind::InvalidUniformBufferCount(vertex_res.push_constant_buffers.len()), + SemanticsErrorKind::InvalidUniformBufferCount( + vertex_res.push_constant_buffers.len(), + ), )); } @@ -166,46 +182,178 @@ impl CrossReflect if fragment_res.push_constant_buffers.len() > 1 { return Err(ShaderReflectError::FragmentSemanticError( - SemanticsErrorKind::InvalidUniformBufferCount(fragment_res.push_constant_buffers.len()), + SemanticsErrorKind::InvalidUniformBufferCount( + fragment_res.push_constant_buffers.len(), + ), )); } Ok(()) } } -#[derive(Copy, Clone)] -enum SemanticErrorBlame { - Vertex, - Fragment -} - struct UboData { id: u32, descriptor_set: u32, binding: u32, - size: u32 + size: u32, +} + +struct TextureData<'a> { + id: u32, + name: &'a str, + descriptor_set: u32, + binding: u32, +} + +// todo: might want to take these crate helpers out. + +#[derive(Copy, Clone)] +enum SemanticErrorBlame { + Vertex, + Fragment, } impl SemanticErrorBlame { fn error(self, kind: SemanticsErrorKind) -> ShaderReflectError { return match self { SemanticErrorBlame::Vertex => ShaderReflectError::VertexSemanticError(kind), - SemanticErrorBlame::Fragment => ShaderReflectError::FragmentSemanticError(kind) + SemanticErrorBlame::Fragment => ShaderReflectError::FragmentSemanticError(kind), + }; + } +} + +trait TextureSemanticMap { + fn get_texture_semantic( + &self, + name: &str, + ) -> Option>; +} + +trait VariableSemanticMap { + fn get_variable_semantic( + &self, + name: &str, + ) -> Option>; +} + +impl VariableSemanticMap for FxHashMap { + fn get_variable_semantic( + &self, + name: &str, + ) -> Option> { + match self.get(name) { + // existing uniforms in the semantic map have priority + None => match name { + "MVP" => Some(SemanticMap { + semantics: VariableSemantics::MVP, + index: 0, + }), + "OutputSize" => Some(SemanticMap { + semantics: VariableSemantics::Output, + index: 0, + }), + "FinalViewportSize" => Some(SemanticMap { + semantics: VariableSemantics::FinalViewport, + index: 0, + }), + "FrameCount" => Some(SemanticMap { + semantics: VariableSemantics::FrameCount, + index: 0, + }), + "FrameDirection" => Some(SemanticMap { + semantics: VariableSemantics::FrameDirection, + index: 0, + }), + _ => None + }, + Some(UniformSemantic::Variable(variable)) => Some(*variable), + Some(UniformSemantic::Texture(_)) => None, } } } -impl CrossReflect - where - T: spirv_cross::spirv::Target, - Ast: spirv_cross::spirv::Compile, - Ast: spirv_cross::spirv::Parse, +impl TextureSemanticMap for FxHashMap { + fn get_texture_semantic( + &self, + name: &str, + ) -> Option> { + match self.get(name) { + None => { + if let Some(semantics) = TextureSemantics::TEXTURE_SEMANTICS + .iter() + .find(|f| name.starts_with(f.size_uniform_name())) + { + if semantics.is_array() { + let index = &name[semantics.size_uniform_name().len()..]; + let Ok(index) = u32::from_str(index) else { + return None; + }; + return Some(SemanticMap { + semantics: *semantics, + index, + }); + } else if name == semantics.size_uniform_name() { + return Some(SemanticMap { + semantics: *semantics, + index: 0, + }); + } + } + return None + } + Some(UniformSemantic::Variable(_)) => None, + Some(UniformSemantic::Texture(texture)) => Some(*texture), + } + } +} + +impl TextureSemanticMap for FxHashMap> { + fn get_texture_semantic( + &self, + name: &str, + ) -> Option> { + match self.get(name) { + None => { + if let Some(semantics) = TextureSemantics::TEXTURE_SEMANTICS + .iter() + .find(|f| name.starts_with(f.texture_name())) + { + if semantics.is_array() { + let index = &name[semantics.texture_name().len()..]; + let Ok(index) = u32::from_str(index) else {return None}; + return Some(SemanticMap { + semantics: *semantics, + index, + }); + } else if name == semantics.texture_name() { + return Some(SemanticMap { + semantics: *semantics, + index: 0, + }); + } + } + return None + } + Some(texture) => Some(*texture), + } + } +} + +impl CrossReflect +where + T: spirv_cross::spirv::Target, + Ast: spirv_cross::spirv::Compile, + Ast: spirv_cross::spirv::Parse, { - fn get_ubo_data(ast: &Ast, ubo: &Resource, blame: SemanticErrorBlame) -> Result { + fn get_ubo_data( + ast: &Ast, + ubo: &Resource, + blame: SemanticErrorBlame, + ) -> Result { let descriptor_set = ast.get_decoration(ubo.id, Decoration::DescriptorSet)?; - let binding = ast.get_decoration(ubo.id, Decoration::Binding)?; - if binding >= MAX_BINDINGS_COUNT { - return Err(blame.error(SemanticsErrorKind::InvalidBinding(binding))) + let binding = ast.get_decoration(ubo.id, Decoration::Binding)?; + if binding >= MAX_BINDINGS_COUNT { + return Err(blame.error(SemanticsErrorKind::InvalidBinding(binding))); } if descriptor_set != 0 { return Err(blame.error(SemanticsErrorKind::InvalidDescriptorSet(descriptor_set))); @@ -215,11 +363,15 @@ impl CrossReflect descriptor_set, binding, id: ubo.id, - size + size, }) } - fn get_push_size(ast: &Ast, push: &Resource, blame: SemanticErrorBlame) -> Result { + fn get_push_size( + ast: &Ast, + push: &Resource, + blame: SemanticErrorBlame, + ) -> Result { let size = ast.get_declared_struct_size(push.base_type_id)?; if size >= MAX_PUSH_BUFFER_SIZE { return Err(blame.error(SemanticsErrorKind::InvalidPushBufferSize(size))); @@ -227,103 +379,161 @@ impl CrossReflect Ok(size) } - fn add_active_buffer_range(ast: &Ast, resource: &Resource, options: &ReflectOptions, meta: &mut ReflectMeta, offset_type: impl Fn(usize) -> MemberOffset, blame: SemanticErrorBlame) -> Result<(), ShaderReflectError> { - + fn reflect_buffer_range_metas( + ast: &Ast, + resource: &Resource, + options: &ReflectOptions, + meta: &mut ReflectMeta, + offset_type: impl Fn(usize) -> MemberOffset, + blame: SemanticErrorBlame, + ) -> Result<(), ShaderReflectError> { let ranges = ast.get_active_buffer_ranges(resource.id)?; + eprintln!("{:?}", ranges); for range in ranges { let name = ast.get_member_name(resource.base_type_id, range.index)?; - let res_type = ast.get_type(resource.base_type_id)?; - let range_type = match res_type { + let ubo_type = ast.get_type(resource.base_type_id)?; + let range_type = match ubo_type { Type::Struct { member_types, .. } => { - let range_type = member_types.get(range.index as usize) + let range_type = member_types + .get(range.index as usize) .cloned() .ok_or(blame.error(SemanticsErrorKind::InvalidRange(range.index)))?; ast.get_type(range_type)? } - _ => return Err(blame.error(SemanticsErrorKind::InvalidResourceType)) + _ => return Err(blame.error(SemanticsErrorKind::InvalidResourceType)), }; - match options.uniform_semantics.get(&name) { - None => return Err(blame.error(SemanticsErrorKind::UnknownSemantics(name))), - Some(UniformSemantic::Variable(parameter)) => { - let Some(typeinfo) = parameter.semantics.validate_type(&range_type) else { - return Err(blame.error(SemanticsErrorKind::InvalidTypeForSemantic(name))) - }; + if let Some(parameter) = options + .uniform_semantics + .get_variable_semantic(&name) + { + let Some(typeinfo) = parameter.semantics.validate_type(&range_type) else { + return Err(blame.error(SemanticsErrorKind::InvalidTypeForSemantic(name))) + }; - match ¶meter.semantics { - VariableSemantics::FloatParameter => { - let offset = offset_type(range.offset); - if let Some(meta) = meta.parameter_meta.get(¶meter.index) { - if offset != meta.offset { - return Err(ShaderReflectError::MismatchedOffset { semantic: name, vertex: meta.offset, fragment: offset }) - } - if meta.components != typeinfo.size { - return Err(ShaderReflectError::MismatchedComponent { semantic: name, vertex: meta.components, fragment: typeinfo.size }) - } - } else { - meta.parameter_meta.insert(parameter.index, VariableMeta { - offset, - components: typeinfo.size + match ¶meter.semantics { + VariableSemantics::FloatParameter => { + let offset = offset_type(range.offset); + if let Some(meta) = meta.parameter_meta.get(¶meter.index) { + if offset != meta.offset { + return Err(ShaderReflectError::MismatchedOffset { + semantic: name, + vertex: meta.offset, + fragment: offset, }); } - } - semantics => { - let offset = offset_type(range.offset); - if let Some(meta) = meta.variable_meta.get(semantics) { - if offset != meta.offset { - return Err(ShaderReflectError::MismatchedOffset { semantic: name, vertex: meta.offset, fragment: offset }) - } - if meta.components != typeinfo.size * typeinfo.columns { - return Err(ShaderReflectError::MismatchedComponent { semantic: name, vertex: meta.components, fragment: typeinfo.size }) - } - } else { - meta.parameter_meta.insert(parameter.index, VariableMeta { - offset, - components: typeinfo.size * typeinfo.columns + if meta.components != typeinfo.size { + return Err(ShaderReflectError::MismatchedComponent { + semantic: name, + vertex: meta.components, + fragment: typeinfo.size, }); } + } else { + meta.parameter_meta.insert( + parameter.index, + VariableMeta { + offset, + components: typeinfo.size, + }, + ); } } - }, - Some(UniformSemantic::Texture(texture)) => { - let Some(_typeinfo) = texture.semantics.validate_type(&range_type) else { - return Err(blame.error(SemanticsErrorKind::InvalidTypeForSemantic(name))) - }; - - if let TextureSemantics::PassOutput = texture.semantics { - if texture.index >= options.pass_number { - return Err(ShaderReflectError::NonCausalFilterChain { pass: options.pass_number, target: texture.index }) + semantics => { + let offset = offset_type(range.offset); + if let Some(meta) = meta.variable_meta.get(semantics) { + if offset != meta.offset { + return Err(ShaderReflectError::MismatchedOffset { + semantic: name, + vertex: meta.offset, + fragment: offset, + }); + } + if meta.components != typeinfo.size * typeinfo.columns { + return Err(ShaderReflectError::MismatchedComponent { + semantic: name, + vertex: meta.components, + fragment: typeinfo.size, + }); + } + } else { + meta.variable_meta.insert( + *semantics, + VariableMeta { + offset, + components: typeinfo.size * typeinfo.columns, + }, + ); } } + } + } else if let Some(texture) = options + .uniform_semantics + .get_texture_semantic(&name) + { + let Some(_typeinfo) = texture.semantics.validate_type(&range_type) else { + return Err(blame.error(SemanticsErrorKind::InvalidTypeForSemantic(name))) + }; - let offset = offset_type(range.offset); - if let Some(meta) = meta.texture_meta.get(&texture) { - if offset != meta.offset { - return Err(ShaderReflectError::MismatchedOffset { semantic: name, vertex: meta.offset, fragment: offset }) - } - } else { - meta.texture_meta.insert(texture.clone(), TextureMeta { - offset, - stage_mask: BindingStage::empty(), - texture: false + if let TextureSemantics::PassOutput = texture.semantics { + if texture.index >= options.pass_number { + return Err(ShaderReflectError::NonCausalFilterChain { + pass: options.pass_number, + target: texture.index, }); } } + + let offset = offset_type(range.offset); + if let Some(meta) = meta.texture_size_meta.get_mut(&texture) { + if offset != meta.offset { + return Err(ShaderReflectError::MismatchedOffset { + semantic: name, + vertex: meta.offset, + fragment: offset, + }); + } + + meta.stage_mask.insert(match blame { + SemanticErrorBlame::Vertex => BindingStage::VERTEX, + SemanticErrorBlame::Fragment => BindingStage::FRAGMENT + }); + } else { + meta.texture_size_meta.insert( + texture.clone(), + TextureSizeMeta { + offset, + // todo: fix this. + stage_mask: match blame { + SemanticErrorBlame::Vertex => BindingStage::VERTEX, + SemanticErrorBlame::Fragment => BindingStage::FRAGMENT + } + }, + ); + } + } else { + return Err(blame.error(SemanticsErrorKind::UnknownSemantics(name))) } } Ok(()) } - fn reflect_ubos(&self, vertex_ubo: Option<&Resource>, fragment_ubo: Option<&Resource>) -> Result, ShaderReflectError> { + fn reflect_ubos( + &self, + vertex_ubo: Option<&Resource>, + fragment_ubo: Option<&Resource>, + ) -> Result, ShaderReflectError> { match (vertex_ubo, fragment_ubo) { (None, None) => Ok(None), (Some(vertex_ubo), Some(fragment_ubo)) => { - let vertex_ubo = Self::get_ubo_data(&self.vertex, vertex_ubo, SemanticErrorBlame::Vertex)?; - let fragment_ubo = Self::get_ubo_data(&self.fragment, fragment_ubo, SemanticErrorBlame::Fragment)?; + let vertex_ubo = + Self::get_ubo_data(&self.vertex, vertex_ubo, SemanticErrorBlame::Vertex)?; + let fragment_ubo = + Self::get_ubo_data(&self.fragment, fragment_ubo, SemanticErrorBlame::Fragment)?; if vertex_ubo.binding != fragment_ubo.binding { return Err(ShaderReflectError::MismatchedUniformBuffer { vertex: vertex_ubo.binding, - fragment: fragment_ubo.binding + fragment: fragment_ubo.binding, }); } @@ -331,61 +541,124 @@ impl CrossReflect Ok(Some(UboReflection { binding: vertex_ubo.binding, size, - stage_mask: BindingStage::VERTEX | BindingStage::FRAGMENT + stage_mask: BindingStage::VERTEX | BindingStage::FRAGMENT, })) } (Some(vertex_ubo), None) => { - let vertex_ubo = Self::get_ubo_data(&self.vertex, vertex_ubo, SemanticErrorBlame::Vertex)?; + let vertex_ubo = + Self::get_ubo_data(&self.vertex, vertex_ubo, SemanticErrorBlame::Vertex)?; Ok(Some(UboReflection { binding: vertex_ubo.binding, size: vertex_ubo.size, - stage_mask: BindingStage::VERTEX + stage_mask: BindingStage::VERTEX, })) } (None, Some(fragment_ubo)) => { - let fragment_ubo = Self::get_ubo_data(&self.fragment, fragment_ubo, SemanticErrorBlame::Fragment)?; + let fragment_ubo = + Self::get_ubo_data(&self.fragment, fragment_ubo, SemanticErrorBlame::Fragment)?; Ok(Some(UboReflection { binding: fragment_ubo.binding, size: fragment_ubo.size, - stage_mask: BindingStage::FRAGMENT + stage_mask: BindingStage::FRAGMENT, })) } } } - fn reflect_push_constant_buffer(&self, vertex_pcb: Option<&Resource>, fragment_pcb: Option<&Resource>) -> Result, ShaderReflectError> { + fn reflect_texture_metas( + &self, + texture: TextureData, + options: &ReflectOptions, + meta: &mut ReflectMeta, + ) -> Result<(), ShaderReflectError> { + let Some(semantic) = options.non_uniform_semantics.get_texture_semantic(texture.name) else { + return Err(SemanticErrorBlame::Fragment.error(SemanticsErrorKind::UnknownSemantics(texture.name.to_string()))) + }; + + if semantic.semantics == TextureSemantics::PassOutput && semantic.index >= options.pass_number { + return Err(ShaderReflectError::NonCausalFilterChain { pass: options.pass_number, target: semantic.index }) + } + + meta.texture_meta.insert(semantic, TextureImage { + binding: texture.binding + }); + Ok(()) + } + + fn reflect_texture<'a>( + &'a self, + texture: &'a Resource, + ) -> Result, ShaderReflectError> { + let descriptor_set = self + .fragment + .get_decoration(texture.id, Decoration::DescriptorSet)?; + let binding = self + .fragment + .get_decoration(texture.id, Decoration::Binding)?; + if descriptor_set != 0 { + return Err(ShaderReflectError::FragmentSemanticError( + SemanticsErrorKind::InvalidDescriptorSet(descriptor_set), + )); + } + if binding >= MAX_BINDINGS_COUNT { + return Err(ShaderReflectError::FragmentSemanticError( + SemanticsErrorKind::InvalidBinding(binding), + )); + } + Ok(TextureData { + id: texture.id, + name: &texture.name, + descriptor_set, + binding, + }) + } + + fn reflect_push_constant_buffer( + &self, + vertex_pcb: Option<&Resource>, + fragment_pcb: Option<&Resource>, + ) -> Result, ShaderReflectError> { match (vertex_pcb, fragment_pcb) { (None, None) => Ok(None), (Some(vertex_push), Some(fragment_push)) => { - let vertex_size = Self::get_push_size(&self.vertex, vertex_push, SemanticErrorBlame::Vertex)?; - let fragment_size = Self::get_push_size(&self.fragment, fragment_push, SemanticErrorBlame::Fragment)?; + let vertex_size = + Self::get_push_size(&self.vertex, vertex_push, SemanticErrorBlame::Vertex)?; + let fragment_size = Self::get_push_size( + &self.fragment, + fragment_push, + SemanticErrorBlame::Fragment, + )?; let size = std::cmp::max(vertex_size, fragment_size); Ok(Some(PushReflection { size, - stage_mask: BindingStage::VERTEX | BindingStage::FRAGMENT + stage_mask: BindingStage::VERTEX | BindingStage::FRAGMENT, })) } (Some(vertex_push), None) => { - let vertex_size = Self::get_push_size(&self.vertex, vertex_push, SemanticErrorBlame::Vertex)?; + let vertex_size = + Self::get_push_size(&self.vertex, vertex_push, SemanticErrorBlame::Vertex)?; Ok(Some(PushReflection { size: vertex_size, - stage_mask: BindingStage::VERTEX + stage_mask: BindingStage::VERTEX, })) } (None, Some(fragment_push)) => { - let fragment_size = Self::get_push_size(&self.fragment, fragment_push, SemanticErrorBlame::Fragment)?; + let fragment_size = Self::get_push_size( + &self.fragment, + fragment_push, + SemanticErrorBlame::Fragment, + )?; Ok(Some(PushReflection { size: fragment_size, - stage_mask: BindingStage::FRAGMENT + stage_mask: BindingStage::FRAGMENT, })) } } } } - impl ReflectShader for CrossReflect where T: spirv_cross::spirv::Target, @@ -410,26 +683,72 @@ where let mut meta = ReflectMeta::default(); if let Some(ubo) = vertex_ubo { - Self::add_active_buffer_range(&self.vertex, ubo, options, &mut meta, MemberOffset::Ubo, SemanticErrorBlame::Vertex)?; + Self::reflect_buffer_range_metas( + &self.vertex, + ubo, + options, + &mut meta, + MemberOffset::Ubo, + SemanticErrorBlame::Vertex, + )?; } if let Some(ubo) = fragment_ubo { - Self::add_active_buffer_range(&self.fragment, ubo, options, &mut meta, MemberOffset::Ubo, SemanticErrorBlame::Vertex)?; + Self::reflect_buffer_range_metas( + &self.fragment, + ubo, + options, + &mut meta, + MemberOffset::Ubo, + SemanticErrorBlame::Fragment, + )?; } if let Some(push) = vertex_push { - Self::add_active_buffer_range(&self.vertex, push, options, &mut meta, MemberOffset::PushConstant, SemanticErrorBlame::Vertex)?; + Self::reflect_buffer_range_metas( + &self.vertex, + push, + options, + &mut meta, + MemberOffset::PushConstant, + SemanticErrorBlame::Vertex, + )?; } if let Some(push) = fragment_push { - Self::add_active_buffer_range(&self.fragment, push, options, &mut meta, MemberOffset::PushConstant, SemanticErrorBlame::Vertex)?; + Self::reflect_buffer_range_metas( + &self.fragment, + push, + options, + &mut meta, + MemberOffset::PushConstant, + SemanticErrorBlame::Fragment, + )?; } // todo: slang_reflection: 556 + let mut ubo_bindings = 0u16; + if vertex_ubo.is_some() || fragment_ubo.is_some() { + ubo_bindings = 1 << ubo.as_ref().expect("UBOs should be present").binding; + } + + for sampled_image in &fragment_res.sampled_images { + let texture_data = self.reflect_texture(sampled_image)?; + if ubo_bindings & (1 << texture_data.binding) != 0 { + return Err(ShaderReflectError::BindingInUse(texture_data.binding)); + } + ubo_bindings |= (1 << texture_data.binding); + + self.reflect_texture_metas(texture_data, options, &mut meta)?; + } + + // slang-reflection:611 + + Ok(ShaderReflection { ubo, push_constant, - meta + meta, }) } } @@ -437,22 +756,24 @@ where #[cfg(test)] mod test { use crate::reflect::cross::CrossReflect; + use crate::reflect::{ReflectOptions, ReflectShader}; use rspirv::binary::Disassemble; use spirv_cross::{glsl, hlsl}; - use crate::reflect::{ReflectOptions, ReflectShader}; #[test] pub fn test_into() { let result = librashader_preprocess::load_shader_source("../test/basic.slang").unwrap(); - + let spirv = crate::front::shaderc::compile_spirv(&result).unwrap(); let mut reflect = CrossReflect::::try_from(spirv).unwrap(); - let huh = reflect.reflect(&ReflectOptions { - pass_number: 0, - uniform_semantics: Default::default(), - non_uniform_semantics: Default::default() - }).unwrap(); - eprintln!("{:?}", huh); + let huh = reflect + .reflect(&ReflectOptions { + pass_number: 0, + uniform_semantics: Default::default(), + non_uniform_semantics: Default::default(), + }) + .unwrap(); + eprintln!("{:#?}", huh); eprintln!("{:#}", reflect.fragment.compile().unwrap()) // let mut loader = rspirv::dr::Loader::new(); // rspirv::binary::parse_words(spirv.fragment.as_binary(), &mut loader).unwrap(); diff --git a/librashader-reflect/src/reflect/mod.rs b/librashader-reflect/src/reflect/mod.rs index 5e073ee..2860022 100644 --- a/librashader-reflect/src/reflect/mod.rs +++ b/librashader-reflect/src/reflect/mod.rs @@ -1,11 +1,11 @@ +use crate::error::{SemanticsErrorKind, ShaderReflectError}; +use crate::reflect::semantics::{SemanticMap, ShaderReflection, TextureSizeMeta, TextureSemantics, VariableMeta, VariableSemantics, TextureImage}; use rustc_hash::FxHashMap; -use crate::error::ShaderReflectError; -use crate::reflect::semantics::{SemanticMap, ShaderReflection, VariableSemantics, TextureSemantics, VariableMeta, TextureMeta}; mod cross; mod naga; -pub mod semantics; mod rspirv; +pub mod semantics; pub trait ReflectShader { fn reflect(&self, options: &ReflectOptions) -> Result; @@ -14,7 +14,7 @@ pub trait ReflectShader { #[derive(Debug)] pub enum UniformSemantic { Variable(SemanticMap), - Texture(SemanticMap) + Texture(SemanticMap), } #[derive(Debug)] @@ -28,35 +28,51 @@ pub struct ReflectOptions { pub struct ReflectMeta { pub parameter_meta: FxHashMap, pub variable_meta: FxHashMap, - pub texture_meta: FxHashMap, TextureMeta> + pub texture_meta: FxHashMap, TextureImage>, + pub texture_size_meta: FxHashMap, TextureSizeMeta>, } pub fn builtin_uniform_semantics() -> FxHashMap { let mut map = FxHashMap::default(); - map.insert("MVP".into(), UniformSemantic::Variable(SemanticMap { - semantics: VariableSemantics::MVP, - index: 0 - })); + map.insert( + "MVP".into(), + UniformSemantic::Variable(SemanticMap { + semantics: VariableSemantics::MVP, + index: 0, + }), + ); - map.insert("OutputSize".into(), UniformSemantic::Variable(SemanticMap { - semantics: VariableSemantics::Output, - index: 0 - })); + map.insert( + "OutputSize".into(), + UniformSemantic::Variable(SemanticMap { + semantics: VariableSemantics::Output, + index: 0, + }), + ); - map.insert("FinalViewportSize".into(), UniformSemantic::Variable(SemanticMap { - semantics: VariableSemantics::FinalViewport, - index: 0 - })); + map.insert( + "FinalViewportSize".into(), + UniformSemantic::Variable(SemanticMap { + semantics: VariableSemantics::FinalViewport, + index: 0, + }), + ); - map.insert("FrameCount".into(), UniformSemantic::Variable(SemanticMap { - semantics: VariableSemantics::FrameCount, - index: 0 - })); + map.insert( + "FrameCount".into(), + UniformSemantic::Variable(SemanticMap { + semantics: VariableSemantics::FrameCount, + index: 0, + }), + ); - map.insert("FrameDirection".into(), UniformSemantic::Variable(SemanticMap { - semantics: VariableSemantics::FrameDirection, - index: 0 - })); + map.insert( + "FrameDirection".into(), + UniformSemantic::Variable(SemanticMap { + semantics: VariableSemantics::FrameDirection, + index: 0, + }), + ); map -} \ No newline at end of file +} diff --git a/librashader-reflect/src/reflect/rspirv.rs b/librashader-reflect/src/reflect/rspirv.rs index 45bbe57..4c0c643 100644 --- a/librashader-reflect/src/reflect/rspirv.rs +++ b/librashader-reflect/src/reflect/rspirv.rs @@ -1,7 +1,7 @@ -use rspirv_reflect::Reflection; -use shaderc::CompilationArtifact; use crate::error::ShaderReflectError; use crate::front::shaderc::GlslangCompilation; +use rspirv_reflect::Reflection; +use shaderc::CompilationArtifact; pub struct RspirvReflect { vertex: Reflection, @@ -18,7 +18,6 @@ impl TryFrom for RspirvReflect { type Error = ShaderReflectError; fn try_from(value: GlslangCompilation) -> Result { - let vertex = parse_reflection(value.vertex)?; let fragment = parse_reflection(value.fragment)?; diff --git a/librashader-reflect/src/reflect/semantics.rs b/librashader-reflect/src/reflect/semantics.rs index b3f015e..6b5727f 100644 --- a/librashader-reflect/src/reflect/semantics.rs +++ b/librashader-reflect/src/reflect/semantics.rs @@ -1,6 +1,6 @@ use crate::error::ShaderReflectError; -use bitflags::bitflags; use crate::reflect::ReflectMeta; +use bitflags::bitflags; pub const BASE_SEMANTICS_COUNT: usize = 5; pub const MAX_BINDINGS_COUNT: u32 = 16; @@ -34,6 +34,43 @@ pub enum TextureSemantics { User = 5, } +impl TextureSemantics { + pub const TEXTURE_SEMANTICS: [TextureSemantics; 6] = [ + TextureSemantics::Original, + TextureSemantics::Source, + TextureSemantics::OriginalHistory, + TextureSemantics::PassOutput, + TextureSemantics::PassFeedback, + TextureSemantics::User, + ]; + + pub fn size_uniform_name(&self) -> &'static str { + match self { + TextureSemantics::Original => "OriginalSize", + TextureSemantics::Source => "SourceSize", + TextureSemantics::OriginalHistory => "OriginalHistorySize", + TextureSemantics::PassOutput => "PassOutputSize", + TextureSemantics::PassFeedback => "PassFeedbackSize", + TextureSemantics::User => "UserSize", + } + } + + pub fn texture_name(&self) -> &'static str { + match self { + TextureSemantics::Original => "Original", + TextureSemantics::Source => "Source", + TextureSemantics::OriginalHistory => "OriginalHistory", + TextureSemantics::PassOutput => "PassOutput", + TextureSemantics::PassFeedback => "PassFeedback", + TextureSemantics::User => "User", + } + } + + pub fn is_array(&self) -> bool { + !matches!(self, TextureSemantics::Original | TextureSemantics::Source) + } +} + pub struct TypeInfo { pub size: u32, pub columns: u32, @@ -45,7 +82,7 @@ pub trait ValidateTypeSemantics { #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] pub struct SemanticMap { pub(crate) semantics: T, - pub(crate) index: u32 + pub(crate) index: u32, } bitflags! { @@ -78,27 +115,31 @@ pub struct PushReflection { #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub enum MemberOffset { Ubo(usize), - PushConstant(usize) + PushConstant(usize), } #[derive(Debug)] pub struct VariableMeta { // this might bite us in the back because retroarch keeps separate UBO/push offsets.. eh pub offset: MemberOffset, - pub components: u32 + pub components: u32, } #[derive(Debug)] -pub struct TextureMeta { +pub struct TextureSizeMeta { // this might bite us in the back because retroarch keeps separate UBO/push offsets.. pub offset: MemberOffset, pub stage_mask: BindingStage, - pub texture: bool +} + +#[derive(Debug)] +pub struct TextureImage { + pub binding: u32, } #[derive(Debug)] pub struct ShaderReflection { pub ubo: Option, pub push_constant: Option, - pub meta: ReflectMeta + pub meta: ReflectMeta, } diff --git a/test/basic.slang b/test/basic.slang index 64ea63b..a280382 100644 --- a/test/basic.slang +++ b/test/basic.slang @@ -31,5 +31,5 @@ layout(binding = 1) uniform sampler2D Source; void main() { // FragColor = texture(sampler2D(Source, _Source_sampler), vTexCoord) * ColorMod; - FragColor = texture(Source, vTexCoord) * ColorMod; + FragColor = texture(Source, vTexCoord) * SourceSize[0]; } \ No newline at end of file