#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);
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)
{
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;
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;
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;
}
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;
}
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);
}
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;
}
--- /dev/null
+#include <assert.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <sys/eventfd.h>
+
+#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;
+}