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