Skip to content

Commit

Permalink
Make winit an opt-in feature of egui-wgpu
Browse files Browse the repository at this point in the history
  • Loading branch information
emilk committed May 10, 2022
1 parent cb57b07 commit 086e6a4
Show file tree
Hide file tree
Showing 5 changed files with 166 additions and 152 deletions.
2 changes: 1 addition & 1 deletion eframe/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ tracing = "0.1"

# optional:
egui_glow = { version = "0.18.0", path = "../egui_glow", optional = true, default-features = false }
egui-wgpu = { version = "0.18.0", path = "../egui-wgpu", optional = true, default-features = false }
egui-wgpu = { version = "0.18.0", path = "../egui-wgpu", optional = true, features = ["winit"] }
glow = { version = "0.11", optional = true }
ron = { version = "0.7", optional = true }
serde = { version = "1", optional = true, features = ["derive"] }
Expand Down
3 changes: 2 additions & 1 deletion eframe/src/native/run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,8 @@ pub fn run_wgpu(
.unwrap();

// SAFETY: `window` must outlive `painter`.
let mut painter = unsafe { egui_wgpu::Painter::new(&window) };
#[allow(unsafe_code)]
let mut painter = unsafe { egui_wgpu::winit::Painter::new(&window) };

let mut integration = epi_integration::EpiIntegration::new(
painter.max_texture_side(),
Expand Down
11 changes: 9 additions & 2 deletions egui-wgpu/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,20 @@ keywords = ["wgpu", "egui", "gui", "gamedev"]
include = ["../LICENSE-APACHE", "../LICENSE-MIT", "**/*.rs", "Cargo.toml"]


[features]
# Make it easy to create bindings for winit.
winit = ["dep:pollster", "dep:winit"]


[dependencies]
egui = { version = "0.18.1", path = "../egui", default-features = false, features = [
"bytemuck",
] }

bytemuck = "1.7"
pollster = "0.2"
tracing = "0.1"
wgpu = { version = "0.12", features = ["webgl"] }
winit = "0.26"

# Optional:
pollster = { version = "0.2", optional = true }
winit = { version = "0.26", optional = true }
152 changes: 4 additions & 148 deletions egui-wgpu/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,153 +2,9 @@

#![allow(unsafe_code)]

/// Low-level painting of [`egui`] on [`wgpu`].
pub mod 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 [`Painter`].
pub unsafe fn new(window: &'window winit::window::Window) -> Self {
let instance = wgpu::Instance::new(wgpu::Backends::PRIMARY | wgpu::Backends::GL);
let surface = unsafe { instance.create_surface(&window) };

// WGPU 0.11+ support force fallback (if HW implementation not supported), set it to true or false (optional).
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, 1);

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: u32, height: u32) {
self.surface_config.width = width;
self.surface_config.height = height;
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,
}),
)
.unwrap();

// 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?
}
}
/// Module for painting [`egui`] with [`wgpu`] on [`winit`].
#[cfg(feature = "winit")]
pub mod winit;
150 changes: 150 additions & 0 deletions egui-wgpu/src/winit.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
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 [`Painter`].
pub unsafe fn new(window: &winit::window::Window) -> Self {
let instance = wgpu::Instance::new(wgpu::Backends::PRIMARY | wgpu::Backends::GL);
let surface = instance.create_surface(&window);

// WGPU 0.11+ support force fallback (if HW implementation not supported), set it to true or false (optional).
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, 1);

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: u32, height: u32) {
self.surface_config.width = width;
self.surface_config.height = height;
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,
}),
)
.unwrap();

// 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?
}
}

0 comments on commit 086e6a4

Please sign in to comment.