From 4189cdaceb63ff4a866d59b43eb7508ba4bf4fd4 Mon Sep 17 00:00:00 2001 From: Joonbum Ko Date: Fri, 12 Jan 2024 17:10:23 +0900 Subject: [PATCH] wl_egl: implement idle_cb that waits for buffers to be finalized AS-IS - The parent thread waits for buffer to be released in traversing the buffer list of surface. PROBLEMS - Whenever a buffer lis is traversed, the thread should be paused, but the timeout error is often occured in pausing operation. TO-BE - Perform the operation of releasing buffer while traversing buffer list in __idle_cb_buffers_finalize. - __idle_cb_buffers_finalize is called whenever the main loop's idles, and not called after all buffers are released. - The parent thread sets a timeout (1s) and waits for all buffers to be released, and if the timeout error occurs, forcibly releases the buffers that were not released just before tbm_queue destroy. Change-Id: I6a31786dbe5b7340f7b9b8b00741d5fc3009c78b Signed-off-by: Joonbum Ko --- src/tpl_wl_egl_thread.c | 203 ++++++++++++++++++++++++++++++---------- 1 file changed, 156 insertions(+), 47 deletions(-) diff --git a/src/tpl_wl_egl_thread.c b/src/tpl_wl_egl_thread.c index a3abe39..cfe5ef6 100755 --- a/src/tpl_wl_egl_thread.c +++ b/src/tpl_wl_egl_thread.c @@ -158,6 +158,8 @@ struct _tpl_wl_egl_surface { tpl_bool_t set_serial_is_used; tpl_bool_t initialized_in_thread; tpl_bool_t frontbuffer_activated; + tpl_bool_t buffers_finalize_done; + tpl_bool_t need_force_release; /* To make sure that tpl_gsource has been successfully finalized. */ tpl_bool_t gsource_finalized; @@ -220,6 +222,8 @@ struct _tpl_wl_egl_buffer { /* for checking draw done */ tpl_bool_t draw_done; + tpl_bool_t release_pending; + #if TIZEN_FEATURE_ENABLE /* to get release event via zwp_linux_buffer_release_v1 */ struct zwp_linux_buffer_release_v1 *buffer_release; @@ -288,6 +292,8 @@ _thread_wl_surface_commit(tpl_wl_egl_surface_t *wl_egl_surface, tpl_wl_egl_buffer_t *wl_egl_buffer); static void __cb_surface_vblank_free(void *data); +static void +_buffers_force_release(tpl_wl_egl_surface_t *wl_egl_surface); static struct tizen_private * tizen_private_create() @@ -1641,6 +1647,9 @@ _thread_wl_egl_surface_fini(tpl_wl_egl_surface_t *wl_egl_surface) #endif if (wl_egl_surface->tbm_queue) { + if (wl_egl_surface->need_force_release) + _buffers_force_release(wl_egl_surface); + TPL_INFO("[TBM_QUEUE_DESTROY]", "wl_egl_surface(%p) tbm_queue(%p)", wl_egl_surface, wl_egl_surface->tbm_queue); @@ -2090,59 +2099,29 @@ _thread_wl_egl_surface_init(tpl_wl_egl_surface_t *wl_egl_surface) } static void -_tpl_wl_egl_surface_buffer_clear(tpl_wl_egl_surface_t *wl_egl_surface) +_buffers_force_release(tpl_wl_egl_surface_t *wl_egl_surface) { tbm_surface_queue_error_e tsq_err = TBM_SURFACE_QUEUE_ERROR_NONE; - tpl_wl_egl_display_t *wl_egl_display = wl_egl_surface->wl_egl_display; tpl_bool_t need_to_release = TPL_FALSE; tpl_bool_t need_to_cancel = TPL_FALSE; - buffer_status_t status = RELEASED; - int buffer_cnt = 0; - int idx = 0; - - tpl_gthread_pause_in_idle(wl_egl_display->thread); + tpl_list_node_t *node = NULL; - buffer_cnt = __tpl_list_get_count(wl_egl_surface->buffers); + TPL_INFO("[BUFFER_FORCE_RELEASE_BEGIN]", "wl_egl_surface(%p)", wl_egl_surface); + node = __tpl_list_get_front_node(wl_egl_surface->buffers); + do { + if (!node) break; - while (!__tpl_list_is_empty(wl_egl_surface->buffers)) { - tpl_wl_egl_buffer_t wl_egl_buffer(__tpl_list_pop_front(wl_egl_surface->buffers, - NULL)); + tpl_wl_egl_buffer_t wl_egl_buffer(__tpl_list_node_get_data(node)); tpl_gmutex_lock(&wl_egl_buffer->mutex); + buffer_status_t status = wl_egl_buffer->status; - status = wl_egl_buffer->status; - - TPL_INFO("[BUFFER_CLEAR]", - "[%d/%d] wl_egl_surface(%p) wl_egl_buffer(%p) tbm_surface(%p) status(%s)", - ++idx, buffer_cnt, wl_egl_surface, wl_egl_buffer, - wl_egl_buffer->tbm_surface, - status_to_string[status]); - - if (status >= ENQUEUED) { - tpl_result_t wait_result = TPL_ERROR_NONE; - - while (status < COMMITTED && wait_result != TPL_ERROR_TIME_OUT) { - tpl_gthread_continue(wl_egl_display->thread); - wait_result = tpl_gcond_timed_wait(&wl_egl_buffer->cond, - &wl_egl_buffer->mutex, - 500); /* 500ms */ - tpl_gthread_pause_in_idle(wl_egl_display->thread); - status = wl_egl_buffer->status; /* update status */ - - if (wait_result == TPL_ERROR_TIME_OUT) { - TPL_WARN("timeout occured waiting signaled. wl_egl_buffer(%p) status(%s)", - wl_egl_buffer, status_to_string[status]); - } - } + if (status == RELEASED) { + tpl_gmutex_unlock(&wl_egl_buffer->mutex); + continue; } - /* ACQUIRED, WAITING_SIGNALED, WAITING_VBLANK, COMMITTED */ - /* It has been acquired but has not yet been released, so this - * buffer must be released. */ need_to_release = (status >= ACQUIRED && status <= COMMITTED); - - /* After dequeue, it has not been enqueued yet - * so cancel_dequeue must be performed. */ need_to_cancel = (status == DEQUEUED); if (need_to_release) { @@ -2157,21 +2136,130 @@ _tpl_wl_egl_surface_buffer_clear(tpl_wl_egl_surface_t *wl_egl_surface) tsq_err = tbm_surface_queue_cancel_dequeue(wl_egl_surface->tbm_queue, wl_egl_buffer->tbm_surface); if (tsq_err != TBM_SURFACE_QUEUE_ERROR_NONE) - TPL_ERR("Failed to release tbm_surface(%p) tsq_err(%d)", + TPL_ERR("Failed to cancel dequeue tbm_surface(%p) tsq_err(%d)", wl_egl_buffer->tbm_surface, tsq_err); } wl_egl_buffer->status = RELEASED; + TPL_INFO("[FORCE_RELEASE]", "wl_egl_buffer(%p) status(%s -> %s)", + wl_egl_buffer, + status_to_string[status], + status_to_string[RELEASED]); + tpl_gmutex_unlock(&wl_egl_buffer->mutex); - if (need_to_release || need_to_cancel || status == ENQUEUED) + if (need_to_release || need_to_cancel) tbm_surface_internal_unref(wl_egl_buffer->tbm_surface); + + } while ((node = __tpl_list_node_next(node))); + + wl_egl_surface->need_force_release = TPL_FALSE; + TPL_INFO("[BUFFER_FORCE_RELEASE_END]", "wl_egl_surface(%p)", wl_egl_surface); +} + +static int +__idle_cb_buffers_finalize(void *data) +{ + tpl_wl_egl_surface_t wl_egl_surface(data); + TPL_CHECK_ON_NULL_RETURN_VAL(wl_egl_surface, TPL_FALSE); + + int pending_cnt = 0; + tpl_list_node_t *node = NULL; + tpl_bool_t ret = TPL_TRUE; + + tpl_gmutex_lock(&wl_egl_surface->surf_mutex); + + if (wl_egl_surface->buffers_finalize_done) { + tpl_gmutex_unlock(&wl_egl_surface->surf_mutex); + return TPL_FALSE; + } + + tpl_gmutex_lock(&wl_egl_surface->buffers_mutex); + + node = __tpl_list_get_front_node(wl_egl_surface->buffers); + do { + if (!node) break; + + tpl_wl_egl_buffer_t wl_egl_buffer(__tpl_list_node_get_data(node)); + + tpl_gmutex_lock(&wl_egl_buffer->mutex); + buffer_status_t status = wl_egl_buffer->status; + + if (status == RELEASED) { + tpl_gmutex_unlock(&wl_egl_buffer->mutex); + continue; + } + + if (status > DEQUEUED && status < COMMITTED) { + if (!wl_egl_buffer->release_pending) { + TPL_INFO("[RELEASE_PENDING]", "wl_egl_surface(%p) wl_egl_buffer(%p) status(%s)", + wl_egl_surface, wl_egl_buffer, status_to_string[status]); + TPL_INFO("[RELEASE_PENDING]", "tbm_surface(%p) bo(%d)", + wl_egl_buffer->tbm_surface, wl_egl_buffer->bo_name); + wl_egl_buffer->release_pending = TPL_TRUE; + } + + pending_cnt++; + tpl_gmutex_unlock(&wl_egl_buffer->mutex); + continue; + } + + if (status == COMMITTED) { + tbm_surface_queue_error_e tsq_err = TBM_SURFACE_QUEUE_ERROR_NONE; + tpl_wl_egl_display_t *wl_egl_display = wl_egl_surface->wl_egl_display; + + tsq_err = tbm_surface_queue_release(wl_egl_surface->tbm_queue, + wl_egl_buffer->tbm_surface); + if (tsq_err != TBM_SURFACE_QUEUE_ERROR_NONE) + TPL_ERR("Failed to release. tbm_surface(%p) tsq_err(%d)", + wl_egl_buffer->tbm_surface, tsq_err); + + if (wl_egl_display->wl_tbm_client && wl_egl_buffer->wl_buffer) { + wayland_tbm_client_destroy_buffer(wl_egl_display->wl_tbm_client, + (void *)wl_egl_buffer->wl_buffer); + wl_egl_buffer->wl_buffer = NULL; + wl_display_flush(wl_egl_display->wl_display); + } + + } else if (status == DEQUEUED) { + tbm_surface_queue_error_e tsq_err = TBM_SURFACE_QUEUE_ERROR_NONE; + tsq_err = tbm_surface_queue_cancel_dequeue(wl_egl_surface->tbm_queue, + wl_egl_buffer->tbm_surface); + if (tsq_err != TBM_SURFACE_QUEUE_ERROR_NONE) + TPL_ERR("Failed to cancel dequeue. tbm_surface(%p) tsq_err(%d)", + wl_egl_buffer->tbm_surface, tsq_err); + } + + TPL_INFO("[RELEASE]", "wl_egl_buffer(%p) status(%s -> %s)", + wl_egl_buffer, + status_to_string[status], + status_to_string[RELEASED]); + + wl_egl_buffer->status = RELEASED; + + tpl_gmutex_unlock(&wl_egl_buffer->mutex); + + tbm_surface_internal_unref(wl_egl_buffer->tbm_surface); + + } while ((node = __tpl_list_node_next(node))); + + tpl_gmutex_unlock(&wl_egl_surface->buffers_mutex); + + if (pending_cnt == 0) { + wl_egl_surface->buffers_finalize_done = TPL_TRUE; + tpl_gcond_signal(&wl_egl_surface->surf_cond); + TPL_INFO("[BUFFERS_FINALIZE DONE]", "wl_egl_surface(%p)", wl_egl_surface); + ret = TPL_FALSE; } - tpl_gthread_continue(wl_egl_display->thread); + tpl_gmutex_unlock(&wl_egl_surface->surf_mutex); + + return ret; } +#define BUFFER_CLEAR_WAITING_TIMEOUT_MS 1000 + static void __tpl_wl_egl_surface_fini(tpl_surface_t *surface) { @@ -2188,15 +2276,34 @@ __tpl_wl_egl_surface_fini(tpl_surface_t *surface) wl_egl_display = wl_egl_surface->wl_egl_display; TPL_CHECK_ON_NULL_RETURN(wl_egl_display); + tpl_result_t result_of_waiting = TPL_ERROR_NONE; + TPL_INFO("[SURFACE_FINI][BEGIN]", "wl_egl_surface(%p) wl_surface(%p) tbm_queue(%p)", wl_egl_surface, wl_egl_surface->wl_surface, wl_egl_surface->tbm_queue); - _tpl_wl_egl_surface_buffer_clear(wl_egl_surface); + tpl_gmutex_lock(&wl_egl_surface->surf_mutex); + + _print_buffer_lists(wl_egl_surface); + + wl_egl_surface->need_force_release = TPL_FALSE; + wl_egl_surface->buffers_finalize_done = TPL_FALSE; + tpl_gthread_add_idle(wl_egl_display->thread, + __idle_cb_buffers_finalize, wl_egl_surface); + while (!wl_egl_surface->buffers_finalize_done && + result_of_waiting != TPL_ERROR_TIME_OUT) { + result_of_waiting = tpl_gcond_timed_wait(&wl_egl_surface->surf_cond, + &wl_egl_surface->surf_mutex, + BUFFER_CLEAR_WAITING_TIMEOUT_MS); + if (result_of_waiting == TPL_ERROR_TIME_OUT) + TPL_WARN("buffer clear timeout. wl_egl_surface(%p)", wl_egl_surface); + + wl_egl_surface->buffers_finalize_done = TPL_TRUE; + wl_egl_surface->need_force_release = (result_of_waiting != TPL_ERROR_NONE); + } if (wl_egl_surface->surf_source) { - tpl_gmutex_lock(&wl_egl_surface->surf_mutex); // Send destroy mesage to thread tpl_gsource_destroy(wl_egl_surface->surf_source, TPL_TRUE); /* This is a protection to prevent problems that arise in unexpected situations @@ -2208,9 +2315,10 @@ __tpl_wl_egl_surface_fini(tpl_surface_t *surface) tpl_gcond_wait(&wl_egl_surface->surf_cond, &wl_egl_surface->surf_mutex); } wl_egl_surface->surf_source = NULL; - tpl_gmutex_unlock(&wl_egl_surface->surf_mutex); } + tpl_gmutex_unlock(&wl_egl_surface->surf_mutex); + if (wl_egl_surface->wl_egl_window) { struct wl_egl_window *wl_egl_window = wl_egl_surface->wl_egl_window; struct tizen_private tizen_private(wl_egl_window->driver_private); @@ -2404,6 +2512,7 @@ _wl_egl_buffer_init(tpl_wl_egl_buffer_t *wl_egl_buffer, TPL_ASSERT(tizen_private); wl_egl_buffer->draw_done = TPL_FALSE; + wl_egl_buffer->release_pending = TPL_FALSE; wl_egl_buffer->need_to_commit = TPL_TRUE; #if TIZEN_FEATURE_ENABLE wl_egl_buffer->buffer_release = NULL; -- 2.34.1