From ced3b5d803cd044526672c8458029b5a6519b86a Mon Sep 17 00:00:00 2001 From: Silvestrs Timofejevs Date: Mon, 2 Sep 2019 09:32:01 +0100 Subject: [PATCH] null_platform: add support for explicit synchronisation This change adds support for the 'in' fence, the 'out' fence will potentially be added in subsequent optimisation changes. Explicit synchronisation KMS 'in' and 'out' properties have been first added in Linux kernel 4.10 as an additional feature to the atomic mode setting. 9626014258a5957ff120b3987ee72decdbe0c798 - 'in' property beaf5af48034c9e2ebb8b2b1fb12dc4d8aeba99e - 'out' property Unlike many other features - it doesn't have an in kernel capability flag, so the support for it can be tested in the following way: KMS creates an additional set of properties when the version of kernel is new enough to support it ('in' and 'out' fence properties). When the user-space requests all the properties from the kernel via drmModeObjectGetProperties it is possible to determine whether the required property is supported by the kernel or not. The explicit synchronisation is used when available, otherwise the implicit sync model is used instead. If explicit synchronisation is available but 'in' fence failed to attach in KMS, the system falls back to the implicit synchronisation model. egl_dri2.c already creates the GPU 'out' fence that is then used by the null_platform as KMS 'in' fence. null_platform takes owenership of this fence fd during the swap buffers operation, at which point the fence fd is no longer available to be used outside of null_platform. EGL_LOG_LEVEL=debug should give some useful information in case of explicit synchronisation failure. As mentioned above, this failure will result in implicit synchronisation being used instead. Change-Id: Ib9c4a0bc3ea1b21192ee37909d7580d6b7b366ec --- src/egl/drivers/dri2/egl_dri2.h | 2 + src/egl/drivers/dri2/platform_null.c | 77 +++++++++++++++++++++++++++- 2 files changed, 77 insertions(+), 2 deletions(-) diff --git a/src/egl/drivers/dri2/egl_dri2.h b/src/egl/drivers/dri2/egl_dri2.h index 64d60ed..eb6e455 100644 --- a/src/egl/drivers/dri2/egl_dri2.h +++ b/src/egl/drivers/dri2/egl_dri2.h @@ -106,6 +106,7 @@ struct wl_buffer; #ifdef HAVE_NULL_PLATFORM struct display_output { bool in_use; + bool in_fence_supported; uint32_t connector_id; drmModePropertyRes **connector_prop_res; uint32_t crtc_id; @@ -338,6 +339,7 @@ struct swap_queue_elem uint32_t swap_interval; uint32_t back_id; uint32_t fb_id; + int kms_in_fence_fd; }; enum { diff --git a/src/egl/drivers/dri2/platform_null.c b/src/egl/drivers/dri2/platform_null.c index 5b7c1ec..d36dc0c 100644 --- a/src/egl/drivers/dri2/platform_null.c +++ b/src/egl/drivers/dri2/platform_null.c @@ -401,6 +401,9 @@ display_output_atomic_init(int fd, struct display_output *output) if (!output->atomic_state) goto err_destroy_mode_prop_blob; + if (property_id_get_for_name(plane_prop_res, "IN_FENCE_FD")) + output->in_fence_supported = true; + output->connector_prop_res = connector_prop_res; output->crtc_prop_res = crtc_prop_res; output->plane_prop_res = plane_prop_res; @@ -418,6 +421,53 @@ err_free_connector_prop_res: return false; } +static void +display_output_atomic_add_in_fence(struct display_output *output, + int kms_in_fence_fd) +{ + const struct object_property obj_sync_props[] = { + object_property_set_named(output, plane, "IN_FENCE_FD", kms_in_fence_fd), + }; + int err; + + /* Explicit synchronisation is not being used */ + if (kms_in_fence_fd < 0) + return; + + err = atomic_state_add_object_properties(output->atomic_state, + obj_sync_props, + ARRAY_SIZE(obj_sync_props)); + if (err) + _eglLog(_EGL_DEBUG, "%s: failed to add props ERR = %d", __func__, err); +} + +static void +atomic_claim_in_fence_fd(struct dri2_egl_surface *dri2_surf, + struct swap_queue_elem *swap_data) +{ + /* Explicit synchronisation is not being used */ + if (!dri2_surf->enable_out_fence) + return; + + if (dri2_surf->out_fence_fd < 0) { + _eglLog(_EGL_DEBUG, "%s: missing 'in' fence", __func__); + return; + } + + /* Take ownership of the fd */ + swap_data->kms_in_fence_fd = dri2_surf->out_fence_fd; + dri2_surf->out_fence_fd = -1; +} + +static void +atomic_relinquish_in_fence_fd(struct dri2_egl_surface *dri2_surf, + struct swap_queue_elem *swap_data) +{ + /* KMS is now in control of the fence (post drmModeAtomicCommit) */ + close(swap_data->kms_in_fence_fd); + swap_data->kms_in_fence_fd = -1; +} + static int display_output_atomic_flip(int fd, struct display_output *output, uint32_t fb_id, uint32_t flags, void *flip_data) @@ -425,6 +475,8 @@ display_output_atomic_flip(int fd, struct display_output *output, uint32_t fb_id const struct object_property obj_props[] = { object_property_set_named(output, plane, "FB_ID", fb_id), }; + struct dri2_egl_surface *dri2_surf = flip_data; + struct swap_queue_elem *swap_data = dri2_surf->swap_data; int err; /* Reset atomic state */ @@ -435,13 +487,19 @@ display_output_atomic_flip(int fd, struct display_output *output, uint32_t fb_id if (err) return err; + display_output_atomic_add_in_fence(output, swap_data->kms_in_fence_fd); + /* * Don't block - like drmModePageFlip, drmModeAtomicCommit will return * -EBUSY if the commit can't be queued in the kernel. */ flags |= DRM_MODE_ATOMIC_NONBLOCK; - return drmModeAtomicCommit(fd, output->atomic_state, flags, flip_data); + err = drmModeAtomicCommit(fd, output->atomic_state, flags, flip_data); + + atomic_relinquish_in_fence_fd(dri2_surf, swap_data); + + return err; } static int @@ -489,6 +547,8 @@ swap_enqueue_data(struct dri2_egl_surface *dri2_surf, uint32_t back_id, swap_data->fb_id = dri2_surf->back->fb_id; swap_data->back_id = back_id; + atomic_claim_in_fence_fd(dri2_surf, swap_data); + dri2_surf->swap_queue_idx_tail++; dri2_surf->swap_queue_idx_tail %= ARRAY_SIZE(dri2_surf->swap_queue); @@ -1025,11 +1085,21 @@ err_unlock: return false; } +static void surface_swap_queue_init(struct dri2_egl_surface *dri2_surf) +{ + struct swap_queue_elem *swap_queue = &dri2_surf->swap_queue[0]; + const int num_el = ARRAY_SIZE(dri2_surf->swap_queue); + + for (int i = 0; i < num_el; i++) + swap_queue[i].kms_in_fence_fd = -1; +} + static _EGLSurface * create_surface(_EGLDisplay *disp, _EGLConfig *config, EGLint type, const EGLint *attrib_list) { struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp); + struct display_output *output = &dri2_dpy->output; struct dri2_egl_config *dri2_config = dri2_egl_config(config); struct dri2_egl_surface *dri2_surf; const __DRIconfig *dri_config; @@ -1043,7 +1113,8 @@ create_surface(_EGLDisplay *disp, _EGLConfig *config, EGLint type, } surf = &dri2_surf->base; - if (!dri2_init_surface(surf, disp, type, config, attrib_list, false, NULL)) + if (!dri2_init_surface(surf, disp, type, config, attrib_list, + output->in_fence_supported, NULL)) goto err_free_surface; dri_config = dri2_get_dri_config(dri2_config, type, @@ -1066,6 +1137,8 @@ create_surface(_EGLDisplay *disp, _EGLConfig *config, EGLint type, dri2_surf->format = dri2_null_formats[format_idx].dri_image_format; + surface_swap_queue_init(dri2_surf); + return surf; err_free_surface: