Use a dynamic buffer pool

pts-frame-sched
Andri Yngvason 2023-10-01 12:32:45 +00:00
parent 338ccec704
commit 5f492da3d1
6 changed files with 291 additions and 45 deletions

View File

@ -0,0 +1,37 @@
/*
* Copyright (c) 2023 Andri Yngvason
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
#pragma once
#include "buffer.h"
#include <stdbool.h>
#include <stdint.h>
struct buffer_pool;
struct buffer;
struct buffer_pool* buffer_pool_create(enum buffer_type type, uint16_t width,
uint16_t height, uint32_t format, uint16_t stride, int scale);
void buffer_pool_destroy(struct buffer_pool* self);
bool buffer_pool_resize(struct buffer_pool* self, uint16_t width,
uint16_t height, uint32_t format, uint16_t stride, int scale);
struct buffer* buffer_pool_acquire(struct buffer_pool* self);
void buffer_pool_damage_all(struct buffer_pool* self,
struct pixman_region16* damage);

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2022 Andri Yngvason
* Copyright (c) 2022 - 2023 Andri Yngvason
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -16,6 +16,8 @@
#pragma once
#include "sys/queue.h"
#include <stdbool.h>
#include <unistd.h>
#include <stdint.h>
@ -31,6 +33,15 @@ enum buffer_type {
};
struct buffer {
int ref;
int hold;
LIST_ENTRY(buffer) registry_link;
TAILQ_ENTRY(buffer) pool_link;
void (*release_fn)(struct buffer*, void* ud);
void* release_ud;
enum buffer_type type;
int width, height;
@ -38,8 +49,6 @@ struct buffer {
size_t size;
uint32_t format;
struct wl_buffer* wl_buffer;
bool is_attached;
bool please_clean_up;
struct pixman_region16 damage;
// wl_shm:
@ -53,3 +62,12 @@ struct buffer {
struct buffer* buffer_create_shm(int width, int height, int stride, uint32_t format);
struct buffer* buffer_create_dmabuf(int width, int height, uint32_t format);
void buffer_destroy(struct buffer* self);
void buffer_ref(struct buffer*);
void buffer_unref(struct buffer*);
void buffer_set_release_fn(struct buffer*, void (*)(struct buffer*, void* ud),
void* userdata);
void buffer_hold(struct buffer*);
void buffer_release(struct buffer*);

View File

@ -77,6 +77,7 @@ sources = [
'src/renderer.c',
'src/renderer-egl.c',
'src/buffer.c',
'src/buffer-pool.c',
'src/open-h264.c',
'src/cursor.c',
'src/rfbproto.c',

163
src/buffer-pool.c 100644
View File

@ -0,0 +1,163 @@
/*
* Copyright (c) 2023 Andri Yngvason
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
#include "buffer-pool.h"
#include "buffer.h"
#include "sys/queue.h"
#include <stdlib.h>
#include <assert.h>
LIST_HEAD(buffer_list, buffer);
TAILQ_HEAD(bufferq, buffer);
struct buffer_pool {
int ref;
struct buffer_list registry;
struct bufferq buffers;
enum buffer_type type;
uint16_t width;
uint16_t height;
int32_t stride;
uint32_t format;
int scale;
};
struct buffer_pool* buffer_pool_create(enum buffer_type type, uint16_t width,
uint16_t height, uint32_t format, uint16_t stride, int scale)
{
struct buffer_pool* self = calloc(1, sizeof(*self));
if (!self)
return NULL;
self->ref = 1;
LIST_INIT(&self->registry);
TAILQ_INIT(&self->buffers);
self->type = type;
self->width = width;
self->height = height;
self->stride = stride;
self->format = format;
self->scale = scale;
return self;
}
static void buffer_pool__unref_buffers(struct buffer_pool* self)
{
while (!LIST_EMPTY(&self->registry)) {
struct buffer* buffer = LIST_FIRST(&self->registry);
LIST_REMOVE(buffer, registry_link);
buffer_set_release_fn(buffer, NULL, NULL);
buffer_unref(buffer);
}
TAILQ_INIT(&self->buffers);
}
void buffer_pool_destroy(struct buffer_pool* self)
{
buffer_pool__unref_buffers(self);
free(self);
}
bool buffer_pool_resize(struct buffer_pool* self, uint16_t width,
uint16_t height, uint32_t format, uint16_t stride, int scale)
{
if (width == self->width && height == self->height &&
format == self->format && stride == self->stride &&
scale == self->scale)
return false;
buffer_pool__unref_buffers(self);
self->width = width;
self->height = height;
self->stride = stride;
self->format = format;
self->scale = scale;
return true;
}
static void buffer_pool__on_buffer_release(struct buffer* buffer, void* userdata)
{
struct buffer_pool* self = userdata;
if (buffer->width != self->width || buffer->height != self->height ||
buffer->format != self->format ||
(buffer->type == BUFFER_WL_SHM && buffer->stride != self->stride) ||
buffer->scale != self->scale) {
buffer_unref(buffer);
} else {
TAILQ_INSERT_TAIL(&self->buffers, buffer, pool_link);
}
}
static struct buffer* buffer_pool__acquire_new(struct buffer_pool* self)
{
struct buffer* buffer;
switch (self->type) {
case BUFFER_WL_SHM:
buffer = buffer_create_shm(self->width, self->height,
self->stride, self->format);
break;
case BUFFER_DMABUF:
buffer = buffer_create_dmabuf(self->width, self->height,
self->format);
break;
case BUFFER_UNSPEC:
abort();
}
if (!buffer)
return NULL;
buffer->scale = self->scale;
LIST_INSERT_HEAD(&self->registry, buffer, registry_link);
buffer_set_release_fn(buffer, buffer_pool__on_buffer_release, self);
return buffer;
}
static struct buffer* buffer_pool__acquire_from_list(struct buffer_pool* self)
{
struct buffer* buffer = TAILQ_FIRST(&self->buffers);
assert(buffer);
TAILQ_REMOVE(&self->buffers, buffer, pool_link);
return buffer;
}
struct buffer* buffer_pool_acquire(struct buffer_pool* self)
{
return TAILQ_EMPTY(&self->buffers) ?
buffer_pool__acquire_new(self) :
buffer_pool__acquire_from_list(self);
}
void buffer_pool_damage_all(struct buffer_pool* self,
struct pixman_region16* damage)
{
struct buffer* buffer;
LIST_FOREACH(buffer, &self->registry, registry_link) {
pixman_region_union(&buffer->damage, damage, damage);
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2022 Andri Yngvason
* Copyright (c) 2022 - 2023 Andri Yngvason
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -25,24 +25,22 @@
#include <gbm.h>
#include <assert.h>
// TODO: Some buffers needn't be wayland buffers.
/* Origin: main.c */
extern struct wl_shm* wl_shm;
extern struct gbm_device* gbm_device;
extern struct zwp_linux_dmabuf_v1* zwp_linux_dmabuf_v1;
static void buffer_release(void* data, struct wl_buffer* wl_buffer)
static void buffer_wl_release(void* data, struct wl_buffer* wl_buffer)
{
(void)wl_buffer;
struct buffer* self = data;
self->is_attached = false;
if (self->please_clean_up) {
buffer_destroy(self);
}
buffer_release(self);
}
static const struct wl_buffer_listener buffer_listener = {
.release = buffer_release,
.release = buffer_wl_release,
};
struct buffer* buffer_create_shm(int width, int height, int stride,
@ -54,6 +52,7 @@ struct buffer* buffer_create_shm(int width, int height, int stride,
if (!self)
return NULL;
self->ref = 1;
self->type = BUFFER_WL_SHM;
self->width = width;
self->height = height;
@ -106,6 +105,7 @@ struct buffer* buffer_create_dmabuf(int width, int height, uint32_t format)
if (!self)
return NULL;
self->ref = 1;
self->type = BUFFER_DMABUF;
self->width = width;
self->height = height;
@ -159,9 +159,6 @@ void buffer_destroy(struct buffer* self)
if (!self)
return;
if (self->is_attached)
self->please_clean_up = true;
pixman_region_fini(&self->damage);
wl_buffer_destroy(self->wl_buffer);
@ -179,3 +176,41 @@ void buffer_destroy(struct buffer* self)
free(self);
}
void buffer_ref(struct buffer* self)
{
++self->ref;
}
void buffer_unref(struct buffer* self)
{
if (self && --self->ref == 0)
buffer_destroy(self);
}
void buffer_set_release_fn(struct buffer* self,
void (*fn)(struct buffer*, void* ud), void* userdata)
{
self->release_fn = fn;
self->release_ud = userdata;
}
void buffer_hold(struct buffer* self)
{
assert(self);
++self->hold;
}
void buffer_release(struct buffer* self)
{
assert(self);
int hold = --self->hold;
if (hold != 0)
return;
if (self->release_fn)
self->release_fn(self, self->release_ud);
else
buffer_unref(self);
}

View File

@ -42,6 +42,7 @@
#include "pixels.h"
#include "region.h"
#include "buffer.h"
#include "buffer-pool.h"
#include "renderer.h"
#include "renderer-egl.h"
#include "linux-dmabuf-unstable-v1.h"
@ -63,9 +64,8 @@ struct window {
struct xdg_surface* xdg_surface;
struct xdg_toplevel* xdg_toplevel;
struct buffer* buffers[3];
struct buffer_pool* buffers;
struct buffer* back_buffer;
int buffer_index;
struct pixman_region16 current_damage;
@ -243,8 +243,10 @@ void on_wayland_event(void* obj)
}
}
if (wl_display_dispatch_pending(wl_display) < 0)
if (wl_display_dispatch_pending(wl_display) < 0) {
fprintf(stderr, "Failed to dispatch pending\n");
abort();
}
}
static int init_wayland_event_handler(void)
@ -279,7 +281,7 @@ static int init_signal_handler(void)
static void window_attach(struct window* w, int x, int y)
{
w->back_buffer->is_attached = true;
buffer_hold(w->back_buffer);
wl_surface_attach(w->wl_surface, w->back_buffer->wl_buffer, x, y);
wl_surface_set_buffer_scale(window->wl_surface,
window->back_buffer->scale);
@ -370,8 +372,9 @@ static void window_commit(struct window* w)
static void window_swap(struct window* w)
{
w->buffer_index = (w->buffer_index + 1) % 3;
w->back_buffer = w->buffers[w->buffer_index];
buffer_unref(w->back_buffer);
w->back_buffer = buffer_pool_acquire(w->buffers);
buffer_ref(w->back_buffer);
}
static void window_damage(struct window* w, int x, int y, int width, int height)
@ -401,24 +404,19 @@ static void window_resize(struct window* w, int width, int height, int scale)
if (width == 0 || height == 0 || scale == 0)
return;
if (w->back_buffer && w->back_buffer->width == width &&
w->back_buffer->height == height &&
w->back_buffer->scale == scale)
return;
for (int i = 0; i < 3; ++i)
buffer_destroy(w->buffers[i]);
for (int i = 0; i < 3; ++i) {
w->buffers[i] = have_egl
? buffer_create_dmabuf(scale * width, scale * height,
dmabuf_format)
: buffer_create_shm(scale * width, scale * height,
scale * 4 * width, shm_format);
w->buffers[i]->scale = scale;
uint32_t format = have_egl ? dmabuf_format : shm_format;
if (!w->buffers) {
enum buffer_type type = have_egl ? BUFFER_DMABUF : BUFFER_WL_SHM;
w->buffers = buffer_pool_create(type, scale * width,
scale * height, format, scale * 4 * width,
scale);
} else {
buffer_pool_resize(w->buffers, scale * width, scale * height,
format, scale * 4 * width, scale);
}
w->back_buffer = w->buffers[0];
w->back_buffer = buffer_pool_acquire(w->buffers);
buffer_ref(w->back_buffer);
}
static void xdg_toplevel_configure(void* data, struct xdg_toplevel* toplevel,
@ -479,9 +477,8 @@ wl_surface_failure:
static void window_destroy(struct window* w)
{
for (int i = 0; i < 3; ++i)
buffer_destroy(w->buffers[i]);
buffer_unref(w->back_buffer);
buffer_pool_destroy(w->buffers);
free(w->vnc_fb);
xdg_toplevel_destroy(w->xdg_toplevel);
xdg_surface_destroy(w->xdg_surface);
@ -592,9 +589,7 @@ static void get_frame_damage(struct vnc_client* client,
static void apply_buffer_damage(struct pixman_region16* damage)
{
for (int i = 0; i < 3; ++i)
pixman_region_union(&window->buffers[i]->damage,
&window->buffers[i]->damage, damage);
buffer_pool_damage_all(window->buffers, damage);
}
static void window_damage_region(struct window* w,
@ -636,9 +631,6 @@ static void render_from_vnc(void)
if (window->is_frame_committed)
return;
if (window->back_buffer->is_attached)
fprintf(stderr, "Oops, back-buffer is still attached.\n");
window_attach(window, 0, 0);
double scale;