wayland: Move buffer use count to MetaWaylandSurface

Each wl_surface.commit with a newly attached buffer should result in
one wl_buffer.release for the attached buffer. For example attaching
the same buffer to two different surfaces must always result in two
wl_buffer.release events being emitted by the server. The client is
responsible for counting the wl_buffer.release events and be sure to
have received as many release events as it has attached and committed
the buffer, before reusing it.

https://bugzilla.gnome.org/show_bug.cgi?id=762828
This commit is contained in:
Jonas Ådahl 2016-03-16 14:47:53 +08:00
parent aa7bc501d5
commit 10a0114856
7 changed files with 117 additions and 99 deletions

View File

@ -136,7 +136,8 @@ meta_surface_actor_wayland_get_subsurface_rect (MetaSurfaceActorWayland *self,
MetaRectangle *rect)
{
MetaWaylandSurface *surface = meta_surface_actor_wayland_get_surface (self);
CoglTexture *texture = surface->buffer->texture;
MetaWaylandBuffer *buffer = meta_wayland_surface_get_buffer (surface);
CoglTexture *texture = buffer->texture;
MetaWindow *toplevel_window;
int monitor_scale;
float x, y;

View File

@ -726,7 +726,7 @@ client_window_should_be_mapped (MetaWindow *window)
{
#ifdef HAVE_WAYLAND
if (window->client_type == META_WINDOW_CLIENT_TYPE_WAYLAND &&
!window->surface->buffer)
!meta_wayland_surface_get_buffer (window->surface))
return FALSE;
#endif
@ -1554,7 +1554,7 @@ meta_window_should_be_showing (MetaWindow *window)
{
#ifdef HAVE_WAYLAND
if (window->client_type == META_WINDOW_CLIENT_TYPE_WAYLAND &&
!window->surface->buffer)
!meta_wayland_surface_get_buffer (window->surface))
return FALSE;
#endif

View File

@ -53,25 +53,6 @@ meta_wayland_buffer_destroy_handler (struct wl_listener *listener,
g_object_unref (buffer);
}
void
meta_wayland_buffer_ref_use_count (MetaWaylandBuffer *buffer)
{
g_warn_if_fail (buffer->resource);
buffer->use_count++;
}
void
meta_wayland_buffer_unref_use_count (MetaWaylandBuffer *buffer)
{
g_return_if_fail (buffer->use_count != 0);
buffer->use_count--;
if (buffer->use_count == 0 && buffer->resource)
wl_resource_queue_event (buffer->resource, WL_BUFFER_RELEASE);
}
MetaWaylandBuffer *
meta_wayland_buffer_from_resource (struct wl_resource *resource)
{
@ -106,7 +87,6 @@ meta_wayland_buffer_ensure_texture (MetaWaylandBuffer *buffer)
CoglTexture *texture;
struct wl_shm_buffer *shm_buffer;
g_return_val_if_fail (buffer->use_count != 0, NULL);
g_return_val_if_fail (buffer->resource, NULL);
if (buffer->texture)
@ -142,8 +122,6 @@ meta_wayland_buffer_process_damage (MetaWaylandBuffer *buffer,
{
struct wl_shm_buffer *shm_buffer;
g_return_if_fail (buffer->use_count != 0);
shm_buffer = wl_shm_buffer_get (buffer->resource);
if (shm_buffer)

View File

@ -39,7 +39,6 @@ struct _MetaWaylandBuffer
struct wl_listener destroy_listener;
CoglTexture *texture;
uint32_t use_count;
};
#define META_TYPE_WAYLAND_BUFFER (meta_wayland_buffer_get_type ())
@ -47,8 +46,6 @@ G_DECLARE_FINAL_TYPE (MetaWaylandBuffer, meta_wayland_buffer,
META, WAYLAND_BUFFER, GObject);
MetaWaylandBuffer * meta_wayland_buffer_from_resource (struct wl_resource *resource);
void meta_wayland_buffer_ref_use_count (MetaWaylandBuffer *buffer);
void meta_wayland_buffer_unref_use_count (MetaWaylandBuffer *buffer);
CoglTexture * meta_wayland_buffer_ensure_texture (MetaWaylandBuffer *buffer);
void meta_wayland_buffer_process_damage (MetaWaylandBuffer *buffer,
cairo_region_t *region);

View File

@ -966,24 +966,25 @@ update_cursor_sprite_texture (MetaWaylandSurface *surface)
MetaWaylandSurfaceRoleCursor *cursor_role =
META_WAYLAND_SURFACE_ROLE_CURSOR (surface->role);
MetaCursorSprite *cursor_sprite = cursor_role->cursor_sprite;
MetaWaylandBuffer *buffer = meta_wayland_surface_get_buffer (surface);
g_return_if_fail (!surface->buffer || surface->buffer->texture);
g_return_if_fail (!buffer || buffer->texture);
if (surface->buffer)
if (buffer)
{
meta_cursor_sprite_set_texture (cursor_sprite,
surface->buffer->texture,
buffer->texture,
cursor_role->hot_x * surface->scale,
cursor_role->hot_y * surface->scale);
if (surface->using_buffer)
if (surface->buffer_ref.use_count > 0)
{
struct wl_resource *buffer;
struct wl_resource *buffer_resource;
buffer = surface->buffer->resource;
buffer_resource = buffer->resource;
meta_cursor_renderer_realize_cursor_from_wl_buffer (cursor_renderer,
cursor_sprite,
buffer);
buffer_resource);
}
}
else

View File

@ -175,56 +175,29 @@ meta_wayland_surface_assign_role (MetaWaylandSurface *surface,
}
}
static void
surface_use_buffer (MetaWaylandSurface *surface)
{
g_return_if_fail (!surface->using_buffer);
meta_wayland_buffer_ref_use_count (surface->buffer);
surface->using_buffer = TRUE;
}
static void
surface_stop_using_buffer (MetaWaylandSurface *surface)
{
if (!surface->using_buffer)
return;
meta_wayland_buffer_unref_use_count (surface->buffer);
surface->using_buffer = FALSE;
}
static void
surface_set_buffer (MetaWaylandSurface *surface,
MetaWaylandBuffer *buffer)
{
if (surface->buffer == buffer)
return;
if (surface->buffer)
surface_stop_using_buffer (surface);
g_set_object (&surface->buffer, buffer);
}
static void
surface_process_damage (MetaWaylandSurface *surface,
cairo_region_t *region)
{
MetaWaylandBuffer *buffer = surface->buffer_ref.buffer;
unsigned int buffer_width;
unsigned int buffer_height;
cairo_rectangle_int_t surface_rect;
cairo_region_t *scaled_region;
int i, n_rectangles;
if (!surface->buffer)
/* If the client destroyed the buffer it attached before committing, but
* still posted damage, or posted damage without any buffer, don't try to
* process it on the non-existing buffer.
*/
if (!buffer)
return;
/* Intersect the damage region with the surface region before scaling in
* order to avoid integer overflow when scaling a damage region is too large
* (for example INT32_MAX which mesa passes). */
buffer_width = cogl_texture_get_width (surface->buffer->texture);
buffer_height = cogl_texture_get_height (surface->buffer->texture);
buffer_width = cogl_texture_get_width (buffer->texture);
buffer_height = cogl_texture_get_height (buffer->texture);
surface_rect = (cairo_rectangle_int_t) {
.width = buffer_width / surface->scale,
.height = buffer_height / surface->scale,
@ -236,7 +209,7 @@ surface_process_damage (MetaWaylandSurface *surface,
scaled_region = meta_region_scale (region, surface->scale);
/* First update the buffer. */
meta_wayland_buffer_process_damage (surface->buffer, scaled_region);
meta_wayland_buffer_process_damage (buffer, scaled_region);
/* Now damage the actor. The actor expects damage in the unscaled texture
* coordinate space, i.e. same as the buffer. */
@ -290,7 +263,7 @@ calculate_surface_window_geometry (MetaWaylandSurface *surface,
if (!CLUTTER_ACTOR_IS_VISIBLE (CLUTTER_ACTOR (surface_actor)))
return;
if (!surface->buffer)
if (!surface->buffer_ref.buffer)
return;
meta_surface_actor_wayland_get_subsurface_rect (surface_actor,
@ -326,6 +299,36 @@ destroy_window (MetaWaylandSurface *surface)
g_assert (surface->window == NULL);
}
MetaWaylandBuffer *
meta_wayland_surface_get_buffer (MetaWaylandSurface *surface)
{
return surface->buffer_ref.buffer;
}
void
meta_wayland_surface_ref_buffer_use_count (MetaWaylandSurface *surface)
{
g_return_if_fail (surface->buffer_ref.buffer);
g_warn_if_fail (surface->buffer_ref.buffer->resource);
surface->buffer_ref.use_count++;
}
void
meta_wayland_surface_unref_buffer_use_count (MetaWaylandSurface *surface)
{
MetaWaylandBuffer *buffer = surface->buffer_ref.buffer;
g_return_if_fail (surface->buffer_ref.use_count != 0);
surface->buffer_ref.use_count--;
g_return_if_fail (buffer);
if (surface->buffer_ref.use_count == 0 && buffer->resource)
wl_resource_queue_event (buffer->resource, WL_BUFFER_RELEASE);
}
static void
queue_surface_actor_frame_callbacks (MetaWaylandSurface *surface,
MetaWaylandPendingState *pending)
@ -344,6 +347,7 @@ toplevel_surface_commit (MetaWaylandSurfaceRole *surface_role,
{
MetaWaylandSurface *surface =
meta_wayland_surface_role_get_surface (surface_role);
MetaWaylandBuffer *buffer = surface->buffer_ref.buffer;
MetaWindow *window = surface->window;
queue_surface_actor_frame_callbacks (surface, pending);
@ -359,12 +363,12 @@ toplevel_surface_commit (MetaWaylandSurfaceRole *surface_role,
/* For wl_shell, it's equivalent to an unmap. Semantics
* are poorly defined, so we can choose some that are
* convenient for us. */
if (surface->buffer && !window)
if (buffer && !window)
{
window = meta_window_wayland_new (meta_get_display (), surface);
meta_wayland_surface_set_window (surface, window);
}
else if (surface->buffer == NULL && window)
else if (buffer == NULL && window)
{
destroy_window (surface);
return;
@ -372,7 +376,7 @@ toplevel_surface_commit (MetaWaylandSurfaceRole *surface_role,
}
else
{
if (surface->buffer == NULL)
if (buffer == NULL)
{
/* XDG surfaces can't commit NULL buffers */
wl_resource_post_error (surface->resource,
@ -390,7 +394,7 @@ toplevel_surface_commit (MetaWaylandSurfaceRole *surface_role,
{
MetaRectangle geom = { 0 };
CoglTexture *texture = surface->buffer->texture;
CoglTexture *texture = buffer->texture;
/* Update the buffer rect immediately. */
window->buffer_rect.width = cogl_texture_get_width (texture);
window->buffer_rect.height = cogl_texture_get_height (texture);
@ -559,7 +563,7 @@ subsurface_surface_commit (MetaWaylandSurfaceRole *surface_role,
queue_surface_actor_frame_callbacks (surface, pending);
if (surface->buffer != NULL)
if (surface->buffer_ref.buffer != NULL)
clutter_actor_show (CLUTTER_ACTOR (surface_actor));
else
clutter_actor_hide (CLUTTER_ACTOR (surface_actor));
@ -656,28 +660,46 @@ static void
apply_pending_state (MetaWaylandSurface *surface,
MetaWaylandPendingState *pending)
{
gboolean release_new_buffer = FALSE;
MetaSurfaceActorWayland *surface_actor_wayland =
META_SURFACE_ACTOR_WAYLAND (surface->surface_actor);
if (pending->newly_attached)
{
if (!surface->buffer && surface->window)
gboolean switched_buffer;
if (!surface->buffer_ref.buffer && surface->window)
meta_window_queue (surface->window, META_QUEUE_CALC_SHOWING);
surface_set_buffer (surface, pending->buffer);
/* Always release any previously held buffer. If the buffer held is same
* as the newly attached buffer, we still need to release it here, because
* wl_surface.attach+commit and wl_buffer.release on the attached buffer
* is symmetric.
*/
if (surface->buffer_held)
meta_wayland_surface_unref_buffer_use_count (surface);
if (pending->buffer && !surface->using_buffer)
switched_buffer = g_set_object (&surface->buffer_ref.buffer,
pending->buffer);
if (pending->buffer)
meta_wayland_surface_ref_buffer_use_count (surface);
if (switched_buffer && pending->buffer)
{
struct wl_shm_buffer *shm_buffer = wl_shm_buffer_get (pending->buffer->resource);
CoglTexture *texture;
surface_use_buffer (surface);
CoglTexture *texture = meta_wayland_buffer_ensure_texture (pending->buffer);
meta_surface_actor_wayland_set_texture (META_SURFACE_ACTOR_WAYLAND (surface->surface_actor), texture);
/* Release the buffer as soon as possible, so the client can reuse it
*/
if (shm_buffer)
release_new_buffer = TRUE;
texture = meta_wayland_buffer_ensure_texture (pending->buffer);
meta_surface_actor_wayland_set_texture (surface_actor_wayland,
texture);
}
/* If the newly attached buffer is going to be accessed directly without
* making a copy, such as an EGL buffer, mark it as in-use don't release
* it until is replaced by a subsequent wl_surface.commit or when the
* wl_surface is destroyed.
*/
surface->buffer_held = (pending->buffer &&
!wl_shm_buffer_get (pending->buffer->resource));
}
if (pending->scale > 0)
@ -686,8 +708,12 @@ apply_pending_state (MetaWaylandSurface *surface,
if (!cairo_region_is_empty (pending->damage))
surface_process_damage (surface, pending->damage);
if (release_new_buffer)
surface_stop_using_buffer (surface);
/* If we have a buffer that we are not using, decrease the use count so it may
* be released if no-one else has a use-reference to it.
*/
if (pending->newly_attached &&
!surface->buffer_held && surface->buffer_ref.buffer)
meta_wayland_surface_unref_buffer_use_count (surface);
surface->offset_x += pending->dx;
surface->offset_y += pending->dy;
@ -732,8 +758,7 @@ apply_pending_state (MetaWaylandSurface *surface,
pending_state_signals[PENDING_STATE_SIGNAL_APPLIED],
0);
meta_surface_actor_wayland_sync_state (
META_SURFACE_ACTOR_WAYLAND (surface->surface_actor));
meta_surface_actor_wayland_sync_state (surface_actor_wayland);
pending_state_reset (pending);
@ -1122,7 +1147,10 @@ wl_surface_destructor (struct wl_resource *resource)
if (surface->window)
destroy_window (surface);
surface_set_buffer (surface, NULL);
if (surface->buffer_held)
meta_wayland_surface_unref_buffer_use_count (surface);
g_clear_object (&surface->buffer_ref.buffer);
g_clear_object (&surface->pending);
if (surface->opaque_region)
@ -2771,10 +2799,10 @@ meta_wayland_surface_calculate_input_region (MetaWaylandSurface *surface)
cairo_rectangle_int_t buffer_rect;
CoglTexture *texture;
if (!surface->buffer)
if (!surface->buffer_ref.buffer)
return NULL;
texture = surface->buffer->texture;
texture = surface->buffer_ref.buffer->texture;
buffer_rect = (cairo_rectangle_int_t) {
.width = cogl_texture_get_width (texture) / surface->scale,
.height = cogl_texture_get_height (texture) / surface->scale,

View File

@ -151,8 +151,6 @@ struct _MetaWaylandSurface
MetaSurfaceActor *surface_actor;
MetaWaylandSurfaceRole *role;
MetaWindow *window;
MetaWaylandBuffer *buffer;
gboolean using_buffer;
cairo_region_t *input_region;
cairo_region_t *opaque_region;
int scale;
@ -160,6 +158,15 @@ struct _MetaWaylandSurface
GList *subsurfaces;
GHashTable *outputs_to_destroy_notify_id;
/* Buffer reference state. */
struct {
MetaWaylandBuffer *buffer;
unsigned int use_count;
} buffer_ref;
/* Buffer renderer state. */
gboolean buffer_held;
/* List of pending frame callbacks that needs to stay queued longer than one
* commit sequence, such as when it has not yet been assigned a role.
*/
@ -230,6 +237,12 @@ MetaWaylandSurface *meta_wayland_surface_create (MetaWaylandCompositor *composit
gboolean meta_wayland_surface_assign_role (MetaWaylandSurface *surface,
GType role_type);
MetaWaylandBuffer *meta_wayland_surface_get_buffer (MetaWaylandSurface *surface);
void meta_wayland_surface_ref_buffer_use_count (MetaWaylandSurface *surface);
void meta_wayland_surface_unref_buffer_use_count (MetaWaylandSurface *surface);
void meta_wayland_surface_set_window (MetaWaylandSurface *surface,
MetaWindow *window);