use serde::Serialize;
use crate::{
resource,
resources::image::Image,
solver::SolveErrors,
types::{AlphaMode, ShaderTypes},
Reference, ReferenceModel, Solver,
};
#[derive(Debug, Serialize)]
pub struct Material {
pub(crate) double_sided: bool,
pub(crate) alpha_mode: AlphaMode,
pub shaders: Vec<Reference<Shader>>,
pub model: RenderModel,
pub parameters: Vec<Parameter>,
}
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)]
pub struct MaterialModel {
pub(crate) double_sided: bool,
pub(crate) alpha_mode: AlphaMode,
pub(crate) shaders: Vec<ReferenceModel<Shader>>,
pub model: RenderModel,
pub parameters: Vec<ParameterModel>,
}
impl Material {
pub fn into_shaders(self) -> Vec<Reference<Shader>> {
self.shaders
}
pub fn shaders(&self) -> &[Reference<Shader>] {
&self.shaders
}
pub fn shaders_mut(&mut self) -> &mut [Reference<Shader>] {
&mut self.shaders
}
pub fn alpha_mode(&self) -> &AlphaMode {
&self.alpha_mode
}
}
super::impl_resource_model!(Material, MaterialModel, "Material");
impl<'de> Solver<'de, Reference<Material>> for ReferenceModel<MaterialModel> {
fn solve(self, storage_backend: &dyn resource::ReadStorageBackend) -> Result<Reference<Material>, SolveErrors> {
let (gr, reader) = storage_backend.read(self.id()).ok_or(SolveErrors::StorageError)?;
let MaterialModel {
double_sided,
alpha_mode,
shaders,
model,
parameters,
} = crate::from_slice(&gr.resource).map_err(|e| SolveErrors::DeserializationFailed(e.to_string()))?;
Ok(Reference::from_model(
self,
Material {
double_sided,
alpha_mode,
shaders: shaders
.into_iter()
.map(|s| s.solve(storage_backend))
.collect::<Result<Vec<_>, _>>()?,
model,
parameters: parameters
.into_iter()
.map(|p| p.solve(storage_backend))
.collect::<Result<Vec<_>, _>>()?,
},
reader,
))
}
}
#[derive(Debug, serde::Serialize)]
pub struct VariantVariable {
pub name: String,
pub r#type: String,
pub value: Value,
}
#[derive(Debug, serde::Serialize, serde::Deserialize, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)]
pub struct VariantVariableModel {
pub name: String,
pub r#type: String,
pub value: ValueModel,
}
impl<'de> Solver<'de, VariantVariable> for VariantVariableModel {
fn solve(self, storage_backend: &dyn resource::ReadStorageBackend) -> Result<VariantVariable, SolveErrors> {
Ok(VariantVariable {
name: self.name,
r#type: self.r#type,
value: match self.value {
ValueModel::Scalar(scalar) => Value::Scalar(scalar),
ValueModel::Vector3(vector) => Value::Vector3(vector),
ValueModel::Vector4(vector) => Value::Vector4(vector),
ValueModel::Image(image) => Value::Image(image.solve(storage_backend)?),
},
})
}
}
#[derive(Debug, serde::Serialize)]
pub struct Variant {
pub material: Reference<Material>,
pub variables: Vec<VariantVariable>,
pub alpha_mode: AlphaMode,
}
#[derive(Debug, serde::Serialize, serde::Deserialize, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)]
pub struct VariantModel {
pub material: ReferenceModel<MaterialModel>,
pub variables: Vec<VariantVariableModel>,
pub alpha_mode: AlphaMode,
}
super::impl_resource_model!(Variant, VariantModel, "Variant");
impl<'de> Solver<'de, Reference<Variant>> for ReferenceModel<VariantModel> {
fn solve(self, storage_backend: &dyn resource::ReadStorageBackend) -> Result<Reference<Variant>, SolveErrors> {
let (gr, reader) = storage_backend.read(self.id()).ok_or(SolveErrors::StorageError)?;
let VariantModel {
material,
variables,
alpha_mode,
} = crate::from_slice(&gr.resource).map_err(|e| SolveErrors::DeserializationFailed(e.to_string()))?;
Ok(Reference::from_model(
self,
Variant {
material: material.solve(storage_backend)?,
variables: variables
.into_iter()
.map(|v| v.solve(storage_backend))
.collect::<Result<Vec<_>, _>>()?,
alpha_mode,
},
reader,
))
}
}
pub use crate::shader::besl::evaluation::{BindingKind, TextureView};
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)]
pub struct Binding {
pub name: String,
pub slot: u32,
pub kind: BindingKind,
pub count: u32,
pub read: bool,
pub write: bool,
}
impl Binding {
pub fn new(slot: u32, kind: BindingKind, count: u32, read: bool, write: bool) -> Self {
assert!(
count > 0,
"Invalid resource count. The most likely cause is that a shader interface resource was declared with an empty array."
);
assert!(
slot.checked_add(count).is_some(),
"Invalid resource slot range. The most likely cause is that a persisted shader resource array extends beyond the flat slot space."
);
Self {
name: String::new(),
slot,
kind,
count,
read,
write,
}
}
pub fn named(name: impl Into<String>, slot: u32, kind: BindingKind, count: u32, read: bool, write: bool) -> Self {
Self {
name: name.into(),
..Self::new(slot, kind, count, read, write)
}
}
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)]
pub struct ShaderInterface {
pub workgroup_size: Option<(u32, u32, u32)>,
pub bindings: Vec<Binding>,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)]
pub enum ShaderArtifact {
Spirv,
Hlsl { entry_point: String },
Msl { entry_point: String },
Mtlb { entry_point: String },
Dxil,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)]
pub struct Shader {
pub id: String,
pub stage: ShaderTypes,
pub interface: ShaderInterface,
pub artifact: ShaderArtifact,
pub source_hash: u64,
}
impl Shader {
pub fn id(&self) -> &str {
&self.id
}
}
super::impl_direct_resource!(Shader, "Shader");
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)]
pub struct RenderModel {
pub name: String,
pub pass: String,
}
#[derive(Debug, serde::Serialize)]
pub enum Value {
Scalar(f32),
Vector3([f32; 3]),
Vector4([f32; 4]),
Image(Reference<Image>),
}
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)]
pub enum ValueModel {
Scalar(f32),
Vector3([f32; 3]),
Vector4([f32; 4]),
Image(ReferenceModel<Image>),
}
#[derive(Debug, serde::Serialize)]
pub struct Parameter {
pub r#type: String,
pub name: String,
pub value: Value,
}
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)]
pub struct ParameterModel {
pub r#type: String,
pub name: String,
pub value: ValueModel,
}
impl<'de> Solver<'de, Parameter> for ParameterModel {
fn solve(self, storage_backend: &dyn resource::ReadStorageBackend) -> Result<Parameter, SolveErrors> {
Ok(Parameter {
r#type: self.r#type,
name: self.name,
value: match self.value {
ValueModel::Scalar(scalar) => Value::Scalar(scalar),
ValueModel::Vector3(vector) => Value::Vector3(vector),
ValueModel::Vector4(vector) => Value::Vector4(vector),
ValueModel::Image(image) => Value::Image(image.solve(storage_backend)?),
},
})
}
}
#[derive(Debug, serde::Serialize)]
pub enum Property {
Factor(Value),
Texture(String),
}
#[cfg(test)]
mod tests {
use super::{Binding, BindingKind, ShaderArtifact};
#[test]
fn persisted_bindings_keep_named_and_unnamed_construction_distinct() {
let unnamed = Binding::new(0, BindingKind::StorageBuffer, 1, true, false);
let named = Binding::named("scene", 1, BindingKind::StorageBuffer, 1, true, false);
assert!(unnamed.name.is_empty());
assert_eq!(named.name, "scene");
}
#[test]
#[should_panic(expected = "Invalid resource slot range")]
fn persisted_binding_rejects_flat_slot_overflow() {
Binding::new(u32::MAX, BindingKind::StorageBuffer, 1, true, false);
}
#[test]
fn dxil_shader_artifact_round_trips_through_resource_archiving() {
let bytes = crate::to_vec(&ShaderArtifact::Dxil).expect("DXIL artifact should serialize");
let artifact: ShaderArtifact = crate::from_slice(&bytes).expect("DXIL artifact should deserialize");
assert!(matches!(artifact, ShaderArtifact::Dxil));
}
}