BR/FR: OpenGL context with continuous repaint hogs one core 100%

I’ve noticed that one of my cores was 100% blocked when using an OpenGL context with continuous repaint turned on.

So, as an experiment, I turned the continuous repaint off, set the swap interval to 0, and used a vblank attachment to trigger a repaint. The OpenGL context now refreshes at a stable sync framerate (60 Hz in my test case), and none of my cores is hogged 100% anymore. Overall CPU consumption dropped from 8% to 2-3% (I have a 20-core system = 10 cores doubled by Hyperthreading). So one core used to 100% equals 5% load.

This is a massive improvement. Could the JUCE team replace the current “continuous repaint” logic with a simple VBlank attachment and archive the same, or at least document this discovery so future users don’t fall into the same trap?

3 Likes

That is definitely not the normal state, it must be a bug in the vsync detection. At least with JUCE7, it works. May I ask what hardware/OS are you using? I once had a similar phenomenon inside a Parallels instance.

I don’t see 100% core usage in the OpenGLDemo example project on macOS Arm or Windows 11.

Please provide as much detail as possible so that we can reproduce this issue. It would be helpful to have:

  • A minimal example project demonstrating the problem, ideally one of the JUCE examples
  • The OS version, graphics driver version, and full hardware spec of the machine that demonstrates the problem
  • The OpenGL and GLSL versions in use

We’re unlikely to make any changes in this area unless it’s possible to demonstrate a clear problem with the current implementation. In particular, it took a long time to get the current implementation working properly on macOS 10.12 with DRAW_ASYNC enabled (details here), and I’d be reluctant to make any changes that might cause regressions there.

1 Like

I have seen this too, for years. It spins a thread and will repaint as fast as it possibly can. It seems to randomly stop after a while of runtime. It can also be fixed by putting a sleep at the end of each loop for a short while. I’m not sure if this is technically a bug, as in videogame word this is kind of the point of things, to be constantly updating your framebuffer as often as you can.

You can also use DwmFlush to similar effect. I think the root of the problem is that SwapBuffers for whatever reason reports as CPU usage when really it should be a wait state. Is it actually using CPU? I don’t know for sure, it might just be marked incorrectly by CPU monitors for some reason.

After adding this little DwmFlush helper class, my CPU reports as 0% most of the time, even with fairly heavy OpenGL usage.

Windows 11 22631.3527 - all the latest updates installed
Nvidia GTX 1060 6GB with latest Nvidia Studio Drivers 552.22

DxDiag attached

openGLContext.setOpenGLVersionRequired ( openGL4_3 );
openGLContext.setComponentPaintingEnabled ( true );
openGLContext.setContinuousRepainting ( true );
openGLContext.setSwapInterval ( 1 );

DxDiag.txt (92.9 KB)

Sorry to be harsh. I consider testing and debugging in emulations and virtualizations diagnostic malpractice. You go from having zero information to having misinformation. You’re not measuring/testing the target system; you’re measuring/testing the emulation/virtualization software.

I don’t know which video games you’ve been part of, but during my career, we’ve waited for the next v-sync once the drawing was complete, or if we were over the frame time budget, then we swapped without waiting. Otherwise, you will get tearing, which looks extremely unpleasant and distracting.

My solution is a lot simpler, platform agnostic, and a one-liner if you ignore the extra line in the setup to set the swap interval from 1 to 0.

Does this machine also have integrated graphics? Do you get different results if you use the integrated graphics device?

Do you see the same thing in the JUCE examples if you apply the changes from your post?

I’ve disabled it in the BIOS, don’t have drivers for it, etc.

Nvidia cards are very popular, so comparing them with Intel-integrated graphics is not very helpful (IMO). I want to make sure my solution works everywhere and with as few resources as possible.

The vBlankAttachment is a good, tested solution and this workaround works perfectly, ignoring any potential faults in the swapchain implementation of the OpenGL driver.

I will give this a try next.

  1. It doesn’t happen on other platforms
  2. The snippet provided is like 5 lines of code
  3. I’m literally just trying to help

Your immediate guess as to the best solution isn’t automatically the brilliant perfect answer just because it works on your test machine. This type of change likely has many effects you haven’t even thought about.

Yes, I’ve changed the required version to 4.3, set the swap interval to 0, and disabled the continuous repaint, and finally added the vblankattachment. CPU usage went from 5-8% to 1-2%.

Previously one core was always at 100%, afterwards none of them were.

I also disabled the “controlOverlay”. If I leave that enabled, the framerate is worse (only 30 fps instead of 60 fps = it misses every second frame), but CPU usage was still lower.

  1. I will give your solution a try.
  2. Please don’t exaggerate for effect. Even a charitable line count comes to 22, which doesn’t include calling the flush function.
  3. I know, but I didn’t see the point since my solution seems much more straightforward. It might have its benefits after playing with the OpenGLDemo from the JUCE examples.

People definitely render JUCE Components through OpenGL, so this sounds like a deal-breaker.

I can’t use your solution for quick testing because I also have to include Windows.h, and that causes all kinds of problems with my project.

Yeah, I understand. My workaround works very well for my specific project and provides enormous CPU savings, but I can now see that it’s not a one-size-fits-all solution.

…but neither is the current solution :wink:

I’ve managed to work around that pesky Window.h problem, and I can confirm that your solution works. Both for my project AND the DeamRunner OpenGL demo. Buttery smooth 60 fps without hogging ANY of the cores. Thank you :slight_smile:

@reuk Yes, this includes the controls-overlay as well. You should really try it yourself. It works wonders.

1 Like