From 156b7dbe2193bb2f3895151229a7cd0fa71acfbf Mon Sep 17 00:00:00 2001 From: Henk Wiedig Date: Mon, 3 Aug 2026 15:32:40 +0200 Subject: [PATCH 1/2] allows seamless colortrans switching --- src/frame_colorcorrect.h | 5 +++++ src/frame_processor.cpp | 28 ++++++++++++++++++++-------- src/frame_processor.h | 3 ++- 3 files changed, 27 insertions(+), 9 deletions(-) diff --git a/src/frame_colorcorrect.h b/src/frame_colorcorrect.h index 682bb5c9..5c2c7f33 100644 --- a/src/frame_colorcorrect.h +++ b/src/frame_colorcorrect.h @@ -41,6 +41,11 @@ class FrameColorCorrect { void deinit(); bool ready() const { return ready_; } + // Update the correction parameters of an already-initialised context. + // The uniforms are uploaded per frame in process(), so this takes effect + // on the next frame. Must be called from the processor thread. + void set_params(float gain, float offset) { gain_ = gain; offset_ = offset; } + // Register the current OSD DMA-buf for compositing. Called whenever the // OSD double-buffer switches. The EGLImage is cached and only re-imported // when prime_fd changes. Must be called from the processor thread. diff --git a/src/frame_processor.cpp b/src/frame_processor.cpp index 425a708a..4066d58a 100644 --- a/src/frame_processor.cpp +++ b/src/frame_processor.cpp @@ -80,11 +80,14 @@ void FrameProcessor::set_osd_blend(int prime_fd, uint32_t w, uint32_t h, uint32_ } void FrameProcessor::set_color_correction(float gain, float offset, int drm_fd) { - cc_gain_ = gain; - cc_offset_ = offset; + cc_gain_.store(gain, std::memory_order_relaxed); + cc_offset_.store(offset, std::memory_order_relaxed); if (drm_fd >= 0) drm_fd_ = drm_fd; // update if caller supplies one - color_correct_.store(true, std::memory_order_relaxed); - // Actual EGL/GL init happens lazily on the processor thread (first frame) + // Release-store so the processor thread can't observe the enable before + // the gain/offset it belongs to. + color_correct_.store(true, std::memory_order_release); + // Actual EGL/GL init happens lazily on the processor thread (first frame); + // an already-initialised context picks the new params up on the next frame. } // ── Processor thread entry point ──────────────────────────────────────────── @@ -174,9 +177,16 @@ void FrameProcessor::process_loop() { // The GL render target is at OUTPUT size (dst_w × dst_h); // samplerExternalOES handles any input-to-output resize implicitly. // Re-init when output dimensions change. - bool need_gl = drm_fd_ >= 0 && - (color_correct_.load(std::memory_order_relaxed) || - osd_snap.prime_fd >= 0); + // The GL context is created once and outlives any number of + // recordings, so the correction parameters must be re-read from + // the (UI-writable) members every frame -- baking them in at + // init() would freeze whatever colortrans state happened to be + // active when the first DVR started. + bool cc_on = color_correct_.load(std::memory_order_acquire); + float cc_gain = cc_on ? cc_gain_.load(std::memory_order_relaxed) : 1.f; + float cc_offset = cc_on ? cc_offset_.load(std::memory_order_relaxed) : 0.f; + + bool need_gl = drm_fd_ >= 0 && (cc_on || osd_snap.prime_fd >= 0); if (gl_init_done_ && (dst_w != gl_out_w_ || dst_h != gl_out_h_)) { color_gl_.deinit(); gl_init_done_ = false; @@ -185,7 +195,7 @@ void FrameProcessor::process_loop() { gl_init_done_ = true; gl_out_w_ = dst_w; gl_out_h_ = dst_h; - color_gl_.init(drm_fd_, dst_w, dst_h, cc_gain_, cc_offset_); + color_gl_.init(drm_fd_, dst_w, dst_h, cc_gain, cc_offset); } bool ok; @@ -194,6 +204,8 @@ void FrameProcessor::process_loop() { // → NV12 (RGA CSC, no resize — GBM BO is at output size) ok = color_gl_.ready(); if (ok) { + color_gl_.set_params(cc_gain, cc_offset); + if (osd_snap.prime_fd >= 0) color_gl_.set_osd(osd_snap.prime_fd, osd_snap.width, osd_snap.height, diff --git a/src/frame_processor.h b/src/frame_processor.h index 17afc112..da01d5d9 100644 --- a/src/frame_processor.h +++ b/src/frame_processor.h @@ -130,7 +130,8 @@ class FrameProcessor { std::atomic color_correct_{false}; bool gl_init_done_{false}; uint32_t gl_out_w_{0}, gl_out_h_{0}; // output dims at last GL init - float cc_gain_{1.f}, cc_offset_{0.f}; + std::atomic cc_gain_{1.f}; + std::atomic cc_offset_{0.f}; int drm_fd_{-1}; // DRM fd for GBM/EGL (passed at construction) FrameColorCorrect color_gl_; }; From 0e489c7c14c6a2459eba99eaa06183624bbcf910 Mon Sep 17 00:00:00 2001 From: Henk Wiedig Date: Mon, 3 Aug 2026 16:18:30 +0200 Subject: [PATCH 2/2] fix drm_fd_ access --- src/frame_processor.cpp | 3 +-- src/frame_processor.h | 14 +++++++++----- src/main.cpp | 6 +++--- 3 files changed, 13 insertions(+), 10 deletions(-) diff --git a/src/frame_processor.cpp b/src/frame_processor.cpp index 4066d58a..66fc95c1 100644 --- a/src/frame_processor.cpp +++ b/src/frame_processor.cpp @@ -79,10 +79,9 @@ void FrameProcessor::set_osd_blend(int prime_fd, uint32_t w, uint32_t h, uint32_ osd_info_.stride_px = stride_px; } -void FrameProcessor::set_color_correction(float gain, float offset, int drm_fd) { +void FrameProcessor::set_color_correction(float gain, float offset) { cc_gain_.store(gain, std::memory_order_relaxed); cc_offset_.store(offset, std::memory_order_relaxed); - if (drm_fd >= 0) drm_fd_ = drm_fd; // update if caller supplies one // Release-store so the processor thread can't observe the enable before // the gain/offset it belongs to. color_correct_.store(true, std::memory_order_release); diff --git a/src/frame_processor.h b/src/frame_processor.h index da01d5d9..1c8366fe 100644 --- a/src/frame_processor.h +++ b/src/frame_processor.h @@ -65,8 +65,9 @@ class FrameProcessor { void set_resolution(EncResolution r) { target_res_.store((int)r, std::memory_order_relaxed); } // Enable GPU color correction using the DRM gamma formula y = clamp((x+offset)*gain, 0, 1). - // Safe to call from any thread. drm_fd is used to create the GBM/EGL context (lazy). - void set_color_correction(float gain, float offset, int drm_fd); + // Safe to call from any thread. The GBM/EGL context is created lazily on + // the processor thread from the DRM fd given at construction. + void set_color_correction(float gain, float offset); // Enable/disable color correction at runtime without changing the stored params. // Thread-safe: safe to toggle from the UI thread while the pacer is running. @@ -128,11 +129,14 @@ class FrameProcessor { // Written by UI thread (set_color_correction / set_color_correction_enabled), // read by processor thread — must be atomic where shared. std::atomic color_correct_{false}; - bool gl_init_done_{false}; - uint32_t gl_out_w_{0}, gl_out_h_{0}; // output dims at last GL init + bool gl_init_done_{false}; // processor thread only + uint32_t gl_out_w_{0}, gl_out_h_{0}; // processor thread only: dims at last GL init std::atomic cc_gain_{1.f}; std::atomic cc_offset_{0.f}; - int drm_fd_{-1}; // DRM fd for GBM/EGL (passed at construction) + // DRM fd for GBM/EGL. Immutable after construction: the processor thread + // reads it on every frame, so a runtime write from the UI thread would be + // a data race. const enforces that rather than leaving it to convention. + const int drm_fd_; FrameColorCorrect color_gl_; }; diff --git a/src/main.cpp b/src/main.cpp index 1bbb716d..e746bae0 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -683,7 +683,7 @@ extern "C" { void dvr_reenc_notify_colortrans(int enabled) { if (!frame_proc) return; if (enabled) - frame_proc->set_color_correction(live_colortrans_gain, live_colortrans_offset, drm_fd); + frame_proc->set_color_correction(live_colortrans_gain, live_colortrans_offset); else frame_proc->set_color_correction_enabled(false); } @@ -830,7 +830,7 @@ extern "C" { dvr_reenc_on_fatal_error); if (enable_live_colortrans) frame_proc->set_color_correction(live_colortrans_gain, - live_colortrans_offset, drm_fd); + live_colortrans_offset); pthread_create(&g_tid_fproc, NULL, &FrameProcessor::__THREAD__, frame_proc); dvr_configure_reenc_receiver(); if (receiver) receiver->set_dvr_reenc_on_start(dvr_reenc_on_start); @@ -1878,7 +1878,7 @@ int main(int argc, char **argv) dvr_reenc_on_fatal_error); if (enable_live_colortrans) { frame_proc->set_color_correction(live_colortrans_gain, - live_colortrans_offset, drm_fd); + live_colortrans_offset); spdlog::info("Encoder color correction enabled: gain={} offset={}", live_colortrans_gain, live_colortrans_offset); }