use crate::render::wgpu::rendergraph::{PassExecutionContext, PassNode};
const UI_DEPTH_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::Depth32Float;
pub struct EguiPass {
renderer: egui_wgpu::Renderer,
screen_descriptor: egui_wgpu::ScreenDescriptor,
paint_jobs: Vec<egui::ClippedPrimitive>,
depth_texture: Option<wgpu::Texture>,
depth_view: Option<wgpu::TextureView>,
depth_size: (u32, u32),
}
impl EguiPass {
pub fn new(device: &wgpu::Device, surface_format: wgpu::TextureFormat) -> Self {
let renderer = egui_wgpu::Renderer::new(
device,
surface_format,
egui_wgpu::RendererOptions {
depth_stencil_format: Some(UI_DEPTH_FORMAT),
msaa_samples: 1,
..Default::default()
},
);
Self {
renderer,
screen_descriptor: egui_wgpu::ScreenDescriptor {
size_in_pixels: [1, 1],
pixels_per_point: 1.0,
},
paint_jobs: Vec::new(),
depth_texture: None,
depth_view: None,
depth_size: (0, 0),
}
}
fn ensure_depth_texture(&mut self, device: &wgpu::Device, width: u32, height: u32) {
if self.depth_size == (width, height) && self.depth_texture.is_some() {
return;
}
let texture = device.create_texture(&wgpu::TextureDescriptor {
label: Some("UI Depth Texture"),
size: wgpu::Extent3d {
width,
height,
depth_or_array_layers: 1,
},
mip_level_count: 1,
sample_count: 1,
dimension: wgpu::TextureDimension::D2,
format: UI_DEPTH_FORMAT,
usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
view_formats: &[],
});
let view = texture.create_view(&wgpu::TextureViewDescriptor::default());
self.depth_texture = Some(texture);
self.depth_view = Some(view);
self.depth_size = (width, height);
}
pub fn update_textures(
&mut self,
device: &wgpu::Device,
queue: &wgpu::Queue,
textures_delta: &egui::TexturesDelta,
) {
for (id, image_delta) in &textures_delta.set {
self.renderer
.update_texture(device, queue, *id, image_delta);
}
for id in &textures_delta.free {
self.renderer.free_texture(id);
}
}
pub fn set_paint_jobs(&mut self, paint_jobs: Vec<egui::ClippedPrimitive>) {
self.paint_jobs = paint_jobs;
}
pub fn set_screen_descriptor(&mut self, screen_descriptor: egui_wgpu::ScreenDescriptor) {
self.screen_descriptor = screen_descriptor;
}
pub fn register_texture(
&mut self,
device: &wgpu::Device,
texture_view: &wgpu::TextureView,
) -> egui::TextureId {
self.renderer
.register_native_texture(device, texture_view, wgpu::FilterMode::Linear)
}
pub fn update_native_texture(
&mut self,
device: &wgpu::Device,
texture_id: egui::TextureId,
texture_view: &wgpu::TextureView,
) {
self.renderer.update_egui_texture_from_wgpu_texture(
device,
texture_view,
wgpu::FilterMode::Linear,
texture_id,
);
}
}
impl PassNode<crate::ecs::world::World> for EguiPass {
fn name(&self) -> &str {
"egui_pass"
}
fn reads(&self) -> Vec<&str> {
vec![]
}
fn writes(&self) -> Vec<&str> {
vec![]
}
fn reads_writes(&self) -> Vec<&str> {
vec!["swapchain"]
}
fn prepare(
&mut self,
device: &wgpu::Device,
queue: &wgpu::Queue,
_world: &crate::ecs::world::World,
) {
if !self.paint_jobs.is_empty() {
let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
label: Some("Egui Prepare Encoder"),
});
self.renderer.update_buffers(
device,
queue,
&mut encoder,
&self.paint_jobs,
&self.screen_descriptor,
);
queue.submit(std::iter::once(encoder.finish()));
}
}
fn execute<'r, 'e>(
&mut self,
context: PassExecutionContext<'r, 'e, crate::ecs::world::World>,
) -> crate::render::wgpu::rendergraph::Result<
Vec<crate::render::wgpu::rendergraph::SubGraphRunCommand<'r>>,
> {
if !context.is_pass_enabled() {
return Ok(context.into_sub_graph_commands());
}
let (swapchain_view, swapchain_load, swapchain_store) =
context.get_color_attachment("swapchain")?;
let [width, height] = self.screen_descriptor.size_in_pixels;
self.ensure_depth_texture(context.device, width, height);
if !self.paint_jobs.is_empty() {
let depth_stencil_attachment =
self.depth_view
.as_ref()
.map(|view| wgpu::RenderPassDepthStencilAttachment {
view,
depth_ops: Some(wgpu::Operations {
load: wgpu::LoadOp::Clear(1.0),
store: wgpu::StoreOp::Discard,
}),
stencil_ops: None,
});
self.renderer.render(
&mut context
.encoder
.begin_render_pass(&wgpu::RenderPassDescriptor {
label: Some("Egui Pass"),
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
view: swapchain_view,
resolve_target: None,
ops: wgpu::Operations {
load: swapchain_load,
store: swapchain_store,
},
depth_slice: None,
})],
depth_stencil_attachment,
timestamp_writes: None,
occlusion_query_set: None,
})
.forget_lifetime(),
&self.paint_jobs,
&self.screen_descriptor,
);
}
Ok(context.into_sub_graph_commands())
}
}