OpenGL context with continuous repaint causes UI freeze

Dear JUCErs,

using the recent develop branch I found that when I set my OpenGL context to continuously redraw at the screen refresh rate using setContinuousRepainting (true), it can cause other UI elements to freeze for a long time, sometimes in the order of 5 or more seconds. Using older versions of JUCE this did not cause a problem.

My application does not draw anything yet, I simply derived from Component and OpenGLRenderer and override newOpenGLContextCreated, renderOpenGL and openGLContextClosing, all empty. In the constructor I attach the context via

  _glContext.setRenderer (this);
  _glContext.setContinuousRepainting (true);
  _glContext.attachTo (*this);

This already causes other UI processing to become sluggish with sometimes very long delays. I’m not performing any time-intensive operations (or much operations at all, really), in neither the message thread nor the OpenGL thread. The process monitor shows low CPU usage and the profiler does not show any unusual bottleneck.

I did a git bisect and the offending commit is 2ae87f95f1506ab98b6be1ad276a63147b310ce9: OpenGL: Carry out all GL rendering on a single thread. Looking at the diff, it seems that a lot of locking towards the message thread was introduced. I suspect that congestion of these locks causes the message thread to freeze for longer periods of time.

I’m running on Linux and did not have the opportunity to test on another platform yet.

@reuk I’ve read your comments on another threads related to this commit (JUCE 7 currently not suitable for OpenGL? - #6 by parawave). Unfortunately, for me the commit introduced problems rather than improving things. I would be happy to assist you in any testing or debugging regarding the current issues around OpenGL rendering!

Best,
Patric

I tested the same code on Windows and can report that the problem does not occur on Windows using current develop. Can’t test on MacOS, but I suspect this is a Linux/X11 issue then. Would be more than happy to help to resolve this!

I created a minimal example project that exposes the problem: GitHub - drlight-code/juce-opengl-minimal

It can be built via CMake and builds the Standalone and VST3 targets by default so both can be tested. The behavior is identical for me, so Standalone will probably be sufficient to reproduce the issue.

Some version info of my tested setup:
Arch Linux (up-to-date rolling release)
Kernel version 6.3.4-arch1-1
GCC version 13.1.1
OpenGL version string: 4.6.0 NVIDIA 530.41.03
xorg-server version: 21.1.8
Window manager: i3 and not running any desktop compositor

Hope this helps, if I can assist debugging somehow please let me know!

Thanks and best regards,
Patric

I should add that this was on Windows 10. I will try to get my hands on a Win11 system to test there as well. The example in this thread, where the poster reports problems for Win11, also causes problems on my Linux system: Windows 11 Issue with openGL

It’s difficult to say for sure, but I suspect that the main thread is getting blocked waiting for a lock to safely communicate with the X window system. The OpenGL thread currently takes the X lock each time it swaps the GL context buffers, but I’m not sure whether this is strictly necessary.

Please can you try removing the following line from OpenGLContext::NativeContext::swapBuffers() in juce_OpenGL_linux.h:266, and check whether the performance of the UI is improved?

XWindowSystemUtilities::ScopedXLock xLock;

Hi reuk, yes that fixes it for me :+1: Doing professional graphics development for a while I’ve never seen a requirement to communicate directly with the native window system when performing the buffer swap (with OpenGL, Vulkan or DirectX). The operation will be performed asynchronously anyway. The glXSwapBuffers as well as the implicit glFlush will typically return before the pending queue of GPU operations has finished. The actual swap will be performed during the screen vertical retrace of the monitor. Correctly synchronizing this to the native window system will to my knowledge always be handled by the graphics API implementation. Thanks for your help and fast response! :muscle:

Well actually. I’m stating this from a graphics programmer’s point of view where the graphics API is the only thing that ever outputs to the native window because everything is rendered via OpenGL, for example. If you have concurrent code that draws directly to the X window from a different thread, you might still have to perform locking via XLockDisplay. I can’t tell for sure from looking over the JUCE code whether that can happen with the asynchronous rendering thread and the way the 2D/GL graphics are composited.

That fix is now on develop:

1 Like