1use {
2 crate::{
3 demo::Graphics,
4 graphics::{
5 streaming_renderer::{
6 Mesh, StreamingRenderer, Texture, TextureAtlas, TextureLoader,
7 TrianglesMesh, Vertex,
8 },
9 vulkan::Frame,
10 },
11 unwrap_here,
12 },
13 anyhow::Result,
14 ash::vk,
15 egui::{epaint::Primitive, ImageData, ViewportInfo},
16 egui_winit::EventResponse,
17 nalgebra::Matrix4,
18 std::{collections::HashMap, sync::Arc},
19 winit::{dpi::PhysicalSize, event::WindowEvent, window::Window},
20};
21
22pub struct EguiPainter {
27 state: egui_winit::State,
28 projection: Matrix4<f32>,
29 used_meshes: Vec<TrianglesMesh>,
30 free_meshes: Vec<TrianglesMesh>,
31 egui_textures: HashMap<egui::TextureId, i32>,
32 texture_loader: TextureLoader,
33 atlas: TextureAtlas,
34 renderer: StreamingRenderer,
35}
36
37fn ortho_projection(
41 pixels_per_point: f32,
42 width: f32,
43 height: f32,
44) -> Matrix4<f32> {
45 let w = width / pixels_per_point;
46 let h = height / pixels_per_point;
47 #[rustfmt::skip]
48 let projection = Matrix4::new(
49 2.0 / w, 0.0, 0.0, -1.0,
50 0.0, 2.0 / h, 0.0, -1.0,
51 0.0, 0.0, 1.0, 0.0,
52 0.0, 0.0, 0.0, 1.0,
53 );
54 projection
55}
56
57fn egui_rect_to_vk_rect(pixels_per_point: f32, rect: egui::Rect) -> vk::Rect2D {
60 vk::Rect2D {
61 offset: vk::Offset2D {
62 x: (pixels_per_point * rect.min.x) as i32,
63 y: (pixels_per_point * rect.min.y) as i32,
64 },
65 extent: vk::Extent2D {
66 width: (pixels_per_point * rect.width()) as u32,
67 height: (pixels_per_point * rect.height()) as u32,
68 },
69 }
70}
71
72impl EguiPainter {
73 pub fn new(graphics: &Graphics, window: &Window) -> Result<Self> {
74 let state = egui_winit::State::new(
75 egui::Context::default(),
76 egui::ViewportId::ROOT,
77 window,
78 Some(window.scale_factor() as f32),
79 None,
80 None,
81 );
82 let pixels_per_point =
83 egui_winit::pixels_per_point(state.egui_ctx(), window);
84 let PhysicalSize { width, height } = window.inner_size();
85 let atlas = unwrap_here!(
86 "Create EGUI painter texture atlas",
87 TextureAtlas::new(&graphics.vulkan)
88 );
89 let texture_loader = unwrap_here!(
90 "Create EGUI texture loader",
91 TextureLoader::new(graphics.vulkan.clone())
92 );
93 let renderer = unwrap_here!(
94 "Create EGUI painter streaming renderer instance",
95 StreamingRenderer::new(
96 &graphics.vulkan,
97 graphics.swapchain.format(),
98 &graphics.frames_in_flight,
99 &atlas
100 )
101 );
102 Ok(Self {
103 state,
104 projection: ortho_projection(
105 pixels_per_point,
106 width as f32,
107 height as f32,
108 ),
109 used_meshes: vec![],
110 free_meshes: vec![],
111 egui_textures: HashMap::new(),
112 texture_loader,
113 atlas,
114 renderer,
115 })
116 }
117
118 pub fn on_window_event(
121 &mut self,
122 window: &Window,
123 event: &WindowEvent,
124 ) -> EventResponse {
125 if let &WindowEvent::Resized(PhysicalSize { width, height }) = event {
126 self.projection = ortho_projection(
127 egui_winit::pixels_per_point(self.state.egui_ctx(), window),
128 width as f32,
129 height as f32,
130 );
131 }
132 self.state.on_window_event(window, event)
133 }
134
135 pub fn rebuild_swapchain_resources(
138 &mut self,
139 gfx: &Graphics,
140 ) -> Result<()> {
141 if self.renderer.image_format() == gfx.swapchain.format() {
142 return Ok(()); }
144
145 unwrap_here!(
146 "Wait for frames to finish before rebuilding EGUI painter resources",
147 gfx.frames_in_flight.wait_for_all_frames_to_complete()
148 );
149
150 self.free_meshes.clear();
152 self.used_meshes.clear();
153
154 self.renderer = unwrap_here!(
155 "Rebuild streaming renderer for the EGUI painter",
156 StreamingRenderer::new(
157 &gfx.vulkan,
158 gfx.swapchain.format(),
159 &gfx.frames_in_flight,
160 &self.atlas
161 )
162 );
163
164 Ok(())
166 }
167
168 pub unsafe fn draw(&mut self, gfx: &Graphics, frame: &Frame) -> Result<()> {
178 self.renderer
179 .bind_texture_atlas(&gfx.vulkan, frame, &self.atlas);
180 unwrap_here!(
181 "Prepare EGUI clipped primitive meshes",
182 self.renderer.prepare_meshes(
183 &gfx.vulkan,
184 frame,
185 &self
186 .used_meshes
187 .iter()
188 .map(|mesh| mesh as &dyn Mesh)
189 .collect::<Vec<_>>(),
190 )
191 );
192 unwrap_here!(
193 "Write EGUI draw commands to frame command buffer",
194 self.renderer.write_draw_commands(&gfx.vulkan, frame)
195 );
196 Ok(())
197 }
198
199 pub fn run(
200 &mut self,
201 gfx: &Graphics,
202 window: &Window,
203 run_ui: impl FnMut(&egui::Context),
204 ) -> Result<()> {
205 let raw_input = {
206 let mut viewport_info = ViewportInfo::default();
207 egui_winit::update_viewport_info(
208 &mut viewport_info,
209 self.state.egui_ctx(),
210 window,
211 false,
212 );
213 let viewport_id = self.state.egui_input().viewport_id;
214 self.state
215 .egui_input_mut()
216 .viewports
217 .insert(viewport_id, viewport_info);
218
219 let PhysicalSize { width, height } = window.inner_size();
220 self.state.egui_input_mut().screen_rect = Some(egui::Rect {
221 min: egui::pos2(0.0, 0.0),
222 max: egui::pos2(width as f32, height as f32),
223 });
224
225 self.state.take_egui_input(window)
226 };
227 let full_output = self.state.egui_ctx().run(raw_input, run_ui);
228 self.state
229 .handle_platform_output(window, full_output.platform_output);
230
231 for (egui_texture_id, delta) in &full_output.textures_delta.set {
233 let needs_replaced = if let Some(existing_texture_id) =
234 self.egui_textures.get(egui_texture_id)
235 {
236 let existing_texture =
239 self.atlas.get_texture(*existing_texture_id);
240
241 (existing_texture.width() as usize) < delta.image.width()
242 || (existing_texture.height() as usize)
243 < delta.image.height()
244 } else {
245 true };
247
248 if needs_replaced {
250 log::info!("Creating egui texture: {:?}", egui_texture_id);
252
253 let texture = unwrap_here!(
254 "Create EGUI texture",
255 Texture::builder()
256 .ctx(&gfx.vulkan)
257 .dimensions((
258 delta.image.width() as u32,
259 delta.image.height() as u32,
260 ))
261 .format(vk::Format::R8G8B8A8_UNORM)
262 .memory_property_flags(
263 vk::MemoryPropertyFlags::DEVICE_LOCAL,
264 )
265 .image_usage_flags(
266 vk::ImageUsageFlags::TRANSFER_DST
267 | vk::ImageUsageFlags::SAMPLED,
268 )
269 .build()
270 );
271
272 self.egui_textures.insert(
273 *egui_texture_id,
274 self.atlas.add_texture(&gfx.vulkan, Arc::new(texture)),
275 );
276 }
277 }
278
279 for (egui_texture_id, delta) in full_output.textures_delta.set {
282 let texture_id = if let Some(texture_id) =
283 self.egui_textures.get(&egui_texture_id)
284 {
285 *texture_id
286 } else {
287 continue;
288 };
289 let texture = self.atlas.get_texture(texture_id);
290 let ImageData::Color(color) = delta.image;
291 let pixels = color
292 .pixels
293 .iter()
294 .flat_map(|color| color.to_srgba_unmultiplied())
295 .collect::<Vec<u8>>();
296 let offset = if let Some([w, h]) = delta.pos {
297 [w as u32, h as u32]
298 } else {
299 [0, 0]
300 };
301 let size = {
302 let [w, h] = color.size;
303 [w as u32, h as u32]
304 };
305 unwrap_here!(
306 "Update image data in EGUI texture",
307 self.texture_loader.tex_sub_image(
308 &gfx.vulkan,
309 texture,
310 &pixels,
311 offset,
312 size,
313 )
314 );
315 }
316
317 let pixels_per_point = full_output.pixels_per_point;
318
319 self.free_meshes.extend(self.used_meshes.drain(0..));
321
322 let clipped_primitives = self.state.egui_ctx().tessellate(
324 full_output.shapes,
325 self.state.egui_ctx().pixels_per_point(),
326 );
327
328 for clipped_primitive in &clipped_primitives {
329 let mut triangles_mesh = self.get_next_free_mesh();
330 triangles_mesh.set_scissor(egui_rect_to_vk_rect(
331 pixels_per_point,
332 clipped_primitive.clip_rect,
333 ));
334 match &clipped_primitive.primitive {
335 Primitive::Mesh(mesh) => {
336 let texture_id = *self
337 .egui_textures
338 .get(&mesh.texture_id)
339 .unwrap_or(&-1);
340 triangles_mesh.indexed_triangles(
341 mesh.vertices.iter().map(|egui_vertex| {
342 Vertex::new(
343 [egui_vertex.pos.x, egui_vertex.pos.y, 0.0],
344 [egui_vertex.uv.x, egui_vertex.uv.y],
345 egui_vertex.color.to_normalized_gamma_f32(),
346 texture_id,
347 )
348 }),
349 mesh.indices.iter().copied(),
350 );
351 }
352 Primitive::Callback(_) => {
353 log::warn!(
354 "Callbacks unsupported by this backend (currently)"
355 );
356 }
357 }
358 self.used_meshes.push(triangles_mesh);
359 }
360
361 Ok(())
362 }
363
364 fn get_next_free_mesh(&mut self) -> TrianglesMesh {
365 let mut mesh = self.free_meshes.pop().unwrap_or_else(|| {
366 TrianglesMesh::new(1_000, self.renderer.default_material().clone())
367 });
368 mesh.clear();
369 mesh.set_transform(self.projection);
370 mesh
371 }
372}