From: Steinar H. Gunderson Date: Mon, 23 Nov 2015 00:44:27 +0000 (+0100) Subject: Redo frame buffering again. X-Git-Tag: 1.0.0~84 X-Git-Url: https://git.sesse.net/?p=nageru;a=commitdiff_plain;h=196392c483a9cbf7fd7b572d9242aa283031453f Redo frame buffering again. This partially reverts 86f32b5a in that it removes the exact counting; however, the system wasn't safe at all (threading, for instance, but more generally, that the entire state could change between display and get chain). Put all the state within the chain setup instead. --- diff --git a/input_state.h b/input_state.h new file mode 100644 index 0000000..26015ef --- /dev/null +++ b/input_state.h @@ -0,0 +1,24 @@ +#ifndef _INPUT_STATE_H +#define _INPUT_STATE_H 1 + +#include "defs.h" +#include "ref_counted_frame.h" + +struct BufferedFrame { + RefCountedFrame frame; + unsigned field_number; +}; + +// Encapsulates the state of all inputs at any given instant. +// In particular, this is captured by Theme::get_chain(), +// so that it can hold on to all the frames it needs for rendering. +struct InputState { + // For each card, the last three frames (or fields), with 0 being the + // most recent one. Note that we only need the actual history if we have + // interlaced output (for deinterlacing), so if we detect progressive input, + // we immediately clear out all history and all entries will point to the same + // frame. + BufferedFrame buffered_frames[MAX_CARDS][FRAME_HISTORY_LENGTH]; +}; + +#endif // !defined(_INPUT_STATE_H) diff --git a/mixer.cpp b/mixer.cpp index 04a3553..b6824d5 100644 --- a/mixer.cpp +++ b/mixer.cpp @@ -59,6 +59,22 @@ void convert_fixed24_to_fp32(float *dst, size_t out_channels, const uint8_t *src } } +void insert_new_frame(RefCountedFrame frame, unsigned field_num, bool interlaced, unsigned card_index, InputState *input_state) +{ + if (interlaced) { + for (unsigned frame_num = FRAME_HISTORY_LENGTH; frame_num --> 1; ) { // :-) + input_state->buffered_frames[card_index][frame_num] = + input_state->buffered_frames[card_index][frame_num - 1]; + } + input_state->buffered_frames[card_index][0] = { frame, field_num }; + } else { + for (unsigned frame_num = 0; frame_num < FRAME_HISTORY_LENGTH; ++frame_num) { + input_state->buffered_frames[card_index][frame_num] = { frame, field_num }; + } + } +} + + } // namespace Mixer::Mixer(const QSurfaceFormat &format, unsigned num_cards) @@ -528,16 +544,7 @@ void Mixer::thread_func() continue; assert(card->new_frame != nullptr); - if (card->new_frame_interlaced) { - for (unsigned frame_num = FRAME_HISTORY_LENGTH; frame_num --> 1; ) { // :-) - buffered_frames[card_index][frame_num] = buffered_frames[card_index][frame_num - 1]; - } - buffered_frames[card_index][0] = { card->new_frame, card->new_frame_field }; - } else { - for (unsigned frame_num = 0; frame_num < FRAME_HISTORY_LENGTH; ++frame_num) { - buffered_frames[card_index][frame_num] = { card->new_frame, card->new_frame_field }; - } - } + insert_new_frame(card->new_frame, card->new_frame_field, card->new_frame_interlaced, card_index, &input_state); check_error(); // The new texture might still be uploaded, @@ -551,7 +558,7 @@ void Mixer::thread_func() } // Get the main chain from the theme, and set its state immediately. - Theme::Chain theme_main_chain = theme->get_chain(0, pts(), WIDTH, HEIGHT); + Theme::Chain theme_main_chain = theme->get_chain(0, pts(), WIDTH, HEIGHT, input_state); EffectChain *chain = theme_main_chain.chain; theme_main_chain.setup_chain(); @@ -600,7 +607,7 @@ void Mixer::thread_func() // Set up preview and any additional channels. for (int i = 1; i < theme->get_num_channels() + 2; ++i) { DisplayFrame display_frame; - Theme::Chain chain = theme->get_chain(i, pts(), WIDTH, HEIGHT); // FIXME: dimensions + Theme::Chain chain = theme->get_chain(i, pts(), WIDTH, HEIGHT, input_state); // FIXME: dimensions display_frame.chain = chain.chain; display_frame.setup_chain = chain.setup_chain; display_frame.ready_fence = fence; diff --git a/mixer.h b/mixer.h index 07a31e9..6d0cbce 100644 --- a/mixer.h +++ b/mixer.h @@ -34,6 +34,7 @@ #include "timebase.h" #include "stereocompressor.h" #include "filter.h" +#include "input_state.h" class H264Encoder; class QSurface; @@ -170,16 +171,6 @@ public: void reset_meters(); - struct BufferedFrame { - RefCountedFrame frame; - unsigned field_number; - }; - - BufferedFrame get_buffered_frame(int card, int history_pos) - { - return buffered_frames[card][history_pos]; - } - private: void bm_frame(unsigned card_index, uint16_t timecode, FrameAllocator::Frame video_frame, size_t video_offset, uint16_t video_format, @@ -237,12 +228,7 @@ private: }; CaptureCard cards[MAX_CARDS]; // protected by - // For each card, the last three frames (or fields), with 0 being the - // most recent one. Note that we only need the actual history if we have - // interlaced output (for deinterlacing), so if we detect progressive input, - // we immediately clear out all history and all entries will point to the same - // frame. - BufferedFrame buffered_frames[MAX_CARDS][FRAME_HISTORY_LENGTH]; + InputState input_state; class OutputChannel { public: diff --git a/theme.cpp b/theme.cpp index cfb9527..62e3640 100644 --- a/theme.cpp +++ b/theme.cpp @@ -448,19 +448,13 @@ void LiveInputWrapper::connect_signal(int signal_num) signal_num = theme->map_signal(signal_num); - Mixer::BufferedFrame frame = global_mixer->get_buffered_frame(signal_num, 0); + BufferedFrame frame = theme->input_state->buffered_frames[signal_num][0]; const PBOFrameAllocator::Userdata *userdata = (const PBOFrameAllocator::Userdata *)frame.frame->userdata; input->set_texture_num(0, userdata->tex_y[frame.field_number]); input->set_texture_num(1, userdata->tex_cbcr[frame.field_number]); input->set_width(userdata->last_width[frame.field_number]); input->set_height(userdata->last_height[frame.field_number]); - - // Hold on to the refcount so that we don't release the input frame - // until we're done rendering from it. - if (theme->used_input_frames_collector != nullptr) { - theme->used_input_frames_collector->push_back(frame.frame); - } } Theme::Theme(const char *filename, ResourcePool *resource_pool, unsigned num_cards) @@ -514,7 +508,7 @@ void Theme::register_class(const char *class_name, const luaL_Reg *funcs) assert(lua_gettop(L) == 0); } -Theme::Chain Theme::get_chain(unsigned num, float t, unsigned width, unsigned height) +Theme::Chain Theme::get_chain(unsigned num, float t, unsigned width, unsigned height, InputState input_state) { Chain chain; @@ -526,12 +520,10 @@ Theme::Chain Theme::get_chain(unsigned num, float t, unsigned width, unsigned he lua_pushnumber(L, width); lua_pushnumber(L, height); - this->used_input_frames_collector = &chain.input_frames; if (lua_pcall(L, 4, 2, 0) != 0) { fprintf(stderr, "error running function `get_chain': %s\n", lua_tostring(L, -1)); exit(1); } - this->used_input_frames_collector = nullptr; chain.chain = (EffectChain *)luaL_checkudata(L, -2, "EffectChain"); if (!lua_isfunction(L, -1)) { @@ -543,9 +535,11 @@ Theme::Chain Theme::get_chain(unsigned num, float t, unsigned width, unsigned he lua_pop(L, 2); assert(lua_gettop(L) == 0); - chain.setup_chain = [this, funcref]{ + chain.setup_chain = [this, funcref, input_state]{ unique_lock lock(m); + this->input_state = &input_state; + // Set up state, including connecting signals. lua_rawgeti(L, LUA_REGISTRYINDEX, funcref->get()); if (lua_pcall(L, 0, 0, 0) != 0) { @@ -555,6 +549,14 @@ Theme::Chain Theme::get_chain(unsigned num, float t, unsigned width, unsigned he assert(lua_gettop(L) == 0); }; + // TODO: Can we do better, e.g. by running setup_chain() and seeing what it references? + // Actually, setup_chain does maybe hold all the references we need now anyway? + for (unsigned card_index = 0; card_index < num_cards; ++card_index) { + for (unsigned frame_num = 0; frame_num < FRAME_HISTORY_LENGTH; ++frame_num) { + chain.input_frames.push_back(input_state.buffered_frames[card_index][frame_num].frame); + } + } + return chain; } diff --git a/theme.h b/theme.h index 2b3b4ab..223968a 100644 --- a/theme.h +++ b/theme.h @@ -16,6 +16,7 @@ #include #include "defs.h" +#include "input_state.h" #include "ref_counted_frame.h" namespace movit { @@ -47,7 +48,7 @@ public: std::vector input_frames; }; - Chain get_chain(unsigned num, float t, unsigned width, unsigned height); + Chain get_chain(unsigned num, float t, unsigned width, unsigned height, InputState input_state); int get_num_channels() const { return num_channels; } int map_signal(int signal_num); @@ -65,15 +66,13 @@ private: void register_class(const char *class_name, const luaL_Reg *funcs); std::mutex m; - lua_State *L; + lua_State *L; // Protected by . + const InputState *input_state; // Protected by . Only set temporarily, during chain setup. movit::ResourcePool *resource_pool; int num_channels; unsigned num_cards; std::set signals_warned_about; - // All input frames needed for the current chain. Filled during call to get_chain(), - // as inputs get connected. - std::vector *used_input_frames_collector; friend class LiveInputWrapper; };