diff options
Diffstat (limited to 'engine/src/shader.rs')
-rw-r--r-- | engine/src/shader.rs | 156 |
1 files changed, 156 insertions, 0 deletions
diff --git a/engine/src/shader.rs b/engine/src/shader.rs new file mode 100644 index 0000000..428e5a8 --- /dev/null +++ b/engine/src/shader.rs @@ -0,0 +1,156 @@ +use std::collections::hash_map::DefaultHasher; +use std::fs::read_to_string; +use std::hash::{Hash, Hasher}; +use std::path::{Path, PathBuf}; + +use crate::shader_preprocessor::{Error as ShaderPreprocessorError, ShaderPreprocessor}; + +/// Shader program +#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)] +pub struct Program +{ + shaders: Vec<Shader>, +} + +impl Program +{ + #[must_use] + pub fn new() -> Self + { + Self { shaders: Vec::new() } + } + + pub fn push_shader(&mut self, shader: Shader) + { + self.shaders.push(shader); + } + + pub fn append_shaders(&mut self, shaders: impl IntoIterator<Item = Shader>) + { + self.shaders.extend(shaders); + } + + #[must_use] + pub fn shaders(&self) -> &[Shader] + { + &self.shaders + } + + pub(crate) fn u64_hash(&self) -> u64 + { + let mut hasher = DefaultHasher::new(); + + self.hash(&mut hasher); + + hasher.finish() + } +} + +#[derive(Debug, Clone, Hash, PartialEq, Eq)] +pub struct Shader +{ + kind: Kind, + source: String, + file: PathBuf, +} + +impl Shader +{ + /// Reads a shader from the specified source file. + /// + /// # Errors + /// Will return `Err` if: + /// - Reading the file fails + /// - The shader source is not ASCII + pub fn read_shader_file(kind: Kind, shader_file: &Path) -> Result<Self, Error> + { + let source = read_to_string(shader_file).map_err(|err| Error::ReadFailed { + source: err, + shader_file: shader_file.to_path_buf(), + })?; + + if !source.is_ascii() { + return Err(Error::SourceNotAscii); + } + + Ok(Self { + kind, + source, + file: shader_file.to_path_buf(), + }) + } + + /// Preprocesses the shaders. + /// + /// # Errors + /// Returns `Err` if preprocessing fails. + pub fn preprocess(self) -> Result<Self, Error> + { + let shader_preprocessor = ShaderPreprocessor::new( + self.file + .parent() + .ok_or(Error::SourcePathHasNoParent)? + .to_path_buf(), + ); + + let source_preprocessed = shader_preprocessor + .preprocess(self.source, &self.file) + .map_err(|err| Error::PreprocessFailed { + source: err, + shader_file: self.file.clone(), + })?; + + Ok(Self { + kind: self.kind, + source: source_preprocessed, + file: self.file.clone(), + }) + } + + #[must_use] + pub fn kind(&self) -> Kind + { + self.kind + } + + #[must_use] + pub fn source(&self) -> &str + { + &self.source + } +} + +/// Shader kind. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum Kind +{ + Vertex, + Fragment, +} + +/// Shader error +#[derive(Debug, thiserror::Error)] +pub enum Error +{ + #[error("Failed to read shader {}", shader_file.display())] + ReadFailed + { + #[source] + source: std::io::Error, + shader_file: PathBuf, + }, + + #[error("Shader source is not ASCII")] + SourceNotAscii, + + #[error("Failed to preprocess shader {}", shader_file.display())] + PreprocessFailed + { + #[source] + source: ShaderPreprocessorError, + shader_file: PathBuf, + }, + + #[error("Shader source path has no parent")] + SourcePathHasNoParent, +} |