aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMarius Vlad <marius.vlad@collabora.com>2023-08-28 11:57:32 +0300
committerMarius Vlad <marius.vlad@collabora.com>2023-08-28 13:16:23 +0300
commit8f9f567e0cd39f93ee5298afe95e17f76cb0413f (patch)
tree3a0c121ec8b23e92a20b0a2d9c001bd16928dcf3
parente48e0b3102b63a65944463c807b4c2d851bda3f4 (diff)
main: Redo buffer handling in camera-gstreamer
Using this time a buffer list, similar to what simple-shm is doing to avoid getting back that a buffer is busy, due to *not* prunning older buffers. As such, double buffering seems to be sufficient so we don't need to resort to having 3 buffers. This should fix switching back and forth between other applications and the camera application. Tested with vivid module. Bug-AGL: SPEC-4884 Signed-off-by: Marius Vlad <marius.vlad@collabora.com> Change-Id: Ia3987782b3e107a0055f9680fecfed0f9a96eec6
-rw-r--r--app/main.cpp167
1 files changed, 111 insertions, 56 deletions
diff --git a/app/main.cpp b/app/main.cpp
index bfcef46..4451261 100644
--- a/app/main.cpp
+++ b/app/main.cpp
@@ -31,6 +31,8 @@
#define WINDOW_WIDTH_POS_X 640
#define WINDOW_WIDTH_POS_Y 180
+#define MAX_BUFFER_ALLOC 2
+
// C++ requires a cast and we in wayland we do the cast implictly
#define WL_ARRAY_FOR_EACH(pos, array, type) \
for (pos = (type)(array)->data; \
@@ -59,6 +61,9 @@ struct buffer {
struct wl_buffer *buffer;
void *shm_data;
int busy;
+ int width, height;
+ size_t size; /* width * 4 * height */
+ struct wl_list buffer_link; /** window::buffer_list */
};
struct window {
@@ -66,6 +71,8 @@ struct window {
int x, y;
int width, height;
+ int init_width;
+ int init_height;
struct wl_surface *surface;
@@ -75,8 +82,9 @@ struct window {
int fullscreen, maximized;
- struct buffer buffers[2];
+ struct wl_list buffer_list;
struct wl_callback *callback;
+ bool needs_update_buffer;
};
@@ -93,6 +101,58 @@ static int running = 1;
static void
redraw(void *data, struct wl_callback *callback, uint32_t time);
+static struct buffer *
+alloc_buffer(struct window *window, int width, int height)
+{
+ struct buffer *buffer = static_cast<struct buffer *>(calloc(1, sizeof(*buffer)));
+
+ buffer->width = width;
+ buffer->height = height;
+ wl_list_insert(&window->buffer_list, &buffer->buffer_link);
+
+ return buffer;
+}
+
+static void
+destroy_buffer(struct buffer *buffer)
+{
+ if (buffer->buffer)
+ wl_buffer_destroy(buffer->buffer);
+
+ munmap(buffer->shm_data, buffer->size);
+ wl_list_remove(&buffer->buffer_link);
+ free(buffer);
+}
+
+static struct buffer *
+pick_free_buffer(struct window *window)
+{
+ struct buffer *b;
+ struct buffer *buffer = NULL;
+
+ wl_list_for_each(b, &window->buffer_list, buffer_link) {
+ if (!b->busy) {
+ buffer = b;
+ break;
+ }
+ }
+
+ return buffer;
+}
+
+static void
+prune_old_released_buffers(struct window *window)
+{
+ struct buffer *b, *b_next;
+
+ wl_list_for_each_safe(b, b_next,
+ &window->buffer_list, buffer_link) {
+ if (!b->busy && (b->width != window->width ||
+ b->height != window->height))
+ destroy_buffer(b);
+ }
+}
+
static void
paint_pixels(void *image, int padding, int width, int height, uint32_t time)
{
@@ -144,6 +204,10 @@ create_shm_buffer(struct display *display, struct buffer *buffer,
close(fd);
buffer->shm_data = data;
+ buffer->size = size;
+ buffer->width = width;
+ buffer->height = height;
+
fprintf(stdout, "Created shm buffer with width %d, height %d\n", width, height);
return 0;
}
@@ -154,26 +218,19 @@ get_next_buffer(struct window *window)
struct buffer *buffer = NULL;
int ret = 0;
- /* we need to create new buffers for the resized value so discard
- * the 'old' one and force creation of the buffer with the newer
- * dimensions */
- if (window->wait_for_configure && window->maximized) {
- /* The 'old' buffer might not exist if maximized is received
- * from the start. */
- if (window->buffers[0].buffer && !window->buffers[0].busy) {
- wl_buffer_destroy(window->buffers[0].buffer);
- window->buffers[0].buffer = NULL;
- window->wait_for_configure = false;
- }
+ if (window->needs_update_buffer) {
+ int i;
+
+ for (i = 0; i < MAX_BUFFER_ALLOC; i++)
+ alloc_buffer(window, window->width, window->height);
+
+ window->needs_update_buffer = false;
}
- if (!window->buffers[0].busy) {
- buffer = &window->buffers[0];
- } else if (!window->buffers[1].busy) {
- buffer = &window->buffers[1];
- } else {
+ buffer = pick_free_buffer(window);
+ if (!buffer)
return NULL;
- }
+
if (!buffer->buffer) {
ret = create_shm_buffer(window->display, buffer, window->width,
@@ -197,31 +254,33 @@ static const struct wl_callback_listener frame_listener = {
static void
redraw(void *data, struct wl_callback *callback, uint32_t time)
{
- struct window *window = static_cast<struct window *>(data);
- struct buffer *buffer;
+ struct window *window = static_cast<struct window *>(data);
+ struct buffer *buffer;
+
+ prune_old_released_buffers(window);
- buffer = get_next_buffer(window);
- if (!buffer) {
- fprintf(stderr,
- !callback ? "Failed to create the first buffer.\n" :
- "Both buffers busy at redraw(). Server bug?\n");
- abort();
- }
+ buffer = get_next_buffer(window);
+ if (!buffer) {
+ fprintf(stderr,
+ !callback ? "Failed to create the first buffer.\n" :
+ "Both buffers busy at redraw(). Server bug?\n");
+ abort();
+ }
// do the actual painting
paint_pixels(buffer->shm_data, 0x0, window->width, window->height, time);
- wl_surface_attach(window->surface, buffer->buffer, 0, 0);
- wl_surface_damage(window->surface, 0, 0, window->width, window->height);
+ wl_surface_attach(window->surface, buffer->buffer, 0, 0);
+ wl_surface_damage(window->surface, 0, 0, window->width, window->height);
- if (callback)
- wl_callback_destroy(callback);
+ if (callback)
+ wl_callback_destroy(callback);
- window->callback = wl_surface_frame(window->surface);
- wl_callback_add_listener(window->callback, &frame_listener, window);
- wl_surface_commit(window->surface);
+ window->callback = wl_surface_frame(window->surface);
+ wl_callback_add_listener(window->callback, &frame_listener, window);
+ wl_surface_commit(window->surface);
- buffer->busy = 1;
+ buffer->busy = 1;
}
static void
@@ -449,6 +508,7 @@ handle_xdg_surface_configure(void *data, struct xdg_surface *surface, uint32_t s
if (window->wait_for_configure) {
redraw(window, NULL, 0);
+ window->wait_for_configure = false;
}
}
@@ -482,29 +542,18 @@ handle_xdg_toplevel_configure(void *data, struct xdg_toplevel *xdg_toplevel,
if (width > 0 && height > 0) {
if (!window->fullscreen && !window->maximized) {
- window->width = width;
- window->height = height;
+ window->init_width = width;
+ window->init_height = height;
}
window->width = width;
window->height = height;
} else if (!window->fullscreen && !window->maximized) {
- if (width == 0)
- window->width = WINDOW_WIDTH_SIZE;
- else
- window->width = width;
-
- if (height == 0)
- window->height = WINDOW_HEIGHT_SIZE;
- else
- window->height = height;
+ window->width = window->init_width;
+ window->height = window->init_height;
}
- /* if we've been resized set wait_for_configure to adjust the fb size
- * in the frame callback handler, which will also clear this up */
- if ((window->width > 0 && window->width != WINDOW_WIDTH_SIZE) &&
- (window->height > 0 && window->height != WINDOW_HEIGHT_SIZE)) {
- window->wait_for_configure = true;
- }
+ window->needs_update_buffer = true;
+
}
static void
@@ -522,6 +571,7 @@ static struct window *
create_window(struct display *display, int width, int height, const char *app_id)
{
struct window *window;
+ int i;
assert(display->wm_base != NULL);
@@ -529,6 +579,7 @@ create_window(struct display *display, int width, int height, const char *app_id
if (!window)
return NULL;
+ wl_list_init(&window->buffer_list);
window->callback = NULL;
window->display = display;
window->width = width;
@@ -554,6 +605,9 @@ create_window(struct display *display, int width, int height, const char *app_id
window->wait_for_configure = true;
}
+ for (i = 0; i < MAX_BUFFER_ALLOC; i++)
+ alloc_buffer(window, window->width, window->height);
+
return window;
}
@@ -561,13 +615,14 @@ create_window(struct display *display, int width, int height, const char *app_id
static void
destroy_window(struct window *window)
{
+ struct buffer *buffer, *buffer_next;
+
if (window->callback)
wl_callback_destroy(window->callback);
- if (window->buffers[0].buffer)
- wl_buffer_destroy(window->buffers[0].buffer);
- if (window->buffers[1].buffer)
- wl_buffer_destroy(window->buffers[1].buffer);
+ wl_list_for_each_safe(buffer, buffer_next,
+ &window->buffer_list, buffer_link)
+ destroy_buffer(buffer);
if (window->xdg_toplevel)
xdg_toplevel_destroy(window->xdg_toplevel);