use egui::{ClippedPrimitive, Context};
use winit::event::WindowEvent;
use crate::{modules::Schedule, modules::WinitMain, utils::Timing, Dependencies, Handle, Module};
use self::platform::{Platform, PlatformDescriptor};
use super::{renderer::SdrSurfaceRenderer, GraphicsContext, Prepare, Renderer, Scheduler, Time};
pub mod platform;
pub struct Egui {
pub platform: platform::Platform,
pub renderer: egui_wgpu::Renderer,
paint_jobs: Vec<ClippedPrimitive>,
textures_delta: egui::TexturesDelta,
deps: Deps,
}
#[derive(Debug, Dependencies)]
pub struct Deps {
renderer: Handle<Renderer>,
ctx: Handle<GraphicsContext>,
winit: Handle<WinitMain>,
time: Handle<Time>,
scheduler: Handle<Scheduler>,
}
impl Module for Egui {
type Config = ();
type Dependencies = Deps;
fn new(_config: Self::Config, deps: Self::Dependencies) -> anyhow::Result<Self> {
let platform = Platform::new(PlatformDescriptor {
physical_size: deps.ctx.size,
pixels_per_point: 1.0 / deps.ctx.scale_factor() as f32, font_definitions: Default::default(),
style: Default::default(),
});
let renderer = egui_wgpu::Renderer::new(&deps.ctx.device, deps.ctx.surface_format, None, 1);
Ok(Egui {
platform,
renderer,
textures_delta: Default::default(),
paint_jobs: Vec::new(),
deps,
})
}
fn intialize(handle: Handle<Self>) -> anyhow::Result<()> {
let mut winit = handle.deps.winit;
winit.register_window_event_listener(handle, Self::receive_window_event);
let mut renderer = handle.deps.renderer;
renderer.register_prepare(handle);
renderer.register_surface_renderer(handle, Timing::LATE);
let mut scheduler = handle.deps.scheduler;
scheduler.register(handle, Schedule::Update, Timing::EARLY, Self::begin_frame);
Ok(())
}
}
impl Prepare for Egui {
fn prepare(
&mut self,
device: &wgpu::Device,
queue: &wgpu::Queue,
encoder: &mut wgpu::CommandEncoder,
) {
let output = self.platform.end_frame();
self.paint_jobs.clear();
for id in self.textures_delta.free.drain(..) {
self.renderer.free_texture(&id)
}
self.textures_delta = output.textures_delta;
for (id, image_delta) in self.textures_delta.set.iter() {
self.renderer
.update_texture(device, queue, *id, image_delta);
}
self.paint_jobs = self
.context()
.tessellate(output.shapes, output.pixels_per_point);
let screen_descriptor = self.platform.screen_descriptor();
self.renderer
.update_buffers(device, queue, encoder, &self.paint_jobs, &screen_descriptor);
}
}
impl SdrSurfaceRenderer for Egui {
fn render<'e>(&'e self, encoder: &'e mut wgpu::CommandEncoder, view: &wgpu::TextureView) {
let color_attachment = wgpu::RenderPassColorAttachment {
view,
resolve_target: None,
ops: wgpu::Operations {
load: wgpu::LoadOp::Load,
store: wgpu::StoreOp::Discard,
},
};
let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
label: Some("Renderpass"),
color_attachments: &[Some(color_attachment)],
depth_stencil_attachment: None,
occlusion_query_set: None,
timestamp_writes: None,
});
let screen_descriptor = self.platform.screen_descriptor();
self.renderer
.render(&mut render_pass, &self.paint_jobs, &screen_descriptor);
}
}
impl Egui {
pub fn context(&self) -> Context {
self.platform.context()
}
pub fn receive_window_event(&mut self, event: &WindowEvent) {
self.platform.handle_event(event);
}
pub fn begin_frame(&mut self) {
let total_elapsed_seconds = self.deps.time.total().as_secs_f64();
self.platform.begin_frame(total_elapsed_seconds);
}
}