diff options
Diffstat (limited to 'engine/src/rendering/shader.rs')
| -rw-r--r-- | engine/src/rendering/shader.rs | 264 |
1 files changed, 182 insertions, 82 deletions
diff --git a/engine/src/rendering/shader.rs b/engine/src/rendering/shader.rs index b357b2c..926302a 100644 --- a/engine/src/rendering/shader.rs +++ b/engine/src/rendering/shader.rs @@ -1,6 +1,5 @@ use std::any::type_name; use std::borrow::Cow; -use std::collections::{HashMap, HashSet}; use std::fmt::{Debug, Display, Write}; use std::path::Path; use std::str::Utf8Error; @@ -144,20 +143,52 @@ impl Program &self.metadata } - pub fn get_entry_point_code(&self, entry_point_index: u32) -> Result<Blob, Error> + pub fn get_entry_point_code( + &self, + entry_point_index: u32, + target_id: CompileTargetId, + ) -> Result<Blob, Error> { - let blob = self.inner.entry_point_code(entry_point_index.into(), 0)?; + let blob = self + .inner + .entry_point_code(entry_point_index.into(), i64::from(target_id.index))?; Ok(Blob { inner: blob }) } - pub fn reflection(&self, target: u32) - -> Result<self::reflection::Program<'_>, Error> + pub fn reflection( + &self, + target_id: CompileTargetId, + ) -> Result<self::reflection::Program<'_>, Error> { - let reflection = self.inner.layout(i64::from(target))?; + let reflection = self.inner.layout(i64::from(target_id.index))?; Ok(self::reflection::Program { inner: reflection }) } + + pub fn create_vertex_desc( + &self, + target_id: CompileTargetId, + ) -> Result<VertexDescription, VertexDescCreationError> + { + if !self + .metadata + .link_entrypoints + .contains(EntrypointFlags::VERTEX) + { + return Err(VertexDescCreationError::NoVertexStageEntrypointInProgram); + } + + let program_reflection = self + .reflection(target_id) + .map_err(VertexDescCreationError::GetProgramReflectionFailed)?; + + VertexDescription::new( + &program_reflection + .get_entry_point_by_name("vertex_main") + .expect("Not possible"), + ) + } } impl Debug for Program @@ -183,6 +214,7 @@ impl Blob self.inner.as_slice() } + #[allow(clippy::missing_errors_doc)] pub fn as_str(&self) -> Result<&str, Utf8Error> { self.inner.as_str() @@ -202,14 +234,63 @@ pub struct Settings #[derive(Sole)] pub struct Context { - _global_session: SlangGlobalSession, - session: SlangSession, - modules: HashMap<AssetId, Module>, - programs: HashMap<AssetId, Program>, + global_session: SlangGlobalSession, + session: Option<SlangSession>, + modules: hashbrown::HashMap<AssetId, Module>, + programs: hashbrown::HashMap<AssetId, Program>, } impl Context { + #[allow(clippy::missing_errors_doc)] + pub fn init_session( + &mut self, + compile_targets: &[&CompileTarget], + ) -> Result<Vec<CompileTargetId>, ContextSessionInitError> + { + if self.session.is_some() { + return Err(ContextSessionInitError::AlreadyInitialized); + } + + if compile_targets.len() > u32::MAX as usize { + return Err(ContextSessionInitError::TooManyCompileTargets); + } + + let session_options = shader_slang::CompilerOptions::default() + .optimization(shader_slang::OptimizationLevel::None) + .matrix_layout_row(true) + .debug_information(SlangDebugInfoLevel::Maximal) + .no_mangle(true); + + let (compile_target_ids, target_descs) = compile_targets + .iter() + .enumerate() + .map(|(index, compile_target)| { + // The length of compile_targets is not greater than u32::MAX + #[allow(clippy::cast_possible_wrap, clippy::cast_possible_truncation)] + ( + CompileTargetId { index: index as u32 }, + shader_slang::TargetDesc::default() + .format(compile_target.format.into_slang()), + ) + }) + .collect::<(Vec<_>, Vec<_>)>(); + + let session_desc = shader_slang::SessionDesc::default() + .targets(&target_descs) + .search_paths([""]) + .options(&session_options); + + let Some(session) = self.global_session.create_session(&session_desc) else { + tracing::error!("Failed to create shader-slang session"); + return Err(ContextSessionInitError::Internal); + }; + + self.session = Some(session); + + Ok(compile_target_ids) + } + #[must_use] pub fn get_module(&self, asset_id: &AssetId) -> Option<&Module> { @@ -229,6 +310,10 @@ impl Context link_entrypoints: EntrypointFlags, ) -> Result<Program, ComposeProgramError> { + let Some(session) = self.session.as_ref() else { + return Err(ComposeProgramError::ContextSessionNotInitialized); + }; + let entry_points = link_entrypoints .iter() .filter_map(|entrypoint_flag| { @@ -254,43 +339,63 @@ impl Context .chain([SlangComponentType::from(module.inner)]) .collect::<Vec<_>>(); - let program = self - .session + let program = session .create_composite_component_type(&components) .map_err(|err| ComposeProgramError::Other(Error(err)))?; - let vertex_desc = if link_entrypoints.contains(EntrypointFlags::VERTEX) { - // TODO: Do not have hard coded target here - let Ok(program_reflection) = program.layout(0) else { - unreachable!(); - }; - - let program_reflection = - self::reflection::Program { inner: program_reflection }; - - Some(VertexDescription::new( - &program_reflection - .get_entry_point_by_name("vertex_main") - .expect("Not possible"), - )?) - } else { - None - }; - Ok(Program { inner: program, - metadata: ProgramMetadata { vertex_desc }, + metadata: ProgramMetadata { link_entrypoints }, }) } } +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct CompileTargetId +{ + index: u32, +} + +#[derive(Debug, Default, Clone, Copy)] +#[non_exhaustive] +pub struct CompileTarget +{ + pub format: CompileTargetFormat, +} + +impl CompileTarget +{ + #[must_use] + pub fn format(mut self, format: CompileTargetFormat) -> Self + { + self.format = format; + self + } +} + +#[derive(Debug, Default, Clone, Copy)] +#[non_exhaustive] +pub enum CompileTargetFormat +{ + #[default] + Glsl, +} + +impl CompileTargetFormat +{ + fn into_slang(self) -> shader_slang::CompileTarget + { + match self { + Self::Glsl => shader_slang::CompileTarget::Glsl, + } + } +} + #[derive(Debug, Clone)] #[non_exhaustive] pub struct ProgramMetadata { - /// If the program has a entry point in the vertex stage, this field will contain a - /// description of the vertex type passed to the entry point. - pub vertex_desc: Option<VertexDescription>, + pub link_entrypoints: EntrypointFlags, } #[derive(Debug, Clone)] @@ -303,14 +408,10 @@ pub struct VertexDescription impl VertexDescription { #[tracing::instrument(skip_all, fields(vs_entry_point_name = vs_entrypoint.name()))] - pub fn new( + fn new( vs_entrypoint: &self::reflection::EntryPointReflection<'_>, - ) -> Result<Self, VertexDescriptionError> + ) -> Result<Self, VertexDescCreationError> { - if vs_entrypoint.stage() != self::reflection::Stage::Vertex { - return Err(VertexDescriptionError::EntrypointNotInVertexStage); - } - let inputs = VarInputDfsIter::new(vs_entrypoint).inspect(|var_input| { if var_input.type_layout.kind() != self::reflection::TypeKind::Struct { return; @@ -327,7 +428,7 @@ impl VertexDescription ); }); - let mut seen_inputs = HashSet::<VertexInputSemName>::new(); + let mut seen_inputs = hashbrown::HashSet::<VertexInputSemName>::new(); Ok(Self { inputs: inputs @@ -339,7 +440,7 @@ impl VertexDescription let semantic_name = var_input.var_layout.semantic_name().ok_or_else(|| { - VertexDescriptionError::VertexInputMissingSemanticName { + VertexDescCreationError::VertexInputMissingSemanticName { name: name.to_owned(), } })?; @@ -349,7 +450,7 @@ impl VertexDescription if seen_inputs.contains(&semantic_name) { return Err( - VertexDescriptionError::VertexInputHasOccupiedSemanticName { + VertexDescCreationError::VertexInputHasOccupiedSemanticName { name: name.to_owned(), }, ); @@ -364,9 +465,11 @@ impl VertexDescription var_input.type_layout.scalar_type(), ) else { - return Err(VertexDescriptionError::UnsupportedVertexInputType { - name: name.to_owned(), - }); + return Err( + VertexDescCreationError::UnsupportedVertexInputType { + name: name.to_owned(), + }, + ); }; seen_inputs.insert(semantic_name.clone()); @@ -457,10 +560,13 @@ impl Display for VertexInputSemName } #[derive(Debug, thiserror::Error)] -pub enum VertexDescriptionError +pub enum VertexDescCreationError { - #[error("Entrypoint is not in vertex stage")] - EntrypointNotInVertexStage, + #[error("Shader program has no entry point in vertex stage")] + NoVertexStageEntrypointInProgram, + + #[error("Failed to get shader program reflection")] + GetProgramReflectionFailed(#[source] Error), #[error("Type of vertex input '{name}' is not supported")] UnsupportedVertexInputType @@ -486,6 +592,9 @@ pub enum VertexDescriptionError #[derive(Debug, thiserror::Error)] pub enum ComposeProgramError { + #[error("A shader context session has not been initialized")] + ContextSessionNotInitialized, + #[error("Shader module does not have a '{entrypoint_name}' entry point")] EntrypointNotFoundError { @@ -493,13 +602,26 @@ pub enum ComposeProgramError }, #[error("Failed to create vertex description")] - VertexDescriptionCreationFailed(#[from] VertexDescriptionError), + VertexDescriptionCreationFailed(#[from] VertexDescCreationError), #[error(transparent)] Other(Error), } #[derive(Debug, thiserror::Error)] +pub enum ContextSessionInitError +{ + #[error("A shader context session has already been initialized")] + AlreadyInitialized, + + #[error("Too many compile targets. Count would overflow a u32")] + TooManyCompileTargets, + + #[error("An internal error occurred")] + Internal, +} + +#[derive(Debug, thiserror::Error)] #[error(transparent)] pub struct Error(#[from] shader_slang::Error); @@ -595,9 +717,7 @@ fn import( source: std::fs::read_to_string(file_path) .map_err(ImportError::ReadFileFailed)? .into(), - link_entrypoints: settings - .map(|settings| settings.link_entrypoints) - .unwrap_or_default(), + link_entrypoints: settings.map_or_default(|settings| settings.link_entrypoints), }); Ok(()) @@ -633,36 +753,12 @@ pub(super) fn prepare(collector: &mut crate::ecs::extension::Collector<'_>) return; }; - let session_options = shader_slang::CompilerOptions::default() - .optimization(shader_slang::OptimizationLevel::None) - .matrix_layout_row(true) - .debug_information(SlangDebugInfoLevel::Maximal) - .no_mangle(true); - - let target_desc = shader_slang::TargetDesc::default() - .format(shader_slang::CompileTarget::Glsl) - // .format(shader_slang::CompileTarget::Spirv) - .profile(global_session.find_profile("glsl_330")); - // .profile(global_session.find_profile("spirv_1_5")); - - let targets = [target_desc]; - - let session_desc = shader_slang::SessionDesc::default() - .targets(&targets) - .search_paths([""]) - .options(&session_options); - - let Some(session) = global_session.create_session(&session_desc) else { - tracing::error!("Failed to create shader-slang session"); - return; - }; - collector .add_sole(Context { - _global_session: global_session, - session, - modules: HashMap::new(), - programs: HashMap::new(), + global_session, + session: None, + modules: hashbrown::HashMap::new(), + programs: hashbrown::HashMap::new(), }) .ok(); @@ -695,6 +791,10 @@ fn load_modules( let assets = assets.get()?; let asset_events = asset_events.get()?; + let Some(session) = context.session.as_ref() else { + return Ok(()); + }; + for AssetEvent::Loaded(asset_id, asset_label) in asset_events.iter_last_tick() { let asset_handle = AssetHandle::<ModuleSource>::from_id(*asset_id); @@ -708,7 +808,7 @@ fn load_modules( tracing::debug!(asset_label=?asset_label, "Loading shader module"); - let module = match load_module(&context.session, module_source) { + let module = match load_module(session, module_source) { Ok(module) => module, Err(err) => { tracing::error!("Failed to load shader module: {err}"); |
