Skip to content

Fix black 3D viewport when building with Qt6#1017

Open
jenningsloy318 wants to merge 3 commits intoleozide:masterfrom
jenningsloy318:fix/qt6-black-viewport
Open

Fix black 3D viewport when building with Qt6#1017
jenningsloy318 wants to merge 3 commits intoleozide:masterfrom
jenningsloy318:fix/qt6-black-viewport

Conversation

@jenningsloy318
Copy link
Copy Markdown

The 3D viewport, brick thumbnails, and canvas were rendered entirely black when LeoCAD was built against Qt6, while the rest of the UI (menus, panels, timeline) worked normally.

Root cause: Qt6's QOpenGLWidget internal FBO pipeline silently failed to render with the legacy GLSL 110 shaders using attribute/varying keywords, even though the OpenGL 4.6 Compatibility Profile context accepted them without errors.

Changes:

  • Upgrade GLSL shader prefix from #version 110 to #version 330 core, replacing attribute/varying with in/out, gl_FragColor with an explicit out vec4 FragColor, and texture2D/textureCube with texture

  • Always initialize QSurfaceFormat with depth buffer (24-bit), stencil buffer (8-bit), and renderableType set to Desktop OpenGL, instead of only setting the format when anti-aliasing samples > 1. This ensures proper depth testing and prevents Qt6 EGL from creating an OpenGL ES context on NVIDIA

  • Guard Qt::AA_ShareOpenGLContexts with a Qt5 version check since it was removed in Qt6

  • Remove unused QOpenGLFunctions_3_2_Core include (versionFunctions API was removed in Qt6)

Tested on Linux (NVIDIA GTX 1070 Ti, driver 580.105.08) with Qt 6.x on Wayland-EGL. All 16 shaders compile and 8 programs link successfully. Viewport renders gradient background, brick geometry, part thumbnails, and view sphere correctly.

The 3D viewport, brick thumbnails, and canvas were rendered entirely
black when LeoCAD was built against Qt6, while the rest of the UI
(menus, panels, timeline) worked normally.

Root cause: Qt6's QOpenGLWidget internal FBO pipeline silently failed
to render with the legacy GLSL 110 shaders using attribute/varying
keywords, even though the OpenGL 4.6 Compatibility Profile context
accepted them without errors.

Changes:

- Upgrade GLSL shader prefix from #version 110 to #version 330 core,
  replacing attribute/varying with in/out, gl_FragColor with an
  explicit out vec4 FragColor, and texture2D/textureCube with texture

- Always initialize QSurfaceFormat with depth buffer (24-bit), stencil
  buffer (8-bit), and renderableType set to Desktop OpenGL, instead of
  only setting the format when anti-aliasing samples > 1. This ensures
  proper depth testing and prevents Qt6 EGL from creating an OpenGL ES
  context on NVIDIA

- Guard Qt::AA_ShareOpenGLContexts with a Qt5 version check since it
  was removed in Qt6

- Remove unused QOpenGLFunctions_3_2_Core include (versionFunctions
  API was removed in Qt6)

Tested on Linux (NVIDIA GTX 1070 Ti, driver 580.105.08) with Qt 6.x
on Wayland-EGL. All 16 shaders compile and 8 programs link
successfully. Viewport renders gradient background, brick geometry,
part thumbnails, and view sphere correctly.
The previous commit hardcoded #version 330 core shaders which requires
OpenGL 3.3+. This would break Qt5 builds on older hardware with only
OpenGL 2.x or 3.0-3.2 support.

Detect the GL version at runtime via QOpenGLContext::currentContext()
format and select the appropriate shader prefix:

- GL >= 3.3: Use #version 330 core with modern in/out syntax
- GL < 3.3: Fall back to #version 110 with legacy attribute/varying

Verified builds and renders correctly on both Qt5 and Qt6 with
OpenGL 4.6 (NVIDIA). The legacy path preserves the original shader
behavior for older GL contexts.

lcInitializeSurfaceFormat(argc, argv);

#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We rely on shared contexts, it's supported in Qt6. The app crashes with this change.

QCoreApplication Application(argc, argv);
const lcCommandLineOptions Options = lcApplication::ParseCommandLineOptions();

QSurfaceFormat Format = QSurfaceFormat::defaultFormat();
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The original code here works better:

  • We need to set the AA samples here for CLI rendering
  • We don't need stencil, it may take precision away from the depth buffer depending on the driver
  • We use GLES on iOS

@leozide
Copy link
Copy Markdown
Owner

leozide commented Mar 20, 2026

I don't think the black screen you're seeing is directly related to Qt6, I've been using it for a while without problems on both Mac and Windows.

@jenningsloy318
Copy link
Copy Markdown
Author

I am not sure , but I am running under debian Linux 13 with lxqt 2.3 ( qt 6.8.2 and wayland/labwc).

and these changes are modified by claude code, so i am not expert in this, can you please help me debug this ?

@leozide
Copy link
Copy Markdown
Owner

leozide commented Mar 24, 2026

This is something specific to your setup, so I can't help debug directly. See if reverting the changes to qtmain.cpp still work and we can take the shader changes only.

@jenningsloy318
Copy link
Copy Markdown
Author

I reverted qtmain.cpp, the issue comes back

image

Wrap the QSurfaceFormat::setRenderableType(QSurfaceFormat::OpenGL) call
in #ifndef LC_OPENGLES so that iOS builds using OpenGL ES are not forced
to desktop OpenGL, while Linux/Wayland with NVIDIA EGL still gets the
explicit desktop OpenGL context needed to prevent a driver crash in
libnvidia-eglcore.so during glDrawArrays.
@jenningsloy318
Copy link
Copy Markdown
Author

jenningsloy318 commented Mar 29, 2026

Hi @leozide, I tried reverting all qtmain.cpp changes as you suggested, keeping only the shader changes. The black viewport returned — and it turned out to be a segfault inside
libnvidia-eglcore.so during glDrawArrays, regardless of shader version (#version 110 crashes too).

The NVIDIA EGL driver on Wayland requires explicit depth/stencil buffer sizes for proper context creation. Here's how I addressed your concerns:

  • Stencil: Both depth(24) and stencil(8) are required — removing either causes the NVIDIA EGL crash. D24S8 is the standard format and doesn't reduce depth precision.
  • GLES on iOS: setRenderableType(OpenGL) is now wrapped in #ifndef LC_OPENGLES.
  • Shared contexts: On NVIDIA EGL/Wayland, Qt::AA_ShareOpenGLContexts on Qt6 triggers the crash. The #if guard ensures it's only called on Qt5.

If the stencil or AA_ShareOpenGLContexts guard causes problems on Mac/Windows, we could platform-guard them with #ifdef Q_OS_LINUX.

@leozide
Copy link
Copy Markdown
Owner

leozide commented Mar 31, 2026

D24S8 definitely reduces precision compared to D32, it's 8 bits less for depth.

Shared Contexts are a requirement as we don't create resources per context. Your driver may be sharing resources by default but it won't happen everywhere, this is not something that can be disabled on any platform.

It would be interesting to try 1 change at a time to see what's actually fixing the black window for you.

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.

2 participants