Skip to content

feat(display): request deep QOpenGLWidget texture for 10-bit GL presentation (spike) - #65

Closed
mcoliver wants to merge 1 commit into
mainfrom
spike/qopenglwidget-10bit
Closed

mcoliver wants to merge 1 commit into
mainfrom
spike/qopenglwidget-10bit

Conversation

@mcoliver

@mcoliver mcoliver commented Sep 29, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

Spike: can the plain QOpenGLWidget view do true 10-bit output if we stop Qt from truncating it to 8 bits?

Background

Upstream OpenRV's Qt 6 port (#606, Oct 2024) replaced QGLWidget with QOpenGLWidget.

  • QGLWidget rendered straight into the window's back buffer.
  • QOpenGLWidget renders into a Qt-owned offscreen buffer that Qt then composites into the window. Qt creates that buffer as GL_RGBA8 unless setTextureFormat() is called, and we never called it.

So since the port, every frame on the OpenGL path has been cut to 8 bits before compositing. The 10/10/10/2 surface format we request only affects the GL context, not that buffer. Upstream's answer was two new presentation layers, Vulkan (#1319) and Metal (#1327), which we ported. Neither PR mentions setTextureFormat(). (Thanks to Seth for pointing at this.)

Changes

  • GLView: when the display format asks for more than 8 bits, calls setTextureFormat(GL_RGB10_A2). UTV_GL_TEXTURE_FORMAT=rgba8|rgb10a2|rgba16f overrides it for A/B testing.

  • main.cpp: sets a matching deep QSurfaceFormat::defaultFormat() before QApplication is created, so the top-level window surface Qt composites into is also deep. On macOS this only happens when Metal is bypassed.

  • RvDocument: UTV_PRESENTATION_BACKEND=gl forces the OpenGL view over Vulkan/Metal, and the chosen backend is logged.

  • Diagnostics: on first paint, logs the bit depth at every stage (widget texture, widget buffer, context, window surface):

    INFO: GL presentation: requested display 10/10/10/2, widget texture GL_RGB10_A2, widget FBO 10/10/10/2, context ..., window surface ...
    
  • docs/testing/10bit-presentation.md: step-by-step instructions for testers with 10-bit panels (A/B/C/D rows, test image, what to report).

The default 8-bit display format is unchanged: no texture format is set and no default format is changed.

Status / what's verified

  • Builds on macOS. Local CI below builds Windows and Linux.
  • Preferences at 10/10/10/2 with UTV_PRESENTATION_BACKEND=gl: the app launches and keeps running (no crash in 30 s). Backend selection and bit depths are not yet confirmed, because the window never painted without a display.
  • Visual verification on real 10-bit panels (Windows, Linux, macOS): needs testers, see the doc. My session had no display attached, so I couldn't check the first-paint log or the image.

Decision this informs

  • B (GL, 10-bit buffer) as smooth as D (Vulkan) on Windows/Linux: the OpenGL path can do SDR 10-bit on its own. The Vulkan layer could become optional or be removed, which drops a dependency, cross-API memory sharing and a CPU fallback.
  • macOS: expected to stay 8-bit on GL, since Qt's macOS OpenGL windows are 8-bit. The Metal path stays regardless, because it is the only way to get EDR/HDR.

🤖 Generated with Claude Code

…ntation

Spike. Since the Qt 6 port replaced QGLWidget with QOpenGLWidget, GLView
renders into a Qt-owned FBO that Qt creates as GL_RGBA8 unless
setTextureFormat() is called. Output was therefore truncated to 8 bits
before Qt composited it into the window, regardless of the 10/10/10/2
display format requested for the context. That is what the Vulkan
(Linux/Windows) and Metal (macOS) presentation layers were added to work
around.

- GLView: when the display format asks for >8 bits per channel, call
  setTextureFormat(GL_RGB10_A2). UTV_GL_TEXTURE_FORMAT=rgba8|rgb10a2|rgba16f
  overrides it for A/B testing.
- main: set a matching deep QSurfaceFormat::defaultFormat() before
  QApplication so the top-level window surface Qt composites into is deep
  too (on macOS only when the Metal backend is bypassed).
- RvDocument: UTV_PRESENTATION_BACKEND=gl forces the QOpenGLWidget path
  over Vulkan/Metal, and the chosen backend is logged.
- GLView logs the bit depth at each stage (widget texture, widget FBO,
  context, window surface) on first paint.
- docs/testing/10bit-presentation.md: tester instructions for 10-bit
  displays.

Needs verification on real 10-bit panels; no behaviour change for the
default 8-bit display format.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Signed-off-by: Michael Oliver <mcoliver@gmail.com>
@mcoliver

Copy link
Copy Markdown
Collaborator Author

Closing this spike without merging. Recording the finding here in case the OpenGL fallback ever matters.

Finding: QOpenGLWidget renders into a Qt-owned FBO that Qt creates as GL_RGBA8 unless setTextureFormat() is called. Since the Qt 6 port replaced QGLWidget (#606 upstream), the OpenGL presentation path has therefore truncated output to 8 bits, whatever display format is requested. The fix is setTextureFormat(GL_RGB10_A2) plus a deep QSurfaceFormat::defaultFormat(), as in this branch.

Why not merge: 10-bit/HDR presentation is served by Metal (macOS, see #69) and Vulkan (Windows/Linux). The OpenGL presentation path is only a fallback for machines without those (VMs, remote desktop, old drivers), where 10-bit display isn't needed. Vulkan is also the route to HDR (HDR10/scRGB) on Windows/Linux, which OpenGL can't do: Qt's GL backend is SDR-only. So the Vulkan presenter stays regardless of whether this fix works.

Headless 10-bit+ image and video I/O is unaffected: utvio renders offscreen in float and writes deep formats (verified by the round-trip test in #64).

The long-term direction is porting the renderer off OpenGL, tracked in the QRhi issue linked below. The branch spike/qopenglwidget-10bit is kept for reference.

@mcoliver mcoliver closed this Sep 30, 2026
@mcoliver

mcoliver commented Sep 30, 2026 •

Copy link
Copy Markdown
Collaborator Author

Renderer port tracked in #70.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant