mirror of
https://gitlab.freedesktop.org/wlroots/wlroots.git
synced 2025-04-16 15:36:33 +00:00
scene: Optimize rendering of single-pixel buffers
The single-pixel buffer protocol is used to allow wayland clients to easily draw solid-color rectangles by presenting a 1x1-pixel buffer and scaling it to the desired size. This patch improves how these buffers are then handled in the scene-tree renderer. We already ignore opaque black rectangles at the very bottom (and anything under them) because we assume we'll be rendering on a black background. This patch detects black opaque single-pixel buffers and handles them in the same way as black opaque rectangles. It also renders single-pixel buffers as rectangles rather than buffers because this is probably more efficient in the underlying renderer. In wlr_scene_surface we cache whether the attached buffer is a single-pixel buffer. This is done because the wlr_single_pixel_buffer_v1 will be destroyed after texture upload, after which it becomes much more annoying to check if the buffer is a single-pixel buffer.
This commit is contained in:
parent
5563d23b81
commit
792bee9657
@ -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;
|
||||
};
|
||||
|
||||
|
@ -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 =
|
||||
|
@ -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,
|
||||
|
Loading…
Reference in New Issue
Block a user