diff --git a/include/wlr/types/wlr_raster.h b/include/wlr/types/wlr_raster.h index 6e654bb21..39295c6e8 100644 --- a/include/wlr/types/wlr_raster.h +++ b/include/wlr/types/wlr_raster.h @@ -19,10 +19,19 @@ struct wlr_renderer; struct wlr_drm_syncobj_timeline; struct wlr_surface; +struct wlr_raster_source { + struct wlr_texture *texture; + struct wl_list link; + + struct wl_listener renderer_destroy; +}; + struct wlr_raster { // May be NULL struct wlr_buffer *buffer; + struct wl_list sources; + uint32_t width, height; bool opaque; @@ -39,7 +48,6 @@ struct wlr_raster { struct wl_listener buffer_release; - struct wlr_texture *texture; struct wl_listener renderer_destroy; }; diff --git a/types/wlr_raster.c b/types/wlr_raster.c index 703722a9a..68f05c821 100644 --- a/types/wlr_raster.c +++ b/types/wlr_raster.c @@ -24,6 +24,7 @@ struct wlr_raster *wlr_raster_create(struct wlr_buffer *buffer, return NULL; } + wl_list_init(&raster->sources); wl_signal_init(&raster->events.destroy); assert(buffer); @@ -45,6 +46,12 @@ struct wlr_raster *wlr_raster_create(struct wlr_buffer *buffer, return raster; } +static void raster_source_destroy(struct wlr_raster_source *source) { + wl_list_remove(&source->link); + wl_list_remove(&source->renderer_destroy.link); + free(source); +} + static void raster_consider_destroy(struct wlr_raster *raster) { if (raster->n_locks > 0) { return; @@ -52,9 +59,10 @@ static void raster_consider_destroy(struct wlr_raster *raster) { wl_signal_emit_mutable(&raster->events.destroy, NULL); - if (raster->texture) { - wl_list_remove(&raster->renderer_destroy.link); - wlr_texture_destroy(raster->texture); + struct wlr_raster_source *source, *source_tmp; + wl_list_for_each_safe(source, source_tmp, &raster->sources, link) { + wlr_texture_destroy(source->texture); + raster_source_destroy(source); } wl_list_remove(&raster->buffer_release.link); @@ -79,33 +87,63 @@ void wlr_raster_unlock(struct wlr_raster *raster) { } static void raster_detach(struct wlr_raster *raster, struct wlr_texture *texture) { - assert(texture); - assert(raster->texture == texture); + if (!texture) { + return; + } - wl_list_remove(&raster->renderer_destroy.link); - raster->texture = NULL; + struct wlr_raster_source *source; + wl_list_for_each(source, &raster->sources, link) { + if (source->texture == texture) { + raster_source_destroy(source); + return; + } + } + + assert(false); } static void handle_renderer_destroy(struct wl_listener *listener, void *data) { - struct wlr_raster *raster = wl_container_of(listener, raster, renderer_destroy); - raster_detach(raster, raster->texture); + struct wlr_raster_source *source = wl_container_of(listener, source, renderer_destroy); + raster_source_destroy(source); } static void raster_attach(struct wlr_raster *raster, struct wlr_texture *texture) { assert(texture->width == raster->width && texture->height == raster->height); - assert(!raster->texture); - raster->renderer_destroy.notify = handle_renderer_destroy; - wl_signal_add(&texture->renderer->events.destroy, &raster->renderer_destroy); + struct wlr_raster_source *source; + wl_list_for_each(source, &raster->sources, link) { + assert(source->texture != texture); + } - raster->texture = texture; + source = calloc(1, sizeof(*source)); + if (!source) { + return; + } + + source->renderer_destroy.notify = handle_renderer_destroy; + wl_signal_add(&texture->renderer->events.destroy, &source->renderer_destroy); + + wl_list_insert(&raster->sources, &source->link); + source->texture = texture; +} + +static struct wlr_texture *wlr_raster_get_texture(struct wlr_raster *raster, + struct wlr_renderer *renderer) { + struct wlr_raster_source *source; + wl_list_for_each(source, &raster->sources, link) { + if (source->texture->renderer == renderer) { + return source->texture; + } + } + + return NULL; } struct wlr_texture *wlr_raster_obtain_texture(struct wlr_raster *raster, struct wlr_renderer *renderer) { - if (raster->texture) { - assert(raster->texture->renderer == renderer); - return raster->texture; + struct wlr_texture *texture = wlr_raster_get_texture(raster, renderer); + if (texture) { + return texture; } assert(raster->buffer); @@ -116,7 +154,7 @@ struct wlr_texture *wlr_raster_obtain_texture(struct wlr_raster *raster, return client_buffer->texture; } - struct wlr_texture *texture = wlr_texture_from_buffer(renderer, raster->buffer); + texture = wlr_texture_from_buffer(renderer, raster->buffer); if (texture) { raster_attach(raster, texture); } @@ -153,21 +191,18 @@ static void raster_update_handle_old_raster_destroy(struct wl_listener *listener struct raster_update_state *state = wl_container_of(listener, state, old_raster_destroy); // if the new raster already has a texture, there's nothing we can do to help. - if (state->new_raster->texture) { - assert(state->new_raster->texture->renderer == state->old_raster->texture->renderer); + if (!wl_list_empty(&state->new_raster->sources)) { destroy_raster_update_state(state); return; } - struct wlr_texture *texture = state->old_raster->texture; - if (!texture) { - destroy_raster_update_state(state); - return; - } - - if (wlr_texture_update_from_buffer(texture, state->buffer, &state->damage)) { - raster_detach(state->old_raster, texture); - raster_attach(state->new_raster, texture); + struct wlr_raster_source *source, *tmp_source; + wl_list_for_each_safe(source, tmp_source, &state->old_raster->sources, link) { + struct wlr_texture *texture = source->texture; + if (wlr_texture_update_from_buffer(texture, state->buffer, &state->damage)) { + raster_detach(state->old_raster, texture); + raster_attach(state->new_raster, texture); + } } destroy_raster_update_state(state); @@ -254,7 +289,7 @@ static void surface_raster_handle_buffer_prerelease(struct wl_listener *listener } // if there was a failed texture upload, keep on locking the buffer - if (!raster->texture) { + if (wl_list_empty(&raster->sources)) { wlr_buffer_lock(raster->buffer); surface_raster->locking_buffer = true; } @@ -372,6 +407,26 @@ struct wlr_raster *wlr_raster_from_surface(struct wlr_surface *surface) { return wlr_raster_lock(surface_raster->raster); } + // before we try to update the old raster, remove obsolete textures + struct wlr_raster_source *source, *tmp_source; + wl_list_for_each_safe(source, tmp_source, &surface_raster->raster->sources, link) { + struct wlr_texture *texture = source->texture; + + bool found = false; + struct wlr_surface_output *output; + wl_list_for_each(output, &surface->current_outputs, link) { + if (output->output->renderer == texture->renderer) { + found = true; + break; + } + } + + if (!found) { + raster_detach(surface_raster->raster, texture); + wlr_texture_destroy(texture); + } + } + raster = raster_update(surface_raster->raster, surface->current.buffer, &surface->buffer_damage, &options); } else {