can change window settings at runtime (#644)

can change window settings at runtime
This commit is contained in:
François 2020-10-15 20:42:19 +02:00 committed by GitHub
parent 9c48e5cccb
commit 76cc25823d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 251 additions and 60 deletions

View File

@ -74,8 +74,8 @@ pub fn camera_system<T: CameraProjection + Component>(
} }
for (entity, mut camera, mut camera_projection) in &mut query.iter() { for (entity, mut camera, mut camera_projection) in &mut query.iter() {
if let Some(window) = windows.get(camera.window) { if let Some(window) = windows.get(camera.window) {
if changed_window_ids.contains(&window.id) || added_cameras.contains(&entity) { if changed_window_ids.contains(&window.id()) || added_cameras.contains(&entity) {
camera_projection.update(window.width as usize, window.height as usize); camera_projection.update(window.width() as usize, window.height() as usize);
camera.projection_matrix = camera_projection.get_projection_matrix(); camera.projection_matrix = camera_projection.get_projection_matrix();
camera.depth_calculation = camera_projection.depth_calculation(); camera.depth_calculation = camera_projection.depth_calculation();
} }

View File

@ -56,11 +56,11 @@ impl Node for WindowSwapChainNode {
// create window swapchain when window is resized or created // create window swapchain when window is resized or created
if self if self
.window_created_event_reader .window_created_event_reader
.find_latest(&window_created_events, |e| e.id == window.id) .find_latest(&window_created_events, |e| e.id == window.id())
.is_some() .is_some()
|| self || self
.window_resized_event_reader .window_resized_event_reader
.find_latest(&window_resized_events, |e| e.id == window.id) .find_latest(&window_resized_events, |e| e.id == window.id())
.is_some() .is_some()
{ {
render_resource_context.create_swap_chain(window); render_resource_context.create_swap_chain(window);

View File

@ -56,11 +56,11 @@ impl Node for WindowTextureNode {
if self if self
.window_created_event_reader .window_created_event_reader
.find_latest(&window_created_events, |e| e.id == window.id) .find_latest(&window_created_events, |e| e.id == window.id())
.is_some() .is_some()
|| self || self
.window_resized_event_reader .window_resized_event_reader
.find_latest(&window_resized_events, |e| e.id == window.id) .find_latest(&window_resized_events, |e| e.id == window.id())
.is_some() .is_some()
{ {
let render_resource_context = render_context.resources_mut(); let render_resource_context = render_context.resources_mut();
@ -68,8 +68,8 @@ impl Node for WindowTextureNode {
render_resource_context.remove_texture(old_texture); render_resource_context.remove_texture(old_texture);
} }
self.descriptor.size.width = window.width; self.descriptor.size.width = window.width();
self.descriptor.size.height = window.height; self.descriptor.size.height = window.height();
let texture_resource = render_resource_context.create_texture(self.descriptor); let texture_resource = render_resource_context.create_texture(self.descriptor);
output.set(WINDOW_TEXTURE, RenderResourceId::Texture(texture_resource)); output.set(WINDOW_TEXTURE, RenderResourceId::Texture(texture_resource));
} }

View File

@ -105,7 +105,7 @@ impl FlexSurface {
pub fn update_window(&mut self, window: &Window) { pub fn update_window(&mut self, window: &Window) {
let stretch = &mut self.stretch; let stretch = &mut self.stretch;
let node = self.window_nodes.entry(window.id).or_insert_with(|| { let node = self.window_nodes.entry(window.id()).or_insert_with(|| {
stretch stretch
.new_node(stretch::style::Style::default(), Vec::new()) .new_node(stretch::style::Style::default(), Vec::new())
.unwrap() .unwrap()
@ -116,8 +116,8 @@ impl FlexSurface {
*node, *node,
stretch::style::Style { stretch::style::Style {
size: stretch::geometry::Size { size: stretch::geometry::Size {
width: stretch::style::Dimension::Points(window.width as f32), width: stretch::style::Dimension::Points(window.width() as f32),
height: stretch::style::Dimension::Points(window.height as f32), height: stretch::style::Dimension::Points(window.height() as f32),
}, },
..Default::default() ..Default::default()
}, },
@ -189,7 +189,7 @@ pub fn flex_node_system(
// update window children (for now assuming all Nodes live in the primary window) // update window children (for now assuming all Nodes live in the primary window)
if let Some(primary_window) = windows.get_primary() { if let Some(primary_window) = windows.get_primary() {
flex_surface.set_window_children(primary_window.id, root_node_query.iter().iter()); flex_surface.set_window_children(primary_window.id(), root_node_query.iter().iter());
} }
// update children // update children

View File

@ -274,22 +274,25 @@ impl RenderResourceContext for WgpuRenderResourceContext {
let swap_chain_descriptor: wgpu::SwapChainDescriptor = window.wgpu_into(); let swap_chain_descriptor: wgpu::SwapChainDescriptor = window.wgpu_into();
let surface = surfaces let surface = surfaces
.get(&window.id) .get(&window.id())
.expect("No surface found for window"); .expect("No surface found for window");
let swap_chain = self let swap_chain = self
.device .device
.create_swap_chain(surface, &swap_chain_descriptor); .create_swap_chain(surface, &swap_chain_descriptor);
window_swap_chains.insert(window.id, swap_chain); window_swap_chains.insert(window.id(), swap_chain);
} }
fn next_swap_chain_texture(&self, window: &bevy_window::Window) -> TextureId { fn next_swap_chain_texture(&self, window: &bevy_window::Window) -> TextureId {
if let Some(texture_id) = self.try_next_swap_chain_texture(window.id) { if let Some(texture_id) = self.try_next_swap_chain_texture(window.id()) {
texture_id texture_id
} else { } else {
self.resources.window_swap_chains.write().remove(&window.id); self.resources
.window_swap_chains
.write()
.remove(&window.id());
self.create_swap_chain(window); self.create_swap_chain(window);
self.try_next_swap_chain_texture(window.id) self.try_next_swap_chain_texture(window.id())
.expect("Failed to acquire next swap chain texture!") .expect("Failed to acquire next swap chain texture!")
} }
} }

View File

@ -74,9 +74,9 @@ impl WgpuRenderer {
#[cfg(feature = "bevy_winit")] #[cfg(feature = "bevy_winit")]
{ {
let winit_windows = resources.get::<bevy_winit::WinitWindows>().unwrap(); let winit_windows = resources.get::<bevy_winit::WinitWindows>().unwrap();
let winit_window = winit_windows.get_window(window.id).unwrap(); let winit_window = winit_windows.get_window(window.id()).unwrap();
let surface = unsafe { self.instance.create_surface(winit_window.deref()) }; let surface = unsafe { self.instance.create_surface(winit_window.deref()) };
render_resource_context.set_window_surface(window.id, surface); render_resource_context.set_window_surface(window.id(), surface);
} }
} }
} }

View File

@ -563,9 +563,9 @@ impl WgpuFrom<&Window> for wgpu::SwapChainDescriptor {
wgpu::SwapChainDescriptor { wgpu::SwapChainDescriptor {
usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT, usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT,
format: wgpu::TextureFormat::Bgra8UnormSrgb, format: wgpu::TextureFormat::Bgra8UnormSrgb,
width: window.width, width: window.width(),
height: window.height, height: window.height(),
present_mode: if window.vsync { present_mode: if window.vsync() {
wgpu::PresentMode::Fifo wgpu::PresentMode::Fifo
} else { } else {
wgpu::PresentMode::Immediate wgpu::PresentMode::Immediate

View File

@ -33,16 +33,41 @@ impl Default for WindowId {
#[derive(Debug)] #[derive(Debug)]
pub struct Window { pub struct Window {
pub id: WindowId, id: WindowId,
pub width: u32, width: u32,
pub height: u32, height: u32,
pub title: String, title: String,
pub vsync: bool, vsync: bool,
pub resizable: bool, resizable: bool,
pub decorations: bool, decorations: bool,
pub mode: WindowMode, mode: WindowMode,
#[cfg(target_arch = "wasm32")] #[cfg(target_arch = "wasm32")]
pub canvas: Option<String>, pub canvas: Option<String>,
command_queue: Vec<WindowCommand>,
}
#[derive(Debug)]
pub enum WindowCommand {
SetWindowMode {
mode: WindowMode,
resolution: (u32, u32),
},
SetTitle {
title: String,
},
SetResolution {
width: u32,
height: u32,
},
SetVsync {
vsync: bool,
},
SetResizable {
resizable: bool,
},
SetDecorations {
decorations: bool,
},
} }
/// Defines the way a window is displayed /// Defines the way a window is displayed
@ -70,8 +95,91 @@ impl Window {
mode: window_descriptor.mode, mode: window_descriptor.mode,
#[cfg(target_arch = "wasm32")] #[cfg(target_arch = "wasm32")]
canvas: window_descriptor.canvas.clone(), canvas: window_descriptor.canvas.clone(),
command_queue: Vec::new(),
} }
} }
#[inline]
pub fn id(&self) -> WindowId {
self.id
}
#[inline]
pub fn width(&self) -> u32 {
self.width
}
#[inline]
pub fn height(&self) -> u32 {
self.height
}
pub fn set_resolution(&mut self, width: u32, height: u32) {
self.width = width;
self.height = height;
self.command_queue
.push(WindowCommand::SetResolution { width, height });
}
#[doc(hidden)]
pub fn update_resolution_from_backend(&mut self, width: u32, height: u32) {
self.width = width;
self.height = height;
}
pub fn title(&self) -> &str {
&self.title
}
pub fn set_title(&mut self, title: String) {
self.title = title.to_string();
self.command_queue.push(WindowCommand::SetTitle { title });
}
pub fn vsync(&self) -> bool {
self.vsync
}
pub fn set_vsync(&mut self, vsync: bool) {
self.vsync = vsync;
self.command_queue.push(WindowCommand::SetVsync { vsync });
}
pub fn resizable(&self) -> bool {
self.resizable
}
pub fn set_resizable(&mut self, resizable: bool) {
self.resizable = resizable;
self.command_queue
.push(WindowCommand::SetResizable { resizable });
}
pub fn decorations(&self) -> bool {
self.decorations
}
pub fn set_decorations(&mut self, decorations: bool) {
self.decorations = decorations;
self.command_queue
.push(WindowCommand::SetDecorations { decorations });
}
pub fn mode(&self) -> WindowMode {
self.mode
}
pub fn set_mode(&mut self, mode: WindowMode) {
self.mode = mode;
self.command_queue.push(WindowCommand::SetWindowMode {
mode,
resolution: (self.width, self.height),
});
}
pub fn drain_commands<'a>(&'a mut self) -> impl Iterator<Item = WindowCommand> + 'a {
self.command_queue.drain(..)
}
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]

View File

@ -8,7 +8,7 @@ pub struct Windows {
impl Windows { impl Windows {
pub fn add(&mut self, window: Window) { pub fn add(&mut self, window: Window) {
self.windows.insert(window.id, window); self.windows.insert(window.id(), window);
} }
pub fn get(&self, id: WindowId) -> Option<&Window> { pub fn get(&self, id: WindowId) -> Option<&Window> {
@ -23,7 +23,15 @@ impl Windows {
self.get(WindowId::primary()) self.get(WindowId::primary())
} }
pub fn get_primary_mut(&mut self) -> Option<&mut Window> {
self.get_mut(WindowId::primary())
}
pub fn iter(&self) -> impl Iterator<Item = &Window> { pub fn iter(&self) -> impl Iterator<Item = &Window> {
self.windows.values() self.windows.values()
} }
pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut Window> {
self.windows.values_mut()
}
} }

View File

@ -9,7 +9,7 @@ pub use winit_config::*;
pub use winit_windows::*; pub use winit_windows::*;
use bevy_app::{prelude::*, AppExit}; use bevy_app::{prelude::*, AppExit};
use bevy_ecs::Resources; use bevy_ecs::{IntoThreadLocalSystem, Resources, World};
use bevy_math::Vec2; use bevy_math::Vec2;
use bevy_window::{ use bevy_window::{
CreateWindow, CursorMoved, Window, WindowCloseRequested, WindowCreated, WindowResized, Windows, CreateWindow, CursorMoved, Window, WindowCloseRequested, WindowCreated, WindowResized, Windows,
@ -29,7 +29,60 @@ impl Plugin for WinitPlugin {
// stopping us. there are plans to remove the lifetime: https://github.com/rust-windowing/winit/pull/1456 // stopping us. there are plans to remove the lifetime: https://github.com/rust-windowing/winit/pull/1456
// .add_event::<winit::event::WindowEvent>() // .add_event::<winit::event::WindowEvent>()
.init_resource::<WinitWindows>() .init_resource::<WinitWindows>()
.set_runner(winit_runner); .set_runner(winit_runner)
.add_system(change_window.thread_local_system());
}
}
fn change_window(_: &mut World, resources: &mut Resources) {
let winit_windows = resources.get::<WinitWindows>().unwrap();
let mut windows = resources.get_mut::<Windows>().unwrap();
for bevy_window in windows.iter_mut() {
let id = bevy_window.id();
for command in bevy_window.drain_commands() {
match command {
bevy_window::WindowCommand::SetWindowMode {
mode,
resolution: (width, height),
} => {
let window = winit_windows.get_window(id).unwrap();
match mode {
bevy_window::WindowMode::BorderlessFullscreen => {
window.set_fullscreen(Some(winit::window::Fullscreen::Borderless(None)))
}
bevy_window::WindowMode::Fullscreen { use_size } => window.set_fullscreen(
Some(winit::window::Fullscreen::Exclusive(match use_size {
true => get_fitting_videomode(
&window.current_monitor().unwrap(),
width,
height,
),
false => get_best_videomode(&window.current_monitor().unwrap()),
})),
),
bevy_window::WindowMode::Windowed => window.set_fullscreen(None),
}
}
bevy_window::WindowCommand::SetTitle { title } => {
let window = winit_windows.get_window(id).unwrap();
window.set_title(&title);
}
bevy_window::WindowCommand::SetResolution { width, height } => {
let window = winit_windows.get_window(id).unwrap();
window.set_inner_size(winit::dpi::PhysicalSize::new(width, height));
}
bevy_window::WindowCommand::SetVsync { .. } => (),
bevy_window::WindowCommand::SetResizable { resizable } => {
let window = winit_windows.get_window(id).unwrap();
window.set_resizable(resizable);
}
bevy_window::WindowCommand::SetDecorations { decorations } => {
let window = winit_windows.get_window(id).unwrap();
window.set_decorations(decorations);
}
}
}
} }
} }
@ -118,15 +171,14 @@ pub fn winit_runner(mut app: App) {
let winit_windows = app.resources.get_mut::<WinitWindows>().unwrap(); let winit_windows = app.resources.get_mut::<WinitWindows>().unwrap();
let mut windows = app.resources.get_mut::<Windows>().unwrap(); let mut windows = app.resources.get_mut::<Windows>().unwrap();
let window_id = winit_windows.get_window_id(winit_window_id).unwrap(); let window_id = winit_windows.get_window_id(winit_window_id).unwrap();
let mut window = windows.get_mut(window_id).unwrap(); let window = windows.get_mut(window_id).unwrap();
window.width = size.width; window.update_resolution_from_backend(size.width, size.height);
window.height = size.height;
let mut resize_events = app.resources.get_mut::<Events<WindowResized>>().unwrap(); let mut resize_events = app.resources.get_mut::<Events<WindowResized>>().unwrap();
resize_events.send(WindowResized { resize_events.send(WindowResized {
id: window_id, id: window_id,
height: window.height as usize, height: window.height() as usize,
width: window.width as usize, width: window.width() as usize,
}); });
} }
event::Event::WindowEvent { event::Event::WindowEvent {
@ -231,7 +283,7 @@ fn handle_create_window_events(
for create_window_event in create_window_event_reader.iter(&create_window_events) { for create_window_event in create_window_event_reader.iter(&create_window_events) {
let window = Window::new(create_window_event.id, &create_window_event.descriptor); let window = Window::new(create_window_event.id, &create_window_event.descriptor);
winit_windows.create_window(event_loop, &window); winit_windows.create_window(event_loop, &window);
let window_id = window.id; let window_id = window.id();
windows.add(window); windows.add(window);
window_created_events.send(WindowCreated { id: window_id }); window_created_events.send(WindowCreated { id: window_id });
} }

View File

@ -23,24 +23,31 @@ impl WinitWindows {
#[cfg(not(target_os = "windows"))] #[cfg(not(target_os = "windows"))]
let mut winit_window_builder = winit::window::WindowBuilder::new(); let mut winit_window_builder = winit::window::WindowBuilder::new();
winit_window_builder = match window.mode { winit_window_builder = match window.mode() {
WindowMode::BorderlessFullscreen => winit_window_builder.with_fullscreen(Some( WindowMode::BorderlessFullscreen => winit_window_builder.with_fullscreen(Some(
winit::window::Fullscreen::Borderless(event_loop.primary_monitor()), winit::window::Fullscreen::Borderless(event_loop.primary_monitor()),
)), )),
WindowMode::Fullscreen { use_size } => winit_window_builder.with_fullscreen(Some( WindowMode::Fullscreen { use_size } => winit_window_builder.with_fullscreen(Some(
winit::window::Fullscreen::Exclusive(match use_size { winit::window::Fullscreen::Exclusive(match use_size {
true => get_fitting_videomode(&event_loop.primary_monitor().unwrap(), &window), true => get_fitting_videomode(
&event_loop.primary_monitor().unwrap(),
window.width(),
window.height(),
),
false => get_best_videomode(&event_loop.primary_monitor().unwrap()), false => get_best_videomode(&event_loop.primary_monitor().unwrap()),
}), }),
)), )),
_ => winit_window_builder _ => winit_window_builder
.with_inner_size(winit::dpi::PhysicalSize::new(window.width, window.height)) .with_inner_size(winit::dpi::PhysicalSize::new(
.with_resizable(window.resizable) window.width(),
.with_decorations(window.decorations), window.height(),
))
.with_resizable(window.resizable())
.with_decorations(window.decorations()),
}; };
#[allow(unused_mut)] #[allow(unused_mut)]
let mut winit_window_builder = winit_window_builder.with_title(&window.title); let mut winit_window_builder = winit_window_builder.with_title(window.title());
#[cfg(target_arch = "wasm32")] #[cfg(target_arch = "wasm32")]
{ {
@ -64,8 +71,10 @@ impl WinitWindows {
let winit_window = winit_window_builder.build(&event_loop).unwrap(); let winit_window = winit_window_builder.build(&event_loop).unwrap();
self.window_id_to_winit.insert(window.id, winit_window.id()); self.window_id_to_winit
self.winit_to_window_id.insert(winit_window.id(), window.id); .insert(window.id(), winit_window.id());
self.winit_to_window_id
.insert(winit_window.id(), window.id());
#[cfg(target_arch = "wasm32")] #[cfg(target_arch = "wasm32")]
{ {
@ -96,9 +105,10 @@ impl WinitWindows {
self.winit_to_window_id.get(&id).cloned() self.winit_to_window_id.get(&id).cloned()
} }
} }
fn get_fitting_videomode( pub fn get_fitting_videomode(
monitor: &winit::monitor::MonitorHandle, monitor: &winit::monitor::MonitorHandle,
window: &Window, width: u32,
height: u32,
) -> winit::monitor::VideoMode { ) -> winit::monitor::VideoMode {
let mut modes = monitor.video_modes().collect::<Vec<_>>(); let mut modes = monitor.video_modes().collect::<Vec<_>>();
@ -111,11 +121,9 @@ fn get_fitting_videomode(
modes.sort_by(|a, b| { modes.sort_by(|a, b| {
use std::cmp::Ordering::*; use std::cmp::Ordering::*;
match abs_diff(a.size().width, window.width).cmp(&abs_diff(b.size().width, window.width)) { match abs_diff(a.size().width, width).cmp(&abs_diff(b.size().width, width)) {
Equal => { Equal => {
match abs_diff(a.size().height, window.height) match abs_diff(a.size().height, height).cmp(&abs_diff(b.size().height, height)) {
.cmp(&abs_diff(b.size().height, window.height))
{
Equal => b.refresh_rate().cmp(&a.refresh_rate()), Equal => b.refresh_rate().cmp(&a.refresh_rate()),
default => default, default => default,
} }
@ -127,7 +135,7 @@ fn get_fitting_videomode(
modes.first().unwrap().clone() modes.first().unwrap().clone()
} }
fn get_best_videomode(monitor: &winit::monitor::MonitorHandle) -> winit::monitor::VideoMode { pub fn get_best_videomode(monitor: &winit::monitor::MonitorHandle) -> winit::monitor::VideoMode {
let mut modes = monitor.video_modes().collect::<Vec<_>>(); let mut modes = monitor.video_modes().collect::<Vec<_>>();
modes.sort_by(|a, b| { modes.sort_by(|a, b| {
use std::cmp::Ordering::*; use std::cmp::Ordering::*;

View File

@ -48,11 +48,13 @@ fn bounce_system(
windows: Res<Windows>, windows: Res<Windows>,
mut sprites: Query<(&Transform, &mut Velocity)>, mut sprites: Query<(&Transform, &mut Velocity)>,
) { ) {
let Window { width, height, .. } = windows.get_primary().expect("No primary window"); let window = windows.get_primary().expect("No primary window");
let left = *width as f32 / -2.0; let width = window.width();
let right = *width as f32 / 2.0; let height = window.height();
let bottom = *height as f32 / -2.0; let left = width as f32 / -2.0;
let top = *height as f32 / 2.0; let right = width as f32 / 2.0;
let bottom = height as f32 / -2.0;
let top = height as f32 / 2.0;
sprites sprites
.iter() .iter()
// Batch size of 32 is chosen to limit the overhead of // Batch size of 32 is chosen to limit the overhead of

View File

@ -5,12 +5,22 @@ fn main() {
App::build() App::build()
.add_resource(WindowDescriptor { .add_resource(WindowDescriptor {
title: "I am a window!".to_string(), title: "I am a window!".to_string(),
width: 300, width: 500,
height: 300, height: 300,
vsync: true, vsync: true,
resizable: false, resizable: false,
..Default::default() ..Default::default()
}) })
.add_default_plugins() .add_default_plugins()
.add_system(change_title.system())
.run(); .run();
} }
/// This system will then change the title during execution
fn change_title(time: Res<Time>, mut windows: ResMut<Windows>) {
let window = windows.get_primary_mut().unwrap();
window.set_title(format!(
"Seconds since startup: {}",
time.seconds_since_startup.round()
));
}