diff options
| author | HampusM <hampus@hampusmat.com> | 2026-08-08 16:11:13 +0200 |
|---|---|---|
| committer | HampusM <hampus@hampusmat.com> | 2026-08-08 16:23:34 +0200 |
| commit | a79511cd3033254d9f3c93857d96f1c39751c730 (patch) | |
| tree | 3f8ef07910737dc47481859264c525ee4d1938c7 | |
| parent | 8520817a39d6554246485ce0f8775a7cd8e9c3a7 (diff) | |
| -rw-r--r-- | Cargo.lock | 22 | ||||
| -rw-r--r-- | engine/Cargo.toml | 7 | ||||
| -rw-r--r-- | engine/src/ui/dear_imgui.rs | 337 | ||||
| -rw-r--r-- | engine/src/ui/view/world.rs | 47 |
4 files changed, 208 insertions, 205 deletions
@@ -499,36 +499,36 @@ checksum = "f27ae1dd37df86211c42e150270f82743308803d90a6f6e6651cd730d5e1732f" [[package]] name = "dear-imgui-build-support" -version = "0.15.1" -source = "git+https://github.com/Latias94/dear-imgui-rs?rev=cd2425b699e746852757e9290257bbf09129cfa7#cd2425b699e746852757e9290257bbf09129cfa7" +version = "0.16.0-alpha.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9910bfb7b9c312809c1957efc9bd4c96e275ec08bc4888020472dab9bd2ae576" dependencies = [ "cc", "pkg-config", + "serde", + "serde_json", "vcpkg", ] [[package]] name = "dear-imgui-rs" -version = "0.15.1" -source = "git+https://github.com/Latias94/dear-imgui-rs?rev=cd2425b699e746852757e9290257bbf09129cfa7#cd2425b699e746852757e9290257bbf09129cfa7" +version = "0.16.0-alpha.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4842e82949f15b8b507fd2808529311e6ddacba25721b4e0c4b7c868243174fd" dependencies = [ "bitflags 2.11.1", - "cfg-if", "dear-imgui-sys", - "mint", "parking_lot", "thiserror 2.0.18", - "tracing", - "tracing-subscriber", ] [[package]] name = "dear-imgui-sys" -version = "0.15.1" -source = "git+https://github.com/Latias94/dear-imgui-rs?rev=cd2425b699e746852757e9290257bbf09129cfa7#cd2425b699e746852757e9290257bbf09129cfa7" +version = "0.16.0-alpha.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2894ef8941cca0807c4e56f4c80e1f479ccc61100554c803df4e4cc955ec3dd" dependencies = [ "cc", - "cfg-if", "dear-imgui-build-support", "mint", ] diff --git a/engine/Cargo.toml b/engine/Cargo.toml index 0c1f5c3..31fa6eb 100644 --- a/engine/Cargo.toml +++ b/engine/Cargo.toml @@ -42,10 +42,9 @@ git = "https://github.com/HampusMat/slang-rs" branch = "prebuilt-and-artifacts" [dependencies.dear-imgui-rs] -git = "https://github.com/Latias94/dear-imgui-rs" -# This commit has a fix for linking with libstdc++ on non-msvc Windows targets. When a -# release with this fix is available, it should be used instead -rev = "cd2425b699e746852757e9290257bbf09129cfa7" +# This alpha release has a fix for linking with libstdc++ on non-msvc Windows targets. +# When a stable release with this fix is available, it should be used instead +version = "=0.16.0-alpha.1" features = ["freetype"] [build-dependencies] diff --git a/engine/src/ui/dear_imgui.rs b/engine/src/ui/dear_imgui.rs index 60067e1..212f24c 100644 --- a/engine/src/ui/dear_imgui.rs +++ b/engine/src/ui/dear_imgui.rs @@ -1,3 +1,5 @@ +use std::collections::HashMap; +use std::hint::cold_path; use std::path::{Path, PathBuf}; use std::sync::atomic::{AtomicU64, Ordering}; use std::sync::LazyLock; @@ -9,6 +11,7 @@ use dear_imgui_rs::{ }; use ecs::component::local::Local; use ecs::component::Component; +use ecs::error::Context as _; use ecs::event::component::{Changed, EventMatchExt}; use ecs::pair::Pair; use ecs::query::term::With; @@ -99,6 +102,8 @@ pub struct Context pub enabled: bool, ctx: inner_context_wrapper::InnerContextWrapper, texture_lookup: MapVec<TextureLookupId, RenderingObjectId>, + texture_id_lookup: + HashMap<dear_imgui_rs::SnapshotTextureId, dear_imgui_rs::TextureId>, } impl Context @@ -112,9 +117,12 @@ impl Context self.ctx.get_frame() } - pub fn register_texture(&mut self, texture_data: &mut bindings::OwnedTextureData) + pub fn register_texture( + &mut self, + texture_data: bindings::OwnedTextureData, + ) -> bindings::ManagedTextureId { - self.ctx.register_texture(texture_data); + self.ctx.register_texture(texture_data) } } @@ -152,6 +160,7 @@ impl ecs::extension::Extension for Extension enabled: self.start_enabled, ctx: inner_context_wrapper::InnerContextWrapper::new(), texture_lookup: MapVec::with_capacity(8), + texture_id_lookup: HashMap::with_capacity(8), }; if let Err(err) = context @@ -166,19 +175,21 @@ impl ecs::extension::Extension for Extension "Freetype font rasterizer support is not enabled" ); - context.ctx.font_atlas_mut().add_font_from_memory_ttf( - include_bytes!("../../res/FiraMono-Regular.ttf"), - 14.0, - Some( - &dear_imgui_rs::FontConfig::default() - .name("FiraMono-Regular.ttf") - .font_loader_flags( - dear_imgui_rs::FontLoaderFlags::BITMAP - | dear_imgui_rs::FontLoaderFlags::FORCE_AUTOHINT, - ), - ), - None, - ); + unsafe { + context.ctx.font_atlas().add_font_from_memory_ttf( + include_bytes!("../../res/FiraMono-Regular.ttf"), + 14.0, + Some( + &dear_imgui_rs::FontConfig::default() + .name("FiraMono-Regular.ttf") + .font_loader_flags( + dear_imgui_rs::FontLoaderFlags::BITMAP + | dear_imgui_rs::FontLoaderFlags::FORCE_AUTOHINT, + ), + ), + None, + ); + } collector.add_sole(context).ok(); @@ -504,32 +515,42 @@ fn add_drawing_render_pass( .unwrap(), ); - let Context { enabled: _, ctx, texture_lookup } = context; - - let draw_data = ctx.render(); - - let mut textures = draw_data.textures_mut(); - - while let Some(mut texture_data) = textures.next() { - match texture_data.status() { - dear_imgui_rs::TextureStatus::WantCreate => { - let Some(texture_pixels) = texture_data.pixels() else { - texture_data.set_status(dear_imgui_rs::TextureStatus::OK); - continue; - }; - - if !matches!(texture_data.format(), dear_imgui_rs::TextureFormat::RGBA32) - { + let Context { + enabled: _, + ctx, + texture_lookup, + texture_id_lookup, + } = context; + + let mut render_frame = ctx.render(); + + let mut tex_feedbacks = Vec::with_capacity(render_frame.texture_requests().len()); + + for texture_request in render_frame.texture_requests() { + match texture_request.operation() { + dear_imgui_rs::TextureOp::Create { + format, + width, + height, + row_pitch: _, + pixels, + } => { + if !matches!(format, dear_imgui_rs::TextureFormat::RGBA32) { unimplemented!(); } + let texture_lookup_id = TextureLookupId::new(); + + tracing::trace!( + snapshot_tex_id = ?texture_request.texture(), + tex_lookup_id = ?texture_lookup_id, + "Performing texture operation: Create" + ); + let image = match Image::from_pixels(ImagePixelBuffer::<Rgba<u8>, _>::new( - texture_pixels, - Dimens { - width: texture_data.width(), - height: texture_data.height(), - }, + pixels.as_slice(), + Dimens { width: *width, height: *height }, )) { Ok(image) => image, Err(ImagePixelBufferLenIncorrectForSize) => { @@ -548,90 +569,127 @@ fn add_drawing_render_pass( })), )); - let texture_lookup_id = TextureLookupId::new(); - - texture_data.set_tex_id(texture_lookup_id.into()); - texture_lookup.insert(texture_lookup_id, texture_object_id); - texture_data.set_status(dear_imgui_rs::TextureStatus::OK); - } - dear_imgui_rs::TextureStatus::WantUpdates => { - if texture_data.tex_id().is_null() { - tracing::error!(concat!( - "Update was requested for texture with a null ID. Did you forget ", - "to set the texture status to TextureStatus::WantCreate?")); + texture_id_lookup + .insert(texture_request.texture(), texture_lookup_id.into()); - continue; - } + let texture_feedback = + match texture_request.uploaded(texture_lookup_id.into()) { + Ok(texture_feedback) => texture_feedback, + Err( + dear_imgui_rs::TextureFeedbackError::UploadForDestroy + | dear_imgui_rs::TextureFeedbackError::DestroyForUpload, + ) => unreachable!(), + }; - if texture_data.pixels().is_none() { - texture_data.set_status(dear_imgui_rs::TextureStatus::OK); - continue; + tex_feedbacks.push(texture_feedback); + } + dear_imgui_rs::TextureOp::Update { format, width: _, height: _, rects } => { + if !matches!(format, dear_imgui_rs::TextureFormat::RGBA32) { + unimplemented!(); } - let mut rects: Vec<dear_imgui_rs::texture::TextureRect> = - texture_data.updates().collect(); + let texture_lookup_id = + TextureLookupId::from(texture_id_lookup[&texture_request.texture()]); - if rects.is_empty() { - let rect = texture_data.update_rect(); - if rect.w > 0 && rect.h > 0 { - rects.push(rect); - } - } + tracing::trace!( + snapshot_tex_id = ?texture_request.texture(), + tex_lookup_id = ?texture_lookup_id, + "Performing texture operation: Update" + ); - if rects.is_empty() { - texture_data.set_status(dear_imgui_rs::TextureStatus::OK); - continue; - } - - let Some(texture_object_id) = - texture_lookup.get(&TextureLookupId::from(texture_data.tex_id())) + let Some(texture_object_id) = texture_lookup.get(&texture_lookup_id) else { tracing::error!( - "Unknown texture {}. Skipping update", - texture_data.tex_id().id() + snapshot_tex_id = ?texture_request.texture(), + tex_lookup_id = ?texture_lookup_id, + "Unknown texture. Skipping update", ); - texture_data.set_status(dear_imgui_rs::TextureStatus::OK); continue; }; - render_pass.commands.extend(rects.iter().filter_map(|rect| { - let Some(rect_image) = get_texture_rect_image(&texture_data, rect) + render_pass.commands.reserve(rects.len()); + + for rect in rects { + let Ok(rect_image) = + Image::from_pixels(ImagePixelBuffer::<Rgba<u8>, _>::new( + rect.data.as_slice(), + Dimens::<u32> { + width: rect.rect.w.into(), + height: rect.rect.h.into(), + }, + )) else { - return None; + cold_path(); + tracing::error!("Texture update rect size is incorrect"); + continue; }; - Some(RenderingCommand::UpdateTexture { - obj_id: texture_object_id.clone(), + render_pass.commands.push(RenderingCommand::UpdateTexture { + obj_id: *texture_object_id, image: rect_image, - offset: Vec2 { x: rect.x as u32, y: rect.y as u32 }, - }) - })); + offset: Vec2 { + x: rect.rect.x as u32, + y: rect.rect.y as u32, + }, + }); + } + + let texture_feedback = + match texture_request.uploaded(texture_lookup_id.into()) { + Ok(texture_feedback) => texture_feedback, + Err( + dear_imgui_rs::TextureFeedbackError::UploadForDestroy + | dear_imgui_rs::TextureFeedbackError::DestroyForUpload, + ) => unreachable!(), + }; - texture_data.set_status(dear_imgui_rs::TextureStatus::OK); + tex_feedbacks.push(texture_feedback); } - dear_imgui_rs::TextureStatus::WantDestroy => { - let Some(texture_object_id) = - texture_lookup.remove(TextureLookupId::from(texture_data.tex_id())) + dear_imgui_rs::TextureOp::Destroy => { + let texture_lookup_id = + TextureLookupId::from(texture_id_lookup[&texture_request.texture()]); + + tracing::trace!( + snapshot_tex_id = ?texture_request.texture(), + tex_lookup_id = ?texture_lookup_id, + "Performing texture operation: Destroy" + ); + + let Some(texture_object_id) = texture_lookup.remove(texture_lookup_id) else { tracing::error!( - "Unknown texture {}. Skipping destroyal", - texture_data.tex_id().id() + texture = ?texture_lookup_id, + "Unknown texture. Skipping destroyal", ); - texture_data.set_status(dear_imgui_rs::TextureStatus::OK); continue; }; render_pass .commands .push(RenderingCommand::RemoveTexture(texture_object_id)); + + let texture_feedback = match texture_request.destroyed() { + Ok(texture_feedback) => texture_feedback, + Err( + dear_imgui_rs::TextureFeedbackError::UploadForDestroy + | dear_imgui_rs::TextureFeedbackError::DestroyForUpload, + ) => unreachable!(), + }; + + tex_feedbacks.push(texture_feedback); } - _ => {} } } - let [display_width, display_height] = draw_data.display_size; + render_frame + .reconcile_texture_feedback(tex_feedbacks) + .with_context(|| "Failed to reconcile texture feedback")?; + + let draw_data = render_frame.draw_data(); + + let [display_width, display_height] = draw_data.display_size(); // let [scale_width, scale_height] = draw_data.framebuffer_scale; @@ -691,18 +749,14 @@ fn add_drawing_render_pass( for command in draw_list.commands() { match command { - ImguiDrawCmd::Elements { count, cmd_params, raw_cmd } => { + ImguiDrawCmd::Elements { count, cmd_params } => { let Some(scissor_box) = calc_draw_cmd_scissor_box(draw_data, &cmd_params) else { continue; }; - let tex_id = dear_imgui_rs::TextureId::new(u64::from(unsafe { - let mut cmd_copy = *raw_cmd; - - dear_imgui_rs::sys::ImDrawCmd_GetTexID(&mut cmd_copy) - })); + let tex_id = cmd_params.texture_id; let Some(texture_object_id) = texture_lookup.get(&TextureLookupId::from(tex_id)) @@ -745,59 +799,6 @@ fn add_drawing_render_pass( Ok(()) } -fn get_texture_rect_image( - texture_data: &dear_imgui_rs::TextureData, - rect: &dear_imgui_rs::TextureRect, -) -> Option<Image> -{ - let texture_pixels = texture_data.pixels()?; - - let tex_width = texture_data.width(); - let tex_height = texture_data.height(); - - if tex_width == 0 || tex_height == 0 { - return None; - } - - let rect_pos = Vec2::<u32> { x: rect.x.into(), y: rect.y.into() }; - - let rect_size = Dimens::<u32> { - width: rect.w.into(), - height: rect.h.into(), - }; - - if rect_size.width == 0 - || rect_size.height == 0 - || rect_pos.x >= tex_width - || rect_pos.y >= tex_height - { - return None; - } - - let rect_size = Dimens { - width: rect_size.width.min(tex_width.saturating_sub(rect_pos.x)), - height: rect_size.height.min(tex_height.saturating_sub(rect_pos.y)), - }; - - match texture_data.format() { - dear_imgui_rs::TextureFormat::RGBA32 => { - let Ok(texture_image) = - Image::from_pixels(ImagePixelBuffer::<Rgba<u8>, _>::new( - texture_pixels, - Dimens { width: tex_width, height: tex_height }, - )) - else { - return None; - }; - - Some(texture_image.sub_view(rect_pos, rect_size).to_image()) - } - dear_imgui_rs::TextureFormat::Alpha8 => { - unimplemented!(); - } - } -} - fn calc_draw_cmd_scissor_box( draw_data: &dear_imgui_rs::DrawData, cmd_params: &dear_imgui_rs::DrawCmdParams, @@ -805,23 +806,23 @@ fn calc_draw_cmd_scissor_box( { let clip_rect = cmd_params.clip_rect; - let clip_min_x = - (clip_rect[0] - draw_data.display_pos[0]) * draw_data.framebuffer_scale[0]; + let display_size = draw_data.display_size(); + let display_pos = draw_data.display_pos(); + let framebuffer_scale = draw_data.framebuffer_scale(); - let clip_min_y = - (clip_rect[1] - draw_data.display_pos[1]) * draw_data.framebuffer_scale[1]; + let clip_min_x = (clip_rect[0] - display_pos[0]) * framebuffer_scale[0]; - let clip_max_x = - (clip_rect[2] - draw_data.display_pos[0]) * draw_data.framebuffer_scale[0]; + let clip_min_y = (clip_rect[1] - display_pos[1]) * framebuffer_scale[1]; - let clip_max_y = - (clip_rect[3] - draw_data.display_pos[1]) * draw_data.framebuffer_scale[1]; + let clip_max_x = (clip_rect[2] - display_pos[0]) * framebuffer_scale[0]; + + let clip_max_y = (clip_rect[3] - display_pos[1]) * framebuffer_scale[1]; if clip_max_x <= clip_min_x || clip_max_y <= clip_min_y { return None; } - let fb_height = draw_data.display_size[1] * draw_data.framebuffer_scale[1]; + let fb_height = display_size[1] * framebuffer_scale[1]; Some(ScissorBox { size: Some(Dimens { @@ -971,6 +972,7 @@ mod inner_context_wrapper { ctx: Pin<Box<dear_imgui_rs::Context>>, frame: *mut dear_imgui_rs::Ui, + _renderer_consumer: dear_imgui_rs::RendererConsumer, } impl InnerContextWrapper @@ -985,7 +987,13 @@ mod inner_context_wrapper ctx.io_mut() .set_backend_flags(dear_imgui_rs::BackendFlags::RENDERER_HAS_TEXTURES); - Self { ctx, frame: null_mut() } + let _renderer_consumer = ctx.create_renderer_consumer().unwrap(); + + Self { + ctx, + frame: null_mut(), + _renderer_consumer, + } } pub fn set_settings_ini_file_path( @@ -998,10 +1006,10 @@ mod inner_context_wrapper pub fn register_texture( &mut self, - texture_data: &mut dear_imgui_rs::texture::OwnedTextureData, - ) + texture_data: dear_imgui_rs::texture::OwnedTextureData, + ) -> dear_imgui_rs::ManagedTextureId { - self.ctx.register_user_texture(texture_data); + self.ctx.register_texture(texture_data) } pub fn get_io_mut(&mut self) -> &mut dear_imgui_rs::Io @@ -1009,12 +1017,17 @@ mod inner_context_wrapper self.ctx.io_mut() } - pub fn font_atlas_mut(&mut self) -> dear_imgui_rs::FontAtlas + pub fn font_atlas(&self) -> &dear_imgui_rs::FontAtlas { - self.ctx.font_atlas_mut() + self.ctx.font_atlas() } - pub fn render(&mut self) -> &mut dear_imgui_rs::DrawData + // pub fn create_renderer_consumer(&mut self) -> dear_imgui_rs::RendererConsumer + // { + // self.ctx.create_renderer_consumer().unwrap() + // } + + pub fn render(&mut self) -> dear_imgui_rs::RenderedFrame<'_> { self.ctx.render() } diff --git a/engine/src/ui/view/world.rs b/engine/src/ui/view/world.rs index 2bf3ca5..3ea0611 100644 --- a/engine/src/ui/view/world.rs +++ b/engine/src/ui/view/world.rs @@ -38,8 +38,8 @@ pub struct State { spawning_entity: Option<Uid>, add_component_popup_state: Option<AddComponentPopupState>, - despawn_icon_tex: Option<dear_imgui_bindings::OwnedTextureData>, - warning_icon_tex: Option<dear_imgui_bindings::OwnedTextureData>, + despawn_icon_tex_id: Option<dear_imgui_bindings::ManagedTextureId>, + warning_icon_tex_id: Option<dear_imgui_bindings::ManagedTextureId>, } impl Default for State @@ -49,8 +49,8 @@ impl Default for State Self { spawning_entity: None, add_component_popup_state: None, - despawn_icon_tex: None, - warning_icon_tex: None, + despawn_icon_tex_id: None, + warning_icon_tex_id: None, } } } @@ -80,11 +80,11 @@ pub fn show( let State { spawning_entity, add_component_popup_state, - despawn_icon_tex, - warning_icon_tex, + despawn_icon_tex_id, + warning_icon_tex_id, } = &mut *state; - let despawn_icon_tex = despawn_icon_tex.get_or_insert_with(|| { + let despawn_icon_tex_id = *despawn_icon_tex_id.get_or_insert_with(|| { create_texture( dear_imgui_context, Image::from_reader( @@ -95,7 +95,7 @@ pub fn show( ) }); - let warning_icon_tex = warning_icon_tex.get_or_insert_with(|| { + let warning_icon_tex_id = *warning_icon_tex_id.get_or_insert_with(|| { create_texture( dear_imgui_context, Image::from_reader( @@ -129,8 +129,8 @@ pub fn show( create_entity_widgets( frame, add_component_popup_state, - despawn_icon_tex, - warning_icon_tex, + despawn_icon_tex_id, + warning_icon_tex_id, &ent_handle, world, &mut actions, @@ -150,7 +150,7 @@ pub fn show( fn create_texture( dear_imgui_context: &mut DearImguiContext, image: Image, -) -> dear_imgui_bindings::OwnedTextureData +) -> dear_imgui_bindings::ManagedTextureId { let image = image.into_rgba8(); @@ -164,11 +164,7 @@ fn create_texture( texture_data.set_data(image.as_bytes()); - texture_data.set_status(dear_imgui_bindings::TextureStatus::WantCreate); - - dear_imgui_context.register_texture(&mut texture_data); - - texture_data + dear_imgui_context.register_texture(texture_data) } fn create_spawn_button_widgets( @@ -206,8 +202,8 @@ fn create_spawn_button_widgets( fn create_entity_widgets( frame: &dear_imgui_bindings::Ui, add_component_popup_state: &mut Option<AddComponentPopupState>, - despawn_icon_tex: &mut dear_imgui_bindings::OwnedTextureData, - warning_icon_tex: &mut dear_imgui_bindings::OwnedTextureData, + despawn_icon_tex: dear_imgui_bindings::ManagedTextureId, + warning_icon_tex: dear_imgui_bindings::ManagedTextureId, ent_handle: &EntityHandle, world: &World, actions: &mut Actions, @@ -370,7 +366,7 @@ fn create_component_widgets( fn create_pair_component_widgets( frame: &dear_imgui_bindings::Ui, - warning_icon_tex: &mut dear_imgui_bindings::OwnedTextureData, + warning_icon_tex_id: dear_imgui_bindings::ManagedTextureId, ent_handle: &EntityHandle, pair_id: Uid, world: &World, @@ -446,7 +442,7 @@ fn create_pair_component_widgets( let Some(pair_data_ty) = pair_data_comp_info.type_reflection else { frame.same_line(); - frame.image(&mut **warning_icon_tex, [16.0, 16.0]); + frame.image(warning_icon_tex_id, [16.0, 16.0]); if frame.is_item_hovered() { frame.tooltip_text(format!( @@ -559,7 +555,7 @@ fn create_add_component_button_widget( fn create_despawn_button_widget( frame: &dear_imgui_bindings::Ui, - despawn_icon_tex: &mut dear_imgui_bindings::OwnedTextureData, + despawn_icon_tex: dear_imgui_bindings::ManagedTextureId, ent_handle: &EntityHandle, actions: &mut Actions, ) @@ -582,7 +578,7 @@ fn create_despawn_button_widget( if frame .image_button_config( &format!("ent_{}_despawn_button", ent_handle.uid()), - despawn_icon_tex.as_mut(), + despawn_icon_tex, [14.0, 14.0], ) .build() @@ -777,12 +773,7 @@ fn show_add_component_popup( add_component_popup_state.new_component.is_none(), ); - let add_text_size = frame.current_font().calc_text_size( - frame.clone_style().font_size_base(), - frame.content_region_avail_width(), - 0.0, - "Add", - ); + let add_text_size = frame.calc_text_size("Add"); if frame.button_with_size( "Add##add_component_popup_add_button", |
