Pausing Quantum Renderer

Johan Vos johan.vos at
Sun Nov 22 11:24:45 UTC 2015

I implemented this in the javafxports clone of the OpenJFX 8u-dev repo, and
the diff is here:

I am more than happy to create an issue in the openjdk bug system
(enhancement?) and provide a patch there as well, but I think it needs a
bit more discussion first?

- Johan

On Sat, Nov 21, 2015 at 9:23 PM, Johan Vos <johan.vos at> wrote:

> I have a working implementation that needs more stress-testing on
> different platforms, but it seems a good and easy solution so far.
> I have this on QuantumToolkit:
>     @Override
>     public void pauseRenderer(){
>         Application.invokeAndWait(() -> this.pause = true);
>         PaintCollector.getInstance().waitForRenderingToComplete();
>     };
>     public void resumeRenderer(){
>         Application.invokeAndWait(() -> this.pause = false);
>     };
> pause is a boolean that is checked for in
> void pulse(boolean collect) { ... }
> The difficulty I mentioned in my previous mail (how do we know there are
> no renderJobs pending/running) was solved easily as there exists this
> PaintCollector.waitForRenderingToComplete method.
> This might make the pauseRenderer a bit slower, and maybe this is not
> needed in all usecases. In that case, we can remove it from the
> pauseRenderer() and we can add it either in the Monocle implementation that
> will call pauseRenderer, or in a Android/iOS specific code.
> However, it seems to me that if you want to pause the renderer, you most
> often want to make sure no one is still writing to the glSurface after the
> pauseRenderer method is called, so I think it makes sense to keep it there?
> - Johan
> On Fri, Nov 20, 2015 at 8:14 AM, Johan Vos <johan.vos at> wrote:
>> I didn't plan to intercept or cancel pending/submitted jobs, but I have
>> to wait until they are done before the android callback method returns.
>> When Android is about to destroy the context, it will call the
>> surfaceTextureDestroyed method on the Activity (the FXActivity in our
>> case). As long as that method doesn't return, the context won't be
>> destroyed. But once that method returns, the context might become invalid
>> any moment. So if there are still jobs that want to do a swapBuffer after
>> we return, those can crash or (even worse) corrupt the egl system.
>> So it seems to me inside the implementation of surfaceTextureDestroyed,
>> we need to achieve 2 things:
>> 1. make sure no new pulses are generated.
>> 2. don't return while the QuantumRenderer is still executing jobs.
>> Those 2 things can be combined in a single Toolkit.pauseRenderer() but it
>> might be better to only achieve the first task in Toolkit.pauseRenderer().
>> The contract for this method is than that no new pulses will be
>> generated, but existing renderJobs might still be running when this method
>> returns.
>> The second thing, waiting for the renderJobs to be finished, can be done
>> in the Android specific implementation.
>> - Johan
>> On Thu, Nov 19, 2015 at 10:20 PM, Kevin Rushforth <
>> kevin.rushforth at> wrote:
>>> This might be a tricky semantic to achieve, and great care will be
>>> needed to ensure no deadlocks or race conditions. Not running any more
>>> pulses after this method returns seems fine, but it might be better to let
>>> any existing renderJobs run (possibly discarding the results). This way you
>>> could send the pause message to the renderer as a special renderJob and not
>>> have to worry about jobs that are scheduled but not yet run.
>>> -- Kevin
>>> Johan Vos wrote:
>>>> After some experiments, here is my current thinking:
>>>> Toolkit can have 2 new methods:
>>>> pauseRenderer()
>>>> resumeRenderer()
>>>> When pauseRenderer is called, it should be guaranteed that after this
>>>> call,
>>>> no new pulses are fired until resumeRenderer is called.
>>>> That is not hard, but it is not enough. Before we pause the pulses, the
>>>> previous pulse probably submitted a renderJob to Prism, executed on the
>>>> QuantumRenderer ThreadPoolExecutor. That job should run fine, as the
>>>> next
>>>> pulse (when we're back) will call
>>>> GlassScene.waitForRenderingToComplete().
>>>> So we have to wait until there are no running or pending tasks in the
>>>> QuantumRenderer as well.
>>>> - Johan
>>>> On Wed, Nov 18, 2015 at 9:58 PM, David Hill <David.Hill at>
>>>> wrote:
>>>>> On 11/18/15, 3:45 PM, Johan Vos wrote:
>>>>> Johan,
>>>>>     I think that it would be reasonable to put in something to Quantum
>>>>> that causes the render loop to "pause", and then resume later. I
>>>>> envision
>>>>> this toggle as causing the render loop to skip, rather than tinkering
>>>>> with
>>>>> the pulses.
>>>>> When resume is called, it might be best to treat the world as dirty.
>>>>> Added to Toolkit, this would allow someone like Monocle to make the
>>>>> toggles as is appropriate.
>>>>> If this works for you, perhaps you could prototype it ?
>>>>> regards,
>>>>>    Dave
>>>>>> On Android, a JavaFX Application might transfer control to another app
>>>>>> (and
>>>>>> become invisible) and enter the foreground later. In that case, the
>>>>>> GLSurface we are rendering on becomes invalid. In order to avoid
>>>>>> problems
>>>>>> and save battery, we want to pause the renderer thread, but this
>>>>>> turns out
>>>>>> to be more difficult than I expected.
>>>>>> When our app transfers control, we get a callback from Android. We
>>>>>> intercept this in javafxports, and we set the Screen width/height to
>>>>>> 0/0
>>>>>> as
>>>>>> we don't want to render on the (invalid) surface while we lost
>>>>>> control.
>>>>>> When we regain control, we resize the Screen and the app renders
>>>>>> again.
>>>>>> The reason we set the width/height to 0/0 is because the
>>>>>> PresentingPainter
>>>>>> will call SceneState.isValid() and this returns false in case
>>>>>> getWidth()
>>>>>> or
>>>>>> getHeight() are 0.
>>>>>> However, SceneState extends PresentableState and it overrides the
>>>>>> update
>>>>>> method. It will call PresentatbleState.update() which will set the
>>>>>> viewWidth to the width of the new Screen (hence, 0) , but after that
>>>>>> it
>>>>>> overwrites the viewWidth with camera.getViewWidth(). The latter still
>>>>>> contains the old value. A quick inspection shows that
>>>>>> camera.setViewWidth()
>>>>>> is called when validate(...) is called on NGDefaultCamera, which is
>>>>>> called
>>>>>> by ES2Context.updateRenderTarget() which happens during rendering,
>>>>>> hence
>>>>>> *after* the PresentingPainter checks if the width is 0.
>>>>>> So immediately after we set the width of the Screen to 0 (on the FX
>>>>>> App
>>>>>> Thread), a Pulse happens, and this one still things the screen is the
>>>>>> original size. While the pulse is happening, the android system
>>>>>> destroys
>>>>>> our context, and the rendering fails. Moreover, the EGL system is in a
>>>>>> unpredictable state (recreating the surface fails).
>>>>>> A very dirty workaround for this is to wait for 1 pulse (with the new
>>>>>> pulselistener API this should be possible) before we return from the
>>>>>> callback method called by Android when the surface is about to be
>>>>>> destroyed. That way, we will have 1 bogus rendering on an existing
>>>>>> (but
>>>>>> about-to-be-destroyed) surface.
>>>>>> But it would be better if there is some way to tell the quantum
>>>>>> renderer
>>>>>> to
>>>>>> immediately stop rendering. Existing pulses are no problem, as the
>>>>>> renderLock guarantuees that we set the size to 0 only when no other
>>>>>> thread
>>>>>> (quantum renderer) has a lock on the renderLock.
>>>>>> - Johan
>>>>> --
>>>>> David Hill<David.Hill at>
>>>>> Java Embedded Development
>>>>> "A man's feet should be planted in his country, but his eyes should
>>>>> survey
>>>>> the world."
>>>>> -- George Santayana (1863 - 1952)

More information about the openjfx-dev mailing list