-
-
Notifications
You must be signed in to change notification settings - Fork 3.7k
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] - use marker components for cameras instead of name strings #3635
[Merged by Bors] - use marker components for cameras instead of name strings #3635
Conversation
We should think about how to coordinate this with #3412. @HackerFoo, do you have a preference? |
|
||
/// Component bundle for camera entities with perspective projection | ||
/// | ||
/// Use this for 3D rendering. | ||
#[derive(Bundle)] | ||
pub struct PerspectiveCameraBundle { | ||
pub struct PerspectiveCameraBundle<M: Component> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I love this style of API; I've used it before in my games and it's great.
pub fn new_3d() -> Self { | ||
Default::default() | ||
PerspectiveCameraBundle::new() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This feels a bit weird to me? Why not just use PerspectiveCameraBundle<Camera3d>::default()
instead? And then specialize the default impls based on which generic is passed in.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wanted to have only one Default
impl so that PerspectiveCameraBundle { ..Default::default() }
works without complaining about an uninferred type parameter.
I'd like #3412 and #3568 to be considered, because they make it useful to run the I also have an idea for specifying the pass order in |
Rebasing one on top of the other once it is merged shouldnt be too hard, the changes are in similar code paths but are mostly unrelated. |
This is looking much cleaner. I'm really happy to see this finally get cleaned up. Just a few nits :) |
And here’s another instance of the same question. I’ve seen this 4 times in the past two days. :) @mockersf suggested adding enable/disable for lights while I was doing some other things but it’s not trivial imo because of the question of whether to have something like a ZST for the less likely case (i.e. an empty |
I'm in favor of this: being able to standardize the ecosystem around this pattern is super useful.
So, IMO archetype fragmentation is less important performance-wise than the requirement to constantly check values. I'd prefer a marker component: if we use sparse-set storage it'll be faster to add and remove too. I would be fine with either Overall I think a |
How about Similar mechanisms are used for physics (e.g. in Rapier.) |
I suppose this could be done with some other component if needed. |
👍
I didn’t know this so I hoped those who know more about the ECS performance impacts would speak up so thanks! :)
Sounds good to me. I generally prefer to have positive logic in the sense of avoiding thinking “if !thing {} else {}” but in these marker component cases it feels like, from an ECS perspective, it makes more sense to mark the less frequent case. That’s how I ended up with NotShadowCaster and NotShadowReceiver. Following that logic and assuming that relatively few entities would be disabled, marking as Inactive makes sense to me. :) |
I think the strategy here is then:
|
It would be useful to be able wrap any component in I suggest not tying it to this PR, though. The existing component |
Yeah okay @HackerFoo is right: we should use the simplest possible solution for this PR, and then a better solution later. |
An exclusive marker component would be generally useful. Maybe it could be both a resource containing just an optional entity, and a component, where inserting it updates the resource and removes the component from the previous entity. I've used both marker components and resources to identify specific entities. Marker components are more flexible and can't have invalid entities, but resources are probably faster and are guaranteed unique. |
Another problem with marker components is that they require command updates and if you have system dependencies with a stage that’s a problem. Why can there only be one camera of one type in a scene? What about split screen local for example? |
There can only be one active camera of one type per render target. Split screen would involve at least two render targets: either two targets with custom viewports or three targets .... two for each split and the third for compositing. Cameras will correlate to single render targets. "Exclusive marker components" wouldn't cover this case. "Non-exclusive marker components" would work, because you could iterate the active list and filter down to the target currently being rendered. "Exclusive resource" also wouldn't work for multiple render targets. My plan there was either to make the |
I'm pretty partial to this design: "every RenderTarget is an entity, every RenderTarget has Given that right now RenderTarget entities aren't a thing (im working on those now), I think building a hard-coded |
The example in #3552 uses two cameras with the same render target ( As further motivation, I've considered updating the cameras at different rates (the first is mostly static without user interaction), so I've been thinking about how to store the colors from the first pass and only rendering the second. I could use compositing, but passes are expensive on mobile devices - I saved ~30% GPU time by just eliminating the clear pass, and a bunch more by eliminating other passes. Nonetheless, rendering multiple cameras directly to a single texture has been useful for me in practice. |
This will still be possible with the outlined plan, as RenderTargets won't be "exclusive". You can create a second RenderTarget that also targets I like this design because it embraces the fact that the core pipeline is designed around "one active camera of a given type at a time", and lets us treat cameras as "simple logical views" instead of trying to treat them as "owners of render pass configuration". |
Just added an |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would prefer basic docs throughout this PR, but I can live without them and add them in a follow-up.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this a good approach, and the code quality is good. LGTM.
bors r+ |
**Problem** - whenever you want more than one of the builtin cameras (for example multiple windows, split screen, portals), you need to add a render graph node that executes the correct sub graph, extract the camera into the render world and add the correct `RenderPhase<T>` components - querying for the 3d camera is annoying because you need to compare the camera's name to e.g. `CameraPlugin::CAMERA_3d` **Solution** - Introduce the marker types `Camera3d`, `Camera2d` and `CameraUi` -> `Query<&mut Transform, With<Camera3d>>` works - `PerspectiveCameraBundle::new_3d()` and `PerspectiveCameraBundle::<Camera3d>::default()` contain the `Camera3d` marker - `OrthographicCameraBundle::new_3d()` has `Camera3d`, `OrthographicCameraBundle::new_2d()` has `Camera2d` - remove `ActiveCameras`, `ExtractedCameraNames` - run 2d, 3d and ui passes for every camera of their respective marker -> no custom setup for multiple windows example needed **Open questions** - do we need a replacement for `ActiveCameras`? What about a component `ActiveCamera { is_active: bool }` similar to `Visibility`? Co-authored-by: Carter Anderson <[email protected]>
The example was broken in #3635 when the `ActiveCamera` logic was introduced, after which there could only be one active `Camera3d` globally. Ideally there could be one `Camera3d` per render target, not globally, but that isn't the case yet. To fix the example, we need to - don't use `Camera3d` twice, add a new `SecondWindowCamera3d` marker - add the `CameraTypePlugin::<SecondWindowCamera3d>` - extract the correct `RenderPhase`s - add a 3d pass driver node for the secondary camera Fixes #4378 Co-authored-by: Jakob Hellermann <[email protected]>
…#3635) **Problem** - whenever you want more than one of the builtin cameras (for example multiple windows, split screen, portals), you need to add a render graph node that executes the correct sub graph, extract the camera into the render world and add the correct `RenderPhase<T>` components - querying for the 3d camera is annoying because you need to compare the camera's name to e.g. `CameraPlugin::CAMERA_3d` **Solution** - Introduce the marker types `Camera3d`, `Camera2d` and `CameraUi` -> `Query<&mut Transform, With<Camera3d>>` works - `PerspectiveCameraBundle::new_3d()` and `PerspectiveCameraBundle::<Camera3d>::default()` contain the `Camera3d` marker - `OrthographicCameraBundle::new_3d()` has `Camera3d`, `OrthographicCameraBundle::new_2d()` has `Camera2d` - remove `ActiveCameras`, `ExtractedCameraNames` - run 2d, 3d and ui passes for every camera of their respective marker -> no custom setup for multiple windows example needed **Open questions** - do we need a replacement for `ActiveCameras`? What about a component `ActiveCamera { is_active: bool }` similar to `Visibility`? Co-authored-by: Carter Anderson <[email protected]>
The example was broken in bevyengine#3635 when the `ActiveCamera` logic was introduced, after which there could only be one active `Camera3d` globally. Ideally there could be one `Camera3d` per render target, not globally, but that isn't the case yet. To fix the example, we need to - don't use `Camera3d` twice, add a new `SecondWindowCamera3d` marker - add the `CameraTypePlugin::<SecondWindowCamera3d>` - extract the correct `RenderPhase`s - add a 3d pass driver node for the secondary camera Fixes bevyengine#4378 Co-authored-by: Jakob Hellermann <[email protected]>
…#3635) **Problem** - whenever you want more than one of the builtin cameras (for example multiple windows, split screen, portals), you need to add a render graph node that executes the correct sub graph, extract the camera into the render world and add the correct `RenderPhase<T>` components - querying for the 3d camera is annoying because you need to compare the camera's name to e.g. `CameraPlugin::CAMERA_3d` **Solution** - Introduce the marker types `Camera3d`, `Camera2d` and `CameraUi` -> `Query<&mut Transform, With<Camera3d>>` works - `PerspectiveCameraBundle::new_3d()` and `PerspectiveCameraBundle::<Camera3d>::default()` contain the `Camera3d` marker - `OrthographicCameraBundle::new_3d()` has `Camera3d`, `OrthographicCameraBundle::new_2d()` has `Camera2d` - remove `ActiveCameras`, `ExtractedCameraNames` - run 2d, 3d and ui passes for every camera of their respective marker -> no custom setup for multiple windows example needed **Open questions** - do we need a replacement for `ActiveCameras`? What about a component `ActiveCamera { is_active: bool }` similar to `Visibility`? Co-authored-by: Carter Anderson <[email protected]>
The example was broken in bevyengine#3635 when the `ActiveCamera` logic was introduced, after which there could only be one active `Camera3d` globally. Ideally there could be one `Camera3d` per render target, not globally, but that isn't the case yet. To fix the example, we need to - don't use `Camera3d` twice, add a new `SecondWindowCamera3d` marker - add the `CameraTypePlugin::<SecondWindowCamera3d>` - extract the correct `RenderPhase`s - add a 3d pass driver node for the secondary camera Fixes bevyengine#4378 Co-authored-by: Jakob Hellermann <[email protected]>
Problem
RenderPhase<T>
componentsCameraPlugin::CAMERA_3d
Solution
Camera3d
,Camera2d
andCameraUi
->
Query<&mut Transform, With<Camera3d>>
worksPerspectiveCameraBundle::new_3d()
andPerspectiveCameraBundle::<Camera3d>::default()
contain theCamera3d
markerOrthographicCameraBundle::new_3d()
hasCamera3d
,OrthographicCameraBundle::new_2d()
hasCamera2d
ActiveCameras
,ExtractedCameraNames
-> no custom setup for multiple windows example needed
Open questions
ActiveCameras
? What about a componentActiveCamera { is_active: bool }
similar toVisibility
?