buffer: add a release event

Consumers call wlr_buffer_lock. Once all consumers are done with the
buffer, only the producer should have a reference to the buffer. In this
case, we can release the buffer (and let the producer re-use it).
This commit is contained in:
Simon Ser 2020-02-28 13:25:05 +01:00
parent 1674ca725c
commit 6595db6409
6 changed files with 98 additions and 37 deletions

View file

@ -10,23 +10,12 @@ void wlr_buffer_init(struct wlr_buffer *buffer,
const struct wlr_buffer_impl *impl) {
assert(impl->destroy);
buffer->impl = impl;
buffer->n_refs = 1;
wl_signal_init(&buffer->events.destroy);
wl_signal_init(&buffer->events.release);
}
struct wlr_buffer *wlr_buffer_ref(struct wlr_buffer *buffer) {
buffer->n_refs++;
return buffer;
}
void wlr_buffer_unref(struct wlr_buffer *buffer) {
if (buffer == NULL) {
return;
}
assert(buffer->n_refs > 0);
buffer->n_refs--;
if (buffer->n_refs > 0) {
static void buffer_consider_destroy(struct wlr_buffer *buffer) {
if (!buffer->dropped || buffer->n_locks > 0) {
return;
}
@ -35,6 +24,36 @@ void wlr_buffer_unref(struct wlr_buffer *buffer) {
buffer->impl->destroy(buffer);
}
void wlr_buffer_drop(struct wlr_buffer *buffer) {
if (buffer == NULL) {
return;
}
assert(!buffer->dropped);
buffer->dropped = true;
buffer_consider_destroy(buffer);
}
struct wlr_buffer *wlr_buffer_lock(struct wlr_buffer *buffer) {
buffer->n_locks++;
return buffer;
}
void wlr_buffer_unlock(struct wlr_buffer *buffer) {
if (buffer == NULL) {
return;
}
assert(buffer->n_locks > 0);
buffer->n_locks--;
if (buffer->n_locks == 0) {
wl_signal_emit(&buffer->events.release, NULL);
}
buffer_consider_destroy(buffer);
}
bool wlr_buffer_get_dmabuf(struct wlr_buffer *buffer,
struct wlr_dmabuf_attributes *attribs) {
if (!buffer->impl->get_dmabuf) {
@ -134,7 +153,17 @@ static void client_buffer_resource_handle_destroy(struct wl_listener *listener,
// which case we'll read garbage. We decide to accept this risk.
}
struct wlr_client_buffer *wlr_client_buffer_create(
static void client_buffer_handle_release(struct wl_listener *listener,
void *data) {
struct wlr_client_buffer *buffer =
wl_container_of(listener, buffer, release);
if (!buffer->resource_released && buffer->resource != NULL) {
wl_buffer_send_release(buffer->resource);
buffer->resource_released = true;
}
}
struct wlr_client_buffer *wlr_client_buffer_import(
struct wlr_renderer *renderer, struct wl_resource *resource) {
assert(wlr_resource_is_buffer(resource));
@ -198,6 +227,13 @@ struct wlr_client_buffer *wlr_client_buffer_create(
wl_resource_add_destroy_listener(resource, &buffer->resource_destroy);
buffer->resource_destroy.notify = client_buffer_resource_handle_destroy;
buffer->release.notify = client_buffer_handle_release;
wl_signal_add(&buffer->base.events.release, &buffer->release);
// Ensure the buffer will be released before being destroyed
wlr_buffer_lock(&buffer->base);
wlr_buffer_drop(&buffer->base);
return buffer;
}
@ -206,7 +242,7 @@ struct wlr_client_buffer *wlr_client_buffer_apply_damage(
pixman_region32_t *damage) {
assert(wlr_resource_is_buffer(resource));
if (buffer->base.n_refs > 1) {
if (buffer->base.n_locks > 1) {
// Someone else still has a reference to the buffer
return NULL;
}

View file

@ -429,7 +429,7 @@ static void output_state_clear_buffer(struct wlr_output_state *state) {
return;
}
wlr_buffer_unref(state->buffer);
wlr_buffer_unlock(state->buffer);
state->buffer = NULL;
state->committed &= ~WLR_OUTPUT_STATE_BUFFER;
@ -601,7 +601,7 @@ bool wlr_output_attach_buffer(struct wlr_output *output,
output_state_clear_buffer(&output->pending);
output->pending.committed |= WLR_OUTPUT_STATE_BUFFER;
output->pending.buffer_type = WLR_OUTPUT_STATE_BUFFER_SCANOUT;
output->pending.buffer = wlr_buffer_ref(buffer);
output->pending.buffer = wlr_buffer_lock(buffer);
return true;
}

View file

@ -283,7 +283,7 @@ static void surface_apply_damage(struct wlr_surface *surface) {
if (resource == NULL) {
// NULL commit
if (surface->buffer != NULL) {
wlr_buffer_unref(&surface->buffer->base);
wlr_buffer_unlock(&surface->buffer->base);
}
surface->buffer = NULL;
return;
@ -300,14 +300,14 @@ static void surface_apply_damage(struct wlr_surface *surface) {
}
struct wlr_client_buffer *buffer =
wlr_client_buffer_create(surface->renderer, resource);
wlr_client_buffer_import(surface->renderer, resource);
if (buffer == NULL) {
wlr_log(WLR_ERROR, "Failed to upload buffer");
return;
}
if (surface->buffer != NULL) {
wlr_buffer_unref(&surface->buffer->base);
wlr_buffer_unlock(&surface->buffer->base);
}
surface->buffer = buffer;
}
@ -580,7 +580,7 @@ static void surface_handle_resource_destroy(struct wl_resource *resource) {
pixman_region32_fini(&surface->opaque_region);
pixman_region32_fini(&surface->input_region);
if (surface->buffer != NULL) {
wlr_buffer_unref(&surface->buffer->base);
wlr_buffer_unlock(&surface->buffer->base);
}
free(surface);
}