-
-
Notifications
You must be signed in to change notification settings - Fork 4.2k
Make GILRS
and WINIT_WINDOWS
public
#19575
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
--- | ||
title: Replace `Gilrs`, `AccessKitAdapters`, and `WinitWindows` resources | ||
pull_requests: [18386, 17730, 19575] | ||
--- | ||
|
||
## NonSend Resources Replaced | ||
|
||
As an effort to remove `!Send` resources in Bevy, we replaced the following resources: | ||
|
||
* `Gilrs` - _For wasm32 only, other platforms are unchanged -_ Replaced with `bevy_gilrs::GILRS` | ||
* `WinitWindows` - Replaced with `bevy_winit::WINIT_WINDOWS` | ||
* `AccessKitAdapters` - Replaced with `bevy_winit::ACCESS_KIT_ADAPTERS` | ||
|
||
Each of these are now using `thread_local`s to store the data and are temporary solutions to storing `!Send` data. Even though `thread_local`s are thread safe, they should not be accessed from other threads. If they are accessed from other threads, the data will be uninitialized in each non-main thread, which isn't very useful. | ||
|
||
Here is an example of how the data can now be accessed. This example will use `WINIT_WINDOWS` as an example, but the same technique can be applied to the others: | ||
|
||
### Immutable Access | ||
|
||
```rust | ||
use bevy_winit::WINIT_WINDOWS; | ||
|
||
... | ||
|
||
WINIT_WINDOWS.with_borrow(|winit_windows| { | ||
// do things with `winit_windows` | ||
}); | ||
``` | ||
|
||
### Mutable Access | ||
|
||
```rust | ||
use bevy_winit::WINIT_WINDOWS; | ||
|
||
... | ||
|
||
WINIT_WINDOWS.with_borrow_mut(|winit_windows| { | ||
// do things with `winit_windows` | ||
}); | ||
``` | ||
|
||
If a borrow is attempted while the data is borrowed elsewhere, the method will panic. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is there any guidance on how to borrow WINIT_WINDOWS without panicking? I have a system in a custom schedule running after use bevy::app::MainScheduleOrder;
use bevy::ecs::schedule::ExecutorKind;
use bevy::ecs::schedule::ScheduleLabel;
use bevy::ecs::system::NonSendMarker;
use bevy::prelude::*;
use bevy_winit::WINIT_WINDOWS;
use std::any::type_name;
pub struct WindowIconPlugin;
impl Plugin for WindowIconPlugin {
fn build(&self, app: &mut App) {
app.register_type::<WindowIcon>();
app.add_message::<AddWindowIconWithRetry>();
app.add_systems(Update, emit_window_icon_requests);
// Set up a custom schedule to run the window icon update as an exclusive system.
// We want to avoid `RefCell already borrowed` issues.
let mut custom_update_schedule = Schedule::new(UpdateWindowIconsSchedule);
custom_update_schedule.set_executor_kind(ExecutorKind::SingleThreaded);
app.add_schedule(custom_update_schedule);
let mut main_schedule_order = app.world_mut().resource_mut::<MainScheduleOrder>();
main_schedule_order.insert_after(Last, UpdateWindowIconsSchedule);
app.add_systems(UpdateWindowIconsSchedule, set_window_icon);
}
}
#[derive(ScheduleLabel, Debug, Hash, PartialEq, Eq, Clone)]
struct UpdateWindowIconsSchedule;
#[derive(Debug, Component, Reflect)]
pub struct WindowIcon {
pub image: Handle<Image>,
}
#[derive(Debug, Clone, Reflect, Message)]
pub struct AddWindowIconWithRetry {
pub image: Handle<Image>,
pub window: Entity,
}
impl WindowIcon {
pub fn new(image: Handle<Image>) -> Self {
Self { image }
}
}
// Sends an AddWindowIconWithRetry message for any entity that just received WindowIcon
fn emit_window_icon_requests(
mut q: Query<(Entity, &WindowIcon), Added<WindowIcon>>,
mut writer: MessageWriter<AddWindowIconWithRetry>,
) {
for (entity, icon) in &mut q {
debug!("Detected new WindowIcon on {:?}", entity);
writer.write(AddWindowIconWithRetry {
image: icon.image.clone(),
window: entity,
});
}
}
fn set_window_icon(
mut events: ParamSet<(
MessageReader<AddWindowIconWithRetry>,
MessageWriter<AddWindowIconWithRetry>,
)>,
assets: Res<Assets<Image>>,
_main_thread_marker: NonSendMarker,
) {
if events.p0().is_empty() {
return;
}
WINIT_WINDOWS.with_borrow_mut(|windows| { info!("Ahoy!") });
// let outgoing = WINIT_WINDOWS.with_borrow_mut(|windows| {
// let mut outgoing = Vec::new();
// for event in events.p0().read() {
// debug!(?event.window, ?windows, "Handling {}", type_name::<AddWindowIconWithRetry>());
// // Identify window
// let target_window = windows.get_window(event.window);
// let Some(window) = target_window else {
// warn!(
// ?windows,
// "Window {:?} does not exist, retrying later...",
// event.window
// );
// outgoing.push(event.clone());
// continue;
// };
// // Fetch the image asset
// let Some(image) = assets.get(&event.image) else {
// error!(
// "Image handle {:?} not found in assets, the window will not have our custom icon",
// event.image
// );
// continue;
// };
// // Acquire pixel data from the image
// let Some(image_data) = image.data.clone() else {
// error!(
// "Image handle {:?} has no data, the window will not have our custom icon",
// event.image
// );
// continue;
// };
// // Convert between formats
// let icon = match winit::window::Icon::from_rgba(
// image_data,
// image.texture_descriptor.size.width,
// image.texture_descriptor.size.height,
// ) {
// Ok(icon) => icon,
// Err(e) => {
// error!("Failed to construct window icon: {:?}", e);
// continue;
// }
// };
// // Set the window icon
// info!(image_size = ?image.size(), "Setting window icon");
// window.set_window_icon(Some(icon));
// }
// outgoing
// });
// for event in outgoing {
// events.p1().write(event);
// }
} EDIT: just going to create a discussion post for this question There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Created #21250 where I demonstrate my problem and I identified the solution: The real problem was that I was using with_borrow_mut when with_borrow is sufficient. |
||
|
||
## NonSend Systems | ||
|
||
Previously, the use of a `!Send` resource in a system would force the system to execute on the main thread. Since `!Send` resources are removed in Bevy, we needed to create a new way to prevent systems from running on non-main threads. To do this, you can now use `bevy_ecs::system::NonSendMarker` as a system parameter: | ||
|
||
```rust | ||
use bevy_ecs::system::NonSendMarker; | ||
|
||
fn my_system( | ||
_non_send_marker: NonSendMarker, | ||
) { | ||
ACCESS_KIT_ADAPTERS.with_borrow_mut(|adapters| { | ||
// do things with adapters | ||
}); | ||
} | ||
``` | ||
|
||
To prevent a panic, if any of the `!Send` resource replacements mentioned in this document are used in a system, the system should _always_ be marked as `!Send` with `bevy_ecs::system::NonSendMarker`. |
Uh oh!
There was an error while loading. Please reload this page.