Skip to content
This repository has been archived by the owner on Nov 1, 2021. It is now read-only.

Add back wlr_buffer #1062

Merged
merged 5 commits into from
Jun 16, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 63 additions & 0 deletions include/wlr/types/wlr_buffer.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
#ifndef WLR_TYPES_WLR_BUFFER_H
#define WLR_TYPES_WLR_BUFFER_H

#include <pixman.h>
#include <wayland-server.h>

/**
* A client buffer.
*/
struct wlr_buffer {
/**
* The buffer resource, if any. Will be NULL if the client destroys it.
*/
struct wl_resource *resource;
/**
* The buffer's texture, if any. A buffer will not have a texture if the
* client destroys the buffer before it has been released.
*/
struct wlr_texture *texture;
bool released;
size_t n_refs;

struct wl_listener resource_destroy;
};

struct wlr_renderer;

/**
* Check if a resource is a wl_buffer resource.
*/
bool wlr_resource_is_buffer(struct wl_resource *resource);
/**
* Get the size of a wl_buffer resource.
*/
bool wlr_buffer_get_resource_size(struct wl_resource *resource,
struct wlr_renderer *renderer, int *width, int *height);

/**
* Upload a buffer to the GPU and reference it.
*/
struct wlr_buffer *wlr_buffer_create(struct wlr_renderer *renderer,
struct wl_resource *resource);
/**
* Reference the buffer.
*/
struct wlr_buffer *wlr_buffer_ref(struct wlr_buffer *buffer);
/**
* Unreference the buffer. After this call, `buffer` may not be accessed
* anymore.
*/
void wlr_buffer_unref(struct wlr_buffer *buffer);
/**
* Try to update the buffer's content. On success, returns the updated buffer
* and destroys the provided `buffer`. On error, `buffer` is intact and NULL is
* returned.
*
* Fails if there's more than one reference to the buffer or if the texture
* isn't mutable.
*/
struct wlr_buffer *wlr_buffer_apply_damage(struct wlr_buffer *buffer,
struct wl_resource *resource, pixman_region32_t *damage);

#endif
15 changes: 14 additions & 1 deletion include/wlr/types/wlr_surface.h
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,13 @@ struct wlr_subsurface {
struct wlr_surface {
struct wl_resource *resource;
struct wlr_renderer *renderer;
struct wlr_texture *texture;
/**
* The surface's buffer, if any. A surface has an attached buffer when it
* commits with a non-null buffer in its pending state. A surface will not
* have a buffer if it has never committed one, has committed a null buffer,
* or something went wrong with uploading the buffer.
*/
struct wlr_buffer *buffer;
struct wlr_surface_state *current, *pending;
const char *role; // the lifetime-bound role or null

Expand Down Expand Up @@ -124,6 +130,13 @@ int wlr_surface_set_role(struct wlr_surface *surface, const char *role,
*/
bool wlr_surface_has_buffer(struct wlr_surface *surface);

/**
* Get the texture of the buffer currently attached to this surface. Returns
* NULL if no buffer is currently attached or if something went wrong with
* uploading the buffer.
*/
struct wlr_texture *wlr_surface_get_texture(struct wlr_surface *surface);

/**
* Create a new subsurface resource with the provided new ID. If `resource_list`
* is non-NULL, adds the subsurface's resource to the list.
Expand Down
6 changes: 3 additions & 3 deletions rootston/output.c
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,8 @@ static void render_surface(struct wlr_surface *surface, int sx, int sy,
struct roots_output *output = data->output;
float rotation = data->layout.rotation;

if (!wlr_surface_has_buffer(surface)) {
struct wlr_texture *texture = wlr_surface_get_texture(surface);
if (texture == NULL) {
return;
}

Expand Down Expand Up @@ -230,8 +231,7 @@ static void render_surface(struct wlr_surface *surface, int sx, int sy,
pixman_box32_t *rects = pixman_region32_rectangles(&damage, &nrects);
for (int i = 0; i < nrects; ++i) {
scissor_output(output, &rects[i]);
wlr_render_texture_with_matrix(renderer, surface->texture, matrix,
data->alpha);
wlr_render_texture_with_matrix(renderer, texture, matrix, data->alpha);
}

damage_finish:
Expand Down
1 change: 1 addition & 0 deletions types/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ lib_wlr_types = static_library(
'xdg_shell_v6/wlr_xdg_surface_v6.c',
'xdg_shell_v6/wlr_xdg_toplevel_v6.c',
'wlr_box.c',
'wlr_buffer.c',
'wlr_compositor.c',
'wlr_cursor.c',
'wlr_gamma_control.c',
Expand Down
193 changes: 193 additions & 0 deletions types/wlr_buffer.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
#include <assert.h>
#include <stdlib.h>
#include <wlr/types/wlr_buffer.h>
#include <wlr/types/wlr_linux_dmabuf.h>
#include <wlr/render/wlr_renderer.h>
#include <wlr/util/log.h>

bool wlr_resource_is_buffer(struct wl_resource *resource) {
return strcmp(wl_resource_get_class(resource), wl_buffer_interface.name) == 0;
}

bool wlr_buffer_get_resource_size(struct wl_resource *resource,
struct wlr_renderer *renderer, int *width, int *height) {
assert(wlr_resource_is_buffer(resource));

struct wl_shm_buffer *shm_buf = wl_shm_buffer_get(resource);
if (shm_buf != NULL) {
*width = wl_shm_buffer_get_width(shm_buf);
*height = wl_shm_buffer_get_height(shm_buf);
} else if (wlr_renderer_resource_is_wl_drm_buffer(renderer,
resource)) {
wlr_renderer_wl_drm_buffer_get_size(renderer, resource,
width, height);
} else if (wlr_dmabuf_resource_is_buffer(resource)) {
struct wlr_dmabuf_buffer *dmabuf =
wlr_dmabuf_buffer_from_buffer_resource(resource);
*width = dmabuf->attributes.width;
*height = dmabuf->attributes.height;
} else {
*width = *height = 0;
return false;
}

return true;
}


static void buffer_resource_handle_destroy(struct wl_listener *listener,
void *data) {
struct wlr_buffer *buffer =
wl_container_of(listener, buffer, resource_destroy);
wl_list_remove(&buffer->resource_destroy.link);
wl_list_init(&buffer->resource_destroy.link);
buffer->resource = NULL;

// At this point, if the wl_buffer comes from linux-dmabuf or wl_drm, we
// still haven't released it (ie. we'll read it in the future) but the
// client destroyed it. Reading the texture itself should be fine because
// we still hold a reference to the DMA-BUF via the texture. However the
// client could decide to re-use the same DMA-BUF for something else, in
// which case we'll read garbage. We decide to accept this risk.
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should the comment mention that it's a protocol violation on the client side, to reuse the buffers in this case?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, it's not clear that it's a protocol violation.

}

struct wlr_buffer *wlr_buffer_create(struct wlr_renderer *renderer,
struct wl_resource *resource) {
assert(wlr_resource_is_buffer(resource));

struct wlr_texture *texture = NULL;
bool released = false;

struct wl_shm_buffer *shm_buf = wl_shm_buffer_get(resource);
if (shm_buf != NULL) {
enum wl_shm_format fmt = wl_shm_buffer_get_format(shm_buf);
int32_t stride = wl_shm_buffer_get_stride(shm_buf);
int32_t width = wl_shm_buffer_get_width(shm_buf);
int32_t height = wl_shm_buffer_get_height(shm_buf);

wl_shm_buffer_begin_access(shm_buf);
void *data = wl_shm_buffer_get_data(shm_buf);
texture = wlr_texture_from_pixels(renderer, fmt, stride,
width, height, data);
wl_shm_buffer_end_access(shm_buf);

// We have uploaded the data, we don't need to access the wl_buffer
// anymore
wl_buffer_send_release(resource);
released = true;
} else if (wlr_renderer_resource_is_wl_drm_buffer(renderer, resource)) {
texture = wlr_texture_from_wl_drm(renderer, resource);
} else if (wlr_dmabuf_resource_is_buffer(resource)) {
struct wlr_dmabuf_buffer *dmabuf =
wlr_dmabuf_buffer_from_buffer_resource(resource);
texture = wlr_texture_from_dmabuf(renderer, &dmabuf->attributes);

// We have imported the DMA-BUF, but we need to prevent the client from
// re-using the same DMA-BUF for the next frames, so we don't release
// the buffer yet.
} else {
wlr_log(L_ERROR, "Cannot upload texture: unknown buffer type");
return NULL;
}

if (texture == NULL) {
wlr_log(L_ERROR, "Failed to upload texture");
return NULL;
}

struct wlr_buffer *buffer = calloc(1, sizeof(struct wlr_buffer));
if (buffer == NULL) {
wlr_texture_destroy(texture);
return NULL;
}
buffer->resource = resource;
buffer->texture = texture;
buffer->released = released;
buffer->n_refs = 1;

wl_resource_add_destroy_listener(resource, &buffer->resource_destroy);
buffer->resource_destroy.notify = buffer_resource_handle_destroy;

return buffer;
}

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) {
return;
}

if (!buffer->released && buffer->resource != NULL) {
wl_buffer_send_release(buffer->resource);
}

wl_list_remove(&buffer->resource_destroy.link);
wlr_texture_destroy(buffer->texture);
free(buffer);
}

struct wlr_buffer *wlr_buffer_apply_damage(struct wlr_buffer *buffer,
struct wl_resource *resource, pixman_region32_t *damage) {
assert(wlr_resource_is_buffer(resource));

if (buffer->n_refs > 1) {
// Someone else still has a reference to the buffer
return NULL;
}

struct wl_shm_buffer *shm_buf = wl_shm_buffer_get(resource);
if (shm_buf == NULL) {
// Uploading only damaged regions only works for wl_shm buffers
return NULL;
}

enum wl_shm_format fmt = wl_shm_buffer_get_format(shm_buf);
int32_t stride = wl_shm_buffer_get_stride(shm_buf);
int32_t width = wl_shm_buffer_get_width(shm_buf);
int32_t height = wl_shm_buffer_get_height(shm_buf);

int32_t texture_width, texture_height;
wlr_texture_get_size(buffer->texture, &texture_width, &texture_height);
if (width != texture_width || height != texture_height) {
return NULL;
}

wl_shm_buffer_begin_access(shm_buf);
void *data = wl_shm_buffer_get_data(shm_buf);

int n;
pixman_box32_t *rects = pixman_region32_rectangles(damage, &n);
for (int i = 0; i < n; ++i) {
pixman_box32_t *r = &rects[i];
if (!wlr_texture_write_pixels(buffer->texture, fmt, stride,
r->x2 - r->x1, r->y2 - r->y1, r->x1, r->y1,
r->x1, r->y1, data)) {
wl_shm_buffer_end_access(shm_buf);
return NULL;
}
}

wl_shm_buffer_end_access(shm_buf);

// We have uploaded the data, we don't need to access the wl_buffer
// anymore
wl_buffer_send_release(resource);

wl_list_remove(&buffer->resource_destroy.link);
wl_resource_add_destroy_listener(resource, &buffer->resource_destroy);
buffer->resource_destroy.notify = buffer_resource_handle_destroy;

buffer->resource = resource;
buffer->released = true;
return buffer;
}
10 changes: 5 additions & 5 deletions types/wlr_output.c
Original file line number Diff line number Diff line change
Expand Up @@ -367,7 +367,8 @@ static void output_fullscreen_surface_render(struct wlr_output *output,
struct wlr_renderer *renderer = wlr_backend_get_renderer(output->backend);
assert(renderer);

if (!wlr_surface_has_buffer(surface)) {
struct wlr_texture *texture = wlr_surface_get_texture(surface);
if (texture == NULL) {
wlr_renderer_clear(renderer, (float[]){0, 0, 0, 1});
return;
}
Expand All @@ -386,8 +387,7 @@ static void output_fullscreen_surface_render(struct wlr_output *output,
for (int i = 0; i < nrects; ++i) {
output_scissor(output, &rects[i]);
wlr_renderer_clear(renderer, (float[]){0, 0, 0, 1});
wlr_render_texture_with_matrix(surface->renderer, surface->texture,
matrix, 1.0f);
wlr_render_texture_with_matrix(surface->renderer, texture, matrix, 1.0f);
}
wlr_renderer_scissor(renderer, NULL);

Expand Down Expand Up @@ -418,7 +418,7 @@ static void output_cursor_render(struct wlr_output_cursor *cursor,

struct wlr_texture *texture = cursor->texture;
if (cursor->surface != NULL) {
texture = cursor->surface->texture;
texture = wlr_surface_get_texture(cursor->surface);
}
if (texture == NULL) {
return;
Expand Down Expand Up @@ -700,7 +700,7 @@ static bool output_cursor_attempt_hardware(struct wlr_output_cursor *cursor) {
enum wl_output_transform transform = WL_OUTPUT_TRANSFORM_NORMAL;
struct wlr_texture *texture = cursor->texture;
if (cursor->surface != NULL) {
texture = cursor->surface->texture;
texture = wlr_surface_get_texture(cursor->surface);
scale = cursor->surface->current->scale;
transform = cursor->surface->current->transform;
}
Expand Down
Loading