]> git.sesse.net Git - nageru/commitdiff
Move to one JPEG decoder thread per view.
authorSteinar H. Gunderson <sgunderson@bigfoot.com>
Sun, 10 Mar 2019 22:29:24 +0000 (23:29 +0100)
committerSteinar H. Gunderson <sgunderson@bigfoot.com>
Sun, 10 Mar 2019 22:29:24 +0000 (23:29 +0100)
This is, surprisingly, the most useful for VA-API decodes; they can
have long latency at 1080p, and Futatabi's dropping scheme sometimes
caused massive unfairness. Our system doesn't pipeline all that
nicely, so just having multiple threads was the simplest solution.
The risk is that we now access VA-API from multiple threads, which
has a tendency to tickle bugs, but we'll see.

Of course, for CPU decoding, you will also benefit.

futatabi/jpeg_frame_view.cpp
futatabi/jpeg_frame_view.h
futatabi/main.cpp

index c9b8090544b8313f7cf769812580fa4e11e0fd39..d35964ce4c0709861d68b5cbfdcee18e2c9e1643 100644 (file)
@@ -59,19 +59,6 @@ struct LRUFrame {
        size_t last_used;
 };
 
-struct PendingDecode {
-       JPEGFrameView *destination;
-
-       // For actual decodes (only if frame below is nullptr).
-       FrameOnDisk primary, secondary;
-       float fade_alpha;  // Irrelevant if secondary.stream_idx == -1.
-
-       // Already-decoded frames are also sent through PendingDecode,
-       // so that they get drawn in the right order. If frame is nullptr,
-       // it's a real decode.
-       shared_ptr<Frame> frame;
-};
-
 // There can be multiple JPEGFrameView instances, so make all the metrics static.
 once_flag jpeg_metrics_inited;
 atomic<int64_t> metric_jpeg_cache_used_bytes{ 0 };  // Same value as cache_bytes_used.
@@ -86,12 +73,9 @@ atomic<int64_t> metric_jpeg_vaapi_fail_frames{ 0 };
 
 }  // namespace
 
-thread JPEGFrameView::jpeg_decoder_thread;
 mutex cache_mu;
 map<FrameOnDisk, LRUFrame, FrameOnDiskLexicalOrder> cache;  // Under cache_mu.
 size_t cache_bytes_used = 0;  // Under cache_mu.
-condition_variable any_pending_decodes;
-deque<PendingDecode> pending_decodes;  // Under cache_mu.
 atomic<size_t> event_counter{ 0 };
 extern QGLWidget *global_share_widget;
 extern atomic<bool> should_quit;
@@ -277,7 +261,7 @@ void JPEGFrameView::jpeg_decoder_thread_func()
                CacheMissBehavior cache_miss_behavior = DECODE_IF_NOT_IN_CACHE;
                {
                        unique_lock<mutex> lock(cache_mu);  // TODO: Perhaps under another lock?
-                       any_pending_decodes.wait(lock, [] {
+                       any_pending_decodes.wait(lock, [this] {
                                return !pending_decodes.empty() || should_quit.load();
                        });
                        if (should_quit.load())
@@ -285,20 +269,14 @@ void JPEGFrameView::jpeg_decoder_thread_func()
                        decode = pending_decodes.front();
                        pending_decodes.pop_front();
 
-                       size_t num_pending = 0;
-                       for (const PendingDecode &other_decode : pending_decodes) {
-                               if (other_decode.destination == decode.destination) {
-                                       ++num_pending;
-                               }
-                       }
-                       if (num_pending > 3) {
+                       if (pending_decodes.size() > 3) {
                                cache_miss_behavior = RETURN_NULLPTR_IF_NOT_IN_CACHE;
                        }
                }
 
                if (decode.frame != nullptr) {
                        // Already decoded, so just show it.
-                       decode.destination->setDecodedFrame(decode.frame, nullptr, 1.0f);
+                       setDecodedFrame(decode.frame, nullptr, 1.0f);
                        continue;
                }
 
@@ -312,7 +290,7 @@ void JPEGFrameView::jpeg_decoder_thread_func()
                        }
 
                        bool found_in_cache;
-                       shared_ptr<Frame> frame = decode_jpeg_with_cache(frame_spec, cache_miss_behavior, &decode.destination->frame_reader, &found_in_cache);
+                       shared_ptr<Frame> frame = decode_jpeg_with_cache(frame_spec, cache_miss_behavior, &frame_reader, &found_in_cache);
 
                        if (frame == nullptr) {
                                assert(cache_miss_behavior == RETURN_NULLPTR_IF_NOT_IN_CACHE);
@@ -339,11 +317,11 @@ void JPEGFrameView::jpeg_decoder_thread_func()
                }
 
                // TODO: Could we get jitter between non-interpolated and interpolated frames here?
-               decode.destination->setDecodedFrame(primary_frame, secondary_frame, decode.fade_alpha);
+               setDecodedFrame(primary_frame, secondary_frame, decode.fade_alpha);
        }
 }
 
-void JPEGFrameView::shutdown()
+JPEGFrameView::~JPEGFrameView()
 {
        any_pending_decodes.notify_all();
        jpeg_decoder_thread.join();
@@ -374,7 +352,6 @@ void JPEGFrameView::setFrame(unsigned stream_idx, FrameOnDisk frame, FrameOnDisk
        decode.primary = frame;
        decode.secondary = secondary_frame;
        decode.fade_alpha = fade_alpha;
-       decode.destination = this;
        pending_decodes.push_back(decode);
        any_pending_decodes.notify_all();
 }
@@ -384,24 +361,18 @@ void JPEGFrameView::setFrame(shared_ptr<Frame> frame)
        lock_guard<mutex> lock(cache_mu);
        PendingDecode decode;
        decode.frame = std::move(frame);
-       decode.destination = this;
        pending_decodes.push_back(decode);
        any_pending_decodes.notify_all();
 }
 
-ResourcePool *resource_pool = nullptr;
-
 void JPEGFrameView::initializeGL()
 {
        glDisable(GL_BLEND);
        glDisable(GL_DEPTH_TEST);
        check_error();
 
-       static once_flag once;
-       call_once(once, [] {
-               resource_pool = new ResourcePool;
-               jpeg_decoder_thread = std::thread(jpeg_decoder_thread_func);
-       });
+       resource_pool = new ResourcePool;
+       jpeg_decoder_thread = std::thread(&JPEGFrameView::jpeg_decoder_thread_func, this);
 
        ycbcr_converter.reset(new YCbCrConverter(YCbCrConverter::OUTPUT_TO_RGBA, resource_pool));
 
index 9af7d2cb7e05f2b30eac4bc9c1841e6239805cd0..b66e2656d437cedc7d6407c45f5f11afe59ff5bf 100644 (file)
@@ -13,6 +13,8 @@
 #include <movit/mix_effect.h>
 #include <movit/ycbcr_input.h>
 #include <stdint.h>
+#include <condition_variable>
+#include <deque>
 #include <thread>
 
 enum CacheMissBehavior {
@@ -29,6 +31,7 @@ class JPEGFrameView : public QGLWidget {
 
 public:
        JPEGFrameView(QWidget *parent);
+       ~JPEGFrameView();
 
        void setFrame(unsigned stream_idx, FrameOnDisk frame, FrameOnDisk secondary_frame = {}, float fade_alpha = 0.0f);
        void setFrame(std::shared_ptr<Frame> frame);
@@ -40,8 +43,6 @@ public:
        void setDecodedFrame(std::shared_ptr<Frame> frame, std::shared_ptr<Frame> secondary_frame, float fade_alpha);
        void set_overlay(const std::string &text);  // Blank for none.
 
-       static void shutdown();
-
 signals:
        void clicked();
 
@@ -51,7 +52,7 @@ protected:
        void paintGL() override;
 
 private:
-       static void jpeg_decoder_thread_func();
+       void jpeg_decoder_thread_func();
 
        FrameReader frame_reader;
 
@@ -73,7 +74,22 @@ private:
 
        int gl_width, gl_height;
 
-       static std::thread jpeg_decoder_thread;
+       std::thread jpeg_decoder_thread;
+       movit::ResourcePool *resource_pool = nullptr;
+
+       struct PendingDecode {
+               // For actual decodes (only if frame below is nullptr).
+               FrameOnDisk primary, secondary;
+               float fade_alpha;  // Irrelevant if secondary.stream_idx == -1.
+
+               // Already-decoded frames are also sent through PendingDecode,
+               // so that they get drawn in the right order. If frame is nullptr,
+               // it's a real decode.
+               std::shared_ptr<Frame> frame;
+       };
+
+       std::condition_variable any_pending_decodes;
+       std::deque<PendingDecode> pending_decodes;  // Under cache_mu.
 };
 
 #endif  // !defined(_JPEG_FRAME_VIEW_H)
index 7c5f660ae5b97f928aed6b9c8daabe18a5744cc4..a3a550e1afb6f1ce35562b3acfa921130baadae1 100644 (file)
@@ -275,7 +275,6 @@ int main(int argc, char **argv)
 
        should_quit = true;
        record_thread.join();
-       JPEGFrameView::shutdown();
 
        return ret;
 }