librashader/librashader-reflect/src/reflect/cross.rs

892 lines
32 KiB
Rust
Raw Normal View History

2022-11-07 16:25:11 +11:00
use crate::error::{SemanticsErrorKind, ShaderCompileError, ShaderReflectError};
use crate::front::shaderc::GlslangCompilation;
use crate::reflect::semantics::{BindingStage, MAX_BINDINGS_COUNT, MAX_PUSH_BUFFER_SIZE, MemberOffset, PushReflection, ReflectSemantics, ShaderReflection, TextureBinding, TextureSemanticMap, TextureSemantics, TextureSizeMeta, TypeInfo, UboReflection, ValidateTypeSemantics, VariableMeta, VariableSemanticMap, VariableSemantics};
use crate::reflect::{align_uniform_size, ReflectMeta, ReflectShader};
2022-11-11 17:53:02 +11:00
use spirv_cross::hlsl::ShaderModel;
2022-10-27 17:22:44 +11:00
use spirv_cross::spirv::{Ast, Decoration, Module, Resource, ShaderResources, Type};
2022-11-22 08:53:36 +11:00
use spirv_cross::{ErrorCode, glsl, hlsl};
2022-11-01 11:29:25 +11:00
use crate::back::cross::{GlslangGlslContext, GlslangHlslContext};
use crate::back::targets::{GLSL, HLSL};
2022-11-11 17:53:02 +11:00
use crate::back::{CompileShader, ShaderCompilerOutput};
pub struct CrossReflect<T>
where
T: spirv_cross::spirv::Target,
{
vertex: Ast<T>,
fragment: Ast<T>,
}
2022-11-11 18:26:57 +11:00
pub struct CompiledAst<T>
where
2022-11-22 08:21:50 +11:00
T: spirv_cross::spirv::Target,
{
2022-11-11 18:26:57 +11:00
pub vertex: Ast<T>,
pub fragment: Ast<T>,
}
pub(crate) type HlslReflect = CrossReflect<hlsl::Target>;
pub(crate) type GlslReflect = CrossReflect<glsl::Target>;
2022-11-09 17:11:25 +11:00
impl ValidateTypeSemantics<Type> for VariableSemantics {
fn validate_type(&self, ty: &Type) -> Option<TypeInfo> {
2022-10-28 11:39:39 +11:00
let (Type::Float { ref array, vecsize, columns } | Type::Int { ref array, vecsize, columns } | Type::UInt { ref array, vecsize, columns }) = *ty else {
return None
};
if !array.is_empty() {
2022-10-27 17:22:44 +11:00
return None;
}
let valid = match self {
2022-10-27 17:22:44 +11:00
VariableSemantics::MVP => {
2022-10-28 11:39:39 +11:00
matches!(ty, Type::Float { .. }) && vecsize == 4 && columns == 4
2022-10-27 17:22:44 +11:00
}
VariableSemantics::FrameCount => {
2022-10-28 11:39:39 +11:00
matches!(ty, Type::UInt { .. }) && vecsize == 1 && columns == 1
2022-10-27 17:22:44 +11:00
}
VariableSemantics::FrameDirection => {
2022-10-28 11:39:39 +11:00
matches!(ty, Type::Int { .. }) && vecsize == 1 && columns == 1
2022-10-27 17:22:44 +11:00
}
VariableSemantics::FloatParameter => {
2022-10-28 11:39:39 +11:00
matches!(ty, Type::Float { .. }) && vecsize == 1 && columns == 1
2022-10-27 17:22:44 +11:00
}
_ => matches!(ty, Type::Float { .. }) && vecsize == 4 && columns == 1,
};
if valid {
Some(TypeInfo {
size: vecsize,
2022-10-27 17:22:44 +11:00
columns,
})
} else {
None
}
}
}
impl ValidateTypeSemantics<Type> for TextureSemantics {
fn validate_type(&self, ty: &Type) -> Option<TypeInfo> {
2022-10-28 11:39:39 +11:00
let Type::Float { ref array, vecsize, columns } = *ty else {
return None
};
if !array.is_empty() {
2022-10-27 17:22:44 +11:00
return None;
}
if vecsize == 4 && columns == 1 {
Some(TypeInfo {
size: vecsize,
2022-10-27 17:22:44 +11:00
columns,
})
} else {
None
}
}
}
impl<T> TryFrom<GlslangCompilation> for CrossReflect<T>
where
T: spirv_cross::spirv::Target,
Ast<T>: spirv_cross::spirv::Compile<T>,
Ast<T>: spirv_cross::spirv::Parse<T>,
{
2022-11-07 16:25:11 +11:00
type Error = ShaderReflectError;
fn try_from(value: GlslangCompilation) -> Result<Self, Self::Error> {
let vertex_module = Module::from_words(value.vertex.as_binary());
let fragment_module = Module::from_words(value.fragment.as_binary());
2022-11-11 17:53:02 +11:00
let vertex = Ast::parse(&vertex_module)?;
let fragment = Ast::parse(&fragment_module)?;
2022-11-07 16:25:11 +11:00
Ok(CrossReflect { vertex, fragment })
}
}
2022-10-27 17:22:44 +11:00
impl<T> CrossReflect<T>
where
T: spirv_cross::spirv::Target,
Ast<T>: spirv_cross::spirv::Compile<T>,
Ast<T>: spirv_cross::spirv::Parse<T>,
{
2022-10-27 17:22:44 +11:00
fn validate(
&self,
vertex_res: &ShaderResources,
fragment_res: &ShaderResources,
) -> Result<(), ShaderReflectError> {
if !vertex_res.sampled_images.is_empty()
|| !vertex_res.storage_buffers.is_empty()
|| !vertex_res.subpass_inputs.is_empty()
|| !vertex_res.storage_images.is_empty()
|| !vertex_res.atomic_counters.is_empty()
{
return Err(ShaderReflectError::VertexSemanticError(
SemanticsErrorKind::InvalidResourceType,
));
}
if !fragment_res.storage_buffers.is_empty()
|| !fragment_res.subpass_inputs.is_empty()
|| !fragment_res.storage_images.is_empty()
|| !fragment_res.atomic_counters.is_empty()
{
return Err(ShaderReflectError::FragmentSemanticError(
SemanticsErrorKind::InvalidResourceType,
));
}
let vert_inputs = vertex_res.stage_inputs.len();
if vert_inputs != 2 {
return Err(ShaderReflectError::VertexSemanticError(
SemanticsErrorKind::InvalidInputCount(vert_inputs),
));
}
let frag_outputs = fragment_res.stage_outputs.len();
if frag_outputs != 1 {
return Err(ShaderReflectError::FragmentSemanticError(
SemanticsErrorKind::InvalidOutputCount(frag_outputs),
));
}
2022-10-27 17:22:44 +11:00
let fragment_location = self
.fragment
.get_decoration(fragment_res.stage_outputs[0].id, Decoration::Location)?;
if fragment_location != 0 {
return Err(ShaderReflectError::FragmentSemanticError(
SemanticsErrorKind::InvalidLocation(fragment_location),
));
}
2022-11-01 11:29:25 +11:00
let vert_mask = vertex_res.stage_inputs.iter().try_fold(0, |mask, input| {
2022-10-27 17:22:44 +11:00
Ok::<u32, ErrorCode>(
mask | 1 << self.vertex.get_decoration(input.id, Decoration::Location)?,
)
})?;
if vert_mask != 0x3 {
return Err(ShaderReflectError::VertexSemanticError(
SemanticsErrorKind::InvalidLocation(vert_mask),
));
}
if vertex_res.uniform_buffers.len() > 1 {
return Err(ShaderReflectError::VertexSemanticError(
SemanticsErrorKind::InvalidUniformBufferCount(vertex_res.uniform_buffers.len()),
));
}
if vertex_res.push_constant_buffers.len() > 1 {
return Err(ShaderReflectError::VertexSemanticError(
2022-10-27 17:22:44 +11:00
SemanticsErrorKind::InvalidUniformBufferCount(
vertex_res.push_constant_buffers.len(),
),
));
}
if fragment_res.uniform_buffers.len() > 1 {
return Err(ShaderReflectError::FragmentSemanticError(
SemanticsErrorKind::InvalidUniformBufferCount(fragment_res.uniform_buffers.len()),
));
}
if fragment_res.push_constant_buffers.len() > 1 {
return Err(ShaderReflectError::FragmentSemanticError(
2022-10-27 17:22:44 +11:00
SemanticsErrorKind::InvalidUniformBufferCount(
fragment_res.push_constant_buffers.len(),
),
));
}
Ok(())
}
}
2022-10-27 17:22:44 +11:00
struct UboData {
id: u32,
descriptor_set: u32,
binding: u32,
size: u32,
}
2022-10-27 17:22:44 +11:00
struct TextureData<'a> {
id: u32,
2022-10-27 17:22:44 +11:00
name: &'a str,
descriptor_set: u32,
binding: u32,
2022-10-27 17:22:44 +11:00
}
// todo: might want to take these crate helpers out.
#[derive(Copy, Clone)]
enum SemanticErrorBlame {
Vertex,
Fragment,
}
impl SemanticErrorBlame {
fn error(self, kind: SemanticsErrorKind) -> ShaderReflectError {
2022-11-01 11:29:25 +11:00
match self {
SemanticErrorBlame::Vertex => ShaderReflectError::VertexSemanticError(kind),
2022-10-27 17:22:44 +11:00
SemanticErrorBlame::Fragment => ShaderReflectError::FragmentSemanticError(kind),
2022-11-01 11:29:25 +11:00
}
2022-10-27 17:22:44 +11:00
}
}
impl<T> CrossReflect<T>
where
T: spirv_cross::spirv::Target,
Ast<T>: spirv_cross::spirv::Compile<T>,
Ast<T>: spirv_cross::spirv::Parse<T>,
{
2022-10-27 17:22:44 +11:00
fn get_ubo_data(
ast: &Ast<T>,
ubo: &Resource,
blame: SemanticErrorBlame,
) -> Result<UboData, ShaderReflectError> {
let descriptor_set = ast.get_decoration(ubo.id, Decoration::DescriptorSet)?;
2022-10-27 17:22:44 +11:00
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,
2022-10-27 17:22:44 +11:00
size,
})
}
2022-10-27 17:22:44 +11:00
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)
}
2022-10-27 17:22:44 +11:00
fn reflect_buffer_range_metas(
ast: &Ast<T>,
resource: &Resource,
2022-11-20 10:48:54 +11:00
pass_number: usize,
2022-11-07 16:25:11 +11:00
semantics: &ReflectSemantics,
2022-10-27 17:22:44 +11:00
meta: &mut ReflectMeta,
offset_type: impl Fn(usize) -> MemberOffset,
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)?;
2022-10-27 17:22:44 +11:00
let ubo_type = ast.get_type(resource.base_type_id)?;
let range_type = match ubo_type {
Type::Struct { member_types, .. } => {
2022-10-27 17:22:44 +11:00
let range_type = member_types
.get(range.index as usize)
.cloned()
.ok_or(blame.error(SemanticsErrorKind::InvalidRange(range.index)))?;
ast.get_type(range_type)?
}
2022-10-27 17:22:44 +11:00
_ => return Err(blame.error(SemanticsErrorKind::InvalidResourceType)),
};
2022-11-07 16:25:11 +11:00
if let Some(parameter) = semantics.uniform_semantics.get_variable_semantic(&name) {
2022-10-27 17:22:44 +11:00
let Some(typeinfo) = parameter.semantics.validate_type(&range_type) else {
return Err(blame.error(SemanticsErrorKind::InvalidTypeForSemantic(name)))
};
match &parameter.semantics {
VariableSemantics::FloatParameter => {
let offset = offset_type(range.offset);
if let Some(meta) = meta.parameter_meta.get(&name) {
2022-10-27 17:22:44 +11:00
if offset != meta.offset {
return Err(ShaderReflectError::MismatchedOffset {
semantic: name,
vertex: meta.offset,
fragment: offset,
});
}
2022-10-27 17:22:44 +11:00
if meta.components != typeinfo.size {
return Err(ShaderReflectError::MismatchedComponent {
semantic: name,
vertex: meta.components,
fragment: typeinfo.size,
});
}
2022-10-27 17:22:44 +11:00
} else {
meta.parameter_meta.insert(
name.clone(),
2022-10-27 17:22:44 +11:00
VariableMeta {
2022-11-12 17:23:49 +11:00
id: name,
2022-10-27 17:22:44 +11:00
offset,
components: typeinfo.size,
},
);
}
}
2022-10-27 17:22:44 +11:00
semantics => {
let offset = offset_type(range.offset);
if let Some(meta) = meta.variable_meta.get(semantics) {
if offset != meta.offset {
return Err(ShaderReflectError::MismatchedOffset {
semantic: name,
vertex: meta.offset,
fragment: offset,
});
}
if meta.components != typeinfo.size * typeinfo.columns {
return Err(ShaderReflectError::MismatchedComponent {
semantic: name,
vertex: meta.components,
fragment: typeinfo.size,
});
}
} else {
meta.variable_meta.insert(
*semantics,
VariableMeta {
2022-11-12 17:23:49 +11:00
id: name,
2022-10-27 17:22:44 +11:00
offset,
components: typeinfo.size * typeinfo.columns,
},
);
}
}
2022-10-27 17:22:44 +11:00
}
2022-11-07 16:25:11 +11:00
} else if let Some(texture) = semantics.uniform_semantics.get_texture_semantic(&name) {
2022-10-27 17:22:44 +11:00
let Some(_typeinfo) = texture.semantics.validate_type(&range_type) else {
return Err(blame.error(SemanticsErrorKind::InvalidTypeForSemantic(name)))
};
if let TextureSemantics::PassOutput = texture.semantics {
2022-11-07 16:25:11 +11:00
if texture.index >= pass_number {
2022-10-27 17:22:44 +11:00
return Err(ShaderReflectError::NonCausalFilterChain {
2022-11-07 16:25:11 +11:00
pass: pass_number,
2022-10-27 17:22:44 +11:00
target: texture.index,
});
}
}
2022-11-12 17:23:49 +11:00
// this will break if range is both ubo and push buf whatever
2022-10-27 17:22:44 +11:00
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,
});
}
2022-10-27 17:22:44 +11:00
meta.stage_mask.insert(match blame {
SemanticErrorBlame::Vertex => BindingStage::VERTEX,
2022-10-28 11:39:39 +11:00
SemanticErrorBlame::Fragment => BindingStage::FRAGMENT,
2022-10-27 17:22:44 +11:00
});
} else {
meta.texture_size_meta.insert(
2022-11-01 11:29:25 +11:00
texture,
2022-10-27 17:22:44 +11:00
TextureSizeMeta {
offset,
2022-11-07 16:25:11 +11:00
// todo: fix this. to allow both
2022-10-27 17:22:44 +11:00
stage_mask: match blame {
SemanticErrorBlame::Vertex => BindingStage::VERTEX,
2022-10-28 11:39:39 +11:00
SemanticErrorBlame::Fragment => BindingStage::FRAGMENT,
},
2022-11-22 08:21:50 +11:00
id: name,
2022-10-27 17:22:44 +11:00
},
);
}
2022-10-27 17:22:44 +11:00
} else {
2022-10-28 11:39:39 +11:00
return Err(blame.error(SemanticsErrorKind::UnknownSemantics(name)));
}
}
Ok(())
}
2022-10-27 17:22:44 +11:00
fn reflect_ubos(
2022-11-07 16:25:11 +11:00
&mut self,
2022-10-27 17:22:44 +11:00
vertex_ubo: Option<&Resource>,
fragment_ubo: Option<&Resource>,
) -> Result<Option<UboReflection>, ShaderReflectError> {
2022-11-07 16:25:11 +11:00
if let Some(vertex_ubo) = vertex_ubo {
self.vertex
.set_decoration(vertex_ubo.id, Decoration::Binding, 0)?;
2022-11-07 16:25:11 +11:00
}
if let Some(fragment_ubo) = fragment_ubo {
self.fragment
.set_decoration(fragment_ubo.id, Decoration::Binding, 0)?;
2022-11-07 16:25:11 +11:00
}
match (vertex_ubo, fragment_ubo) {
(None, None) => Ok(None),
(Some(vertex_ubo), Some(fragment_ubo)) => {
2022-10-27 17:22:44 +11:00
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,
2022-10-27 17:22:44 +11:00
fragment: fragment_ubo.binding,
});
}
let size = std::cmp::max(vertex_ubo.size, fragment_ubo.size);
Ok(Some(UboReflection {
binding: vertex_ubo.binding,
size: align_uniform_size(size),
2022-10-27 17:22:44 +11:00
stage_mask: BindingStage::VERTEX | BindingStage::FRAGMENT,
}))
}
(Some(vertex_ubo), None) => {
2022-10-27 17:22:44 +11:00
let vertex_ubo =
Self::get_ubo_data(&self.vertex, vertex_ubo, SemanticErrorBlame::Vertex)?;
Ok(Some(UboReflection {
binding: vertex_ubo.binding,
size: align_uniform_size(vertex_ubo.size),
2022-10-27 17:22:44 +11:00
stage_mask: BindingStage::VERTEX,
}))
}
(None, Some(fragment_ubo)) => {
2022-10-27 17:22:44 +11:00
let fragment_ubo =
Self::get_ubo_data(&self.fragment, fragment_ubo, SemanticErrorBlame::Fragment)?;
Ok(Some(UboReflection {
binding: fragment_ubo.binding,
size: align_uniform_size(fragment_ubo.size),
2022-10-27 17:22:44 +11:00
stage_mask: BindingStage::FRAGMENT,
}))
}
}
}
2022-10-27 17:22:44 +11:00
fn reflect_texture_metas(
&self,
texture: TextureData,
2022-11-20 10:48:54 +11:00
pass_number: usize,
2022-11-07 16:25:11 +11:00
semantics: &ReflectSemantics,
2022-10-27 17:22:44 +11:00
meta: &mut ReflectMeta,
) -> Result<(), ShaderReflectError> {
2022-11-07 16:25:11 +11:00
let Some(semantic) = semantics.non_uniform_semantics.get_texture_semantic(texture.name) else {
2022-10-27 17:22:44 +11:00
return Err(SemanticErrorBlame::Fragment.error(SemanticsErrorKind::UnknownSemantics(texture.name.to_string())))
};
if semantic.semantics == TextureSemantics::PassOutput && semantic.index >= pass_number {
2022-10-28 11:39:39 +11:00
return Err(ShaderReflectError::NonCausalFilterChain {
2022-11-07 16:25:11 +11:00
pass: pass_number,
2022-10-28 11:39:39 +11:00
target: semantic.index,
});
2022-10-27 17:22:44 +11:00
}
2022-10-28 11:39:39 +11:00
meta.texture_meta.insert(
semantic,
TextureBinding {
2022-10-28 11:39:39 +11:00
binding: texture.binding,
},
);
2022-10-27 17:22:44 +11:00
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(
2022-11-07 16:25:11 +11:00
&mut self,
2022-10-27 17:22:44 +11:00
vertex_pcb: Option<&Resource>,
fragment_pcb: Option<&Resource>,
) -> Result<Option<PushReflection>, ShaderReflectError> {
2022-11-07 16:25:11 +11:00
if let Some(vertex_pcb) = vertex_pcb {
self.vertex
.set_decoration(vertex_pcb.id, Decoration::Binding, 1)?;
2022-11-07 16:25:11 +11:00
}
if let Some(fragment_pcb) = fragment_pcb {
self.fragment
.set_decoration(fragment_pcb.id, Decoration::Binding, 1)?;
2022-11-07 16:25:11 +11:00
}
match (vertex_pcb, fragment_pcb) {
(None, None) => Ok(None),
(Some(vertex_push), Some(fragment_push)) => {
2022-10-27 17:22:44 +11:00
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: align_uniform_size(size),
2022-10-27 17:22:44 +11:00
stage_mask: BindingStage::VERTEX | BindingStage::FRAGMENT,
}))
}
(Some(vertex_push), None) => {
2022-10-27 17:22:44 +11:00
let vertex_size =
Self::get_push_size(&self.vertex, vertex_push, SemanticErrorBlame::Vertex)?;
Ok(Some(PushReflection {
size: align_uniform_size(vertex_size),
2022-10-27 17:22:44 +11:00
stage_mask: BindingStage::VERTEX,
}))
}
(None, Some(fragment_push)) => {
2022-10-27 17:22:44 +11:00
let fragment_size = Self::get_push_size(
&self.fragment,
fragment_push,
SemanticErrorBlame::Fragment,
)?;
Ok(Some(PushReflection {
size: align_uniform_size(fragment_size),
2022-10-27 17:22:44 +11:00
stage_mask: BindingStage::FRAGMENT,
}))
}
}
}
}
impl<T> ReflectShader for CrossReflect<T>
where
T: spirv_cross::spirv::Target,
Ast<T>: spirv_cross::spirv::Compile<T>,
Ast<T>: spirv_cross::spirv::Parse<T>,
{
fn reflect(
&mut self,
2022-11-20 10:48:54 +11:00
pass_number: usize,
semantics: &ReflectSemantics,
) -> Result<ShaderReflection, ShaderReflectError> {
let vertex_res = self.vertex.get_shader_resources()?;
let fragment_res = self.fragment.get_shader_resources()?;
self.validate(&vertex_res, &fragment_res)?;
2022-11-01 11:29:25 +11:00
let vertex_ubo = vertex_res.uniform_buffers.first();
let fragment_ubo = fragment_res.uniform_buffers.first();
let ubo = self.reflect_ubos(vertex_ubo, fragment_ubo)?;
2022-11-01 11:29:25 +11:00
let vertex_push = vertex_res.push_constant_buffers.first();
let fragment_push = fragment_res.push_constant_buffers.first();
let push_constant = self.reflect_push_constant_buffer(vertex_push, fragment_push)?;
let mut meta = ReflectMeta::default();
if let Some(ubo) = vertex_ubo {
2022-10-27 17:22:44 +11:00
Self::reflect_buffer_range_metas(
&self.vertex,
ubo,
2022-11-07 16:25:11 +11:00
pass_number,
semantics,
2022-10-27 17:22:44 +11:00
&mut meta,
MemberOffset::Ubo,
SemanticErrorBlame::Vertex,
)?;
}
if let Some(ubo) = fragment_ubo {
2022-10-27 17:22:44 +11:00
Self::reflect_buffer_range_metas(
&self.fragment,
ubo,
2022-11-07 16:25:11 +11:00
pass_number,
semantics,
2022-10-27 17:22:44 +11:00
&mut meta,
MemberOffset::Ubo,
SemanticErrorBlame::Fragment,
)?;
}
if let Some(push) = vertex_push {
2022-10-27 17:22:44 +11:00
Self::reflect_buffer_range_metas(
&self.vertex,
push,
2022-11-07 16:25:11 +11:00
pass_number,
semantics,
2022-10-27 17:22:44 +11:00
&mut meta,
MemberOffset::PushConstant,
SemanticErrorBlame::Vertex,
)?;
}
if let Some(push) = fragment_push {
2022-10-27 17:22:44 +11:00
Self::reflect_buffer_range_metas(
&self.fragment,
push,
2022-11-07 16:25:11 +11:00
pass_number,
semantics,
2022-10-27 17:22:44 +11:00
&mut meta,
MemberOffset::PushConstant,
SemanticErrorBlame::Fragment,
)?;
}
2022-10-27 17:22:44 +11:00
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));
}
2022-11-01 11:29:25 +11:00
ubo_bindings |= 1 << texture_data.binding;
2022-10-27 17:22:44 +11:00
2022-11-07 16:25:11 +11:00
self.reflect_texture_metas(texture_data, pass_number, semantics, &mut meta)?;
2022-10-27 17:22:44 +11:00
}
Ok(ShaderReflection {
ubo,
push_constant,
2022-10-27 17:22:44 +11:00
meta,
})
}
}
impl CompileShader<GLSL> for CrossReflect<glsl::Target> {
type Options = glsl::Version;
2022-11-12 17:23:49 +11:00
type Context = GlslangGlslContext;
2022-11-07 16:25:11 +11:00
fn compile(
2022-11-11 18:26:57 +11:00
mut self,
version: Self::Options,
2022-11-11 18:26:57 +11:00
) -> Result<ShaderCompilerOutput<String, Self::Context>, ShaderCompileError> {
let mut options: glsl::CompilerOptions = Default::default();
options.version = version;
options.fragment.default_float_precision = glsl::Precision::High;
options.fragment.default_int_precision = glsl::Precision::High;
options.enable_420_pack_extension = false;
self.vertex.set_compiler_options(&options)?;
self.fragment.set_compiler_options(&options)?;
2022-11-07 16:25:11 +11:00
2022-11-09 17:11:25 +11:00
let vertex_resources = self.vertex.get_shader_resources()?;
let fragment_resources = self.fragment.get_shader_resources()?;
for res in &vertex_resources.stage_inputs {
self.vertex.unset_decoration(res.id, Decoration::Location)?;
}
for res in &vertex_resources.stage_outputs {
2022-11-24 18:24:05 +11:00
// let location = self.vertex.get_decoration(res.id, Decoration::Location)?;
// self.vertex
// .set_name(res.id, &format!("LIBRA_VARYING_{location}"))?;
2022-11-09 17:11:25 +11:00
self.vertex.unset_decoration(res.id, Decoration::Location)?;
}
for res in &fragment_resources.stage_inputs {
2022-11-24 18:24:05 +11:00
// let location = self.fragment.get_decoration(res.id, Decoration::Location)?;
// self.fragment
// .set_name(res.id, &format!("LIBRA_VARYING_{location}"))?;
self.fragment
.unset_decoration(res.id, Decoration::Location)?;
2022-11-09 17:11:25 +11:00
}
if vertex_resources.push_constant_buffers.len() > 1 {
return Err(ShaderCompileError::SpirvCrossCompileError(
ErrorCode::CompilationError(String::from(
"Cannot have more than one push constant buffer",
)),
));
2022-11-09 17:11:25 +11:00
}
for res in &vertex_resources.push_constant_buffers {
2022-11-20 10:48:54 +11:00
self.vertex.set_name(res.id, "LIBRA_PUSH_VERTEX_INSTANCE")?;
self.vertex
2022-11-20 10:48:54 +11:00
.set_name(res.base_type_id, "LIBRA_PUSH_VERTEX")?;
2022-11-09 17:11:25 +11:00
}
// todo: options
2022-11-22 08:21:50 +11:00
let _flatten = false;
2022-11-09 17:11:25 +11:00
if vertex_resources.uniform_buffers.len() > 1 {
return Err(ShaderCompileError::SpirvCrossCompileError(
ErrorCode::CompilationError(String::from(
"Cannot have more than one uniform buffer",
)),
));
2022-11-09 17:11:25 +11:00
}
for res in &vertex_resources.uniform_buffers {
2022-11-12 17:23:49 +11:00
// if flatten {
// self.vertex.flatten_buffer_block(res.id)?;
// }
2022-11-20 10:48:54 +11:00
self.vertex.set_name(res.id, "LIBRA_UBO_VERTEX_INSTANCE")?;
self.vertex.set_name(res.base_type_id, "LIBRA_UBO_VERTEX")?;
self.vertex
.unset_decoration(res.id, Decoration::DescriptorSet)?;
2022-11-09 17:11:25 +11:00
self.vertex.unset_decoration(res.id, Decoration::Binding)?;
}
if fragment_resources.push_constant_buffers.len() > 1 {
return Err(ShaderCompileError::SpirvCrossCompileError(
ErrorCode::CompilationError(String::from(
"Cannot have more than one push constant buffer",
)),
));
2022-11-09 17:11:25 +11:00
}
for res in &fragment_resources.push_constant_buffers {
self.fragment
2022-11-20 10:48:54 +11:00
.set_name(res.id, "LIBRA_PUSH_FRAGMENT_INSTANCE")?;
self.fragment
2022-11-20 10:48:54 +11:00
.set_name(res.base_type_id, "LIBRA_PUSH_FRAGMENT")?;
2022-11-09 17:11:25 +11:00
}
if fragment_resources.uniform_buffers.len() > 1 {
return Err(ShaderCompileError::SpirvCrossCompileError(
ErrorCode::CompilationError(String::from(
"Cannot have more than one uniform buffer",
)),
));
2022-11-09 17:11:25 +11:00
}
for res in &fragment_resources.uniform_buffers {
2022-11-12 17:23:49 +11:00
// if flatten {
// self.fragment.flatten_buffer_block(res.id)?;
// }
self.fragment
2022-11-20 10:48:54 +11:00
.set_name(res.id, "LIBRA_UBO_FRAGMENT_INSTANCE")?;
self.fragment
2022-11-20 10:48:54 +11:00
.set_name(res.base_type_id, "LIBRA_UBO_FRAGMENT")?;
self.fragment
.unset_decoration(res.id, Decoration::DescriptorSet)?;
self.fragment
.unset_decoration(res.id, Decoration::Binding)?;
2022-11-09 17:11:25 +11:00
}
let mut texture_fixups = Vec::new();
for res in fragment_resources.sampled_images {
2022-11-09 17:11:25 +11:00
let binding = self.fragment.get_decoration(res.id, Decoration::Binding)?;
self.fragment
.unset_decoration(res.id, Decoration::DescriptorSet)?;
self.fragment
.unset_decoration(res.id, Decoration::Binding)?;
let mut name = res.name;
name.push('\0');
texture_fixups.push((name, binding));
2022-11-09 17:11:25 +11:00
}
2022-11-11 17:53:02 +11:00
Ok(ShaderCompilerOutput {
2022-11-07 16:25:11 +11:00
vertex: self.vertex.compile()?,
2022-11-09 17:11:25 +11:00
fragment: self.fragment.compile()?,
2022-11-12 17:23:49 +11:00
context: GlslangGlslContext {
2022-11-20 14:03:58 +11:00
sampler_bindings: texture_fixups,
2022-11-11 18:26:57 +11:00
compiler: CompiledAst {
vertex: self.vertex,
2022-11-22 08:21:50 +11:00
fragment: self.fragment,
},
2022-11-11 18:26:57 +11:00
},
2022-11-07 16:25:11 +11:00
})
}
}
impl CompileShader<HLSL> for CrossReflect<hlsl::Target> {
type Options = Option<()>;
type Context = GlslangHlslContext;
2022-11-07 16:25:11 +11:00
fn compile(
2022-11-11 18:26:57 +11:00
mut self,
_options: Self::Options,
) -> Result<ShaderCompilerOutput<String, GlslangHlslContext>, ShaderCompileError> {
let mut options = hlsl::CompilerOptions::default();
options.shader_model = ShaderModel::V5_0;
self.vertex.set_compiler_options(&options)?;
self.fragment.set_compiler_options(&options)?;
2022-11-07 16:25:11 +11:00
2022-11-11 17:53:02 +11:00
Ok(ShaderCompilerOutput {
2022-11-07 16:25:11 +11:00
vertex: self.vertex.compile()?,
2022-11-09 17:11:25 +11:00
fragment: self.fragment.compile()?,
context: GlslangHlslContext {
compiler: CompiledAst {
vertex: self.vertex,
fragment: self.fragment
}
},
2022-11-07 16:25:11 +11:00
})
}
}
#[cfg(test)]
mod test {
use crate::reflect::cross::CrossReflect;
2022-11-22 08:53:36 +11:00
use crate::reflect::ReflectShader;
use rustc_hash::FxHashMap;
use crate::back::CompileShader;
2022-11-22 08:53:36 +11:00
use crate::reflect::semantics::{ReflectSemantics, SemanticMap, UniformSemantic, VariableSemantics};
2022-11-22 08:21:50 +11:00
use librashader_preprocess::ShaderSource;
use spirv_cross::glsl;
use spirv_cross::glsl::{CompilerOptions, Version};
2022-11-22 09:47:38 +11:00
use crate::front::shaderc::GlslangCompilation;
#[test]
pub fn test_into() {
2022-11-22 08:21:50 +11:00
let result = ShaderSource::load("../test/basic.slang").unwrap();
2022-11-07 16:25:11 +11:00
let mut uniform_semantics: FxHashMap<String, UniformSemantic> = Default::default();
2022-11-22 08:21:50 +11:00
for (_index, param) in result.parameters.iter().enumerate() {
uniform_semantics.insert(
param.id.clone(),
UniformSemantic::Variable(SemanticMap {
semantics: VariableSemantics::FloatParameter,
index: (),
}),
);
2022-11-07 16:25:11 +11:00
}
2022-11-22 09:47:38 +11:00
let spirv = GlslangCompilation::compile(&result).unwrap();
2022-11-07 16:25:11 +11:00
let mut reflect = CrossReflect::<glsl::Target>::try_from(spirv).unwrap();
2022-11-22 08:21:50 +11:00
let _shader_reflection = reflect
.reflect(
0,
&ReflectSemantics {
uniform_semantics,
non_uniform_semantics: Default::default(),
},
)
2022-10-27 17:22:44 +11:00
.unwrap();
2022-11-07 16:25:11 +11:00
let mut opts = CompilerOptions::default();
opts.version = Version::V4_60;
opts.enable_420_pack_extension = false;
2022-11-22 08:21:50 +11:00
let compiled = reflect.compile(Version::V3_30).unwrap();
2022-11-07 16:25:11 +11:00
// eprintln!("{shader_reflection:#?}");
eprintln!("{:#}", compiled.fragment)
// let mut loader = rspirv::dr::Loader::new();
// rspirv::binary::parse_words(spirv.fragment.as_binary(), &mut loader).unwrap();
// let module = loader.module();
// println!("{:#}", module.disassemble());
}
}