A different way to handle pulse timing
David.Hill at Oracle.com
Fri Jul 26 09:28:31 PDT 2013
On 7/26/13 Jul 26, 1:22 AM, Richard Bair wrote:
> As for multiple scenes, I'm actually curious how this happens today. If I have 2 scenes, and we have just a single render thread servicing both, then when I go to present, it blocks? Or is there a non-blocking present method that we use instead? Because if we block, then having 2 scenes would cut you down to 30fps maximum, wouldn't it? If we are non-blocking today (is that possible?) then the only way this proposed solution would work is if there was a different render thread per stage (which actually is something I think we ought to be doing anyway?).
Currently we block/lock rendering using
so you can take a 'uses' to see where it is used.
In general though, we use PaintCollector to stage render jobs in a pulse - one per dirty Scene. This operation is on the user event thread, and is blocked by any pending render tasks (in effect it waits for a current pulse to complete).
The PaintCollector uses window state", similar to a shadow scene graph, so that the render operation is using state that is consistent (this is done using SceneState)
Each render task (scene) is executed separately on the render thread, and takes the renderLock while it is doing its thing. This means that there is an unlocked state between queued rendered scene tasks.
Rendering tends to be done via either PresentingPainter (accelerated) or UploadingPainter (sw).
The above is the simplified view - and shows that user event thread can be happily running along doing stuff while the render thread is doing its thing - at least until it is blocked by needed to push another render pulse.
But... it is a touch more complicated than that, as we found that there are a number of user event thread operations that really can't be happening when we have a render operation going. These are mostly related to "window operations", like resizing or closing a window. Changing a windows state while rendering to it causes "unpredictable" results on many platforms. Because of this, there are a number of operations where we take the renderLock before calling from Quantum into glass. A sampling of these cases are WindowStage setScene, setVisible, close. The idea is that Glass should be treated as single threaded.
Note that embedded arm behaves a touch different - because we have a single GL graphics context and no "real" windows - we always paint every stage/scene from back to front. In effect, we are the compositor of the screen as we are the "window manage". This is pretty obvious in the PaintCollector class. Given that we have a single graphics context - there is no way we would want one render thread per scene there.
Another note - the addition of SceneState solved a problem, one that was easily seen with HelloWindowAbuse, which created/resized/closed windows at a frantic pace. There was some discussion at the time that we might have been able to save less state in SceneState because we already have that data in other places, like the SceneGraph. SceneState was a compromise solution that provided the quickest fix for the least amount of code reconstruction, which of course means that there is likely room for improvement.
So, that is my view of the elephant <https://en.wikipedia.org/wiki/Blind_men_and_an_elephant>, and I am sure that others will have a different take :-)
David Hill <David.Hill at Oracle.com>
Java Embedded Development
Education: that which reveals to the wise, and conceals from the stupid, the vast limits of their knowledge.
-- Mark Twain
More information about the openjfx-dev