Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Windows as Entities #4947

Closed
wants to merge 32 commits into from
Closed
Show file tree
Hide file tree
Changes from 29 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
3e5ff59
Replacing `Window_id` with `Entity`
Weibye Jun 6, 2022
07da9f5
Adding `WindowCommandsExtensions` for `Commands`
Weibye Jun 6, 2022
0c11956
Exploding `Window` into multiple coponents
Weibye Jun 6, 2022
ee35b2a
Adding `WindowCommands` to replace methods previously existing on `Wi…
Weibye Jun 6, 2022
f9a4529
Adding new `ExitCondition`
Weibye Jun 6, 2022
0e0c43c
Updating bevy_window to follow new pattern more
Weibye Jun 6, 2022
9325098
Deleting `Windows` as this should now be replaced by a query
Weibye Jun 6, 2022
b1f57bb
Split `change_window` system into multiple system that handle `Window…
Weibye Jun 6, 2022
29453d7
Start reworking `winit_runner()`
Weibye Jun 6, 2022
b64d741
Add initial sketch of example
Weibye Jun 6, 2022
1e1599b
Adding notes
Weibye Jun 6, 2022
00cee99
Fully implement spawning of created window
Weibye Jun 6, 2022
7c4b815
Start updating `bevy_render` to new patterns
Weibye Jun 6, 2022
317fef3
More cleanup of winit main loop
Weibye Jun 9, 2022
28944d5
minor fix
Weibye Jun 9, 2022
1d95cdb
And more tweaks
Weibye Jun 10, 2022
8df34dc
continuing refactor of winit-loop
Weibye Jun 10, 2022
537e4b0
Finish setting up components for new windows
Weibye Jun 10, 2022
c97365a
cleanup
Weibye Jun 10, 2022
0e05569
Start attempting to get rendering to compile again
Weibye Jun 10, 2022
d852a55
Updating `bevy_text`
Weibye Jun 10, 2022
dc6875c
Fixing up `bevy_ui`
Weibye Jun 10, 2022
0696336
Cleanup
Weibye Jun 10, 2022
aca8773
Fixing up `bevy_ui`
Weibye Jun 10, 2022
f6f793a
fixing examples
Weibye Jun 10, 2022
c2b99cd
Fixing up more examples
Weibye Jun 12, 2022
98da3ce
Every finally compiles!
Weibye Jun 13, 2022
60e81be
Fixing runtime bugs
Weibye Jun 13, 2022
5099960
Wrangling with timing issues in render
Weibye Jun 13, 2022
6182f9f
Window setup operations need to happen during build-time
Weibye Jun 14, 2022
c409ceb
Further attempts at spawning components in build-time
Weibye Jun 26, 2022
542660a
Using system-state apply
Weibye Jun 26, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions crates/bevy_ecs/src/system/commands/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,12 @@ impl<'w, 's> Commands<'w, 's> {
Self { queue, entities }
}

// TODO: Can this be solved in any other way?
/// Returns true if this commands has the entity present
pub fn has_entity(&self, entity: Entity) -> bool {
return self.entities.contains(entity);
}
Comment on lines +65 to +69
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is needed to be able to not grant WindowCommands on an entity that does not exist, see: https://github.com/bevyengine/bevy/pull/4947/files#diff-9e0af223dcea6a0265d880b70764a4362c0c84438fb28f5f558a369cda91021cR27


/// Creates a new empty [`Entity`] and returns an [`EntityCommands`] builder for it.
///
/// To directly spawn an entity with a [`Bundle`] included, you can use
Expand Down
48 changes: 31 additions & 17 deletions crates/bevy_render/src/camera/camera.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,16 @@ use bevy_ecs::{
component::Component,
entity::Entity,
event::EventReader,
prelude::With,
query::Added,
reflect::ReflectComponent,
system::{Commands, ParamSet, Query, Res},
};
use bevy_math::{Mat4, UVec2, Vec2, Vec3};
use bevy_reflect::prelude::*;
use bevy_transform::components::GlobalTransform;
use bevy_utils::HashSet;
use bevy_window::{WindowCreated, WindowId, WindowResized, Windows};
use bevy_utils::{default, HashSet};
use bevy_window::{Window, WindowCreated, WindowResized, WindowResolution};
use serde::{Deserialize, Serialize};
use std::{borrow::Cow, ops::Range};
use wgpu::Extent3d;
Expand Down Expand Up @@ -97,7 +98,7 @@ impl Default for Camera {
priority: 0,
viewport: None,
computed: Default::default(),
target: Default::default(),
target: RenderTarget::PrimaryWindow,
depth_calculation: Default::default(),
}
}
Expand Down Expand Up @@ -220,15 +221,17 @@ impl CameraRenderGraph {
/// swapchain or an [`Image`].
#[derive(Debug, Clone, Reflect, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub enum RenderTarget {
/// Renders the camera's view to the current primary window
PrimaryWindow,
/// Window to which the camera's view is rendered.
Window(WindowId),
Window(Entity),
/// Image to which the camera's view is rendered.
Image(Handle<Image>),
}

impl Default for RenderTarget {
fn default() -> Self {
Self::Window(Default::default())
Self::PrimaryWindow
}
}

Expand All @@ -245,20 +248,28 @@ impl RenderTarget {
RenderTarget::Image(image_handle) => {
images.get(image_handle).map(|image| &image.texture_view)
}
RenderTarget::PrimaryWindow => todo!(),
}
}

pub fn get_render_target_info(
&self,
windows: &Windows,
windows: &Query<(&WindowResolution), With<Window>>, // TODO: Maybe this could just be a Vec?
images: &Assets<Image>,
) -> Option<RenderTargetInfo> {
Some(match self {
RenderTarget::Window(window_id) => {
let window = windows.get(*window_id)?;
RenderTargetInfo {
physical_size: UVec2::new(window.physical_width(), window.physical_height()),
scale_factor: window.scale_factor(),
if let Ok(resolution) = windows.get(*window_id) {
RenderTargetInfo {
physical_size: UVec2::new(
resolution.physical_width(),
resolution.physical_height(),
),
scale_factor: resolution.scale_factor(),
}
} else {
// TODO: Helpful panic comment
panic!("Render target does not point to a valid window");
}
}
RenderTarget::Image(image_handle) => {
Expand All @@ -269,17 +280,20 @@ impl RenderTarget {
scale_factor: 1.0,
}
}
RenderTarget::PrimaryWindow => todo!(),
})
}

// Check if this render target is contained in the given changed windows or images.
fn is_changed(
&self,
changed_window_ids: &[WindowId],
changed_window_ids: &[Entity],
changed_image_handles: &HashSet<&Handle<Image>>,
) -> bool {
match self {
RenderTarget::Window(window_id) => changed_window_ids.contains(window_id),
RenderTarget::Image(image_handle) => changed_image_handles.contains(&image_handle),
RenderTarget::PrimaryWindow => todo!(),
}
}
}
Expand All @@ -303,32 +317,32 @@ pub fn camera_system<T: CameraProjection + Component>(
mut window_resized_events: EventReader<WindowResized>,
mut window_created_events: EventReader<WindowCreated>,
mut image_asset_events: EventReader<AssetEvent<Image>>,
windows: Res<Windows>,
windows: Query<&WindowResolution, With<Window>>,
images: Res<Assets<Image>>,
mut queries: ParamSet<(
Query<(Entity, &mut Camera, &mut T)>,
Query<Entity, Added<Camera>>,
)>,
) {
let mut changed_window_ids = Vec::new();
let mut changed_window_ids: Vec<Entity> = Vec::new();
// handle resize events. latest events are handled first because we only want to resize each
// window once
for event in window_resized_events.iter().rev() {
if changed_window_ids.contains(&event.id) {
if changed_window_ids.contains(&event.entity) {
continue;
}

changed_window_ids.push(event.id);
changed_window_ids.push(event.entity);
}

// handle resize events. latest events are handled first because we only want to resize each
// window once
for event in window_created_events.iter().rev() {
if changed_window_ids.contains(&event.id) {
if changed_window_ids.contains(&event.entity) {
continue;
}

changed_window_ids.push(event.id);
changed_window_ids.push(event.entity);
}

let changed_image_handles: HashSet<&Handle<Image>> = image_asset_events
Expand Down
58 changes: 46 additions & 12 deletions crates/bevy_render/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@ pub mod prelude {
};
}

use bevy_window::{PrimaryWindow, Window, WindowHandle};
pub use once_cell;
use settings::WgpuSettings;

use crate::{
camera::CameraPlugin,
Expand All @@ -42,8 +44,9 @@ use crate::{
};
use bevy_app::{App, AppLabel, Plugin};
use bevy_asset::{AddAsset, AssetServer};
use bevy_ecs::prelude::*;
use bevy_ecs::{prelude::*, system::SystemState};
use bevy_utils::tracing::debug;
use core::panic;
use std::ops::{Deref, DerefMut};

/// Contains the default Bevy rendering backend based on wgpu.
Expand Down Expand Up @@ -114,27 +117,58 @@ struct ScratchRenderWorld(World);
impl Plugin for RenderPlugin {
/// Initializes the renderer, sets up the [`RenderStage`](RenderStage) and creates the rendering sub-app.
fn build(&self, app: &mut App) {
app.add_asset::<Shader>()
.add_debug_asset::<Shader>()
.init_asset_loader::<ShaderLoader>()
.init_debug_asset_loader::<ShaderLoader>()
.register_type::<Color>();

let options = app
.world
.get_resource::<settings::WgpuSettings>()
.cloned()
.unwrap_or_default();

app.add_asset::<Shader>()
.add_debug_asset::<Shader>()
.init_asset_loader::<ShaderLoader>()
.init_debug_asset_loader::<ShaderLoader>()
.register_type::<Color>();
bevy_utils::tracing::info!("Tryin to request primarywindow");

let mut system_state: SystemState<(
Query<&WindowHandle, With<Window>>,
Res<PrimaryWindow>,
// Res<WgpuSettings>,
)> = SystemState::new(&mut app.world);
let (
window_query,
primary_window,
// options, // This was .clone().unwrap_or_default(). Will this work the same?
) = system_state.get(&mut app.world);

bevy_utils::tracing::info!("Should have resource here");

if let Some(backends) = options.backends {
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)
});
raw_handle
// TODO: This can probably be cleaner
if let Ok(handle_component) = window_query.get(
primary_window
.window
.expect("There should be a primary window"),
) {
Weibye marked this conversation as resolved.
Show resolved Hide resolved
// TODO: Make sure this is ok
unsafe {
let handle = handle_component.raw_window_handle().get_handle();
Some(instance.create_surface(&handle))
}
} else {
// TODO: Helpful panic comment
panic!("No WindowHandle component on primary window");
}
// let handle_component =
// // 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)
// });
// raw_handle
};
let request_adapter_options = wgpu::RequestAdapterOptions {
power_preference: options.power_preference,
Expand Down
54 changes: 31 additions & 23 deletions crates/bevy_render/src/view/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@ use crate::{
use bevy_app::{App, Plugin};
use bevy_ecs::prelude::*;
use bevy_utils::{tracing::debug, HashMap, HashSet};
use bevy_window::{PresentMode, RawWindowHandleWrapper, WindowClosed, WindowId, Windows};
use bevy_window::{
PresentMode, RawWindowHandleWrapper, Window, WindowClosed, WindowHandle, WindowPresentation,
WindowResolution,
};
use std::ops::{Deref, DerefMut};
use wgpu::TextureFormat;

Expand Down Expand Up @@ -39,7 +42,7 @@ impl Plugin for WindowRenderPlugin {
}

pub struct ExtractedWindow {
pub id: WindowId,
pub id: Entity,
pub handle: RawWindowHandleWrapper,
pub physical_width: u32,
pub physical_height: u32,
Expand All @@ -50,11 +53,11 @@ pub struct ExtractedWindow {

#[derive(Default)]
pub struct ExtractedWindows {
pub windows: HashMap<WindowId, ExtractedWindow>,
pub windows: HashMap<Entity, ExtractedWindow>,
}

impl Deref for ExtractedWindows {
type Target = HashMap<WindowId, ExtractedWindow>;
type Target = HashMap<Entity, ExtractedWindow>;

fn deref(&self) -> &Self::Target {
&self.windows
Expand All @@ -70,27 +73,32 @@ impl DerefMut for ExtractedWindows {
fn extract_windows(
mut render_world: ResMut<RenderWorld>,
mut closed: EventReader<WindowClosed>,
windows: Res<Windows>,
windows: Query<
(
Entity,
&WindowResolution,
&WindowHandle,
&WindowPresentation,
),
With<Window>,
>,
) {
let mut extracted_windows = render_world.get_resource_mut::<ExtractedWindows>().unwrap();
for window in windows.iter() {
for (entity, resolution, handle, presentation) in windows.iter() {
let (new_width, new_height) = (
window.physical_width().max(1),
window.physical_height().max(1),
resolution.physical_width().max(1),
resolution.physical_height().max(1),
);

let mut extracted_window =
extracted_windows
.entry(window.id())
.or_insert(ExtractedWindow {
id: window.id(),
handle: window.raw_window_handle(),
physical_width: new_width,
physical_height: new_height,
present_mode: window.present_mode(),
swap_chain_texture: None,
size_changed: false,
});
let mut extracted_window = extracted_windows.entry(entity).or_insert(ExtractedWindow {
id: entity,
handle: handle.raw_window_handle(),
physical_width: new_width,
physical_height: new_height,
present_mode: presentation.present_mode(),
swap_chain_texture: None,
size_changed: false,
});

// NOTE: Drop the swap chain frame here
extracted_window.swap_chain_texture = None;
Expand All @@ -110,15 +118,15 @@ fn extract_windows(
}
}
for closed_window in closed.iter() {
extracted_windows.remove(&closed_window.id);
extracted_windows.remove(&closed_window.entity);
}
}

#[derive(Default)]
pub struct WindowSurfaces {
surfaces: HashMap<WindowId, wgpu::Surface>,
surfaces: HashMap<Entity, wgpu::Surface>,
/// List of windows that we have already called the initial `configure_surface` for
configured_windows: HashSet<WindowId>,
configured_windows: HashSet<Entity>,
}

pub fn prepare_windows(
Expand Down
Loading