1
0
mirror of https://github.com/emilk/egui.git synced 2026-09-02 06:40:06 -04:00

WGPU implementation

This commit is contained in:
Konkitoman
2023-07-27 18:18:14 +03:00
parent 46e85856b9
commit 769c71a714
5 changed files with 552 additions and 338 deletions

View File

@@ -1518,6 +1518,9 @@ pub use glow_integration::run_glow;
mod wgpu_integration { mod wgpu_integration {
use std::sync::Arc; use std::sync::Arc;
use egui::ViewportRender;
use winit::window::ResizeDirection;
use super::*; use super::*;
/// State that is initialized when the application is first starts running via /// State that is initialized when the application is first starts running via
@@ -1527,6 +1530,17 @@ mod wgpu_integration {
painter: egui_wgpu::winit::Painter, painter: egui_wgpu::winit::Painter,
integration: epi_integration::EpiIntegration, integration: epi_integration::EpiIntegration,
app: Box<dyn epi::App>, app: Box<dyn epi::App>,
windows: HashMap<
u64,
(
Option<winit::window::Window>,
Option<egui_winit::State>,
Option<Arc<Box<ViewportRender>>>,
u64,
ViewportBuilder,
),
>,
windows_id: HashMap<winit::window::WindowId, u64>,
} }
struct WgpuWinitApp { struct WgpuWinitApp {
@@ -1538,7 +1552,6 @@ mod wgpu_integration {
/// Window surface state that's initialized when the app starts running via a Resumed event /// Window surface state that's initialized when the app starts running via a Resumed event
/// and on Android will also be destroyed if the application is paused. /// and on Android will also be destroyed if the application is paused.
window: Option<winit::window::Window>,
is_focused: bool, is_focused: bool,
} }
@@ -1560,7 +1573,6 @@ mod wgpu_integration {
app_name: app_name.to_owned(), app_name: app_name.to_owned(),
native_options, native_options,
running: None, running: None,
window: None,
app_creator: Some(app_creator), app_creator: Some(app_creator),
is_focused: true, is_focused: true,
} }
@@ -1571,23 +1583,38 @@ mod wgpu_integration {
storage: Option<&dyn epi::Storage>, storage: Option<&dyn epi::Storage>,
title: &str, title: &str,
native_options: &NativeOptions, native_options: &NativeOptions,
) -> std::result::Result<winit::window::Window, winit::error::OsError> { ) -> std::result::Result<(winit::window::Window, ViewportBuilder), winit::error::OsError>
{
let window_settings = epi_integration::load_window_settings(storage); let window_settings = epi_integration::load_window_settings(storage);
let window_builder = let window_builder =
epi_integration::window_builder(event_loop, title, native_options, window_settings); epi_integration::window_builder(event_loop, title, native_options, window_settings);
let window = create_winit_window_builder(&window_builder).build(event_loop)?; let window = create_winit_window_builder(&window_builder).build(event_loop)?;
epi_integration::apply_native_options_to_window(&window, native_options); epi_integration::apply_native_options_to_window(&window, native_options);
Ok(window) Ok((window, window_builder))
} }
#[allow(unsafe_code)] fn build_windows(&mut self, event_loop: &EventLoopWindowTarget<UserEvent>) {
fn set_window( let Some(running) = &mut self.running else {return};
&mut self,
window: winit::window::Window, for (id, (window, state, _, _, builder)) in running.windows.iter_mut() {
) -> std::result::Result<(), egui_wgpu::WgpuError> { if window.is_none() {
self.window = Some(window); if let Ok(new_window) = create_winit_window_builder(&builder).build(event_loop)
{
running.windows_id.insert(new_window.id(), *id);
pollster::block_on(running.painter.set_window(*id, Some(&new_window)));
*window = Some(new_window);
*state = Some(egui_winit::State::new(event_loop));
}
}
}
}
fn set_window(&mut self, id: u64) -> std::result::Result<(), egui_wgpu::WgpuError> {
if let Some(running) = &mut self.running { if let Some(running) = &mut self.running {
pollster::block_on(running.painter.set_window(self.window.as_ref()))?; if let Some((window, _, _, _, _)) = running.windows.get(&id) {
pollster::block_on(running.painter.set_window(id, window.as_ref().map(|w| w)))?;
}
} }
Ok(()) Ok(())
} }
@@ -1595,8 +1622,8 @@ mod wgpu_integration {
#[allow(unsafe_code)] #[allow(unsafe_code)]
#[cfg(target_os = "android")] #[cfg(target_os = "android")]
fn drop_window(&mut self) -> std::result::Result<(), egui_wgpu::WgpuError> { fn drop_window(&mut self) -> std::result::Result<(), egui_wgpu::WgpuError> {
self.window = None;
if let Some(running) = &mut self.running { if let Some(running) = &mut self.running {
running.windows.remove(&0);
pollster::block_on(running.painter.set_window(None))?; pollster::block_on(running.painter.set_window(None))?;
} }
Ok(()) Ok(())
@@ -1607,99 +1634,104 @@ mod wgpu_integration {
event_loop: &EventLoopWindowTarget<UserEvent>, event_loop: &EventLoopWindowTarget<UserEvent>,
storage: Option<Box<dyn epi::Storage>>, storage: Option<Box<dyn epi::Storage>>,
window: winit::window::Window, window: winit::window::Window,
builder: ViewportBuilder,
) -> std::result::Result<(), egui_wgpu::WgpuError> { ) -> std::result::Result<(), egui_wgpu::WgpuError> {
unimplemented!(); #[allow(unsafe_code, unused_mut, unused_unsafe)]
// #[allow(unsafe_code, unused_mut, unused_unsafe)] let mut painter = egui_wgpu::winit::Painter::new(
// let mut painter = egui_wgpu::winit::Painter::new( self.native_options.wgpu_options.clone(),
// self.native_options.wgpu_options.clone(), self.native_options.multisampling.max(1) as _,
// self.native_options.multisampling.max(1) as _, egui_wgpu::depth_format_from_bits(
// egui_wgpu::depth_format_from_bits( self.native_options.depth_buffer,
// self.native_options.depth_buffer, self.native_options.stencil_buffer,
// self.native_options.stencil_buffer, ),
// ), self.native_options.transparent,
// self.native_options.transparent, );
// ); pollster::block_on(painter.set_window(0, Some(&window)))?;
// pollster::block_on(painter.set_window(Some(&window)))?;
// let wgpu_render_state = painter.render_state(); let wgpu_render_state = painter.render_state();
// let system_theme = system_theme(&window, &self.native_options); let system_theme = system_theme(&window, &self.native_options);
// let mut integration = epi_integration::EpiIntegration::new( let mut integration = epi_integration::EpiIntegration::new(
// event_loop, event_loop,
// painter.max_texture_side().unwrap_or(2048), painter.max_texture_side().unwrap_or(2048),
// &window, &window,
// system_theme, system_theme,
// &self.app_name, &self.app_name,
// &self.native_options, &self.native_options,
// storage, storage,
// #[cfg(feature = "glow")] #[cfg(feature = "glow")]
// None, None,
// wgpu_render_state.clone(), wgpu_render_state.clone(),
// ); );
// #[cfg(feature = "accesskit")]
// {
// integration.init_accesskit(&window, self.repaint_proxy.lock().unwrap().clone());
// }
// let theme = system_theme.unwrap_or(self.native_options.default_theme);
// integration.egui_ctx.set_visuals(theme.egui_visuals());
// window.set_ime_allowed(true); let mut state = egui_winit::State::new(event_loop);
#[cfg(feature = "accesskit")]
{
integration.init_accesskit(
&mut state,
&window,
self.repaint_proxy.lock().unwrap().clone(),
);
}
let theme = system_theme.unwrap_or(self.native_options.default_theme);
integration.egui_ctx.set_visuals(theme.egui_visuals());
// { window.set_ime_allowed(true);
// let event_loop_proxy = self.repaint_proxy.clone();
// // !!! TODO !!! {
// // Need a better way to redraw all the windows idependent of one eachother let event_loop_proxy = self.repaint_proxy.clone();
// integration integration
// .egui_ctx .egui_ctx
// .set_request_repaint_callback(move |info| { .set_request_repaint_callback(move |info| {
// log::trace!("request_repaint_callback: {info:?}"); log::trace!("request_repaint_callback: {info:?}");
// let when = Instant::now() + info.after; let when = Instant::now() + info.after;
// let frame_nr = info.current_frame_nr; let frame_nr = info.current_frame_nr;
// // !!! WARNING !!! event_loop_proxy
// // This will only work for 10 windows .lock()
.unwrap()
.send_event(UserEvent::RequestRepaint {
when,
frame_nr,
window_id: info.window_id,
})
.ok();
});
}
// for i in 0..10 { let app_creator = std::mem::take(&mut self.app_creator)
// event_loop_proxy .expect("Single-use AppCreator has unexpectedly already been taken");
// .lock() let mut app = app_creator(&epi::CreationContext {
// .unwrap() egui_ctx: integration.egui_ctx.clone(),
// .send_event(UserEvent::RequestRepaint { integration_info: integration.frame.info(),
// when, storage: integration.frame.storage(),
// frame_nr, #[cfg(feature = "glow")]
// window_id: i, gl: None,
// }) wgpu_render_state,
// .ok(); raw_display_handle: window.raw_display_handle(),
// } raw_window_handle: window.raw_window_handle(),
// }); });
// }
// let app_creator = std::mem::take(&mut self.app_creator) if app.warm_up_enabled() {
// .expect("Single-use AppCreator has unexpectedly already been taken"); integration.warm_up(app.as_mut(), &window, &mut state);
// let mut app = app_creator(&epi::CreationContext { }
// egui_ctx: integration.egui_ctx.clone(),
// integration_info: integration.frame.info(),
// storage: integration.frame.storage(),
// #[cfg(feature = "glow")]
// gl: None,
// wgpu_render_state,
// raw_display_handle: window.raw_display_handle(),
// raw_window_handle: window.raw_window_handle(),
// });
// if app.warm_up_enabled() { let mut windows_id = HashMap::default();
// integration.warm_up(app.as_mut(), &window); windows_id.insert(window.id(), 0);
// }
// self.running = Some(WgpuWinitRunning { let mut windows = HashMap::default();
// painter, windows.insert(0, (Some(window), Some(state), None, 0, builder));
// integration,
// app,
// });
// self.window = Some(window);
// Ok(()) self.running = Some(WgpuWinitRunning {
painter,
integration,
app,
windows,
windows_id,
});
Ok(())
} }
} }
@@ -1718,20 +1750,28 @@ mod wgpu_integration {
self.running.as_ref().map(|r| &r.integration) self.running.as_ref().map(|r| &r.integration)
} }
fn window(&self, _: winit::window::WindowId) -> Option<&winit::window::Window> { fn window(&self, window_id: winit::window::WindowId) -> Option<&winit::window::Window> {
self.window.as_ref() self.running
.as_ref()
.map(|r| r.windows_id.get(&window_id).map(|id| r.windows.get(id)))
.flatten()
.flatten()
.map(|w| w.0.as_ref().map(|w| w))
.flatten()
} }
fn get_window_winit_id(&self, id: u64) -> Option<winit::window::WindowId> { fn get_window_winit_id(&self, id: u64) -> Option<winit::window::WindowId> {
if id == 0 { self.running
return self.window.as_ref().map(|w| w.id()); .as_ref()
} .map(|r| r.windows.get(&id))
None .flatten()
.map(|w| w.0.as_ref().map(|w| w.id()))
.flatten()
} }
fn save_and_destroy(&mut self) { fn save_and_destroy(&mut self) {
if let Some(mut running) = self.running.take() { if let Some(mut running) = self.running.take() {
if let Some(window) = &self.window { if let Some((Some(window), _, _, _, _)) = running.windows.get(&0) {
running.integration.save(running.app.as_mut(), window); running.integration.save(running.app.as_mut(), window);
} }
@@ -1746,80 +1786,172 @@ mod wgpu_integration {
} }
fn run_ui_and_paint(&mut self, window_id: winit::window::WindowId) -> Vec<EventResult> { fn run_ui_and_paint(&mut self, window_id: winit::window::WindowId) -> Vec<EventResult> {
// !!! WARNING !!! if let Some(running) = &mut self.running {
// Nothing is implemented for WGPU #[cfg(feature = "puffin")]
puffin::GlobalProfiler::lock().new_frame();
crate::profile_scope!("frame");
unimplemented!("Multiples windows in wgpu is not implemented"); let WgpuWinitRunning {
// if let (Some(running), Some(window)) = (&mut self.running, &self.window) { app,
// #[cfg(feature = "puffin")] integration,
// puffin::GlobalProfiler::lock().new_frame(); painter,
// crate::profile_scope!("frame"); windows,
windows_id,
} = running;
// let WgpuWinitRunning { let egui::FullOutput {
// app, platform_output,
// integration, repaint_after,
// painter, textures_delta,
// } = running; shapes,
mut viewports,
viewport_commands,
};
{
let Some((viewport_id, (Some(window), Some(state), render, parent_viewport_id, _))) = windows_id.get(&window_id).map(|id|(windows.get_mut(id).map(|w|(*id, w)))).flatten() else{return vec![]};
integration.egui_ctx.set_current_viewport_id(viewport_id);
// let egui::FullOutput { pollster::block_on(painter.set_window(viewport_id, Some(&window)));
// platform_output,
// repaint_after,
// textures_delta,
// shapes,
// } = integration.update(app.as_mut(), window);
// integration.handle_platform_output(window, platform_output); egui::FullOutput {
platform_output,
repaint_after,
textures_delta,
shapes,
viewports,
viewport_commands,
} = integration.update(
app.as_mut(),
window,
state,
render.clone(),
viewport_id,
*parent_viewport_id,
);
// let clipped_primitives = { integration.handle_platform_output(window, platform_output, state);
// crate::profile_scope!("tessellate");
// integration.egui_ctx.tessellate(shapes)
// };
// let screenshot_requested = &mut integration.frame.output.screenshot_requested; let clipped_primitives = {
crate::profile_scope!("tessellate");
integration.egui_ctx.tessellate(shapes)
};
// let screenshot = painter.paint_and_update_textures( let screenshot_requested = &mut integration.frame.output.screenshot_requested;
// integration.egui_ctx.pixels_per_point(),
// app.clear_color(&integration.egui_ctx.style().visuals),
// &clipped_primitives,
// &textures_delta,
// *screenshot_requested,
// );
// *screenshot_requested = false;
// integration.frame.screenshot.set(screenshot);
// integration.post_rendering(app.as_mut(), window); let screenshot = painter.paint_and_update_textures(
// integration.post_present(window); viewport_id,
integration.egui_ctx.pixels_per_point(),
app.clear_color(&integration.egui_ctx.style().visuals),
&clipped_primitives,
&textures_delta,
*screenshot_requested,
);
*screenshot_requested = false;
integration.frame.screenshot.set(screenshot);
// let control_flow = if integration.should_close() { integration.post_rendering(app.as_mut(), window);
// EventResult::Exit integration.post_present(window);
// } else if repaint_after.is_zero() { }
// EventResult::RepaintNext
// } else if let Some(repaint_after_instant) =
// std::time::Instant::now().checked_add(repaint_after)
// {
// // if repaint_after is something huge and can't be added to Instant,
// // we will use `ControlFlow::Wait` instead.
// // technically, this might lead to some weird corner cases where the user *WANTS*
// // winit to use `WaitUntil(MAX_INSTANT)` explicitly. they can roll their own
// // egui backend impl i guess.
// EventResult::RepaintAt(repaint_after_instant)
// } else {
// EventResult::Wait
// };
// integration.maybe_autosave(app.as_mut(), window); let mut active_viewports_ids = vec![0];
// if window.is_minimized() == Some(true) { viewports.retain_mut(|(id, parent, builder, render)| {
// // On Mac, a minimized Window uses up all CPU: if let Some(w) = windows.get_mut(id) {
// // https://github.com/emilk/egui/issues/325 w.2 = Some(render.clone());
// crate::profile_scope!("bg_sleep"); w.3 = *parent;
// std::thread::sleep(std::time::Duration::from_millis(10)); active_viewports_ids.push(*id);
// } return false;
} else {
true
}
});
// control_flow for (id, parent, builder, render) in viewports {
// } else { windows.insert(id, (None, None, Some(render), parent, builder));
// EventResult::Wait active_viewports_ids.push(id);
// } }
// TODO Make this more efficient
for (id, command) in viewport_commands {
if let Some(window) = windows.get(&id) {
if let Some(win) = &window.0 {
match command {
egui::window::ViewportCommand::Drag => {
// if this is not checked on x11 the input will be permanently taken until the app is killed!
// if let Some(focus) = self.is_focused {
// if focus == id {
win.drag_window();
// }
// }
}
egui::window::ViewportCommand::InnerSize(width, height) => {
win.set_inner_size(winit::dpi::PhysicalSize::new(
width, height,
));
}
egui::window::ViewportCommand::Resize(top, bottom, right, left) => {
win.drag_resize_window(match (top, bottom, right, left) {
(true, false, false, false) => ResizeDirection::North,
(false, true, false, false) => ResizeDirection::South,
(false, false, true, false) => ResizeDirection::East,
(false, false, false, true) => ResizeDirection::West,
(true, false, true, false) => ResizeDirection::NorthEast,
(false, true, true, false) => ResizeDirection::SouthEast,
(true, false, false, true) => ResizeDirection::NorthWest,
(false, true, false, true) => ResizeDirection::SouthWest,
_ => ResizeDirection::East,
});
}
}
}
}
}
windows.retain(|id, _| active_viewports_ids.contains(id));
windows_id.retain(|_, id| active_viewports_ids.contains(id));
let mut control_flow = vec![EventResult::Wait];
for repaint_after in repaint_after {
control_flow.push(if integration.should_close() {
EventResult::Exit
} else if repaint_after.1.is_zero() {
if let Some((Some(window_id), _, _, _, _)) = windows.get(&repaint_after.0) {
EventResult::RepaintNext(window_id.id())
} else {
EventResult::Wait
}
} else if let Some(repaint_after_instant) =
std::time::Instant::now().checked_add(repaint_after.1)
{
// if repaint_after is something huge and can't be added to Instant,
// we will use `ControlFlow::Wait` instead.
// technically, this might lead to some weird corner cases where the user *WANTS*
// winit to use `WaitUntil(MAX_INSTANT)` explicitly. they can roll their own
// egui backend impl i guess.
if let Some((Some(window_id), _, _, _, _)) = windows.get(&repaint_after.0) {
EventResult::RepaintAt(window_id.id(), repaint_after_instant)
} else {
EventResult::Wait
}
} else {
EventResult::Wait
});
}
let Some((viewport_id, (Some(window), Some(state), render, parent_viewport_id, _))) = windows_id.get(&window_id).map(|id|(windows.get_mut(id).map(|w|(*id, w)))).flatten() else{return vec![]};
integration.maybe_autosave(app.as_mut(), window);
if window.is_minimized() == Some(true) {
// On Mac, a minimized Window uses up all CPU:
// https://github.com/emilk/egui/issues/325
crate::profile_scope!("bg_sleep");
std::thread::sleep(std::time::Duration::from_millis(10));
}
control_flow
} else {
vec![EventResult::Wait]
}
} }
fn on_event( fn on_event(
@@ -1827,132 +1959,179 @@ mod wgpu_integration {
event_loop: &EventLoopWindowTarget<UserEvent>, event_loop: &EventLoopWindowTarget<UserEvent>,
event: &winit::event::Event<'_, UserEvent>, event: &winit::event::Event<'_, UserEvent>,
) -> Result<EventResult> { ) -> Result<EventResult> {
// !!! WARNING !!! self.build_windows(event_loop);
Ok(match event {
winit::event::Event::Resumed => {
if let Some(running) = &self.running {
if running.windows.get(&0).is_none() {
let window = Self::create_window(
event_loop,
running.integration.frame.storage(),
&self.app_name,
&self.native_options,
)?;
self.set_window(0)?;
}
} else {
let storage = epi_integration::create_storage(
self.native_options
.app_id
.as_ref()
.unwrap_or(&self.app_name),
);
let (window, builder) = Self::create_window(
event_loop,
storage.as_deref(),
&self.app_name,
&self.native_options,
)?;
self.init_run_state(event_loop, storage, window, builder)?;
}
EventResult::RepaintNow(
self.running
.as_ref()
.unwrap()
.windows
.get(&0)
.unwrap()
.0
.as_ref()
.unwrap()
.id(),
)
}
winit::event::Event::Suspended => {
#[cfg(target_os = "android")]
self.drop_window()?;
EventResult::Wait
}
unimplemented!("Multiples windows is not implemented for WGPU"); winit::event::Event::WindowEvent { event, window_id } => {
if let Some(running) = &mut self.running {
// On Windows, if a window is resized by the user, it should repaint synchronously, inside the
// event handler.
//
// If this is not done, the compositor will assume that the window does not want to redraw,
// and continue ahead.
//
// In eframe's case, that causes the window to rapidly flicker, as it struggles to deliver
// new frames to the compositor in time.
//
// The flickering is technically glutin or glow's fault, but we should be responding properly
// to resizes anyway, as doing so avoids dropping frames.
//
// See: https://github.com/emilk/egui/issues/903
let mut repaint_asap = false;
// Ok(match event { match &event {
// winit::event::Event::Resumed => { winit::event::WindowEvent::Focused(new_focused) => {
// if let Some(running) = &self.running { self.is_focused = *new_focused;
// if self.window.is_none() { }
// let window = Self::create_window( winit::event::WindowEvent::Resized(physical_size) => {
// event_loop, repaint_asap = true;
// running.integration.frame.storage(),
// &self.app_name,
// &self.native_options,
// )?;
// self.set_window(window)?;
// }
// } else {
// let storage = epi_integration::create_storage(
// self.native_options
// .app_id
// .as_ref()
// .unwrap_or(&self.app_name),
// );
// let window = Self::create_window(
// event_loop,
// storage.as_deref(),
// &self.app_name,
// &self.native_options,
// )?;
// self.init_run_state(event_loop, storage, window)?;
// }
// EventResult::RepaintNow
// }
// winit::event::Event::Suspended => {
// #[cfg(target_os = "android")]
// self.drop_window()?;
// EventResult::Wait
// }
// winit::event::Event::WindowEvent { event, .. } => { // Resize with 0 width and height is used by winit to signal a minimize event on Windows.
// if let Some(running) = &mut self.running { // See: https://github.com/rust-windowing/winit/issues/208
// // On Windows, if a window is resized by the user, it should repaint synchronously, inside the // This solves an issue where the app would panic when minimizing on Windows.
// // event handler. if let Some(viewport_id) =
// // running.windows_id.get(window_id).cloned()
// // If this is not done, the compositor will assume that the window does not want to redraw, {
// // and continue ahead. if physical_size.width > 0 && physical_size.height > 0 {
// // running.painter.on_window_resized(
// // In eframe's case, that causes the window to rapidly flicker, as it struggles to deliver viewport_id,
// // new frames to the compositor in time. physical_size.width,
// // physical_size.height,
// // The flickering is technically glutin or glow's fault, but we should be responding properly );
// // to resizes anyway, as doing so avoids dropping frames. }
// // }
// // See: https://github.com/emilk/egui/issues/903 }
// let mut repaint_asap = false; winit::event::WindowEvent::ScaleFactorChanged {
new_inner_size,
..
} => {
if let Some(viewport_id) =
running.windows_id.get(window_id).cloned()
{
repaint_asap = true;
running.painter.on_window_resized(
viewport_id,
new_inner_size.width,
new_inner_size.height,
);
}
}
winit::event::WindowEvent::CloseRequested
if running.integration.should_close() =>
{
log::debug!("Received WindowEvent::CloseRequested");
return Ok(EventResult::Exit);
}
_ => {}
};
// match &event { let event_response =
// winit::event::WindowEvent::Focused(new_focused) => { if let Some((id, (Some(window), Some(state), _, _, _))) = running
// self.is_focused = *new_focused; .windows_id
// } .get(window_id)
// winit::event::WindowEvent::Resized(physical_size) => { .map(|id| running.windows.get_mut(id).map(|w| (*id, w)))
// repaint_asap = true; .flatten()
{
Some(running.integration.on_event(
running.app.as_mut(),
event,
window_id,
state,
id,
))
} else {
None
};
// // Resize with 0 width and height is used by winit to signal a minimize event on Windows. if running.integration.should_close() {
// // See: https://github.com/rust-windowing/winit/issues/208 EventResult::Exit
// // This solves an issue where the app would panic when minimizing on Windows. } else if let Some(event_response) = event_response {
// if physical_size.width > 0 && physical_size.height > 0 { if event_response.repaint {
// running.painter.on_window_resized( if repaint_asap {
// physical_size.width, EventResult::RepaintNow(*window_id)
// physical_size.height, } else {
// ); EventResult::RepaintNext(*window_id)
// } }
// } } else {
// winit::event::WindowEvent::ScaleFactorChanged { EventResult::Wait
// new_inner_size, }
// .. } else {
// } => { EventResult::Wait
// repaint_asap = true; }
// running } else {
// .painter EventResult::Wait
// .on_window_resized(new_inner_size.width, new_inner_size.height); }
// } }
// winit::event::WindowEvent::CloseRequested #[cfg(feature = "accesskit")]
// if running.integration.should_close() => winit::event::Event::UserEvent(UserEvent::AccessKitActionRequest(
// { accesskit_winit::ActionRequestEvent { request, window_id },
// log::debug!("Received WindowEvent::CloseRequested"); )) => {
// return Ok(EventResult::Exit); if let Some(running) = &mut self.running {
// } if let Some((_, Some(state), _, _, _)) = running
// _ => {} .windows_id
// }; .get(window_id)
.map(|id| running.windows.get_mut(id))
// let event_response = .flatten()
// running.integration.on_event(running.app.as_mut(), event); {
// if running.integration.should_close() { running.integration.on_accesskit_action_request(
// EventResult::Exit request.clone(),
// } else if event_response.repaint { window_id,
// if repaint_asap { state,
// EventResult::RepaintNow );
// } else { }
// EventResult::RepaintNext // As a form of user input, accessibility actions should
// } // lead to a repaint.
// } else { EventResult::RepaintNext(*window_id)
// EventResult::Wait } else {
// } EventResult::Wait
// } else { }
// EventResult::Wait }
// } _ => EventResult::Wait,
// } })
// #[cfg(feature = "accesskit")]
// winit::event::Event::UserEvent(UserEvent::AccessKitActionRequest(
// accesskit_winit::ActionRequestEvent { request, .. },
// )) => {
// if let Some(running) = &mut self.running {
// running
// .integration
// .on_accesskit_action_request(request.clone());
// // As a form of user input, accessibility actions should
// // lead to a repaint.
// EventResult::RepaintNext
// } else {
// EventResult::Wait
// }
// }
// _ => EventResult::Wait,
// })
//
} }
fn get_window_id(&self, id: &winit::window::WindowId) -> Option<u64> { fn get_window_id(&self, id: &winit::window::WindowId) -> Option<u64> {

View File

@@ -1,5 +1,7 @@
use std::sync::Arc; use std::sync::Arc;
use epaint::ahash::HashMap;
use crate::{renderer, RenderState, SurfaceErrorAction, WgpuConfiguration}; use crate::{renderer, RenderState, SurfaceErrorAction, WgpuConfiguration};
struct SurfaceState { struct SurfaceState {
@@ -83,7 +85,7 @@ pub struct Painter {
instance: wgpu::Instance, instance: wgpu::Instance,
render_state: Option<RenderState>, render_state: Option<RenderState>,
surface_state: Option<SurfaceState>, surfaces: HashMap<u64, SurfaceState>,
} }
impl Painter { impl Painter {
@@ -120,7 +122,7 @@ impl Painter {
instance, instance,
render_state: None, render_state: None,
surface_state: None, surfaces: HashMap::default(),
msaa_texture_view: None, msaa_texture_view: None,
} }
} }
@@ -185,60 +187,72 @@ impl Painter {
/// If the provided wgpu configuration does not match an available device. /// If the provided wgpu configuration does not match an available device.
pub async fn set_window( pub async fn set_window(
&mut self, &mut self,
viewport_id: u64,
window: Option<&winit::window::Window>, window: Option<&winit::window::Window>,
) -> Result<(), crate::WgpuError> { ) -> Result<(), crate::WgpuError> {
match window { match window {
Some(window) => { Some(window) => {
let surface = unsafe { self.instance.create_surface(&window)? };
let render_state = if let Some(render_state) = &self.render_state {
render_state
} else {
let render_state = RenderState::create(
&self.configuration,
&self.instance,
&surface,
self.depth_format,
self.msaa_samples,
)
.await?;
self.render_state.get_or_insert(render_state)
};
let alpha_mode = if self.support_transparent_backbuffer {
let supported_alpha_modes =
surface.get_capabilities(&render_state.adapter).alpha_modes;
// Prefer pre multiplied over post multiplied!
if supported_alpha_modes.contains(&wgpu::CompositeAlphaMode::PreMultiplied) {
wgpu::CompositeAlphaMode::PreMultiplied
} else if supported_alpha_modes
.contains(&wgpu::CompositeAlphaMode::PostMultiplied)
{
wgpu::CompositeAlphaMode::PostMultiplied
} else {
log::warn!("Transparent window was requested, but the active wgpu surface does not support a `CompositeAlphaMode` with transparency.");
wgpu::CompositeAlphaMode::Auto
}
} else {
wgpu::CompositeAlphaMode::Auto
};
let supports_screenshot =
!matches!(render_state.adapter.get_info().backend, wgpu::Backend::Gl);
let size = window.inner_size(); let size = window.inner_size();
self.surface_state = Some(SurfaceState { if self.surfaces.get(&viewport_id).is_none() {
surface, let surface = unsafe { self.instance.create_surface(&window)? };
width: size.width,
height: size.height, let render_state = if let Some(render_state) = &self.render_state {
alpha_mode, render_state
supports_screenshot, } else {
}); let render_state = RenderState::create(
self.resize_and_generate_depth_texture_view_and_msaa_view(size.width, size.height); &self.configuration,
&self.instance,
&surface,
self.depth_format,
self.msaa_samples,
)
.await?;
self.render_state.get_or_insert(render_state)
};
let alpha_mode = if self.support_transparent_backbuffer {
let supported_alpha_modes =
surface.get_capabilities(&render_state.adapter).alpha_modes;
// Prefer pre multiplied over post multiplied!
if supported_alpha_modes.contains(&wgpu::CompositeAlphaMode::PreMultiplied)
{
wgpu::CompositeAlphaMode::PreMultiplied
} else if supported_alpha_modes
.contains(&wgpu::CompositeAlphaMode::PostMultiplied)
{
wgpu::CompositeAlphaMode::PostMultiplied
} else {
log::warn!("Transparent window was requested, but the active wgpu surface does not support a `CompositeAlphaMode` with transparency.");
wgpu::CompositeAlphaMode::Auto
}
} else {
wgpu::CompositeAlphaMode::Auto
};
let supports_screenshot =
!matches!(render_state.adapter.get_info().backend, wgpu::Backend::Gl);
self.surfaces.insert(
viewport_id,
SurfaceState {
surface,
width: size.width,
height: size.height,
alpha_mode,
supports_screenshot,
},
);
}
self.resize_and_generate_depth_texture_view_and_msaa_view(
viewport_id,
size.width,
size.height,
);
} }
None => { None => {
self.surface_state = None; log::warn!("All surfaces was deleted!");
self.surfaces.clear();
} }
} }
Ok(()) Ok(())
@@ -257,11 +271,12 @@ impl Painter {
fn resize_and_generate_depth_texture_view_and_msaa_view( fn resize_and_generate_depth_texture_view_and_msaa_view(
&mut self, &mut self,
viewport_id: u64,
width_in_pixels: u32, width_in_pixels: u32,
height_in_pixels: u32, height_in_pixels: u32,
) { ) {
let render_state = self.render_state.as_ref().unwrap(); let render_state = self.render_state.as_ref().unwrap();
let surface_state = self.surface_state.as_mut().unwrap(); let surface_state = self.surfaces.get_mut(&viewport_id).unwrap();
surface_state.width = width_in_pixels; surface_state.width = width_in_pixels;
surface_state.height = height_in_pixels; surface_state.height = height_in_pixels;
@@ -314,9 +329,15 @@ impl Painter {
}); });
} }
pub fn on_window_resized(&mut self, width_in_pixels: u32, height_in_pixels: u32) { pub fn on_window_resized(
if self.surface_state.is_some() { &mut self,
viewport_id: u64,
width_in_pixels: u32,
height_in_pixels: u32,
) {
if self.surfaces.get(&viewport_id).is_some() {
self.resize_and_generate_depth_texture_view_and_msaa_view( self.resize_and_generate_depth_texture_view_and_msaa_view(
viewport_id,
width_in_pixels, width_in_pixels,
height_in_pixels, height_in_pixels,
); );
@@ -427,6 +448,7 @@ impl Painter {
// Returns a vector with the frame's pixel data if it was requested. // Returns a vector with the frame's pixel data if it was requested.
pub fn paint_and_update_textures( pub fn paint_and_update_textures(
&mut self, &mut self,
viewport_id: u64,
pixels_per_point: f32, pixels_per_point: f32,
clear_color: [f32; 4], clear_color: [f32; 4],
clipped_primitives: &[epaint::ClippedPrimitive], clipped_primitives: &[epaint::ClippedPrimitive],
@@ -436,7 +458,7 @@ impl Painter {
crate::profile_function!(); crate::profile_function!();
let render_state = self.render_state.as_mut()?; let render_state = self.render_state.as_mut()?;
let surface_state = self.surface_state.as_ref()?; let surface_state = self.surfaces.get(&viewport_id)?;
let output_frame = { let output_frame = {
crate::profile_scope!("get_current_texture"); crate::profile_scope!("get_current_texture");

View File

@@ -4,6 +4,7 @@ use eframe::{
egui_wgpu::wgpu::util::DeviceExt, egui_wgpu::wgpu::util::DeviceExt,
egui_wgpu::{self, wgpu}, egui_wgpu::{self, wgpu},
}; };
use egui::ViewportRender;
pub struct Custom3d { pub struct Custom3d {
angle: f32, angle: f32,
@@ -96,7 +97,16 @@ impl Custom3d {
} }
impl eframe::App for Custom3d { impl eframe::App for Custom3d {
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) { fn update(
&mut self,
ctx: &egui::Context,
frame: &mut eframe::Frame,
render: Option<&ViewportRender>,
) {
if let Some(render) = render {
render(ctx, frame.viewport_id(), frame.parent_viewport_id());
return;
}
egui::CentralPanel::default().show(ctx, |ui| { egui::CentralPanel::default().show(ctx, |ui| {
egui::ScrollArea::both() egui::ScrollArea::both()
.auto_shrink([false; 2]) .auto_shrink([false; 2])

View File

@@ -9,5 +9,5 @@ publish = false
[dependencies] [dependencies]
eframe = { path = "../../crates/eframe" } eframe = { path = "../../crates/eframe", features = ["wgpu"] }
env_logger = "0.10" env_logger = "0.10"

View File

@@ -9,7 +9,10 @@ fn main() {
eframe::run_simple_native( eframe::run_simple_native(
"Viewports Examples", "Viewports Examples",
NativeOptions::default(), NativeOptions {
renderer: eframe::Renderer::Wgpu,
..NativeOptions::default()
},
move |ctx, _frame| { move |ctx, _frame| {
egui::CentralPanel::default().show(ctx, |ui| { egui::CentralPanel::default().show(ctx, |ui| {
ui.label(format!("Frame: {}", ui.ctx().frame_nr())); ui.label(format!("Frame: {}", ui.ctx().frame_nr()));