use crate::render::BackendState;
use egui::{ClippedPrimitive, Context, FullOutput, TexturesDelta};
use egui_wgpu::{Renderer as EguiRenderer, RendererOptions, ScreenDescriptor};
use egui_winit::{self, EventResponse};
use glam::UVec2;
use wgpu::{CommandEncoder, Device, Queue, TextureFormat, TextureView};
use winit::{event::WindowEvent, window::Window};
pub struct EguiSystem {
ctx: Context,
state: egui_winit::State,
renderer: EguiRenderer,
screen_desc: ScreenDescriptor,
pending: Option<(TexturesDelta, Vec<ClippedPrimitive>)>,
pending_free: Vec<egui::TextureId>,
}
impl EguiSystem {
pub fn new(window: &Window, backend: &BackendState) -> Self {
let ctx = Context::default();
let viewport_id = egui::ViewportId::ROOT;
let native_pixels_per_point = Some(window.scale_factor() as f32);
let theme = window.theme();
let max_texture_side = None;
let state = egui_winit::State::new(
ctx.clone(),
viewport_id,
window,
native_pixels_per_point,
theme,
max_texture_side,
);
let color_format: TextureFormat = backend.surface_view_format;
let renderer = EguiRenderer::new(&backend.device, color_format, RendererOptions::default());
let size_in_pixels = [backend.surface_config.width, backend.surface_config.height];
let pixels_per_point = egui_winit::pixels_per_point(&ctx, window);
let screen_desc = ScreenDescriptor {
size_in_pixels,
pixels_per_point,
};
Self {
ctx,
state,
renderer,
screen_desc,
pending: None,
pending_free: Vec::new(),
}
}
pub fn context(&self) -> &Context {
&self.ctx
}
pub fn on_window_event(&mut self, window: &Window, event: &WindowEvent) -> EventResponse {
self.state.on_window_event(window, event)
}
pub fn begin_frame(&mut self, window: &Window) -> &Context {
let raw_input = self.state.take_egui_input(window);
self.ctx.begin_pass(raw_input);
&self.ctx
}
pub fn end_frame(&mut self, window: &Window) {
let FullOutput {
platform_output,
textures_delta,
shapes,
..
} = self.ctx.end_pass();
self.state.handle_platform_output(window, platform_output);
let pixels_per_point = egui_winit::pixels_per_point(&self.ctx, window);
let clipped_primitives = self.ctx.tessellate(shapes, pixels_per_point);
self.pending = Some((textures_delta, clipped_primitives));
}
pub fn paint(
&mut self,
window: &Window,
device: &Device,
queue: &Queue,
encoder: &mut CommandEncoder,
target_view: &TextureView,
window_size: UVec2,
) {
let Some((textures_delta, primitives)) = self.pending.take() else {
return;
};
self.screen_desc.size_in_pixels = [window_size.x, window_size.y];
self.screen_desc.pixels_per_point = egui_winit::pixels_per_point(&self.ctx, window);
for (id, delta) in &textures_delta.set {
self.renderer.update_texture(device, queue, *id, delta);
}
self.pending_free.extend(textures_delta.free);
let _ =
self.renderer
.update_buffers(device, queue, encoder, &primitives, &self.screen_desc);
{
let pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
label: Some("egui Render Pass"),
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
view: target_view,
resolve_target: None,
ops: wgpu::Operations {
load: wgpu::LoadOp::Load,
store: wgpu::StoreOp::Store,
},
depth_slice: None,
})],
depth_stencil_attachment: None,
occlusion_query_set: None,
timestamp_writes: None,
});
self.renderer
.render(&mut pass.forget_lifetime(), &primitives, &self.screen_desc);
}
}
pub fn after_submit(&mut self) {
for id in self.pending_free.drain(..) {
self.renderer.free_texture(&id);
}
}
}