Make raw_window_handle field in Window and ExtractedWindow an Option. (#6114)

# Objective

- Trying to make it possible to do write tests that don't require a raw window handle.
- Fixes https://github.com/bevyengine/bevy/issues/6106.

## Solution

- Make the interface and type changes.  Avoid accessing `None`.
---

## Changelog

- Converted `raw_window_handle` field in both `Window` and `ExtractedWindow` to `Option<RawWindowHandleWrapper>`.
- Revised accessor function `Window::raw_window_handle()` to return `Option<RawWindowHandleWrapper>`.
- Skip conditions in loops that would require a raw window handle (to create a `Surface`, for example).

## Migration Guide

`Window::raw_window_handle()` now returns `Option<RawWindowHandleWrapper>`.


Co-authored-by: targrub <62773321+targrub@users.noreply.github.com>
This commit is contained in:
targrub 2022-10-17 14:19:24 +00:00
parent bfd6285c3b
commit 964b047466
4 changed files with 87 additions and 19 deletions

View File

@ -144,14 +144,21 @@ impl Plugin for RenderPlugin {
.register_type::<Color>(); .register_type::<Color>();
if let Some(backends) = options.backends { if let Some(backends) = options.backends {
let windows = app.world.resource_mut::<bevy_window::Windows>();
let instance = wgpu::Instance::new(backends); let instance = wgpu::Instance::new(backends);
let surface = { let surface = {
let windows = app.world.resource_mut::<bevy_window::Windows>(); if let Some(window) = windows.get_primary() {
let raw_handle = windows.get_primary().map(|window| unsafe { if let Some(raw_window_handle) = window.raw_window_handle() {
let handle = window.raw_window_handle().get_handle(); unsafe {
instance.create_surface(&handle) let handle = raw_window_handle.get_handle();
}); Some(instance.create_surface(&handle))
raw_handle }
} else {
None
}
} else {
None
}
}; };
let request_adapter_options = wgpu::RequestAdapterOptions { let request_adapter_options = wgpu::RequestAdapterOptions {
power_preference: options.power_preference, power_preference: options.power_preference,

View File

@ -38,7 +38,7 @@ impl Plugin for WindowRenderPlugin {
pub struct ExtractedWindow { pub struct ExtractedWindow {
pub id: WindowId, pub id: WindowId,
pub handle: RawWindowHandleWrapper, pub raw_window_handle: Option<RawWindowHandleWrapper>,
pub physical_width: u32, pub physical_width: u32,
pub physical_height: u32, pub physical_height: u32,
pub present_mode: PresentMode, pub present_mode: PresentMode,
@ -83,7 +83,7 @@ fn extract_windows(
.entry(window.id()) .entry(window.id())
.or_insert(ExtractedWindow { .or_insert(ExtractedWindow {
id: window.id(), id: window.id(),
handle: window.raw_window_handle(), raw_window_handle: window.raw_window_handle(),
physical_width: new_width, physical_width: new_width,
physical_height: new_height, physical_height: new_height,
present_mode: window.present_mode(), present_mode: window.present_mode(),
@ -161,14 +161,20 @@ pub fn prepare_windows(
render_instance: Res<RenderInstance>, render_instance: Res<RenderInstance>,
render_adapter: Res<RenderAdapter>, render_adapter: Res<RenderAdapter>,
) { ) {
let window_surfaces = window_surfaces.deref_mut(); for window in windows
for window in windows.windows.values_mut() { .windows
.values_mut()
// value of raw_winndow_handle only None if synthetic test
.filter(|x| x.raw_window_handle.is_some())
{
let window_surfaces = window_surfaces.deref_mut();
let surface = window_surfaces let surface = window_surfaces
.surfaces .surfaces
.entry(window.id) .entry(window.id)
.or_insert_with(|| unsafe { .or_insert_with(|| unsafe {
// NOTE: On some OSes this MUST be called from the main thread. // NOTE: On some OSes this MUST be called from the main thread.
render_instance.create_surface(&window.handle.get_handle()) render_instance
.create_surface(&window.raw_window_handle.as_ref().unwrap().get_handle())
}); });
let swap_chain_descriptor = wgpu::SurfaceConfiguration { let swap_chain_descriptor = wgpu::SurfaceConfiguration {

View File

@ -187,6 +187,59 @@ impl WindowResizeConstraints {
/// } /// }
/// } /// }
/// ``` /// ```
/// To test code that uses `Window`s, one can test it with varying `Window` parameters by
/// creating `WindowResizeConstraints` or `WindowDescriptor` structures.
/// values by setting
///
/// ```
/// # use bevy_utils::default;
/// # use bevy_window::{Window, WindowCommand, WindowDescriptor, WindowId, WindowResizeConstraints};
/// # fn compute_window_area(w: &Window) -> f32 {
/// # w.width() * w.height()
/// # }
/// # fn grow_window_to_text_size(_window: &mut Window, _text: &str) {}
/// # fn set_new_title(window: &mut Window, text: String) { window.set_title(text); }
/// # fn a_window_resize_test() {
/// let resize_constraints = WindowResizeConstraints {
/// min_width: 400.0,
/// min_height: 300.0,
/// max_width: 1280.0,
/// max_height: 1024.0,
/// };
/// let window_descriptor = WindowDescriptor {
/// width: 800.0,
/// height: 600.0,
/// resizable: true,
/// resize_constraints,
/// ..default()
/// };
/// let mut window = Window::new(
/// WindowId::new(),
/// &window_descriptor,
/// 100, // physical_width
/// 100, // physical_height
/// 1.0, // scale_factor
/// None, None);
///
/// let area = compute_window_area(&window);
/// assert_eq!(area, 100.0 * 100.0);
///
/// grow_window_to_text_size(&mut window, "very long text that does not wrap");
/// assert_eq!(window.physical_width(), window.requested_width() as u32);
/// grow_window_to_text_size(&mut window, "very long text that does wrap, creating a maximum width window");
/// assert_eq!(window.physical_width(), window.requested_width() as u32);
///
/// set_new_title(&mut window, "new title".to_string());
/// let mut found_command = false;
/// for command in window.drain_commands() {
/// if command == (WindowCommand::SetTitle{ title: "new title".to_string() }) {
/// found_command = true;
/// break;
/// }
/// }
/// assert_eq!(found_command, true);
/// }
/// ```
#[derive(Debug)] #[derive(Debug)]
pub struct Window { pub struct Window {
id: WindowId, id: WindowId,
@ -206,7 +259,7 @@ pub struct Window {
cursor_visible: bool, cursor_visible: bool,
cursor_locked: bool, cursor_locked: bool,
physical_cursor_position: Option<DVec2>, physical_cursor_position: Option<DVec2>,
raw_window_handle: RawWindowHandleWrapper, raw_window_handle: Option<RawWindowHandleWrapper>,
focused: bool, focused: bool,
mode: WindowMode, mode: WindowMode,
canvas: Option<String>, canvas: Option<String>,
@ -217,7 +270,7 @@ pub struct Window {
/// ///
/// Bevy apps don't interact with this `enum` directly. Instead, they should use the methods on [`Window`]. /// Bevy apps don't interact with this `enum` directly. Instead, they should use the methods on [`Window`].
/// This `enum` is meant for authors of windowing plugins. See the documentation on [`crate::WindowPlugin`] for more information. /// This `enum` is meant for authors of windowing plugins. See the documentation on [`crate::WindowPlugin`] for more information.
#[derive(Debug)] #[derive(Debug, PartialEq)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
pub enum WindowCommand { pub enum WindowCommand {
/// Set the window's [`WindowMode`]. /// Set the window's [`WindowMode`].
@ -315,7 +368,7 @@ impl Window {
physical_height: u32, physical_height: u32,
scale_factor: f64, scale_factor: f64,
position: Option<IVec2>, position: Option<IVec2>,
raw_window_handle: RawWindowHandle, raw_window_handle: Option<RawWindowHandle>,
) -> Self { ) -> Self {
Window { Window {
id, id,
@ -335,7 +388,7 @@ impl Window {
cursor_locked: window_descriptor.cursor_locked, cursor_locked: window_descriptor.cursor_locked,
cursor_icon: CursorIcon::Default, cursor_icon: CursorIcon::Default,
physical_cursor_position: None, physical_cursor_position: None,
raw_window_handle: RawWindowHandleWrapper::new(raw_window_handle), raw_window_handle: raw_window_handle.map(RawWindowHandleWrapper::new),
focused: true, focused: true,
mode: window_descriptor.mode, mode: window_descriptor.mode,
canvas: window_descriptor.canvas.clone(), canvas: window_descriptor.canvas.clone(),
@ -719,9 +772,11 @@ impl Window {
pub fn is_focused(&self) -> bool { pub fn is_focused(&self) -> bool {
self.focused self.focused
} }
/// Get the [`RawWindowHandleWrapper`] corresponding to this window /// Get the [`RawWindowHandleWrapper`] corresponding to this window if set.
pub fn raw_window_handle(&self) -> RawWindowHandleWrapper { ///
self.raw_window_handle.clone() /// During normal use, this can be safely unwrapped; the value should only be [`None`] when synthetically constructed for tests.
pub fn raw_window_handle(&self) -> Option<RawWindowHandleWrapper> {
self.raw_window_handle.as_ref().cloned()
} }
/// The "html canvas" element selector. /// The "html canvas" element selector.

View File

@ -201,7 +201,7 @@ impl WinitWindows {
inner_size.height, inner_size.height,
scale_factor, scale_factor,
position, position,
raw_window_handle, Some(raw_window_handle),
) )
} }