Skip to content

Commit

Permalink
Enable creating "virtual" windows without corresponding OS window
Browse files Browse the repository at this point in the history
Virtual windows will by default not have a surface texture associated to
them, but implementors can set the texture in `ExtractedWindow`
manually.

This is intended to be used when embedding games into other appications
like editors or for running games headless.
  • Loading branch information
MDeiml committed Oct 14, 2022
1 parent 92ba622 commit eee987b
Show file tree
Hide file tree
Showing 3 changed files with 110 additions and 56 deletions.
6 changes: 3 additions & 3 deletions crates/bevy_render/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -147,9 +147,9 @@ impl Plugin for RenderPlugin {
let instance = wgpu::Instance::new(backends);
let surface = {
let windows = app.world.resource_mut::<bevy_window::Windows>();
let raw_handle = windows.get_primary().map(|window| unsafe {
let handle = window.raw_window_handle().get_handle();
instance.create_surface(&handle)
let raw_handle = windows.get_primary().and_then(|window| unsafe {
let handle = window.raw_window_handle()?.get_handle();
Some(instance.create_surface(&handle))
});
raw_handle
};
Expand Down
100 changes: 52 additions & 48 deletions crates/bevy_render/src/view/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ impl Plugin for WindowRenderPlugin {

pub struct ExtractedWindow {
pub id: WindowId,
pub handle: RawWindowHandleWrapper,
pub handle: Option<RawWindowHandleWrapper>,
pub physical_width: u32,
pub physical_height: u32,
pub present_mode: PresentMode,
Expand Down Expand Up @@ -132,6 +132,8 @@ pub struct WindowSurfaces {

/// Creates and (re)configures window surfaces, and obtains a swapchain texture for rendering.
///
/// This will not handle [virtual windows](Window::new_virtual).
///
/// NOTE: `get_current_texture` in `prepare_windows` can take a long time if the GPU workload is
/// the performance bottleneck. This can be seen in profiles as multiple prepare-stage systems all
/// taking an unusually long time to complete, and all finishing at about the same time as the
Expand Down Expand Up @@ -163,56 +165,58 @@ pub fn prepare_windows(
) {
let window_surfaces = window_surfaces.deref_mut();
for window in windows.windows.values_mut() {
let surface = window_surfaces
.surfaces
.entry(window.id)
.or_insert_with(|| unsafe {
// NOTE: On some OSes this MUST be called from the main thread.
render_instance.create_surface(&window.handle.get_handle())
});

let swap_chain_descriptor = wgpu::SurfaceConfiguration {
format: *surface
.get_supported_formats(&render_adapter)
.get(0)
.unwrap_or_else(|| {
panic!(
"No supported formats found for surface {:?} on adapter {:?}",
surface, render_adapter
)
}),
width: window.physical_width,
height: window.physical_height,
usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
present_mode: match window.present_mode {
PresentMode::Fifo => wgpu::PresentMode::Fifo,
PresentMode::Mailbox => wgpu::PresentMode::Mailbox,
PresentMode::Immediate => wgpu::PresentMode::Immediate,
PresentMode::AutoVsync => wgpu::PresentMode::AutoVsync,
PresentMode::AutoNoVsync => wgpu::PresentMode::AutoNoVsync,
},
};

// Do the initial surface configuration if it hasn't been configured yet. Or if size or
// present mode changed.
if window_surfaces.configured_windows.insert(window.id)
|| window.size_changed
|| window.present_mode_changed
{
render_device.configure_surface(surface, &swap_chain_descriptor);
}
if let Some(handle) = &window.handle {
let surface = window_surfaces
.surfaces
.entry(window.id)
.or_insert_with(|| unsafe {
// NOTE: On some OSes this MUST be called from the main thread.
render_instance.create_surface(&handle.get_handle())
});

let frame = match surface.get_current_texture() {
Ok(swap_chain_frame) => swap_chain_frame,
Err(wgpu::SurfaceError::Outdated) => {
let swap_chain_descriptor = wgpu::SurfaceConfiguration {
format: *surface
.get_supported_formats(&render_adapter)
.get(0)
.unwrap_or_else(|| {
panic!(
"No supported formats found for surface {:?} on adapter {:?}",
surface, render_adapter
)
}),
width: window.physical_width,
height: window.physical_height,
usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
present_mode: match window.present_mode {
PresentMode::Fifo => wgpu::PresentMode::Fifo,
PresentMode::Mailbox => wgpu::PresentMode::Mailbox,
PresentMode::Immediate => wgpu::PresentMode::Immediate,
PresentMode::AutoVsync => wgpu::PresentMode::AutoVsync,
PresentMode::AutoNoVsync => wgpu::PresentMode::AutoNoVsync,
},
};

// Do the initial surface configuration if it hasn't been configured yet. Or if size or
// present mode changed.
if window_surfaces.configured_windows.insert(window.id)
|| window.size_changed
|| window.present_mode_changed
{
render_device.configure_surface(surface, &swap_chain_descriptor);
surface
.get_current_texture()
.expect("Error reconfiguring surface")
}
err => err.expect("Failed to acquire next swap chain texture!"),
};

window.swap_chain_texture = Some(TextureView::from(frame));
let frame = match surface.get_current_texture() {
Ok(swap_chain_frame) => swap_chain_frame,
Err(wgpu::SurfaceError::Outdated) => {
render_device.configure_surface(surface, &swap_chain_descriptor);
surface
.get_current_texture()
.expect("Error reconfiguring surface")
}
err => err.expect("Failed to acquire next swap chain texture!"),
};

window.swap_chain_texture = Some(TextureView::from(frame));
}
}
}
60 changes: 55 additions & 5 deletions crates/bevy_window/src/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ impl WindowResizeConstraints {
}
}

/// An operating system window that can present content and receive user input.
/// An operating system or virtual window that can present content and receive user input.
///
/// To create a window, use a [`EventWriter<CreateWindow>`](`crate::CreateWindow`).
///
Expand Down Expand Up @@ -206,7 +206,7 @@ pub struct Window {
cursor_visible: bool,
cursor_locked: bool,
physical_cursor_position: Option<DVec2>,
raw_window_handle: RawWindowHandleWrapper,
raw_window_handle: Option<RawWindowHandleWrapper>,
focused: bool,
mode: WindowMode,
canvas: Option<String>,
Expand Down Expand Up @@ -335,14 +335,57 @@ impl Window {
cursor_locked: window_descriptor.cursor_locked,
cursor_icon: CursorIcon::Default,
physical_cursor_position: None,
raw_window_handle: RawWindowHandleWrapper::new(raw_window_handle),
raw_window_handle: Some(RawWindowHandleWrapper::new(raw_window_handle)),
focused: true,
mode: window_descriptor.mode,
canvas: window_descriptor.canvas.clone(),
fit_canvas_to_parent: window_descriptor.fit_canvas_to_parent,
command_queue: Vec::new(),
}
}

/// Creates a new virtual [`Window`].
///
/// This window does not have to correspond to an operator system window.
///
/// It differs from a non-virtual window, in that the caller is responsible
/// for creating and presenting surface textures and inserting them into
/// [`ExtractedWindow`](https://docs.rs/bevy/*/bevy/render/view/struct.ExtractedWindow.html).
pub fn new_virtual(
id: WindowId,
window_descriptor: &WindowDescriptor,
physical_width: u32,
physical_height: u32,
scale_factor: f64,
position: Option<IVec2>,
) -> Self {
Window {
id,
requested_width: window_descriptor.width,
requested_height: window_descriptor.height,
position,
physical_width,
physical_height,
resize_constraints: window_descriptor.resize_constraints,
scale_factor_override: window_descriptor.scale_factor_override,
backend_scale_factor: scale_factor,
title: window_descriptor.title.clone(),
present_mode: window_descriptor.present_mode,
resizable: window_descriptor.resizable,
decorations: window_descriptor.decorations,
cursor_visible: window_descriptor.cursor_visible,
cursor_locked: window_descriptor.cursor_locked,
cursor_icon: CursorIcon::Default,
physical_cursor_position: None,
raw_window_handle: None,
focused: true,
mode: window_descriptor.mode,
canvas: window_descriptor.canvas.clone(),
fit_canvas_to_parent: window_descriptor.fit_canvas_to_parent,
command_queue: Vec::new(),
}
}

/// Get the window's [`WindowId`].
#[inline]
pub fn id(&self) -> WindowId {
Expand Down Expand Up @@ -719,8 +762,15 @@ impl Window {
pub fn is_focused(&self) -> bool {
self.focused
}
/// Get the [`RawWindowHandleWrapper`] corresponding to this window
pub fn raw_window_handle(&self) -> RawWindowHandleWrapper {
/// Get the [`RawWindowHandleWrapper`] corresponding to this window.
///
/// A return value of `None` signifies that this is a virtual window and does not
/// correspond to an OS window. The creator of the window is responsible
/// for creating and presenting surface textures and inserting them into
/// [`ExtractedWindow`](https://docs.rs/bevy/*/bevy/render/view/struct.ExtractedWindow.html).
///
/// See [`Self::new_virtual`].
pub fn raw_window_handle(&self) -> Option<RawWindowHandleWrapper> {
self.raw_window_handle.clone()
}

Expand Down

0 comments on commit eee987b

Please sign in to comment.