-
-
Notifications
You must be signed in to change notification settings - Fork 3.5k
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
Persistent Render World #14252
Persistent Render World #14252
Conversation
current implementation has a flaw. Simply using Added is insufficient for correct functionality. When a main world entity is despawned or its component is modified which need to sync with render world, the corresponding render entity fails to update appropriately. |
IMO, to create a robust retained render world, we should follow these steps:
To avoid complexity and risk in steps 2 and 3, some bit magic operations on the entity might be very helpful. It's best to split it into multiple PRs. Step 1 can be the first PR, steps 2 through 4 can be grouped into the next PR, and step 5 can be handled last. |
This reverts commit b2c1414.
Thank you for the advice @re0312, it's a lot more of a sophisticated approach than I'm going to take initially. If this doesn't yield satisfactory performance results I will look into implementing your approach. The plan is now as follows:
This achieves the primary goal of this PR while being relatively easy to implement. I'll have to check the performance of course, but hopefully, there are so few extracted entities, that it's negligible (a lot of rendering doesn't use entities at all). |
I've done all the things I said I would do, while continually testing it on the One downside of the EntityHashMap approach is that the extraction can no longer run in parallel, as every extraction system needs a mutable reference to the mapping. This is quite a slowdown, but given that the number of extractions for 3d_scene is about 4, I have not had any major issues. |
This CI warning appears to be due to this PR. How does the performance compare, just very broadly on say |
Extraction not being able to run in parallel is probably going to be a deal breaker. But you should run the stress tests and check the perf. |
I'll get around to a full review tomorrow probably. But like @hymm mentioned adding the struct SpawnExtracted<B: Bundle> {
bundle: B,
main_world_entity: Entity
} that accesses the entity map internally? That way we can avoid extra overhead in Extract too, and we don't seem to care about overhead in ExtractCommands as much. |
take a quick review ,the current state appears to have several drawbacks:
|
@re0312 Could you elaborate on / give an example of 2? I'm not entirely sure what you mean. |
Component may depend on other entities. For example, consider Therefore, you must manually set up every component used in the render world which maybe depends on other entities (not just cameras). This complexity is why I previously mentioned that it's risky and challenging to expose this functionality in an ergonomic way. Moreover, if the dependency relationships are multiple or involve complex nested structures, you cannot rely on a simple |
I think it'd work to switch to a component that stores the render ID, |
Would we have to do any extra shenanigans to add a RenderId component in the main world compared to a MainId component in the render world? I did a little experimentation with the latter and it's possible to greatly reduce calls to the hashmap and do the remaining ones outside of Extract anyways. The flow is basically:
After extract but before all other rendering, update marked components using the hashmap. |
https://github.com/re0312/bevy/tree/retain_world |
I'm a bit surprised by that if we're losing most of the multithreading. I've done tests before switching to the single threaded executor and the Extract schedule was one of the biggest benefactors of multithreading. How are you measuring this? Do you have a tracy log of many foxes?
I suggested RenderId since you'd be able to use |
The trick would just be to do the mapping during command application so we avoid extra overhead during extract. It would also only apply on the initial insert, so syncing data for existing components would stay fast. In the case of needing to update |
The implementation approach I used in #1449 is maintains |
Closing in favor of #14449 |
Objective
Every Bevy app has (at least) two ECS Worlds, the main world and the render world. These are held separately so gameplay code can run parallel to the rendering code. For changes in the main world to affect the render world (and eventually the screen), the data from the former has to be hoisted over to the render world in the Extract stage. See the figure I've graciously stolen from @inodentry:
The article in the cheatbook also gives a good overview.
This extraction happens for every frame and in order to not overload the render world with entities, all entities are removed in the Cleanup stage, a frame earlier. This, however, causes a problem for more advanced ECS features like Components as Entities and Queries as Entities. Queries and Components are not extracted (nor does it make sense to) and it doesn't make sense to remove them every frame. It causes very big, very annoying problems.
Solution
Stop removing all entities every frame and instead of extracting all (render-relevant) entities, keep more careful track of them by way of an entity-entity mapping between main world entities and render world entities.
Drawbacks
Currently, there is a one-to-one mapping between render world entities and main world entities, i.e. they have the same IDs in both worlds. This is likely not going to be the case moving forward. This has never been a guarantee, so for the few people who have relied on this behaviour: I'm sorry.
Also, and I feel like I can say this with some confidence, the performance is going to get worse. How much worse, I cannot say yet, but given that we are going to do a lot more bookkeeping, it's expected to get worse.