use bitflags::bitflags;
use librashader_common::map::{FastHashMap, ShortString};
use std::fmt::{Display, Formatter};
use std::str::FromStr;
pub const MAX_BINDINGS_COUNT: u32 = 16;
pub const MAX_PUSH_BUFFER_SIZE: u32 = 128;
#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Copy, Clone, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum UniformType {
Mat4,
Vec4,
Unsigned,
Signed,
Float,
}
#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Copy, Clone, Hash)]
#[repr(i32)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum UniqueSemantics {
MVP = 0,
Output = 1,
FinalViewport = 2,
FrameCount = 3,
FrameDirection = 4,
Rotation = 5,
TotalSubFrames = 6,
CurrentSubFrame = 7,
FloatParameter = 8,
}
impl UniqueSemantics {
pub const fn semantics(self) -> Semantic<UniqueSemantics, ()> {
Semantic {
semantics: self,
index: (),
}
}
pub const fn binding_type(&self) -> UniformType {
match self {
UniqueSemantics::MVP => UniformType::Mat4,
UniqueSemantics::Output => UniformType::Vec4,
UniqueSemantics::FinalViewport => UniformType::Vec4,
UniqueSemantics::FrameCount => UniformType::Unsigned,
UniqueSemantics::FrameDirection => UniformType::Signed,
UniqueSemantics::Rotation => UniformType::Unsigned,
UniqueSemantics::TotalSubFrames => UniformType::Unsigned,
UniqueSemantics::CurrentSubFrame => UniformType::Unsigned,
UniqueSemantics::FloatParameter => UniformType::Float,
}
}
pub const fn as_str(&self) -> &'static str {
match self {
UniqueSemantics::MVP => "MVP",
UniqueSemantics::Output => "Output",
UniqueSemantics::FinalViewport => "FinalViewport",
UniqueSemantics::FrameCount => "FrameCount",
UniqueSemantics::FrameDirection => "FrameDirection",
UniqueSemantics::Rotation => "Rotation",
UniqueSemantics::TotalSubFrames => "TotalSubFrames",
UniqueSemantics::CurrentSubFrame => "CurrentSubFrame",
UniqueSemantics::FloatParameter => "FloatParameter",
}
}
}
impl Display for UniqueSemantics {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.as_str())
}
}
#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Copy, Clone, Hash)]
#[repr(i32)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum TextureSemantics {
Original = 0,
Source = 1,
OriginalHistory = 2,
PassOutput = 3,
PassFeedback = 4,
User = 5,
}
impl TextureSemantics {
pub(crate) const TEXTURE_SEMANTICS: [TextureSemantics; 6] = [
TextureSemantics::Source,
TextureSemantics::OriginalHistory,
TextureSemantics::Original,
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_indexed(&self) -> bool {
!matches!(self, TextureSemantics::Original | TextureSemantics::Source)
}
pub const fn semantics(self, index: usize) -> Semantic<TextureSemantics> {
Semantic {
semantics: self,
index,
}
}
}
pub(crate) struct TypeInfo {
pub size: u32,
pub columns: u32,
}
pub(crate) trait ValidateTypeSemantics<T> {
fn validate_type(&self, ty: &T) -> Option<TypeInfo>;
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub struct Semantic<T, I = usize> {
pub semantics: T,
pub index: I,
}
bitflags! {
#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Clone, Copy)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(transparent))]
pub struct BindingStage: u8 {
const NONE = 0b00000000;
const VERTEX = 0b00000001;
const FRAGMENT = 0b00000010;
}
}
#[derive(Clone, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct BufferReflection<T> {
pub binding: T,
pub size: u32,
pub stage_mask: BindingStage,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct MemberOffset {
pub ubo: Option<usize>,
pub push: Option<usize>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum UniformMemberBlock {
Ubo,
PushConstant,
}
impl UniformMemberBlock {
pub const TYPES: [UniformMemberBlock; 2] =
[UniformMemberBlock::Ubo, UniformMemberBlock::PushConstant];
}
impl MemberOffset {
pub(crate) fn new(off: usize, ty: UniformMemberBlock) -> Self {
match ty {
UniformMemberBlock::Ubo => MemberOffset {
ubo: Some(off),
push: None,
},
UniformMemberBlock::PushConstant => MemberOffset {
ubo: None,
push: Some(off),
},
}
}
pub fn offset(&self, ty: UniformMemberBlock) -> Option<usize> {
match ty {
UniformMemberBlock::Ubo => self.ubo,
UniformMemberBlock::PushConstant => self.push,
}
}
pub(crate) fn offset_mut(&mut self, ty: UniformMemberBlock) -> &mut Option<usize> {
match ty {
UniformMemberBlock::Ubo => &mut self.ubo,
UniformMemberBlock::PushConstant => &mut self.push,
}
}
}
#[derive(Clone, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct VariableMeta {
pub offset: MemberOffset,
pub size: u32,
pub id: ShortString,
}
#[derive(Clone, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct TextureSizeMeta {
pub offset: MemberOffset,
pub stage_mask: BindingStage,
pub id: ShortString,
}
#[derive(Clone, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct TextureBinding {
pub binding: u32,
}
#[derive(Clone, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct ShaderReflection {
pub ubo: Option<BufferReflection<u32>>,
pub push_constant: Option<BufferReflection<Option<u32>>>,
pub meta: BindingMeta,
}
pub trait UniformMeta {
fn offset(&self) -> MemberOffset;
fn id(&self) -> &str;
}
impl UniformMeta for VariableMeta {
fn offset(&self) -> MemberOffset {
self.offset
}
fn id(&self) -> &str {
&self.id
}
}
impl UniformMeta for TextureSizeMeta {
fn offset(&self) -> MemberOffset {
self.offset
}
fn id(&self) -> &str {
&self.id
}
}
pub trait TextureSemanticMap {
fn texture_semantic(&self, name: &str) -> Option<Semantic<TextureSemantics>>;
}
impl TextureSemanticMap for FastHashMap<ShortString, UniformSemantic> {
fn texture_semantic(&self, name: &str) -> Option<Semantic<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_indexed() {
let index = &name[semantics.size_uniform_name().len()..];
let Ok(index) = usize::from_str(index) else {
return None;
};
return Some(Semantic {
semantics: *semantics,
index,
});
} else if name == semantics.size_uniform_name() {
return Some(Semantic {
semantics: *semantics,
index: 0,
});
}
}
None
}
Some(UniformSemantic::Unique(_)) => None,
Some(UniformSemantic::Texture(texture)) => Some(*texture),
}
}
}
impl TextureSemanticMap for FastHashMap<ShortString, Semantic<TextureSemantics>> {
fn texture_semantic(&self, name: &str) -> Option<Semantic<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_indexed() {
let index = &name[semantics.texture_name().len()..];
let Ok(index) = usize::from_str(index) else {
return None;
};
return Some(Semantic {
semantics: *semantics,
index,
});
} else if name == semantics.texture_name() {
return Some(Semantic {
semantics: *semantics,
index: 0,
});
}
}
None
}
Some(texture) => Some(*texture),
}
}
}
pub trait UniqueSemanticMap {
fn unique_semantic(&self, name: &str) -> Option<Semantic<UniqueSemantics, ()>>;
}
impl UniqueSemanticMap for FastHashMap<ShortString, UniformSemantic> {
fn unique_semantic(&self, name: &str) -> Option<Semantic<UniqueSemantics, ()>> {
match self.get(name) {
None => match name {
"MVP" => Some(Semantic {
semantics: UniqueSemantics::MVP,
index: (),
}),
"OutputSize" => Some(Semantic {
semantics: UniqueSemantics::Output,
index: (),
}),
"FinalViewportSize" => Some(Semantic {
semantics: UniqueSemantics::FinalViewport,
index: (),
}),
"FrameCount" => Some(Semantic {
semantics: UniqueSemantics::FrameCount,
index: (),
}),
"FrameDirection" => Some(Semantic {
semantics: UniqueSemantics::FrameDirection,
index: (),
}),
"Rotation" => Some(Semantic {
semantics: UniqueSemantics::Rotation,
index: (),
}),
"TotalSubFrames" => Some(Semantic {
semantics: UniqueSemantics::TotalSubFrames,
index: (),
}),
"CurrentSubFrame" => Some(Semantic {
semantics: UniqueSemantics::CurrentSubFrame,
index: (),
}),
_ => None,
},
Some(UniformSemantic::Unique(variable)) => Some(*variable),
Some(UniformSemantic::Texture(_)) => None,
}
}
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum UniformSemantic {
Unique(Semantic<UniqueSemantics, ()>),
Texture(Semantic<TextureSemantics>),
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct ShaderSemantics {
pub uniform_semantics: FastHashMap<ShortString, UniformSemantic>,
pub texture_semantics: FastHashMap<ShortString, Semantic<TextureSemantics>>,
}
#[derive(Debug, Clone, Eq, Hash, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum UniformBinding {
Parameter(ShortString),
SemanticVariable(UniqueSemantics),
TextureSize(Semantic<TextureSemantics>),
}
impl From<UniqueSemantics> for UniformBinding {
fn from(value: UniqueSemantics) -> Self {
UniformBinding::SemanticVariable(value)
}
}
impl From<Semantic<TextureSemantics>> for UniformBinding {
fn from(value: Semantic<TextureSemantics>) -> Self {
UniformBinding::TextureSize(value)
}
}
#[derive(Debug, Default, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct BindingMeta {
#[cfg_attr(feature = "serde", serde(rename = "param"))]
pub parameter_meta: FastHashMap<ShortString, VariableMeta>,
#[cfg_attr(feature = "serde", serde(rename = "unique"))]
pub unique_meta: FastHashMap<UniqueSemantics, VariableMeta>,
#[cfg_attr(feature = "serde", serde(rename = "texture"))]
pub texture_meta: FastHashMap<Semantic<TextureSemantics>, TextureBinding>,
#[cfg_attr(feature = "serde", serde(rename = "texture_size"))]
pub texture_size_meta: FastHashMap<Semantic<TextureSemantics>, TextureSizeMeta>,
}
#[cfg(feature = "serde")]
mod serde_impl {
use super::*;
use serde::de::{Deserialize, Visitor};
use serde::ser::Serialize;
use serde::{Deserializer, Serializer};
struct TextureSemanticVisitor;
impl<'de> Visitor<'de> for TextureSemanticVisitor {
type Value = Semantic<TextureSemantics>;
fn expecting(&self, formatter: &mut Formatter) -> std::fmt::Result {
formatter.write_str("a string of the form (Semantic)N?")
}
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
match v {
"Original" => Ok(TextureSemantics::Original.semantics(0)),
"Source" => Ok(TextureSemantics::Source.semantics(0)),
other => {
let Some(index) = other.find(|c: char| c.is_digit(10)) else {
return Err(E::custom(format!(
"expected index for indexed texture semantic {v}"
)));
};
let (semantic, index) = other.split_at(index);
let Ok(index) = index.parse::<usize>() else {
return Err(E::custom(format!(
"could not parse index {index} of texture semantic {v}"
)));
};
match semantic {
"OriginalHistory" => Ok(TextureSemantics::OriginalHistory.semantics(index)),
"PassOutput" => Ok(TextureSemantics::PassOutput.semantics(index)),
"PassFeedback" => Ok(TextureSemantics::PassFeedback.semantics(index)),
_ => Ok(TextureSemantics::User.semantics(index)),
}
}
}
}
}
impl<'de> Deserialize<'de> for Semantic<TextureSemantics> {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
deserializer.deserialize_str(TextureSemanticVisitor)
}
}
impl Serialize for Semantic<TextureSemantics> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
if self.semantics.is_indexed() {
serializer.serialize_str(&format!(
"{}{}",
self.semantics.texture_name(),
self.index
))
} else {
serializer.serialize_str(&format!("{}", self.semantics.texture_name()))
}
}
}
struct UniqueSemanticsVisitor;
impl<'de> Visitor<'de> for UniqueSemanticsVisitor {
type Value = Semantic<UniqueSemantics, ()>;
fn expecting(&self, formatter: &mut Formatter) -> std::fmt::Result {
formatter.write_str("a valid uniform semantic name")
}
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
Ok(match v {
"MVP" => Semantic {
semantics: UniqueSemantics::MVP,
index: (),
},
"OutputSize" => Semantic {
semantics: UniqueSemantics::Output,
index: (),
},
"FinalViewportSize" => Semantic {
semantics: UniqueSemantics::FinalViewport,
index: (),
},
"FrameCount" => Semantic {
semantics: UniqueSemantics::FrameCount,
index: (),
},
"FrameDirection" => Semantic {
semantics: UniqueSemantics::FrameDirection,
index: (),
},
"Rotation" => Semantic {
semantics: UniqueSemantics::Rotation,
index: (),
},
"TotalSubFrames" => Semantic {
semantics: UniqueSemantics::TotalSubFrames,
index: (),
},
"CurrentSubFrame" => Semantic {
semantics: UniqueSemantics::CurrentSubFrame,
index: (),
},
_ => return Err(E::custom(format!("unknown unique semantic {v}"))),
})
}
}
impl Serialize for Semantic<UniqueSemantics, ()> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_str(self.semantics.as_str())
}
}
impl<'de> Deserialize<'de> for Semantic<UniqueSemantics, ()> {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
deserializer.deserialize_str(UniqueSemanticsVisitor)
}
}
}