reflect: finish buffer parameter parsing
This commit is contained in:
parent
3a0b545ad2
commit
75bbd3eacf
|
@ -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 {
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
#![feature(let_else)]
|
||||||
|
|
||||||
mod error;
|
mod error;
|
||||||
mod front;
|
mod front;
|
||||||
mod reflect;
|
mod reflect;
|
||||||
|
|
|
@ -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 ¶meter.semantics {
|
let Some(typeinfo) = parameter.semantics.validate_type(&range_type) else {
|
||||||
VariableSemantics::FloatParameter => {}
|
return Err(blame.error(SemanticsErrorKind::InvalidTypeForSemantic(name)))
|
||||||
semantics => {
|
};
|
||||||
|
|
||||||
|
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
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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();
|
||||||
|
|
|
@ -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> {
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue