From c020496935f9ee3e7be435e7bccd5df864caee2b Mon Sep 17 00:00:00 2001 From: "Ryan J. Quinlan" Date: Fri, 4 Sep 2026 09:21:46 -0700 Subject: [PATCH 1/3] Fix invalid QML comment syntax and guard empty shader info log FileSystemBrowser.qml used a Python-style `#` comment, which QML does not support. shader_program_base.cpp dereferenced an empty info-log vector when a driver reports a compile failure but leaves the log length at 0. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01B2oxqFLjvrhWTVsUWonajB Signed-off-by: Ryan J. Quinlan --- .../xstudio/FileSystemBrowser.qml | 2 +- src/ui/opengl/src/shader_program_base.cpp | 28 +++++++++++++------ 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/src/plugin/utility/filesystem_browser/FileSystemBrowser/xstudio/FileSystemBrowser.qml b/src/plugin/utility/filesystem_browser/FileSystemBrowser/xstudio/FileSystemBrowser.qml index 3dfeb190d..226fc5112 100644 --- a/src/plugin/utility/filesystem_browser/FileSystemBrowser/xstudio/FileSystemBrowser.qml +++ b/src/plugin/utility/filesystem_browser/FileSystemBrowser/xstudio/FileSystemBrowser.qml @@ -31,7 +31,7 @@ Item { property var previewing: previewMediaUuid != helpers.QVariantFromUuidString("") ? previewMediaUuid == currentPlayhead.mediaUuid : false - # uri requires triple slash before drive letter on Windows, since this is the root. + // uri requires triple slash before drive letter on Windows, since this is the root. property var thumbSrcRoot: Qt.platform.os === "windows" ? "image://thumbnail/file:///" : "image://thumbnail/file://" FileSystemBrowserScanResultsModel { diff --git a/src/ui/opengl/src/shader_program_base.cpp b/src/ui/opengl/src/shader_program_base.cpp index 0ebfb7676..cced7d795 100644 --- a/src/ui/opengl/src/shader_program_base.cpp +++ b/src/ui/opengl/src/shader_program_base.cpp @@ -573,9 +573,15 @@ GLuint compile_frag_shader(const std::string fragmentSource) { GLint maxLength = 0; glGetShaderiv(fragmentShader, GL_INFO_LOG_LENGTH, &maxLength); - // The maxLength includes the NULL character - std::vector infoLog(maxLength); - glGetShaderInfoLog(fragmentShader, maxLength, &maxLength, &infoLog[0]); + // The maxLength includes the NULL character. Some drivers report a + // compile failure but leave the info log empty, so guard against + // that instead of dereferencing an empty vector's data(). + std::string info_log_str = "(no info log provided by driver)"; + if (maxLength > 0) { + std::vector infoLog(maxLength); + glGetShaderInfoLog(fragmentShader, maxLength, &maxLength, infoLog.data()); + info_log_str.assign(infoLog.data()); + } // We don't need the shader anymore. glDeleteShader(fragmentShader); @@ -592,7 +598,7 @@ GLuint compile_frag_shader(const std::string fragmentSource) { // Use the infoLog as you see fit. std::stringstream e; e << "Fragment shader error:\n\n" - << infoLog.data() << "\n\nin program: \n\n" + << info_log_str << "\n\nin program: \n\n" << source_with_linenumbers; throw std::runtime_error(e.str().c_str()); } @@ -618,9 +624,15 @@ GLuint compile_vertex_shader(const std::string vertexSource) { GLint maxLength = 0; glGetShaderiv(vertexShader, GL_INFO_LOG_LENGTH, &maxLength); - // The maxLength includes the NULL character - std::vector infoLog(maxLength); - glGetShaderInfoLog(vertexShader, maxLength, &maxLength, &infoLog[0]); + // The maxLength includes the NULL character. Some drivers report a + // compile failure but leave the info log empty, so guard against + // that instead of dereferencing an empty vector's data(). + std::string info_log_str = "(no info log provided by driver)"; + if (maxLength > 0) { + std::vector infoLog(maxLength); + glGetShaderInfoLog(vertexShader, maxLength, &maxLength, infoLog.data()); + info_log_str.assign(infoLog.data()); + } // We don't need the shader anymore. glDeleteShader(vertexShader); @@ -628,7 +640,7 @@ GLuint compile_vertex_shader(const std::string vertexSource) { // Use the infoLog as you see fit. std::stringstream e; e << "Vertex shader error:\n\n" - << infoLog.data() << "\n\nin program: \n\n" + << info_log_str << "\n\nin program: \n\n" << vertexSource; throw std::runtime_error(e.str().c_str()); From faff66770521ffd8f56754f4365879b57e0a62f8 Mon Sep 17 00:00:00 2001 From: "Ryan J. Quinlan" Date: Fri, 4 Sep 2026 09:39:01 -0700 Subject: [PATCH 2/3] Guard empty program-link info log in GLShaderProgram::compile Same undefined-behavior pattern as the vertex/fragment shader compile paths: some drivers report a program link failure but leave the info log empty, and dereferencing an empty vector's data() segfaulted during viewport renderer setup (SDF text shader link) on Apple's Metal-backed OpenGL. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01B2oxqFLjvrhWTVsUWonajB Signed-off-by: Ryan J. Quinlan --- src/ui/opengl/src/shader_program_base.cpp | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/ui/opengl/src/shader_program_base.cpp b/src/ui/opengl/src/shader_program_base.cpp index cced7d795..dd18256a3 100644 --- a/src/ui/opengl/src/shader_program_base.cpp +++ b/src/ui/opengl/src/shader_program_base.cpp @@ -844,9 +844,15 @@ void GLShaderProgram::compile(const bool force_combine_frag_shaders) { GLint maxLength = 0; glGetProgramiv(program_, GL_INFO_LOG_LENGTH, &maxLength); - // The maxLength includes the NULL character - std::vector infoLog(maxLength); - glGetProgramInfoLog(program_, maxLength, &maxLength, &infoLog[0]); + // The maxLength includes the NULL character. Some drivers report a + // link failure but leave the info log empty, so guard against that + // instead of dereferencing an empty vector's data(). + std::string info_log_str = "(no info log provided by driver)"; + if (maxLength > 0) { + std::vector infoLog(maxLength); + glGetProgramInfoLog(program_, maxLength, &maxLength, infoLog.data()); + info_log_str.assign(infoLog.data()); + } // Detach shaders after failed link .... (not clear if this is correct, // but no errors have been observed) @@ -873,7 +879,7 @@ void GLShaderProgram::compile(const bool force_combine_frag_shaders) { // Use the infoLog as you see fit. std::stringstream e; - e << "Shader link error:\n\n" << infoLog.data(); + e << "Shader link error:\n\n" << info_log_str; std::for_each( fragment_shaders_.begin(), From aec4111fc3aa1fdfd16be90be56d81b305e3f4dc Mon Sep 17 00:00:00 2001 From: "Ryan J. Quinlan" Date: Fri, 4 Sep 2026 09:50:02 -0700 Subject: [PATCH 3/3] Guard against null active_shader_program_ in draw_image If even the "no image" fallback shader fails to compile/link (e.g. a driver that reports failure with an empty info log), active_shader_program_ is left null. bind_textures() already treats this as nothing-to-draw, but draw_image() dereferenced it unconditionally, segfaulting as soon as media was loaded and a frame draw was attempted. This reliably crashed on an Apple M4 Max where the default GLSL shaders fail to link on the OpenGL-4.1-over-Metal driver. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01B2oxqFLjvrhWTVsUWonajB Signed-off-by: Ryan J. Quinlan --- src/ui/opengl/src/opengl_viewport_renderer.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/ui/opengl/src/opengl_viewport_renderer.cpp b/src/ui/opengl/src/opengl_viewport_renderer.cpp index 5d6431a14..53af90059 100644 --- a/src/ui/opengl/src/opengl_viewport_renderer.cpp +++ b/src/ui/opengl/src/opengl_viewport_renderer.cpp @@ -479,6 +479,12 @@ void OpenGLViewportRenderer::draw_image( const Imath::M44f &viewport_to_image_space, const float viewport_du_dx) { + // active_shader_program_ can be null if even the fallback "no image" shader + // failed to compile/link (e.g. a driver issue) - bind_textures() already + // treats this as nothing-to-draw, so match that here rather than + // dereferencing a null program. + if (!active_shader_program_) + return; active_shader_program_->use();