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:
parent
1674ca725c
commit
6595db6409
|
@ -404,8 +404,8 @@ static bool drm_connector_commit_buffer(struct wlr_output *output) {
|
||||||
|
|
||||||
conn->pageflip_pending = true;
|
conn->pageflip_pending = true;
|
||||||
if (output->pending.buffer_type == WLR_OUTPUT_STATE_BUFFER_SCANOUT) {
|
if (output->pending.buffer_type == WLR_OUTPUT_STATE_BUFFER_SCANOUT) {
|
||||||
wlr_buffer_unref(conn->pending_buffer);
|
wlr_buffer_unlock(conn->pending_buffer);
|
||||||
conn->pending_buffer = wlr_buffer_ref(output->pending.buffer);
|
conn->pending_buffer = wlr_buffer_lock(output->pending.buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
wlr_output_update_enabled(output, true);
|
wlr_output_update_enabled(output, true);
|
||||||
|
@ -1539,7 +1539,7 @@ static void page_flip_handler(int fd, unsigned seq,
|
||||||
|
|
||||||
// Release the old buffer as it's not displayed anymore. The pending
|
// Release the old buffer as it's not displayed anymore. The pending
|
||||||
// buffer becomes the current buffer.
|
// buffer becomes the current buffer.
|
||||||
wlr_buffer_unref(conn->current_buffer);
|
wlr_buffer_unlock(conn->current_buffer);
|
||||||
conn->current_buffer = conn->pending_buffer;
|
conn->current_buffer = conn->pending_buffer;
|
||||||
conn->pending_buffer = NULL;
|
conn->pending_buffer = NULL;
|
||||||
|
|
||||||
|
@ -1660,8 +1660,8 @@ static void drm_connector_cleanup(struct wlr_drm_connector *conn) {
|
||||||
conn->output.needs_frame = false;
|
conn->output.needs_frame = false;
|
||||||
conn->output.frame_pending = false;
|
conn->output.frame_pending = false;
|
||||||
|
|
||||||
wlr_buffer_unref(conn->pending_buffer);
|
wlr_buffer_unlock(conn->pending_buffer);
|
||||||
wlr_buffer_unref(conn->current_buffer);
|
wlr_buffer_unlock(conn->current_buffer);
|
||||||
conn->pending_buffer = conn->current_buffer = NULL;
|
conn->pending_buffer = conn->current_buffer = NULL;
|
||||||
|
|
||||||
/* Fallthrough */
|
/* Fallthrough */
|
||||||
|
|
|
@ -114,7 +114,7 @@ static void destroy_wl_buffer(struct wlr_wl_buffer *buffer) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
wl_buffer_destroy(buffer->wl_buffer);
|
wl_buffer_destroy(buffer->wl_buffer);
|
||||||
wlr_buffer_unref(buffer->buffer);
|
wlr_buffer_unlock(buffer->buffer);
|
||||||
free(buffer);
|
free(buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -173,7 +173,7 @@ static struct wlr_wl_buffer *create_wl_buffer(struct wlr_wl_backend *wl,
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
buffer->wl_buffer = wl_buffer;
|
buffer->wl_buffer = wl_buffer;
|
||||||
buffer->buffer = wlr_buffer_ref(wlr_buffer);
|
buffer->buffer = wlr_buffer_lock(wlr_buffer);
|
||||||
|
|
||||||
wl_buffer_add_listener(wl_buffer, &buffer_listener, buffer);
|
wl_buffer_add_listener(wl_buffer, &buffer_listener, buffer);
|
||||||
|
|
||||||
|
|
|
@ -21,27 +21,49 @@ struct wlr_buffer_impl {
|
||||||
struct wlr_dmabuf_attributes *attribs);
|
struct wlr_dmabuf_attributes *attribs);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A buffer containing pixel data.
|
||||||
|
*
|
||||||
|
* A buffer has a single producer (the party who created the buffer) and
|
||||||
|
* multiple consumers (parties reading the buffer). When all consumers are done
|
||||||
|
* with the buffer, it gets released and can be re-used by the producer. When
|
||||||
|
* the producer and all consumers are done with the buffer, it gets destroyed.
|
||||||
|
*/
|
||||||
struct wlr_buffer {
|
struct wlr_buffer {
|
||||||
const struct wlr_buffer_impl *impl;
|
const struct wlr_buffer_impl *impl;
|
||||||
|
|
||||||
size_t n_refs;
|
bool dropped;
|
||||||
|
size_t n_locks;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
struct wl_signal destroy;
|
struct wl_signal destroy;
|
||||||
|
struct wl_signal release;
|
||||||
} events;
|
} events;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize a buffer. This function should be called by producers. The
|
||||||
|
* initialized buffer is referenced: once the producer is done with the buffer
|
||||||
|
* they should call wlr_buffer_drop.
|
||||||
|
*/
|
||||||
void wlr_buffer_init(struct wlr_buffer *buffer,
|
void wlr_buffer_init(struct wlr_buffer *buffer,
|
||||||
const struct wlr_buffer_impl *impl);
|
const struct wlr_buffer_impl *impl);
|
||||||
/**
|
/**
|
||||||
* Reference the buffer.
|
* Unreference the buffer. This function should be called by producers when
|
||||||
|
* they are done with the buffer.
|
||||||
*/
|
*/
|
||||||
struct wlr_buffer *wlr_buffer_ref(struct wlr_buffer *buffer);
|
void wlr_buffer_drop(struct wlr_buffer *buffer);
|
||||||
/**
|
/**
|
||||||
* Unreference the buffer. After this call, `buffer` may not be accessed
|
* Lock the buffer. This function should be called by consumers to make
|
||||||
* anymore.
|
* sure the buffer can be safely read from. Once the consumer is done with the
|
||||||
|
* buffer, they should call wlr_buffer_unlock.
|
||||||
*/
|
*/
|
||||||
void wlr_buffer_unref(struct wlr_buffer *buffer);
|
struct wlr_buffer *wlr_buffer_lock(struct wlr_buffer *buffer);
|
||||||
|
/**
|
||||||
|
* Unlock the buffer. This function should be called by consumers once they are
|
||||||
|
* done with the buffer.
|
||||||
|
*/
|
||||||
|
void wlr_buffer_unlock(struct wlr_buffer *buffer);
|
||||||
/**
|
/**
|
||||||
* Reads the DMA-BUF attributes of the buffer. If this buffer isn't a DMA-BUF,
|
* Reads the DMA-BUF attributes of the buffer. If this buffer isn't a DMA-BUF,
|
||||||
* returns false.
|
* returns false.
|
||||||
|
@ -70,6 +92,7 @@ struct wlr_client_buffer {
|
||||||
struct wlr_texture *texture;
|
struct wlr_texture *texture;
|
||||||
|
|
||||||
struct wl_listener resource_destroy;
|
struct wl_listener resource_destroy;
|
||||||
|
struct wl_listener release;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct wlr_renderer;
|
struct wlr_renderer;
|
||||||
|
@ -84,9 +107,11 @@ bool wlr_resource_is_buffer(struct wl_resource *resource);
|
||||||
bool wlr_resource_get_buffer_size(struct wl_resource *resource,
|
bool wlr_resource_get_buffer_size(struct wl_resource *resource,
|
||||||
struct wlr_renderer *renderer, int *width, int *height);
|
struct wlr_renderer *renderer, int *width, int *height);
|
||||||
/**
|
/**
|
||||||
* Upload a buffer to the GPU and reference it.
|
* Import a client buffer and lock it.
|
||||||
|
*
|
||||||
|
* Once the caller is done with the buffer, they must call wlr_buffer_unlock.
|
||||||
*/
|
*/
|
||||||
struct wlr_client_buffer *wlr_client_buffer_create(
|
struct wlr_client_buffer *wlr_client_buffer_import(
|
||||||
struct wlr_renderer *renderer, struct wl_resource *resource);
|
struct wlr_renderer *renderer, struct wl_resource *resource);
|
||||||
/**
|
/**
|
||||||
* Try to update the buffer's content. On success, returns the updated buffer
|
* Try to update the buffer's content. On success, returns the updated buffer
|
||||||
|
|
|
@ -10,23 +10,12 @@ void wlr_buffer_init(struct wlr_buffer *buffer,
|
||||||
const struct wlr_buffer_impl *impl) {
|
const struct wlr_buffer_impl *impl) {
|
||||||
assert(impl->destroy);
|
assert(impl->destroy);
|
||||||
buffer->impl = impl;
|
buffer->impl = impl;
|
||||||
buffer->n_refs = 1;
|
|
||||||
wl_signal_init(&buffer->events.destroy);
|
wl_signal_init(&buffer->events.destroy);
|
||||||
|
wl_signal_init(&buffer->events.release);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct wlr_buffer *wlr_buffer_ref(struct wlr_buffer *buffer) {
|
static void buffer_consider_destroy(struct wlr_buffer *buffer) {
|
||||||
buffer->n_refs++;
|
if (!buffer->dropped || buffer->n_locks > 0) {
|
||||||
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) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -35,6 +24,36 @@ void wlr_buffer_unref(struct wlr_buffer *buffer) {
|
||||||
buffer->impl->destroy(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,
|
bool wlr_buffer_get_dmabuf(struct wlr_buffer *buffer,
|
||||||
struct wlr_dmabuf_attributes *attribs) {
|
struct wlr_dmabuf_attributes *attribs) {
|
||||||
if (!buffer->impl->get_dmabuf) {
|
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.
|
// 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) {
|
struct wlr_renderer *renderer, struct wl_resource *resource) {
|
||||||
assert(wlr_resource_is_buffer(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);
|
wl_resource_add_destroy_listener(resource, &buffer->resource_destroy);
|
||||||
buffer->resource_destroy.notify = client_buffer_resource_handle_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;
|
return buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -206,7 +242,7 @@ struct wlr_client_buffer *wlr_client_buffer_apply_damage(
|
||||||
pixman_region32_t *damage) {
|
pixman_region32_t *damage) {
|
||||||
assert(wlr_resource_is_buffer(resource));
|
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
|
// Someone else still has a reference to the buffer
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
|
@ -429,7 +429,7 @@ static void output_state_clear_buffer(struct wlr_output_state *state) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
wlr_buffer_unref(state->buffer);
|
wlr_buffer_unlock(state->buffer);
|
||||||
state->buffer = NULL;
|
state->buffer = NULL;
|
||||||
|
|
||||||
state->committed &= ~WLR_OUTPUT_STATE_BUFFER;
|
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_state_clear_buffer(&output->pending);
|
||||||
output->pending.committed |= WLR_OUTPUT_STATE_BUFFER;
|
output->pending.committed |= WLR_OUTPUT_STATE_BUFFER;
|
||||||
output->pending.buffer_type = WLR_OUTPUT_STATE_BUFFER_SCANOUT;
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -283,7 +283,7 @@ static void surface_apply_damage(struct wlr_surface *surface) {
|
||||||
if (resource == NULL) {
|
if (resource == NULL) {
|
||||||
// NULL commit
|
// NULL commit
|
||||||
if (surface->buffer != NULL) {
|
if (surface->buffer != NULL) {
|
||||||
wlr_buffer_unref(&surface->buffer->base);
|
wlr_buffer_unlock(&surface->buffer->base);
|
||||||
}
|
}
|
||||||
surface->buffer = NULL;
|
surface->buffer = NULL;
|
||||||
return;
|
return;
|
||||||
|
@ -300,14 +300,14 @@ static void surface_apply_damage(struct wlr_surface *surface) {
|
||||||
}
|
}
|
||||||
|
|
||||||
struct wlr_client_buffer *buffer =
|
struct wlr_client_buffer *buffer =
|
||||||
wlr_client_buffer_create(surface->renderer, resource);
|
wlr_client_buffer_import(surface->renderer, resource);
|
||||||
if (buffer == NULL) {
|
if (buffer == NULL) {
|
||||||
wlr_log(WLR_ERROR, "Failed to upload buffer");
|
wlr_log(WLR_ERROR, "Failed to upload buffer");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (surface->buffer != NULL) {
|
if (surface->buffer != NULL) {
|
||||||
wlr_buffer_unref(&surface->buffer->base);
|
wlr_buffer_unlock(&surface->buffer->base);
|
||||||
}
|
}
|
||||||
surface->buffer = buffer;
|
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->opaque_region);
|
||||||
pixman_region32_fini(&surface->input_region);
|
pixman_region32_fini(&surface->input_region);
|
||||||
if (surface->buffer != NULL) {
|
if (surface->buffer != NULL) {
|
||||||
wlr_buffer_unref(&surface->buffer->base);
|
wlr_buffer_unlock(&surface->buffer->base);
|
||||||
}
|
}
|
||||||
free(surface);
|
free(surface);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue