diff options
Diffstat (limited to 'video/out/opengl/context_drm_egl.c')
-rw-r--r-- | video/out/opengl/context_drm_egl.c | 674 |
1 files changed, 249 insertions, 425 deletions
diff --git a/video/out/opengl/context_drm_egl.c b/video/out/opengl/context_drm_egl.c index 86ce57122c..da28093562 100644 --- a/video/out/opengl/context_drm_egl.c +++ b/video/out/opengl/context_drm_egl.c @@ -26,11 +26,14 @@ #include <gbm.h> #include <EGL/egl.h> #include <EGL/eglext.h> +#include <drm_fourcc.h> #include "libmpv/render_gl.h" -#include "video/out/drm_common.h" #include "common/common.h" #include "osdep/timer.h" +#include "video/out/drm_atomic.h" +#include "video/out/drm_common.h" +#include "video/out/present_sync.h" #include "egl_helpers.h" #include "common.h" @@ -44,35 +47,18 @@ #define EGL_PLATFORM_GBM_KHR 0x31D7 #endif -#define USE_MASTER 0 - -#ifndef EGL_EXT_platform_base -typedef EGLDisplay (EGLAPIENTRYP PFNEGLGETPLATFORMDISPLAYEXTPROC) - (EGLenum platform, void *native_display, const EGLint *attrib_list); -#endif - -struct framebuffer -{ - int fd; - uint32_t width, height; - uint32_t id; -}; - struct gbm_frame { struct gbm_bo *bo; - struct drm_vsync_tuple vsync; }; -struct gbm -{ +struct gbm { struct gbm_surface *surface; struct gbm_device *device; struct gbm_frame **bo_queue; unsigned int num_bos; }; -struct egl -{ +struct egl { EGLDisplay display; EGLContext context; EGLSurface surface; @@ -80,31 +66,16 @@ struct egl struct priv { GL gl; - struct kms *kms; - - drmEventContext ev; - drmModeCrtc *old_crtc; struct egl egl; struct gbm gbm; - struct framebuffer *fb; GLsync *vsync_fences; unsigned int num_vsync_fences; uint32_t gbm_format; - - bool active; - bool waiting_for_flip; - - bool vt_switcher_active; - struct vt_switcher vt_switcher; - - bool still; - bool paused; - - struct drm_vsync_tuple vsync; - struct vo_vsync_info vsync_info; + uint64_t *gbm_modifiers; + unsigned int num_gbm_modifiers; struct mpv_opengl_drm_params_v2 drm_params; struct mpv_opengl_drm_draw_surface_size draw_surface_size; @@ -118,10 +89,18 @@ static const char *gbm_format_to_string(uint32_t format) return "GBM_FORMAT_XRGB8888"; case GBM_FORMAT_ARGB8888: return "GBM_FORMAT_ARGB8888"; + case GBM_FORMAT_XBGR8888: + return "GBM_FORMAT_XBGR8888"; + case GBM_FORMAT_ABGR8888: + return "GBM_FORMAT_ABGR8888"; case GBM_FORMAT_XRGB2101010: return "GBM_FORMAT_XRGB2101010"; case GBM_FORMAT_ARGB2101010: return "GBM_FORMAT_ARGB2101010"; + case GBM_FORMAT_XBGR2101010: + return "GBM_FORMAT_XBGR2101010"; + case GBM_FORMAT_ABGR2101010: + return "GBM_FORMAT_ABGR2101010"; default: return "UNKNOWN"; } @@ -129,7 +108,7 @@ static const char *gbm_format_to_string(uint32_t format) // Allow falling back to an ARGB EGLConfig when we have an XRGB framebuffer. // Also allow falling back to an XRGB EGLConfig for ARGB framebuffers, since -// this seems neccessary to work with broken Mali drivers that don't report +// this seems necessary to work with broken Mali drivers that don't report // their EGLConfigs as supporting alpha properly. static uint32_t fallback_format_for(uint32_t format) { @@ -138,10 +117,18 @@ static uint32_t fallback_format_for(uint32_t format) return GBM_FORMAT_ARGB8888; case GBM_FORMAT_ARGB8888: return GBM_FORMAT_XRGB8888; + case GBM_FORMAT_XBGR8888: + return GBM_FORMAT_ABGR8888; + case GBM_FORMAT_ABGR8888: + return GBM_FORMAT_XBGR8888; case GBM_FORMAT_XRGB2101010: return GBM_FORMAT_ARGB2101010; case GBM_FORMAT_ARGB2101010: return GBM_FORMAT_XRGB2101010; + case GBM_FORMAT_XBGR2101010: + return GBM_FORMAT_ABGR2101010; + case GBM_FORMAT_ABGR2101010: + return GBM_FORMAT_XBGR2101010; default: return 0; } @@ -182,18 +169,15 @@ static int match_config_to_visual(void *user_data, EGLConfig *configs, int num_c static EGLDisplay egl_get_display(struct gbm_device *gbm_device) { - const char *ext = eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS); + EGLDisplay ret; - if (ext) { - PFNEGLGETPLATFORMDISPLAYEXTPROC get_platform_display = NULL; - get_platform_display = (void *) eglGetProcAddress("eglGetPlatformDisplayEXT"); - - if (get_platform_display && strstr(ext, "EGL_MESA_platform_gbm")) - return get_platform_display(EGL_PLATFORM_GBM_MESA, gbm_device, NULL); + ret = mpegl_get_display(EGL_PLATFORM_GBM_MESA, "EGL_MESA_platform_gbm", gbm_device); + if (ret != EGL_NO_DISPLAY) + return ret; - if (get_platform_display && strstr(ext, "EGL_KHR_platform_gbm")) - return get_platform_display(EGL_PLATFORM_GBM_KHR, gbm_device, NULL); - } + ret = mpegl_get_display(EGL_PLATFORM_GBM_KHR, "EGL_KHR_platform_gbm", gbm_device); + if (ret != EGL_NO_DISPLAY) + return ret; return eglGetDisplay(gbm_device); } @@ -219,9 +203,14 @@ static bool init_egl(struct ra_ctx *ctx) &p->egl.context, &config)) return false; + MP_VERBOSE(ctx, "Initializing EGL surface\n"); - p->egl.surface - = eglCreateWindowSurface(p->egl.display, config, p->gbm.surface, NULL); + p->egl.surface = mpegl_create_window_surface( + p->egl.display, config, p->gbm.surface); + if (p->egl.surface == EGL_NO_SURFACE) { + p->egl.surface = eglCreateWindowSurface( + p->egl.display, config, p->gbm.surface, NULL); + } if (p->egl.surface == EGL_NO_SURFACE) { MP_ERR(ctx, "Failed to create EGL surface.\n"); return false; @@ -232,8 +221,9 @@ static bool init_egl(struct ra_ctx *ctx) static bool init_gbm(struct ra_ctx *ctx) { struct priv *p = ctx->priv; + struct vo_drm_state *drm = ctx->vo->drm; MP_VERBOSE(ctx->vo, "Creating GBM device\n"); - p->gbm.device = gbm_create_device(p->kms->fd); + p->gbm.device = gbm_create_device(drm->fd); if (!p->gbm.device) { MP_ERR(ctx->vo, "Failed to create GBM device.\n"); return false; @@ -241,12 +231,22 @@ static bool init_gbm(struct ra_ctx *ctx) MP_VERBOSE(ctx->vo, "Initializing GBM surface (%d x %d)\n", p->draw_surface_size.width, p->draw_surface_size.height); - p->gbm.surface = gbm_surface_create( - p->gbm.device, - p->draw_surface_size.width, - p->draw_surface_size.height, - p->gbm_format, - GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING); + if (p->num_gbm_modifiers == 0) { + p->gbm.surface = gbm_surface_create( + p->gbm.device, + p->draw_surface_size.width, + p->draw_surface_size.height, + p->gbm_format, + GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING); + } else { + p->gbm.surface = gbm_surface_create_with_modifiers( + p->gbm.device, + p->draw_surface_size.width, + p->draw_surface_size.height, + p->gbm_format, + p->gbm_modifiers, + p->num_gbm_modifiers); + } if (!p->gbm.surface) { MP_ERR(ctx->vo, "Failed to create GBM surface.\n"); return false; @@ -265,261 +265,85 @@ static void framebuffer_destroy_callback(struct gbm_bo *bo, void *data) static void update_framebuffer_from_bo(struct ra_ctx *ctx, struct gbm_bo *bo) { struct priv *p = ctx->priv; + struct vo_drm_state *drm = ctx->vo->drm; struct framebuffer *fb = gbm_bo_get_user_data(bo); if (fb) { - p->fb = fb; + drm->fb = fb; return; } fb = talloc_zero(ctx, struct framebuffer); - fb->fd = p->kms->fd; + fb->fd = drm->fd; fb->width = gbm_bo_get_width(bo); fb->height = gbm_bo_get_height(bo); - uint32_t stride = gbm_bo_get_stride(bo); - uint32_t handle = gbm_bo_get_handle(bo).u32; + uint64_t modifier = gbm_bo_get_modifier(bo); - int ret = drmModeAddFB2(fb->fd, fb->width, fb->height, + int ret; + if (p->num_gbm_modifiers == 0 || modifier == DRM_FORMAT_MOD_INVALID) { + uint32_t stride = gbm_bo_get_stride(bo); + uint32_t handle = gbm_bo_get_handle(bo).u32; + ret = drmModeAddFB2(fb->fd, fb->width, fb->height, p->gbm_format, (uint32_t[4]){handle, 0, 0, 0}, (uint32_t[4]){stride, 0, 0, 0}, (uint32_t[4]){0, 0, 0, 0}, &fb->id, 0); - - if (ret) { - MP_ERR(ctx->vo, "Failed to create framebuffer: %s\n", mp_strerror(errno)); - } - gbm_bo_set_user_data(bo, fb, framebuffer_destroy_callback); - p->fb = fb; -} - -static bool crtc_setup_atomic(struct ra_ctx *ctx) -{ - struct priv *p = ctx->priv; - struct drm_atomic_context *atomic_ctx = p->kms->atomic_context; - - if (!drm_atomic_save_old_state(atomic_ctx)) { - MP_WARN(ctx->vo, "Failed to save old DRM atomic state\n"); - } - - drmModeAtomicReqPtr request = drmModeAtomicAlloc(); - if (!request) { - MP_ERR(ctx->vo, "Failed to allocate drm atomic request\n"); - return false; - } - - if (drm_object_set_property(request, atomic_ctx->connector, "CRTC_ID", p->kms->crtc_id) < 0) { - MP_ERR(ctx->vo, "Could not set CRTC_ID on connector\n"); - return false; - } - - if (!drm_mode_ensure_blob(p->kms->fd, &p->kms->mode)) { - MP_ERR(ctx->vo, "Failed to create DRM mode blob\n"); - goto err; - } - if (drm_object_set_property(request, atomic_ctx->crtc, "MODE_ID", p->kms->mode.blob_id) < 0) { - MP_ERR(ctx->vo, "Could not set MODE_ID on crtc\n"); - goto err; - } - if (drm_object_set_property(request, atomic_ctx->crtc, "ACTIVE", 1) < 0) { - MP_ERR(ctx->vo, "Could not set ACTIVE on crtc\n"); - goto err; - } - - drm_object_set_property(request, atomic_ctx->draw_plane, "FB_ID", p->fb->id); - drm_object_set_property(request, atomic_ctx->draw_plane, "CRTC_ID", p->kms->crtc_id); - drm_object_set_property(request, atomic_ctx->draw_plane, "SRC_X", 0); - drm_object_set_property(request, atomic_ctx->draw_plane, "SRC_Y", 0); - drm_object_set_property(request, atomic_ctx->draw_plane, "SRC_W", p->draw_surface_size.width << 16); - drm_object_set_property(request, atomic_ctx->draw_plane, "SRC_H", p->draw_surface_size.height << 16); - drm_object_set_property(request, atomic_ctx->draw_plane, "CRTC_X", 0); - drm_object_set_property(request, atomic_ctx->draw_plane, "CRTC_Y", 0); - drm_object_set_property(request, atomic_ctx->draw_plane, "CRTC_W", p->kms->mode.mode.hdisplay); - drm_object_set_property(request, atomic_ctx->draw_plane, "CRTC_H", p->kms->mode.mode.vdisplay); - - int ret = drmModeAtomicCommit(p->kms->fd, request, DRM_MODE_ATOMIC_ALLOW_MODESET, NULL); - if (ret) - MP_ERR(ctx->vo, "Failed to commit ModeSetting atomic request (%d)\n", ret); - - drmModeAtomicFree(request); - return ret == 0; - - err: - drmModeAtomicFree(request); - return false; -} - -static bool crtc_release_atomic(struct ra_ctx *ctx) -{ - struct priv *p = ctx->priv; - - struct drm_atomic_context *atomic_ctx = p->kms->atomic_context; - drmModeAtomicReqPtr request = drmModeAtomicAlloc(); - if (!request) { - MP_ERR(ctx->vo, "Failed to allocate drm atomic request\n"); - return false; - } - - if (!drm_atomic_restore_old_state(request, atomic_ctx)) { - MP_WARN(ctx->vo, "Got error while restoring old state\n"); - } - - int ret = drmModeAtomicCommit(p->kms->fd, request, DRM_MODE_ATOMIC_ALLOW_MODESET, NULL); - - if (ret) - MP_WARN(ctx->vo, "Failed to commit ModeSetting atomic request (%d)\n", ret); - - drmModeAtomicFree(request); - return ret == 0; -} - -static bool crtc_setup(struct ra_ctx *ctx) -{ - struct priv *p = ctx->priv; - if (p->active) - return true; - - if (p->kms->atomic_context) { - int ret = crtc_setup_atomic(ctx); - p->active = true; - return ret; - } else { - p->old_crtc = drmModeGetCrtc(p->kms->fd, p->kms->crtc_id); - int ret = drmModeSetCrtc(p->kms->fd, p->kms->crtc_id, p->fb->id, - 0, 0, &p->kms->connector->connector_id, 1, - &p->kms->mode.mode); - p->active = true; - return ret == 0; - } -} - -static void crtc_release(struct ra_ctx *ctx) -{ - struct priv *p = ctx->priv; - - if (!p->active) - return; - p->active = false; - - if (p->kms->atomic_context) { - if (p->kms->atomic_context->old_state.saved) { - if (!crtc_release_atomic(ctx)) - MP_ERR(ctx->vo, "Failed to restore previous mode\n"); - } } else { - if (p->old_crtc) { - drmModeSetCrtc(p->kms->fd, - p->old_crtc->crtc_id, p->old_crtc->buffer_id, - p->old_crtc->x, p->old_crtc->y, - &p->kms->connector->connector_id, 1, - &p->old_crtc->mode); - drmModeFreeCrtc(p->old_crtc); - p->old_crtc = NULL; + MP_VERBOSE(ctx, "GBM surface using modifier 0x%"PRIX64"\n", modifier); + + uint32_t handles[4] = {0}; + uint32_t strides[4] = {0}; + uint32_t offsets[4] = {0}; + uint64_t modifiers[4] = {0}; + + const int num_planes = gbm_bo_get_plane_count(bo); + for (int i = 0; i < num_planes; ++i) { + handles[i] = gbm_bo_get_handle_for_plane(bo, i).u32; + strides[i] = gbm_bo_get_stride_for_plane(bo, i); + offsets[i] = gbm_bo_get_offset(bo, i); + modifiers[i] = modifier; } - } -} -static void release_vt(void *data) -{ - struct ra_ctx *ctx = data; - MP_VERBOSE(ctx->vo, "Releasing VT\n"); - crtc_release(ctx); - if (USE_MASTER) { - //this function enables support for switching to x, weston etc. - //however, for whatever reason, it can be called only by root users. - //until things change, this is commented. - struct priv *p = ctx->priv; - if (drmDropMaster(p->kms->fd)) { - MP_WARN(ctx->vo, "Failed to drop DRM master: %s\n", - mp_strerror(errno)); - } + ret = drmModeAddFB2WithModifiers(fb->fd, fb->width, fb->height, + p->gbm_format, + handles, strides, offsets, modifiers, + &fb->id, DRM_MODE_FB_MODIFIERS); } -} - -static void acquire_vt(void *data) -{ - struct ra_ctx *ctx = data; - MP_VERBOSE(ctx->vo, "Acquiring VT\n"); - if (USE_MASTER) { - struct priv *p = ctx->priv; - if (drmSetMaster(p->kms->fd)) { - MP_WARN(ctx->vo, "Failed to acquire DRM master: %s\n", - mp_strerror(errno)); - } + if (ret) { + MP_ERR(ctx->vo, "Failed to create framebuffer: %s\n", mp_strerror(errno)); } - - crtc_setup(ctx); + gbm_bo_set_user_data(bo, fb, framebuffer_destroy_callback); + drm->fb = fb; } static void queue_flip(struct ra_ctx *ctx, struct gbm_frame *frame) { - struct priv *p = ctx->priv; - struct drm_atomic_context *atomic_ctx = p->kms->atomic_context; - int ret; + struct vo_drm_state *drm = ctx->vo->drm; update_framebuffer_from_bo(ctx, frame->bo); - // Alloc and fill the data struct for the page flip callback - struct drm_pflip_cb_closure *data = talloc(ctx, struct drm_pflip_cb_closure); - data->frame_vsync = &frame->vsync; - data->vsync = &p->vsync; - data->vsync_info = &p->vsync_info; - data->waiting_for_flip = &p->waiting_for_flip; - data->log = ctx->log; - - 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, data); - if (ret) { - MP_WARN(ctx->vo, "Failed to commit atomic request (%d)\n", ret); - talloc_free(data); - } - } else { - ret = drmModePageFlip(p->kms->fd, p->kms->crtc_id, p->fb->id, - DRM_MODE_PAGE_FLIP_EVENT, data); - if (ret) { - MP_WARN(ctx->vo, "Failed to queue page flip: %s\n", mp_strerror(errno)); - talloc_free(data); - } - } - p->waiting_for_flip = true; + struct drm_atomic_context *atomic_ctx = drm->atomic_context; + drm_object_set_property(atomic_ctx->request, atomic_ctx->draw_plane, "FB_ID", drm->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); - if (atomic_ctx) { - drmModeAtomicFree(atomic_ctx->request); - atomic_ctx->request = drmModeAtomicAlloc(); - } -} + int ret = drmModeAtomicCommit(drm->fd, atomic_ctx->request, + DRM_MODE_ATOMIC_NONBLOCK | DRM_MODE_PAGE_FLIP_EVENT, drm); -static void wait_on_flip(struct ra_ctx *ctx) -{ - struct priv *p = ctx->priv; + if (ret) + MP_WARN(ctx->vo, "Failed to commit atomic request: %s\n", mp_strerror(ret)); + drm->waiting_for_flip = !ret; - // poll page flip finish event - 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); - return; - } - } - } + drmModeAtomicFree(atomic_ctx->request); + atomic_ctx->request = drmModeAtomicAlloc(); } static void enqueue_bo(struct ra_ctx *ctx, struct gbm_bo *bo) { struct priv *p = ctx->priv; - p->vsync.sbc++; struct gbm_frame *new_frame = talloc(p, struct gbm_frame); new_frame->bo = bo; - new_frame->vsync = p->vsync; MP_TARRAY_APPEND(p, p->gbm.bo_queue, p->gbm.num_bos, new_frame); } @@ -569,10 +393,11 @@ 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; + struct vo_drm_state *drm = ctx->vo->drm; - 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; + if (!drm->atomic_context->request) { + drm->atomic_context->request = drmModeAtomicAlloc(); + p->drm_params.atomic_request_ptr = &drm->atomic_context->request; } return ra_gl_ctx_start_frame(sw, out_fbo); @@ -581,9 +406,9 @@ static bool drm_egl_start_frame(struct ra_swapchain *sw, struct ra_fbo *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; + struct vo_drm_state *drm = ctx->vo->drm; - p->still = frame->still; + drm->still = frame->still; return ra_gl_ctx_submit_frame(sw, frame); } @@ -592,9 +417,10 @@ 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 + struct vo_drm_state *drm = ctx->vo->drm; + const bool drain = drm->paused || drm->still; // True when we need to drain the swapchain - if (!p->active) + if (!drm->active) return; wait_fence(ctx); @@ -611,8 +437,8 @@ static void drm_egl_swap_buffers(struct ra_swapchain *sw) while (drain || p->gbm.num_bos > ctx->vo->opts->swapchain_depth || !gbm_surface_has_free_buffers(p->gbm.surface)) { - if (p->waiting_for_flip) { - wait_on_flip(ctx); + if (drm->waiting_for_flip) { + vo_drm_wait_on_flip(drm); swapchain_step(ctx); } if (p->gbm.num_bos <= 1) @@ -635,42 +461,41 @@ static const struct ra_swapchain_fns drm_egl_swapchain = { static void drm_egl_uninit(struct ra_ctx *ctx) { struct priv *p = ctx->priv; - struct drm_atomic_context *atomic_ctx = p->kms->atomic_context; + struct vo_drm_state *drm = ctx->vo->drm; + if (drm) { + struct drm_atomic_context *atomic_ctx = drm->atomic_context; + + if (drmModeAtomicCommit(drm->fd, atomic_ctx->request, 0, NULL)) + MP_ERR(ctx->vo, "Failed to commit atomic request: %s\n", + mp_strerror(errno)); - if (atomic_ctx) { - int ret = drmModeAtomicCommit(p->kms->fd, atomic_ctx->request, 0, NULL); - if (ret) - MP_ERR(ctx->vo, "Failed to commit atomic request (%d)\n", ret); drmModeAtomicFree(atomic_ctx->request); } ra_gl_ctx_uninit(ctx); + vo_drm_uninit(ctx->vo); - crtc_release(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); - eglDestroySurface(p->egl.display, p->egl.surface); - gbm_surface_destroy(p->gbm.surface); - eglTerminate(p->egl.display); - gbm_device_destroy(p->gbm.device); - p->egl.context = EGL_NO_CONTEXT; - eglDestroyContext(p->egl.display, p->egl.context); + if (p) { + // 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); + } - close(p->drm_params.render_fd); + eglMakeCurrent(p->egl.display, EGL_NO_SURFACE, EGL_NO_SURFACE, + EGL_NO_CONTEXT); + if (p->egl.display != EGL_NO_DISPLAY) { + eglDestroySurface(p->egl.display, p->egl.surface); + eglDestroyContext(p->egl.display, p->egl.context); + } + if (p->gbm.surface) + gbm_surface_destroy(p->gbm.surface); + eglTerminate(p->egl.display); + if (p->gbm.device) + gbm_device_destroy(p->gbm.device); - if (p->kms) { - kms_destroy(p->kms); - p->kms = 0; + if (p->drm_params.render_fd != -1) + close(p->drm_params.render_fd); } } @@ -682,16 +507,9 @@ static void drm_egl_uninit(struct ra_ctx *ctx) static bool probe_gbm_format(struct ra_ctx *ctx, uint32_t argb_format, uint32_t xrgb_format) { struct priv *p = ctx->priv; + struct vo_drm_state *drm = ctx->vo->drm; - if (!p->kms->atomic_context) { - p->gbm_format = xrgb_format; - MP_VERBOSE(ctx->vo, "Not using DRM Atomic: Use %s for draw plane.\n", - gbm_format_to_string(xrgb_format)); - return true; - } - - drmModePlane *drmplane = - drmModeGetPlane(p->kms->fd, p->kms->atomic_context->draw_plane->id); + drmModePlane *drmplane = drmModeGetPlane(drm->fd, drm->atomic_context->draw_plane->id); bool have_argb = false; bool have_xrgb = false; bool result = false; @@ -718,86 +536,119 @@ static bool probe_gbm_format(struct ra_ctx *ctx, uint32_t argb_format, uint32_t return result; } -static void drm_egl_get_vsync(struct ra_ctx *ctx, struct vo_vsync_info *info) +static bool probe_gbm_modifiers(struct ra_ctx *ctx) { struct priv *p = ctx->priv; - *info = p->vsync_info; -} + struct vo_drm_state *drm = ctx->vo->drm; -static bool drm_egl_init(struct ra_ctx *ctx) -{ - if (ctx->opts.probing) { - MP_VERBOSE(ctx, "DRM EGL backend can be activated only manually.\n"); + drmModePropertyBlobPtr blob = drm_object_get_property_blob(drm->atomic_context->draw_plane, + "IN_FORMATS"); + if (!blob) { + MP_VERBOSE(ctx->vo, "Failed to find IN_FORMATS property\n"); return false; } - struct priv *p = ctx->priv = talloc_zero(ctx, struct priv); - p->ev.version = DRM_EVENT_CONTEXT_VERSION; - p->ev.page_flip_handler = &drm_pflip_cb; - - p->vt_switcher_active = vt_switcher_init(&p->vt_switcher, ctx->vo->log); - if (p->vt_switcher_active) { - vt_switcher_acquire(&p->vt_switcher, acquire_vt, ctx); - vt_switcher_release(&p->vt_switcher, release_vt, ctx); - } else { - MP_WARN(ctx, "Failed to set up VT switcher. Terminal switching will be unavailable.\n"); + struct drm_format_modifier_blob *data = blob->data; + uint32_t *fmts = (uint32_t *)((char *)data + data->formats_offset); + struct drm_format_modifier *mods = + (struct drm_format_modifier *)((char *)data + data->modifiers_offset); + + for (unsigned int j = 0; j < data->count_modifiers; ++j) { + struct drm_format_modifier *mod = &mods[j]; + for (uint64_t k = 0; k < 64; ++k) { + if (mod->formats & (1ull << k)) { + uint32_t fmt = fmts[k + mod->offset]; + if (fmt == p->gbm_format) { + MP_TARRAY_APPEND(p, p->gbm_modifiers, + p->num_gbm_modifiers, mod->modifier); + MP_VERBOSE(ctx->vo, "Supported modifier: 0x%"PRIX64"\n", + (uint64_t)mod->modifier); + break; + } + } + } } + drmModeFreePropertyBlob(blob); - MP_VERBOSE(ctx, "Initializing KMS\n"); - p->kms = kms_create(ctx->log, ctx->vo->opts->drm_opts->drm_connector_spec, - ctx->vo->opts->drm_opts->drm_mode_spec, - ctx->vo->opts->drm_opts->drm_draw_plane, - ctx->vo->opts->drm_opts->drm_drmprime_video_plane, - ctx->vo->opts->drm_opts->drm_atomic); - if (!p->kms) { - MP_ERR(ctx, "Failed to create KMS.\n"); - return false; + if (p->num_gbm_modifiers == 0) { + MP_VERBOSE(ctx->vo, "No supported DRM modifiers found.\n"); } + return true; +} - if (ctx->vo->opts->drm_opts->drm_draw_surface_size.wh_valid) { - if (p->kms->atomic_context) { - p->draw_surface_size.width = ctx->vo->opts->drm_opts->drm_draw_surface_size.w; - p->draw_surface_size.height = ctx->vo->opts->drm_opts->drm_draw_surface_size.h; - } else { - p->draw_surface_size.width = p->kms->mode.mode.hdisplay; - p->draw_surface_size.height = p->kms->mode.mode.vdisplay; - MP_WARN(ctx, "Setting draw plane size is only available with DRM atomic, defaulting to screen resolution\n"); - } +static void drm_egl_get_vsync(struct ra_ctx *ctx, struct vo_vsync_info *info) +{ + struct vo_drm_state *drm = ctx->vo->drm; + present_sync_get_info(drm->present, info); +} + +static bool drm_egl_init(struct ra_ctx *ctx) +{ + if (!vo_drm_init(ctx->vo)) + goto err; + + struct priv *p = ctx->priv = talloc_zero(ctx, struct priv); + struct vo_drm_state *drm = ctx->vo->drm; + + if (ctx->vo->drm->opts->draw_surface_size.wh_valid) { + p->draw_surface_size.width = ctx->vo->drm->opts->draw_surface_size.w; + p->draw_surface_size.height = ctx->vo->drm->opts->draw_surface_size.h; } else { - p->draw_surface_size.width = p->kms->mode.mode.hdisplay; - p->draw_surface_size.height = p->kms->mode.mode.vdisplay; + p->draw_surface_size.width = drm->mode.mode.hdisplay; + p->draw_surface_size.height = drm->mode.mode.vdisplay; } + drm->width = p->draw_surface_size.width; + drm->height = p->draw_surface_size.height; + uint32_t argb_format; uint32_t xrgb_format; - if (DRM_OPTS_FORMAT_XRGB2101010 == ctx->vo->opts->drm_opts->drm_format) { + switch (ctx->vo->drm->opts->drm_format) { + case DRM_OPTS_FORMAT_XRGB2101010: argb_format = GBM_FORMAT_ARGB2101010; xrgb_format = GBM_FORMAT_XRGB2101010; - } else { + break; + case DRM_OPTS_FORMAT_XBGR2101010: + argb_format = GBM_FORMAT_ABGR2101010; + xrgb_format = GBM_FORMAT_XBGR2101010; + break; + case DRM_OPTS_FORMAT_XBGR8888: + argb_format = GBM_FORMAT_ABGR8888; + xrgb_format = GBM_FORMAT_XBGR8888; + break; + default: + if (drm->opts->drm_format != DRM_OPTS_FORMAT_XRGB8888) { + MP_VERBOSE(ctx->vo, "Requested format not supported by context, " + "falling back to xrgb8888\n"); + } argb_format = GBM_FORMAT_ARGB8888; xrgb_format = GBM_FORMAT_XRGB8888; + break; } if (!probe_gbm_format(ctx, argb_format, xrgb_format)) { MP_ERR(ctx->vo, "No suitable format found on draw plane (tried: %s and %s).\n", gbm_format_to_string(argb_format), gbm_format_to_string(xrgb_format)); - return false; + goto err; } + // It is not fatal if this fails. We'll just try without modifiers. + probe_gbm_modifiers(ctx); + if (!init_gbm(ctx)) { MP_ERR(ctx->vo, "Failed to setup GBM.\n"); - return false; + goto err; } if (!init_egl(ctx)) { MP_ERR(ctx->vo, "Failed to setup EGL.\n"); - return false; + goto err; } if (!eglMakeCurrent(p->egl.display, p->egl.surface, p->egl.surface, p->egl.context)) { MP_ERR(ctx->vo, "Failed to make context current.\n"); - return false; + goto err; } mpegl_load_functions(&p->gl, ctx->vo->log); @@ -808,38 +659,39 @@ static bool drm_egl_init(struct ra_ctx *ctx) 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; + goto err; } + enqueue_bo(ctx, new_bo); update_framebuffer_from_bo(ctx, new_bo); - if (!p->fb || !p->fb->id) { + if (!drm->fb || !drm->fb->id) { MP_ERR(ctx, "Failed to create framebuffer.\n"); - return false; + goto err; } - if (!crtc_setup(ctx)) { + if (!vo_drm_acquire_crtc(ctx->vo->drm)) { MP_ERR(ctx, "Failed to set CRTC for connector %u: %s\n", - p->kms->connector->connector_id, mp_strerror(errno)); - return false; + drm->connector->connector_id, mp_strerror(errno)); + goto err; } - p->drm_params.fd = p->kms->fd; - p->drm_params.crtc_id = p->kms->crtc_id; - p->drm_params.connector_id = p->kms->connector->connector_id; - if (p->kms->atomic_context) - p->drm_params.atomic_request_ptr = &p->kms->atomic_context->request; - char *rendernode_path = drmGetRenderDeviceNameFromFd(p->kms->fd); + vo_drm_set_monitor_par(ctx->vo); + + p->drm_params.fd = drm->fd; + p->drm_params.crtc_id = drm->crtc_id; + p->drm_params.connector_id = drm->connector->connector_id; + p->drm_params.atomic_request_ptr = &drm->atomic_context->request; + char *rendernode_path = drmGetRenderDeviceNameFromFd(drm->fd); if (rendernode_path) { MP_VERBOSE(ctx, "Opening render node \"%s\"\n", rendernode_path); p->drm_params.render_fd = open(rendernode_path, O_RDWR | O_CLOEXEC); if (p->drm_params.render_fd == -1) { - MP_WARN(ctx, "Cannot open render node \"%s\": %s. VAAPI hwdec will be disabled\n", - rendernode_path, mp_strerror(errno)); + MP_WARN(ctx, "Cannot open render node: %s\n", mp_strerror(errno)); } free(rendernode_path); } else { p->drm_params.render_fd = -1; - MP_VERBOSE(ctx, "Could not find path to render node. VAAPI hwdec will be disabled\n"); + MP_VERBOSE(ctx, "Could not find path to render node.\n"); } struct ra_gl_ctx_params params = { @@ -847,80 +699,52 @@ static bool drm_egl_init(struct ra_ctx *ctx) .get_vsync = &drm_egl_get_vsync, }; if (!ra_gl_ctx_init(ctx, &p->gl, params)) - return false; + goto err; ra_add_native_resource(ctx->ra, "drm_params_v2", &p->drm_params); ra_add_native_resource(ctx->ra, "drm_draw_surface_size", &p->draw_surface_size); - p->vsync_info.vsync_duration = 0; - p->vsync_info.skipped_vsyncs = -1; - p->vsync_info.last_queue_display_time = -1; - return true; + +err: + drm_egl_uninit(ctx); + return false; } static bool drm_egl_reconfig(struct ra_ctx *ctx) { - struct priv *p = ctx->priv; - ctx->vo->dwidth = p->fb->width; - ctx->vo->dheight = p->fb->height; - ra_gl_ctx_resize(ctx->swapchain, p->fb->width, p->fb->height, 0); + struct vo_drm_state *drm = ctx->vo->drm; + ctx->vo->dwidth = drm->fb->width; + ctx->vo->dheight = drm->fb->height; + ra_gl_ctx_resize(ctx->swapchain, drm->fb->width, drm->fb->height, 0); return true; } static int drm_egl_control(struct ra_ctx *ctx, int *events, int request, void *arg) { - struct priv *p = ctx->priv; - switch (request) { - case VOCTRL_GET_DISPLAY_FPS: { - double fps = kms_get_display_fps(p->kms); - if (fps <= 0) - break; - *(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; - p->vsync_info.last_queue_display_time = -1; - p->vsync_info.skipped_vsyncs = 0; - p->vsync.ust = 0; - p->vsync.msc = 0; - return VO_TRUE; - } - return VO_NOTIMPL; + int ret = vo_drm_control(ctx->vo, events, request, arg); + return ret; } -static void wait_events(struct ra_ctx *ctx, int64_t until_time_us) +static void drm_egl_wait_events(struct ra_ctx *ctx, int64_t until_time_ns) { - struct priv *p = ctx->priv; - if (p->vt_switcher_active) { - int64_t wait_us = until_time_us - mp_time_us(); - int timeout_ms = MPCLAMP((wait_us + 500) / 1000, 0, 10000); - vt_switcher_poll(&p->vt_switcher, timeout_ms); - } else { - vo_wait_default(ctx->vo, until_time_us); - } + vo_drm_wait_events(ctx->vo, until_time_ns); } -static void wakeup(struct ra_ctx *ctx) +static void drm_egl_wakeup(struct ra_ctx *ctx) { - struc |