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

Proposal towards modern EntityNote for better window ergonomics and more #12521

Open
DasLixou opened this issue Mar 16, 2024 · 4 comments
Open
Labels
A-ECS Entities, components, systems, and events C-Enhancement A new feature X-Controversial There is active debate or serious implications around merging this PR

Comments

@DasLixou
Copy link
Contributor

DasLixou commented Mar 16, 2024

What problem does this solve or what need does it fill?

In bevy, there are a few places where indexing an entity is kinda hacked and feels unergonomic. This applies to WindowRef, where we either want to get the one Primary window, where there technically can still be 0 or more, or any other window entity referenced by its EntityId. This proposal wants to break the one hardcoded Primary special case, provide a more ergonomic way to get the actual entity behind a window and give the user the power to use markers other than Primary for windows or other kinds of stuff.

What solution would you like?

The general term would be entity labeling/marking.
Instead of a WindowRef, we should have an enum which can either directly access an entity via its id, or retrieve a singleton entity by a given marker. It would look something like this:

enum EntityNote {
    Entity(EntityId),
    Marker(_),
}

(I was originally heading for EntityRef, but that's already used, better name proposals welcome :3)
A WindowRef would then be handled like this:

#[derive(Marker)]
struct PrimaryWindow;

fn system(windows: Query<&Window>) {
    // marker
    let primary = EntityNote::marker(PrimaryWindow);
    let _ = windows.get(primary);
    
    // id (long)
    let other = EntityNote::entity(id);
    let _ = windows.get(other);

    // id (short)
    let _ = windows.get(id);
}

EntityNotes should only be used for stuff where things don't break after changing the entity behind a marker.

Markers are dynamic identifiers for entitys, every marker should directly refer to one entity. What happens when a marker is unset and if there should be an "optional" marker where we can also remove the marker is yet to answer.

There currently are two ideas towards handling entity markers:

Resource-based markers

See #6556
The idea is to have a resource which contains the actual EntityId and can be easily changed from any system by just mutating that resource. One big advantage about this is that it's fairly easy to implement and already has a (not-updated and horrible) prototype from me: bevy_ecs_markers.

Singleton Components

See #8944
The idea is to make a component which can only be applied once in the whole ecs world. While this has some caveats such as that we have to go deeply into the ecs and that it's also unsure how and if this will work for enum markers as seen in the example in #6556, one advantage I could think of is that when we want it, we could still use With and Without in queries to make non-overlapping systems run in parallel.

I think we should investigate both and see what gives us better performance and ergonomics.

What alternative(s) have you considered?

Just use a boring util, e.g.

#[derive(SystemParam)]
pub struct Windows<'w, 's> {
    primary: Query<'w, 's, &'static Window, With<PrimaryWindow>>,
    secondary: Query<'w, 's, &'static Window, Without<PrimaryWindow>>,
}

impl Windows<'_, '_> {
    #[inline(always)]
    pub fn get(&self, window: WindowRef) -> Option<&Window> {
        match window {
            WindowRef::Primary => self.primary.iter().next(),
            WindowRef::Entity(e) => self.secondary.get(e).ok(),
        }
    }
}

to have at least some better ergonomics hiding the two queries.

@DasLixou DasLixou added C-Enhancement A new feature S-Needs-Triage This issue needs to be labelled labels Mar 16, 2024
@alice-i-cecile alice-i-cecile added A-ECS Entities, components, systems, and events X-Controversial There is active debate or serious implications around merging this PR and removed S-Needs-Triage This issue needs to be labelled labels Mar 16, 2024
@alice-i-cecile
Copy link
Member

I'm having trouble understanding the motivation here: my gut instinct is "no new ECS features without a clear and compelling motivation".

I generally prefer the marker component solution (faster, plays nice with inspectors, keeps data localized), and don't see why we might want to swap between the two designs dynamically.

@DasLixou
Copy link
Contributor Author

DasLixou commented Mar 17, 2024

Oh sorry then that was unclear. The main motivation comes from WindowRef having a Primary "marker" and making retrieving of the actual window entity quote annoying with two queries.
With dynamically changed, I wasn't referring to the way of storing markers, there should be only one way in the end. What I meant is that we can dynamically change which window is the primary window and that this could bring problems to some things who don't account the update

@alice-i-cecile
Copy link
Member

this could bring problems to some things who don't account the update

Definitely agree: we should really try to avoid caching this information redundantly. There's all sorts of nasty bugs that can occur otherwise.

@DasLixou
Copy link
Contributor Author

Not directly caching.. What I think about is e.g. that changing the primary window will not directly fire a window resize event, so a system who does something with the window size might not update correctly. For that we may want a simple api to also include marker updates or similar

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-ECS Entities, components, systems, and events C-Enhancement A new feature X-Controversial There is active debate or serious implications around merging this PR
Projects
None yet
Development

No branches or pull requests

2 participants