diff options
-rw-r--r-- | video/out/opengl/context_drm_egl.c | 209 |
1 files changed, 150 insertions, 59 deletions
diff --git a/video/out/opengl/context_drm_egl.c b/video/out/opengl/context_drm_egl.c index 6aa3d95e79..7e14155d28 100644 --- a/video/out/opengl/context_drm_egl.c +++ b/video/out/opengl/context_drm_egl.c @@ -50,8 +50,8 @@ struct gbm { struct gbm_surface *surface; struct gbm_device *device; - struct gbm_bo *bo; - struct gbm_bo *next_bo; + struct gbm_bo **bo; + unsigned int num_bos; }; struct egl @@ -72,6 +72,9 @@ struct priv { struct gbm gbm; struct framebuffer *fb; + GLsync *vsync_fences; + unsigned int num_vsync_fences; + uint32_t gbm_format; bool active; @@ -80,6 +83,9 @@ struct priv { bool vt_switcher_active; struct vt_switcher vt_switcher; + bool still; + bool paused; + struct mpv_opengl_drm_params drm_params; struct mpv_opengl_drm_draw_surface_size draw_surface_size; }; @@ -355,15 +361,6 @@ static void crtc_release(struct ra_ctx *ctx) return; p->active = false; - // wait for current page flip - while (p->waiting_for_flip) { - int ret = drmHandleEvent(p->kms->fd, &p->ev); - if (ret) { - MP_ERR(ctx->vo, "drmHandleEvent failed: %i\n", ret); - break; - } - } - if (p->kms->atomic_context) { if (p->kms->atomic_context->old_state.saved) { if (!crtc_release_atomic(ctx)) @@ -414,44 +411,19 @@ static void acquire_vt(void *data) crtc_setup(ctx); } -static bool drm_atomic_egl_start_frame(struct ra_swapchain *sw, struct ra_fbo *out_fbo) -{ - struct priv *p = sw->ctx->priv; - if (p->kms->atomic_context) { - if (!p->kms->atomic_context->request) { - p->kms->atomic_context->request = drmModeAtomicAlloc(); - p->drm_params.atomic_request_ptr = &p->kms->atomic_context->request; - } - return ra_gl_ctx_start_frame(sw, out_fbo); - } - return false; -} - -static const struct ra_swapchain_fns drm_atomic_swapchain = { - .start_frame = drm_atomic_egl_start_frame, -}; - -static void drm_egl_swap_buffers(struct ra_ctx *ctx) +static void queue_flip(struct ra_ctx *ctx) { struct priv *p = ctx->priv; struct drm_atomic_context *atomic_ctx = p->kms->atomic_context; int ret; - if (!p->active) - return; - - eglSwapBuffers(p->egl.display, p->egl.surface); - p->gbm.next_bo = gbm_surface_lock_front_buffer(p->gbm.surface); - p->waiting_for_flip = true; - update_framebuffer_from_bo(ctx, p->gbm.next_bo); - if (atomic_ctx) { drm_object_set_property(atomic_ctx->request, atomic_ctx->draw_plane, "FB_ID", p->fb->id); drm_object_set_property(atomic_ctx->request, atomic_ctx->draw_plane, "CRTC_ID", atomic_ctx->crtc->id); drm_object_set_property(atomic_ctx->request, atomic_ctx->draw_plane, "ZPOS", 1); ret = drmModeAtomicCommit(p->kms->fd, atomic_ctx->request, - DRM_MODE_ATOMIC_NONBLOCK | DRM_MODE_PAGE_FLIP_EVENT, NULL); + DRM_MODE_ATOMIC_NONBLOCK | DRM_MODE_PAGE_FLIP_EVENT, p); if (ret) MP_WARN(ctx->vo, "Failed to commit atomic request (%d)\n", ret); } else { @@ -461,30 +433,129 @@ static void drm_egl_swap_buffers(struct ra_ctx *ctx) MP_WARN(ctx->vo, "Failed to queue page flip: %s\n", mp_strerror(errno)); } } + p->waiting_for_flip = true; + + if (atomic_ctx) { + drmModeAtomicFree(atomic_ctx->request); + atomic_ctx->request = drmModeAtomicAlloc(); + } +} + +static void wait_on_flip(struct ra_ctx *ctx) +{ + struct priv *p = ctx->priv; // poll page flip finish event - const int timeout_ms = 3000; - struct pollfd fds[1] = { { .events = POLLIN, .fd = p->kms->fd } }; - poll(fds, 1, timeout_ms); - if (fds[0].revents & POLLIN) { - ret = drmHandleEvent(p->kms->fd, &p->ev); - if (ret != 0) { - MP_ERR(ctx->vo, "drmHandleEvent failed: %i\n", ret); - p->waiting_for_flip = false; - return; + while (p->waiting_for_flip) { + const int timeout_ms = 3000; + struct pollfd fds[1] = { { .events = POLLIN, .fd = p->kms->fd } }; + poll(fds, 1, timeout_ms); + if (fds[0].revents & POLLIN) { + const int ret = drmHandleEvent(p->kms->fd, &p->ev); + if (ret != 0) + MP_ERR(ctx->vo, "drmHandleEvent failed: %i\n", ret); } } - p->waiting_for_flip = false; +} - if (atomic_ctx) { - drmModeAtomicFree(atomic_ctx->request); - atomic_ctx->request = drmModeAtomicAlloc(); +static void swapchain_step(struct ra_ctx *ctx) +{ + struct priv *p = ctx->priv; + + if (p->gbm.num_bos > 0 && p->gbm.bo[0]) + gbm_surface_release_buffer(p->gbm.surface, p->gbm.bo[0]); + MP_TARRAY_REMOVE_AT(p->gbm.bo, p->gbm.num_bos, 0); +} + +static void new_fence(struct ra_ctx *ctx) +{ + struct priv *p = ctx->priv; + + if (p->gl.FenceSync) { + GLsync fence = p->gl.FenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); + if (fence) + MP_TARRAY_APPEND(p, p->vsync_fences, p->num_vsync_fences, fence); } +} + +static void wait_fence(struct ra_ctx *ctx) +{ + struct priv *p = ctx->priv; + + while (p->num_vsync_fences && (p->num_vsync_fences >= p->gbm.num_bos)) { + p->gl.ClientWaitSync(p->vsync_fences[0], GL_SYNC_FLUSH_COMMANDS_BIT, 1e9); + p->gl.DeleteSync(p->vsync_fences[0]); + MP_TARRAY_REMOVE_AT(p->vsync_fences, p->num_vsync_fences, 0); + } +} + +static bool drm_egl_start_frame(struct ra_swapchain *sw, struct ra_fbo *out_fbo) +{ + struct ra_ctx *ctx = sw->ctx; + struct priv *p = ctx->priv; + + if (p->kms->atomic_context && !p->kms->atomic_context->request) { + p->kms->atomic_context->request = drmModeAtomicAlloc(); + p->drm_params.atomic_request_ptr = &p->kms->atomic_context->request; + } + + return ra_gl_ctx_start_frame(sw, out_fbo); +} + +static bool drm_egl_submit_frame(struct ra_swapchain *sw, const struct vo_frame *frame) +{ + struct ra_ctx *ctx = sw->ctx; + struct priv *p = ctx->priv; - gbm_surface_release_buffer(p->gbm.surface, p->gbm.bo); - p->gbm.bo = p->gbm.next_bo; + p->still = frame->still; + + return ra_gl_ctx_submit_frame(sw, frame); } +static void drm_egl_swap_buffers(struct ra_swapchain *sw) +{ + struct ra_ctx *ctx = sw->ctx; + struct priv *p = ctx->priv; + const bool drain = p->paused || p->still; // True when we need to drain the swapchain + + if (!p->active) + return; + + wait_fence(ctx); + + eglSwapBuffers(p->egl.display, p->egl.surface); + + struct gbm_bo *new_bo = gbm_surface_lock_front_buffer(p->gbm.surface); + if (!new_bo) { + MP_ERR(ctx->vo, "Couldn't lock front buffer\n"); + return; + } + MP_TARRAY_APPEND(p, p->gbm.bo, p->gbm.num_bos, new_bo); + new_fence(ctx); + + while (drain || p->gbm.num_bos > ctx->opts.swapchain_depth || !gbm_surface_has_free_buffers(p->gbm.surface)) { + if (p->waiting_for_flip) { + wait_on_flip(ctx); + swapchain_step(ctx); + } + if (p->gbm.num_bos <= 1) + break; + if (!p->gbm.bo[1]) { + MP_ERR(ctx->vo, "Hole in swapchain?\n"); + swapchain_step(ctx); + continue; + } + update_framebuffer_from_bo(ctx, p->gbm.bo[1]); + queue_flip(ctx); + } +} + +static const struct ra_swapchain_fns drm_egl_swapchain = { + .start_frame = drm_egl_start_frame, + .submit_frame = drm_egl_submit_frame, + .swap_buffers = drm_egl_swap_buffers, +}; + static void drm_egl_uninit(struct ra_ctx *ctx) { struct priv *p = ctx->priv; @@ -503,6 +574,12 @@ static void drm_egl_uninit(struct ra_ctx *ctx) if (p->vt_switcher_active) vt_switcher_destroy(&p->vt_switcher); + // According to GBM documentation all BO:s must be released before + // gbm_surface_destroy can be called on the surface. + while (p->gbm.num_bos) { + swapchain_step(ctx); + } + eglMakeCurrent(p->egl.display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); eglDestroyContext(p->egl.display, p->egl.context); @@ -565,6 +642,13 @@ static bool probe_gbm_format(struct ra_ctx *ctx, uint32_t argb_format, uint32_t return result; } +static void page_flipped(int fd, unsigned int frame, unsigned int sec, + unsigned int usec, void *data) +{ + struct priv *p = data; + p->waiting_for_flip = false; +} + static bool drm_egl_init(struct ra_ctx *ctx) { if (ctx->opts.probing) { @@ -574,6 +658,7 @@ static bool drm_egl_init(struct ra_ctx *ctx) struct priv *p = ctx->priv = talloc_zero(ctx, struct priv); p->ev.version = DRM_EVENT_CONTEXT_VERSION; + p->ev.page_flip_handler = page_flipped; p->vt_switcher_active = vt_switcher_init(&p->vt_switcher, ctx->vo->log); if (p->vt_switcher_active) { @@ -644,12 +729,13 @@ static bool drm_egl_init(struct ra_ctx *ctx) eglSwapBuffers(p->egl.display, p->egl.surface); MP_VERBOSE(ctx, "Preparing framebuffer\n"); - p->gbm.bo = gbm_surface_lock_front_buffer(p->gbm.surface); - if (!p->gbm.bo) { + struct gbm_bo *new_bo = gbm_surface_lock_front_buffer(p->gbm.surface); + if (!new_bo) { MP_ERR(ctx, "Failed to lock GBM surface.\n"); return false; } - update_framebuffer_from_bo(ctx, p->gbm.bo); + MP_TARRAY_APPEND(p, p->gbm.bo, p->gbm.num_bos, new_bo); + update_framebuffer_from_bo(ctx, new_bo); if (!p->fb || !p->fb->id) { MP_ERR(ctx, "Failed to create framebuffer.\n"); return false; @@ -681,9 +767,7 @@ static bool drm_egl_init(struct ra_ctx *ctx) } struct ra_gl_ctx_params params = { - .swap_buffers = drm_egl_swap_buffers, - .external_swapchain = p->kms->atomic_context ? &drm_atomic_swapchain : - NULL, + .external_swapchain = &drm_egl_swapchain, }; if (!ra_gl_ctx_init(ctx, &p->gl, params)) return false; @@ -715,6 +799,13 @@ static int drm_egl_control(struct ra_ctx *ctx, int *events, int request, *(double*)arg = fps; return VO_TRUE; } + case VOCTRL_PAUSE: + ctx->vo->want_redraw = true; + p->paused = true; + return VO_TRUE; + case VOCTRL_RESUME: + p->paused = false; + return VO_TRUE; } return VO_NOTIMPL; } |