use std::any::TypeId;
use crate::hot_reloading::HotReloadState;
use naga_oil::compose::{Composer, ComposerError};
use std::path::PathBuf;
use std::sync::OnceLock;
use dashmap::DashMap;
use wgpu::Device;
use wgpu::naga::Module;
#[derive(Debug, Default, Clone)]
pub struct ShaderRegistry {
paths: DashMap<TypeId, PathBuf>,
}
impl ShaderRegistry {
pub fn get() -> &'static ShaderRegistry {
static SHADER_REGISTRY: OnceLock<ShaderRegistry> = OnceLock::new();
SHADER_REGISTRY.get_or_init(ShaderRegistry::default)
}
pub fn set_path<T: Shader>(&self, path: PathBuf) {
self.paths.insert(TypeId::of::<T>(), path);
}
pub fn get_path<T: Shader>(&self) -> Option<PathBuf> {
self.paths.get(&TypeId::of::<T>()).map(|p| p.clone())
}
pub fn remove_path<T: Shader>(&self) {
self.paths.remove(&TypeId::of::<T>());
}
}
pub trait Shader: Sized + 'static {
const FILE_PATH: &'static str;
fn from_device(device: &wgpu::Device) -> Result<Self, ComposerError>;
fn src() -> String;
fn flat_wgsl() -> Result<String, ComposerError> {
let module = Self::naga_module()?;
Ok(crate::utils::naga_module_to_wgsl(&module))
}
fn naga_module() -> Result<Module, ComposerError>;
fn compose(composer: &mut Composer) -> Result<(), ComposerError>;
fn composer() -> Result<Composer, ComposerError> {
let mut composer = Composer::default();
Self::compose(&mut composer)?;
Ok(composer)
}
fn absolute_path() -> Option<PathBuf>;
fn set_absolute_path(path: PathBuf) {
ShaderRegistry::get().paths.insert(TypeId::of::<Self>(), path);
}
fn watch_sources(state: &mut HotReloadState) -> notify::Result<()>;
fn needs_reload(state: &HotReloadState) -> bool;
fn reload_if_changed(
&mut self,
device: &Device,
state: &HotReloadState,
) -> Result<bool, ComposerError> {
if Self::needs_reload(state) {
*self = Self::from_device(device)?;
Ok(true)
} else {
Ok(false)
}
}
}