reflect: finish buffer parameter parsing

This commit is contained in:
chyyran 2022-10-26 01:19:04 -04:00
parent 3a0b545ad2
commit 75bbd3eacf
5 changed files with 194 additions and 12 deletions

View file

@ -1,4 +1,5 @@
use thiserror::Error; use thiserror::Error;
use crate::reflect::semantics::MemberOffset;
#[derive(Error, Debug)] #[derive(Error, Debug)]
pub enum ShaderCompileError { pub enum ShaderCompileError {
@ -23,7 +24,8 @@ pub enum SemanticsErrorKind {
InvalidBinding(u32), InvalidBinding(u32),
InvalidResourceType, InvalidResourceType,
InvalidRange(u32), InvalidRange(u32),
UnknownSemantics(String) UnknownSemantics(String),
InvalidTypeForSemantic(String)
} }
#[derive(Error, Debug)] #[derive(Error, Debug)]
@ -41,7 +43,12 @@ pub enum ShaderReflectError {
#[error("vertx and fragment shader must have same binding")] #[error("vertx and fragment shader must have same binding")]
MismatchedUniformBuffer { vertex: u32, fragment: u32 }, MismatchedUniformBuffer { vertex: u32, fragment: u32 },
#[error("filter chain is non causal")] #[error("filter chain is non causal")]
NonCausalFilterChain { pass: u32, target: u32 } NonCausalFilterChain { pass: u32, target: u32 },
#[error("mismatched offset")]
MismatchedOffset { semantic: String, vertex: MemberOffset, fragment: MemberOffset },
#[error("mismatched component")]
MismatchedComponent { semantic: String, vertex: u32, fragment: u32 },
} }
impl From<Vec<naga::front::glsl::Error>> for ShaderCompileError { impl From<Vec<naga::front::glsl::Error>> for ShaderCompileError {

View file

@ -1,3 +1,5 @@
#![feature(let_else)]
mod error; mod error;
mod front; mod front;
mod reflect; mod reflect;

View file

@ -1,10 +1,11 @@
use crate::error::{ShaderReflectError, SemanticsErrorKind}; use crate::error::{ShaderReflectError, SemanticsErrorKind};
use crate::front::shaderc::GlslangCompilation; use crate::front::shaderc::GlslangCompilation;
use crate::reflect::semantics::{BindingStage, UboReflection, MAX_BINDINGS_COUNT, ShaderReflection, PushReflection, MAX_PUSH_BUFFER_SIZE, VariableSemantics, TextureSemantics}; 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::{ReflectOptions, ReflectShader, UniformSemantic}; use crate::reflect::{ReflectMeta, ReflectOptions, ReflectShader, UniformSemantic};
use spirv_cross::spirv::{Ast, Decoration, Module, Resource, ShaderResources, Type}; use spirv_cross::spirv::{Ast, Decoration, Module, Resource, ShaderResources, Type};
use std::fmt::Debug; use std::fmt::Debug;
use rustc_hash::FxHashMap;
use spirv_cross::{ErrorCode, hlsl}; use spirv_cross::{ErrorCode, hlsl};
use spirv_cross::hlsl::{CompilerOptions, ShaderModel}; use spirv_cross::hlsl::{CompilerOptions, ShaderModel};
@ -16,6 +17,56 @@ where
fragment: Ast<T>, fragment: Ast<T>,
} }
impl ValidateTypeSemantics<Type> for VariableSemantics {
fn validate_type(&self, ty: &Type) -> Option<TypeInfo> {
let (&Type::Float { ref array, vecsize, columns } | &Type::Int { ref array, vecsize, columns } | &Type::UInt { ref array, vecsize, columns }) = ty else {
return None
};
if !array.is_empty() {
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
};
if valid {
Some(TypeInfo {
size: vecsize,
columns
})
} else {
None
}
}
}
impl ValidateTypeSemantics<Type> for TextureSemantics {
fn validate_type(&self, ty: &Type) -> Option<TypeInfo> {
let &Type::Float { ref array, vecsize, columns } = ty else {
return None
};
if !array.is_empty() {
return None
}
if vecsize == 4 && columns == 1 {
Some(TypeInfo {
size: vecsize,
columns
})
} else {
None
}
}
}
impl TryFrom<GlslangCompilation> for CrossReflect<hlsl::Target> impl TryFrom<GlslangCompilation> for CrossReflect<hlsl::Target>
{ {
type Error = ShaderReflectError; type Error = ShaderReflectError;
@ -176,7 +227,8 @@ impl <T> CrossReflect<T>
Ok(size) Ok(size)
} }
fn add_active_buffer_range(ast: &Ast<T>, resource: &Resource, options: &ReflectOptions, blame: SemanticErrorBlame) -> Result<(), ShaderReflectError> { fn add_active_buffer_range(ast: &Ast<T>, 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)?; let ranges = ast.get_active_buffer_ranges(resource.id)?;
for range in ranges { for range in ranges {
let name = ast.get_member_name(resource.base_type_id, range.index)?; let name = ast.get_member_name(resource.base_type_id, range.index)?;
@ -194,21 +246,68 @@ impl <T> CrossReflect<T>
match options.uniform_semantics.get(&name) { match options.uniform_semantics.get(&name) {
None => return Err(blame.error(SemanticsErrorKind::UnknownSemantics(name))), None => return Err(blame.error(SemanticsErrorKind::UnknownSemantics(name))),
Some(UniformSemantic::Variable(parameter)) => { Some(UniformSemantic::Variable(parameter)) => {
match &parameter.semantics { let Some(typeinfo) = parameter.semantics.validate_type(&range_type) else {
VariableSemantics::FloatParameter => {} return Err(blame.error(SemanticsErrorKind::InvalidTypeForSemantic(name)))
semantics => { };
match &parameter.semantics {
VariableSemantics::FloatParameter => {
let offset = offset_type(range.offset);
if let Some(meta) = meta.parameter_meta.get(&parameter.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
});
}
}
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
});
}
} }
} }
}, },
Some(UniformSemantic::Texture(texture)) => { 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 let TextureSemantics::PassOutput = texture.semantics {
if texture.index >= options.pass_number { if texture.index >= options.pass_number {
return Err(ShaderReflectError::NonCausalFilterChain { pass: options.pass_number, target: texture.index }) return Err(ShaderReflectError::NonCausalFilterChain { pass: options.pass_number, target: texture.index })
} }
} }
// todo: validaate type 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
});
}
} }
} }
} }
@ -308,9 +407,29 @@ where
let push_constant = self.reflect_push_constant_buffer(vertex_push, fragment_push)?; let push_constant = self.reflect_push_constant_buffer(vertex_push, fragment_push)?;
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)?;
}
if let Some(ubo) = fragment_ubo {
Self::add_active_buffer_range(&self.fragment, ubo, options, &mut meta, MemberOffset::Ubo, SemanticErrorBlame::Vertex)?;
}
if let Some(push) = vertex_push {
Self::add_active_buffer_range(&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)?;
}
// todo: slang_reflection: 556
Ok(ShaderReflection { Ok(ShaderReflection {
ubo, ubo,
push_constant push_constant,
meta
}) })
} }
} }
@ -320,12 +439,20 @@ mod test {
use crate::reflect::cross::CrossReflect; use crate::reflect::cross::CrossReflect;
use rspirv::binary::Disassemble; use rspirv::binary::Disassemble;
use spirv_cross::{glsl, hlsl}; use spirv_cross::{glsl, hlsl};
use crate::reflect::{ReflectOptions, ReflectShader};
#[test] #[test]
pub fn test_into() { pub fn test_into() {
let result = librashader_preprocess::load_shader_source("../test/basic.slang").unwrap(); let result = librashader_preprocess::load_shader_source("../test/basic.slang").unwrap();
let spirv = crate::front::shaderc::compile_spirv(&result).unwrap(); let spirv = crate::front::shaderc::compile_spirv(&result).unwrap();
let mut reflect = CrossReflect::<hlsl::Target>::try_from(spirv).unwrap(); let mut reflect = CrossReflect::<hlsl::Target>::try_from(spirv).unwrap();
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()) eprintln!("{:#}", reflect.fragment.compile().unwrap())
// let mut loader = rspirv::dr::Loader::new(); // let mut loader = rspirv::dr::Loader::new();
// rspirv::binary::parse_words(spirv.fragment.as_binary(), &mut loader).unwrap(); // rspirv::binary::parse_words(spirv.fragment.as_binary(), &mut loader).unwrap();

View file

@ -1,6 +1,6 @@
use rustc_hash::FxHashMap; use rustc_hash::FxHashMap;
use crate::error::ShaderReflectError; use crate::error::ShaderReflectError;
use crate::reflect::semantics::{SemanticMap, ShaderReflection, VariableSemantics, TextureSemantics}; use crate::reflect::semantics::{SemanticMap, ShaderReflection, VariableSemantics, TextureSemantics, VariableMeta, TextureMeta};
mod cross; mod cross;
mod naga; mod naga;
@ -11,15 +11,24 @@ pub trait ReflectShader {
fn reflect(&self, options: &ReflectOptions) -> Result<ShaderReflection, ShaderReflectError>; fn reflect(&self, options: &ReflectOptions) -> Result<ShaderReflection, ShaderReflectError>;
} }
#[derive(Debug)]
pub enum UniformSemantic { pub enum UniformSemantic {
Variable(SemanticMap<VariableSemantics>), Variable(SemanticMap<VariableSemantics>),
Texture(SemanticMap<TextureSemantics>) Texture(SemanticMap<TextureSemantics>)
} }
#[derive(Debug)]
pub struct ReflectOptions { pub struct ReflectOptions {
pub pass_number: u32, pub pass_number: u32,
pub uniform_semantics: FxHashMap<String, UniformSemantic>, pub uniform_semantics: FxHashMap<String, UniformSemantic>,
pub non_uniform_semantics: FxHashMap<String, SemanticMap<TextureSemantics>> pub non_uniform_semantics: FxHashMap<String, SemanticMap<TextureSemantics>>,
}
#[derive(Debug, Default)]
pub struct ReflectMeta {
pub parameter_meta: FxHashMap<u32, VariableMeta>,
pub variable_meta: FxHashMap<VariableSemantics, VariableMeta>,
pub texture_meta: FxHashMap<SemanticMap<TextureSemantics>, TextureMeta>
} }
pub fn builtin_uniform_semantics() -> FxHashMap<String, UniformSemantic> { pub fn builtin_uniform_semantics() -> FxHashMap<String, UniformSemantic> {

View file

@ -1,10 +1,12 @@
use crate::error::ShaderReflectError; use crate::error::ShaderReflectError;
use bitflags::bitflags; use bitflags::bitflags;
use crate::reflect::ReflectMeta;
pub const BASE_SEMANTICS_COUNT: usize = 5; pub const BASE_SEMANTICS_COUNT: usize = 5;
pub const MAX_BINDINGS_COUNT: u32 = 16; pub const MAX_BINDINGS_COUNT: u32 = 16;
pub const MAX_PUSH_BUFFER_SIZE: u32 = 128; pub const MAX_PUSH_BUFFER_SIZE: u32 = 128;
#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Copy, Clone, Hash)]
#[repr(i32)] #[repr(i32)]
pub enum VariableSemantics { pub enum VariableSemantics {
// mat4, MVP // mat4, MVP
@ -21,6 +23,7 @@ pub enum VariableSemantics {
FloatParameter = 5, FloatParameter = 5,
} }
#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Copy, Clone, Hash)]
#[repr(i32)] #[repr(i32)]
pub enum TextureSemantics { pub enum TextureSemantics {
Original = 0, Original = 0,
@ -31,6 +34,15 @@ pub enum TextureSemantics {
User = 5, User = 5,
} }
pub struct TypeInfo {
pub size: u32,
pub columns: u32,
}
pub trait ValidateTypeSemantics<T> {
fn validate_type(&self, ty: &T) -> Option<TypeInfo>;
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub struct SemanticMap<T> { pub struct SemanticMap<T> {
pub(crate) semantics: T, pub(crate) semantics: T,
pub(crate) index: u32 pub(crate) index: u32
@ -50,18 +62,43 @@ impl BindingStage {
} }
} }
#[derive(Debug)]
pub struct UboReflection { pub struct UboReflection {
pub binding: u32, pub binding: u32,
pub size: u32, pub size: u32,
pub stage_mask: BindingStage, pub stage_mask: BindingStage,
} }
#[derive(Debug)]
pub struct PushReflection { pub struct PushReflection {
pub size: u32, pub size: u32,
pub stage_mask: BindingStage, pub stage_mask: BindingStage,
} }
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum MemberOffset {
Ubo(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
}
#[derive(Debug)]
pub struct TextureMeta {
// 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 ShaderReflection { pub struct ShaderReflection {
pub ubo: Option<UboReflection>, pub ubo: Option<UboReflection>,
pub push_constant: Option<PushReflection>, pub push_constant: Option<PushReflection>,
pub meta: ReflectMeta
} }