149 lines
4.8 KiB
Rust
149 lines
4.8 KiB
Rust
![]() |
use crate::renderer;
|
||
|
|
||
|
/// Everything you need to paint egui with [`wgpu`] on [`winit`].
|
||
|
///
|
||
|
/// Alternatively you can use [`crate::renderer`] directly.
|
||
|
pub struct Painter {
|
||
|
device: wgpu::Device,
|
||
|
queue: wgpu::Queue,
|
||
|
surface_config: wgpu::SurfaceConfiguration,
|
||
|
surface: wgpu::Surface,
|
||
|
egui_rpass: renderer::RenderPass,
|
||
|
}
|
||
|
|
||
|
impl Painter {
|
||
|
/// Creates a [`wgpu`] surface for the given window, and things required to render egui onto it.
|
||
|
///
|
||
|
/// # Safety
|
||
|
/// The given `window` must outlive the returned [`Painter`].
|
||
|
pub unsafe fn new(window: &winit::window::Window, msaa_samples: u32) -> Self {
|
||
|
let instance = wgpu::Instance::new(wgpu::Backends::PRIMARY | wgpu::Backends::GL);
|
||
|
let surface = instance.create_surface(&window);
|
||
|
|
||
|
let adapter = pollster::block_on(instance.request_adapter(&wgpu::RequestAdapterOptions {
|
||
|
power_preference: wgpu::PowerPreference::HighPerformance,
|
||
|
compatible_surface: Some(&surface),
|
||
|
force_fallback_adapter: false,
|
||
|
}))
|
||
|
.unwrap();
|
||
|
|
||
|
let (device, queue) = pollster::block_on(adapter.request_device(
|
||
|
&wgpu::DeviceDescriptor {
|
||
|
features: wgpu::Features::default(),
|
||
|
limits: wgpu::Limits::default(),
|
||
|
label: None,
|
||
|
},
|
||
|
None,
|
||
|
))
|
||
|
.unwrap();
|
||
|
|
||
|
let size = window.inner_size();
|
||
|
let surface_format = surface.get_preferred_format(&adapter).unwrap();
|
||
|
let surface_config = wgpu::SurfaceConfiguration {
|
||
|
usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
|
||
|
format: surface_format,
|
||
|
width: size.width as u32,
|
||
|
height: size.height as u32,
|
||
|
present_mode: wgpu::PresentMode::Fifo, // TODO: make vsync configurable
|
||
|
};
|
||
|
surface.configure(&device, &surface_config);
|
||
|
|
||
|
let egui_rpass = renderer::RenderPass::new(&device, surface_format, msaa_samples);
|
||
|
|
||
|
Self {
|
||
|
device,
|
||
|
queue,
|
||
|
surface_config,
|
||
|
surface,
|
||
|
egui_rpass,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
pub fn max_texture_side(&self) -> usize {
|
||
|
self.device.limits().max_texture_dimension_2d as usize
|
||
|
}
|
||
|
|
||
|
pub fn on_window_resized(&mut self, width_in_pixels: u32, height_in_pixels: u32) {
|
||
|
self.surface_config.width = width_in_pixels;
|
||
|
self.surface_config.height = height_in_pixels;
|
||
|
self.surface.configure(&self.device, &self.surface_config);
|
||
|
}
|
||
|
|
||
|
pub fn paint_and_update_textures(
|
||
|
&mut self,
|
||
|
pixels_per_point: f32,
|
||
|
clear_color: egui::Rgba,
|
||
|
clipped_primitives: &[egui::ClippedPrimitive],
|
||
|
textures_delta: &egui::TexturesDelta,
|
||
|
) {
|
||
|
let output_frame = match self.surface.get_current_texture() {
|
||
|
Ok(frame) => frame,
|
||
|
Err(wgpu::SurfaceError::Outdated) => {
|
||
|
// This error occurs when the app is minimized on Windows.
|
||
|
// Silently return here to prevent spamming the console with:
|
||
|
// "The underlying surface has changed, and therefore the swap chain must be updated"
|
||
|
return;
|
||
|
}
|
||
|
Err(e) => {
|
||
|
tracing::warn!("Dropped frame with error: {e}");
|
||
|
return;
|
||
|
}
|
||
|
};
|
||
|
let output_view = output_frame
|
||
|
.texture
|
||
|
.create_view(&wgpu::TextureViewDescriptor::default());
|
||
|
|
||
|
let mut encoder = self
|
||
|
.device
|
||
|
.create_command_encoder(&wgpu::CommandEncoderDescriptor {
|
||
|
label: Some("encoder"),
|
||
|
});
|
||
|
|
||
|
// Upload all resources for the GPU.
|
||
|
let screen_descriptor = renderer::ScreenDescriptor {
|
||
|
size_in_pixels: [self.surface_config.width, self.surface_config.height],
|
||
|
pixels_per_point,
|
||
|
};
|
||
|
|
||
|
for (id, image_delta) in &textures_delta.set {
|
||
|
self.egui_rpass
|
||
|
.update_texture(&self.device, &self.queue, *id, image_delta);
|
||
|
}
|
||
|
for id in &textures_delta.free {
|
||
|
self.egui_rpass.free_texture(id);
|
||
|
}
|
||
|
|
||
|
self.egui_rpass.update_buffers(
|
||
|
&self.device,
|
||
|
&self.queue,
|
||
|
clipped_primitives,
|
||
|
&screen_descriptor,
|
||
|
);
|
||
|
|
||
|
// Record all render passes.
|
||
|
self.egui_rpass.execute(
|
||
|
&mut encoder,
|
||
|
&output_view,
|
||
|
clipped_primitives,
|
||
|
&screen_descriptor,
|
||
|
Some(wgpu::Color {
|
||
|
r: clear_color.r() as f64,
|
||
|
g: clear_color.g() as f64,
|
||
|
b: clear_color.b() as f64,
|
||
|
a: clear_color.a() as f64,
|
||
|
}),
|
||
|
);
|
||
|
|
||
|
// Submit the commands.
|
||
|
self.queue.submit(std::iter::once(encoder.finish()));
|
||
|
|
||
|
// Redraw egui
|
||
|
output_frame.present();
|
||
|
}
|
||
|
|
||
|
#[allow(clippy::unused_self)]
|
||
|
pub fn destroy(&mut self) {
|
||
|
// TODO: something here?
|
||
|
}
|
||
|
}
|