reflect: implement shader reflection

This commit is contained in:
chyyran 2022-10-27 02:22:44 -04:00
parent 75bbd3eacf
commit 073921b9fb
8 changed files with 604 additions and 211 deletions

View file

@ -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 {

View file

@ -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);

View file

@ -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();
} }
} }

View file

@ -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 &parameter.semantics { match &parameter.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(&parameter.index) { if let Some(meta) = meta.parameter_meta.get(&parameter.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,22 +756,24 @@ 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() {
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 { 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();

View file

@ -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
} }

View file

@ -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)?;

View file

@ -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,
} }

View file

@ -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];
} }