Skip to content

Allow using RenderPlugin and WinitPlugin with WindowPlugin being disabled #19042

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

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
19 changes: 11 additions & 8 deletions crates/bevy_picking/src/input.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ use bevy_math::Vec2;
use bevy_platform::collections::{HashMap, HashSet};
use bevy_reflect::prelude::*;
use bevy_render::camera::RenderTarget;
use bevy_window::{PrimaryWindow, WindowEvent, WindowRef};
use bevy_window::{PrimaryWindow, WindowEvent, WindowPlugin, WindowRef};
use tracing::debug;

use crate::pointer::{
Expand Down Expand Up @@ -80,20 +80,23 @@ impl Plugin for PointerInputPlugin {
app.insert_resource(*self)
.add_systems(Startup, spawn_mouse_pointer)
.add_systems(
Last,
deactivate_touch_pointers.run_if(PointerInputPlugin::is_touch_enabled),
)
.register_type::<Self>()
.register_type::<PointerInputPlugin>();

if app.is_plugin_added::<WindowPlugin>() {
app.add_systems(
First,
(
mouse_pick_events.run_if(PointerInputPlugin::is_mouse_enabled),
touch_pick_events.run_if(PointerInputPlugin::is_touch_enabled),
)
.chain()
.in_set(PickSet::Input),
)
.add_systems(
Last,
deactivate_touch_pointers.run_if(PointerInputPlugin::is_touch_enabled),
)
.register_type::<Self>()
.register_type::<PointerInputPlugin>();
);
}
}
}

Expand Down
78 changes: 43 additions & 35 deletions crates/bevy_render/src/camera/camera_driver_node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ impl Node for CameraDriverNode {
world: &World,
) -> Result<(), NodeRunError> {
let sorted_cameras = world.resource::<SortedCameras>();
let windows = world.resource::<ExtractedWindows>();
let mut camera_windows = <HashSet<_>>::default();
for sorted_camera in &sorted_cameras.0 {
let Ok(camera) = self.cameras.get_manual(world, sorted_camera.entity) else {
Expand All @@ -41,15 +40,22 @@ impl Node for CameraDriverNode {
let mut run_graph = true;
if let Some(NormalizedRenderTarget::Window(window_ref)) = camera.target {
let window_entity = window_ref.entity();
if windows
.windows
.get(&window_entity)
.is_some_and(|w| w.physical_width > 0 && w.physical_height > 0)
{
camera_windows.insert(window_entity);
if let Some(windows) = world.get_resource::<ExtractedWindows>() {
if windows
.windows
.get(&window_entity)
.is_some_and(|w| w.physical_width > 0 && w.physical_height > 0)
{
camera_windows.insert(window_entity);
} else {
// The window doesn't exist anymore or zero-sized so we don't need to run the graph
run_graph = false;
}
} else {
// The window doesn't exist anymore or zero-sized so we don't need to run the graph
run_graph = false;
tracing::warn!(
"Camera is targeting Window {}, but ExtractedWindows is not available.",
window_entity
);
}
}
if run_graph {
Expand All @@ -61,35 +67,37 @@ impl Node for CameraDriverNode {

// wgpu (and some backends) require doing work for swap chains if you call `get_current_texture()` and `present()`
// This ensures that Bevy doesn't crash, even when there are no cameras (and therefore no work submitted).
for (id, window) in world.resource::<ExtractedWindows>().iter() {
if camera_windows.contains(id) {
continue;
}
if let Some(windows) = world.get_resource::<ExtractedWindows>() {
for (id, window) in windows.iter() {
if camera_windows.contains(id) {
continue;
}

let Some(swap_chain_texture) = &window.swap_chain_texture_view else {
continue;
};
let Some(swap_chain_texture) = &window.swap_chain_texture_view else {
continue;
};

#[cfg(feature = "trace")]
let _span = tracing::info_span!("no_camera_clear_pass").entered();
let pass_descriptor = RenderPassDescriptor {
label: Some("no_camera_clear_pass"),
color_attachments: &[Some(RenderPassColorAttachment {
view: swap_chain_texture,
resolve_target: None,
ops: Operations {
load: LoadOp::Clear(clear_color_global.to_linear().into()),
store: StoreOp::Store,
},
})],
depth_stencil_attachment: None,
timestamp_writes: None,
occlusion_query_set: None,
};
#[cfg(feature = "trace")]
let _span = tracing::info_span!("no_camera_clear_pass").entered();
let pass_descriptor = RenderPassDescriptor {
label: Some("no_camera_clear_pass"),
color_attachments: &[Some(RenderPassColorAttachment {
view: swap_chain_texture,
resolve_target: None,
ops: Operations {
load: LoadOp::Clear(clear_color_global.to_linear().into()),
store: StoreOp::Store,
},
})],
depth_stencil_attachment: None,
timestamp_writes: None,
occlusion_query_set: None,
};

render_context
.command_encoder()
.begin_render_pass(&pass_descriptor);
render_context
.command_encoder()
.begin_render_pass(&pass_descriptor);
}
}

Ok(())
Expand Down
23 changes: 14 additions & 9 deletions crates/bevy_render/src/camera/projection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use bevy_ecs::prelude::*;
use bevy_math::{ops, AspectRatio, Mat4, Rect, Vec2, Vec3A, Vec4};
use bevy_reflect::{std_traits::ReflectDefault, Reflect, ReflectDeserialize, ReflectSerialize};
use bevy_transform::{components::GlobalTransform, TransformSystem};
use bevy_window::WindowPlugin;
use derive_more::derive::From;
use serde::{Deserialize, Serialize};

Expand All @@ -24,21 +25,25 @@ impl Plugin for CameraProjectionPlugin {
.register_type::<OrthographicProjection>()
.register_type::<CustomProjection>()
.add_systems(
PostUpdate,
crate::view::update_frusta
.in_set(VisibilitySystems::UpdateFrusta)
.after(crate::camera::camera_system)
.after(TransformSystem::TransformPropagate),
);

if app.is_plugin_added::<WindowPlugin>() {
app.add_systems(
PostStartup,
crate::camera::camera_system.in_set(CameraUpdateSystem),
)
.add_systems(
PostUpdate,
(
crate::camera::camera_system
.in_set(CameraUpdateSystem)
.before(AssetEvents),
crate::view::update_frusta
.in_set(VisibilitySystems::UpdateFrusta)
.after(crate::camera::camera_system)
.after(TransformSystem::TransformPropagate),
),
(crate::camera::camera_system
.in_set(CameraUpdateSystem)
.before(AssetEvents),),
);
}
}
}

Expand Down
6 changes: 4 additions & 2 deletions crates/bevy_render/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ use bevy_ecs::schedule::ScheduleBuildSettings;
use bevy_utils::prelude::default;
pub use extract_param::Extract;

use bevy_window::{PrimaryWindow, RawHandleWrapperHolder};
use bevy_window::{PrimaryWindow, RawHandleWrapperHolder, WindowPlugin};
use experimental::occlusion_culling::OcclusionCullingPlugin;
use globals::GlobalsPlugin;
use render_asset::{
Expand Down Expand Up @@ -392,8 +392,10 @@ impl Plugin for RenderPlugin {
}
};

if app.is_plugin_added::<WindowPlugin>() {
app.add_plugins(WindowRenderPlugin);
}
app.add_plugins((
WindowRenderPlugin,
CameraPlugin,
ViewPlugin,
MeshPlugin,
Expand Down
17 changes: 9 additions & 8 deletions crates/bevy_render/src/renderer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,14 +85,15 @@ pub fn render_system(world: &mut World, state: &mut SystemState<Query<Entity, Wi
world.entity_mut(view_entity).remove::<ViewTarget>();
}

let mut windows = world.resource_mut::<ExtractedWindows>();
for window in windows.values_mut() {
if let Some(surface_texture) = window.swap_chain_texture.take() {
// TODO(clean): winit docs recommends calling pre_present_notify before this.
// though `present()` doesn't present the frame, it schedules it to be presented
// by wgpu.
// https://docs.rs/winit/0.29.9/wasm32-unknown-unknown/winit/window/struct.Window.html#method.pre_present_notify
surface_texture.present();
if let Some(mut windows) = world.get_resource_mut::<ExtractedWindows>() {
for window in windows.values_mut() {
if let Some(surface_texture) = window.swap_chain_texture.take() {
// TODO(clean): winit docs recommends calling pre_present_notify before this.
// though `present()` doesn't present the frame, it schedules it to be presented
// by wgpu.
// https://docs.rs/winit/0.29.9/wasm32-unknown-unknown/winit/window/struct.Window.html#method.pre_present_notify
surface_texture.present();
}
}
}

Expand Down
28 changes: 19 additions & 9 deletions crates/bevy_render/src/view/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,8 @@ impl Plugin for ViewPlugin {
VisibilityRangePlugin,
));

let has_windows_render_plugin = app.is_plugin_added::<WindowRenderPlugin>();

if let Some(render_app) = app.get_sub_app_mut(RenderApp) {
render_app.add_systems(
Render,
Expand All @@ -128,18 +130,26 @@ impl Plugin for ViewPlugin {
clear_view_attachments
.in_set(RenderSet::ManageViews)
.before(create_surfaces),
prepare_view_attachments
.in_set(RenderSet::ManageViews)
.before(prepare_view_targets)
.after(prepare_windows),
prepare_view_targets
.in_set(RenderSet::ManageViews)
.after(prepare_windows)
.after(crate::render_asset::prepare_assets::<GpuImage>)
.ambiguous_with(crate::camera::sort_cameras), // doesn't use `sorted_camera_index_for_target`
prepare_view_uniforms.in_set(RenderSet::PrepareResources),
),
);

if has_windows_render_plugin {
render_app.add_systems(
Render,
(
prepare_view_attachments
.in_set(RenderSet::ManageViews)
.before(prepare_view_targets)
.after(prepare_windows),
prepare_view_targets
.in_set(RenderSet::ManageViews)
.after(prepare_windows)
.after(crate::render_asset::prepare_assets::<GpuImage>)
.ambiguous_with(crate::camera::sort_cameras), // doesn't use `sorted_camera_index_for_target`
),
);
}
}
}

Expand Down
Loading