From 404c0e915de381c2337c79657f80b0aa95c1c995 Mon Sep 17 00:00:00 2001 From: Frank Binns Date: Wed, 25 Oct 2017 18:15:00 +0100 Subject: [PATCH 22/67] egl/tizen: create an internal _EGLImage for each tbm surface Create an internal _EGLImage the first time a tbm surface is seen by eglCreateImageKHR (with either the EGL_WAYLAND_BUFFER_WL or EGL_NATIVE_SURFACE_TIZEN target) and return a copy of it to the caller. This avoids some tbm surfaces being frequently mapped and then unmapped from the GPU. --- src/egl/drivers/dri2/egl_dri2.c | 236 +++++++++++++++++++++++++- src/egl/drivers/dri2/egl_dri2.h | 12 ++ src/egl/drivers/dri2/platform_tizen.c | 6 + 3 files changed, 251 insertions(+), 3 deletions(-) diff --git a/src/egl/drivers/dri2/egl_dri2.c b/src/egl/drivers/dri2/egl_dri2.c index eaa0baed066..db993f8f059 100644 --- a/src/egl/drivers/dri2/egl_dri2.c +++ b/src/egl/drivers/dri2/egl_dri2.c @@ -1264,6 +1264,10 @@ dri2_display_destroy(_EGLDisplay *disp) break; #ifdef HAVE_TIZEN_PLATFORM case _EGL_PLATFORM_TIZEN: + if (dri2_dpy->image_list_mutex_initialized) { + pthread_mutex_destroy(&dri2_dpy->image_list_mutex); + dri2_dpy->image_list_mutex_initialized = false; + } if (dri2_dpy->tpl_dpy) tpl_object_unreference((tpl_object_t *) dri2_dpy->tpl_dpy); break; @@ -1320,6 +1324,60 @@ dri2_egl_surface_free_local_buffers(struct dri2_egl_surface *dri2_surf) } } +static void +dri2_display_release_resources_tizen(_EGLDisplay *disp) +{ +#ifdef HAVE_TIZEN_PLATFORM + struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp); + _EGLResource *image_elem; + + /* Destroy _EGLImages in the image_list */ + pthread_mutex_lock(&dri2_dpy->image_list_mutex); + image_elem = dri2_dpy->image_list; + dri2_dpy->image_list = NULL; + pthread_mutex_unlock(&dri2_dpy->image_list_mutex); + + while (image_elem) { + _EGLImage *img = (_EGLImage *) image_elem; + struct dri2_egl_image *dri2_img = dri2_egl_image(img); + + image_elem = image_elem->Next; + + /* + * Delete the tbm surface user data (_EGLImage), unless the tbm surface + * was destroyed since we started processing the image_list. + */ + if (dri2_img->tbm_surf) + tbm_surface_internal_delete_user_data(dri2_img->tbm_surf, + (unsigned long) disp); + + /* + * dri2_orphan_tbm_surf_egl_image won't be able to transfer the + * _EGLImage to the orphan_image_list once we have started processing + * the image_list so we must destroy it ourselves. + */ + dri2_dpy->image->destroyImage(dri2_img->dri_image); + free(dri2_img); + } + + /* Destroy _EGLimages in the orphan_image_list*/ + pthread_mutex_lock(&dri2_dpy->image_list_mutex); + image_elem = dri2_dpy->orphan_image_list; + dri2_dpy->orphan_image_list = NULL; + pthread_mutex_unlock(&dri2_dpy->image_list_mutex); + + while (image_elem) { + _EGLImage *img = (_EGLImage *) image_elem; + struct dri2_egl_image *dri2_img = dri2_egl_image(img); + + image_elem = image_elem->Next; + + dri2_dpy->image->destroyImage(dri2_img->dri_image); + free(dri2_img); + } +#endif +} + /** * Called via eglTerminate(), drv->Terminate(). * @@ -1332,6 +1390,8 @@ dri2_terminate(_EGLDisplay *disp) /* Release all non-current Context/Surfaces. */ _eglReleaseDisplayResources(disp); + dri2_display_release_resources_tizen(disp); + dri2_display_release(disp); return EGL_TRUE; @@ -2309,6 +2369,9 @@ dri2_create_image_khr_renderbuffer(_EGLDisplay *disp, _EGLContext *ctx, } #ifdef HAVE_TIZEN_PLATFORM +static EGLBoolean +dri2_destroy_image_khr(_EGLDisplay *disp, _EGLImage *image); + int dri2_fourcc_from_tbm_format(tbm_format format) { @@ -2405,14 +2468,145 @@ fail_close: return NULL; } +static void +dri2_orphan_tbm_surf_egl_image(void *user_data) +{ + _EGLImage *img = user_data; + struct dri2_egl_image *dri2_img = dri2_egl_image(img); + _EGLDisplay *disp = img->Resource.Display; + struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp); + + /* + * Transfer the passed in _EGLImage from the image_list to the + * orphan_image_list so that it can be cleaned up at some later + * point. This is necessary as the disp->Mutex needs to be held + * in order destroy the _EGLImage and this could potentially cause + * a deadlock in the event that the tbm surface is destroyed. + */ + pthread_mutex_lock(&dri2_dpy->image_list_mutex); + /* + * This may be NULL if called via dri2_terminate or if dri2_terminate is + * running in another thread. + */ + if (dri2_dpy->image_list) { + _EGLResource *image_elem; + + /* Remove the _EGLImage from the image_list */ + image_elem = dri2_dpy->image_list; + if (image_elem != &img->Resource) { + while (image_elem) { + if (image_elem->Next == &img->Resource) + break; + image_elem = image_elem->Next; + } + image_elem->Next = img->Resource.Next; + } else { + dri2_dpy->image_list = img->Resource.Next; + } + + /* Add the _EGLImage to the orphan_image_list */ + img->Resource.Next = dri2_dpy->orphan_image_list; + dri2_dpy->orphan_image_list = &img->Resource; + } + + /* + * This function may have been called via tbm_surface_destroy so tbm_surf + * may be invalid after this function returns. + */ + dri2_img->tbm_surf = NULL; + pthread_mutex_unlock(&dri2_dpy->image_list_mutex); +} + +static _EGLImage * +dri2_get_tbm_surf_egl_image(_EGLDisplay *disp, _EGLContext *ctx, + tbm_surface_h tbm_surf) +{ + struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp); + struct dri2_egl_image *dri2_img; + _EGLImage *img; + int ret; + + ret = tbm_surface_internal_get_user_data(tbm_surf, (unsigned long) disp, + (void **) &img); + if (ret) + return img; + + img = dri2_create_image_tbm_surface(disp, ctx, tbm_surf); + if (!img) { + _eglError(EGL_BAD_PARAMETER, "dri2_create_image_tbm_surface failed"); + goto fail_exit; + } + dri2_img = dri2_egl_image(img); + + /* Use 'disp' as the 'key' as the _EGLimage is tied to it */ + ret = tbm_surface_internal_add_user_data(tbm_surf, (unsigned long) disp, + dri2_orphan_tbm_surf_egl_image); + if (!ret) { + _eglError(EGL_BAD_PARAMETER, "dri2_get_tbm_surf_egl_image"); + goto fail_destroy_image; + } + + ret = tbm_surface_internal_set_user_data(tbm_surf, (unsigned long) disp, img); + if (!ret) { + _eglError(EGL_BAD_PARAMETER, "dri2_get_tbm_surf_egl_image"); + goto fail_delete_user_data; + } + + /* + * Store the tbm surface so that the user data (_EGLImage) can be destroyed + * in dri2_terminate. We don't take a reference on the surface as this would + * prevent it from being destroyed until eglTerminate is called. This isn't + * an issue since it will be set to NULL, via dri2_orphan_tbm_surf_egl_image, + * once the surface is destroyed (although it's actually safe to call tbm + * surface functions with stale/NULL pointers). + */ + dri2_img->tbm_surf = tbm_surf; + + /* + * Add to the list of _EGLImages that are associated with tbm surfaces. + * This allows the _EGLImage to be destroyed if the application calls + * eglTerminate before the tbm surface is destroyed. + */ + pthread_mutex_lock(&dri2_dpy->image_list_mutex); + img->Resource.Next = dri2_dpy->image_list; + dri2_dpy->image_list = &img->Resource; + pthread_mutex_unlock(&dri2_dpy->image_list_mutex); + + return img; + +fail_delete_user_data: + tbm_surface_internal_delete_user_data(tbm_surf, (unsigned long) disp); +fail_destroy_image: + dri2_destroy_image_khr(disp, img); +fail_exit: + return NULL; +} + static _EGLImage * dri2_create_image_tizen(_EGLDisplay *disp, _EGLContext *ctx, EGLClientBuffer _buffer, const EGLint *attr_list) { - tbm_surface_h tbm_surf = (tbm_surface_h)_buffer; + struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp); + tbm_surface_h tbm_surf = (tbm_surface_h) _buffer; + _EGLImage *img; + struct dri2_egl_image *dri2_img; + __DRIimage *dri_image; + + img = dri2_get_tbm_surf_egl_image(disp, ctx, tbm_surf); + if (!img) { + _eglError(EGL_BAD_PARAMETER, "dri2_create_image_tizen"); + return NULL; + } + dri2_img = dri2_egl_image(img); + + dri_image = dri2_dpy->image->fromPlanar(dri2_img->dri_image, 0, NULL); + if (!dri_image) { + _eglError(EGL_BAD_PARAMETER, "dri2_create_image_tizen"); + return NULL; + } - return dri2_create_image_tbm_surface(disp, ctx, tbm_surf); + return dri2_create_image_from_dri(disp, dri_image); } static _EGLImage * @@ -2424,6 +2618,9 @@ dri2_create_image_wayland_wl_buffer_tizen(_EGLDisplay *disp, _EGLContext *ctx, _EGLImageAttribs attrs; tbm_surface_h tbm_surf; tbm_surface_info_s info; + _EGLImage *img; + struct dri2_egl_image *dri2_img; + __DRIimage *dri_image; tbm_surf = tpl_display_get_buffer_from_native_pixmap(dri2_dpy->tpl_dpy, (tpl_handle_t) _buffer); @@ -2454,7 +2651,21 @@ dri2_create_image_wayland_wl_buffer_tizen(_EGLDisplay *disp, _EGLContext *ctx, return NULL; } - return dri2_create_image_tbm_surface(disp, ctx, tbm_surf); + img = dri2_get_tbm_surf_egl_image(disp, ctx, tbm_surf); + if (!img) { + _eglError(EGL_BAD_PARAMETER, "dri2_create_image_wayland_wl_buffer_tizen"); + return NULL; + } + dri2_img = dri2_egl_image(img); + + dri_image = + dri2_dpy->image->fromPlanar(dri2_img->dri_image, attrs.PlaneWL, NULL); + if (!dri_image) { + _eglError(EGL_BAD_PARAMETER, "dri2_create_image_wayland_wl_buffer_tizen"); + return NULL; + } + + return dri2_create_image_from_dri(disp, dri_image); } #endif @@ -3377,10 +3588,29 @@ dri2_destroy_image_khr(_EGLDisplay *disp, _EGLImage *image) { struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp); struct dri2_egl_image *dri2_img = dri2_egl_image(image); +#ifdef HAVE_TIZEN_PLATFORM + _EGLResource *image_elem; +#endif dri2_dpy->image->destroyImage(dri2_img->dri_image); free(dri2_img); +#ifdef HAVE_TIZEN_PLATFORM + /* Take the opportunity to destroy orphaned tbm surface _EGLImages */ + pthread_mutex_lock(&dri2_dpy->image_list_mutex); + image_elem = dri2_dpy->orphan_image_list; + dri2_dpy->orphan_image_list = NULL; + pthread_mutex_unlock(&dri2_dpy->image_list_mutex); + + while (image_elem) { + dri2_img = dri2_egl_image((_EGLImage *) image_elem); + image_elem = image_elem->Next; + + dri2_dpy->image->destroyImage(dri2_img->dri_image); + free(dri2_img); + } +#endif + return EGL_TRUE; } diff --git a/src/egl/drivers/dri2/egl_dri2.h b/src/egl/drivers/dri2/egl_dri2.h index a8bfe45b1f5..546bc0a0dbc 100644 --- a/src/egl/drivers/dri2/egl_dri2.h +++ b/src/egl/drivers/dri2/egl_dri2.h @@ -268,6 +268,15 @@ struct dri2_egl_display #ifdef HAVE_TIZEN_PLATFORM tpl_display_t *tpl_dpy; + /* + * The image_list_mutex protects the image_list and orphan_image_list. It + * should never be held while calling a libtbm function as this may result + * in a deadlock. + */ + pthread_mutex_t image_list_mutex; + bool image_list_mutex_initialized; + _EGLResource *image_list; + _EGLResource *orphan_image_list; #endif }; @@ -413,6 +422,9 @@ struct dri2_egl_image { _EGLImage base; __DRIimage *dri_image; +#ifdef HAVE_TIZEN_PLATFORM + tbm_surface_h tbm_surf; +#endif }; struct dri2_egl_sync { diff --git a/src/egl/drivers/dri2/platform_tizen.c b/src/egl/drivers/dri2/platform_tizen.c index ad75c115d7e..49462152beb 100644 --- a/src/egl/drivers/dri2/platform_tizen.c +++ b/src/egl/drivers/dri2/platform_tizen.c @@ -891,6 +891,7 @@ dri2_initialize_tizen(_EGLDisplay *dpy) { struct dri2_egl_display *dri2_dpy; int i; + int err; dri2_dpy = calloc(1, sizeof(*dri2_dpy)); if (!dri2_dpy) @@ -906,6 +907,11 @@ dri2_initialize_tizen(_EGLDisplay *dpy) goto cleanup; } + err = pthread_mutex_init(&dri2_dpy->image_list_mutex, NULL); + if (err) + goto cleanup; + dri2_dpy->image_list_mutex_initialized = true; + for (i = TIZEN_DRM_RENDER_MINOR_START; i <= TIZEN_DRM_RENDER_MINOR_MAX; i++) { char *render_path; -- 2.25.1