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

[Merged by Bors] - Windows as Entities #5589

Closed
wants to merge 25 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
f2adf37
Windows as Entities
Aceeri Aug 16, 2022
9641d01
Fix scene viewer for windows as entities
Aceeri Dec 26, 2022
fb7c553
simplify multiple_windows
cart Jan 3, 2023
57575f9
Flatten out canvas
cart Jan 3, 2023
235a8f7
Combined Cursor type
cart Jan 3, 2023
85bbeee
Remove WindowState in favor of internal queues
cart Jan 3, 2023
eb33c00
Simpler Window queries in examples
cart Jan 3, 2023
29cccfb
Fix scale factor setting on resolution
Aceeri Jan 3, 2023
87a12c3
Rename some stuff, better comments
Aceeri Jan 3, 2023
40b134c
Fix scene viewer
Aceeri Jan 3, 2023
c53ae75
Move hit test to cursor
Aceeri Jan 5, 2023
22b0e9b
Formatting
Aceeri Jan 5, 2023
266294a
Remove future work comment
Aceeri Jan 5, 2023
9e0b8bc
Fix unnecesary reference
Aceeri Jan 5, 2023
45c6c28
Re-add feature flagging for initial window creation
Aceeri Jan 5, 2023
dd7d414
Don't set position/size if it is already equal
Aceeri Jan 5, 2023
022ca4a
Fix up fallthrough example
Aceeri Jan 5, 2023
b0d5173
Try to avoid any funkiness with window managers by making sure we act…
Aceeri Jan 7, 2023
5c4ff0c
Handle scale factor properly
cart Jan 17, 2023
cbebdad
Scale factor convenience method for Window
Aceeri Jan 18, 2023
831c488
Fix up logic on creating fullscreen/windowed modes
Aceeri Jan 18, 2023
2e7d0fe
Organize the window component a bit better
Aceeri Jan 18, 2023
83fa3e2
Formatting, unused imports
Aceeri Jan 18, 2023
e3f80cb
Add physical width/height methods to Window
Aceeri Jan 18, 2023
2da4815
Fix window resize feedback bug
cart Jan 19, 2023
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
142 changes: 83 additions & 59 deletions crates/bevy_render/src/camera/camera.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ use bevy_ecs::{
component::Component,
entity::Entity,
event::EventReader,
prelude::With,
reflect::ReflectComponent,
system::{Commands, Query, Res},
};
Expand All @@ -21,7 +22,10 @@ use bevy_reflect::prelude::*;
use bevy_reflect::FromReflect;
use bevy_transform::components::GlobalTransform;
use bevy_utils::HashSet;
use bevy_window::{WindowCreated, WindowId, WindowResized, Windows};
use bevy_window::{
NormalizedWindowRef, PrimaryWindow, Window, WindowCreated, WindowRef, WindowResized,
};

use std::{borrow::Cow, ops::Range};
use wgpu::{Extent3d, TextureFormat};

Expand Down Expand Up @@ -325,10 +329,21 @@ impl CameraRenderGraph {

/// The "target" that a [`Camera`] will render to. For example, this could be a [`Window`](bevy_window::Window)
/// swapchain or an [`Image`].
#[derive(Debug, Clone, Reflect, PartialEq, Eq, Hash, PartialOrd, Ord)]
#[derive(Debug, Clone, Reflect)]
pub enum RenderTarget {
/// Window to which the camera's view is rendered.
Window(WindowId),
Window(WindowRef),
/// Image to which the camera's view is rendered.
Image(Handle<Image>),
}

/// Normalized version of the render target.
///
/// Once we have this we shouldn't need to resolve it down anymore.
#[derive(Debug, Clone, Reflect, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub enum NormalizedRenderTarget {
/// Window to which the camera's view is rendered.
Window(NormalizedWindowRef),
/// Image to which the camera's view is rendered.
Image(Handle<Image>),
}
Expand All @@ -340,16 +355,28 @@ impl Default for RenderTarget {
}

impl RenderTarget {
/// Normalize the render target down to a more concrete value, mostly used for equality comparisons.
pub fn normalize(&self, primary_window: Option<Entity>) -> Option<NormalizedRenderTarget> {
match self {
RenderTarget::Window(window_ref) => window_ref
.normalize(primary_window)
.map(NormalizedRenderTarget::Window),
RenderTarget::Image(handle) => Some(NormalizedRenderTarget::Image(handle.clone())),
}
}
}

impl NormalizedRenderTarget {
pub fn get_texture_view<'a>(
&self,
windows: &'a ExtractedWindows,
images: &'a RenderAssets<Image>,
) -> Option<&'a TextureView> {
match self {
RenderTarget::Window(window_id) => windows
.get(window_id)
NormalizedRenderTarget::Window(window_ref) => windows
.get(&window_ref.entity())
.and_then(|window| window.swap_chain_texture.as_ref()),
RenderTarget::Image(image_handle) => {
NormalizedRenderTarget::Image(image_handle) => {
images.get(image_handle).map(|image| &image.texture_view)
}
}
Expand All @@ -362,47 +389,55 @@ impl RenderTarget {
images: &'a RenderAssets<Image>,
) -> Option<TextureFormat> {
match self {
RenderTarget::Window(window_id) => windows
.get(window_id)
NormalizedRenderTarget::Window(window_ref) => windows
.get(&window_ref.entity())
.and_then(|window| window.swap_chain_texture_format),
RenderTarget::Image(image_handle) => {
NormalizedRenderTarget::Image(image_handle) => {
images.get(image_handle).map(|image| image.texture_format)
}
}
}

pub fn get_render_target_info(
pub fn get_render_target_info<'a>(
&self,
windows: &Windows,
resolutions: impl IntoIterator<Item = (Entity, &'a Window)>,
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(),
}
}
RenderTarget::Image(image_handle) => {
match self {
NormalizedRenderTarget::Window(window_ref) => resolutions
.into_iter()
.find(|(entity, _)| *entity == window_ref.entity())
.map(|(_, window)| RenderTargetInfo {
physical_size: UVec2::new(
window.resolution.physical_width(),
window.resolution.physical_height(),
),
scale_factor: window.resolution.scale_factor(),
}),
NormalizedRenderTarget::Image(image_handle) => {
let image = images.get(image_handle)?;
let Extent3d { width, height, .. } = image.texture_descriptor.size;
RenderTargetInfo {
Some(RenderTargetInfo {
physical_size: UVec2::new(width, height),
scale_factor: 1.0,
}
})
}
})
}
}

// 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: &HashSet<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),
NormalizedRenderTarget::Window(window_ref) => {
changed_window_ids.contains(&window_ref.entity())
}
NormalizedRenderTarget::Image(image_handle) => {
changed_image_handles.contains(&image_handle)
}
}
}
}
Expand Down Expand Up @@ -431,29 +466,16 @@ 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>,
primary_window: Query<Entity, With<PrimaryWindow>>,
windows: Query<(Entity, &Window)>,
images: Res<Assets<Image>>,
mut cameras: Query<(&mut Camera, &mut T)>,
) {
let mut changed_window_ids = Vec::new();

// Collect all unique window IDs of changed windows by inspecting created windows
for event in window_created_events.iter() {
if changed_window_ids.contains(&event.id) {
continue;
}

changed_window_ids.push(event.id);
}
let primary_window = primary_window.iter().next();

// Collect all unique window IDs of changed windows by inspecting resized windows
for event in window_resized_events.iter() {
if changed_window_ids.contains(&event.id) {
continue;
}

changed_window_ids.push(event.id);
}
let mut changed_window_ids = HashSet::new();
changed_window_ids.extend(window_created_events.iter().map(|event| event.window));
changed_window_ids.extend(window_resized_events.iter().map(|event| event.window));

let changed_image_handles: HashSet<&Handle<Image>> = image_asset_events
.iter()
Expand All @@ -472,26 +494,26 @@ pub fn camera_system<T: CameraProjection + Component>(
.as_ref()
.map(|viewport| viewport.physical_size);

if camera
.target
.is_changed(&changed_window_ids, &changed_image_handles)
|| camera.is_added()
|| camera_projection.is_changed()
|| camera.computed.old_viewport_size != viewport_size
{
camera.computed.target_info = camera.target.get_render_target_info(&windows, &images);
camera.computed.old_viewport_size = viewport_size;
if let Some(size) = camera.logical_viewport_size() {
camera_projection.update(size.x, size.y);
camera.computed.projection_matrix = camera_projection.get_projection_matrix();
if let Some(normalized_target) = camera.target.normalize(primary_window) {
if normalized_target.is_changed(&changed_window_ids, &changed_image_handles)
|| camera.is_added()
|| camera_projection.is_changed()
|| camera.computed.old_viewport_size != viewport_size
{
camera.computed.target_info =
normalized_target.get_render_target_info(&windows, &images);
if let Some(size) = camera.logical_viewport_size() {
camera_projection.update(size.x, size.y);
camera.computed.projection_matrix = camera_projection.get_projection_matrix();
}
}
}
}
}

#[derive(Component, Debug)]
pub struct ExtractedCamera {
pub target: RenderTarget,
pub target: Option<NormalizedRenderTarget>,
pub physical_viewport_size: Option<UVec2>,
pub physical_target_size: Option<UVec2>,
pub viewport: Option<Viewport>,
Expand All @@ -510,7 +532,9 @@ pub fn extract_cameras(
&VisibleEntities,
)>,
>,
primary_window: Extract<Query<Entity, With<PrimaryWindow>>>,
) {
let primary_window = primary_window.iter().next();
for (entity, camera, camera_render_graph, transform, visible_entities) in query.iter() {
if !camera.is_active {
continue;
Expand All @@ -525,7 +549,7 @@ pub fn extract_cameras(
}
commands.get_or_spawn(entity).insert((
ExtractedCamera {
target: camera.target.clone(),
target: camera.target.normalize(primary_window),
viewport: camera.viewport.clone(),
physical_viewport_size: Some(viewport_size),
physical_target_size: Some(target_size),
Expand Down
6 changes: 3 additions & 3 deletions crates/bevy_render/src/camera/camera_driver_node.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::{
camera::{ExtractedCamera, RenderTarget},
camera::{ExtractedCamera, NormalizedRenderTarget},
render_graph::{Node, NodeRunError, RenderGraphContext, SlotValue},
renderer::RenderContext,
view::ExtractedWindows,
Expand Down Expand Up @@ -52,8 +52,8 @@ impl Node for CameraDriverNode {
}
previous_order_target = Some(new_order_target);
if let Ok((_, camera)) = self.cameras.get_manual(world, entity) {
if let RenderTarget::Window(id) = camera.target {
camera_windows.insert(id);
if let Some(NormalizedRenderTarget::Window(window_ref)) = camera.target {
camera_windows.insert(window_ref.entity());
}
graph
.run_sub_graph(camera.render_graph.clone(), vec![SlotValue::Entity(entity)])?;
Expand Down
21 changes: 11 additions & 10 deletions crates/bevy_render/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ pub mod prelude {
};
}

use bevy_window::{PrimaryWindow, RawHandleWrapper};
use globals::GlobalsPlugin;
pub use once_cell;

Expand All @@ -50,7 +51,7 @@ 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 std::{
any::TypeId,
Expand Down Expand Up @@ -138,17 +139,17 @@ impl Plugin for RenderPlugin {
.init_asset_loader::<ShaderLoader>()
.init_debug_asset_loader::<ShaderLoader>();

let mut system_state: SystemState<Query<&RawHandleWrapper, With<PrimaryWindow>>> =
SystemState::new(&mut app.world);
let primary_window = system_state.get(&app.world);

if let Some(backends) = self.wgpu_settings.backends {
let windows = app.world.resource_mut::<bevy_window::Windows>();
let instance = wgpu::Instance::new(backends);

let surface = windows
.get_primary()
.and_then(|window| window.raw_handle())
.map(|wrapper| unsafe {
let handle = wrapper.get_handle();
instance.create_surface(&handle)
});
let surface = primary_window.get_single().ok().map(|wrapper| unsafe {
// SAFETY: Plugins should be set up on the main thread.
let handle = wrapper.get_handle();
instance.create_surface(&handle)
});

let request_adapter_options = wgpu::RequestAdapterOptions {
power_preference: self.wgpu_settings.power_preference,
Expand Down
6 changes: 3 additions & 3 deletions crates/bevy_render/src/view/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -285,10 +285,10 @@ fn prepare_view_targets(
) {
let mut textures = HashMap::default();
for (entity, camera, view) in cameras.iter() {
if let Some(target_size) = camera.physical_target_size {
if let (Some(target_size), Some(target)) = (camera.physical_target_size, &camera.target) {
if let (Some(out_texture_view), Some(out_texture_format)) = (
camera.target.get_texture_view(&windows, &images),
camera.target.get_texture_format(&windows, &images),
target.get_texture_view(&windows, &images),
target.get_texture_format(&windows, &images),
) {
let size = Extent3d {
width: target_size.x,
Expand Down
Loading