reflect: more work on semantics binding
This commit is contained in:
parent
809c58f34d
commit
3a0b545ad2
|
@ -4,6 +4,7 @@
|
||||||
<mapping directory="$PROJECT_DIR$/.." vcs="Git" />
|
<mapping directory="$PROJECT_DIR$/.." vcs="Git" />
|
||||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||||
<mapping directory="$PROJECT_DIR$/librashader-presets/test/slang-shaders" vcs="Git" />
|
<mapping directory="$PROJECT_DIR$/librashader-presets/test/slang-shaders" vcs="Git" />
|
||||||
|
<mapping directory="$PROJECT_DIR$/naga" vcs="Git" />
|
||||||
<mapping directory="$PROJECT_DIR$/test/slang-shaders" vcs="Git" />
|
<mapping directory="$PROJECT_DIR$/test/slang-shaders" vcs="Git" />
|
||||||
</component>
|
</component>
|
||||||
</project>
|
</project>
|
12
Cargo.lock
generated
12
Cargo.lock
generated
|
@ -427,10 +427,13 @@ dependencies = [
|
||||||
name = "librashader-reflect"
|
name = "librashader-reflect"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
"librashader",
|
"librashader",
|
||||||
"librashader-preprocess",
|
"librashader-preprocess",
|
||||||
"naga",
|
"naga",
|
||||||
"rspirv",
|
"rspirv",
|
||||||
|
"rspirv-reflect",
|
||||||
|
"rustc-hash",
|
||||||
"shaderc",
|
"shaderc",
|
||||||
"spirv_cross",
|
"spirv_cross",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
|
@ -689,6 +692,15 @@ dependencies = [
|
||||||
"spirv",
|
"spirv",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rspirv-reflect"
|
||||||
|
version = "0.7.0"
|
||||||
|
source = "git+https://github.com/Traverse-Research/rspirv-reflect#8894d3570cd738c61e26b08349f55277092b9e3c"
|
||||||
|
dependencies = [
|
||||||
|
"rspirv",
|
||||||
|
"thiserror",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustc-hash"
|
name = "rustc-hash"
|
||||||
version = "1.1.0"
|
version = "1.1.0"
|
||||||
|
|
|
@ -11,7 +11,10 @@ shaderc = { version = "0.8.0" }
|
||||||
spirv_cross = { version = "0.23.1", features = [ "glsl", "hlsl" ] }
|
spirv_cross = { version = "0.23.1", features = [ "glsl", "hlsl" ] }
|
||||||
librashader = { path = "../librashader" }
|
librashader = { path = "../librashader" }
|
||||||
thiserror = "1.0.37"
|
thiserror = "1.0.37"
|
||||||
|
bitflags = "1.3.2"
|
||||||
|
rustc-hash = "1.1.0"
|
||||||
|
rspirv = "0.11.0+1.5.4"
|
||||||
|
|
||||||
|
rspirv-reflect = { git = "https://github.com/Traverse-Research/rspirv-reflect" }
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
librashader-preprocess = { path = "../librashader-preprocess", default-features = false }
|
librashader-preprocess = { path = "../librashader-preprocess", default-features = false }
|
||||||
rspirv = "0.11.0+1.5.4"
|
|
||||||
|
|
|
@ -14,14 +14,16 @@ pub enum ShaderCompileError {
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum SemanticsErrorKind {
|
pub enum SemanticsErrorKind {
|
||||||
InvalidUniformBufferSize(usize),
|
InvalidUniformBufferCount(usize),
|
||||||
InvalidPushBufferSize(usize),
|
InvalidPushBufferSize(u32),
|
||||||
InvalidLocation(u32),
|
InvalidLocation(u32),
|
||||||
InvalidDescriptorSet(u32),
|
InvalidDescriptorSet(u32),
|
||||||
InvalidInputCount(usize),
|
InvalidInputCount(usize),
|
||||||
InvalidOutputCount(usize),
|
InvalidOutputCount(usize),
|
||||||
InvalidBinding(u32),
|
InvalidBinding(u32),
|
||||||
InvalidResourceType,
|
InvalidResourceType,
|
||||||
|
InvalidRange(u32),
|
||||||
|
UnknownSemantics(String)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
#[derive(Error, Debug)]
|
||||||
|
@ -30,14 +32,16 @@ pub enum ShaderReflectError {
|
||||||
NagaCompileError(#[from] naga::front::spv::Error),
|
NagaCompileError(#[from] naga::front::spv::Error),
|
||||||
#[error("spirv")]
|
#[error("spirv")]
|
||||||
SpirvCrossError(#[from] spirv_cross::ErrorCode),
|
SpirvCrossError(#[from] spirv_cross::ErrorCode),
|
||||||
|
#[error("rspirv")]
|
||||||
|
RspirvParseError(#[from] rspirv::binary::ParseState),
|
||||||
#[error("error when verifying vertex semantics")]
|
#[error("error when verifying vertex semantics")]
|
||||||
VertexSemanticError(SemanticsErrorKind),
|
VertexSemanticError(SemanticsErrorKind),
|
||||||
#[error("error when verifying texture semantics")]
|
#[error("error when verifying texture semantics")]
|
||||||
FragmentSemanticError(SemanticsErrorKind),
|
FragmentSemanticError(SemanticsErrorKind),
|
||||||
#[error("vertx and fragment shader must have same binding")]
|
#[error("vertx and fragment shader must have same binding")]
|
||||||
MismatchedUniformBuffer { vertex: Option<u32>, fragment: Option<u32> },
|
MismatchedUniformBuffer { vertex: u32, fragment: u32 },
|
||||||
#[error("binding exceeded max")]
|
#[error("filter chain is non causal")]
|
||||||
InvalidBinding(u32)
|
NonCausalFilterChain { pass: u32, target: u32 }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Vec<naga::front::glsl::Error>> for ShaderCompileError {
|
impl From<Vec<naga::front::glsl::Error>> for ShaderCompileError {
|
||||||
|
|
|
@ -86,8 +86,12 @@ mod test {
|
||||||
|
|
||||||
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 out = Vec::new();
|
let mut spv_out = Vec::new();
|
||||||
writer.write(&module, &info, None, &mut out).unwrap();
|
let pipe = naga::back::spv::PipelineOptions {
|
||||||
|
shader_stage: ShaderStage::Fragment,
|
||||||
|
entry_point: "main".to_string()
|
||||||
|
};
|
||||||
|
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 {
|
||||||
|
@ -107,13 +111,13 @@ mod test {
|
||||||
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(&out, &mut loader).unwrap();
|
rspirv::binary::parse_words(&spv_out, &mut loader).unwrap();
|
||||||
let module = loader.module();
|
let module = loader.module();
|
||||||
println!("--- spirv --");
|
println!("--- spirv --");
|
||||||
println!("{:#}", module.disassemble());
|
println!("{:#}", module.disassemble());
|
||||||
println!("--- cross glsl --");
|
println!("--- cross glsl --");
|
||||||
|
|
||||||
let loaded = spirv_cross::spirv::Module::from_words(&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());
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
|
|
||||||
use crate::error::{ShaderReflectError, SemanticsErrorKind};
|
use crate::error::{ShaderReflectError, SemanticsErrorKind};
|
||||||
use crate::front::shaderc::GlslangCompilation;
|
use crate::front::shaderc::GlslangCompilation;
|
||||||
use crate::reflect::semantics::{MAX_BINDINGS_COUNT, ShaderReflection};
|
use crate::reflect::semantics::{BindingStage, UboReflection, MAX_BINDINGS_COUNT, ShaderReflection, PushReflection, MAX_PUSH_BUFFER_SIZE, VariableSemantics, TextureSemantics};
|
||||||
use crate::reflect::ReflectShader;
|
use crate::reflect::{ReflectOptions, ReflectShader, UniformSemantic};
|
||||||
use spirv_cross::spirv::{Ast, Decoration, Module, ShaderResources};
|
use spirv_cross::spirv::{Ast, Decoration, Module, Resource, ShaderResources, Type};
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
use spirv_cross::{ErrorCode, hlsl};
|
use spirv_cross::{ErrorCode, hlsl};
|
||||||
use spirv_cross::hlsl::{CompilerOptions, ShaderModel};
|
use spirv_cross::hlsl::{CompilerOptions, ShaderModel};
|
||||||
|
@ -35,6 +35,7 @@ impl TryFrom<GlslangCompilation> for CrossReflect<hlsl::Target>
|
||||||
Ok(CrossReflect { vertex, fragment })
|
Ok(CrossReflect { vertex, fragment })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl <T> CrossReflect<T>
|
impl <T> CrossReflect<T>
|
||||||
where
|
where
|
||||||
T: spirv_cross::spirv::Target,
|
T: spirv_cross::spirv::Target,
|
||||||
|
@ -96,96 +97,221 @@ impl <T> CrossReflect<T>
|
||||||
|
|
||||||
if vertex_res.uniform_buffers.len() > 1 {
|
if vertex_res.uniform_buffers.len() > 1 {
|
||||||
return Err(ShaderReflectError::VertexSemanticError(
|
return Err(ShaderReflectError::VertexSemanticError(
|
||||||
SemanticsErrorKind::InvalidUniformBufferSize(vertex_res.uniform_buffers.len()),
|
SemanticsErrorKind::InvalidUniformBufferCount(vertex_res.uniform_buffers.len()),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
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::InvalidUniformBufferSize(vertex_res.push_constant_buffers.len()),
|
SemanticsErrorKind::InvalidUniformBufferCount(vertex_res.push_constant_buffers.len()),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
if fragment_res.uniform_buffers.len() > 1 {
|
if fragment_res.uniform_buffers.len() > 1 {
|
||||||
return Err(ShaderReflectError::FragmentSemanticError(
|
return Err(ShaderReflectError::FragmentSemanticError(
|
||||||
SemanticsErrorKind::InvalidUniformBufferSize(fragment_res.uniform_buffers.len()),
|
SemanticsErrorKind::InvalidUniformBufferCount(fragment_res.uniform_buffers.len()),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
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::InvalidUniformBufferSize(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 {
|
||||||
|
id: u32,
|
||||||
|
descriptor_set: u32,
|
||||||
|
binding: u32,
|
||||||
|
size: u32
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SemanticErrorBlame {
|
||||||
|
fn error(self, kind: SemanticsErrorKind) -> ShaderReflectError {
|
||||||
|
return match self {
|
||||||
|
SemanticErrorBlame::Vertex => ShaderReflectError::VertexSemanticError(kind),
|
||||||
|
SemanticErrorBlame::Fragment => ShaderReflectError::FragmentSemanticError(kind)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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> {
|
||||||
|
let descriptor_set = ast.get_decoration(ubo.id, Decoration::DescriptorSet)?;
|
||||||
|
let binding = ast.get_decoration(ubo.id, Decoration::Binding)?;
|
||||||
|
if binding >= MAX_BINDINGS_COUNT {
|
||||||
|
return Err(blame.error(SemanticsErrorKind::InvalidBinding(binding)))
|
||||||
|
}
|
||||||
|
if descriptor_set != 0 {
|
||||||
|
return Err(blame.error(SemanticsErrorKind::InvalidDescriptorSet(descriptor_set)));
|
||||||
|
}
|
||||||
|
let size = ast.get_declared_struct_size(ubo.base_type_id)?;
|
||||||
|
Ok(UboData {
|
||||||
|
descriptor_set,
|
||||||
|
binding,
|
||||||
|
id: ubo.id,
|
||||||
|
size
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
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)?;
|
||||||
|
if size >= MAX_PUSH_BUFFER_SIZE {
|
||||||
|
return Err(blame.error(SemanticsErrorKind::InvalidPushBufferSize(size)));
|
||||||
|
}
|
||||||
|
Ok(size)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_active_buffer_range(ast: &Ast<T>, resource: &Resource, options: &ReflectOptions, blame: SemanticErrorBlame) -> Result<(), ShaderReflectError> {
|
||||||
|
let ranges = ast.get_active_buffer_ranges(resource.id)?;
|
||||||
|
for range in ranges {
|
||||||
|
let name = ast.get_member_name(resource.base_type_id, range.index)?;
|
||||||
|
let res_type = ast.get_type(resource.base_type_id)?;
|
||||||
|
let range_type = match res_type {
|
||||||
|
Type::Struct { member_types, .. } => {
|
||||||
|
let range_type = member_types.get(range.index as usize)
|
||||||
|
.cloned()
|
||||||
|
.ok_or(blame.error(SemanticsErrorKind::InvalidRange(range.index)))?;
|
||||||
|
ast.get_type(range_type)?
|
||||||
|
}
|
||||||
|
_ => return Err(blame.error(SemanticsErrorKind::InvalidResourceType))
|
||||||
|
};
|
||||||
|
|
||||||
|
match options.uniform_semantics.get(&name) {
|
||||||
|
None => return Err(blame.error(SemanticsErrorKind::UnknownSemantics(name))),
|
||||||
|
Some(UniformSemantic::Variable(parameter)) => {
|
||||||
|
match ¶meter.semantics {
|
||||||
|
VariableSemantics::FloatParameter => {}
|
||||||
|
semantics => {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Some(UniformSemantic::Texture(texture)) => {
|
||||||
|
if let TextureSemantics::PassOutput = texture.semantics {
|
||||||
|
if texture.index >= options.pass_number {
|
||||||
|
return Err(ShaderReflectError::NonCausalFilterChain { pass: options.pass_number, target: texture.index })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// todo: validaate type
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reflect_ubos(&self, vertex_ubo: Option<&Resource>, fragment_ubo: Option<&Resource>) -> Result<Option<UboReflection>, ShaderReflectError> {
|
||||||
|
match (vertex_ubo, fragment_ubo) {
|
||||||
|
(None, None) => Ok(None),
|
||||||
|
(Some(vertex_ubo), Some(fragment_ubo)) => {
|
||||||
|
let vertex_ubo = Self::get_ubo_data(&self.vertex, vertex_ubo, SemanticErrorBlame::Vertex)?;
|
||||||
|
let fragment_ubo = Self::get_ubo_data(&self.fragment, fragment_ubo, SemanticErrorBlame::Fragment)?;
|
||||||
|
if vertex_ubo.binding != fragment_ubo.binding {
|
||||||
|
return Err(ShaderReflectError::MismatchedUniformBuffer {
|
||||||
|
vertex: vertex_ubo.binding,
|
||||||
|
fragment: fragment_ubo.binding
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let size = std::cmp::max(vertex_ubo.size, fragment_ubo.size);
|
||||||
|
Ok(Some(UboReflection {
|
||||||
|
binding: vertex_ubo.binding,
|
||||||
|
size,
|
||||||
|
stage_mask: BindingStage::VERTEX | BindingStage::FRAGMENT
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
(Some(vertex_ubo), None) => {
|
||||||
|
let vertex_ubo = Self::get_ubo_data(&self.vertex, vertex_ubo, SemanticErrorBlame::Vertex)?;
|
||||||
|
Ok(Some(UboReflection {
|
||||||
|
binding: vertex_ubo.binding,
|
||||||
|
size: vertex_ubo.size,
|
||||||
|
stage_mask: BindingStage::VERTEX
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
(None, Some(fragment_ubo)) => {
|
||||||
|
let fragment_ubo = Self::get_ubo_data(&self.fragment, fragment_ubo, SemanticErrorBlame::Fragment)?;
|
||||||
|
Ok(Some(UboReflection {
|
||||||
|
binding: fragment_ubo.binding,
|
||||||
|
size: fragment_ubo.size,
|
||||||
|
stage_mask: BindingStage::FRAGMENT
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reflect_push_constant_buffer(&self, vertex_pcb: Option<&Resource>, fragment_pcb: Option<&Resource>) -> Result<Option<PushReflection>, ShaderReflectError> {
|
||||||
|
match (vertex_pcb, fragment_pcb) {
|
||||||
|
(None, None) => Ok(None),
|
||||||
|
(Some(vertex_push), Some(fragment_push)) => {
|
||||||
|
let vertex_size = Self::get_push_size(&self.vertex, vertex_push, SemanticErrorBlame::Vertex)?;
|
||||||
|
let fragment_size = Self::get_push_size(&self.fragment, fragment_push, SemanticErrorBlame::Fragment)?;
|
||||||
|
|
||||||
|
let size = std::cmp::max(vertex_size, fragment_size);
|
||||||
|
|
||||||
|
Ok(Some(PushReflection {
|
||||||
|
size,
|
||||||
|
stage_mask: BindingStage::VERTEX | BindingStage::FRAGMENT
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
(Some(vertex_push), None) => {
|
||||||
|
let vertex_size = Self::get_push_size(&self.vertex, vertex_push, SemanticErrorBlame::Vertex)?;
|
||||||
|
Ok(Some(PushReflection {
|
||||||
|
size: vertex_size,
|
||||||
|
stage_mask: BindingStage::VERTEX
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
(None, Some(fragment_push)) => {
|
||||||
|
let fragment_size = Self::get_push_size(&self.fragment, fragment_push, SemanticErrorBlame::Fragment)?;
|
||||||
|
Ok(Some(PushReflection {
|
||||||
|
size: fragment_size,
|
||||||
|
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,
|
||||||
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 reflect(&self) -> Result<ShaderReflection, ShaderReflectError> {
|
fn reflect(&self, options: &ReflectOptions) -> Result<ShaderReflection, ShaderReflectError> {
|
||||||
let vertex_res = self.vertex.get_shader_resources()?;
|
let vertex_res = self.vertex.get_shader_resources()?;
|
||||||
let fragment_res = self.fragment.get_shader_resources()?;
|
let fragment_res = self.fragment.get_shader_resources()?;
|
||||||
self.validate(&vertex_res, &fragment_res)?;
|
self.validate(&vertex_res, &fragment_res)?;
|
||||||
|
|
||||||
let vertex_ubo = vertex_res.uniform_buffers.first().map(|f| f.id);
|
let vertex_ubo = vertex_res.uniform_buffers.first().map(|f| f);
|
||||||
let fragment_ubo = fragment_res.uniform_buffers.first().map(|f| f.id);
|
let fragment_ubo = fragment_res.uniform_buffers.first().map(|f| f);
|
||||||
|
|
||||||
let vertex_push = vertex_res.push_constant_buffers.first().map(|f| f.id);
|
let ubo = self.reflect_ubos(vertex_ubo, fragment_ubo)?;
|
||||||
let fragment_push = fragment_res.push_constant_buffers.first().map(|f| f.id);
|
|
||||||
|
|
||||||
if let Some(ubo) = vertex_ubo {
|
let vertex_push = vertex_res.push_constant_buffers.first().map(|f| f);
|
||||||
let desc_set = self.vertex.get_decoration(ubo, Decoration::DescriptorSet)?;
|
let fragment_push = fragment_res.push_constant_buffers.first().map(|f| f);
|
||||||
if desc_set != 0 {
|
|
||||||
return Err(ShaderReflectError::VertexSemanticError(SemanticsErrorKind::InvalidDescriptorSet(desc_set)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(ubo) = fragment_ubo {
|
let push_constant = self.reflect_push_constant_buffer(vertex_push, fragment_push)?;
|
||||||
let desc_set = self.fragment.get_decoration(ubo, Decoration::DescriptorSet)?;
|
|
||||||
if desc_set != 0 {
|
|
||||||
return Err(ShaderReflectError::FragmentSemanticError(SemanticsErrorKind::InvalidDescriptorSet(desc_set)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let vertex_ubo_binding = vertex_ubo.map(|s| self.vertex.get_decoration(s, Decoration::Binding))
|
Ok(ShaderReflection {
|
||||||
.map_or(Ok(None), |v| v.map(Some))?;
|
ubo,
|
||||||
|
push_constant
|
||||||
let fragment_ubo_binding = vertex_ubo.map(|s| self.fragment.get_decoration(s, Decoration::Binding))
|
})
|
||||||
.map_or(Ok(None), |v| v.map(Some))?;
|
|
||||||
|
|
||||||
match (vertex_ubo_binding, fragment_ubo_binding) {
|
|
||||||
(Some(vertex), Some(fragment)) => {
|
|
||||||
if vertex != fragment {
|
|
||||||
return Err(ShaderReflectError::MismatchedUniformBuffer {
|
|
||||||
vertex: vertex_ubo_binding,
|
|
||||||
fragment: fragment_ubo_binding
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
if vertex >= MAX_BINDINGS_COUNT {
|
|
||||||
return Err(ShaderReflectError::InvalidBinding(vertex))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
(Some(vertex), None) => {
|
|
||||||
if vertex >= MAX_BINDINGS_COUNT {
|
|
||||||
return Err(ShaderReflectError::VertexSemanticError(SemanticsErrorKind::InvalidBinding(vertex)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
(None, Some(fragment)) => {
|
|
||||||
if fragment >= MAX_BINDINGS_COUNT {
|
|
||||||
return Err(ShaderReflectError::FragmentSemanticError(SemanticsErrorKind::InvalidBinding(fragment)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
(None, None) => {}
|
|
||||||
}
|
|
||||||
|
|
||||||
// todo: slang_reflection:490
|
|
||||||
todo!()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,53 @@
|
||||||
|
use rustc_hash::FxHashMap;
|
||||||
use crate::error::ShaderReflectError;
|
use crate::error::ShaderReflectError;
|
||||||
use crate::reflect::semantics::ShaderReflection;
|
use crate::reflect::semantics::{SemanticMap, ShaderReflection, VariableSemantics, TextureSemantics};
|
||||||
|
|
||||||
mod cross;
|
mod cross;
|
||||||
mod naga;
|
mod naga;
|
||||||
pub mod semantics;
|
pub mod semantics;
|
||||||
|
mod rspirv;
|
||||||
|
|
||||||
pub trait ReflectShader {
|
pub trait ReflectShader {
|
||||||
fn reflect(&self) -> Result<ShaderReflection, ShaderReflectError>;
|
fn reflect(&self, options: &ReflectOptions) -> Result<ShaderReflection, ShaderReflectError>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub enum UniformSemantic {
|
||||||
|
Variable(SemanticMap<VariableSemantics>),
|
||||||
|
Texture(SemanticMap<TextureSemantics>)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ReflectOptions {
|
||||||
|
pub pass_number: u32,
|
||||||
|
pub uniform_semantics: FxHashMap<String, UniformSemantic>,
|
||||||
|
pub non_uniform_semantics: FxHashMap<String, SemanticMap<TextureSemantics>>
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn builtin_uniform_semantics() -> FxHashMap<String, UniformSemantic> {
|
||||||
|
let mut map = FxHashMap::default();
|
||||||
|
|
||||||
|
map.insert("MVP".into(), UniformSemantic::Variable(SemanticMap {
|
||||||
|
semantics: VariableSemantics::MVP,
|
||||||
|
index: 0
|
||||||
|
}));
|
||||||
|
|
||||||
|
map.insert("OutputSize".into(), UniformSemantic::Variable(SemanticMap {
|
||||||
|
semantics: VariableSemantics::Output,
|
||||||
|
index: 0
|
||||||
|
}));
|
||||||
|
|
||||||
|
map.insert("FinalViewportSize".into(), UniformSemantic::Variable(SemanticMap {
|
||||||
|
semantics: VariableSemantics::FinalViewport,
|
||||||
|
index: 0
|
||||||
|
}));
|
||||||
|
|
||||||
|
map.insert("FrameCount".into(), UniformSemantic::Variable(SemanticMap {
|
||||||
|
semantics: VariableSemantics::FrameCount,
|
||||||
|
index: 0
|
||||||
|
}));
|
||||||
|
|
||||||
|
map.insert("FrameDirection".into(), UniformSemantic::Variable(SemanticMap {
|
||||||
|
semantics: VariableSemantics::FrameDirection,
|
||||||
|
index: 0
|
||||||
|
}));
|
||||||
|
map
|
||||||
|
}
|
42
librashader-reflect/src/reflect/rspirv.rs
Normal file
42
librashader-reflect/src/reflect/rspirv.rs
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
use rspirv_reflect::Reflection;
|
||||||
|
use shaderc::CompilationArtifact;
|
||||||
|
use crate::error::ShaderReflectError;
|
||||||
|
use crate::front::shaderc::GlslangCompilation;
|
||||||
|
|
||||||
|
pub struct RspirvReflect {
|
||||||
|
vertex: Reflection,
|
||||||
|
fragment: Reflection,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_reflection(artifact: CompilationArtifact) -> Result<Reflection, ShaderReflectError> {
|
||||||
|
let mut loader = rspirv::dr::Loader::new();
|
||||||
|
rspirv::binary::parse_words(artifact.as_binary(), &mut loader)?;
|
||||||
|
Ok(Reflection::new(loader.module()))
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<GlslangCompilation> for RspirvReflect {
|
||||||
|
type Error = ShaderReflectError;
|
||||||
|
|
||||||
|
fn try_from(value: GlslangCompilation) -> Result<Self, Self::Error> {
|
||||||
|
|
||||||
|
let vertex = parse_reflection(value.vertex)?;
|
||||||
|
let fragment = parse_reflection(value.fragment)?;
|
||||||
|
|
||||||
|
Ok(RspirvReflect { vertex, fragment })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use crate::reflect::rspirv::RspirvReflect;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
pub fn test_into() {
|
||||||
|
let result = librashader_preprocess::load_shader_source("../test/basic.slang").unwrap();
|
||||||
|
let spirv = crate::front::shaderc::compile_spirv(&result).unwrap();
|
||||||
|
let mut reflect = RspirvReflect::try_from(spirv).unwrap();
|
||||||
|
// let pcr = reflect.fragment.get_push_constant_range().unwrap()
|
||||||
|
// .unwrap();
|
||||||
|
println!("{:?}", reflect.fragment.get_descriptor_sets());
|
||||||
|
// println!("PushConstantInfo size: {}, off: {}", pcr.size, pcr.offset);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,7 +1,9 @@
|
||||||
use crate::error::ShaderReflectError;
|
use crate::error::ShaderReflectError;
|
||||||
|
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;
|
||||||
|
pub const MAX_PUSH_BUFFER_SIZE: u32 = 128;
|
||||||
|
|
||||||
#[repr(i32)]
|
#[repr(i32)]
|
||||||
pub enum VariableSemantics {
|
pub enum VariableSemantics {
|
||||||
|
@ -29,13 +31,37 @@ pub enum TextureSemantics {
|
||||||
User = 5,
|
User = 5,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct BufferReflection {
|
pub struct SemanticMap<T> {
|
||||||
pub binding: Option<u32>,
|
pub(crate) semantics: T,
|
||||||
pub size: usize,
|
pub(crate) index: u32
|
||||||
pub stage_mask: u32,
|
}
|
||||||
|
|
||||||
|
bitflags! {
|
||||||
|
pub struct BindingStage: u8 {
|
||||||
|
const NONE = 0b00000000;
|
||||||
|
const VERTEX = 0b00000001;
|
||||||
|
const FRAGMENT = 0b00000010;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BindingStage {
|
||||||
|
pub fn clear(&mut self) {
|
||||||
|
self.bits = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct UboReflection {
|
||||||
|
pub binding: u32,
|
||||||
|
pub size: u32,
|
||||||
|
pub stage_mask: BindingStage,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct PushReflection {
|
||||||
|
pub size: u32,
|
||||||
|
pub stage_mask: BindingStage,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ShaderReflection {
|
pub struct ShaderReflection {
|
||||||
pub ubo: Option<BufferReflection>,
|
pub ubo: Option<UboReflection>,
|
||||||
pub push_constant: Option<BufferReflection>,
|
pub push_constant: Option<PushReflection>,
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue