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

Provide way to query if the simulation is sleeping, or add invalidateFrameloop prop #49

Closed
RobRendell opened this issue Mar 27, 2020 · 22 comments
Assignees
Labels
enhancement New feature or request

Comments

@RobRendell
Copy link

My app uses <Canvas invalidateFrameloop={true}/> in react-three-fiber to avoid rendering every frame, because most of the time the scene is static (it's a virtual gaming space for tabletop roleplaying games, and if no-one is moving anything around, there's no need to consume power by re-rendering the scene).

I'm adding physics-based dice rolling using use-cannon, but obviously when the physics simulation is running the app needs to re-render every frame. At the moment, the dice's render() methods call useFrame(({invalidate}) => {invalidate();}); so they cause the app to render every frame while they are mounted.

use-cannon defaults to useSleep={true}, so presumably when the die/dice settle, the physics simulation goes to sleep. However, I can't find a way to query the sleep state of the simulation from user-land, so my useFrame call can stop calling invalidate(). If such a getter is not available, I'd like to request that it be added (presumably to the WorkerAPI).

Given that your physics state is hidden away in the workers, it might be hard to make a getter of the sleep status available through the API. As an alternative, since invalidateFrameloop is a standard (if seldom used) feature of react-three-fiber, perhaps a similar prop could be added to <Physics/>, defaulting to false, but if true, use-cannon itself could call invalidate() each frame that the physics sim is not sleeping, and stop calling it when it sleeps.

@drcmda
Copy link
Member

drcmda commented Mar 27, 2020

you're right, it should call invalidate. but it's a bit tricky atm because use-cannon itself is useFrame based. i did try to bring the mutation stuff into the main loop, but got into some scope problems, there's data which only the hooks have. but i think it could call invalidate in the main loop if it detects a change. @codynova is it possible to know outright if anything was moving without going through complex vector old-new checks?

@drcmda drcmda added the enhancement New feature or request label Mar 27, 2020
@codynova
Copy link
Member

The World's internalStep function always sets the dirty flag on the Broadphase, so as far as the Broadphase is concerned, the simulation is always running. I think the easiest way to check for movement would be something like this:

world.bodies.some(body => body.sleepState !== Body.SLEEPING)

which could be used in conjunction with sleepTimeLimit and sleepSpeedLimit for greater control.

@drcmda
Copy link
Member

drcmda commented Mar 27, 2020

it seems to work, that was easier than i thought it would be :-)

@codynova
Copy link
Member

Published in 0.2.8

@drcmda
Copy link
Member

drcmda commented Mar 28, 2020

my only worry is, what if we're dealing with hundredes (thousands?) of objects. it would have to climb through that loop every frame. is there maybe a subscription at the body level we can use to flag active state globally?

@codynova
Copy link
Member

codynova commented Mar 28, 2020

There's no reason we couldn't add one. The World (and sometimes the Solver) already iterate through every body at least one on each frame.

What do you think makes the most sense? The most basic solution would be having bodies add/remove themselves from a World.activeBodies array (or Map), then just checking that length > 0.

I wonder what effect that would have on memory? Schteppe did quite a bit of memory optimization, so if there is a more memory-efficient solution I'd love to know!

@drcmda
Copy link
Member

drcmda commented Mar 28, 2020

that sounds great! yeah, i tend to forget, we can change cannon now. :-D i think mem impact for a bunch of flags won't be a problem.

@codynova
Copy link
Member

I was wondering more about the memory impact of the array of bodies, but I'm not really sure how that works since it's all pass-by-reference, I'd need to actually test it. I will take a stab at the solution I proposed above...

@codynova codynova reopened this Mar 28, 2020
@codynova codynova self-assigned this Mar 28, 2020
@codynova
Copy link
Member

Newer frame validation which simply checks the length of World.activeBodies each frame by referencing getter World.hasActiveBodies released in 0.2.9

@drcmda
Copy link
Member

drcmda commented Mar 28, 2020

nice one! thanks a lot

@codynova
Copy link
Member

Happy to do it! There is probably a more efficient way, by assuming hasActiveBodies = false and checking for non-sleeping bodies in the World step - but it's a little trickier to figure out when it's safe to read a body's sleepState in the step. I may try to revisit it a little later, while keeping the forward-facing API the same (World.hasActiveBodies)...

@RobRendell
Copy link
Author

You guys are amazingly quick! I get up this morning to find all this extra activity :)

When I started reading the reopening comments, my thought was that if you had memory concerns about the array of active bodies, then a simple counter would also work for detecting any activity... each body would increment it when it transitions from asleep to awake (or when it's added in an awake state) and decrement it when it transitions from awake to asleep.

However, I imagine the array of active bodies will be useful for other optimisations too.

@codynova
Copy link
Member

codynova commented Mar 28, 2020

@RobRendell That's a good idea. The counter is closer to what I was imagining doing in the World step. I think we could push it one step further. Rather than keeping a count, just check the body's sleepState in the World step and switch the flag to true if any body is awake - otherwise default to false. The counter would be easier to implement because we can simply call it from the body's wakeUp or sleep methods. The flag is probably (slightly) more performant, but it requires being more careful during the integration with the World step method. I'm wondering if we can come up with any other potential uses for the activeBodies - I can't think of a good reason to keep it.

@codynova
Copy link
Member

Revisit for performance improvements

@codynova codynova reopened this Mar 28, 2020
@RobRendell
Copy link
Author

Well, presumably you only need to iterate over the active bodies when simulating updates? If the scene has lots of sleeping physics objects, not having to iterate over the entire list would be more efficient. I imagine that in "real world" type scenes, most physics objects are at rest...

@codynova
Copy link
Member

codynova commented Mar 28, 2020

The World already has to iterate over every body multiple times in each step to do integrations, solve contact equations, evaluate and alter waking state, etc.

@codynova
Copy link
Member

codynova commented Mar 29, 2020

The new method of updating flags in the World step was released in cannon-es@0.8.7 and use-cannon@0.2.10. I'll wait to close this time until we confirm it's working properly :)

@codynova
Copy link
Member

Seems to work fine based on testing in CompoundBody demo

@RobRendell
Copy link
Author

RobRendell commented Apr 2, 2020

@codynova Sorry to bug you yet again, but this appears to have regressed (checked with both versions 0.2.11 and 0.3.0). The invalidate function is being called all the time, even after the bodies are sleeping. I even checked by adding a useFrame() callback to the CompoundBody demo, and the callback continues to be invoked, apparently indefinitely.

@codynova
Copy link
Member

codynova commented Apr 2, 2020

@RobRendell Do you mind creating a minimum viable example codesandbox? I just tested locally with invalidateFrameloop on the Canvas and a callback useFrame(() => console.log('test')) - and it seems to work fine, unless I'm just missing something

@drcmda
Copy link
Member

drcmda commented Apr 2, 2020

maybe you forgot to build? that is, if you're working with the examples repo. that happens to me all the time. simply updating it wont do.

@RobRendell
Copy link
Author

RobRendell commented Apr 2, 2020

I didn't find the examples repo, so I pulled this repo and copy/pasted the CompoundBody demo into my project.

However, I discovered that the problem was with my configuration - I misunderstood the sleepTimeLimit parameter, and assumed it was in milliseconds. The simulation was able to sleep, I just wasn't waiting long enough :)

Apologies for the false report! Thank you so much for being so responsive!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

3 participants