Skip to content

Commit a47ceb3

Browse files
committed
Use cv::swap and copyTo to eliminate redundant frame copies
Replace clone() with cv::swap() in video_tick for both background and enhance filters. Swap is O(1) — it transfers buffer ownership to the tick thread instead of deep-copying ~8 MB per frame. In the render thread, use copyTo() instead of clone() for the stage surface data. copyTo reuses the existing cv::Mat allocation when dimensions match, avoiding a fresh heap allocation every frame. Add a newFrameAvailable flag (protected by inputBGRALock) so the tick thread only processes genuinely new frames and doesn't re-process stale data after swap empties the shared buffer. Signed-off-by: Xavier Ruiz <github@xav.ie>
1 parent d27ae15 commit a47ceb3

4 files changed

Lines changed: 13 additions & 11 deletions

File tree

src/FilterData.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ struct filter_data : public ORTModelData, public std::enable_shared_from_this<fi
3131
gs_stagesurf_t *stagesurface;
3232

3333
cv::Mat inputBGRA;
34+
std::atomic<bool> newFrameAvailable{false};
3435

3536
std::atomic<bool> isDisabled{false};
3637

src/background-filter.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -536,11 +536,11 @@ void background_filter_video_tick(void *data, float seconds)
536536
// No data to process
537537
return;
538538
}
539-
if (tf->inputBGRA.empty()) {
540-
// No data to process
539+
if (!tf->newFrameAvailable) {
541540
return;
542541
}
543-
imageBGRA = tf->inputBGRA.clone();
542+
cv::swap(imageBGRA, tf->inputBGRA);
543+
tf->newFrameAvailable = false;
544544
}
545545

546546
if (tf->enableImageSimilarity) {

src/enhance-filter.cpp

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -247,18 +247,18 @@ void enhance_filter_video_tick(void *data, float seconds)
247247
return;
248248
}
249249

250-
if (tf->inputBGRA.empty()) {
251-
return;
252-
}
253-
254250
// Get input image from source rendering pipeline
255251
cv::Mat imageBGRA;
256252
{
257253
std::unique_lock<std::mutex> lock(tf->inputBGRALock, std::try_to_lock);
258254
if (!lock.owns_lock()) {
259255
return;
260256
}
261-
imageBGRA = tf->inputBGRA.clone();
257+
if (!tf->newFrameAvailable) {
258+
return;
259+
}
260+
cv::swap(imageBGRA, tf->inputBGRA);
261+
tf->newFrameAvailable = false;
262262
}
263263

264264
cv::Mat outputImage;

src/obs-utils/obs-utils.cpp

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -67,9 +67,10 @@ bool getRGBAFromStageSurface(filter_data *tf, uint32_t &width, uint32_t &height)
6767
std::lock_guard<std::mutex> lock(tf->inputBGRALock);
6868
// Create a temporary Mat that wraps the video_data pointer
6969
cv::Mat temp(height, width, CV_8UC4, video_data, linesize);
70-
// Clone the data to ensure tf->inputBGRA has its own copy
71-
// This prevents use-after-unmap race condition
72-
tf->inputBGRA = temp.clone();
70+
// Copy frame data into tf->inputBGRA, reusing its allocation
71+
// when dimensions match. Ensures we own the pixels before unmap.
72+
temp.copyTo(tf->inputBGRA);
73+
tf->newFrameAvailable = true;
7374
}
7475
gs_stagesurface_unmap(tf->stagesurface);
7576
return true;

0 commit comments

Comments
 (0)