Files
plezy/linux/runner/mpv/plane_render_executor.h
T

87 lines
3.3 KiB
C++
Raw Normal View History

#ifndef PLEZY_LINUX_MPV_PLANE_RENDER_EXECUTOR_H_
#define PLEZY_LINUX_MPV_PLANE_RENDER_EXECUTOR_H_
#include <glib.h>
#include <condition_variable>
#include <deque>
#include <functional>
#include <memory>
#include <mutex>
#include <thread>
#include <utility>
namespace mpv {
// The video plane's render worker (issue #2057).
//
// mpv's render + the plane's eglSwapBuffers used to run on the GTK main
// thread, which also rasters Flutter's UI and dispatches input. A cheap frame
// hides that; a 4K HDR tone-map does not - input reads batch at video-render
// boundaries and every UI repaint waits out the render. This worker exists to
// take exactly that GL work off the main thread. Everything else about the
// plane - Wayland protocol state, watchdogs, colour transitions, geometry -
// deliberately stays on the main thread; see WaylandVideoSurface.
//
// Contract:
// - Jobs run in order on one worker thread and return a bool.
// - Completions run on the GLib main context that was thread-default when
// the executor was constructed, carrying the job's result. They are
// delivered even after ShutdownAndJoin, so they must guard against state
// that has since been torn down (the plugin's generation counter).
// - ShutdownAndJoin drains queued jobs first. A job wedged inside a driver
// call cannot be interrupted; past the timeout the thread is abandoned
// (detached) and false is returned so the caller can leak, rather than
// free, what the job may still touch. The worker owns its state through a
// shared_ptr, never through the executor object, so abandonment leaks that
// state instead of leaving the thread on freed memory.
class PlaneRenderExecutor {
public:
// Runs on the worker thread; the result is handed to the completion.
using Job = std::function<bool()>;
// Runs on the construction-time GLib main context.
using Completion = std::function<void(bool)>;
PlaneRenderExecutor();
~PlaneRenderExecutor();
PlaneRenderExecutor(const PlaneRenderExecutor&) = delete;
PlaneRenderExecutor& operator=(const PlaneRenderExecutor&) = delete;
/// Queues |job|. Returns false only after shutdown has begun, in which case
/// neither the job nor the completion will run.
bool Post(Job job, Completion completion);
/// Stops accepting jobs, waits up to |timeout_ms| for queued jobs to drain,
/// and joins the worker. Returns false when the worker had to be abandoned
/// instead - see the class comment. Idempotent.
bool ShutdownAndJoin(unsigned int timeout_ms);
private:
// Everything the worker touches. Held by shared_ptr from both the executor
// and the worker thread's closure, so an abandoned worker still stands on
// live memory. The completion context reference is owned here and released
// by the destructor - i.e. by whichever side lets go last.
struct Shared {
~Shared();
GMainContext* completion_context = nullptr;
std::mutex mutex;
std::condition_variable wake;
std::condition_variable idle;
std::deque<std::pair<Job, Completion>> jobs;
bool running_job = false;
bool quitting = false;
};
static void Run(const std::shared_ptr<Shared>& shared);
std::shared_ptr<Shared> shared_;
std::thread thread_;
bool abandoned_ = false;
};
} // namespace mpv
#endif // PLEZY_LINUX_MPV_PLANE_RENDER_EXECUTOR_H_