From a8b54995b578abd5547803c605c0d430be775df6 Mon Sep 17 00:00:00 2001 From: Seunghun Lee Date: Wed, 16 Mar 2022 13:18:21 +0900 Subject: [PATCH] libds-tizen: Add ds_tdm_buffer_queue A ds_tdm_buffer_queue is a buffer queue that is able to be acquired from ds_tdm_output. A ds_tdm_buffer_queue provides a handle of native queue to a user and the user may pass it to a renderer. The renderer then should think of native queue as tbm_surface_queue_h. With the tbm_surface_queue_h, the renderer may dequeue a surface from it and draw on the dequeued buffer. After finish drawing on the buffer, the renderer should enqueue it to the tbm_surface_queue_h. As soon as the renderer enqueues buffer to the tbm_surface_queue_h, the user can recieve a notification that the buffer can be acquired from the ds_tbm_buffer_queue using ds_tdm_buffer_queue_add_acquirable_listener(). Then user may acquire a buffer from the ds_tdm_buffer_queue and attach it to a ds_output. Note that although the renderer may enqueue a buffer on different thread from the thread working on libds, but libds will call a callback function which is registered using ds_tdm_buffer_queue_add_acquirable_listener() on the thread working on libds. Change-Id: Ib50d2f11cdb27c0aa34b6beeecc73f012032c685 --- include/libds-tizen/backend/tdm.h | 21 ++ src/libds-tizen/backend/tdm/meson.build | 1 + src/libds-tizen/backend/tdm/output.c | 107 +++++++-- src/libds-tizen/backend/tdm/tdm.h | 5 +- src/libds-tizen/backend/tdm/tdm_buffer_queue.c | 311 +++++++++++++++++++++++++ src/libds-tizen/backend/tdm/tdm_buffer_queue.h | 45 ++++ 6 files changed, 464 insertions(+), 26 deletions(-) create mode 100644 src/libds-tizen/backend/tdm/tdm_buffer_queue.c create mode 100644 src/libds-tizen/backend/tdm/tdm_buffer_queue.h diff --git a/include/libds-tizen/backend/tdm.h b/include/libds-tizen/backend/tdm.h index d2785b2..8c5c605 100644 --- a/include/libds-tizen/backend/tdm.h +++ b/include/libds-tizen/backend/tdm.h @@ -2,14 +2,35 @@ #define LIBDS_TIZEN_BACKEND_TDM_H #include +#include #ifdef __cplusplus extern "C" { #endif +struct ds_tdm_output; + +struct ds_tdm_buffer_queue; + struct ds_backend * ds_tdm_backend_create(struct wl_display *display); +struct ds_tdm_output * +ds_tdm_output_from_output(struct ds_output *ds_output); + +struct ds_tdm_buffer_queue * +ds_tdm_output_get_buffer_queue(struct ds_tdm_output *output); + +void * +ds_tdm_buffer_queue_get_native_queue(struct ds_tdm_buffer_queue *queue); + +struct ds_buffer * +ds_tdm_buffer_queue_acquire(struct ds_tdm_buffer_queue *queue); + +void +ds_tdm_buffer_queue_add_acquirable_listener(struct ds_tdm_buffer_queue *queue, + struct wl_listener *listener); + #ifdef __cplusplus } #endif diff --git a/src/libds-tizen/backend/tdm/meson.build b/src/libds-tizen/backend/tdm/meson.build index 39ffceb..932559f 100644 --- a/src/libds-tizen/backend/tdm/meson.build +++ b/src/libds-tizen/backend/tdm/meson.build @@ -1,6 +1,7 @@ libds_tizen_files += files( 'backend.c', 'output.c', + 'tdm_buffer_queue.c', ) libtdm = dependency('libtdm', required: true) diff --git a/src/libds-tizen/backend/tdm/output.c b/src/libds-tizen/backend/tdm/output.c index f6b6250..10b9666 100644 --- a/src/libds-tizen/backend/tdm/output.c +++ b/src/libds-tizen/backend/tdm/output.c @@ -7,6 +7,7 @@ #include "libds-tizen/allocator/tbm.h" #include "tdm.h" +#include "tdm_buffer_queue.h" static const struct ds_output_interface tdm_output_iface; static bool output_init_modes(struct ds_tdm_output *output); @@ -17,6 +18,32 @@ static bool output_set_pending_fb(struct ds_tdm_output *output, struct ds_buffer *ds_buffer); static bool output_hwc_commit(struct ds_tdm_output *output); +WL_EXPORT struct ds_tdm_output * +ds_tdm_output_from_output(struct ds_output *ds_output) +{ + if (ds_output->iface != &tdm_output_iface) { + ds_err("Given ds_output is not for ds_tdm_output"); + return NULL; + } + + return (struct ds_tdm_output *)ds_output; +} + +WL_EXPORT struct ds_tdm_buffer_queue * +ds_tdm_output_get_buffer_queue(struct ds_tdm_output *output) +{ + if (output->queue) + return output->queue; + + output->queue = create_buffer_queue(output); + if (!output->queue) { + ds_err("Could not create tbm_queue with output(%p)", output); + return NULL; + } + + return output->queue; +} + struct ds_tdm_output * create_tdm_output(struct ds_tdm_backend *tdm, tdm_output *tdm_output) { @@ -89,12 +116,25 @@ destroy_tdm_buffer(struct ds_tdm_buffer *buffer) if (buffer == NULL) return; + if (!buffer->released) + wl_list_remove(&buffer->buffer_release.link); + wl_list_remove(&buffer->buffer_destroy.link); wl_list_remove(&buffer->link); free(buffer); } static void +buffer_handle_buffer_release(struct wl_listener *listener, void *data) +{ + struct ds_tdm_buffer *buffer; + + buffer = wl_container_of(listener, buffer, buffer_release); + wl_list_remove(&buffer->buffer_release.link); + buffer->released = true; +} + +static void buffer_handle_buffer_destroy(struct wl_listener *listener, void *data) { struct ds_tdm_buffer *buffer; @@ -120,7 +160,7 @@ create_tdm_buffer(struct ds_tdm_backend *backend, struct ds_buffer *ds_buffer) return NULL; buffer->surface = surface; - buffer->buffer = ds_buffer_lock(ds_buffer); + buffer->buffer = ds_buffer; wl_list_insert(&backend->buffers, &buffer->link); buffer->buffer_destroy.notify = buffer_handle_buffer_destroy; @@ -137,37 +177,38 @@ get_or_create_tdm_buffer(struct ds_tdm_backend *backend, wl_list_for_each(buffer, &backend->buffers, link) { if (buffer->buffer == ds_buffer && buffer->released) { - buffer->released = false; - ds_buffer_lock(buffer->buffer); - return buffer; + goto out; } } - return create_tdm_buffer(backend, ds_buffer); -} + buffer = create_tdm_buffer(backend, ds_buffer); -static void -tdm_buffer_release(struct ds_tdm_buffer *buffer) -{ - buffer->released = true; - ds_buffer_unlock(buffer->buffer); +out: + buffer->released = false; + + buffer->buffer_release.notify = buffer_handle_buffer_release; + ds_buffer_add_release_listener(ds_buffer, &buffer->buffer_release); + + ds_buffer_lock(buffer->buffer); + + return buffer; } static void output_update_front_buffer(struct ds_tdm_output *output) { if (output->front_buffer) - tdm_buffer_release(output->front_buffer); + ds_buffer_unlock(output->front_buffer); output->front_buffer = output->back_buffer; output->back_buffer = NULL; } static void output_attach_back_buffer(struct ds_tdm_output *output, - struct ds_tdm_buffer *buffer) + struct ds_buffer *buffer) { if (output->back_buffer) - tdm_buffer_release(output->back_buffer); + ds_buffer_unlock(output->back_buffer); output->back_buffer = buffer; } @@ -192,9 +233,8 @@ output_iface_commit(struct ds_output *ds_output) if (!output_hwc_commit(output)) { ds_err("Could not commit tdm output"); - if (output->back_buffer) { - tdm_buffer_release(output->back_buffer); - } + if (output->back_buffer) + ds_buffer_unlock(output->back_buffer); } return true; @@ -217,10 +257,13 @@ output_destroy(struct ds_tdm_output *output) } if (output->back_buffer) - ds_buffer_unlock(output->back_buffer->buffer); + ds_buffer_unlock(output->back_buffer); if (output->front_buffer) - ds_buffer_unlock(output->front_buffer->buffer); + ds_buffer_unlock(output->front_buffer); + + if (output->queue) + buffer_queue_destroy(output->queue); free(output); } @@ -306,24 +349,38 @@ static bool output_set_pending_fb(struct ds_tdm_output *output, struct ds_buffer *ds_buffer) { + struct ds_tdm_queue_buffer *queue_buffer; struct ds_tdm_buffer *buffer; + tbm_surface_h surface = NULL; tdm_region fb_damage; tdm_error err; - buffer = get_or_create_tdm_buffer(output->backend, ds_buffer); - if (!buffer) - return false; + if (output->queue) { + queue_buffer = buffer_queue_find_buffer(output->queue, ds_buffer); + if (queue_buffer) { + ds_buffer_lock(ds_buffer); + surface = queue_buffer->surface; + } + } + + if (!surface) { + buffer = get_or_create_tdm_buffer(output->backend, ds_buffer); + if (!buffer) + return false; + + surface = buffer->surface; + } memset(&fb_damage, 0, sizeof(fb_damage)); err = tdm_hwc_set_client_target_buffer(output->tdm.hwc, - buffer->surface, fb_damage); + surface, fb_damage); if (err != TDM_ERROR_NONE) { ds_err("Could not set hwc client target buffer"); - ds_buffer_unlock(buffer->buffer); + ds_buffer_unlock(ds_buffer); return false; } - output_attach_back_buffer(output, buffer); + output_attach_back_buffer(output, ds_buffer); return true; } diff --git a/src/libds-tizen/backend/tdm/tdm.h b/src/libds-tizen/backend/tdm/tdm.h index 275089c..f19483f 100644 --- a/src/libds-tizen/backend/tdm/tdm.h +++ b/src/libds-tizen/backend/tdm/tdm.h @@ -40,7 +40,8 @@ struct ds_tdm_output struct ds_output base; struct ds_tdm_backend *backend; - struct ds_tdm_buffer *front_buffer, *back_buffer; + struct ds_tdm_buffer_queue *queue; + struct ds_buffer *front_buffer, *back_buffer; struct { tdm_output *output; @@ -57,6 +58,8 @@ struct ds_tdm_buffer struct ds_buffer *buffer; tbm_surface_h surface; struct wl_list link; // ds_wl_backend.buffers + + struct wl_listener buffer_release; struct wl_listener buffer_destroy; bool released; diff --git a/src/libds-tizen/backend/tdm/tdm_buffer_queue.c b/src/libds-tizen/backend/tdm/tdm_buffer_queue.c new file mode 100644 index 0000000..7a2a1aa --- /dev/null +++ b/src/libds-tizen/backend/tdm/tdm_buffer_queue.c @@ -0,0 +1,311 @@ +#include +#include +#include +#include + +#include "libds/log.h" +#include "libds/interfaces/buffer.h" + +#include "tdm.h" +#include "tdm_buffer_queue.h" + +static void +buffer_queue_handle_acquirable(tbm_surface_queue_h surface_queue, + void *data); +static struct ds_tdm_queue_buffer * +create_queue_buffer(struct ds_tdm_buffer_queue *queue, + tbm_surface_h surface); +static void queue_buffer_destroy(struct ds_tdm_queue_buffer *buffer); +static void queue_buffer_drop(struct ds_tdm_queue_buffer *buffer); +static struct ds_buffer * +queue_buffer_acquire(struct ds_tdm_queue_buffer *buffer); +static int buffer_queue_handle_acquirable_efd(int fd, uint32_t mask, + void *data); + +WL_EXPORT void * +ds_tdm_buffer_queue_get_native_queue(struct ds_tdm_buffer_queue *queue) +{ + return (void *)queue->tbm_surface_queue; +} + +WL_EXPORT struct ds_buffer * +ds_tdm_buffer_queue_acquire(struct ds_tdm_buffer_queue *queue) +{ + struct ds_tdm_queue_buffer *buffer; + tbm_surface_h surface; + tbm_surface_queue_error_e err; + + if (!tbm_surface_queue_can_acquire(queue->tbm_surface_queue, 0)) + return NULL; + + err = tbm_surface_queue_acquire(queue->tbm_surface_queue, &surface); + if (err != TBM_SURFACE_QUEUE_ERROR_NONE || + surface == NULL) { + ds_err("Could not acquire tbm_surface from queue(%p)", queue); + return NULL; + } + + wl_list_for_each(buffer, &queue->buffers, link) { + if (buffer->surface == surface) + return queue_buffer_acquire(buffer); + } + + buffer = create_queue_buffer(queue, surface); + if (!buffer) { + ds_err("Could not create tbm_queue_buffer with queue(%p)", queue); + return NULL; + } + + wl_list_insert(&queue->buffers, &buffer->link); + + return queue_buffer_acquire(buffer); +} + +WL_EXPORT void +ds_tdm_buffer_queue_add_acquirable_listener(struct ds_tdm_buffer_queue *queue, + struct wl_listener *listener) +{ + wl_signal_add(&queue->events.acquirable, listener); +} + +struct ds_tdm_buffer_queue * +create_buffer_queue(struct ds_tdm_output *output) +{ + struct ds_tdm_buffer_queue *queue; + tdm_error err; + + queue = calloc(1, sizeof *queue); + if (!queue) + return NULL; + + wl_list_init(&queue->buffers); + + wl_signal_init(&queue->events.acquirable); + + queue->tbm_surface_queue = + tdm_hwc_get_client_target_buffer_queue(output->tdm.hwc, &err); + if (err != TDM_ERROR_NONE || + queue->tbm_surface_queue == NULL) { + ds_err("Could not get target buffer queue: err(%d)", err); + free(queue); + return NULL; + } + + tbm_surface_queue_reset(queue->tbm_surface_queue, + output->base.pending.mode->width, + output->base.pending.mode->height, + tbm_surface_queue_get_format(queue->tbm_surface_queue)); + + /* The callback function for tbm_surface_queue_add_acquirable_cb() may be + * called on different thread. This eventfd is to emit a signal of + * events.acquirable on the thread getting this buffer queue. */ + queue->acquirable_efd = eventfd(0, EFD_NONBLOCK); + if (queue->acquirable_efd < 0) { + ds_log_errno(DS_ERR, + "Could not create eventfd for acquirable callback"); + free(queue); + return NULL; + } + + queue->acquirable_source = wl_event_loop_add_fd( + wl_display_get_event_loop(output->backend->wl_display), + queue->acquirable_efd, + WL_EVENT_READABLE, + buffer_queue_handle_acquirable_efd, + queue); + + tbm_surface_queue_add_acquirable_cb(queue->tbm_surface_queue, + buffer_queue_handle_acquirable, (void *)queue); + + return queue; +} + +void +buffer_queue_destroy(struct ds_tdm_buffer_queue *queue) +{ + struct ds_tdm_queue_buffer *buffer, *buffer_tmp; + + wl_list_for_each_safe(buffer, buffer_tmp, &queue->buffers, link) + queue_buffer_drop(buffer); + + wl_event_source_remove(queue->acquirable_source); + close(queue->acquirable_efd); + tbm_surface_queue_destroy(queue->tbm_surface_queue); + free(queue); +} + +struct ds_tdm_queue_buffer * +buffer_queue_find_buffer(struct ds_tdm_buffer_queue *queue, + struct ds_buffer *ds_buffer) +{ + struct ds_tdm_queue_buffer *buffer; + + wl_list_for_each(buffer, &queue->buffers, link) { + if (&buffer->base == ds_buffer) + return buffer; + } + + return NULL; +} + +static void +buffer_queue_handle_acquirable(tbm_surface_queue_h surface_queue, void *data) +{ + struct ds_tdm_buffer_queue *queue = data; + uint64_t acquirable = 1; + int ret; + + ret = write(queue->acquirable_efd, &acquirable, sizeof(acquirable)); + if (ret < 0) + ds_log_errno(DS_ERR, "Could not write eventfd for acquirable buffer"); +} + +static const struct ds_buffer_interface queue_buffer_iface; + +static struct ds_tdm_queue_buffer * +create_queue_buffer(struct ds_tdm_buffer_queue *queue, tbm_surface_h surface) +{ + struct ds_tdm_queue_buffer *buffer; + + buffer = calloc(1, sizeof *buffer); + if (!buffer) + return NULL; + + ds_buffer_init(&buffer->base, &queue_buffer_iface, + tbm_surface_get_width(surface), + tbm_surface_get_height(surface)); + + buffer->queue = queue; + buffer->surface = surface; + + return buffer; +} + +static void +queue_buffer_destroy(struct ds_tdm_queue_buffer *buffer) +{ + free(buffer); +} + +static struct ds_tdm_queue_buffer * +queue_buffer_from_buffer(struct ds_buffer *ds_buffer) +{ + assert(ds_buffer->iface == &queue_buffer_iface); + return (struct ds_tdm_queue_buffer *)ds_buffer; +} + +static void +queue_buffer_iface_destroy(struct ds_buffer *ds_buffer) +{ + struct ds_tdm_queue_buffer *buffer; + + buffer = queue_buffer_from_buffer(ds_buffer); + queue_buffer_destroy(buffer); +} + +static bool +queue_buffer_iface_begin_data_ptr_access(struct ds_buffer *ds_buffer, + uint32_t flags, void **data, uint32_t *format, size_t *stride) +{ + struct ds_tdm_queue_buffer *buffer; + tbm_surface_info_s info; + int tbm_access_flags = 0; + int ret; + + buffer = queue_buffer_from_buffer(ds_buffer); + + if (!buffer->surface) { + ds_err("No tbm_surface. It's a dropped buffer(%p)", buffer); + return false; + } + + if (flags & DS_BUFFER_DATA_PTR_ACCESS_READ) + tbm_access_flags |= TBM_OPTION_READ; + else if (flags & DS_BUFFER_DATA_PTR_ACCESS_WRITE) + tbm_access_flags |= TBM_OPTION_WRITE; + + ret = tbm_surface_map(buffer->surface, tbm_access_flags, &info); + if (ret != TBM_SURFACE_ERROR_NONE) { + ds_err("Could not map tbm_surface of buffer(%p)", buffer); + return false; + } + + *data = info.planes[0].ptr; + *format = info.format; + *stride = info.planes[0].stride; + + return true; +} + +static void +queue_buffer_iface_end_data_ptr_access(struct ds_buffer *ds_buffer) +{ + struct ds_tdm_queue_buffer *buffer; + + buffer = queue_buffer_from_buffer(ds_buffer); + if (!buffer->surface) { + ds_err("No tbm_surface. It's a dropped buffer(%p)", buffer); + return; + } + + tbm_surface_unmap(buffer->surface); +} + +static const struct ds_buffer_interface queue_buffer_iface = +{ + .destroy = queue_buffer_iface_destroy, + .begin_data_ptr_access = queue_buffer_iface_begin_data_ptr_access, + .end_data_ptr_access = queue_buffer_iface_end_data_ptr_access, +}; + +static void +queue_buffer_handle_buffer_release(struct wl_listener *listener, void *data) +{ + struct ds_tdm_queue_buffer *buffer; + + buffer = wl_container_of(listener, buffer, buffer_release); + + wl_list_remove(&buffer->buffer_release.link); + buffer->acquired = false; + + tbm_surface_queue_release(buffer->queue->tbm_surface_queue, + buffer->surface); +} + +static void +queue_buffer_drop(struct ds_tdm_queue_buffer *buffer) +{ + if (buffer->acquired) + wl_list_remove(&buffer->buffer_release.link); + + wl_list_remove(&buffer->link); + ds_buffer_drop(&buffer->base); + buffer->surface = NULL; +} + +static struct ds_buffer * +queue_buffer_acquire(struct ds_tdm_queue_buffer *buffer) +{ + assert(!buffer->acquired); + + buffer->acquired = true; + buffer->buffer_release.notify = queue_buffer_handle_buffer_release; + ds_buffer_add_release_listener(&buffer->base, &buffer->buffer_release); + + return ds_buffer_lock(&buffer->base); +} + +static int +buffer_queue_handle_acquirable_efd(int fd, uint32_t mask, void *data) +{ + struct ds_tdm_buffer_queue *queue = data; + uint64_t acquirable_event; + + if (read(fd, &acquirable_event, sizeof(acquirable_event)) < 0 && + errno != EAGAIN) + return -1; + + wl_signal_emit(&queue->events.acquirable, queue); + + return 0; +} diff --git a/src/libds-tizen/backend/tdm/tdm_buffer_queue.h b/src/libds-tizen/backend/tdm/tdm_buffer_queue.h new file mode 100644 index 0000000..774b7c3 --- /dev/null +++ b/src/libds-tizen/backend/tdm/tdm_buffer_queue.h @@ -0,0 +1,45 @@ +#ifndef DS_TIZEN_BACKEND_TDM_BUFFER_QUEUE_H +#define DS_TIZEN_BACKEND_TBM_BUFFER_QUEUE_H + +#include +#include + +#include "tdm.h" + +struct ds_tdm_buffer_queue +{ + tbm_surface_queue_h tbm_surface_queue; + struct wl_event_source *acquirable_source; + + struct wl_list buffers; // ds_tdm_queue_buffer.link + + struct { + struct wl_signal acquirable; + } events; + + int acquirable_efd; +}; + +struct ds_tdm_queue_buffer +{ + struct ds_buffer base; + + struct ds_tdm_output *output; + struct ds_tdm_buffer_queue *queue; + tbm_surface_h surface; + + struct wl_list link; + struct wl_listener buffer_release; + + bool acquired; +}; + +struct ds_tdm_buffer_queue *create_buffer_queue(struct ds_tdm_output *output); + +void buffer_queue_destroy(struct ds_tdm_buffer_queue *queue); + +struct ds_tdm_queue_buffer * +buffer_queue_find_buffer(struct ds_tdm_buffer_queue *queue, + struct ds_buffer *ds_buffer); + +#endif -- 2.7.4