use crate::*;
use std::sync::atomic::{AtomicUsize, Ordering};
use wrflib_shader_compiler::ty::Ty;
use wrflib_shader_compiler::{Decl, ShaderAst};
pub struct Shader {
pub build_geom: Option<fn() -> Geometry>,
pub code_to_concatenate: &'static [CodeFragment],
pub shader_id: AtomicUsize,
}
impl Shader {
#[allow(clippy::declare_interior_mutable_const)]
pub const DEFAULT: Shader =
Shader { build_geom: None, code_to_concatenate: &[], shader_id: AtomicUsize::new(Self::UNCOMPILED_SHADER_ID) };
const UNCOMPILED_SHADER_ID: usize = usize::MAX;
}
#[derive(Debug, Default, Clone)]
pub struct CxShaderMapping {
pub rect_instance_props: RectInstanceProps,
pub user_uniform_props: UniformProps,
pub instance_props: InstanceProps,
pub geometry_props: InstanceProps,
pub textures: Vec<PropDef>,
pub geometries: Vec<PropDef>,
pub instances: Vec<PropDef>,
pub user_uniforms: Vec<PropDef>,
pub draw_uniforms: Vec<PropDef>,
pub view_uniforms: Vec<PropDef>,
pub pass_uniforms: Vec<PropDef>,
}
impl CxShaderMapping {
pub fn from_shader_ast(shader_ast: ShaderAst) -> Self {
let mut instances = Vec::new();
let mut geometries = Vec::new();
let mut user_uniforms = Vec::new();
let mut draw_uniforms = Vec::new();
let mut view_uniforms = Vec::new();
let mut pass_uniforms = Vec::new();
let mut textures = Vec::new();
for decl in shader_ast.decls {
match decl {
Decl::Geometry(decl) => {
let prop_def = PropDef { name: decl.ident.to_string(), ty: decl.ty_expr.ty.borrow().clone().unwrap() };
geometries.push(prop_def);
}
Decl::Instance(decl) => {
let prop_def = PropDef { name: decl.ident.to_string(), ty: decl.ty_expr.ty.borrow().clone().unwrap() };
instances.push(prop_def);
}
Decl::Uniform(decl) => {
let prop_def = PropDef { name: decl.ident.to_string(), ty: decl.ty_expr.ty.borrow().clone().unwrap() };
match decl.block_ident {
Some(bi) if bi.with(|string| string == "draw") => {
draw_uniforms.push(prop_def);
}
Some(bi) if bi.with(|string| string == "view") => {
view_uniforms.push(prop_def);
}
Some(bi) if bi.with(|string| string == "pass") => {
pass_uniforms.push(prop_def);
}
None => {
user_uniforms.push(prop_def);
}
_ => (),
}
}
Decl::Texture(decl) => {
let prop_def = PropDef { name: decl.ident.to_string(), ty: decl.ty_expr.ty.borrow().clone().unwrap() };
textures.push(prop_def);
}
_ => (),
}
}
CxShaderMapping {
rect_instance_props: RectInstanceProps::construct(&instances),
user_uniform_props: UniformProps::construct(&user_uniforms),
instance_props: InstanceProps::construct(&instances),
geometry_props: InstanceProps::construct(&geometries),
textures,
instances,
geometries,
pass_uniforms,
view_uniforms,
draw_uniforms,
user_uniforms,
}
}
}
#[derive(Debug, Clone, Hash, PartialEq)]
pub struct PropDef {
pub name: String,
pub ty: Ty,
}
#[derive(Debug, Default, Clone)]
pub struct RectInstanceProps {
pub rect_pos: Option<usize>,
pub rect_size: Option<usize>,
}
impl RectInstanceProps {
pub fn construct(instances: &[PropDef]) -> RectInstanceProps {
let mut rect_pos = None;
let mut rect_size = None;
let mut slot = 0;
for inst in instances {
match inst.name.as_ref() {
"rect_pos" => rect_pos = Some(slot),
"rect_size" => rect_size = Some(slot),
_ => (),
}
slot += inst.ty.size(); }
RectInstanceProps { rect_pos, rect_size }
}
}
#[derive(Debug, Clone)]
pub struct InstanceProp {
pub name: String,
pub ty: Ty,
pub offset: usize,
pub slots: usize,
}
#[derive(Debug, Default, Clone)]
pub struct InstanceProps {
pub props: Vec<InstanceProp>,
pub total_slots: usize,
}
#[derive(Debug, Clone)]
pub struct UniformProp {
pub name: String,
pub ty: Ty,
pub offset: usize,
pub slots: usize,
}
#[derive(Debug, Default, Clone)]
pub struct UniformProps {
pub props: Vec<UniformProp>,
pub total_slots: usize,
}
#[derive(Debug, Clone)]
pub struct NamedProp {
pub name: String,
pub ty: Ty,
pub offset: usize,
pub slots: usize,
}
#[derive(Debug, Default, Clone)]
pub struct NamedProps {
pub props: Vec<NamedProp>,
pub total_slots: usize,
}
impl NamedProps {
pub fn construct(in_props: &[PropDef]) -> NamedProps {
let mut offset = 0;
let out_props = in_props
.iter()
.map(|prop| {
let slots = prop.ty.size();
let prop = NamedProp { ty: prop.ty.clone(), name: prop.name.clone(), offset, slots };
offset += slots;
prop
})
.collect();
NamedProps { props: out_props, total_slots: offset }
}
}
impl InstanceProps {
pub fn construct(in_props: &[PropDef]) -> InstanceProps {
let mut offset = 0;
let out_props = in_props
.iter()
.map(|prop| {
let slots = prop.ty.size();
let prop = InstanceProp { ty: prop.ty.clone(), name: prop.name.clone(), offset, slots };
offset += slots;
prop
})
.collect();
InstanceProps { props: out_props, total_slots: offset }
}
}
impl UniformProps {
pub fn construct(in_props: &[PropDef]) -> UniformProps {
let mut offset = 0;
let out_props = in_props
.iter()
.map(|prop| {
let slots = prop.ty.size();
let prop = UniformProp { ty: prop.ty.clone(), name: prop.name.clone(), offset, slots };
offset += slots;
prop
})
.collect();
UniformProps { props: out_props, total_slots: offset }
}
pub fn find_zbias_uniform_prop(&self) -> Option<usize> {
for prop in &self.props {
if prop.name == "zbias" {
return Some(prop.offset);
}
}
None
}
}
#[derive(Default)]
pub struct CxShader {
pub name: String,
pub gpu_geometry: Option<GpuGeometry>,
pub(crate) platform: Option<CxPlatformShader>,
pub mapping: CxShaderMapping,
pub shader_ast: Option<ShaderAst>,
}
impl Cx {
pub(crate) fn get_shader_id(&mut self, shader: &'static Shader) -> usize {
let shader_id = shader.shader_id.load(Ordering::Relaxed);
if shader_id != Shader::UNCOMPILED_SHADER_ID {
shader_id
} else {
let main_code_fragment = shader.code_to_concatenate.last().expect("No code fragments found");
let shader_name = format!("{}:{}:{}", main_code_fragment.filename, main_code_fragment.line, main_code_fragment.col);
let shader_ast = self.shader_ast_generator.generate_shader_ast(&shader_name, shader.code_to_concatenate);
let gpu_geometry = shader.build_geom.map(|build_geom| GpuGeometry::new(self, (build_geom)()));
let shader_id = self.shaders.len();
self.shaders.push(CxShader {
name: shader_name,
gpu_geometry,
mapping: CxShaderMapping::from_shader_ast(shader_ast.clone()),
platform: None,
shader_ast: Some(shader_ast),
});
self.shader_recompile_ids.push(shader_id);
shader.shader_id.store(shader_id, Ordering::Relaxed);
shader_id
}
}
}