diff options
Diffstat (limited to 'engine')
| -rw-r--r-- | engine/src/asset.rs | 11 | ||||
| -rw-r--r-- | engine/src/renderer.rs | 173 | ||||
| -rw-r--r-- | engine/src/renderer/main_render_pass.rs | 335 | ||||
| -rw-r--r-- | engine/src/renderer/object.rs | 52 | ||||
| -rw-r--r-- | engine/src/renderer/opengl.rs | 1267 | ||||
| -rw-r--r-- | engine/src/util.rs | 26 |
6 files changed, 867 insertions, 997 deletions
diff --git a/engine/src/asset.rs b/engine/src/asset.rs index 9b11317..e9a3831 100644 --- a/engine/src/asset.rs +++ b/engine/src/asset.rs @@ -265,6 +265,17 @@ impl Assets Some(label) } + pub fn get_label_by_id(&self, id: Id) -> Option<LabelOwned> + { + let lookup_entry = self.asset_lookup.borrow().get(&id.label_hash)?.clone(); + + let LookupEntry::Occupied(_, label) = lookup_entry else { + return None; + }; + + Some(label) + } + #[tracing::instrument(skip(self))] pub fn load<'i, Asset: 'static + Send + Sync>( &self, diff --git a/engine/src/renderer.rs b/engine/src/renderer.rs index 14b8d49..fd48792 100644 --- a/engine/src/renderer.rs +++ b/engine/src/renderer.rs @@ -1,16 +1,17 @@ -use std::any::type_name; use std::collections::VecDeque; use std::sync::atomic::{AtomicU64, Ordering}; use bitflags::bitflags; use ecs::actions::Actions; -use ecs::entity::obtainer::Obtainer as EntityObtainer; +use ecs::component::local::Local; use ecs::event::component::{Changed, Removed}; -use ecs::pair::{ChildOf, Pair, Wildcard}; +use ecs::pair::{ChildOf, Pair}; use ecs::phase::{POST_UPDATE as POST_UPDATE_PHASE, Phase}; +use ecs::query::term::With; use ecs::sole::Single; +use ecs::system::Into; +use ecs::system::initializable::Initializable; use ecs::system::observer::Observe; -use ecs::uid::Uid; use ecs::{Component, Query, Sole, declare_entity}; use crate::asset::Handle as AssetHandle; @@ -19,7 +20,7 @@ use crate::data_types::dimens::Dimens; use crate::draw_flags::PolygonModeConfig; use crate::mesh::Mesh; use crate::renderer::blending::Config as BlendingConfig; -use crate::renderer::object::Id as ObjectId; +use crate::renderer::object::{Id as ObjectId, Store as ObjectStore}; use crate::shader::Program as ShaderProgram; use crate::shader::cursor::{ BindingLocation as ShaderBindingLocation, @@ -90,12 +91,19 @@ impl ecs::extension::Extension for Extension collector.add_declared_entity(&POST_RENDER_PHASE); let _ = collector.add_sole(RenderPasses::default()); + let _ = collector.add_sole(CommandQueue::default()); + let _ = collector.add_sole(ObjectStore::default()); let _ = collector.add_sole(self.graphics_props); collector.add_system(*PRE_RENDER_PHASE, main_render_pass::add_main_render_passes); - collector.add_system(*RENDER_PHASE, enqueue_commands_from_render_passes); + collector.add_system( + *RENDER_PHASE, + enqueue_commands_from_render_passes + .into_system() + .initialize((ActiveDrawProperties::default(),)), + ); collector.add_observer(handle_window_changed); collector.add_observer(handle_window_removed); @@ -204,7 +212,6 @@ pub struct RenderPasses #[derive(Debug)] pub struct RenderPass { - pub renderer_ctx_ent_id: Uid, pub surface_id: SurfaceId, pub commands: Vec<Command>, pub draw_properties: DrawProperties, @@ -366,16 +373,10 @@ bitflags! { } } -#[derive(Debug, Default, Clone, Component)] -pub struct ActiveDrawProperties -{ - pub draw_properties: DrawProperties, -} - /// Renderer command FIFO queue. /// /// This component is present in renderer context entities. -#[derive(Debug, Component)] +#[derive(Debug, Sole)] pub struct CommandQueue { queue: VecDeque<Command>, @@ -402,53 +403,19 @@ impl Default for CommandQueue } } -#[derive(Debug, Component)] -pub struct WindowUsingRendererCtx; - -#[derive(Debug, Component)] -pub struct CtxUsedByWindow; - #[tracing::instrument(skip_all)] pub fn enqueue_commands_from_render_passes( - renderer_ctx_query: Query<( - &mut CommandQueue, - &[Pair<CtxUsedByWindow, Wildcard>], - &mut ActiveDrawProperties, - )>, + window_surface_spec_query: Query<(&SurfaceSpec,), (With<Window>,)>, + mut command_queue: Single<CommandQueue>, mut render_passes: Single<RenderPasses>, - entity_obtainer: EntityObtainer, + mut active_draw_props: Local<ActiveDrawProperties>, ) { - let Some((_, _, mut active_draw_props)) = renderer_ctx_query.iter().next() else { - return; - }; - let mut last_render_pass_draw_props = active_draw_props.draw_properties.clone(); for render_pass in render_passes.passes.drain(..) { - let Some(renderer_ctx_ent) = - entity_obtainer.get_entity(render_pass.renderer_ctx_ent_id) - else { - tracing::error!( - renderer_ctx_entity_id=%render_pass.renderer_ctx_ent_id, - "Renderer context entity does not exist" - ); - - continue; - }; - - let Some(mut renderer_ctx_command_queue) = - renderer_ctx_ent.get_mut::<CommandQueue>() - else { - tracing::error!( - renderer_ctx_entity_id=%render_pass.renderer_ctx_ent_id, - "Renderer context entity does not have a command queue component" - ); - continue; - }; - if render_pass.draw_properties != last_render_pass_draw_props { - renderer_ctx_command_queue.push(Command::UpdateDrawProperties( + command_queue.push(Command::UpdateDrawProperties( render_pass.draw_properties.clone(), DrawPropertiesUpdateFlags::all(), )); @@ -465,9 +432,7 @@ pub fn enqueue_commands_from_render_passes( }) .last(); - renderer_ctx_command_queue - .queue - .extend(render_pass.commands); + command_queue.queue.extend(render_pass.commands); if let Some(last_updated_draw_props) = last_updated_draw_props { last_render_pass_draw_props = last_updated_draw_props; @@ -476,35 +441,15 @@ pub fn enqueue_commands_from_render_passes( active_draw_props.draw_properties = last_render_pass_draw_props; - drop(active_draw_props); - - for (mut command_queue, used_by_windows, _) in &renderer_ctx_query { - for ctx_used_by_window in used_by_windows { - let window_ent_id = ctx_used_by_window.id().target_entity(); - - let Some(window_ent) = ctx_used_by_window.get_target_ent() else { - tracing::error!("Window entity does not exist"); - continue; - }; - - let Some(surface_spec) = window_ent.get::<SurfaceSpec>() else { - tracing::debug!( - window_entity_id=%window_ent_id, - "Window entity does not have a {} component", - type_name::<SurfaceSpec>() - ); - continue; - }; - - command_queue.push(Command::SwapBuffers(surface_spec.id)); - } + for (window_surface_spec,) in &window_surface_spec_query { + command_queue.push(Command::SwapBuffers(window_surface_spec.id)); } } #[tracing::instrument(skip_all)] fn handle_window_changed( observe: Observe<Pair<Changed, Window>>, - entity_obtainer: EntityObtainer, + mut command_queue: Single<CommandQueue>, ) { for evt_match in &observe { @@ -514,34 +459,6 @@ fn handle_window_changed( continue; }; - let Some(renderer_ctx_ent_id) = window_ent - .get_matching_components( - Pair::builder() - .relation::<WindowUsingRendererCtx>() - .target_id(Wildcard::uid()) - .build() - .id(), - ) - .next() - .map(|comp_ref| comp_ref.id().target_entity()) - else { - continue; - }; - - let Some(renderer_ctx_ent) = entity_obtainer.get_entity(renderer_ctx_ent_id) - else { - tracing::error!("Renderer context entity does not exist"); - continue; - }; - - let Some(mut command_queue) = renderer_ctx_ent.get_mut::<CommandQueue>() else { - tracing::error!( - "Renderer context entity does not have a {} component", - type_name::<CommandQueue>() - ); - continue; - }; - command_queue.queue.push_front(Command::SetViewport { position: Vec2 { x: 0, y: 0 }, size: *evt_match.get_changed_comp().inner_size(), @@ -554,7 +471,11 @@ fn handle_window_changed( } #[tracing::instrument(skip_all)] -fn handle_window_removed(observe: Observe<Pair<Removed, Window>>, mut actions: Actions) +fn handle_window_removed( + observe: Observe<Pair<Removed, Window>>, + mut command_queue: Single<CommandQueue>, + mut actions: Actions, +) { for evt_match in &observe { let window_ent_id = evt_match.id(); @@ -573,41 +494,9 @@ fn handle_window_removed(observe: Observe<Pair<Removed, Window>>, mut actions: A actions.remove_comps::<(SurfaceSpec,)>(window_ent_id); - let Some(with_renderer_ctx_pair) = window_ent - .get_first_wildcard_pair_match::<WindowUsingRendererCtx, Wildcard>() - else { - tracing::warn!( - "Window entity is missing a ({}, *) pair", - type_name::<WindowUsingRendererCtx>() - ); - continue; - }; - - let Some(renderer_context_ent) = with_renderer_ctx_pair.get_target_ent() else { - continue; - }; - - let Some(mut command_queue) = renderer_context_ent.get_mut::<CommandQueue>() - else { - continue; - }; - command_queue .queue .push_front(Command::RemoveSurface(window_surface_spec.id)); - - let renderer_context_ent_id = with_renderer_ctx_pair.id().target_entity(); - - actions.remove_components( - renderer_context_ent_id, - [Pair::builder() - .relation::<CtxUsedByWindow>() - .target_id(window_ent_id) - .build() - .id()], - ); - - actions.remove_components(window_ent_id, [with_renderer_ctx_pair.id()]); } } @@ -632,3 +521,9 @@ impl<'a> Extend<(ShaderCursor<'a>, ShaderBindingValue)> for PendingShaderBinding )) } } + +#[derive(Debug, Default, Clone, Component)] +struct ActiveDrawProperties +{ + pub draw_properties: DrawProperties, +} diff --git a/engine/src/renderer/main_render_pass.rs b/engine/src/renderer/main_render_pass.rs index ec490e4..1868f3d 100644 --- a/engine/src/renderer/main_render_pass.rs +++ b/engine/src/renderer/main_render_pass.rs @@ -1,8 +1,4 @@ -use std::any::type_name; - use ecs::Query; -use ecs::actions::Actions; -use ecs::pair::{Pair, Wildcard}; use ecs::query::term::{With, Without}; use ecs::sole::Single; @@ -13,8 +9,6 @@ use crate::renderer::object::{Id as RendererObjectId, Store as RendererObjectSto use crate::renderer::{ BufferClearMask as RendererBufferClearMask, Command as RendererCommand, - CommandQueue as RendererCommandQueue, - CtxUsedByWindow as RendererCtxUsedByWindow, DrawMeshOptions as RendererDrawMeshOptions, DrawProperties as RendererDrawProperties, DrawPropertiesUpdateFlags as RendererDrawPropertiesUpdateFlags, @@ -22,7 +16,7 @@ use crate::renderer::{ PendingShaderBindings, RenderPass, RenderPasses as RendererRenderPasses, - SurfaceSpec as RendererSurfaceSpec, + SurfaceSpec, }; use crate::shader::default::ASSET_LABEL as DEFAULT_SHADER_ASSET_LABEL; use crate::shader::{ @@ -42,18 +36,12 @@ type RenderableEntity<'a> = ( #[tracing::instrument(skip_all)] pub fn add_main_render_passes( - renderer_ctx_query: Query< - ( - &RendererObjectStore, - &[Pair<RendererCtxUsedByWindow, Wildcard>], - ), - (With<RendererCommandQueue>,), - >, renderable_query: Query<RenderableEntity<'_>, (Without<NoDraw>,)>, + window_surface_spec_query: Query<(&SurfaceSpec,), (With<Window>,)>, assets: Single<Assets>, shader_context: Single<ShaderContext>, mut render_passes: Single<RendererRenderPasses>, - mut actions: Actions, + mut object_store: Single<RendererObjectStore>, ) { let Some(default_shader_asset) = assets @@ -63,217 +51,180 @@ pub fn add_main_render_passes( return; }; - for (renderer_ctx_ent_id, (object_store, used_by_windows)) in - renderer_ctx_query.iter_with_euids() - { - for ctx_used_by_window in used_by_windows { - let window_ent_id = ctx_used_by_window.id().target_entity(); + for (surface_spec,) in &window_surface_spec_query { + let render_pass = render_passes.passes.push_front_mut(RenderPass { + surface_id: surface_spec.id, + commands: Vec::with_capacity(30), + draw_properties: RendererDrawProperties::default(), + }); - let Some(window_ent) = ctx_used_by_window.get_target_ent() else { - tracing::error!("Window entity does not exist"); - continue; - }; + render_pass + .commands + .push(RendererCommand::MakeCurrent(surface_spec.id)); - if window_ent.get::<Window>().is_none() { - tracing::debug!( - window_entity_id=%window_ent_id, - "Window entity does not have a {} component", - type_name::<Window>() - ); + let default_texture_asset = assets + .get_handle_to_loaded::<Texture>(TEXTURE_WHITE_1X1_ASSET_LABEL.clone()) + .expect("Not possible"); - actions.remove_components( - renderer_ctx_ent_id, - [Pair::builder() - .relation::<RendererCtxUsedByWindow>() - .target_id(window_ent_id) - .build() - .id()], - ); + if !object_store.contains_maybe_pending_with_id(&RendererObjectId::Asset( + default_texture_asset.id(), + )) { + object_store + .insert_pending(RendererObjectId::Asset(default_texture_asset.id())); - continue; - }; + render_pass + .commands + .push(RendererCommand::CreateTexture(default_texture_asset)); + } - let Some(surface_spec) = window_ent.get::<RendererSurfaceSpec>() else { - tracing::debug!( - window_entity_id=%window_ent_id, - "Window entity does not have a {} component", - type_name::<RendererSurfaceSpec>() - ); - continue; - }; + render_pass.commands.push(RendererCommand::ClearBuffers( + RendererBufferClearMask::COLOR | RendererBufferClearMask::DEPTH, + )); - render_passes.passes.push_front(RenderPass { - renderer_ctx_ent_id, - surface_id: surface_spec.id, - commands: Vec::with_capacity(30), - draw_properties: RendererDrawProperties::default(), - }); + for (model, draw_flags, shader, mut pending_shader_bindings) in &renderable_query + { + let shader_asset = match &shader { + Some(shader) => &shader.asset_handle, + None => &default_shader_asset, + }; - let render_pass = render_passes.passes.front_mut().expect("Not possible"); + if pending_shader_bindings.as_ref().map_or_else( + || true, + |pending_shader_bindings| pending_shader_bindings.bindings.is_empty(), + ) { + continue; + } - render_pass - .commands - .push(RendererCommand::MakeCurrent(surface_spec.id)); + let Some(model_spec) = assets.get(&model.spec_asset) else { + continue; + }; - let default_texture_asset = assets - .get_handle_to_loaded::<Texture>(TEXTURE_WHITE_1X1_ASSET_LABEL.clone()) - .expect("Not possible"); + let Some(mesh_asset) = &model_spec.mesh_asset else { + continue; + }; - if !object_store - .contains_with_id(&RendererObjectId::Asset(default_texture_asset.id())) - { - render_pass - .commands - .push(RendererCommand::CreateTexture(default_texture_asset)); + if assets.get(mesh_asset).is_none() { + continue; } - render_pass.commands.push(RendererCommand::ClearBuffers( - RendererBufferClearMask::COLOR | RendererBufferClearMask::DEPTH, - )); + debug_assert!(model_spec.material_names.len() <= 1); - for (model, draw_flags, shader, mut pending_shader_bindings) in - &renderable_query - { - let shader_asset = match &shader { - Some(shader) => &shader.asset_handle, - None => &default_shader_asset, - }; - - if pending_shader_bindings.as_ref().map_or_else( - || true, - |pending_shader_bindings| pending_shader_bindings.bindings.is_empty(), - ) { - continue; + let model_material_asset = match model_spec.find_first_material(&assets) { + MaterialSearchResult::Found(model_material_asset) => { + model_material_asset.clone() + // Some(model_material_asset.clone()) } - - let Some(model_spec) = assets.get(&model.spec_asset) else { + MaterialSearchResult::NotFound | MaterialSearchResult::NoMaterials => { + // MaterialSearchResult::NotFound => { continue; - }; + } // MaterialSearchResult::NoMaterials => None, + }; - let Some(mesh_asset) = &model_spec.mesh_asset else { + if !object_store.contains_maybe_pending_with_id(&RendererObjectId::Asset( + shader_asset.id(), + )) { + let Some(shader_program) = shader_context.get_program(&shader_asset.id()) + else { + tracing::error!( + "Shader context doesn't have a program for shader asset {:?}", + assets.get_label(&shader_asset) + ); continue; }; - if assets.get(mesh_asset).is_none() { - continue; - } + object_store.insert_pending(RendererObjectId::Asset(shader_asset.id())); + + render_pass + .commands + .push(RendererCommand::CreateShaderProgram( + RendererObjectId::Asset(shader_asset.id()), + shader_program.clone(), + )); + } - debug_assert!(model_spec.material_names.len() <= 1); + render_pass.commands.push(RendererCommand::ActivateShader( + RendererObjectId::Asset(shader_asset.id()), + )); - let model_material_asset = match model_spec.find_first_material(&assets) { - MaterialSearchResult::Found(model_material_asset) => { - model_material_asset.clone() - // Some(model_material_asset.clone()) - } - MaterialSearchResult::NotFound - | MaterialSearchResult::NoMaterials => { - // MaterialSearchResult::NotFound => { - continue; - } // MaterialSearchResult::NoMaterials => None, - }; + let Some(model_material) = assets.get(&model_material_asset) else { + // TODO: Handle this case since it may occur + unreachable!(); + }; - if !object_store - .contains_with_id(&RendererObjectId::Asset(shader_asset.id())) - { - let Some(shader_program) = - shader_context.get_program(&shader_asset.id()) - else { - tracing::error!( - "Shader context doesn't have a program for shader asset {:?}", - assets.get_label(&shader_asset) - ); - continue; - }; + for texture_asset in [ + &model_material.ambient_map, + &model_material.diffuse_map, + &model_material.specular_map, + ] + .into_iter() + .flatten() + { + if !object_store.contains_maybe_pending_with_id(&RendererObjectId::Asset( + texture_asset.id(), + )) { + object_store + .insert_pending(RendererObjectId::Asset(texture_asset.id())); render_pass .commands - .push(RendererCommand::CreateShaderProgram( - RendererObjectId::Asset(shader_asset.id()), - shader_program.clone(), - )); + .push(RendererCommand::CreateTexture(texture_asset.clone())); } + } - render_pass.commands.push(RendererCommand::ActivateShader( - RendererObjectId::Asset(shader_asset.id()), - )); - - let Some(model_material) = assets.get(&model_material_asset) else { - // TODO: Handle this case since it may occur - unreachable!(); - }; - - for texture_asset in [ - &model_material.ambient_map, - &model_material.diffuse_map, - &model_material.specular_map, - ] - .into_iter() - .flatten() + if let Some(pending_shader_bindings) = &mut pending_shader_bindings { + for (shader_binding_loc, shader_binding_val) in + pending_shader_bindings.bindings.drain(..) { - if !object_store - .contains_with_id(&RendererObjectId::Asset(texture_asset.id())) - { - render_pass - .commands - .push(RendererCommand::CreateTexture(texture_asset.clone())); - } + render_pass.commands.push(RendererCommand::SetShaderBinding( + shader_binding_loc, + shader_binding_val, + )); } + } - if let Some(pending_shader_bindings) = &mut pending_shader_bindings { - for (shader_binding_loc, shader_binding_val) in - pending_shader_bindings.bindings.drain(..) - { - render_pass.commands.push(RendererCommand::SetShaderBinding( - shader_binding_loc, - shader_binding_val, - )); - } - } + if let Some(draw_flags) = draw_flags.as_deref() + && draw_flags.polygon_mode_config != PolygonModeConfig::default() + { + render_pass + .commands + .push(RendererCommand::UpdateDrawProperties( + RendererDrawProperties { + polygon_mode_config: draw_flags.polygon_mode_config.clone(), + ..Default::default() + }, + RendererDrawPropertiesUpdateFlags::POLYGON_MODE_CONFIG, + )); + } - if let Some(draw_flags) = draw_flags.as_deref() - && draw_flags.polygon_mode_config != PolygonModeConfig::default() - { - render_pass - .commands - .push(RendererCommand::UpdateDrawProperties( - RendererDrawProperties { - polygon_mode_config: draw_flags - .polygon_mode_config - .clone(), - ..Default::default() - }, - RendererDrawPropertiesUpdateFlags::POLYGON_MODE_CONFIG, - )); - } + if !object_store + .contains_maybe_pending_with_id(&RendererObjectId::Asset(mesh_asset.id())) + { + object_store.insert_pending(RendererObjectId::Asset(mesh_asset.id())); - if !object_store - .contains_with_id(&RendererObjectId::Asset(mesh_asset.id())) - { - render_pass.commands.push(RendererCommand::CreateMesh { - obj_id: RendererObjectId::Asset(mesh_asset.id()), - mesh: None, - usage: RendererMeshUsage::Static, - }); - } + render_pass.commands.push(RendererCommand::CreateMesh { + obj_id: RendererObjectId::Asset(mesh_asset.id()), + mesh: None, + usage: RendererMeshUsage::Static, + }); + } - render_pass.commands.push(RendererCommand::DrawMesh( - RendererObjectId::Asset(mesh_asset.id()), - RendererDrawMeshOptions::default(), - )); + render_pass.commands.push(RendererCommand::DrawMesh( + RendererObjectId::Asset(mesh_asset.id()), + RendererDrawMeshOptions::default(), + )); - if let Some(draw_flags) = draw_flags.as_deref() - && draw_flags.polygon_mode_config != PolygonModeConfig::default() - { - render_pass - .commands - .push(RendererCommand::UpdateDrawProperties( - RendererDrawProperties { - polygon_mode_config: PolygonModeConfig::default(), - ..Default::default() - }, - RendererDrawPropertiesUpdateFlags::POLYGON_MODE_CONFIG, - )); - } + if let Some(draw_flags) = draw_flags.as_deref() + && draw_flags.polygon_mode_config != PolygonModeConfig::default() + { + render_pass + .commands + .push(RendererCommand::UpdateDrawProperties( + RendererDrawProperties { + polygon_mode_config: PolygonModeConfig::default(), + ..Default::default() + }, + RendererDrawPropertiesUpdateFlags::POLYGON_MODE_CONFIG, + )); } } } diff --git a/engine/src/renderer/object.rs b/engine/src/renderer/object.rs index 13be4a4..bdff885 100644 --- a/engine/src/renderer/object.rs +++ b/engine/src/renderer/object.rs @@ -1,8 +1,7 @@ use std::collections::HashMap; -use std::collections::hash_map::Entry as HashMapEntry; use std::sync::atomic::{AtomicU64, Ordering}; -use ecs::Component; +use ecs::Sole; use crate::asset::Id as AssetId; @@ -26,23 +25,31 @@ impl Id NEXT_SEQUENTIAL_ID.fetch_add(1, Ordering::Relaxed), )) } + + pub fn into_asset_id(self) -> Option<AssetId> + { + match self { + Self::Asset(asset_id) => Some(asset_id), + Self::Sequential(_) => None, + } + } } #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct SequentialId(u64); /// Renderer object store. -#[derive(Debug, Default, Component)] +#[derive(Debug, Default, Sole)] pub struct Store { - objects: HashMap<Id, Object>, + objects: HashMap<Id, Option<Object>>, } impl Store { pub fn get_obj(&self, id: &Id) -> Option<&Object> { - self.objects.get(id) + self.objects.get(id).and_then(|obj| obj.as_ref()) } pub fn get_texture_obj(&self, id: &Id) -> Option<&Object> @@ -67,46 +74,29 @@ impl Store Some(obj) } - pub fn contains_with_id(&self, id: &Id) -> bool + pub fn contains_maybe_pending_with_id(&self, id: &Id) -> bool { self.objects.contains_key(id) } - pub fn insert(&mut self, id: Id, object: Object) - { - self.objects.insert(id, object); - } - - pub fn remove(&mut self, id: &Id) -> Option<Object> + pub fn contains_non_pending_with_id(&self, id: &Id) -> bool { - self.objects.remove(id) + self.objects.get(id).and_then(|obj| obj.as_ref()).is_some() } - pub fn entry(&mut self, id: Id) -> StoreEntry<'_> + pub fn insert(&mut self, id: Id, object: Object) { - StoreEntry { inner: self.objects.entry(id) } + self.objects.insert(id, Some(object)); } -} - -#[derive(Debug)] -pub struct StoreEntry<'store> -{ - inner: HashMapEntry<'store, Id, Object>, -} -impl<'store> StoreEntry<'store> -{ - pub fn or_insert(self, default_obj: Object) -> &'store mut Object + pub fn insert_pending(&mut self, id: Id) { - self.inner.or_insert(default_obj) + self.objects.insert(id, None); } - pub fn or_insert_with( - self, - default_func: impl FnOnce() -> Object, - ) -> &'store mut Object + pub fn remove(&mut self, id: &Id) -> Option<Option<Object>> { - self.inner.or_insert_with(default_func) + self.objects.remove(id) } } diff --git a/engine/src/renderer/opengl.rs b/engine/src/renderer/opengl.rs index 860416e..2283b32 100644 --- a/engine/src/renderer/opengl.rs +++ b/engine/src/renderer/opengl.rs @@ -4,14 +4,15 @@ use std::borrow::Cow; use std::collections::HashMap; use ecs::actions::Actions; -use ecs::pair::Pair; use ecs::query::term::Without; use ecs::sole::Single; -use ecs::{Component, Query}; +use ecs::{Component, Query, Sole}; +use glutin::config::Config as GlutinConfig; use glutin::display::GetGlDisplay; +use glutin::error::Error as GlutinError; use glutin::prelude::{GlDisplay, PossiblyCurrentGlContext}; use glutin::surface::{ - GlSurface, + GlSurface as _, Surface as GlutinSurface, WindowSurface as GlutinWindowSurface, }; @@ -60,7 +61,12 @@ use opengl_bindings::vertex_array::{ PrimitiveKind, VertexArray, }; -use opengl_bindings::{ContextWithFns, CurrentContextWithFns}; +use opengl_bindings::{ + ContextWithFns, + CurrentContextWithFns, + MakeContextCurrentError as GlMakeContextCurrentError, +}; +use raw_window_handle::WindowHandle; use safer_ffi::layout::ReprC; use zerocopy::{Immutable, IntoBytes}; @@ -82,11 +88,9 @@ use crate::renderer::opengl::glutin_compat::{ }; use crate::renderer::opengl::graphics_mesh::GraphicsMesh; use crate::renderer::{ - ActiveDrawProperties, BufferClearMask, Command as RendererCommand, CommandQueue as RendererCommandQueue, - CtxUsedByWindow as RendererCtxUsedByWindow, DrawMeshOptions, DrawPropertiesUpdateFlags, GraphicsProperties, @@ -94,7 +98,6 @@ use crate::renderer::{ RENDER_PHASE, SurfaceId, SurfaceSpec, - WindowUsingRendererCtx, }; use crate::shader::cursor::BindingValue as ShaderBindingValue; use crate::shader::{ @@ -109,6 +112,7 @@ use crate::texture::{ Texture, Wrapping as TextureWrapping, }; +use crate::util::OptionExt; use crate::vector::{Vec2, Vec3}; use crate::windowing::Context as WindowingContext; use crate::windowing::window::{ @@ -124,13 +128,13 @@ mod graphics_mesh; #[derive(Debug, Component)] struct WindowGlConfig { - gl_config: glutin::config::Config, + gl_config: GlutinConfig, } -#[derive(Component)] +#[derive(Sole, Default)] struct GraphicsContext { - gl_context: ContextWithFns, + gl_context: Option<ContextWithFns>, surfaces: HashMap<SurfaceId, GlutinSurface<GlutinWindowSurface>>, shader_uniform_buffer_objs: HashMap<RendererObjectId, HashMap<u32, opengl_bindings::buffer::Buffer<u8>>>, @@ -160,12 +164,11 @@ impl ecs::extension::Extension for Extension collector.add_system(*POST_RENDER_PHASE, prepare_windows); collector.add_system(*POST_RENDER_PHASE, init_window_graphics); + + let _ = collector.add_sole(GraphicsContext::default()); } } -#[derive(Debug, Component)] -struct SetupFailed; - fn prepare_windows( window_query: Query< (Option<&Window>, &mut WindowCreationAttributes), @@ -173,7 +176,6 @@ fn prepare_windows( Without<CreationReady>, Without<WindowGlConfig>, Without<WindowClosed>, - Without<SetupFailed>, ), >, windowing_context: Single<WindowingContext>, @@ -209,7 +211,6 @@ fn prepare_windows( Ok(window_handle) => window_handle, Err(err) => { tracing::error!("Failed to get window handle: {err}"); - actions.add_components(window_ent_id, (SetupFailed,)); continue; } }; @@ -231,7 +232,6 @@ fn prepare_windows( } Err(err) => { tracing::error!("Failed to create platform graphics display: {err}"); - actions.add_components(window_ent_id, (SetupFailed,)); continue; } }; @@ -248,17 +248,19 @@ fn prepare_windows( #[tracing::instrument(skip_all)] fn init_window_graphics( - window_query: Query< - (&Window, &WindowGlConfig), - (Without<SurfaceSpec>, Without<SetupFailed>), - >, + window_query: Query<(&Window, &WindowGlConfig), (Without<SurfaceSpec>,)>, windowing_context: Single<WindowingContext>, graphics_props: Single<GraphicsProperties>, + mut graphics_ctx: Single<GraphicsContext>, mut actions: Actions, ) { for (window_ent_id, (window, window_gl_config)) in window_query.iter_with_euids() { - tracing::info!("Initializing graphics for window {window_ent_id}"); + tracing::info!( + window_entity_id=%window_ent_id, + window_title=&*window.title, + "Initializing graphics for window" + ); let display = window_gl_config.gl_config.display(); @@ -273,12 +275,10 @@ fn init_window_graphics( entity_id = %window_ent_id, "Windowing context does not contain window" ); - actions.add_components(window_ent_id, (SetupFailed,)); continue; } Err(err) => { tracing::error!("Failed to get window handle: {err}"); - actions.add_components(window_ent_id, (SetupFailed,)); continue; } }; @@ -310,31 +310,23 @@ fn init_window_graphics( } }; - let context = match unsafe { - display.create_context( + let gl_context = match graphics_ctx.gl_context.get_or_try_insert_with_fn(|| { + create_gl_context( &window_gl_config.gl_config, - &glutin::context::ContextAttributesBuilder::new() - .with_debug(graphics_props.debug) - .build(Some(window_handle.as_raw())), + &graphics_props, + window_handle, + &surface, ) - } { - Ok(context) => context, - Err(err) => { - tracing::error!("Failed to create graphics context: {err}"); - continue; - } - }; - - let gl_context = match ContextWithFns::new(context, &surface) { - Ok(context) => context, + }) { + Ok(gl_context) => gl_context, Err(err) => { - tracing::error!("Failed to create graphics context: {err}"); + tracing::error!("Failed to create GL context: {err:#?}"); continue; } }; let Ok(curr_gl_context) = gl_context.make_current(&surface) else { - tracing::error!("Failed to make graphics context current"); + tracing::error!("Failed to make GL context current"); continue; }; @@ -384,692 +376,696 @@ fn init_window_graphics( let surface_id = SurfaceId::new_unique(); - let renderer_ctx_ent_id = actions.spawn(( - GraphicsContext { - gl_context, - surfaces: HashMap::from([(surface_id, surface)]), - shader_uniform_buffer_objs: HashMap::new(), - objects: HashMap::new(), - next_object_key: RendererObjectRawValue::default(), - }, - RendererObjectStore::default(), - RendererCommandQueue::default(), - Pair::builder() - .relation::<RendererCtxUsedByWindow>() - .target_id(window_ent_id) - .build(), - ActiveDrawProperties::default(), - )); - - actions.add_components( - window_ent_id, - ( - SurfaceSpec { id: surface_id }, - Pair::builder() - .relation::<WindowUsingRendererCtx>() - .target_id(renderer_ctx_ent_id) - .build(), - ), - ); + actions.add_components(window_ent_id, (SurfaceSpec { id: surface_id },)); + + graphics_ctx.surfaces.insert(surface_id, surface); } } #[tracing::instrument(skip_all)] fn handle_commands( - renderer_ctx_query: Query<( - &mut GraphicsContext, - &mut RendererObjectStore, - &mut RendererCommandQueue, - )>, + mut graphics_ctx: Single<GraphicsContext>, + mut object_store: Single<RendererObjectStore>, + mut command_queue: Single<RendererCommandQueue>, assets: Single<Assets>, shader_context: Single<ShaderContext>, ) { - for (mut graphics_ctx, mut renderer_object_store, mut command_queue) in - &renderer_ctx_query - { - let GraphicsContext { - ref gl_context, - ref mut surfaces, - ref mut shader_uniform_buffer_objs, - objects: ref mut graphics_ctx_objects, - next_object_key: ref mut next_graphics_ctx_object_key, - } = *graphics_ctx; - - let mut opt_curr_gl_ctx: Option<CurrentContextWithFns> = None; - - let mut activated_gl_shader_program: Option<(RendererObjectId, GlShaderProgram)> = - None; - - for command in command_queue.drain() { - let tracing_span = tracing::info_span!("handle_cmd"); - let _tracing_span_enter = tracing_span.enter(); - - match command { - RendererCommand::RemoveSurface(surface_id) => { - let Some(surface) = surfaces.remove(&surface_id) else { - tracing::error!(surface_id=?surface_id, "Surface does not exist"); - continue; - }; + let GraphicsContext { + ref gl_context, + ref mut surfaces, + ref mut shader_uniform_buffer_objs, + objects: ref mut graphics_ctx_objects, + next_object_key: ref mut next_graphics_ctx_object_key, + } = *graphics_ctx; + + let Some(gl_context) = gl_context else { + return; + }; - if surface.is_current(gl_context.context()) { - opt_curr_gl_ctx = None; + let mut opt_curr_gl_ctx: Option<CurrentContextWithFns> = None; - if let Err(err) = gl_context.context().make_not_current_in_place() - { - tracing::error!( - "Failed to make GL context not current: {err}" - ); - } - } + let mut activated_gl_shader_program: Option<(RendererObjectId, GlShaderProgram)> = + None; - drop(surface); - } - RendererCommand::MakeCurrent(surface_id) => { - let Some(surface) = surfaces.get(&surface_id) else { - tracing::error!(surface_id=?surface_id, "Surface does not exist"); - continue; - }; + for command in command_queue.drain() { + let tracing_span = tracing::info_span!("handle_cmd"); + let _tracing_span_enter = tracing_span.enter(); - let curr_gl_ctx = match gl_context.make_current(surface) { - Ok(current_graphics_context) => current_graphics_context, - Err(err) => { - tracing::error!( - "Failed to make graphics context current: {err}" - ); - continue; - } - }; + match command { + RendererCommand::RemoveSurface(surface_id) => { + let Some(surface) = surfaces.remove(&surface_id) else { + tracing::error!(surface_id=?surface_id, "Surface does not exist"); + continue; + }; + + if surface.is_current(gl_context.context()) { + opt_curr_gl_ctx = None; - opt_curr_gl_ctx = Some(curr_gl_ctx); + if let Err(err) = gl_context.context().make_not_current_in_place() { + tracing::error!("Failed to make GL context not current: {err}"); + } } - RendererCommand::ClearBuffers(buffer_clear_mask) => { - let Some(curr_gl_ctx) = &opt_curr_gl_ctx else { - tracing::error!("No GL context is current"); - continue; - }; - let mut clear_mask = GlBufferClearMask::empty(); + drop(surface); + } + RendererCommand::MakeCurrent(surface_id) => { + let Some(surface) = surfaces.get(&surface_id) else { + tracing::error!(surface_id=?surface_id, "Surface does not exist"); + continue; + }; - clear_mask.set( - GlBufferClearMask::COLOR, - buffer_clear_mask.contains(BufferClearMask::COLOR), - ); + let curr_gl_ctx = match gl_context.make_current(surface) { + Ok(current_graphics_context) => current_graphics_context, + Err(err) => { + tracing::error!("Failed to make graphics context current: {err}"); + continue; + } + }; - clear_mask.set( - GlBufferClearMask::DEPTH, - buffer_clear_mask.contains(BufferClearMask::DEPTH), - ); + opt_curr_gl_ctx = Some(curr_gl_ctx); + } + RendererCommand::ClearBuffers(buffer_clear_mask) => { + let Some(curr_gl_ctx) = &opt_curr_gl_ctx else { + tracing::error!("No GL context is current"); + continue; + }; - clear_mask.set( - GlBufferClearMask::STENCIL, - buffer_clear_mask.contains(BufferClearMask::STENCIL), - ); + let mut clear_mask = GlBufferClearMask::empty(); - clear_buffers(&curr_gl_ctx, clear_mask); - } - RendererCommand::SwapBuffers(surface_id) => { - let Some(surface) = surfaces.get(&surface_id) else { - tracing::error!(surface_id=?surface_id, "Surface does not exist"); - continue; - }; + clear_mask.set( + GlBufferClearMask::COLOR, + buffer_clear_mask.contains(BufferClearMask::COLOR), + ); - if let Err(err) = surface.swap_buffers(gl_context.context()) { - tracing::error!("Failed to swap buffers: {err}"); - } - } - RendererCommand::CreateShaderProgram( - shader_program_obj_id, - shader_program, - ) => { - let Some(curr_gl_ctx) = &opt_curr_gl_ctx else { - tracing::error!("No GL context is current"); - continue; - }; + clear_mask.set( + GlBufferClearMask::DEPTH, + buffer_clear_mask.contains(BufferClearMask::DEPTH), + ); - if renderer_object_store.contains_with_id(&shader_program_obj_id) { - tracing::error!( - object_id=?shader_program_obj_id, - "Object store already contains a object with this ID" - ); - continue; - } + clear_mask.set( + GlBufferClearMask::STENCIL, + buffer_clear_mask.contains(BufferClearMask::STENCIL), + ); - let gl_shader_program = - match create_shader_program(&curr_gl_ctx, &shader_program) { - Ok(gl_shader_program) => gl_shader_program, - Err(err) => { - tracing::error!("Failed to create shader program: {err}"); - continue; - } - }; + clear_buffers(&curr_gl_ctx, clear_mask); + } + RendererCommand::SwapBuffers(surface_id) => { + let Some(surface) = surfaces.get(&surface_id) else { + tracing::error!(surface_id=?surface_id, "Surface does not exist"); + continue; + }; - renderer_object_store.insert( - shader_program_obj_id, - RendererObject::from_raw( - gl_shader_program.into_raw(), - RendererObjectKind::ShaderProgram, - ), + if let Err(err) = surface.swap_buffers(gl_context.context()) { + tracing::error!("Failed to swap buffers: {err}"); + } + } + RendererCommand::CreateShaderProgram( + shader_program_obj_id, + shader_program, + ) => { + let Some(curr_gl_ctx) = &opt_curr_gl_ctx else { + tracing::error!("No GL context is current"); + continue; + }; + + if object_store.contains_non_pending_with_id(&shader_program_obj_id) { + tracing::error!( + shader_program_object_id=?shader_program_obj_id, + shader_asset_label=?shader_program_obj_id + .into_asset_id() + .and_then(|asset_id| assets.get_label_by_id(asset_id)), + "Object store already contains a object with this ID" ); + continue; } - RendererCommand::ActivateShader(shader_program_obj_id) => { - let Some(curr_gl_ctx) = &opt_curr_gl_ctx else { - tracing::error!("No GL context is current"); - continue; - }; - let Some(shader_program_obj) = renderer_object_store - .get_shader_program_obj(&shader_program_obj_id) - else { - tracing::error!( - "Shader object does not exist or has a wrong kind" - ); - continue; + let gl_shader_program = + match create_shader_program(&curr_gl_ctx, &shader_program) { + Ok(gl_shader_program) => gl_shader_program, + Err(err) => { + tracing::error!("Failed to create shader program: {err}"); + continue; + } }; - let gl_shader_program = - GlShaderProgram::from_raw(shader_program_obj.as_raw()); + object_store.insert( + shader_program_obj_id, + RendererObject::from_raw( + gl_shader_program.into_raw(), + RendererObjectKind::ShaderProgram, + ), + ); + } + RendererCommand::ActivateShader(shader_program_obj_id) => { + let Some(curr_gl_ctx) = &opt_curr_gl_ctx else { + tracing::error!("No GL context is current"); + continue; + }; - gl_shader_program.activate(&curr_gl_ctx); + let Some(shader_program_obj) = + object_store.get_shader_program_obj(&shader_program_obj_id) + else { + tracing::error!("Shader object does not exist or has a wrong kind"); + continue; + }; - activated_gl_shader_program = - Some((shader_program_obj_id, gl_shader_program)); - } - RendererCommand::SetShaderBinding(binding_location, binding_value) => { - let Some(curr_gl_ctx) = &opt_curr_gl_ctx else { - tracing::error!("No GL context is current"); - continue; - }; + let gl_shader_program = + GlShaderProgram::from_raw(shader_program_obj.as_raw()); - let Some((activated_gl_shader_program_obj_id, _)) = - &activated_gl_shader_program - else { - tracing::error!("No shader program is activated"); - continue; - }; + gl_shader_program.activate(&curr_gl_ctx); - if let ShaderBindingValue::Texture(texture_asset) = &binding_value { - let Some(texture_obj) = renderer_object_store.get_texture_obj( - &RendererObjectId::Asset(texture_asset.id()), - ) else { - tracing::error!( - "Texture {:?} does not exist in renderer object store", - assets.get_label(texture_asset) - ); - continue; - }; + activated_gl_shader_program = + Some((shader_program_obj_id, gl_shader_program)); + } + RendererCommand::SetShaderBinding(binding_location, binding_value) => { + let Some(curr_gl_ctx) = &opt_curr_gl_ctx else { + tracing::error!("No GL context is current"); + continue; + }; - let gl_texture = GlTexture::from_raw(texture_obj.as_raw()); + let Some((activated_gl_shader_program_obj_id, _)) = + &activated_gl_shader_program + else { + tracing::error!("No shader program is activated"); + continue; + }; - gl_texture.bind_to_texture_unit( - curr_gl_ctx, - binding_location.binding_index, + if let ShaderBindingValue::Texture(texture_asset) = &binding_value { + let Some(texture_obj) = object_store + .get_texture_obj(&RendererObjectId::Asset(texture_asset.id())) + else { + tracing::error!( + "Texture {:?} does not exist in renderer object store", + assets.get_label(texture_asset) ); - - // gl_shader_program.set_uniform_at_location( - // curr_gl_ctx, - // GlUniformLocation::from_number( - // binding_location.binding_index as i32, - // ), - // &binding_location.binding_index, - // ); - continue; - } + }; + + let gl_texture = GlTexture::from_raw(texture_obj.as_raw()); - let binding_index = binding_location.binding_index; - - let uniform_buffer_objs = shader_uniform_buffer_objs - .entry(*activated_gl_shader_program_obj_id) - .or_default(); - - let uniform_buffer = - uniform_buffer_objs.entry(binding_index).or_insert_with(|| { - let uniform_buf = - opengl_bindings::buffer::Buffer::<u8>::new(curr_gl_ctx); - - uniform_buf - .init( - curr_gl_ctx, - binding_location.binding_size, - opengl_bindings::buffer::Usage::Dynamic, - ) - .unwrap(); - - uniform_buf - }); - - // The index into the uniform buffer binding target is for whatever - // shader program is currently bound so the uniform buffer object has - // to be re-bound so that a uniform buffer from another shader isn't - // used - uniform_buffer.bind_to_indexed_target( + gl_texture.bind_to_texture_unit( curr_gl_ctx, - opengl_bindings::buffer::BindingTarget::UniformBuffer, - binding_index as u32, + binding_location.binding_index, ); - let fvec3_value; + // gl_shader_program.set_uniform_at_location( + // curr_gl_ctx, + // GlUniformLocation::from_number( + // binding_location.binding_index as i32, + // ), + // &binding_location.binding_index, + // ); - uniform_buffer - .store_at_byte_offset( - curr_gl_ctx, - binding_location.byte_offset, - match binding_value { - ShaderBindingValue::Uint(ref value) => value.as_bytes(), - ShaderBindingValue::Int(ref value) => value.as_bytes(), - ShaderBindingValue::Float(ref value) => value.as_bytes(), - ShaderBindingValue::FVec3(value) => { - fvec3_value = CF32Vec3::from(value); - - fvec3_value.as_bytes() - } - ShaderBindingValue::FMat4x4(ref value) => { - value.items().as_bytes() - } - ShaderBindingValue::Texture(_) => unreachable!(), - }, - ) - .unwrap(); + continue; } - RendererCommand::CreateTexture(texture_asset) => { - let Some(curr_gl_ctx) = &opt_curr_gl_ctx else { - tracing::error!("No GL context is current"); - continue; - }; - if let Err(err) = create_texture_object( + let binding_index = binding_location.binding_index; + + let uniform_buffer_objs = shader_uniform_buffer_objs + .entry(*activated_gl_shader_program_obj_id) + .or_default(); + + let uniform_buffer = + uniform_buffer_objs.entry(binding_index).or_insert_with(|| { + let uniform_buf = + opengl_bindings::buffer::Buffer::<u8>::new(curr_gl_ctx); + + uniform_buf + .init( + curr_gl_ctx, + binding_location.binding_size, + opengl_bindings::buffer::Usage::Dynamic, + ) + .unwrap(); + + uniform_buf + }); + + // The index into the uniform buffer binding target is for whatever + // shader program is currently bound so the uniform buffer object has + // to be re-bound so that a uniform buffer from another shader isn't + // used + uniform_buffer.bind_to_indexed_target( + curr_gl_ctx, + opengl_bindings::buffer::BindingTarget::UniformBuffer, + binding_index as u32, + ); + + let fvec3_value; + + uniform_buffer + .store_at_byte_offset( curr_gl_ctx, - &mut renderer_object_store, - &assets, - &texture_asset, - ) { - tracing::error!("Failed to create texture object: {err}"); - } + binding_location.byte_offset, + match binding_value { + ShaderBindingValue::Uint(ref value) => value.as_bytes(), + ShaderBindingValue::Int(ref value) => value.as_bytes(), + ShaderBindingValue::Float(ref value) => value.as_bytes(), + ShaderBindingValue::FVec3(value) => { + fvec3_value = CF32Vec3::from(value); + + fvec3_value.as_bytes() + } + ShaderBindingValue::FMat4x4(ref value) => { + value.items().as_bytes() + } + ShaderBindingValue::Texture(_) => unreachable!(), + }, + ) + .unwrap(); + } + RendererCommand::CreateTexture(texture_asset) => { + let Some(curr_gl_ctx) = &opt_curr_gl_ctx else { + tracing::error!("No GL context is current"); + continue; + }; + + if let Err(err) = create_texture_object( + curr_gl_ctx, + &mut object_store, + &assets, + &texture_asset, + ) { + tracing::error!("Failed to create texture object: {err}"); } - RendererCommand::CreateMesh { - obj_id: mesh_object_id, - mesh, - usage: mesh_usage, - } => { - let Some(curr_gl_ctx) = &opt_curr_gl_ctx else { - tracing::error!("No GL context is current"); - continue; - }; + } + RendererCommand::CreateMesh { + obj_id: mesh_object_id, + mesh, + usage: mesh_usage, + } => { + let Some(curr_gl_ctx) = &opt_curr_gl_ctx else { + tracing::error!("No GL context is current"); + continue; + }; - if renderer_object_store.contains_with_id(&mesh_object_id) { - tracing::error!( - object_id=?mesh_object_id, - "Object store already contains a object with this ID" - ); - continue; - } + if object_store.contains_non_pending_with_id(&mesh_object_id) { + tracing::error!( + mesh_object_id=?mesh_object_id, + mesh_asset_label=?mesh_object_id + .into_asset_id() + .and_then(|asset_id| assets.get_label_by_id(asset_id)), + "Object store already contains a object with this ID" + ); + continue; + } - let Some((RendererObjectId::Asset(curr_shader_program_asset_id), _)) = - &activated_gl_shader_program - else { - tracing::error!("No shader program is activated"); - continue; - }; + let Some((RendererObjectId::Asset(curr_shader_program_asset_id), _)) = + &activated_gl_shader_program + else { + tracing::error!("No shader program is activated"); + continue; + }; - let curr_shader_program_metadata = shader_context - .get_program_metadata(curr_shader_program_asset_id) - .expect("Not possible"); + let curr_shader_program_metadata = shader_context + .get_program_metadata(curr_shader_program_asset_id) + .expect("Not possible"); - let Some(vertex_desc) = &curr_shader_program_metadata.vertex_desc - else { - tracing::error!( - "Current shader program does not have a vertex description" - ); - continue; - }; + let Some(vertex_desc) = &curr_shader_program_metadata.vertex_desc else { + tracing::error!( + "Current shader program does not have a vertex description" + ); + continue; + }; - let key = *next_graphics_ctx_object_key; - - let mesh = match mesh_object_id { - RendererObjectId::Asset(mesh_asset_id) => match mesh.as_ref() { - Some(mesh) => mesh, - None => { - let Some(mesh) = - assets.get(&AssetHandle::from_id(mesh_asset_id)) - else { - tracing::error!( - asset_id=?mesh_asset_id, - "Mesh asset does not exist" - ); - continue; - }; - - mesh - } - }, - RendererObjectId::Sequential(_) => { - let Some(mesh) = mesh.as_ref() else { + let key = *next_graphics_ctx_object_key; + + let mesh = match mesh_object_id { + RendererObjectId::Asset(mesh_asset_id) => match mesh.as_ref() { + Some(mesh) => mesh, + None => { + let Some(mesh) = + assets.get(&AssetHandle::from_id(mesh_asset_id)) + else { tracing::error!( - object_id=?mesh_object_id, - "Object ID is sequential but no mesh data is given" + asset_id=?mesh_asset_id, + "Mesh asset does not exist" ); continue; }; mesh } - }; - - let graphics_mesh = match GraphicsMesh::new( - &curr_gl_ctx, - &mesh, - mesh_usage, - &vertex_desc, - ) { - Ok(graphics_mesh) => graphics_mesh, - Err(err) => { - tracing::error!("Failed to create mesh: {err}"); + }, + RendererObjectId::Sequential(_) => { + let Some(mesh) = mesh.as_ref() else { + tracing::error!( + object_id=?mesh_object_id, + "Object ID is sequential but no mesh data is given" + ); continue; - } - }; + }; - graphics_ctx_objects.insert( - key, - GraphicsContextObject::Mesh { - mesh: graphics_mesh, - compatible_shader_program_obj_id: RendererObjectId::Asset( - *curr_shader_program_asset_id, - ), - }, - ); + mesh + } + }; + + let graphics_mesh = match GraphicsMesh::new( + &curr_gl_ctx, + &mesh, + mesh_usage, + &vertex_desc, + ) { + Ok(graphics_mesh) => graphics_mesh, + Err(err) => { + tracing::error!("Failed to create mesh: {err}"); + continue; + } + }; - renderer_object_store.insert( - mesh_object_id, - RendererObject::from_raw( - key, - RendererObjectKind::ImplementationSpecific, + graphics_ctx_objects.insert( + key, + GraphicsContextObject::Mesh { + mesh: graphics_mesh, + compatible_shader_program_obj_id: RendererObjectId::Asset( + *curr_shader_program_asset_id, ), - ); + }, + ); - *next_graphics_ctx_object_key += 1; - } - RendererCommand::UpdateMesh { - obj_id: mesh_object_id, - mesh, - usage: mesh_usage, - } => { - let Some(curr_gl_ctx) = &opt_curr_gl_ctx else { - tracing::error!("No GL context is current"); - continue; - }; + object_store.insert( + mesh_object_id, + RendererObject::from_raw( + key, + RendererObjectKind::ImplementationSpecific, + ), + ); - let Some(mesh_graphics_ctx_obj_key) = renderer_object_store - .get_obj(&mesh_object_id) - .map(|obj| obj.as_raw()) - else { - tracing::error!( - object_id=?mesh_object_id, - "Object store does not contain a mesh object with this ID" - ); - continue; - }; + *next_graphics_ctx_object_key += 1; + } + RendererCommand::UpdateMesh { + obj_id: mesh_object_id, + mesh, + usage: mesh_usage, + } => { + let Some(curr_gl_ctx) = &opt_curr_gl_ctx else { + tracing::error!("No GL context is current"); + continue; + }; - let Some(mesh_graphics_ctx_obj) = - graphics_ctx_objects.get_mut(&mesh_graphics_ctx_obj_key) - else { - tracing::error!( - object_id=?mesh_object_id, - key=mesh_graphics_ctx_obj_key, - "Graphics context does not contain a mesh object with this key" - ); - continue; - }; + let Some(mesh_graphics_ctx_obj_key) = object_store + .get_obj(&mesh_object_id) + .map(|obj| obj.as_raw()) + else { + tracing::error!( + object_id=?mesh_object_id, + "Object store does not contain a mesh object with this ID" + ); + continue; + }; - #[allow(irrefutable_let_patterns)] - let GraphicsContextObject::Mesh { - mesh: graphics_mesh, - compatible_shader_program_obj_id: _, - } = mesh_graphics_ctx_obj - else { - tracing::error!( - object_id=?mesh_object_id, - key=mesh_graphics_ctx_obj_key, - "Graphics context object with this key is not a mesh" - ); - continue; - }; + let Some(mesh_graphics_ctx_obj) = + graphics_ctx_objects.get_mut(&mesh_graphics_ctx_obj_key) + else { + tracing::error!( + object_id=?mesh_object_id, + key=mesh_graphics_ctx_obj_key, + "Graphics context does not contain a mesh object with this key" + ); + continue; + }; + + #[allow(irrefutable_let_patterns)] + let GraphicsContextObject::Mesh { + mesh: graphics_mesh, + compatible_shader_program_obj_id: _, + } = mesh_graphics_ctx_obj + else { + tracing::error!( + object_id=?mesh_object_id, + key=mesh_graphics_ctx_obj_key, + "Graphics context object with this key is not a mesh" + ); + continue; + }; - if let Err(err) = graphics_mesh.update(curr_gl_ctx, &mesh, mesh_usage) - { - tracing::error!("Failed to update mesh: {err}"); - } + if let Err(err) = graphics_mesh.update(curr_gl_ctx, &mesh, mesh_usage) { + tracing::error!("Failed to update mesh: {err}"); } - RendererCommand::RemoveMesh(mesh_object_id) => { - let Some(curr_gl_ctx) = &opt_curr_gl_ctx else { - tracing::error!("No GL context is current"); - continue; - }; - - let Some(mesh_graphics_ctx_obj_key) = renderer_object_store - .remove(&mesh_object_id) - .map(|obj| obj.as_raw()) - else { - tracing::error!( - object_id=?mesh_object_id, - "Object store does not contain a mesh object with this ID" - ); - continue; - }; - - let Some(mesh_graphics_ctx_obj) = - graphics_ctx_objects.remove(&mesh_graphics_ctx_obj_key) - else { - tracing::error!( - object_id=?mesh_object_id, - key=mesh_graphics_ctx_obj_key, - "Graphics context does not contain a mesh object with this key" - ); - continue; - }; + } + RendererCommand::RemoveMesh(mesh_object_id) => { + let Some(curr_gl_ctx) = &opt_curr_gl_ctx else { + tracing::error!("No GL context is current"); + continue; + }; - #[allow(irrefutable_let_patterns)] - let GraphicsContextObject::Mesh { mesh: mut graphics_mesh, .. } = - mesh_graphics_ctx_obj - else { - tracing::error!( - object_id=?mesh_object_id, - key=mesh_graphics_ctx_obj_key, - "Graphics context object with this key is not a mesh" - ); - continue; - }; + let Some(mesh_graphics_ctx_obj_key) = object_store + .remove(&mesh_object_id) + .flatten() + .map(|obj| obj.as_raw()) + else { + tracing::error!( + object_id=?mesh_object_id, + "Object store does not contain a mesh object with this ID" + ); + continue; + }; - graphics_mesh.destroy(curr_gl_ctx); - } - RendererCommand::DrawMesh(mesh_object_id, draw_mesh_opts) => { - let Some(curr_gl_ctx) = &opt_curr_gl_ctx else { - tracing::error!("No GL context is current"); - continue; - }; + let Some(mesh_graphics_ctx_obj) = + graphics_ctx_objects.remove(&mesh_graphics_ctx_obj_key) + else { + tracing::error!( + object_id=?mesh_object_id, + key=mesh_graphics_ctx_obj_key, + "Graphics context does not contain a mesh object with this key" + ); + continue; + }; - let Some(mesh_graphics_ctx_obj_key) = renderer_object_store - .get_obj(&mesh_object_id) - .map(|obj| obj.as_raw()) - else { - tracing::error!( - object_id=?mesh_object_id, - "Object store does not contain a mesh object with this ID" - ); - continue; - }; + #[allow(irrefutable_let_patterns)] + let GraphicsContextObject::Mesh { mesh: mut graphics_mesh, .. } = + mesh_graphics_ctx_obj + else { + tracing::error!( + object_id=?mesh_object_id, + key=mesh_graphics_ctx_obj_key, + "Graphics context object with this key is not a mesh" + ); + continue; + }; - let Some(mesh_graphics_ctx_obj) = - graphics_ctx_objects.get(&mesh_graphics_ctx_obj_key) - else { - tracing::error!( - object_id=?mesh_object_id, - key=mesh_graphics_ctx_obj_key, - "Graphics context does not contain a mesh object with this key" - ); - continue; - }; + graphics_mesh.destroy(curr_gl_ctx); + } + RendererCommand::DrawMesh(mesh_object_id, draw_mesh_opts) => { + let Some(curr_gl_ctx) = &opt_curr_gl_ctx else { + tracing::error!("No GL context is current"); + continue; + }; - #[allow(irrefutable_let_patterns)] - let GraphicsContextObject::Mesh { - mesh: graphics_mesh, - compatible_shader_program_obj_id, - } = mesh_graphics_ctx_obj - else { - tracing::error!( - object_id=?mesh_object_id, - key=mesh_graphics_ctx_obj_key, - "Graphics context object with this key is not a mesh" - ); - continue; - }; + let Some(mesh_graphics_ctx_obj_key) = object_store + .get_obj(&mesh_object_id) + .map(|obj| obj.as_raw()) + else { + tracing::error!( + object_id=?mesh_object_id, + "Object store does not contain a mesh object with this ID" + ); + continue; + }; - if Some(compatible_shader_program_obj_id) - != activated_gl_shader_program.as_ref().map( - |(activated_gl_shader_program_obj_id, _)| { - activated_gl_shader_program_obj_id - }, - ) - { - tracing::error!(concat!( - "Activated shader program is not the ", - "compatible shader program of the mesh" - )); - continue; - } + let Some(mesh_graphics_ctx_obj) = + graphics_ctx_objects.get(&mesh_graphics_ctx_obj_key) + else { + tracing::error!( + object_id=?mesh_object_id, + key=mesh_graphics_ctx_obj_key, + "Graphics context does not contain a mesh object with this key" + ); + continue; + }; + + #[allow(irrefutable_let_patterns)] + let GraphicsContextObject::Mesh { + mesh: graphics_mesh, + compatible_shader_program_obj_id, + } = mesh_graphics_ctx_obj + else { + tracing::error!( + object_id=?mesh_object_id, + key=mesh_graphics_ctx_obj_key, + "Graphics context object with this key is not a mesh" + ); + continue; + }; - if let Err(err) = - draw_mesh(&curr_gl_ctx, graphics_mesh, &draw_mesh_opts) - { - tracing::error!("Failed to draw mesh: {err}"); - }; + if Some(compatible_shader_program_obj_id) + != activated_gl_shader_program.as_ref().map( + |(activated_gl_shader_program_obj_id, _)| { + activated_gl_shader_program_obj_id + }, + ) + { + tracing::error!(concat!( + "Activated shader program is not the ", + "compatible shader program of the mesh" + )); + continue; } - RendererCommand::UpdateDrawProperties( - draw_props, - draw_props_update_flags, - ) => { - let Some(curr_gl_ctx) = &opt_curr_gl_ctx else { - tracing::error!("No GL context is current"); - continue; - }; - if draw_props_update_flags - .contains(DrawPropertiesUpdateFlags::POLYGON_MODE_CONFIG) - { - opengl_bindings::misc::set_polygon_mode( - &curr_gl_ctx, - draw_props.polygon_mode_config.face, - draw_props.polygon_mode_config.mode, - ); - } + if let Err(err) = draw_mesh(&curr_gl_ctx, graphics_mesh, &draw_mesh_opts) + { + tracing::error!("Failed to draw mesh: {err}"); + }; + } + RendererCommand::UpdateDrawProperties( + draw_props, + draw_props_update_flags, + ) => { + let Some(curr_gl_ctx) = &opt_curr_gl_ctx else { + tracing::error!("No GL context is current"); + continue; + }; - if draw_props_update_flags - .contains(DrawPropertiesUpdateFlags::BLENDING_ENABLED) - { - set_enabled( - curr_gl_ctx, - Capability::Blend, - draw_props.blending_enabled, - ); - } + if draw_props_update_flags + .contains(DrawPropertiesUpdateFlags::POLYGON_MODE_CONFIG) + { + opengl_bindings::misc::set_polygon_mode( + &curr_gl_ctx, + draw_props.polygon_mode_config.face, + draw_props.polygon_mode_config.mode, + ); + } - if draw_props_update_flags - .contains(DrawPropertiesUpdateFlags::BLENDING_CONFIG) - { - gl_blending_configure( - curr_gl_ctx, - GlBlendingConfig::default() - .with_source_factor(blending_factor_to_gl( - draw_props.blending_config.source_factor, - )) - .with_destination_factor(blending_factor_to_gl( - draw_props.blending_config.destination_factor, - )) - .with_equation(blending_equation_to_gl( - draw_props.blending_config.equation, - )), - ); - } + if draw_props_update_flags + .contains(DrawPropertiesUpdateFlags::BLENDING_ENABLED) + { + set_enabled( + curr_gl_ctx, + Capability::Blend, + draw_props.blending_enabled, + ); + } - if draw_props_update_flags - .contains(DrawPropertiesUpdateFlags::DEPTH_TEST_ENABLED) - { - set_enabled( - curr_gl_ctx, - Capability::DepthTest, - draw_props.depth_test_enabled, - ); - } + if draw_props_update_flags + .contains(DrawPropertiesUpdateFlags::BLENDING_CONFIG) + { + gl_blending_configure( + curr_gl_ctx, + GlBlendingConfig::default() + .with_source_factor(blending_factor_to_gl( + draw_props.blending_config.source_factor, + )) + .with_destination_factor(blending_factor_to_gl( + draw_props.blending_config.destination_factor, + )) + .with_equation(blending_equation_to_gl( + draw_props.blending_config.equation, + )), + ); + } - if draw_props_update_flags - .contains(DrawPropertiesUpdateFlags::SCISSOR_TEST_ENABLED) - { - set_enabled( - curr_gl_ctx, - Capability::ScissorTest, - draw_props.scissor_test_enabled, - ); - } + if draw_props_update_flags + .contains(DrawPropertiesUpdateFlags::DEPTH_TEST_ENABLED) + { + set_enabled( + curr_gl_ctx, + Capability::DepthTest, + draw_props.depth_test_enabled, + ); + } - if draw_props_update_flags - .contains(DrawPropertiesUpdateFlags::SCISSOR_BOX) - { - gl_define_scissor_box( - curr_gl_ctx, - draw_props.scissor_box.lower_left_corner_pos.into(), - draw_props - .scissor_box - .size - .unwrap_or_else(|| { - let (_, viewport_size) = gl_get_viewport(curr_gl_ctx); - - Dimens::<u16> { - width: viewport_size - .width - .try_into() - .expect("Viewport width too large"), - height: viewport_size - .height - .try_into() - .expect("Viewport height too large"), - } - }) - .into(), - ); - } + if draw_props_update_flags + .contains(DrawPropertiesUpdateFlags::SCISSOR_TEST_ENABLED) + { + set_enabled( + curr_gl_ctx, + Capability::ScissorTest, + draw_props.scissor_test_enabled, + ); + } - if draw_props_update_flags - .contains(DrawPropertiesUpdateFlags::FACE_CULLING_ENABLED) - { - set_enabled( - curr_gl_ctx, - Capability::CullFace, - draw_props.face_culling_enabled, - ); - } + if draw_props_update_flags + .contains(DrawPropertiesUpdateFlags::SCISSOR_BOX) + { + gl_define_scissor_box( + curr_gl_ctx, + draw_props.scissor_box.lower_left_corner_pos.into(), + draw_props + .scissor_box + .size + .unwrap_or_else(|| { + let (_, viewport_size) = gl_get_viewport(curr_gl_ctx); + + Dimens::<u16> { + width: viewport_size + .width + .try_into() + .expect("Viewport width too large"), + height: viewport_size + .height + .try_into() + .expect("Viewport height too large"), + } + }) + .into(), + ); } - RendererCommand::SetViewport { - size: viewport_size, - position: viewport_position, - } => { - let Some(curr_gl_ctx) = &opt_curr_gl_ctx else { - tracing::error!("No GL context is current"); - continue; - }; - if let Err(err) = gl_set_viewport( + if draw_props_update_flags + .contains(DrawPropertiesUpdateFlags::FACE_CULLING_ENABLED) + { + set_enabled( curr_gl_ctx, - &viewport_position.into(), - &viewport_size.into(), - ) { - tracing::error!("Failed to set viewport: {err}"); - } + Capability::CullFace, + draw_props.face_culling_enabled, + ); + } + } + RendererCommand::SetViewport { + size: viewport_size, + position: viewport_position, + } => { + let Some(curr_gl_ctx) = &opt_curr_gl_ctx else { + tracing::error!("No GL context is current"); + continue; + }; + + if let Err(err) = gl_set_viewport( + curr_gl_ctx, + &viewport_position.into(), + &viewport_size.into(), + ) { + tracing::error!("Failed to set viewport: {err}"); } } } } } +fn create_gl_context( + gl_config: &GlutinConfig, + graphics_props: &GraphicsProperties, + window_handle: WindowHandle<'_>, + surface: &GlutinSurface<GlutinWindowSurface>, +) -> Result<ContextWithFns, CreateGlContextError> +{ + let display = gl_config.display(); + + let glutin_context = unsafe { + display.create_context( + gl_config, + &glutin::context::ContextAttributesBuilder::new() + .with_debug(graphics_props.debug) + .build(Some(window_handle.as_raw())), + ) + } + .map_err(CreateGlContextError::CreateGlutinContext)?; + + ContextWithFns::new(glutin_context, &surface) + .map_err(CreateGlContextError::MakeContextCurrent) +} + +#[derive(Debug, thiserror::Error)] +enum CreateGlContextError +{ + #[error("Glutin context creation failed")] + CreateGlutinContext(#[source] GlutinError), + + #[error("Making GL context current failed")] + MakeContextCurrent(#[source] GlMakeContextCurrentError), +} + #[tracing::instrument(skip_all)] fn create_texture_object( curr_gl_ctx: &CurrentContextWithFns<'_>, @@ -1080,9 +1076,10 @@ fn create_texture_object( { let object_id = RendererObjectId::Asset(texture_asset.id()); - if renderer_object_store.contains_with_id(&object_id) { + if renderer_object_store.contains_non_pending_with_id(&object_id) { tracing::error!( texture_object_id=?object_id, + texture_asset_label=?assets.get_label(texture_asset), "Renderer object store already contains object with this ID" ); return Ok(()); diff --git a/engine/src/util.rs b/engine/src/util.rs index 0c81c71..f18a9c7 100644 --- a/engine/src/util.rs +++ b/engine/src/util.rs @@ -68,6 +68,32 @@ impl<Key: Ord, Value> Default for MapVec<Key, Value> } } +pub trait OptionExt<T> +{ + /// Substitute for the currently experimental function + /// [`Option::get_or_try_insert_with`]. + /// See https://github.com/rust-lang/rust/issues/143648 + fn get_or_try_insert_with_fn<Err>( + &mut self, + func: impl Fn() -> Result<T, Err>, + ) -> Result<&mut T, Err>; +} + +impl<T> OptionExt<T> for Option<T> +{ + fn get_or_try_insert_with_fn<Err>( + &mut self, + func: impl FnOnce() -> Result<T, Err>, + ) -> Result<&mut T, Err> + { + if let None = self { + *self = Some(func()?); + } + + Ok(unsafe { self.as_mut().unwrap_unchecked() }) + } +} + macro_rules! try_option { ($expr: expr) => { match $expr { |
