diff options
Diffstat (limited to 'engine/src')
| -rw-r--r-- | engine/src/image.rs | 97 | ||||
| -rw-r--r-- | engine/src/renderer.rs | 317 | ||||
| -rw-r--r-- | engine/src/renderer/main_render_pass.rs | 280 | ||||
| -rw-r--r-- | engine/src/renderer/object.rs | 5 | ||||
| -rw-r--r-- | engine/src/renderer/opengl.rs | 297 |
5 files changed, 528 insertions, 468 deletions
diff --git a/engine/src/image.rs b/engine/src/image.rs index ac95f22..b6d8013 100644 --- a/engine/src/image.rs +++ b/engine/src/image.rs @@ -1,8 +1,10 @@ +use std::any::type_name; use std::fs::File; use std::io::BufReader; use std::path::Path; use image_rs::GenericImageView as _; +use zerocopy::{FromBytes, Immutable}; use crate::color::Color; use crate::data_types::dimens::Dimens; @@ -33,6 +35,32 @@ impl Image }) } + pub fn try_from_bytes( + bytes: &[u8], + dimens: Dimens<u32>, + color_type: ColorType, + ) -> Result<Self, FromBytesError> + { + use image_rs::{Luma, LumaA, Rgb, Rgba}; + + let inner = match color_type { + ColorType::L8 => image_buf_from_bytes::<Luma<u8>>(dimens, bytes)?.into(), + ColorType::La8 => image_buf_from_bytes::<LumaA<u8>>(dimens, bytes)?.into(), + ColorType::Rgb8 => image_buf_from_bytes::<Rgb<u8>>(dimens, bytes)?.into(), + ColorType::Rgba8 => image_buf_from_bytes::<Rgba<u8>>(dimens, bytes)?.into(), + ColorType::L16 => image_buf_from_bytes::<Luma<u16>>(dimens, bytes)?.into(), + ColorType::La16 => image_buf_from_bytes::<LumaA<u16>>(dimens, bytes)?.into(), + ColorType::Rgb16 => image_buf_from_bytes::<Rgb<u16>>(dimens, bytes)?.into(), + ColorType::Rgba16 => image_buf_from_bytes::<Rgba<u16>>(dimens, bytes)?.into(), + ColorType::Rgb32F => image_buf_from_bytes::<Rgb<f32>>(dimens, bytes)?.into(), + ColorType::Rgba32F => { + image_buf_from_bytes::<Rgba<f32>>(dimens, bytes)?.into() + } + }; + + Ok(Self { inner }) + } + pub fn from_color(dimens: impl Into<Dimens<u32>>, color: impl Into<Color<u8>>) -> Self { @@ -173,3 +201,72 @@ pub enum Error #[derive(Debug, thiserror::Error)] #[error(transparent)] pub struct DecodeError(image_rs::ImageError); + +#[derive(Debug, thiserror::Error)] +pub enum FromBytesError +{ + #[error( + "The number of bytes provided is not enough to fit a image with the given size" + )] + NotEnoughBytesForDimensions, + + #[error("Failed to cast bytes to subpixels")] + CastToSubPixelsFailed(#[from] CastToSubPixelsError), +} + +#[derive(Debug, thiserror::Error)] +pub enum CastToSubPixelsError +{ + #[error( + "Source address {:?} ({}) isn't a multiple of the alignment of the type {} ({})", + src, + src_type_name, + dst_type_name, + dst_align + )] + Alignment + { + src_type_name: &'static str, + dst_type_name: &'static str, + src: *const u8, + dst_align: usize, + }, + + #[error( + "Source size {} ({}) is incorrect size for type {}", + src_size, + src_type_name, + dst_type_name + )] + Size + { + src_type_name: &'static str, + dst_type_name: &'static str, + src_size: usize, + }, +} + +fn image_buf_from_bytes<Pixel>( + dimens: Dimens<u32>, + bytes: &[u8], +) -> Result<image_rs::ImageBuffer<Pixel, Vec<Pixel::Subpixel>>, FromBytesError> +where + Pixel: image_rs::Pixel<Subpixel: FromBytes + Immutable>, +{ + let buf = <[Pixel::Subpixel]>::ref_from_bytes(bytes).map_err(|err| match err { + zerocopy::CastError::Alignment(_) => CastToSubPixelsError::Alignment { + src_type_name: type_name::<&[u8]>(), + dst_type_name: type_name::<&[Pixel]>(), + src: bytes.as_ptr(), + dst_align: align_of_val::<[Pixel]>(&[]), + }, + zerocopy::ConvertError::Size(_) => CastToSubPixelsError::Size { + src_type_name: type_name::<&[u8]>(), + dst_type_name: type_name::<&[Pixel]>(), + src_size: bytes.len(), + }, + })?; + + image_rs::ImageBuffer::from_raw(dimens.width, dimens.height, buf.to_vec()) + .ok_or(FromBytesError::NotEnoughBytesForDimensions) +} diff --git a/engine/src/renderer.rs b/engine/src/renderer.rs index 68e8aed..bdbbe0c 100644 --- a/engine/src/renderer.rs +++ b/engine/src/renderer.rs @@ -3,40 +3,34 @@ 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::event::component::Changed; use ecs::pair::{ChildOf, Pair, Wildcard}; use ecs::phase::{POST_UPDATE as POST_UPDATE_PHASE, Phase}; -use ecs::query::term::{With, Without}; use ecs::sole::Single; +use ecs::system::observer::Observe; use ecs::uid::Uid; use ecs::{Component, Query, Sole, declare_entity}; -use crate::asset::{Assets, Handle as AssetHandle}; +use crate::asset::Handle as AssetHandle; use crate::builder; use crate::data_types::dimens::Dimens; -use crate::draw_flags::{DrawFlags, NoDraw, PolygonModeConfig}; +use crate::draw_flags::PolygonModeConfig; use crate::mesh::Mesh; -use crate::model::{MaterialSearchResult, Model}; use crate::renderer::blending::Config as BlendingConfig; -use crate::renderer::object::{Id as ObjectId, Store as ObjectStore}; +use crate::renderer::object::Id as ObjectId; +use crate::shader::Program as ShaderProgram; use crate::shader::cursor::{ BindingLocation as ShaderBindingLocation, BindingValue as ShaderBindingValue, Cursor as ShaderCursor, }; -use crate::shader::default::ASSET_LABEL as DEFAULT_SHADER_ASSET_LABEL; -use crate::shader::{ - Context as ShaderContext, - ModuleSource as ShaderModuleSource, - Program as ShaderProgram, - Shader, -}; -use crate::texture::{Texture, WHITE_1X1_ASSET_LABEL as TEXTURE_WHITE_1X1_ASSET_LABEL}; +use crate::texture::Texture; use crate::vector::Vec2; use crate::windowing::window::Window; pub mod blending; +pub mod main_render_pass; pub mod object; pub mod opengl; @@ -64,6 +58,11 @@ declare_entity!( ) ); +declare_entity!( + pub POST_RENDER_PHASE, + (Phase, Pair::builder().relation::<ChildOf>().target_id(*RENDER_PHASE).build()) +); + builder! { /// Window graphics properties. #[builder(name=GraphicsPropertiesBuilder, derives=(Debug, Clone))] @@ -191,8 +190,14 @@ pub enum Command mesh: Mesh, usage: MeshUsage, }, + RemoveMesh(ObjectId), DrawMesh(ObjectId, DrawMeshOptions), UpdateDrawProperties(DrawProperties, DrawPropertiesUpdateFlags), + SetViewport + { + size: Dimens<u32>, + position: Vec2<u32>, + }, } builder! { @@ -354,237 +359,6 @@ pub struct WindowUsingRendererCtx; #[derive(Debug, Component)] pub struct CtxUsedByWindow; -type RenderableEntity<'a> = ( - &'a Model, - Option<&'a DrawFlags>, - Option<&'a Shader>, - Option<&'a mut PendingShaderBindings>, -); - -#[tracing::instrument(skip_all)] -pub fn add_main_render_passes( - renderer_ctx_query: Query< - (&ObjectStore, &[Pair<CtxUsedByWindow, Wildcard>]), - (With<CommandQueue>,), - >, - renderable_query: Query<RenderableEntity<'_>, (Without<NoDraw>,)>, - assets: Single<Assets>, - shader_context: Single<ShaderContext>, - mut render_passes: Single<RenderPasses>, - mut actions: Actions, -) -{ - let Some(default_shader_asset) = assets - .get_handle_to_loaded::<ShaderModuleSource>(DEFAULT_SHADER_ASSET_LABEL.clone()) - else { - tracing::error!("Default shader asset is not loaded"); - 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(); - - let Some(window_ent) = ctx_used_by_window.get_target_ent() else { - tracing::error!("Window entity does not exist"); - continue; - }; - - 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>() - ); - - actions.remove_components( - renderer_ctx_ent_id, - [Pair::builder() - .relation::<CtxUsedByWindow>() - .target_id(window_ent_id) - .build() - .id()], - ); - - 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; - }; - - render_passes.passes.push_front(RenderPass { - renderer_ctx_ent_id, - surface_id: surface_spec.id, - commands: Vec::with_capacity(30), - draw_properties: DrawProperties::default(), - }); - - let render_pass = render_passes.passes.front_mut().expect("Not possible"); - - render_pass - .commands - .push(Command::MakeCurrent(surface_spec.id)); - - let default_texture_asset = assets - .get_handle_to_loaded::<Texture>(TEXTURE_WHITE_1X1_ASSET_LABEL.clone()) - .expect("Not possible"); - - if !object_store - .contains_with_id(&ObjectId::Asset(default_texture_asset.id())) - { - render_pass - .commands - .push(Command::CreateTexture(default_texture_asset)); - } - - render_pass.commands.push(Command::ClearBuffers( - BufferClearMask::COLOR | BufferClearMask::DEPTH, - )); - - 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 Some(model_spec) = assets.get(&model.spec_asset) else { - continue; - }; - - let Some(mesh_asset) = &model_spec.mesh_asset else { - continue; - }; - - if assets.get(mesh_asset).is_none() { - continue; - } - - debug_assert!(model_spec.material_names.len() <= 1); - - 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, - }; - - if !object_store.contains_with_id(&ObjectId::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; - }; - - render_pass.commands.push(Command::CreateShaderProgram( - ObjectId::Asset(shader_asset.id()), - shader_program.clone(), - )); - } - - render_pass - .commands - .push(Command::ActivateShader(ObjectId::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 !object_store - .contains_with_id(&ObjectId::Asset(texture_asset.id())) - { - render_pass - .commands - .push(Command::CreateTexture(texture_asset.clone())); - } - } - - 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(Command::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(Command::UpdateDrawProperties( - DrawProperties { - polygon_mode_config: draw_flags.polygon_mode_config.clone(), - ..Default::default() - }, - DrawPropertiesUpdateFlags::POLYGON_MODE_CONFIG, - )); - } - - if !object_store.contains_with_id(&ObjectId::Asset(mesh_asset.id())) { - render_pass.commands.push(Command::CreateMesh { - obj_id: ObjectId::Asset(mesh_asset.id()), - mesh: None, - usage: MeshUsage::Static, - }); - } - - render_pass.commands.push(Command::DrawMesh( - ObjectId::Asset(mesh_asset.id()), - DrawMeshOptions::default(), - )); - - if let Some(draw_flags) = draw_flags.as_deref() - && draw_flags.polygon_mode_config != PolygonModeConfig::default() - { - render_pass.commands.push(Command::UpdateDrawProperties( - DrawProperties { - polygon_mode_config: PolygonModeConfig::default(), - ..Default::default() - }, - DrawPropertiesUpdateFlags::POLYGON_MODE_CONFIG, - )); - } - } - } - } -} - #[tracing::instrument(skip_all)] pub fn enqueue_commands_from_render_passes( renderer_ctx_query: Query<( @@ -678,6 +452,59 @@ pub fn enqueue_commands_from_render_passes( } } +#[tracing::instrument(skip_all)] +fn handle_window_changed( + observe: Observe<Pair<Changed, Window>>, + entity_obtainer: EntityObtainer, +) +{ + for evt_match in &observe { + let window_ent = evt_match.get_ent_infallible(); + + let Some(window_surface_spec) = window_ent.get::<SurfaceSpec>() else { + 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(), + }); + + command_queue + .queue + .push_front(Command::MakeCurrent(window_surface_spec.id)); + } +} + +// TODO: Maybe move this struct to somewhere more appropriate #[derive(Default, Clone, Component)] pub struct PendingShaderBindings { diff --git a/engine/src/renderer/main_render_pass.rs b/engine/src/renderer/main_render_pass.rs new file mode 100644 index 0000000..ec490e4 --- /dev/null +++ b/engine/src/renderer/main_render_pass.rs @@ -0,0 +1,280 @@ +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; + +use crate::asset::Assets; +use crate::draw_flags::{DrawFlags, NoDraw, PolygonModeConfig}; +use crate::model::{MaterialSearchResult, Model}; +use crate::renderer::object::{Id as RendererObjectId, Store as RendererObjectStore}; +use crate::renderer::{ + BufferClearMask as RendererBufferClearMask, + Command as RendererCommand, + CommandQueue as RendererCommandQueue, + CtxUsedByWindow as RendererCtxUsedByWindow, + DrawMeshOptions as RendererDrawMeshOptions, + DrawProperties as RendererDrawProperties, + DrawPropertiesUpdateFlags as RendererDrawPropertiesUpdateFlags, + MeshUsage as RendererMeshUsage, + PendingShaderBindings, + RenderPass, + RenderPasses as RendererRenderPasses, + SurfaceSpec as RendererSurfaceSpec, +}; +use crate::shader::default::ASSET_LABEL as DEFAULT_SHADER_ASSET_LABEL; +use crate::shader::{ + Context as ShaderContext, + ModuleSource as ShaderModuleSource, + Shader, +}; +use crate::texture::{Texture, WHITE_1X1_ASSET_LABEL as TEXTURE_WHITE_1X1_ASSET_LABEL}; +use crate::windowing::window::Window; + +type RenderableEntity<'a> = ( + &'a Model, + Option<&'a DrawFlags>, + Option<&'a Shader>, + Option<&'a mut PendingShaderBindings>, +); + +#[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>,)>, + assets: Single<Assets>, + shader_context: Single<ShaderContext>, + mut render_passes: Single<RendererRenderPasses>, + mut actions: Actions, +) +{ + let Some(default_shader_asset) = assets + .get_handle_to_loaded::<ShaderModuleSource>(DEFAULT_SHADER_ASSET_LABEL.clone()) + else { + tracing::error!("Default shader asset is not loaded"); + 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(); + + let Some(window_ent) = ctx_used_by_window.get_target_ent() else { + tracing::error!("Window entity does not exist"); + continue; + }; + + 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>() + ); + + actions.remove_components( + renderer_ctx_ent_id, + [Pair::builder() + .relation::<RendererCtxUsedByWindow>() + .target_id(window_ent_id) + .build() + .id()], + ); + + continue; + }; + + 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_passes.passes.push_front(RenderPass { + renderer_ctx_ent_id, + surface_id: surface_spec.id, + commands: Vec::with_capacity(30), + draw_properties: RendererDrawProperties::default(), + }); + + let render_pass = render_passes.passes.front_mut().expect("Not possible"); + + render_pass + .commands + .push(RendererCommand::MakeCurrent(surface_spec.id)); + + let default_texture_asset = assets + .get_handle_to_loaded::<Texture>(TEXTURE_WHITE_1X1_ASSET_LABEL.clone()) + .expect("Not possible"); + + if !object_store + .contains_with_id(&RendererObjectId::Asset(default_texture_asset.id())) + { + render_pass + .commands + .push(RendererCommand::CreateTexture(default_texture_asset)); + } + + render_pass.commands.push(RendererCommand::ClearBuffers( + RendererBufferClearMask::COLOR | RendererBufferClearMask::DEPTH, + )); + + 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 Some(model_spec) = assets.get(&model.spec_asset) else { + continue; + }; + + let Some(mesh_asset) = &model_spec.mesh_asset else { + continue; + }; + + if assets.get(mesh_asset).is_none() { + continue; + } + + debug_assert!(model_spec.material_names.len() <= 1); + + 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, + }; + + 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; + }; + + render_pass + .commands + .push(RendererCommand::CreateShaderProgram( + RendererObjectId::Asset(shader_asset.id()), + shader_program.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 !object_store + .contains_with_id(&RendererObjectId::Asset(texture_asset.id())) + { + render_pass + .commands + .push(RendererCommand::CreateTexture(texture_asset.clone())); + } + } + + 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 !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::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, + )); + } + } + } + } +} diff --git a/engine/src/renderer/object.rs b/engine/src/renderer/object.rs index 8e2254f..13be4a4 100644 --- a/engine/src/renderer/object.rs +++ b/engine/src/renderer/object.rs @@ -77,6 +77,11 @@ impl Store self.objects.insert(id, object); } + pub fn remove(&mut self, id: &Id) -> Option<Object> + { + self.objects.remove(id) + } + pub fn entry(&mut self, id: Id) -> StoreEntry<'_> { StoreEntry { inner: self.objects.entry(id) } diff --git a/engine/src/renderer/opengl.rs b/engine/src/renderer/opengl.rs index a713d41..1cd46b2 100644 --- a/engine/src/renderer/opengl.rs +++ b/engine/src/renderer/opengl.rs @@ -5,14 +5,12 @@ use std::borrow::Cow; use std::collections::HashMap; use ecs::actions::Actions; -use ecs::entity::obtainer::Obtainer as EntityObtainer; -use ecs::event::component::{Changed, Removed}; -use ecs::pair::{ChildOf, Pair, Wildcard}; -use ecs::phase::Phase; +use ecs::event::component::Removed; +use ecs::pair::{Pair, Wildcard}; use ecs::query::term::Without; use ecs::sole::Single; use ecs::system::observer::Observe; -use ecs::{Component, Query, declare_entity}; +use ecs::{Component, Query}; use glutin::display::GetGlDisplay; use glutin::prelude::GlDisplay; use glutin::surface::{ @@ -38,12 +36,12 @@ use opengl_bindings::debug::{ use opengl_bindings::misc::{ BufferClearMask as GlBufferClearMask, Capability, - SetViewportError as GlSetViewportError, clear_buffers, define_scissor_box as gl_define_scissor_box, enable, get_viewport as gl_get_viewport, set_enabled, + set_viewport as gl_set_viewport, }; use opengl_bindings::shader::{ Error as GlShaderError, @@ -73,7 +71,6 @@ use crate::asset::{Assets, Handle as AssetHandle}; use crate::data_types::dimens::Dimens; use crate::image::{ColorType as ImageColorType, Image}; use crate::matrix::Matrix; -use crate::model::Model; use crate::renderer::blending::{Equation as BlendingEquation, Factor as BlendingFactor}; use crate::renderer::object::{ Id as RendererObjectId, @@ -96,6 +93,7 @@ use crate::renderer::{ DrawMeshOptions, DrawPropertiesUpdateFlags, GraphicsProperties, + POST_RENDER_PHASE, PRE_RENDER_PHASE, RENDER_PHASE, RenderPasses, @@ -128,11 +126,6 @@ use crate::windowing::window::{ mod glutin_compat; mod graphics_mesh; -declare_entity!( - pub POST_RENDER_PHASE, - (Phase, Pair::builder().relation::<ChildOf>().target_id(*RENDER_PHASE).build()) -); - #[derive(Debug, Component)] struct WindowGlConfig { @@ -174,7 +167,10 @@ impl ecs::extension::Extension for Extension let _ = collector.add_sole(RenderPasses::default()); - collector.add_system(*PRE_RENDER_PHASE, super::add_main_render_passes); + collector.add_system( + *PRE_RENDER_PHASE, + crate::renderer::main_render_pass::add_main_render_passes, + ); collector.add_system(*RENDER_PHASE, super::enqueue_commands_from_render_passes); collector.add_system(*RENDER_PHASE, handle_commands); @@ -182,201 +178,12 @@ impl ecs::extension::Extension for Extension collector.add_system(*POST_RENDER_PHASE, prepare_windows); collector.add_system(*POST_RENDER_PHASE, init_window_graphics); - collector.add_observer(handle_model_removed); - - collector.add_observer(handle_window_changed); + collector.add_observer(super::handle_window_changed); collector.add_observer(handle_window_removed); } } #[tracing::instrument(skip_all)] -fn handle_model_removed( - observe: Observe<Pair<Removed, Model>>, - renderer_ctx_query: Query<( - &mut GraphicsContext, - &RendererObjectStore, - Pair<RendererCtxUsedByWindow, Wildcard>, - )>, - assets: Single<Assets>, -) -{ - for evt_match in &observe { - let model_ent_id = evt_match.id(); - - for ( - renderer_ctx_ent_id, - (mut graphics_ctx, renderer_object_store, renderer_ctx_used_by_window), - ) in renderer_ctx_query.iter_with_euids() - { - let GraphicsContext { - ref gl_context, - ref surfaces, - objects: ref mut graphics_ctx_objects, - .. - } = *graphics_ctx; - - let model = evt_match.get_removed_comp(); - - let Some(model_spec) = assets.get(&model.spec_asset) else { - continue; - }; - - let Some(mesh_asset) = &model_spec.mesh_asset else { - continue; - }; - - let Some(graphics_ctx_object_id) = renderer_object_store - .get_obj(&RendererObjectId::Asset(mesh_asset.id())) - .map(|obj| obj.as_raw()) - else { - continue; - }; - - if !graphics_ctx_objects.contains_key(&graphics_ctx_object_id) { - continue; - } - - let Some(window_ent) = renderer_ctx_used_by_window.get_target_ent() else { - tracing::error!( - window_entity_id = %renderer_ctx_used_by_window.id().target_entity(), - "Window entity does not exist" - ); - continue; - }; - - let Some(surface_spec) = window_ent.get::<SurfaceSpec>() else { - tracing::error!( - window_entity_id = %window_ent.uid(), - "Window entity does not have a {} component", - type_name::<SurfaceSpec>() - ); - continue; - }; - - let Some(surface) = surfaces.get(&surface_spec.id) else { - tracing::error!( - window_entity_id = %window_ent.uid(), - "Surface specified by window entity's {} component does not exist", - type_name::<SurfaceSpec>() - ); - continue; - }; - - let curr_gl_ctx = match gl_context.make_current(surface) { - Ok(curr_gl_ctx) => curr_gl_ctx, - Err(err) => { - tracing::error!("{err}"); - continue; - } - }; - - tracing::debug!( - model_entity_id=%model_ent_id, - renderer_ctx_entity_id=%renderer_ctx_ent_id, - "Cleaning up after model in renderer context" - ); - - let Some(removed_graphics_ctx_obj) = - graphics_ctx_objects.remove(&graphics_ctx_object_id) - else { - tracing::warn!( - model_entity_id=%model_ent_id, - "No graphics context object exists for model" - ); - continue; - }; - - #[allow(irrefutable_let_patterns)] - let GraphicsContextObject::Mesh { - mesh: mut graphics_mesh, - compatible_shader_program_obj_id: _, - } = removed_graphics_ctx_obj - else { - tracing::error!( - model_entity_id=%model_ent_id, - "Graphics context object for model is not a mesh" - ); - - continue; - }; - - graphics_mesh.destroy(&curr_gl_ctx); - } - } -} - -#[tracing::instrument(skip_all)] -fn handle_window_changed( - observe: Observe<Pair<Changed, Window>>, - entity_obtainer: EntityObtainer, -) -{ - for evt_match in &observe { - let window_ent = evt_match.get_ent_infallible(); - - tracing::trace!( - new_state = ?evt_match.get_changed_comp(), - "Handling window change" - ); - - let Some(surface_spec) = window_ent.get::<SurfaceSpec>() else { - 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(graphics_context) = renderer_ctx_ent.get::<GraphicsContext>() else { - tracing::error!( - "Renderer context entity does not have a GraphicsContext component" - ); - continue; - }; - - let Some(surface) = graphics_context.surfaces.get(&surface_spec.id) else { - tracing::error!( - window_entity_id = %window_ent.uid(), - "Surface specified by window entity's {} component does not exist", - type_name::<SurfaceSpec>() - ); - continue; - }; - - let Ok(current_graphics_context) = - graphics_context.gl_context.make_current(surface) - else { - tracing::error!("Failed to make graphics context current"); - continue; - }; - - if let Err(err) = set_viewport( - ¤t_graphics_context, - Vec2::default(), - evt_match.get_changed_comp().inner_size(), - ) { - tracing::error!("Failed to set viewport: {err}"); - } - } -} - -#[tracing::instrument(skip_all)] fn handle_window_removed(observe: Observe<Pair<Removed, Window>>, mut actions: Actions) { for evt_match in &observe { @@ -609,9 +416,11 @@ fn init_window_graphics( continue; }; - if let Err(err) = - set_viewport(&curr_gl_context, Vec2 { x: 0, y: 0 }, window.inner_size()) - { + if let Err(err) = gl_set_viewport( + &curr_gl_context, + &Vec2 { x: 0, y: 0 }.into(), + &window.inner_size().clone().into(), + ) { tracing::error!("Failed to set viewport: {err}"); } @@ -1090,6 +899,48 @@ fn handle_commands( 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; + }; + + #[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; + }; + + 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"); @@ -1256,6 +1107,23 @@ fn handle_commands( ); } } + 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}"); + } + } } } } @@ -1323,23 +1191,6 @@ fn create_texture_object( Ok(()) } -fn set_viewport( - current_context: &CurrentContextWithFns<'_>, - position: Vec2<u32>, - size: &Dimens<u32>, -) -> Result<(), GlSetViewportError> -{ - let position = - opengl_bindings::data_types::Vec2::<u32> { x: position.x, y: position.y }; - - let size = opengl_bindings::data_types::Dimens::<u32> { - width: size.width, - height: size.height, - }; - - opengl_bindings::misc::set_viewport(current_context, &position, &size) -} - fn draw_mesh( current_context: &CurrentContextWithFns<'_>, graphics_mesh: &GraphicsMesh, |
