1mod state;
6
7use astrelis_core::profiling::profile_function;
8use astrelis_render::{Frame as RenderFrame, RenderWindow};
9use astrelis_winit::event::EventBatch;
10use state::State;
11
12pub use egui::{
14 self, Align, Align2, Color32, Context as EguiContext, CornerRadius, FontFamily, FontId, Frame,
15 Id, Key, Label, Layout, Margin, Modifiers, Pos2, Rect, Response, RichText, Sense, Slider,
16 Stroke, Style, TextEdit, TextStyle, Ui, Vec2, Visuals, Widget,
17};
18pub use state::EventResponse;
19
20pub struct Egui {
21 context: egui::Context,
22 renderer: egui_wgpu::Renderer,
23 state: State,
24 full_output: Option<egui::FullOutput>,
25}
26
27impl Egui {
28 pub fn new(window: &RenderWindow, graphics_ctx: &astrelis_render::GraphicsContext) -> Self {
29 let context = egui::Context::default();
30 let id = context.viewport_id();
31
32 let visuals = egui::Visuals::dark();
33 context.set_visuals(visuals);
34
35 let state = State::new(context.clone(), id, None, None);
36
37 let renderer = egui_wgpu::Renderer::new(
38 graphics_ctx.device(),
39 window.context().surface_config().format,
40 egui_wgpu::RendererOptions {
41 msaa_samples: 1,
42 depth_stencil_format: None,
43 dithering: false,
44 ..Default::default()
45 },
46 );
47
48 Self {
49 context,
50 renderer,
51 state,
52 full_output: None,
53 }
54 }
55
56 pub fn ui(&mut self, window: &RenderWindow, gui: impl FnMut(&egui::Context)) {
58 profile_function!();
59 let raw_input = self.state.take_input(window);
60 self.full_output.replace(self.context.run(raw_input, gui));
61 }
62
63 pub fn render(&mut self, frame: &RenderFrame<'_>) {
67 profile_function!();
68
69 if self.full_output.is_none() {
70 return;
71 }
72
73 let full_output = self.full_output.take().unwrap();
74 let _ = full_output.platform_output;
76
77 let device = frame.device();
78 let queue = frame.queue();
79
80 let tris = self
81 .context
82 .tessellate(full_output.shapes, full_output.pixels_per_point);
83
84 for (id, image_delta) in &full_output.textures_delta.set {
85 self.renderer
86 .update_texture(device, queue, *id, image_delta);
87 }
88
89 let (width, height) = frame.size();
90 let screen_descriptor = egui_wgpu::ScreenDescriptor {
91 size_in_pixels: [width, height],
92 pixels_per_point: full_output.pixels_per_point,
93 };
94
95 let mut encoder = frame.create_encoder(Some("Egui Buffer Update"));
97 self.renderer
98 .update_buffers(device, queue, &mut encoder, &tris, &screen_descriptor);
99
100 {
102 let surface_view = frame.surface_view();
103 let mut rpass = encoder
104 .begin_render_pass(&wgpu::RenderPassDescriptor {
105 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
106 view: surface_view,
107 resolve_target: None,
108 ops: wgpu::Operations {
109 load: wgpu::LoadOp::Load,
110 store: wgpu::StoreOp::Store,
111 },
112 depth_slice: None,
113 })],
114 depth_stencil_attachment: None,
115 label: Some("Egui Render Pass"),
116 timestamp_writes: None,
117 occlusion_query_set: None,
118 })
119 .forget_lifetime();
120
121 self.renderer.render(&mut rpass, &tris, &screen_descriptor);
122 }
123
124 frame.add_command_buffer(encoder.finish());
126
127 for x in &full_output.textures_delta.free {
128 self.renderer.free_texture(x)
129 }
130 }
131
132 pub fn handle_events(&mut self, window: &RenderWindow, events: &mut EventBatch) -> bool {
134 profile_function!();
135 let mut any_consumed = false;
136
137 events.dispatch(|event| {
138 let response = self.state.on_event(window, event);
139 if response.consumed {
140 any_consumed = true;
141 }
142 let mut status = astrelis_winit::event::HandleStatus::empty();
143 if response.repaint || response.consumed {
144 status |= astrelis_winit::event::HandleStatus::HANDLED;
145 }
146 if response.consumed {
147 status |= astrelis_winit::event::HandleStatus::CONSUMED;
148 }
149 status
150 });
151
152 any_consumed
153 }
154
155 pub fn context(&self) -> &egui::Context {
157 &self.context
158 }
159
160 pub fn register_wgpu_texture(
163 &mut self,
164 device: &wgpu::Device,
165 texture: &wgpu::TextureView,
166 filter: wgpu::FilterMode,
167 ) -> egui::TextureId {
168 self.renderer
169 .register_native_texture(device, texture, filter)
170 }
171
172 pub fn unregister_texture(&mut self, id: egui::TextureId) {
174 self.renderer.free_texture(&id);
175 }
176}