Skip to content

[Bug]: Windows standalone OpenGLContext uses more CPU and feels less smooth at 60 Hz than at 144 Hz for the same workload #1643

@kasnowww147

Description

@kasnowww147

Detailed steps on how to reproduce the bug

I am changing the refresh rate setting of the same physical monitor in Windows display settings. The only variable I change is the monitor refresh rate: 144 Hz vs 60 Hz.

Repro steps:

  1. Build and run a Windows standalone JUCE app that attaches juce::OpenGLContext to a visible component.

  2. Keep the app in a light steady-state rendering scenario. In my case:

    • component painting enabled
    • JUCE continuous repaint disabled
    • in standalone builds, the baseline UI timer is 60 Hz when the app is not in an interaction-boosted state
    • interaction / playback-related paths in the app can raise the effective frame cadence above that baseline, up to about 120 Hz depending on state
  3. In Windows display settings, on the same monitor, set the monitor refresh rate to 144 Hz.

    • Windows Settings -> System -> Display -> Advanced display -> Choose a refresh rate
  4. Run the app and observe CPU usage and UI smoothness.

  5. Without changing the app or code, change the refresh rate of that same monitor to 60 Hz in Windows display settings.

  6. Run the same standalone app again and observe CPU usage and UI smoothness.

Observed result:

  • At 60 Hz, the standalone app uses noticeably more CPU in Task Manager and feels visibly more stuttery.
  • At 144 Hz, the same app on the same monitor is smoother and uses less CPU.
  • The workload is otherwise unchanged.

Local investigation note:
I instrumented JUCE's OpenGL path locally. The extra 60 Hz time does not appear to be mainly in renderOpenGL() or swapBuffers().
Instead, the frame-period wait appears around JUCE's Windows OpenGL context lifecycle / first GL setup call:

  • initially around OpenGLContext::deactivateCurrentContext()
  • and, if per-frame deactivation is avoided, around the first GL setup call of the next frame

Adding an explicit DwmFlush() before entering the native GL scope moves that wait into an explicit compositor wait, which suggests the 60 Hz pacing cost is being hidden inside JUCE's Windows standalone OpenGL path.

What is the expected behaviour?

Changing the refresh rate of the same monitor from 144 Hz to 60 Hz should not make the same standalone JUCE OpenGL app significantly more CPU-heavy and visibly less smooth for the same workload.

Operating systems

Windows

What versions of the operating systems?

Windows 11

Architectures

x86_64

Stacktrace

Plug-in formats (if applicable)

No response

Plug-in host applications (DAWs) (if applicable)

No response

Testing on the develop branch

The bug is present on the develop branch

Code of Conduct

  • I agree to follow the Code of Conduct

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions