diff options
| -rw-r--r-- | engine-ecs/src/actions.rs | 12 | ||||
| -rw-r--r-- | engine-ecs/src/entity.rs | 2 | ||||
| -rw-r--r-- | engine-ecs/src/lib.rs | 38 | ||||
| -rw-r--r-- | engine-ecs/src/sole.rs | 7 | ||||
| -rw-r--r-- | engine-ecs/src/system.rs | 4 | ||||
| -rw-r--r-- | engine/src/rendering.rs | 12 | ||||
| -rw-r--r-- | engine/src/rendering/backend/opengl.rs | 98 | ||||
| -rw-r--r-- | engine/src/rendering/main_pipeline.rs | 12 | ||||
| -rw-r--r-- | engine/src/rendering/shader.rs | 264 | ||||
| -rw-r--r-- | engine/src/ui/dear_imgui.rs | 10 | ||||
| -rw-r--r-- | engine/src/ui/view/world.rs | 182 |
11 files changed, 433 insertions, 208 deletions
diff --git a/engine-ecs/src/actions.rs b/engine-ecs/src/actions.rs index cef51cc..bbc19c6 100644 --- a/engine-ecs/src/actions.rs +++ b/engine-ecs/src/actions.rs @@ -10,6 +10,7 @@ use crate::component::{ use crate::entity::Name as EntityName; use crate::event::component::Removed; use crate::pair::Pair; +use crate::sole::Sole; use crate::system::{Metadata as SystemMetadata, Param as SystemParam}; use crate::uid::Uid; use crate::{ActionQueue, World}; @@ -304,6 +305,16 @@ impl Actions<'_> }); } + /// Queues up creation of a sole at the end of the current tick, returning the [`Uid`] + /// of the sole. + pub fn create_sole<SoleT: Sole>(&mut self, sole: SoleT) -> Uid + { + self.action_queue + .push(Action::CreateSole(sole.into_parts())); + + SoleT::id() + } + /// Stops the [`World`]. The world will finish the current tick and that tick will be /// the last. pub fn stop(&mut self) @@ -339,5 +350,6 @@ pub(crate) enum Action pair_id: Uid, new_pair: ComponentParts, }, + CreateSole(ComponentParts), Stop, } diff --git a/engine-ecs/src/entity.rs b/engine-ecs/src/entity.rs index 4d472ad..e85c49c 100644 --- a/engine-ecs/src/entity.rs +++ b/engine-ecs/src/entity.rs @@ -334,7 +334,7 @@ macro_rules! declare_entity { world.spawn_with_uid( *$ident, ($components).with_elem($crate::entity::Name { - name: concat!(module_path!(), stringify!($ident)).into() + name: concat!(module_path!(), "::", stringify!($ident)).into() }) ); }); diff --git a/engine-ecs/src/lib.rs b/engine-ecs/src/lib.rs index 6028cf7..f9e8143 100644 --- a/engine-ecs/src/lib.rs +++ b/engine-ecs/src/lib.rs @@ -60,10 +60,10 @@ use crate::sole::{Single, Sole}; use crate::system::observer::Observer; use crate::system::{ Callbacks, + Function as SystemFunction, Metadata as SystemMetadata, Param as SystemParam, System, - SystemComponent, }; use crate::uid::Uid; @@ -253,6 +253,7 @@ impl World [ sole.into_parts(), EntityName { name: name.into() }.into_parts(), + crate::sole::Is.into_parts(), ], &ComponentAddingFlags { allow_soles: true }, ); @@ -272,7 +273,7 @@ impl World self.create_ent( ent_id, - [SystemComponent { system }.into_parts()].into_iter().chain( + [SystemFunction { system }.into_parts()].into_iter().chain( ObserverT::observed_events() .into_iter() .map(IntoComponentParts::into_parts), @@ -291,7 +292,7 @@ impl World { let (type_erased_system, mut system_callbacks) = system.finish(); - let system_ent_id = self.spawn((SystemComponent { system: type_erased_system },)); + let system_ent_id = self.spawn((SystemFunction { system: type_erased_system },)); system_callbacks.on_created(self, SystemMetadata { ent_id: system_ent_id }); @@ -491,7 +492,7 @@ impl World .into_iter() .filter_map(|phase_has_system| phase_has_system.get_target_ent()) { - let Some(system) = system_entity.get::<SystemComponent>() else { + let Some(system) = system_entity.get::<SystemFunction>() else { cold_path(); continue; }; @@ -667,6 +668,31 @@ impl World &ComponentAddingFlags { allow_soles: false }, ); } + Action::CreateSole(sole_parts) => { + if let Err(EntityAlreadyExistsError) = + self.data.component_storage.create_entity(sole_parts.id) + { + tracing::error!( + name = sole_parts.name, + "Can not create sole that already exists" + ); + continue; + } + + let name = sole_parts.name; + + Self::add_entity_components( + sole_parts.id, + [ + sole_parts, + EntityName { name: name.into() }.into_parts(), + crate::sole::Is.into_parts(), + ], + &mut self.data.component_storage, + &EventSubmitter::new(&self.data.new_events), + &ComponentAddingFlags { allow_soles: true }, + ); + } Action::Stop => { self.stop.store(true, Ordering::Relaxed); } @@ -874,10 +900,10 @@ impl World { assert!(event_id.is_pair()); - let query = Query::<(&SystemComponent,)>::from_flexible_query( + let query = Query::<(&SystemFunction,)>::from_flexible_query( self.flexible_query( QueryTerms::<QUERY_MAX_TERM_CNT>::builder() - .present([SystemComponent::id(), event_id]) + .present([SystemFunction::id(), event_id]) .build(), ), ); diff --git a/engine-ecs/src/sole.rs b/engine-ecs/src/sole.rs index 9168414..cf5283b 100644 --- a/engine-ecs/src/sole.rs +++ b/engine-ecs/src/sole.rs @@ -7,7 +7,7 @@ use crate::component::{ WithAssociatedId as ComponentWithAssociatedId, }; use crate::system::{Metadata as SystemMetadata, Param as SystemParam}; -use crate::World; +use crate::{Component, World}; /// A component which has a single instance in the [`World`]. pub trait Sole: Any + IntoComponentParts + ComponentWithAssociatedId @@ -20,6 +20,11 @@ pub trait Sole: Any + IntoComponentParts + ComponentWithAssociatedId fn name(&self) -> &'static str; } +/// Marker component for the entities of soles. +#[derive(Debug, Component)] +#[non_exhaustive] +pub struct Is; + /// Holds a reference to a singleton component. #[derive(Debug)] pub struct Single<'world, SoleT: Sole> diff --git a/engine-ecs/src/system.rs b/engine-ecs/src/system.rs index e0eac57..b79f20c 100644 --- a/engine-ecs/src/system.rs +++ b/engine-ecs/src/system.rs @@ -163,8 +163,10 @@ impl Callbacks for NoCallbacks fn on_created(&mut self, _world: &mut World, _metadata: Metadata) {} } +/// System & observer function component. Intentionally not runnable outside of this +/// crate. #[derive(Debug, Component)] -pub(crate) struct SystemComponent +pub struct Function { pub(crate) system: TypeErased, } diff --git a/engine/src/rendering.rs b/engine/src/rendering.rs index bbfee72..1f1be64 100644 --- a/engine/src/rendering.rs +++ b/engine/src/rendering.rs @@ -24,7 +24,10 @@ use crate::mesh::Mesh; use crate::rendering::blending::Config as BlendingConfig; use crate::rendering::object::{Id as ObjectId, Store as ObjectStore}; use crate::rendering::shader::cursor::Binding as ShaderBinding; -use crate::rendering::shader::Program as ShaderProgram; +use crate::rendering::shader::{ + CompileTargetId as ShaderCompileTargetId, + Program as ShaderProgram, +}; use crate::texture::{CubeMapFace, Properties as TextureProperties}; use crate::vector::Vec2; use crate::windowing::dpi::PhysicalSize; @@ -199,6 +202,13 @@ impl SurfaceId } } +/// Properties of the current rendering backend. +#[derive(Debug, Sole)] +pub struct BackendProperties +{ + pub shader_target_id: ShaderCompileTargetId, +} + #[derive(Debug, Default, Sole)] pub struct RenderPasses { diff --git a/engine/src/rendering/backend/opengl.rs b/engine/src/rendering/backend/opengl.rs index b8ac04b..bdaa555 100644 --- a/engine/src/rendering/backend/opengl.rs +++ b/engine/src/rendering/backend/opengl.rs @@ -6,6 +6,7 @@ use std::convert::Infallible; use std::hint::cold_path; use std::num::NonZero; +use ecs::phase::INIT as INIT_PHASE; use ecs::query::term::With; use glutin::config::Config as GlutinConfig; use glutin::display::GetGlDisplay; @@ -118,12 +119,15 @@ use crate::rendering::shader::cursor::{ }; use crate::rendering::shader::reflection::Stage as ShaderStage; use crate::rendering::shader::{ + EntrypointFlags as ShaderEntrypointFlags, Error as ShaderError, Program as ShaderProgram, - ProgramMetadata as ShaderProgramMetadata, + VertexDescCreationError as ShaderVertexDescCreationError, + VertexDescription as ShaderVertexDescription, }; use crate::rendering::{ AssetOrValue, + BackendProperties, BufferClearMask, Command, CommandQueue, @@ -436,7 +440,7 @@ enum BackendResource Shader { program: GlShaderProgram, - program_metadata: ShaderProgramMetadata, + info: ShaderInfo, bindings: IntMap<ShaderBindingIndex, BackendShaderBinding>, }, Texture @@ -489,6 +493,14 @@ enum TextureKind CubeMap, } +#[derive(Debug, Clone)] +struct ShaderInfo +{ + /// If the shader program has a entry point in the vertex stage, this field will + /// contain a description of the vertex type passed to the entry point. + vertex_desc: Option<ShaderVertexDescription>, +} + #[derive(Debug, Default)] #[non_exhaustive] pub struct Extension {} @@ -497,6 +509,8 @@ impl crate::ecs::extension::Extension for Extension { fn collect(self, mut collector: crate::ecs::extension::Collector<'_>) { + collector.add_system(*INIT_PHASE, init); + collector.add_system(*RENDER_PHASE, handle_commands); collector.add_system(*POST_RENDER_PHASE, prepare_windows); @@ -506,6 +520,24 @@ impl crate::ecs::extension::Extension for Extension } } +fn init( + mut shader_context: Single<crate::rendering::shader::Context>, + 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)])?; + + actions.create_sole(BackendProperties { + shader_target_id: shader_target_ids[0], + }); + + Ok(()) +} + fn prepare_windows( window_query: Query<( Option<&Window>, @@ -756,6 +788,7 @@ fn handle_commands( mut object_store: Single<ObjectStore>, mut command_queue: Single<CommandQueue>, assets: Single<Assets>, + backend_properties: Single<BackendProperties>, ) -> Result<(), crate::Error> { let Ok(graphics_ctx) = graphics_ctx.get_mut() else { @@ -766,6 +799,10 @@ fn handle_commands( let command_queue = command_queue.get_mut()?; let assets = assets.get()?; + let Ok(backend_properties) = backend_properties.get() else { + return Ok(()); + }; + let GraphicsContext { ref gl_context, ref mut surfaces, @@ -776,11 +813,8 @@ fn handle_commands( return Ok(()); }; - let mut activated_gl_shader_program: Option<( - ObjectId, - GlShaderProgram, - ShaderProgramMetadata, - )> = None; + let mut activated_gl_shader_program: Option<(ObjectId, GlShaderProgram, ShaderInfo)> = + None; for command in command_queue.drain() { let tracing_span = tracing::info_span!( @@ -895,8 +929,21 @@ fn handle_commands( program: create_shader_program( gl_context, &shader_program, + backend_properties, )?, - program_metadata: shader_program.metadata().clone(), + info: ShaderInfo { + vertex_desc: if shader_program + .metadata() + .link_entrypoints + .contains(ShaderEntrypointFlags::VERTEX) + { + Some(shader_program.create_vertex_desc( + backend_properties.shader_target_id, + )?) + } else { + None + }, + }, bindings: IntMap::with_capacity(4), }) }, @@ -914,7 +961,7 @@ fn handle_commands( continue; }; - let BackendResource::Shader { program, program_metadata, bindings } = + let BackendResource::Shader { program, info: shader_info, bindings } = resource else { unreachable!(); @@ -927,7 +974,7 @@ fn handle_commands( } activated_gl_shader_program = - Some((shader_object_id, program.clone(), program_metadata.clone())); + Some((shader_object_id, program.clone(), shader_info.clone())); } Command::SetShaderBinding( shader_object_id, @@ -1162,7 +1209,7 @@ fn handle_commands( unreachable!(); }; - let Some((shader_program_obj_id, _, shader_program_metadata)) = + let Some((shader_program_obj_id, _, shader_info)) = &activated_gl_shader_program else { tracing::error!("No shader program is activated"); @@ -1174,8 +1221,7 @@ fn handle_commands( vertex_attrs_updated_for_shader != *shader_program_obj_id }, ) { - let Some(shader_program_vertex_desc) = - &shader_program_metadata.vertex_desc + let Some(shader_program_vertex_desc) = &shader_info.vertex_desc else { tracing::error!( "Activated shader program does not have a vertex description" @@ -1799,9 +1845,12 @@ fn cube_map_texture_face_to_gl(face: CubeMapTextureFace) -> GlCubeMapTextureFace fn create_shader_program( current_context: &MaybeCurrentContextWithFns, shader_program: &ShaderProgram, + backend_properties: &BackendProperties, ) -> Result<GlShaderProgram, CreateShaderError> { - let shader_program_reflection = shader_program.reflection(0).expect("Not possible"); + 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() @@ -1812,9 +1861,12 @@ fn create_shader_program( })?; 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", - )) + .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, @@ -1832,9 +1884,12 @@ fn create_shader_program( })?; 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", - )) + .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, @@ -1888,6 +1943,9 @@ enum CreateShaderError #[error("No entrypoint was found for shader stage {0:?}")] NoShaderStageEntrypointFound(ShaderStage), + #[error("Failed to create shader vertex description")] + VertexDescCreationFailed(#[from] ShaderVertexDescCreationError), + #[error(transparent)] ShaderError(#[from] GlShaderError), } diff --git a/engine/src/rendering/main_pipeline.rs b/engine/src/rendering/main_pipeline.rs index 66030ab..754b507 100644 --- a/engine/src/rendering/main_pipeline.rs +++ b/engine/src/rendering/main_pipeline.rs @@ -42,6 +42,7 @@ use crate::rendering::shader::{ }; use crate::rendering::{ AssetOrValue, + BackendProperties, BufferClearMask, Command, DepthFunction, @@ -130,6 +131,7 @@ pub fn add_passes( Traverse<(With<Scene>, With<ActiveScene>), TraverseUp, ChildOf>, )>, shader_context: Single<ShaderContext>, + backend_properties: Single<BackendProperties>, mut assets: Single<Assets>, mut render_passes: Single<RenderPasses>, mut object_store: Single<ObjectStore>, @@ -142,6 +144,10 @@ pub fn add_passes( let render_passes = render_passes.get_mut()?; let object_store = object_store.get_mut()?; + let Ok(backend_properties) = backend_properties.get() else { + return Ok(()); + }; + let Some((scene_ent_id, (scene_env_lighting, scene_skybox, scene_skybox_state))) = scene_query.iter_with_euids().next() else { @@ -186,7 +192,7 @@ pub fn add_passes( let main_3d_shader_cursor = ShaderCursor::new( shaders .main_3d_shader_program - .reflection(0) + .reflection(backend_properties.shader_target_id) .context("Unable to get reflection for main 3D shader")? .global_params_var_layout() .ok_or_else(|| { @@ -327,6 +333,7 @@ pub fn add_passes( &window, shaders.sky_box_shader_obj_id, shaders.sky_box_shader_program, + &backend_properties, )?; } } @@ -820,6 +827,7 @@ fn add_sky_box_commands( window: &Window, sky_box_shader_obj_id: ObjectId, sky_box_shader_program: &ShaderProgram, + backend_properties: &BackendProperties, ) -> Result<(), crate::Error> { render_pass @@ -828,7 +836,7 @@ fn add_sky_box_commands( let sky_box_shader_cursor = ShaderCursor::new( sky_box_shader_program - .reflection(0) + .reflection(backend_properties.shader_target_id) .unwrap() .global_params_var_layout() .unwrap(), 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}"); diff --git a/engine/src/ui/dear_imgui.rs b/engine/src/ui/dear_imgui.rs index 349cf6c..b3a102b 100644 --- a/engine/src/ui/dear_imgui.rs +++ b/engine/src/ui/dear_imgui.rs @@ -55,6 +55,7 @@ use crate::rendering::shader::{ }; use crate::rendering::{ AssetOrValue, + BackendProperties as RenderingBackendProperties, Command as RenderingCommand, DrawMeshOptions, DrawProperties, @@ -243,6 +244,7 @@ fn update( mut assets: Single<Assets>, mut render_passes: Single<RenderPasses>, shader_context: Single<ShaderContext>, + rendering_backend_properties: Single<RenderingBackendProperties>, keyboard: Single<Keyboard>, mouse: Single<Mouse>, mouse_buttons: Single<MouseButtons>, @@ -260,6 +262,10 @@ fn update( let mouse = mouse.get()?; let mouse_buttons = mouse_buttons.get()?; + let Ok(rendering_backend_properties) = rendering_backend_properties.get() else { + return Ok(()); + }; + let Some((window, window_surface)) = target_window_query.iter().next() else { return Ok(()); }; @@ -301,6 +307,7 @@ fn update( context, render_passes, shader_context, + rendering_backend_properties, window_surface.id, shader_asset.clone(), mesh_obj_id, @@ -478,6 +485,7 @@ fn add_drawing_render_pass( context: &mut Context, render_passes: &mut RenderPasses, shader_context: &ShaderContext, + rendering_backend_properties: &RenderingBackendProperties, window_surface_id: SurfaceId, shader_asset: AssetHandle<ShaderModuleSource>, mesh_obj_id: RenderingObjectId, @@ -506,7 +514,7 @@ fn add_drawing_render_pass( let shader_cursor = ShaderCursor::new( shader_program - .reflection(0) + .reflection(rendering_backend_properties.shader_target_id) .unwrap() .global_params_var_layout() .unwrap(), diff --git a/engine/src/ui/view/world.rs b/engine/src/ui/view/world.rs index 411acd7..bf92859 100644 --- a/engine/src/ui/view/world.rs +++ b/engine/src/ui/view/world.rs @@ -4,15 +4,15 @@ use std::io::Cursor; use std::ops::{Deref, DerefMut}; use ecs::error::Context; +use ecs::phase::Phase; +use ecs::query::term::{With, Without}; +use ecs::sole::Is as IsSole; +use ecs::system::Function as SystemFunction; use crate::color::{Rgb, Rgba}; use crate::ecs::actions::Actions; use crate::ecs::component::local::Local; -use crate::ecs::component::{ - Handle as ComponentHandle, - Info as ComponentInfo, - Parts as ComponentParts, -}; +use crate::ecs::component::{Info as ComponentInfo, Parts as ComponentParts}; use crate::ecs::entity::{Handle as EntityHandle, Name as EntityName}; use crate::ecs::sole::Single; use crate::ecs::uid::Uid; @@ -47,7 +47,13 @@ pub struct State } pub fn show( - query: Query<()>, + non_sole_query: Query<( + Without<ComponentInfo>, + Without<Phase>, + Without<SystemFunction>, + Without<IsSole>, + )>, + sole_query: Query<(With<IsSole>,)>, mut dear_imgui_context: Single<DearImguiContext>, mut state: Local<State>, world: &World, @@ -84,7 +90,18 @@ pub fn show( frame.spacing(); frame.spacing(); - for ent_handle in &query.into_flexible_query() { + for ent_handle in &non_sole_query.into_flexible_query() { + create_entity_widgets( + frame, + popup_state, + icon_textures, + &ent_handle, + world, + &mut actions, + ); + } + + for ent_handle in &sole_query.into_flexible_query() { create_entity_widgets( frame, popup_state, @@ -169,18 +186,6 @@ fn create_entity_widgets( actions: &mut Actions, ) { - if ent_handle.component_ids().next().is_some() - && !ent_handle.component_ids().any(|comp_id| { - world.get_entity(comp_id).is_some_and(|comp_ent| { - comp_ent - .get::<ComponentInfo>() - .is_some_and(|comp_info| comp_info.type_reflection.is_some()) - }) - }) - { - return; - } - let ent_title = create_entity_title(ent_handle); let mut is_open = false; @@ -266,14 +271,6 @@ fn create_component_widgets( return; }; - let Some(component_type) = component_info.type_reflection else { - return; - }; - - let Some(mut component) = ent_handle.get_any_by_id_mut(component_id) else { - unreachable!(); - }; - { let _color_token = frame.push_style_color( dear_imgui_bindings::StyleColor::Button, @@ -301,42 +298,43 @@ fn create_component_widgets( frame.same_line(); - let mut component_changed = false; + frame.text(component_info.name); - create_item_title_widget( - frame, - component_info.name, - &ItemType::Reflected(component_type), - None, - ); + if let Some(component_type) = component_info.type_reflection { + let item_type = ItemType::Reflected(component_type); - let item_type = ItemType::Reflected(component_type); + let _token = item_type.indent_or_same_line(frame); - let _token = item_type.indent_or_same_line(frame); + let Some(mut component) = ent_handle.get_any_by_id_mut(component_id) else { + unreachable!(); + }; - add_item_to_frame( - frame, - ItemRef::Mutable(&mut *component), - &mut component_changed, - ItemInfo { - title: component_info.name, - tag: &format!( - "world_view_entity_{}_component_{}", - ent_handle.uid(), - component_info.name, - ), - type_name: None, - ty: item_type, - is_read_only: false, - }, - &[], - ); + let mut component_changed = false; - frame.spacing(); + add_item_to_frame( + frame, + ItemRef::Mutable(&mut *component), + &mut component_changed, + ItemInfo { + title: component_info.name, + tag: &format!( + "world_view_entity_{}_component_{}", + ent_handle.uid(), + component_info.name, + ), + type_name: None, + ty: item_type, + is_read_only: false, + }, + &[], + ); - if component_changed { - component.set_changed(); + if component_changed { + component.set_changed(); + } } + + frame.spacing(); } fn create_pair_component_widgets( @@ -375,22 +373,8 @@ fn create_pair_component_widgets( frame.same_line(); - let relation_comp_info_or_ent_name = - get_comp_info_or_ent_name(world, pair_id.relation()); - - let relation_name: Cow<'_, str> = match &relation_comp_info_or_ent_name { - Some(Either::A(comp_info)) => comp_info.name.into(), - Some(Either::B(ent_name)) => format!("🌐 {}", ent_name.name).into(), - None => format!("<unnamed> {}", pair_id.relation()).into(), - }; - - let target_comp_info_or_ent_name = get_comp_info_or_ent_name(world, pair_id.target()); - - let target_name: Cow<'_, str> = match &target_comp_info_or_ent_name { - Some(Either::A(comp_info)) => comp_info.name.into(), - Some(Either::B(ent_name)) => format!("🌐 {}", ent_name.name).into(), - None => format!("<unnamed> {}", pair_id.target()).into(), - }; + let relation_name = get_comp_name_or_ent_name(world, pair_id.relation()); + let target_name = get_comp_name_or_ent_name(world, pair_id.target()); // TODO: Make relation & target ids editable @@ -402,15 +386,14 @@ fn create_pair_component_widgets( let pair_data_ty_id = (*pair_data).type_id(); - let Some(pair_data_comp_info) = [ - &relation_comp_info_or_ent_name, - &target_comp_info_or_ent_name, - ] - .map(Option::as_ref) - .map(|item| item.and_then(Either::as_a)) - .into_iter() - .flatten() - .find(|comp_info| comp_info.ty_id == pair_data_ty_id) else { + let pair_member_comp_infos = [pair_id.relation(), pair_id.target()] + .map(|member_id| world.get_entity(member_id)?.get::<ComponentInfo>()); + + let Some(pair_data_comp_info) = pair_member_comp_infos + .into_iter() + .filter_map(std::convert::identity) + .find(|comp_info| comp_info.ty_id == pair_data_ty_id) + else { return; }; @@ -465,22 +448,21 @@ fn create_pair_component_widgets( // pairs as event targets } -fn get_comp_info_or_ent_name( - world: &World, - id: Uid, -) -> Option<Either<ComponentHandle<'_, ComponentInfo>, ComponentHandle<'_, EntityName>>> +fn get_comp_name_or_ent_name(world: &World, id: Uid) -> Cow<'static, str> { - let ent = world.get_entity(id)?; + let Some(ent) = world.get_entity(id) else { + return format!("<unnamed> {id}").into(); + }; if let Some(component_info) = ent.get::<ComponentInfo>() { - return Some(Either::A(component_info)); + return component_info.name.into(); } if let Some(entity_name) = ent.get::<EntityName>() { - return Some(Either::B(entity_name)); + return format!("🌐 {}", entity_name.name).into(); } - None + format!("🌐 <unnamed> {id}").into() } fn create_add_component_button_widget( @@ -506,10 +488,24 @@ fn create_add_component_button_widget( [90.0, 162.0, 51.0, 255.0].map(|num| num / 255.0), ); - let is_button_clicked = frame.button(format!( - "+##add_component_to_entity_button_{}", - ent_handle.uid() - )); + let ent_is_sole = ent_handle.has_component::<IsSole>(); + + let is_button_clicked = { + let _disabled_token = frame.begin_disabled_with_cond(ent_is_sole); + + frame.button(format!( + "+##add_component_to_entity_button_{}", + ent_handle.uid() + )) + }; + + if ent_is_sole { + if frame.is_item_hovered_with_flags( + dear_imgui_bindings::ItemHoveredFlags::ALLOW_WHEN_DISABLED, + ) { + frame.tooltip_text("Cannot add a component to the entity of a sole"); + } + } if is_button_clicked { enter_add_component_popup_state(popup_state, ent_handle, ent_title, world); |
