diff --git a/include/wlr/types/wlr_scene.h b/include/wlr/types/wlr_scene.h index 0e3e7750a..b18f3e84e 100644 --- a/include/wlr/types/wlr_scene.h +++ b/include/wlr/types/wlr_scene.h @@ -132,6 +132,12 @@ struct wlr_scene_surface { struct wl_listener frame_done; struct wl_listener surface_destroy; struct wl_listener surface_commit; + + // True if the underlying buffer is a wlr_single_pixel_buffer_v1 + bool is_single_pixel_buffer; + // If is_single_pixel_buffer is set, contains the color of the buffer + // as {R, G, B, A} where the max value of each component is UINT32_MAX + uint32_t single_pixel_buffer_color[4]; } WLR_PRIVATE; }; diff --git a/types/scene/surface.c b/types/scene/surface.c index 1f954373d..079b82946 100644 --- a/types/scene/surface.c +++ b/types/scene/surface.c @@ -7,6 +7,7 @@ #include <wlr/types/wlr_linux_drm_syncobj_v1.h> #include <wlr/types/wlr_output.h> #include <wlr/types/wlr_presentation_time.h> +#include <wlr/types/wlr_single_pixel_buffer_v1.h> #include <wlr/util/transform.h> #include "types/wlr_scene.h" @@ -164,6 +165,24 @@ static void surface_reconfigure(struct wlr_scene_surface *scene_surface) { scene_buffer_unmark_client_buffer(scene_buffer); if (surface->buffer) { + // If this is a buffer change, check if it's a single pixel buffer. + // Cache that so we can still apply rendering optimisations even when + // the original buffer has been freed after texture upload. + if (&surface->buffer->base != scene_buffer->buffer) { + scene_surface->is_single_pixel_buffer = false; + if (surface->buffer->source != NULL) { + struct wlr_single_pixel_buffer_v1 *single_pixel_buffer = + wlr_single_pixel_buffer_v1_try_from_buffer(surface->buffer->source); + if (single_pixel_buffer != NULL) { + scene_surface->is_single_pixel_buffer = true; + scene_surface->single_pixel_buffer_color[0] = single_pixel_buffer->r; + scene_surface->single_pixel_buffer_color[1] = single_pixel_buffer->g; + scene_surface->single_pixel_buffer_color[2] = single_pixel_buffer->b; + scene_surface->single_pixel_buffer_color[3] = single_pixel_buffer->a; + } + } + } + client_buffer_mark_next_can_damage(surface->buffer); struct wlr_linux_drm_syncobj_surface_v1_state *syncobj_surface_state = diff --git a/types/scene/wlr_scene.c b/types/scene/wlr_scene.c index d64ee4ada..481b2404f 100644 --- a/types/scene/wlr_scene.c +++ b/types/scene/wlr_scene.c @@ -1386,6 +1386,24 @@ static void scene_entry_render(struct render_list_entry *entry, const struct ren case WLR_SCENE_NODE_BUFFER:; struct wlr_scene_buffer *scene_buffer = wlr_scene_buffer_from_node(node); + struct wlr_scene_surface *scene_surface = + wlr_scene_surface_try_from_buffer(scene_buffer); + if (scene_surface != NULL && scene_surface->is_single_pixel_buffer) { + // Render the buffer as a rect, this is likely to be more efficient + wlr_render_pass_add_rect(data->render_pass, &(struct wlr_render_rect_options){ + .box = dst_box, + .color = { + .r = (float)scene_surface->single_pixel_buffer_color[0] / (float)UINT32_MAX, + .g = (float)scene_surface->single_pixel_buffer_color[1] / (float)UINT32_MAX, + .b = (float)scene_surface->single_pixel_buffer_color[2] / (float)UINT32_MAX, + .a = (float)scene_surface->single_pixel_buffer_color[3] / + (float)UINT32_MAX * scene_buffer->opacity, + }, + .clip = &render_region, + }); + break; + } + struct wlr_texture *texture = scene_buffer_get_texture(scene_buffer, data->output->output->renderer); if (texture == NULL) { @@ -1736,6 +1754,18 @@ struct render_list_constructor_data { bool fractional_scale; }; +static bool scene_buffer_is_black_opaque(struct wlr_scene_buffer *scene_buffer) { + struct wlr_scene_surface *scene_surface = + wlr_scene_surface_try_from_buffer(scene_buffer); + return scene_surface != NULL && + scene_surface->is_single_pixel_buffer && + scene_surface->single_pixel_buffer_color[0] == 0 && + scene_surface->single_pixel_buffer_color[1] == 0 && + scene_surface->single_pixel_buffer_color[2] == 0 && + scene_surface->single_pixel_buffer_color[3] == UINT32_MAX && + scene_buffer->opacity == 1.0; +} + static bool construct_render_list_iterator(struct wlr_scene_node *node, int lx, int ly, void *_data) { struct render_list_constructor_data *data = _data; @@ -1758,6 +1788,16 @@ static bool construct_render_list_iterator(struct wlr_scene_node *node, } } + // Apply the same special-case to black opaque single-pixel buffers + if (node->type == WLR_SCENE_NODE_BUFFER && data->calculate_visibility && + (!data->fractional_scale || data->render_list->size == 0)) { + struct wlr_scene_buffer *scene_buffer = wlr_scene_buffer_from_node(node); + + if (scene_buffer_is_black_opaque(scene_buffer)) { + return false; + } + } + pixman_region32_t intersection; pixman_region32_init(&intersection); pixman_region32_intersect_rect(&intersection, &node->visible,