reflect: implement shader reflection
This commit is contained in:
parent
75bbd3eacf
commit
073921b9fb
|
@ -1,5 +1,5 @@
|
||||||
use thiserror::Error;
|
|
||||||
use crate::reflect::semantics::MemberOffset;
|
use crate::reflect::semantics::MemberOffset;
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
#[derive(Error, Debug)]
|
||||||
pub enum ShaderCompileError {
|
pub enum ShaderCompileError {
|
||||||
|
@ -25,7 +25,7 @@ pub enum SemanticsErrorKind {
|
||||||
InvalidResourceType,
|
InvalidResourceType,
|
||||||
InvalidRange(u32),
|
InvalidRange(u32),
|
||||||
UnknownSemantics(String),
|
UnknownSemantics(String),
|
||||||
InvalidTypeForSemantic(String)
|
InvalidTypeForSemantic(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
#[derive(Error, Debug)]
|
||||||
|
@ -45,10 +45,19 @@ pub enum ShaderReflectError {
|
||||||
#[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")]
|
#[error("mismatched offset")]
|
||||||
MismatchedOffset { semantic: String, vertex: MemberOffset, fragment: MemberOffset },
|
MismatchedOffset {
|
||||||
|
semantic: String,
|
||||||
|
vertex: MemberOffset,
|
||||||
|
fragment: MemberOffset,
|
||||||
|
},
|
||||||
#[error("mismatched component")]
|
#[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<Vec<naga::front::glsl::Error>> for ShaderCompileError {
|
impl From<Vec<naga::front::glsl::Error>> for ShaderCompileError {
|
||||||
|
|
|
@ -18,14 +18,14 @@ pub fn compile_spirv(source: &ShaderSource) -> Result<NagaCompilation, ShaderCom
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use rspirv::binary::Disassemble;
|
|
||||||
use crate::front::naga::compile_spirv;
|
use crate::front::naga::compile_spirv;
|
||||||
use naga::front::glsl::{Options, Parser};
|
|
||||||
use naga::back::glsl::{PipelineOptions, Version};
|
use naga::back::glsl::{PipelineOptions, Version};
|
||||||
use naga::back::spv::{Capability, WriterFlags};
|
use naga::back::spv::{Capability, WriterFlags};
|
||||||
use naga::{FastHashSet, ShaderStage};
|
use naga::front::glsl::{Options, Parser};
|
||||||
use naga::front::spv::Options as SpvOptions;
|
use naga::front::spv::Options as SpvOptions;
|
||||||
use naga::valid::{Capabilities, ModuleInfo, ValidationFlags};
|
use naga::valid::{Capabilities, ModuleInfo, ValidationFlags};
|
||||||
|
use naga::{FastHashSet, ShaderStage};
|
||||||
|
use rspirv::binary::Disassemble;
|
||||||
#[test]
|
#[test]
|
||||||
pub fn compile_naga_test() {
|
pub fn compile_naga_test() {
|
||||||
let result = librashader_preprocess::load_shader_source(
|
let result = librashader_preprocess::load_shader_source(
|
||||||
|
@ -53,13 +53,11 @@ mod test {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
pub fn compile_shader_roundtrip() {
|
pub fn compile_shader_roundtrip() {
|
||||||
let result = librashader_preprocess::load_shader_source(
|
let result = librashader_preprocess::load_shader_source("../test/basic.slang").unwrap();
|
||||||
"../test/basic.slang",
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
let cross = crate::front::shaderc::compile_spirv(&result).unwrap();
|
let cross = crate::front::shaderc::compile_spirv(&result).unwrap();
|
||||||
let naga_fragment = naga::front::spv::parse_u8_slice(cross.fragment.as_binary_u8(), &SpvOptions::default())
|
let naga_fragment =
|
||||||
.unwrap();
|
naga::front::spv::parse_u8_slice(cross.fragment.as_binary_u8(), &SpvOptions::default())
|
||||||
|
.unwrap();
|
||||||
println!("{:#?}", naga_fragment.constants);
|
println!("{:#?}", naga_fragment.constants);
|
||||||
println!("{:#?}", naga_fragment.global_variables);
|
println!("{:#?}", naga_fragment.global_variables);
|
||||||
println!("{:#?}", naga_fragment.types);
|
println!("{:#?}", naga_fragment.types);
|
||||||
|
@ -67,13 +65,12 @@ mod test {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
pub fn naga_playground() {
|
pub fn naga_playground() {
|
||||||
let result = librashader_preprocess::load_shader_source(
|
let result = librashader_preprocess::load_shader_source("../test/basic.slang").unwrap();
|
||||||
"../test/basic.slang",
|
let spirv = crate::front::shaderc::compile_spirv(&result).unwrap();
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
let spirv = crate::front::shaderc::compile_spirv(&result).unwrap();
|
|
||||||
|
|
||||||
let module = naga::front::spv::parse_u8_slice(spirv.fragment.as_binary_u8(), &SpvOptions::default()).unwrap();
|
let module =
|
||||||
|
naga::front::spv::parse_u8_slice(spirv.fragment.as_binary_u8(), &SpvOptions::default())
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
let capability = FastHashSet::from_iter([Capability::Shader]);
|
let capability = FastHashSet::from_iter([Capability::Shader]);
|
||||||
let mut writer = naga::back::spv::Writer::new(&naga::back::spv::Options {
|
let mut writer = naga::back::spv::Writer::new(&naga::back::spv::Options {
|
||||||
|
@ -81,34 +78,48 @@ mod test {
|
||||||
flags: WriterFlags::all(),
|
flags: WriterFlags::all(),
|
||||||
binding_map: Default::default(),
|
binding_map: Default::default(),
|
||||||
capabilities: Some(capability),
|
capabilities: Some(capability),
|
||||||
bounds_check_policies: Default::default()
|
bounds_check_policies: Default::default(),
|
||||||
}).unwrap();
|
})
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
let mut validator = naga::valid::Validator::new(ValidationFlags::empty(), Capabilities::all());
|
let mut validator =
|
||||||
|
naga::valid::Validator::new(ValidationFlags::empty(), Capabilities::all());
|
||||||
let info = validator.validate(&module).unwrap();
|
let info = validator.validate(&module).unwrap();
|
||||||
let mut spv_out = Vec::new();
|
let mut spv_out = Vec::new();
|
||||||
let pipe = naga::back::spv::PipelineOptions {
|
let pipe = naga::back::spv::PipelineOptions {
|
||||||
shader_stage: ShaderStage::Fragment,
|
shader_stage: ShaderStage::Fragment,
|
||||||
entry_point: "main".to_string()
|
entry_point: "main".to_string(),
|
||||||
};
|
};
|
||||||
writer.write(&module, &info, Some(&pipe), &mut spv_out).unwrap();
|
writer
|
||||||
|
.write(&module, &info, Some(&pipe), &mut spv_out)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
let mut glsl_out = String::new();
|
let mut glsl_out = String::new();
|
||||||
let opts = naga::back::glsl::Options {
|
let opts = naga::back::glsl::Options {
|
||||||
version: Version::Desktop(330),
|
version: Version::Desktop(330),
|
||||||
writer_flags: naga::back::glsl::WriterFlags::all(),
|
writer_flags: naga::back::glsl::WriterFlags::all(),
|
||||||
binding_map: Default::default()
|
binding_map: Default::default(),
|
||||||
};
|
};
|
||||||
let pipe = PipelineOptions {
|
let pipe = PipelineOptions {
|
||||||
shader_stage: ShaderStage::Fragment,
|
shader_stage: ShaderStage::Fragment,
|
||||||
entry_point: "main".to_string(),
|
entry_point: "main".to_string(),
|
||||||
multiview: None
|
multiview: None,
|
||||||
};
|
};
|
||||||
let mut glsl_naga = naga::back::glsl::Writer::new(&mut glsl_out, &module, &info, &opts, &pipe, Default::default()).unwrap();
|
let mut glsl_naga = naga::back::glsl::Writer::new(
|
||||||
|
&mut glsl_out,
|
||||||
|
&module,
|
||||||
|
&info,
|
||||||
|
&opts,
|
||||||
|
&pipe,
|
||||||
|
Default::default(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
glsl_naga.write().unwrap();
|
glsl_naga.write().unwrap();
|
||||||
|
|
||||||
let wgsl = naga::back::wgsl::write_string(&module, &info, naga::back::wgsl::WriterFlags::all()).unwrap();
|
let wgsl =
|
||||||
|
naga::back::wgsl::write_string(&module, &info, naga::back::wgsl::WriterFlags::all())
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
let mut loader = rspirv::dr::Loader::new();
|
let mut loader = rspirv::dr::Loader::new();
|
||||||
rspirv::binary::parse_words(&spv_out, &mut loader).unwrap();
|
rspirv::binary::parse_words(&spv_out, &mut loader).unwrap();
|
||||||
|
@ -118,8 +129,7 @@ mod test {
|
||||||
println!("--- cross glsl --");
|
println!("--- cross glsl --");
|
||||||
|
|
||||||
let loaded = spirv_cross::spirv::Module::from_words(&spv_out);
|
let loaded = spirv_cross::spirv::Module::from_words(&spv_out);
|
||||||
let mut ast = spirv_cross::spirv::Ast::<spirv_cross::glsl::Target>::parse(&loaded)
|
let mut ast = spirv_cross::spirv::Ast::<spirv_cross::glsl::Target>::parse(&loaded).unwrap();
|
||||||
.unwrap();
|
|
||||||
println!("{:#}", ast.compile().unwrap());
|
println!("{:#}", ast.compile().unwrap());
|
||||||
println!("--- naga glsl---");
|
println!("--- naga glsl---");
|
||||||
println!("{:#}", glsl_out);
|
println!("{:#}", glsl_out);
|
||||||
|
|
|
@ -126,10 +126,7 @@ mod test {
|
||||||
use crate::front::shaderc::compile_spirv;
|
use crate::front::shaderc::compile_spirv;
|
||||||
#[test]
|
#[test]
|
||||||
pub fn compile_shader() {
|
pub fn compile_shader() {
|
||||||
let result = librashader_preprocess::load_shader_source(
|
let result = librashader_preprocess::load_shader_source("../test/basic.slang").unwrap();
|
||||||
"../test/basic.slang",
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
let spirv = compile_spirv(&result).unwrap();
|
let spirv = compile_spirv(&result).unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
|
use crate::error::{SemanticsErrorKind, ShaderReflectError};
|
||||||
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, 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 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 rustc_hash::FxHashMap;
|
||||||
use spirv_cross::{ErrorCode, hlsl};
|
|
||||||
use spirv_cross::hlsl::{CompilerOptions, ShaderModel};
|
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<T>
|
pub struct CrossReflect<T>
|
||||||
where
|
where
|
||||||
|
@ -24,21 +24,29 @@ impl ValidateTypeSemantics<Type> for VariableSemantics {
|
||||||
};
|
};
|
||||||
|
|
||||||
if !array.is_empty() {
|
if !array.is_empty() {
|
||||||
return None
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let valid = match self {
|
let valid = match self {
|
||||||
VariableSemantics::MVP => matches!(ty, &Type::Float { .. }) && vecsize == 4 && columns == 4,
|
VariableSemantics::MVP => {
|
||||||
VariableSemantics::FrameCount => matches!(ty, &Type::UInt { .. }) && vecsize == 1 && columns == 1,
|
matches!(ty, &Type::Float { .. }) && vecsize == 4 && columns == 4
|
||||||
VariableSemantics::FrameDirection => matches!(ty, &Type::Int { .. }) && vecsize == 1 && columns == 1,
|
}
|
||||||
VariableSemantics::FloatParameter => matches!(ty, &Type::Float { .. }) && vecsize == 1 && columns == 1,
|
VariableSemantics::FrameCount => {
|
||||||
_ => matches!(ty, Type::Float { .. }) && vecsize == 4 && columns == 1
|
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 {
|
if valid {
|
||||||
Some(TypeInfo {
|
Some(TypeInfo {
|
||||||
size: vecsize,
|
size: vecsize,
|
||||||
columns
|
columns,
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
|
@ -53,13 +61,13 @@ impl ValidateTypeSemantics<Type> for TextureSemantics {
|
||||||
};
|
};
|
||||||
|
|
||||||
if !array.is_empty() {
|
if !array.is_empty() {
|
||||||
return None
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
if vecsize == 4 && columns == 1 {
|
if vecsize == 4 && columns == 1 {
|
||||||
Some(TypeInfo {
|
Some(TypeInfo {
|
||||||
size: vecsize,
|
size: vecsize,
|
||||||
columns
|
columns,
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
|
@ -67,8 +75,7 @@ impl ValidateTypeSemantics<Type> for TextureSemantics {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<GlslangCompilation> for CrossReflect<hlsl::Target>
|
impl TryFrom<GlslangCompilation> for CrossReflect<hlsl::Target> {
|
||||||
{
|
|
||||||
type Error = ShaderReflectError;
|
type Error = ShaderReflectError;
|
||||||
|
|
||||||
fn try_from(value: GlslangCompilation) -> Result<Self, Self::Error> {
|
fn try_from(value: GlslangCompilation) -> Result<Self, Self::Error> {
|
||||||
|
@ -87,13 +94,17 @@ impl TryFrom<GlslangCompilation> for CrossReflect<hlsl::Target>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl <T> CrossReflect<T>
|
impl<T> CrossReflect<T>
|
||||||
where
|
where
|
||||||
T: spirv_cross::spirv::Target,
|
T: spirv_cross::spirv::Target,
|
||||||
Ast<T>: spirv_cross::spirv::Compile<T>,
|
Ast<T>: spirv_cross::spirv::Compile<T>,
|
||||||
Ast<T>: spirv_cross::spirv::Parse<T>,
|
Ast<T>: spirv_cross::spirv::Parse<T>,
|
||||||
{
|
{
|
||||||
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()
|
if !vertex_res.sampled_images.is_empty()
|
||||||
|| !vertex_res.storage_buffers.is_empty()
|
|| !vertex_res.storage_buffers.is_empty()
|
||||||
|| !vertex_res.subpass_inputs.is_empty()
|
|| !vertex_res.subpass_inputs.is_empty()
|
||||||
|
@ -129,17 +140,20 @@ impl <T> CrossReflect<T>
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
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 {
|
if fragment_location != 0 {
|
||||||
return Err(ShaderReflectError::FragmentSemanticError(
|
return Err(ShaderReflectError::FragmentSemanticError(
|
||||||
SemanticsErrorKind::InvalidLocation(fragment_location),
|
SemanticsErrorKind::InvalidLocation(fragment_location),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut vert_mask = vertex_res.stage_inputs.iter()
|
let mut vert_mask = vertex_res.stage_inputs.iter().try_fold(0, |mask, input| {
|
||||||
.try_fold(0, |mask, input| {
|
Ok::<u32, ErrorCode>(
|
||||||
Ok::<u32, ErrorCode>(mask | 1 << self.vertex.get_decoration(input.id, Decoration::Location)?)
|
mask | 1 << self.vertex.get_decoration(input.id, Decoration::Location)?,
|
||||||
})?;
|
)
|
||||||
|
})?;
|
||||||
if vert_mask != 0x3 {
|
if vert_mask != 0x3 {
|
||||||
return Err(ShaderReflectError::VertexSemanticError(
|
return Err(ShaderReflectError::VertexSemanticError(
|
||||||
SemanticsErrorKind::InvalidLocation(vert_mask),
|
SemanticsErrorKind::InvalidLocation(vert_mask),
|
||||||
|
@ -154,7 +168,9 @@ impl <T> CrossReflect<T>
|
||||||
|
|
||||||
if vertex_res.push_constant_buffers.len() > 1 {
|
if vertex_res.push_constant_buffers.len() > 1 {
|
||||||
return Err(ShaderReflectError::VertexSemanticError(
|
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 <T> CrossReflect<T>
|
||||||
|
|
||||||
if fragment_res.push_constant_buffers.len() > 1 {
|
if fragment_res.push_constant_buffers.len() > 1 {
|
||||||
return Err(ShaderReflectError::FragmentSemanticError(
|
return Err(ShaderReflectError::FragmentSemanticError(
|
||||||
SemanticsErrorKind::InvalidUniformBufferCount(fragment_res.push_constant_buffers.len()),
|
SemanticsErrorKind::InvalidUniformBufferCount(
|
||||||
|
fragment_res.push_constant_buffers.len(),
|
||||||
|
),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
|
||||||
enum SemanticErrorBlame {
|
|
||||||
Vertex,
|
|
||||||
Fragment
|
|
||||||
}
|
|
||||||
|
|
||||||
struct UboData {
|
struct UboData {
|
||||||
id: u32,
|
id: u32,
|
||||||
descriptor_set: u32,
|
descriptor_set: u32,
|
||||||
binding: 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 {
|
impl SemanticErrorBlame {
|
||||||
fn error(self, kind: SemanticsErrorKind) -> ShaderReflectError {
|
fn error(self, kind: SemanticsErrorKind) -> ShaderReflectError {
|
||||||
return match self {
|
return match self {
|
||||||
SemanticErrorBlame::Vertex => ShaderReflectError::VertexSemanticError(kind),
|
SemanticErrorBlame::Vertex => ShaderReflectError::VertexSemanticError(kind),
|
||||||
SemanticErrorBlame::Fragment => ShaderReflectError::FragmentSemanticError(kind)
|
SemanticErrorBlame::Fragment => ShaderReflectError::FragmentSemanticError(kind),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
trait TextureSemanticMap<T> {
|
||||||
|
fn get_texture_semantic(
|
||||||
|
&self,
|
||||||
|
name: &str,
|
||||||
|
) -> Option<SemanticMap<TextureSemantics>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
trait VariableSemanticMap<T> {
|
||||||
|
fn get_variable_semantic(
|
||||||
|
&self,
|
||||||
|
name: &str,
|
||||||
|
) -> Option<SemanticMap<VariableSemantics>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl VariableSemanticMap<UniformSemantic> for FxHashMap<String, UniformSemantic> {
|
||||||
|
fn get_variable_semantic(
|
||||||
|
&self,
|
||||||
|
name: &str,
|
||||||
|
) -> Option<SemanticMap<VariableSemantics>> {
|
||||||
|
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 <T> CrossReflect<T>
|
impl TextureSemanticMap<UniformSemantic> for FxHashMap<String, UniformSemantic> {
|
||||||
where
|
fn get_texture_semantic(
|
||||||
T: spirv_cross::spirv::Target,
|
&self,
|
||||||
Ast<T>: spirv_cross::spirv::Compile<T>,
|
name: &str,
|
||||||
Ast<T>: spirv_cross::spirv::Parse<T>,
|
) -> Option<SemanticMap<TextureSemantics>> {
|
||||||
|
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<UniformSemantic> for FxHashMap<String, SemanticMap<TextureSemantics>> {
|
||||||
|
fn get_texture_semantic(
|
||||||
|
&self,
|
||||||
|
name: &str,
|
||||||
|
) -> Option<SemanticMap<TextureSemantics>> {
|
||||||
|
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<T> CrossReflect<T>
|
||||||
|
where
|
||||||
|
T: spirv_cross::spirv::Target,
|
||||||
|
Ast<T>: spirv_cross::spirv::Compile<T>,
|
||||||
|
Ast<T>: spirv_cross::spirv::Parse<T>,
|
||||||
{
|
{
|
||||||
fn get_ubo_data(ast: &Ast<T>, ubo: &Resource, blame: SemanticErrorBlame) -> Result<UboData, ShaderReflectError> {
|
fn get_ubo_data(
|
||||||
|
ast: &Ast<T>,
|
||||||
|
ubo: &Resource,
|
||||||
|
blame: SemanticErrorBlame,
|
||||||
|
) -> Result<UboData, ShaderReflectError> {
|
||||||
let descriptor_set = ast.get_decoration(ubo.id, Decoration::DescriptorSet)?;
|
let descriptor_set = ast.get_decoration(ubo.id, Decoration::DescriptorSet)?;
|
||||||
let binding = ast.get_decoration(ubo.id, Decoration::Binding)?;
|
let binding = ast.get_decoration(ubo.id, Decoration::Binding)?;
|
||||||
if binding >= MAX_BINDINGS_COUNT {
|
if binding >= MAX_BINDINGS_COUNT {
|
||||||
return Err(blame.error(SemanticsErrorKind::InvalidBinding(binding)))
|
return Err(blame.error(SemanticsErrorKind::InvalidBinding(binding)));
|
||||||
}
|
}
|
||||||
if descriptor_set != 0 {
|
if descriptor_set != 0 {
|
||||||
return Err(blame.error(SemanticsErrorKind::InvalidDescriptorSet(descriptor_set)));
|
return Err(blame.error(SemanticsErrorKind::InvalidDescriptorSet(descriptor_set)));
|
||||||
|
@ -215,11 +363,15 @@ impl <T> CrossReflect<T>
|
||||||
descriptor_set,
|
descriptor_set,
|
||||||
binding,
|
binding,
|
||||||
id: ubo.id,
|
id: ubo.id,
|
||||||
size
|
size,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_push_size(ast: &Ast<T>, push: &Resource, blame: SemanticErrorBlame) -> Result<u32, ShaderReflectError> {
|
fn get_push_size(
|
||||||
|
ast: &Ast<T>,
|
||||||
|
push: &Resource,
|
||||||
|
blame: SemanticErrorBlame,
|
||||||
|
) -> Result<u32, ShaderReflectError> {
|
||||||
let size = ast.get_declared_struct_size(push.base_type_id)?;
|
let size = ast.get_declared_struct_size(push.base_type_id)?;
|
||||||
if size >= MAX_PUSH_BUFFER_SIZE {
|
if size >= MAX_PUSH_BUFFER_SIZE {
|
||||||
return Err(blame.error(SemanticsErrorKind::InvalidPushBufferSize(size)));
|
return Err(blame.error(SemanticsErrorKind::InvalidPushBufferSize(size)));
|
||||||
|
@ -227,103 +379,161 @@ impl <T> CrossReflect<T>
|
||||||
Ok(size)
|
Ok(size)
|
||||||
}
|
}
|
||||||
|
|
||||||
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> {
|
fn reflect_buffer_range_metas(
|
||||||
|
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)?;
|
||||||
|
eprintln!("{:?}", ranges);
|
||||||
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)?;
|
||||||
let res_type = ast.get_type(resource.base_type_id)?;
|
let ubo_type = ast.get_type(resource.base_type_id)?;
|
||||||
let range_type = match res_type {
|
let range_type = match ubo_type {
|
||||||
Type::Struct { member_types, .. } => {
|
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()
|
.cloned()
|
||||||
.ok_or(blame.error(SemanticsErrorKind::InvalidRange(range.index)))?;
|
.ok_or(blame.error(SemanticsErrorKind::InvalidRange(range.index)))?;
|
||||||
ast.get_type(range_type)?
|
ast.get_type(range_type)?
|
||||||
}
|
}
|
||||||
_ => return Err(blame.error(SemanticsErrorKind::InvalidResourceType))
|
_ => return Err(blame.error(SemanticsErrorKind::InvalidResourceType)),
|
||||||
};
|
};
|
||||||
|
|
||||||
match options.uniform_semantics.get(&name) {
|
if let Some(parameter) = options
|
||||||
None => return Err(blame.error(SemanticsErrorKind::UnknownSemantics(name))),
|
.uniform_semantics
|
||||||
Some(UniformSemantic::Variable(parameter)) => {
|
.get_variable_semantic(&name)
|
||||||
let Some(typeinfo) = parameter.semantics.validate_type(&range_type) else {
|
{
|
||||||
return Err(blame.error(SemanticsErrorKind::InvalidTypeForSemantic(name)))
|
let Some(typeinfo) = parameter.semantics.validate_type(&range_type) else {
|
||||||
};
|
return Err(blame.error(SemanticsErrorKind::InvalidTypeForSemantic(name)))
|
||||||
|
};
|
||||||
|
|
||||||
match ¶meter.semantics {
|
match ¶meter.semantics {
|
||||||
VariableSemantics::FloatParameter => {
|
VariableSemantics::FloatParameter => {
|
||||||
let offset = offset_type(range.offset);
|
let offset = offset_type(range.offset);
|
||||||
if let Some(meta) = meta.parameter_meta.get(¶meter.index) {
|
if let Some(meta) = meta.parameter_meta.get(¶meter.index) {
|
||||||
if offset != meta.offset {
|
if offset != meta.offset {
|
||||||
return Err(ShaderReflectError::MismatchedOffset { semantic: name, vertex: meta.offset, fragment: offset })
|
return Err(ShaderReflectError::MismatchedOffset {
|
||||||
}
|
semantic: name,
|
||||||
if meta.components != typeinfo.size {
|
vertex: meta.offset,
|
||||||
return Err(ShaderReflectError::MismatchedComponent { semantic: name, vertex: meta.components, fragment: typeinfo.size })
|
fragment: offset,
|
||||||
}
|
|
||||||
} else {
|
|
||||||
meta.parameter_meta.insert(parameter.index, VariableMeta {
|
|
||||||
offset,
|
|
||||||
components: typeinfo.size
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
if meta.components != typeinfo.size {
|
||||||
semantics => {
|
return Err(ShaderReflectError::MismatchedComponent {
|
||||||
let offset = offset_type(range.offset);
|
semantic: name,
|
||||||
if let Some(meta) = meta.variable_meta.get(semantics) {
|
vertex: meta.components,
|
||||||
if offset != meta.offset {
|
fragment: typeinfo.size,
|
||||||
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
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
meta.parameter_meta.insert(
|
||||||
|
parameter.index,
|
||||||
|
VariableMeta {
|
||||||
|
offset,
|
||||||
|
components: typeinfo.size,
|
||||||
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
semantics => {
|
||||||
Some(UniformSemantic::Texture(texture)) => {
|
let offset = offset_type(range.offset);
|
||||||
let Some(_typeinfo) = texture.semantics.validate_type(&range_type) else {
|
if let Some(meta) = meta.variable_meta.get(semantics) {
|
||||||
return Err(blame.error(SemanticsErrorKind::InvalidTypeForSemantic(name)))
|
if offset != meta.offset {
|
||||||
};
|
return Err(ShaderReflectError::MismatchedOffset {
|
||||||
|
semantic: name,
|
||||||
if let TextureSemantics::PassOutput = texture.semantics {
|
vertex: meta.offset,
|
||||||
if texture.index >= options.pass_number {
|
fragment: offset,
|
||||||
return Err(ShaderReflectError::NonCausalFilterChain { pass: options.pass_number, target: texture.index })
|
});
|
||||||
|
}
|
||||||
|
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 TextureSemantics::PassOutput = texture.semantics {
|
||||||
if let Some(meta) = meta.texture_meta.get(&texture) {
|
if texture.index >= options.pass_number {
|
||||||
if offset != meta.offset {
|
return Err(ShaderReflectError::NonCausalFilterChain {
|
||||||
return Err(ShaderReflectError::MismatchedOffset { semantic: name, vertex: meta.offset, fragment: offset })
|
pass: options.pass_number,
|
||||||
}
|
target: texture.index,
|
||||||
} else {
|
|
||||||
meta.texture_meta.insert(texture.clone(), TextureMeta {
|
|
||||||
offset,
|
|
||||||
stage_mask: BindingStage::empty(),
|
|
||||||
texture: false
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn reflect_ubos(&self, vertex_ubo: Option<&Resource>, fragment_ubo: Option<&Resource>) -> Result<Option<UboReflection>, ShaderReflectError> {
|
fn reflect_ubos(
|
||||||
|
&self,
|
||||||
|
vertex_ubo: Option<&Resource>,
|
||||||
|
fragment_ubo: Option<&Resource>,
|
||||||
|
) -> Result<Option<UboReflection>, ShaderReflectError> {
|
||||||
match (vertex_ubo, fragment_ubo) {
|
match (vertex_ubo, fragment_ubo) {
|
||||||
(None, None) => Ok(None),
|
(None, None) => Ok(None),
|
||||||
(Some(vertex_ubo), Some(fragment_ubo)) => {
|
(Some(vertex_ubo), Some(fragment_ubo)) => {
|
||||||
let vertex_ubo = Self::get_ubo_data(&self.vertex, vertex_ubo, SemanticErrorBlame::Vertex)?;
|
let vertex_ubo =
|
||||||
let fragment_ubo = Self::get_ubo_data(&self.fragment, fragment_ubo, SemanticErrorBlame::Fragment)?;
|
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 {
|
if vertex_ubo.binding != fragment_ubo.binding {
|
||||||
return Err(ShaderReflectError::MismatchedUniformBuffer {
|
return Err(ShaderReflectError::MismatchedUniformBuffer {
|
||||||
vertex: vertex_ubo.binding,
|
vertex: vertex_ubo.binding,
|
||||||
fragment: fragment_ubo.binding
|
fragment: fragment_ubo.binding,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -331,61 +541,124 @@ impl <T> CrossReflect<T>
|
||||||
Ok(Some(UboReflection {
|
Ok(Some(UboReflection {
|
||||||
binding: vertex_ubo.binding,
|
binding: vertex_ubo.binding,
|
||||||
size,
|
size,
|
||||||
stage_mask: BindingStage::VERTEX | BindingStage::FRAGMENT
|
stage_mask: BindingStage::VERTEX | BindingStage::FRAGMENT,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
(Some(vertex_ubo), None) => {
|
(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 {
|
Ok(Some(UboReflection {
|
||||||
binding: vertex_ubo.binding,
|
binding: vertex_ubo.binding,
|
||||||
size: vertex_ubo.size,
|
size: vertex_ubo.size,
|
||||||
stage_mask: BindingStage::VERTEX
|
stage_mask: BindingStage::VERTEX,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
(None, Some(fragment_ubo)) => {
|
(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 {
|
Ok(Some(UboReflection {
|
||||||
binding: fragment_ubo.binding,
|
binding: fragment_ubo.binding,
|
||||||
size: fragment_ubo.size,
|
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<Option<PushReflection>, 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<TextureData<'a>, 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<Option<PushReflection>, ShaderReflectError> {
|
||||||
match (vertex_pcb, fragment_pcb) {
|
match (vertex_pcb, fragment_pcb) {
|
||||||
(None, None) => Ok(None),
|
(None, None) => Ok(None),
|
||||||
(Some(vertex_push), Some(fragment_push)) => {
|
(Some(vertex_push), Some(fragment_push)) => {
|
||||||
let vertex_size = Self::get_push_size(&self.vertex, vertex_push, SemanticErrorBlame::Vertex)?;
|
let vertex_size =
|
||||||
let fragment_size = Self::get_push_size(&self.fragment, fragment_push, SemanticErrorBlame::Fragment)?;
|
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);
|
let size = std::cmp::max(vertex_size, fragment_size);
|
||||||
|
|
||||||
Ok(Some(PushReflection {
|
Ok(Some(PushReflection {
|
||||||
size,
|
size,
|
||||||
stage_mask: BindingStage::VERTEX | BindingStage::FRAGMENT
|
stage_mask: BindingStage::VERTEX | BindingStage::FRAGMENT,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
(Some(vertex_push), None) => {
|
(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 {
|
Ok(Some(PushReflection {
|
||||||
size: vertex_size,
|
size: vertex_size,
|
||||||
stage_mask: BindingStage::VERTEX
|
stage_mask: BindingStage::VERTEX,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
(None, Some(fragment_push)) => {
|
(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 {
|
Ok(Some(PushReflection {
|
||||||
size: fragment_size,
|
size: fragment_size,
|
||||||
stage_mask: BindingStage::FRAGMENT
|
stage_mask: BindingStage::FRAGMENT,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
impl<T> ReflectShader for CrossReflect<T>
|
impl<T> ReflectShader for CrossReflect<T>
|
||||||
where
|
where
|
||||||
T: spirv_cross::spirv::Target,
|
T: spirv_cross::spirv::Target,
|
||||||
|
@ -410,26 +683,72 @@ where
|
||||||
let mut meta = ReflectMeta::default();
|
let mut meta = ReflectMeta::default();
|
||||||
|
|
||||||
if let Some(ubo) = vertex_ubo {
|
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 {
|
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 {
|
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 {
|
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
|
// 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 {
|
Ok(ShaderReflection {
|
||||||
ubo,
|
ubo,
|
||||||
push_constant,
|
push_constant,
|
||||||
meta
|
meta,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -437,9 +756,9 @@ where
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use crate::reflect::cross::CrossReflect;
|
use crate::reflect::cross::CrossReflect;
|
||||||
|
use crate::reflect::{ReflectOptions, ReflectShader};
|
||||||
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() {
|
||||||
|
@ -447,12 +766,14 @@ mod test {
|
||||||
|
|
||||||
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 {
|
let huh = reflect
|
||||||
pass_number: 0,
|
.reflect(&ReflectOptions {
|
||||||
uniform_semantics: Default::default(),
|
pass_number: 0,
|
||||||
non_uniform_semantics: Default::default()
|
uniform_semantics: Default::default(),
|
||||||
}).unwrap();
|
non_uniform_semantics: Default::default(),
|
||||||
eprintln!("{:?}", huh);
|
})
|
||||||
|
.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,11 +1,11 @@
|
||||||
|
use crate::error::{SemanticsErrorKind, ShaderReflectError};
|
||||||
|
use crate::reflect::semantics::{SemanticMap, ShaderReflection, TextureSizeMeta, TextureSemantics, VariableMeta, VariableSemantics, TextureImage};
|
||||||
use rustc_hash::FxHashMap;
|
use rustc_hash::FxHashMap;
|
||||||
use crate::error::ShaderReflectError;
|
|
||||||
use crate::reflect::semantics::{SemanticMap, ShaderReflection, VariableSemantics, TextureSemantics, VariableMeta, TextureMeta};
|
|
||||||
|
|
||||||
mod cross;
|
mod cross;
|
||||||
mod naga;
|
mod naga;
|
||||||
pub mod semantics;
|
|
||||||
mod rspirv;
|
mod rspirv;
|
||||||
|
pub mod semantics;
|
||||||
|
|
||||||
pub trait ReflectShader {
|
pub trait ReflectShader {
|
||||||
fn reflect(&self, options: &ReflectOptions) -> Result<ShaderReflection, ShaderReflectError>;
|
fn reflect(&self, options: &ReflectOptions) -> Result<ShaderReflection, ShaderReflectError>;
|
||||||
|
@ -14,7 +14,7 @@ pub trait ReflectShader {
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum UniformSemantic {
|
pub enum UniformSemantic {
|
||||||
Variable(SemanticMap<VariableSemantics>),
|
Variable(SemanticMap<VariableSemantics>),
|
||||||
Texture(SemanticMap<TextureSemantics>)
|
Texture(SemanticMap<TextureSemantics>),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -28,35 +28,51 @@ pub struct ReflectOptions {
|
||||||
pub struct ReflectMeta {
|
pub struct ReflectMeta {
|
||||||
pub parameter_meta: FxHashMap<u32, VariableMeta>,
|
pub parameter_meta: FxHashMap<u32, VariableMeta>,
|
||||||
pub variable_meta: FxHashMap<VariableSemantics, VariableMeta>,
|
pub variable_meta: FxHashMap<VariableSemantics, VariableMeta>,
|
||||||
pub texture_meta: FxHashMap<SemanticMap<TextureSemantics>, TextureMeta>
|
pub texture_meta: FxHashMap<SemanticMap<TextureSemantics>, TextureImage>,
|
||||||
|
pub texture_size_meta: FxHashMap<SemanticMap<TextureSemantics>, TextureSizeMeta>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn builtin_uniform_semantics() -> FxHashMap<String, UniformSemantic> {
|
pub fn builtin_uniform_semantics() -> FxHashMap<String, UniformSemantic> {
|
||||||
let mut map = FxHashMap::default();
|
let mut map = FxHashMap::default();
|
||||||
|
|
||||||
map.insert("MVP".into(), UniformSemantic::Variable(SemanticMap {
|
map.insert(
|
||||||
semantics: VariableSemantics::MVP,
|
"MVP".into(),
|
||||||
index: 0
|
UniformSemantic::Variable(SemanticMap {
|
||||||
}));
|
semantics: VariableSemantics::MVP,
|
||||||
|
index: 0,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
map.insert("OutputSize".into(), UniformSemantic::Variable(SemanticMap {
|
map.insert(
|
||||||
semantics: VariableSemantics::Output,
|
"OutputSize".into(),
|
||||||
index: 0
|
UniformSemantic::Variable(SemanticMap {
|
||||||
}));
|
semantics: VariableSemantics::Output,
|
||||||
|
index: 0,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
map.insert("FinalViewportSize".into(), UniformSemantic::Variable(SemanticMap {
|
map.insert(
|
||||||
semantics: VariableSemantics::FinalViewport,
|
"FinalViewportSize".into(),
|
||||||
index: 0
|
UniformSemantic::Variable(SemanticMap {
|
||||||
}));
|
semantics: VariableSemantics::FinalViewport,
|
||||||
|
index: 0,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
map.insert("FrameCount".into(), UniformSemantic::Variable(SemanticMap {
|
map.insert(
|
||||||
semantics: VariableSemantics::FrameCount,
|
"FrameCount".into(),
|
||||||
index: 0
|
UniformSemantic::Variable(SemanticMap {
|
||||||
}));
|
semantics: VariableSemantics::FrameCount,
|
||||||
|
index: 0,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
map.insert("FrameDirection".into(), UniformSemantic::Variable(SemanticMap {
|
map.insert(
|
||||||
semantics: VariableSemantics::FrameDirection,
|
"FrameDirection".into(),
|
||||||
index: 0
|
UniformSemantic::Variable(SemanticMap {
|
||||||
}));
|
semantics: VariableSemantics::FrameDirection,
|
||||||
|
index: 0,
|
||||||
|
}),
|
||||||
|
);
|
||||||
map
|
map
|
||||||
}
|
}
|
|
@ -1,7 +1,7 @@
|
||||||
use rspirv_reflect::Reflection;
|
|
||||||
use shaderc::CompilationArtifact;
|
|
||||||
use crate::error::ShaderReflectError;
|
use crate::error::ShaderReflectError;
|
||||||
use crate::front::shaderc::GlslangCompilation;
|
use crate::front::shaderc::GlslangCompilation;
|
||||||
|
use rspirv_reflect::Reflection;
|
||||||
|
use shaderc::CompilationArtifact;
|
||||||
|
|
||||||
pub struct RspirvReflect {
|
pub struct RspirvReflect {
|
||||||
vertex: Reflection,
|
vertex: Reflection,
|
||||||
|
@ -18,7 +18,6 @@ impl TryFrom<GlslangCompilation> for RspirvReflect {
|
||||||
type Error = ShaderReflectError;
|
type Error = ShaderReflectError;
|
||||||
|
|
||||||
fn try_from(value: GlslangCompilation) -> Result<Self, Self::Error> {
|
fn try_from(value: GlslangCompilation) -> Result<Self, Self::Error> {
|
||||||
|
|
||||||
let vertex = parse_reflection(value.vertex)?;
|
let vertex = parse_reflection(value.vertex)?;
|
||||||
let fragment = parse_reflection(value.fragment)?;
|
let fragment = parse_reflection(value.fragment)?;
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::error::ShaderReflectError;
|
use crate::error::ShaderReflectError;
|
||||||
use bitflags::bitflags;
|
|
||||||
use crate::reflect::ReflectMeta;
|
use crate::reflect::ReflectMeta;
|
||||||
|
use bitflags::bitflags;
|
||||||
|
|
||||||
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;
|
||||||
|
@ -34,6 +34,43 @@ pub enum TextureSemantics {
|
||||||
User = 5,
|
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 struct TypeInfo {
|
||||||
pub size: u32,
|
pub size: u32,
|
||||||
pub columns: u32,
|
pub columns: u32,
|
||||||
|
@ -45,7 +82,7 @@ pub trait ValidateTypeSemantics<T> {
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
#[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,
|
||||||
}
|
}
|
||||||
|
|
||||||
bitflags! {
|
bitflags! {
|
||||||
|
@ -78,27 +115,31 @@ pub struct PushReflection {
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||||
pub enum MemberOffset {
|
pub enum MemberOffset {
|
||||||
Ubo(usize),
|
Ubo(usize),
|
||||||
PushConstant(usize)
|
PushConstant(usize),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct VariableMeta {
|
pub struct VariableMeta {
|
||||||
// this might bite us in the back because retroarch keeps separate UBO/push offsets.. eh
|
// this might bite us in the back because retroarch keeps separate UBO/push offsets.. eh
|
||||||
pub offset: MemberOffset,
|
pub offset: MemberOffset,
|
||||||
pub components: u32
|
pub components: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct TextureMeta {
|
pub struct TextureSizeMeta {
|
||||||
// this might bite us in the back because retroarch keeps separate UBO/push offsets..
|
// this might bite us in the back because retroarch keeps separate UBO/push offsets..
|
||||||
pub offset: MemberOffset,
|
pub offset: MemberOffset,
|
||||||
pub stage_mask: BindingStage,
|
pub stage_mask: BindingStage,
|
||||||
pub texture: bool
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct TextureImage {
|
||||||
|
pub binding: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[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
|
pub meta: ReflectMeta,
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,5 +31,5 @@ layout(binding = 1) uniform sampler2D Source;
|
||||||
void main()
|
void main()
|
||||||
{
|
{
|
||||||
// FragColor = texture(sampler2D(Source, _Source_sampler), vTexCoord) * ColorMod;
|
// FragColor = texture(sampler2D(Source, _Source_sampler), vTexCoord) * ColorMod;
|
||||||
FragColor = texture(Source, vTexCoord) * ColorMod;
|
FragColor = texture(Source, vTexCoord) * SourceSize[0];
|
||||||
}
|
}
|
Loading…
Reference in a new issue