Merge branch 'feat/egui' into 'main'

feat: add init egui impl

See merge request zoey/vk-rs!2
This commit is contained in:
zoey 2025-04-02 01:44:18 +00:00 committed by zack
commit ae09e61f40
No known key found for this signature in database
GPG key ID: EE8A2B709E2401D1
4 changed files with 799 additions and 55 deletions

724
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -7,6 +7,7 @@ edition = "2021"
egui.workspace = true
ash.workspace = true
ash-window.workspace = true
color-eyre.workspace = true
tracing.workspace = true
tracing-subscriber.workspace = true
winit.workspace = true
@ -17,3 +18,4 @@ gfx_hal = { path = "../gfx_hal" }
renderer = { path = "../renderer" }
resource_manager = { path = "../resource_manager" }
clap = { version = "4.5.34", features = ["derive"] }
egui-winit = "0.31.1"

View file

@ -8,6 +8,8 @@ use std::{
use ash::vk;
use clap::Parser;
use egui::{Context, ViewportId};
use egui_winit::State;
use gfx_hal::{
device::Device, error::GfxHalError, instance::Instance, instance::InstanceConfig,
physical_device::PhysicalDevice, queue::Queue, surface::Surface,
@ -57,6 +59,10 @@ struct Application {
// Renderer
renderer: Renderer,
egui_ctx: Context,
egui_winit: State,
egui_app: EditorUI,
// Windowing
window: Arc<Window>, // Use Arc for potential multi-threading later
@ -65,6 +71,19 @@ struct Application {
last_frame_time: Instant,
}
#[derive(Default)]
struct EditorUI {}
impl EditorUI {
fn title() -> String {
"engine".to_string()
}
fn build_ui(&mut self, ctx: &egui::Context) {
egui::Window::new(Self::title()).show(ctx, |ui| ui.label(Self::title()));
}
}
#[derive(Default)]
struct ApplicationWrapper {
app: Option<Application>,
@ -223,14 +242,6 @@ impl Application {
// Get specific queues (assuming graphics and present are the same for simplicity)
let graphics_queue = device.get_graphics_queue();
let queue_associated_device_handle = graphics_queue.device().raw().handle();
info!(
"App: Queue is associated with Device handle: {:?}",
queue_associated_device_handle
);
assert_eq!(
device_handle_at_creation, queue_associated_device_handle,
"Device handle mismatch immediately after queue creation!"
);
// --- 4. Resource Manager ---
let resource_manager = Arc::new(ResourceManager::new(instance.clone(), device.clone())?);
@ -238,14 +249,6 @@ impl Application {
let renderer_device_handle_to_pass = device.raw().handle();
let renderer_queue_device_handle_to_pass = graphics_queue.device().raw().handle();
info!(
"App: Passing Device handle to Renderer: {:?}",
renderer_device_handle_to_pass
);
info!(
"App: Passing Queue associated with Device handle: {:?}",
renderer_queue_device_handle_to_pass
);
// --- 5. Renderer ---
let initial_size = window.inner_size();
@ -258,6 +261,18 @@ impl Application {
initial_size.width,
initial_size.height,
)?;
let egui_ctx = Context::default();
let egui_winit = State::new(
egui_ctx.clone(),
ViewportId::ROOT,
&window,
None,
None,
None,
);
let egui_app = EditorUI::default();
info!("Renderer initialized.");
Ok(Self {
@ -269,6 +284,9 @@ impl Application {
_resource_manager: resource_manager,
renderer,
window,
egui_winit,
egui_ctx,
egui_app,
frame_count: 0,
last_fps_update_time: Instant::now(),
last_frame_time: Instant::now(),
@ -276,6 +294,8 @@ impl Application {
}
fn handle_event(&mut self, event: &WindowEvent, active_event_loop: &ActiveEventLoop) {
let _ = self.egui_winit.on_window_event(&self.window, event);
match event {
WindowEvent::CloseRequested => {
info!("Close requested. Exiting...");
@ -320,8 +340,30 @@ impl Application {
self.last_fps_update_time = now;
}
let raw_input = self.egui_winit.take_egui_input(&self.window);
let egui::FullOutput {
platform_output,
textures_delta,
shapes,
pixels_per_point,
..
} = self.egui_ctx.run(raw_input, |ctx| {
self.egui_app.build_ui(ctx);
});
self.renderer.update_textures(textures_delta).unwrap();
self.egui_winit
.handle_platform_output(&self.window, platform_output);
let clipped_primitives = self.egui_ctx.tessellate(shapes, pixels_per_point);
// --- Render Frame ---
match self.renderer.render_frame() {
match self
.renderer
.render_frame(pixels_per_point, &clipped_primitives)
{
Ok(_) => {
self.window.request_redraw();
}
@ -450,6 +492,7 @@ struct Args {
// --- Entry Point ---
fn main() -> Result<(), Box<dyn Error>> {
color_eyre::install()?;
let args = Args::parse();
let fmt_layer = tracing_subscriber::fmt::layer()

View file

@ -4,6 +4,7 @@ use std::{
};
use ash::vk;
use egui::{ClippedPrimitive, TextureId, TexturesDelta};
use egui_ash_renderer::{DynamicRendering, Options, Renderer as EguiRenderer};
use gfx_hal::{
device::Device, error::GfxHalError, queue::Queue, surface::Surface, swapchain::Swapchain,
@ -61,6 +62,7 @@ struct FrameData {
command_buffer: vk::CommandBuffer,
image_available_semaphore: Semaphore,
render_finished_semaphore: Semaphore,
textures_to_free: Option<Vec<TextureId>>,
in_flight_fence: Fence,
}
@ -140,7 +142,10 @@ impl Renderer {
color_attachment_format: swapchain.format().format,
depth_attachment_format: Some(depth_format),
},
Options::default(),
Options {
srgb_framebuffer: true,
..Default::default()
},
)?;
Ok(Self {
@ -178,7 +183,32 @@ impl Renderer {
}
}
pub fn render_frame(&mut self) -> Result<(), RendererError> {
pub fn update_textures(&mut self, textures_delta: TexturesDelta) -> Result<(), RendererError> {
tracing::trace!("Updating EGUI textures!");
if !textures_delta.free.is_empty() {
self.frames_data[self.current_frame].textures_to_free =
Some(textures_delta.free.clone());
}
if !textures_delta.set.is_empty() {
self.egui_renderer
.set_textures(
self.device.get_graphics_queue().handle(),
self.frames_data[self.current_frame].command_pool,
textures_delta.set.as_slice(),
)
.expect("Failed to update texture");
}
Ok(())
}
pub fn render_frame(
&mut self,
pixels_per_point: f32,
clipped_primitives: &[ClippedPrimitive],
) -> Result<(), RendererError> {
// --- Handle Resize ---
if self.window_resized {
self.window_resized = false;
@ -190,7 +220,7 @@ impl Renderer {
// --- Wait for Previous Frame ---
let frame_index = self.current_frame;
let frame_data = &self.frames_data[frame_index];
let frame_data = &mut self.frames_data[frame_index];
frame_data.in_flight_fence.wait(None)?; // Wait indefinitely
@ -219,6 +249,11 @@ impl Renderer {
// --- Reset Fence (only after successful acquisition) ---
frame_data.in_flight_fence.reset()?;
if let Some(textures) = frame_data.textures_to_free.take() {
tracing::debug!("Freeing EGUI Textures");
self.egui_renderer.free_textures(&textures)?;
}
// --- Record Command Buffer ---
unsafe {
// Need unsafe for Vulkan commands
@ -344,6 +379,15 @@ impl Renderer {
self.device.raw().cmd_draw(command_buffer, 3, 1, 0, 0);
}
tracing::trace!("Rendering EGUI");
self.egui_renderer.cmd_draw(
command_buffer,
self.swapchain_extent,
pixels_per_point,
clipped_primitives,
)?;
tracing::trace!("Rendered EGUI");
// --- End Dynamic Rendering ---
unsafe {
// Need unsafe for Vulkan commands
@ -862,6 +906,7 @@ impl Renderer {
};
frames_data.push(FrameData {
textures_to_free: None,
command_pool,
command_buffer, // Stays allocated, just reset/rerecorded
image_available_semaphore,