khora_infra/ui/egui/
overlay.rs1use super::renderer::{EguiRenderState, EguiWgpuRenderer};
23use crate::graphics::wgpu::context::WgpuGraphicsContext;
24use egui::ViewportId;
25use khora_core::ui::editor_overlay::{EditorOverlay, OverlayError, OverlayScreenDescriptor};
26use std::any::Any;
27use std::sync::{Arc, Mutex};
28
29pub struct EguiFrameRenderState {
34 pub graphics_context: Arc<Mutex<WgpuGraphicsContext>>,
36 pub encoder: Option<wgpu::CommandEncoder>,
38 pub target_view: wgpu::TextureView,
40 pub width_px: u32,
42 pub height_px: u32,
44}
45
46pub struct EguiOverlay {
51 ctx: egui::Context,
52 winit_state: egui_winit::State,
53 renderer: EguiWgpuRenderer,
54 screen: OverlayScreenDescriptor,
56}
57
58impl EguiOverlay {
59 pub fn new(
67 event_loop: &winit::event_loop::ActiveEventLoop,
68 surface_format: wgpu::TextureFormat,
69 device: &wgpu::Device,
70 shader_source: &str,
71 ) -> Self {
72 let ctx = egui::Context::default();
73 let winit_state = egui_winit::State::new(
74 ctx.clone(),
75 ViewportId::ROOT,
76 event_loop,
77 Some(1.0), None, None, );
81
82 let mut renderer = EguiWgpuRenderer::new(surface_format);
83 renderer.initialize(device, shader_source);
84
85 Self {
86 ctx,
87 winit_state,
88 renderer,
89 screen: OverlayScreenDescriptor {
90 width_px: 1,
91 height_px: 1,
92 scale_factor: 1.0,
93 },
94 }
95 }
96
97 pub fn context(&self) -> egui::Context {
101 self.ctx.clone()
102 }
103
104 pub fn register_viewport_texture(
109 &mut self,
110 device: &wgpu::Device,
111 view: &wgpu::TextureView,
112 ) -> egui::TextureId {
113 self.renderer.register_external_texture(device, view)
114 }
115
116 pub fn update_viewport_texture(
119 &mut self,
120 device: &wgpu::Device,
121 id: egui::TextureId,
122 view: &wgpu::TextureView,
123 ) {
124 self.renderer.update_external_texture(device, id, view);
125 }
126}
127
128impl EditorOverlay for EguiOverlay {
129 fn handle_window_event(&mut self, window: &dyn Any, event: &dyn Any) -> bool {
130 let Some(winit_window) = window.downcast_ref::<winit::window::Window>() else {
131 return false;
132 };
133 let Some(winit_event) = event.downcast_ref::<winit::event::WindowEvent>() else {
134 return false;
135 };
136
137 let response = self.winit_state.on_window_event(winit_window, winit_event);
138 response.consumed
139 }
140
141 fn begin_frame(&mut self, window: &dyn Any, screen: OverlayScreenDescriptor) {
142 self.screen = screen;
143 if let Some(winit_window) = window.downcast_ref::<winit::window::Window>() {
144 let raw_input = self.winit_state.take_egui_input(winit_window);
145 self.ctx.begin_pass(raw_input);
146 } else {
147 log::warn!("EguiOverlay: begin_frame called with non-winit window");
148 }
149 }
150
151 fn ui_context(&self) -> &dyn Any {
152 &self.ctx
153 }
154
155 fn end_frame_and_render(&mut self, render_state: &mut dyn Any) -> Result<(), OverlayError> {
156 let output = self.ctx.end_pass();
157
158 let pixels_per_point = output.pixels_per_point;
160 let primitives = self.ctx.tessellate(output.shapes, pixels_per_point);
161 let textures_delta = output.textures_delta;
162
163 let state = render_state
165 .downcast_mut::<EguiFrameRenderState>()
166 .ok_or_else(|| OverlayError("Expected EguiFrameRenderState".to_string()))?;
167
168 let gc = state
170 .graphics_context
171 .lock()
172 .map_err(|_| OverlayError("Failed to lock graphics context".to_string()))?;
173
174 self.renderer
176 .update_textures(&gc.device, &gc.queue, &textures_delta);
177
178 let encoder = state
180 .encoder
181 .as_mut()
182 .ok_or_else(|| OverlayError("No command encoder available".to_string()))?;
183
184 let mut egui_render_state = EguiRenderState {
186 device: &gc.device,
187 queue: &gc.queue,
188 encoder,
189 target_view: &state.target_view,
190 width_px: self.screen.width_px,
191 height_px: self.screen.height_px,
192 };
193
194 self.renderer
195 .render(&mut egui_render_state, &primitives, pixels_per_point);
196
197 Ok(())
198 }
199
200 fn wants_pointer_input(&self) -> bool {
201 self.ctx.egui_wants_pointer_input()
202 }
203
204 fn wants_keyboard_input(&self) -> bool {
205 self.ctx.egui_wants_keyboard_input()
206 }
207
208 fn as_any(&self) -> &dyn Any {
209 self
210 }
211
212 fn as_any_mut(&mut self) -> &mut dyn Any {
213 self
214 }
215}
216
217unsafe impl Send for EguiOverlay {}
221unsafe impl Sync for EguiOverlay {}