diff --git a/include/wlr/types/wlr_compositor.h b/include/wlr/types/wlr_compositor.h index a8577087d..a664f7484 100644 --- a/include/wlr/types/wlr_compositor.h +++ b/include/wlr/types/wlr_compositor.h @@ -93,6 +93,13 @@ struct wlr_surface { * The buffer position, in surface-local units. */ int sx, sy; + /** + * The surface's raster, if any. A surface has an attached raster when it + * commits with a non-null buffer in its pending state. A surface will not + * have a raster if it has never committed one or has committed a null + * buffer. + */ + struct wlr_raster *raster; /** * The last commit's buffer damage, in buffer-local coordinates. This * contains both the damage accumulated by the client via @@ -157,6 +164,9 @@ struct wlr_surface { } previous; bool opaque; + + struct wlr_raster *old_raster; + struct wl_listener raster_destroy; }; struct wlr_renderer; diff --git a/types/output/cursor.c b/types/output/cursor.c index 0193704e2..a2d53cd91 100644 --- a/types/output/cursor.c +++ b/types/output/cursor.c @@ -427,8 +427,8 @@ static void output_cursor_commit(struct wlr_output_cursor *cursor, struct wlr_surface *surface = cursor->surface; assert(surface != NULL); - if (surface->current.buffer) { - cursor->raster = wlr_raster_create(surface->current.buffer); + if (surface->raster) { + cursor->raster = wlr_raster_lock(surface->raster); } // Some clients commit a cursor surface with a NULL buffer to hide it. diff --git a/types/scene/surface.c b/types/scene/surface.c index d3ab7441b..a0cb4de77 100644 --- a/types/scene/surface.c +++ b/types/scene/surface.c @@ -66,10 +66,7 @@ static void set_raster_with_surface_state(struct wlr_scene_raster *scene_raster, wlr_scene_raster_set_dest_size(scene_raster, state->width, state->height); wlr_scene_raster_set_transform(scene_raster, state->transform); - struct wlr_raster *raster = NULL; - if (surface->current.buffer) { - raster = wlr_raster_create(surface->current.buffer); - } + struct wlr_raster *raster = surface->raster; if (raster) { wlr_scene_raster_set_raster_with_damage(scene_raster, @@ -77,8 +74,6 @@ static void set_raster_with_surface_state(struct wlr_scene_raster *scene_raster, } else { wlr_scene_raster_set_raster(scene_raster, NULL); } - - wlr_raster_unlock(raster); } static void handle_scene_surface_surface_commit( diff --git a/types/wlr_compositor.c b/types/wlr_compositor.c index fc00abb2f..46b0025c6 100644 --- a/types/wlr_compositor.c +++ b/types/wlr_compositor.c @@ -1,6 +1,7 @@ #include #include #include +#include #include #include #include @@ -415,8 +416,34 @@ static void surface_commit_state(struct wlr_surface *surface, surface_state_move(&surface->current, next); if (invalid_buffer) { + if (!surface->current.buffer) { + wlr_raster_unlock(surface->raster); + surface->raster = NULL; + } + + if (!surface->raster || surface->raster->buffer != surface->current.buffer) { + surface->old_raster = surface->raster; + + if (surface->current.buffer) { + surface->raster = wlr_raster_create(surface->current.buffer); + } + + if (surface->old_raster) { + // By the time we unlock the buffer next the buffer might + // get destroyed. We need to start listening here. + wl_signal_add(&surface->old_raster->events.destroy, + &surface->raster_destroy); + + wlr_raster_unlock(surface->old_raster); + } + } + surface_apply_damage(surface); + + wlr_buffer_unlock(surface->current.buffer); + surface->current.buffer = NULL; } + surface_update_opaque_region(surface); surface_update_input_region(surface); @@ -450,6 +477,29 @@ static void surface_commit_state(struct wlr_surface *surface, } wlr_signal_emit_safe(&surface->events.commit, surface); + + if (surface->old_raster) { + wl_list_remove(&surface->raster_destroy.link); + surface->old_raster = NULL; + } + + if (invalid_buffer && surface->raster) { + // make sure there is at least one source for the raster before removing + // the buffer + if (wl_list_empty(&surface->raster->sources)) { + wlr_renderer_raster_upload(surface->renderer, surface->raster); + } + + // unlock the buffer for shm buffers only. Clients may implement + // optimizations if given the shm buffer back immediately. + // + // For other buffers, we want to continue to lock it so that we + // may direct scanout. + if (!wl_list_empty(&surface->raster->sources) && surface->raster->buffer && + buffer_is_shm_client_buffer(surface->raster->buffer)) { + wlr_raster_remove_buffer(surface->raster); + } + } } static void collect_subsurface_damage_iter(struct wlr_surface *surface, @@ -614,6 +664,8 @@ static void surface_handle_resource_destroy(struct wl_resource *resource) { surface_output_destroy(surface_output); } + wlr_raster_unlock(surface->raster); + wlr_signal_emit_safe(&surface->events.destroy, surface); wlr_addon_set_finish(&surface->addons); @@ -640,6 +692,46 @@ static void surface_handle_renderer_destroy(struct wl_listener *listener, wl_resource_destroy(surface->resource); } +static void surface_handle_raster_destroy(struct wl_listener *listener, void *data) { + struct wlr_surface *surface = + wl_container_of(listener, surface, raster_destroy); + + // try to reclaim a texture from this raster so we can try to do a partial + // upload next time. + struct wlr_texture *reuse = NULL; + + struct wlr_texture *texture, *tmp_texture; + wl_list_for_each_safe(texture, tmp_texture, + &surface->old_raster->sources, link) { + // we can be smarter about this, but for now let's just take the first + // texture + wlr_raster_detach(surface->old_raster, texture); + reuse = texture; + break; + } + + surface->old_raster = NULL; + + if (!reuse) { + return; + } + + // if there are already sources for the new raster, don't bother uploading + // if something else already did for us. + if (!wl_list_empty(&surface->raster->sources)) { + wlr_texture_destroy(reuse); + return; + } + + if (!wlr_texture_update_from_raster(reuse, + surface->raster, &surface->buffer_damage)) { + wlr_texture_destroy(reuse); + return; + } + + wlr_raster_attach(surface->raster, reuse); +} + static struct wlr_surface *surface_create(struct wl_client *client, uint32_t version, uint32_t id, struct wlr_renderer *renderer) { struct wlr_surface *surface = calloc(1, sizeof(struct wlr_surface)); @@ -680,11 +772,13 @@ static struct wlr_surface *surface_create(struct wl_client *client, wl_signal_add(&renderer->events.destroy, &surface->renderer_destroy); surface->renderer_destroy.notify = surface_handle_renderer_destroy; + surface->raster_destroy.notify = surface_handle_raster_destroy; + return surface; } bool wlr_surface_has_buffer(struct wlr_surface *surface) { - return surface->current.buffer != NULL; + return surface->raster != NULL; } bool wlr_surface_set_role(struct wlr_surface *surface,