diff options
Diffstat (limited to 'engine/src/rendering')
| -rw-r--r-- | engine/src/rendering/backend/opengl.rs | 183 | ||||
| -rw-r--r-- | engine/src/rendering/shader.rs | 159 |
2 files changed, 252 insertions, 90 deletions
diff --git a/engine/src/rendering/backend/opengl.rs b/engine/src/rendering/backend/opengl.rs index bdaa555..0a58417 100644 --- a/engine/src/rendering/backend/opengl.rs +++ b/engine/src/rendering/backend/opengl.rs @@ -62,7 +62,7 @@ use opengl_bindings::misc::{ }; use opengl_bindings::shader::{ Error as GlShaderError, - Kind as ShaderKind, + Kind as GlShaderKind, Program as GlShaderProgram, Shader as GlShader, }; @@ -119,8 +119,15 @@ use crate::rendering::shader::cursor::{ }; use crate::rendering::shader::reflection::Stage as ShaderStage; use crate::rendering::shader::{ + CompileTarget as ShaderCompileTarget, + CompileTargetFormat as ShaderCompileTargetFormat, + Context as ShaderContext, + ContextSessionInitOpts as ShaderContextSessionInitOpts, + DebugInfoLevel as ShaderDebugInfoLevel, EntrypointFlags as ShaderEntrypointFlags, Error as ShaderError, + LineDirectiveMode as ShaderLineDirectiveMode, + OptimizationLevel as ShaderOptimizationLevel, Program as ShaderProgram, VertexDescCreationError as ShaderVertexDescCreationError, VertexDescription as ShaderVertexDescription, @@ -521,15 +528,27 @@ impl crate::ecs::extension::Extension for Extension } fn init( - mut shader_context: Single<crate::rendering::shader::Context>, + mut shader_context: Single<ShaderContext>, mut actions: Actions, ) -> Result<(), crate::Error> { let shader_context = shader_context.get_mut()?; - let shader_target_ids = shader_context - .init_session(&[&crate::rendering::shader::CompileTarget::default() - .format(crate::rendering::shader::CompileTargetFormat::Glsl)])?; + let shader_target_ids = shader_context.init_session( + &if cfg!(debug_assertions) { + ShaderContextSessionInitOpts::default() + .optimization_level(ShaderOptimizationLevel::Default) + .debug_info_level(ShaderDebugInfoLevel::Maximal) + .line_directive_mode(ShaderLineDirectiveMode::Standard) + .no_mangle(true) + } else { + ShaderContextSessionInitOpts::default() + .optimization_level(ShaderOptimizationLevel::High) + .debug_info_level(ShaderDebugInfoLevel::None) + .line_directive_mode(ShaderLineDirectiveMode::None) + }, + &[&ShaderCompileTarget::default().format(ShaderCompileTargetFormat::Glsl)], + )?; actions.create_sole(BackendProperties { shader_target_id: shader_target_ids[0], @@ -921,12 +940,12 @@ fn handle_commands( } Command::CreateShaderProgram(object_id, shader_program) => { if let Err(err) = backend_resources - .try_create_resource::<CreateShaderError>( + .try_create_resource::<GlShaderCreationError>( object_store, object_id, || { Ok(BackendResource::Shader { - program: create_shader_program( + program: create_and_init_gl_shader_program( gl_context, &shader_program, backend_properties, @@ -1842,107 +1861,123 @@ fn cube_map_texture_face_to_gl(face: CubeMapTextureFace) -> GlCubeMapTextureFace } } -fn create_shader_program( +fn create_and_init_gl_shader_program( current_context: &MaybeCurrentContextWithFns, shader_program: &ShaderProgram, backend_properties: &BackendProperties, -) -> Result<GlShaderProgram, CreateShaderError> +) -> Result<GlShaderProgram, GlShaderCreationError> { + let gl_shader_program = GlShaderProgram::new(current_context); + let shader_program_reflection = shader_program .reflection(backend_properties.shader_target_id) .expect("Not possible"); - let (vs_entry_point_index, vs_entry_point_reflection) = shader_program_reflection - .entry_points() - .enumerate() - .find(|(_, entry_point)| entry_point.stage() == ShaderStage::Vertex) - .ok_or_else(|| { - CreateShaderError::NoShaderStageEntrypointFound(ShaderStage::Vertex) - })?; + if shader_program + .metadata() + .link_entrypoints + .contains(ShaderEntrypointFlags::VERTEX) + { + let Some(vs_entry_point_index) = shader_program_reflection + .entry_points() + .position(|entry_point| entry_point.stage() == ShaderStage::Vertex) + else { + unreachable!(); + }; - let vertex_shader_entry_point_code = shader_program - .get_entry_point_code( - vs_entry_point_index.try_into().expect( - "Vertex shader entry point index does not fit in 32-bit unsigned int", - ), - backend_properties.shader_target_id, - ) - .map_err(|err| CreateShaderError::GetShaderEntryPointCodeFailed { - err, - stage: ShaderStage::Vertex, - entrypoint: vs_entry_point_reflection - .name() - .map_or("(none)".into(), |name| name.to_string().into()), - })?; + let gl_vertex_shader = create_and_init_gl_shader( + current_context, + shader_program, + vs_entry_point_index, + GlShaderKind::Vertex, + backend_properties, + )?; - let (fs_entry_point_index, fs_entry_point_reflection) = shader_program_reflection - .entry_points() - .enumerate() - .find(|(_, entry_point)| entry_point.stage() == ShaderStage::Fragment) - .ok_or_else(|| { - CreateShaderError::NoShaderStageEntrypointFound(ShaderStage::Fragment) - })?; + gl_shader_program.attach(current_context, &gl_vertex_shader); + } - let fragment_shader_entry_point_code = shader_program - .get_entry_point_code( - fs_entry_point_index.try_into().expect( - "Fragment shader entry point index does not fit in 32-bit unsigned int", - ), - backend_properties.shader_target_id, - ) - .map_err(|err| CreateShaderError::GetShaderEntryPointCodeFailed { - err, - stage: ShaderStage::Fragment, - entrypoint: fs_entry_point_reflection - .name() - .map_or("(none)".into(), |name| name.to_string().into()), - })?; + if shader_program + .metadata() + .link_entrypoints + .contains(ShaderEntrypointFlags::FRAGMENT) + { + let Some(fs_entry_point_index) = shader_program_reflection + .entry_points() + .position(|entry_point| entry_point.stage() == ShaderStage::Fragment) + else { + unreachable!(); + }; - let vertex_shader = GlShader::new(current_context, ShaderKind::Vertex); + let gl_fragment_shader = create_and_init_gl_shader( + current_context, + shader_program, + fs_entry_point_index, + GlShaderKind::Fragment, + backend_properties, + )?; - vertex_shader.set_source( - current_context, - vertex_shader_entry_point_code.as_str().unwrap(), - )?; + gl_shader_program.attach(current_context, &gl_fragment_shader); + } - vertex_shader.compile(current_context)?; + gl_shader_program.link(current_context)?; - let fragment_shader = GlShader::new(current_context, ShaderKind::Fragment); + Ok(gl_shader_program) +} - fragment_shader.set_source( - current_context, - fragment_shader_entry_point_code.as_str().unwrap(), - )?; +fn create_and_init_gl_shader( + current_context: &MaybeCurrentContextWithFns, + shader_program: &ShaderProgram, + entry_point_index: usize, + gl_shader_kind: GlShaderKind, + backend_properties: &BackendProperties, +) -> Result<GlShader, GlShaderCreationError> +{ + let entry_point_index = u32::try_from(entry_point_index) + .expect("Shader entry point index does not fit in 32-bit unsigned int"); - fragment_shader.compile(current_context)?; + let entry_point_code = shader_program + .get_entry_point_code(entry_point_index, backend_properties.shader_target_id) + .map_err(|err| GlShaderCreationError::GetShaderEntryPointCodeFailed { + err, + stage: match gl_shader_kind { + GlShaderKind::Vertex => ShaderStage::Vertex, + GlShaderKind::Fragment => ShaderStage::Fragment, + }, + entry_point: shader_program + .reflection(backend_properties.shader_target_id) + .ok() + .and_then(|program_reflection| { + program_reflection + .get_entry_point_by_index(entry_point_index)? + .name() + .map(ToOwned::to_owned) + }) + .map_or("<unknown>".into(), Into::into), + })?; - let gl_shader_program = GlShaderProgram::new(current_context); + let gl_shader = GlShader::new(current_context, gl_shader_kind); - gl_shader_program.attach(current_context, &vertex_shader); - gl_shader_program.attach(current_context, &fragment_shader); + gl_shader.set_source(current_context, entry_point_code.as_str().unwrap())?; - gl_shader_program.link(current_context)?; + gl_shader.compile(current_context)?; - Ok(gl_shader_program) + Ok(gl_shader) } #[derive(Debug, thiserror::Error)] -enum CreateShaderError +enum GlShaderCreationError { #[error( - "Failed to get code of shader program entry point {entrypoint} of stage {stage:?}" + "Failed to get code of shader program entry point {entry_point} of stage {stage:?}" )] GetShaderEntryPointCodeFailed { #[source] err: ShaderError, stage: ShaderStage, - entrypoint: Cow<'static, str>, + entry_point: Cow<'static, str>, }, - #[error("No entrypoint was found for shader stage {0:?}")] - NoShaderStageEntrypointFound(ShaderStage), - #[error("Failed to create shader vertex description")] VertexDescCreationFailed(#[from] ShaderVertexDescCreationError), diff --git a/engine/src/rendering/shader.rs b/engine/src/rendering/shader.rs index 926302a..3fdc2c1 100644 --- a/engine/src/rendering/shader.rs +++ b/engine/src/rendering/shader.rs @@ -10,7 +10,6 @@ use ecs::phase::INIT as INIT_PHASE; use shader_slang::{ Blob as SlangBlob, ComponentType as SlangComponentType, - DebugInfoLevel as SlangDebugInfoLevel, EntryPoint as SlangEntryPoint, GlobalSession as SlangGlobalSession, Module as SlangModule, @@ -236,7 +235,6 @@ pub struct Context { global_session: SlangGlobalSession, session: Option<SlangSession>, - modules: hashbrown::HashMap<AssetId, Module>, programs: hashbrown::HashMap<AssetId, Program>, } @@ -245,6 +243,7 @@ impl Context #[allow(clippy::missing_errors_doc)] pub fn init_session( &mut self, + opts: &ContextSessionInitOpts, compile_targets: &[&CompileTarget], ) -> Result<Vec<CompileTargetId>, ContextSessionInitError> { @@ -257,10 +256,11 @@ impl Context } let session_options = shader_slang::CompilerOptions::default() - .optimization(shader_slang::OptimizationLevel::None) - .matrix_layout_row(true) - .debug_information(SlangDebugInfoLevel::Maximal) - .no_mangle(true); + .matrix_layout_row(true) // Matrices must be row major + .optimization(opts.optimization_level.into_slang()) + .debug_information(opts.debug_info_level.into_slang()) + .line_directive_mode(opts.line_directive_mode.into_slang()) + .no_mangle(opts.no_mangle); let (compile_target_ids, target_descs) = compile_targets .iter() @@ -271,7 +271,10 @@ impl Context ( CompileTargetId { index: index as u32 }, shader_slang::TargetDesc::default() - .format(compile_target.format.into_slang()), + .format(compile_target.format.into_slang()) + // For some reason, the compiler options need to be specified + // for each target as well. Otherwise, they are not used + .options(&session_options), ) }) .collect::<(Vec<_>, Vec<_>)>(); @@ -292,12 +295,6 @@ impl Context } #[must_use] - pub fn get_module(&self, asset_id: &AssetId) -> Option<&Module> - { - self.modules.get(asset_id) - } - - #[must_use] pub fn get_program(&self, asset_id: &AssetId) -> Option<&Program> { self.programs.get(asset_id) @@ -350,6 +347,139 @@ impl Context } } +#[derive(Debug, Default, Clone)] +#[non_exhaustive] +pub struct ContextSessionInitOpts +{ + pub optimization_level: OptimizationLevel, + pub debug_info_level: DebugInfoLevel, + pub line_directive_mode: LineDirectiveMode, + pub no_mangle: bool, +} + +impl ContextSessionInitOpts +{ + pub fn optimization_level(mut self, optimization_level: OptimizationLevel) -> Self + { + self.optimization_level = optimization_level; + self + } + + pub fn debug_info_level(mut self, debug_info_level: DebugInfoLevel) -> Self + { + self.debug_info_level = debug_info_level; + self + } + + pub fn line_directive_mode(mut self, line_directive_mode: LineDirectiveMode) -> Self + { + self.line_directive_mode = line_directive_mode; + self + } + + pub fn no_mangle(mut self, no_mangle: bool) -> Self + { + self.no_mangle = no_mangle; + self + } +} + +#[derive(Debug, Default, Clone, Copy, Hash, PartialEq, Eq)] +#[non_exhaustive] +pub enum OptimizationLevel +{ + /// Don't optimize at all. + None, + + /// Default optimization level: balance code quality and compilation time. + #[default] + Default, + + /// Optimize aggressively. + High, + + /// Include optimizations that may take a very long time, or may involve severe + /// space-vs-speed tradeoffs + Maximal, +} + +impl OptimizationLevel +{ + fn into_slang(self) -> shader_slang::OptimizationLevel + { + match self { + Self::None => shader_slang::OptimizationLevel::None, + Self::Default => shader_slang::OptimizationLevel::Default, + Self::High => shader_slang::OptimizationLevel::High, + Self::Maximal => shader_slang::OptimizationLevel::Maximal, + } + } +} + +#[derive(Debug, Default, Clone, Copy, Hash, PartialEq, Eq)] +#[non_exhaustive] +pub enum DebugInfoLevel +{ + /// Don't emit debug information at all. + #[default] + None, + + /// Emit as little debug information as possible, while still supporting stack + /// trackers. + Minimal, + + /// Emit whatever is the standard level of debug information for each target. + Standard, + + /// Emit as much debug information as possible for each target. + Maximal, +} + +impl DebugInfoLevel +{ + fn into_slang(self) -> shader_slang::DebugInfoLevel + { + match self { + Self::None => shader_slang::DebugInfoLevel::None, + Self::Minimal => shader_slang::DebugInfoLevel::Minimal, + Self::Standard => shader_slang::DebugInfoLevel::Standard, + Self::Maximal => shader_slang::DebugInfoLevel::Maximal, + } + } +} + +#[derive(Debug, Default, Copy, Clone, Hash, PartialEq, Eq)] +#[non_exhaustive] +pub enum LineDirectiveMode +{ + /// Don't emit line directives at all. + #[default] + None, + + /// Emit standard C-style `#line` directives. + Standard, + + /// Emit GLSL-style directives with file *number* instead of name + Glsl, + + /// Use a source map to track line mappings (ie no #line will appear in emitting + /// source) + SourceMap, +} + +impl LineDirectiveMode +{ + fn into_slang(self) -> shader_slang::LineDirectiveMode + { + match self { + Self::None => shader_slang::LineDirectiveMode::None, + Self::Standard => shader_slang::LineDirectiveMode::Standard, + Self::Glsl => shader_slang::LineDirectiveMode::Glsl, + Self::SourceMap => shader_slang::LineDirectiveMode::SourceMap, + } + } +} + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct CompileTargetId { @@ -757,7 +887,6 @@ pub(super) fn prepare(collector: &mut crate::ecs::extension::Collector<'_>) .add_sole(Context { global_session, session: None, - modules: hashbrown::HashMap::new(), programs: hashbrown::HashMap::new(), }) .ok(); @@ -816,8 +945,6 @@ fn load_modules( } }; - context.modules.insert(*asset_id, module.clone()); - if !module_source.link_entrypoints.is_empty() { assert!(!context.programs.contains_key(asset_id)); |
