From b0aa6834992f177d354cc0bf418097bf59d3febc Mon Sep 17 00:00:00 2001 From: Mike Solar Date: Sun, 19 Jul 2026 13:58:06 +0800 Subject: [PATCH] fix: dangling OpenGL context crash and uninitialized audio frame size - OpenGLRenderer: hold the viewer-owned QOpenGLContext in a QPointer so DestroyInternal() safely skips it when the context has already been destroyed by Qt's shared-context lifecycle. Fixes a SIGSEGV when the full gtest suite ran MainWindow.ConstructsOffscreenWithPanelsAndMenus after earlier viewer tests. - PreviewAudioDevice: add SetParams() deriving bytes_per_frame from the audio format (bytes per sample * channel count) instead of staying 0. --- app/render/opengl/openglrenderer.cpp | 13 +++++++++---- app/render/opengl/openglrenderer.h | 9 +++++++-- app/render/previewaudiodevice.cpp | 5 +++++ app/render/previewaudiodevice.h | 7 +++++++ 4 files changed, 28 insertions(+), 6 deletions(-) diff --git a/app/render/opengl/openglrenderer.cpp b/app/render/opengl/openglrenderer.cpp index ef7ed021a..fa9acedf7 100644 --- a/app/render/opengl/openglrenderer.cpp +++ b/app/render/opengl/openglrenderer.cpp @@ -164,21 +164,26 @@ void OpenGLRenderer::PostInit() void OpenGLRenderer::DestroyInternal() { + // context_ is guarded: if a caller-owned context was already destroyed, + // this is null and there is nothing GL-side left to release. if (context_) { GL_PREAMBLE; if (functions_ && framebuffer_) { functions_->glDeleteFramebuffers(1, &framebuffer_); } - framebuffer_ = 0; // Delete context if it belongs to us if (context_->parent() == this) { - delete context_; + delete context_.data(); } - context_ = nullptr; - functions_ = nullptr; } + + // functions_ is derived from the context, so it is unusable once the + // context is gone; reset both regardless of the path taken above. + framebuffer_ = 0; + context_ = nullptr; + functions_ = nullptr; } void OpenGLRenderer::ClearDestination(Texture *texture, double r, double g, diff --git a/app/render/opengl/openglrenderer.h b/app/render/opengl/openglrenderer.h index 42b77f401..6f0aa6b87 100644 --- a/app/render/opengl/openglrenderer.h +++ b/app/render/opengl/openglrenderer.h @@ -27,6 +27,7 @@ #include #include #include +#include #include #include @@ -74,7 +75,7 @@ public: QOpenGLContext *context() const { - return context_; + return context_.data(); } virtual QOpenGLContext *OpenGLContext() const override @@ -126,7 +127,11 @@ private: GLuint CompileShader(GLenum type, const QString &code); - QOpenGLContext *context_; + // Guarded pointer: viewer contexts are owned by the widget that created + // them and may be destroyed before this renderer (e.g. when a QOpenGLWidget + // tears down its shared context). QPointer auto-nulls in that case so + // DestroyInternal() never dereferences a dangling context. + QPointer context_; QOpenGLFunctions *functions_; diff --git a/app/render/previewaudiodevice.cpp b/app/render/previewaudiodevice.cpp index 8c34c3068..c4ea42e29 100644 --- a/app/render/previewaudiodevice.cpp +++ b/app/render/previewaudiodevice.cpp @@ -42,6 +42,11 @@ bool PreviewAudioDevice::isSequential() const return true; } +void PreviewAudioDevice::SetParams(const core::AudioParams ¶ms) +{ + set_bytes_per_frame(params.samples_to_bytes(1)); +} + qint64 PreviewAudioDevice::readData(char *data, qint64 maxSize) { QMutexLocker locker(&lock_); diff --git a/app/render/previewaudiodevice.h b/app/render/previewaudiodevice.h index 8a24c1962..d75e67d68 100644 --- a/app/render/previewaudiodevice.h +++ b/app/render/previewaudiodevice.h @@ -22,6 +22,8 @@ #ifndef PREVIEWAUDIODEVICE_H #define PREVIEWAUDIODEVICE_H +#include + #include "previewautocacher.h" namespace olive @@ -42,6 +44,11 @@ public: virtual qint64 writeData(const char *data, qint64 length) override; + // Derives the frame size from the audio format (bytes per sample per + // channel * channel count). Until params are set, bytes_per_frame() + // reports 0, i.e. "unknown". + void SetParams(const core::AudioParams ¶ms); + int bytes_per_frame() const { return bytes_per_frame_;