summaryrefslogtreecommitdiffstats
path: root/video/out/vo.c
diff options
context:
space:
mode:
authorwm4 <wm4@nowhere>2017-07-23 09:41:51 +0200
committerwm4 <wm4@nowhere>2017-07-24 04:32:55 +0200
commit64d56114ed9258efe2e864315d7130bb58a03d52 (patch)
treef4300ffb5c3d912f958b93acba86b0ed438ddcf7 /video/out/vo.c
parent9e7665b21b530cbbfeb187521dc9db78c2ca60db (diff)
downloadmpv-64d56114ed9258efe2e864315d7130bb58a03d52.tar.bz2
mpv-64d56114ed9258efe2e864315d7130bb58a03d52.tar.xz
vo_opengl: add direct rendering support
Can be enabled via --vd-lavc-dr=yes. See manpage additions for what it does. This reminds of the MPlayer -dr flag, but the implementation is completely different. It's the same basic concept: letting the decoder render into a GPU buffer to avoid a copy. Unlike MPlayer, this doesn't try to go through filters (libavfilter doesn't support this anyway). Unless a filter can work in-place, DR will be silently disabled. MPlayer had very complex semantics about buffer types and management (which apparently nobody ever understood) and weird restrictions that mostly limited it to mpeg2 style codecs. The mpv code does not do any of this, and just lets the decoder allocate an arbitrary number of untyped images. (No MPlayer code was used.) Parts of the code based on work by atomnuker (starting point for the generic code) and haasn (some GL definitions, some basic PBO code, and correct fencing).
Diffstat (limited to 'video/out/vo.c')
-rw-r--r--video/out/vo.c91
1 files changed, 91 insertions, 0 deletions
diff --git a/video/out/vo.c b/video/out/vo.c
index 79fc4f3bb4..e52495e195 100644
--- a/video/out/vo.c
+++ b/video/out/vo.c
@@ -23,9 +23,12 @@
#include <pthread.h>
#include <math.h>
+#include <libavutil/buffer.h>
+
#include "mpv_talloc.h"
#include "config.h"
+#include "osdep/atomic.h"
#include "osdep/timer.h"
#include "osdep/threads.h"
#include "misc/dispatch.h"
@@ -113,6 +116,8 @@ struct vo_internal {
pthread_t thread;
struct mp_dispatch_queue *dispatch;
+ atomic_ullong dr_in_flight;
+
// --- The following fields are protected by lock
pthread_mutex_t lock;
pthread_cond_t wakeup;
@@ -955,6 +960,7 @@ static void *vo_thread(void *ptr)
talloc_free(in->current_frame);
in->current_frame = NULL;
vo->driver->uninit(vo);
+ assert(atomic_load(&vo->in->dr_in_flight) == 0);
return NULL;
}
@@ -1258,3 +1264,88 @@ int lookup_keymap_table(const struct mp_keymap *map, int key)
map++;
return map->to;
}
+
+struct free_dr_context {
+ struct vo *vo;
+ AVBufferRef *ref;
+};
+
+static void vo_thread_free(void *ptr)
+{
+ struct free_dr_context *ctx = ptr;
+
+ unsigned long long v = atomic_fetch_add(&ctx->vo->in->dr_in_flight, -1);
+ assert(v); // value before sub is 0 - unexpected underflow.
+
+ av_buffer_unref(&ctx->ref);
+ talloc_free(ctx);
+}
+
+static void free_dr_buffer_on_vo_thread(void *opaque, uint8_t *data)
+{
+ struct free_dr_context *ctx = opaque;
+
+ // The image could be unreffed even on the VO thread. In practice, this
+ // matters most on VO destruction.
+ if (pthread_equal(ctx->vo->in->thread, pthread_self())) {
+ vo_thread_free(ctx);
+ } else {
+ mp_dispatch_run(ctx->vo->in->dispatch, vo_thread_free, ctx);
+ }
+}
+
+struct get_image_cmd {
+ struct vo *vo;
+ int imgfmt, w, h, stride_align;
+ struct mp_image *res;
+};
+
+static void sync_get_image(void *ptr)
+{
+ struct get_image_cmd *cmd = ptr;
+ struct vo *vo = cmd->vo;
+
+ cmd->res = vo->driver->get_image(vo, cmd->imgfmt, cmd->w, cmd->h,
+ cmd->stride_align);
+ if (!cmd->res)
+ return;
+
+ // We require exactly 1 AVBufferRef.
+ assert(cmd->res->bufs[0]);
+ assert(!cmd->res->bufs[1]);
+
+ // Apply some magic to get it free'd on the VO thread as well. For this to
+ // work, we create a dummy-ref that aliases the original ref, which is why
+ // the original ref must be writable in the first place. (A newly allocated
+ // image should be always writable of course.)
+ assert(mp_image_is_writeable(cmd->res));
+
+ struct free_dr_context *ctx = talloc_zero(NULL, struct free_dr_context);
+ *ctx = (struct free_dr_context){
+ .vo = vo,
+ .ref = cmd->res->bufs[0],
+ };
+
+ AVBufferRef *new_ref = av_buffer_create(ctx->ref->data, ctx->ref->size,
+ free_dr_buffer_on_vo_thread, ctx, 0);
+ if (!new_ref)
+ abort(); // tiny malloc OOM
+
+ cmd->res->bufs[0] = new_ref;
+
+ atomic_fetch_add(&vo->in->dr_in_flight, 1);
+}
+
+struct mp_image *vo_get_image(struct vo *vo, int imgfmt, int w, int h,
+ int stride_align)
+{
+ if (!vo->driver->get_image)
+ return NULL;
+
+ struct get_image_cmd cmd = {
+ .vo = vo,
+ .imgfmt = imgfmt, .w = w, .h = h, .stride_align = stride_align,
+ };
+ mp_dispatch_run(vo->in->dispatch, sync_get_image, &cmd);
+ return cmd.res;
+}