mirror of
				https://gitlab.freedesktop.org/wlroots/wlroots.git
				synced 2025-11-03 09:01:40 -05: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
					
				
					 3 changed files with 65 additions and 0 deletions
				
			
		| 
						 | 
					@ -132,6 +132,12 @@ struct wlr_scene_surface {
 | 
				
			||||||
		struct wl_listener frame_done;
 | 
							struct wl_listener frame_done;
 | 
				
			||||||
		struct wl_listener surface_destroy;
 | 
							struct wl_listener surface_destroy;
 | 
				
			||||||
		struct wl_listener surface_commit;
 | 
							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;
 | 
						} WLR_PRIVATE;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -7,6 +7,7 @@
 | 
				
			||||||
#include <wlr/types/wlr_linux_drm_syncobj_v1.h>
 | 
					#include <wlr/types/wlr_linux_drm_syncobj_v1.h>
 | 
				
			||||||
#include <wlr/types/wlr_output.h>
 | 
					#include <wlr/types/wlr_output.h>
 | 
				
			||||||
#include <wlr/types/wlr_presentation_time.h>
 | 
					#include <wlr/types/wlr_presentation_time.h>
 | 
				
			||||||
 | 
					#include <wlr/types/wlr_single_pixel_buffer_v1.h>
 | 
				
			||||||
#include <wlr/util/transform.h>
 | 
					#include <wlr/util/transform.h>
 | 
				
			||||||
#include "types/wlr_scene.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);
 | 
						scene_buffer_unmark_client_buffer(scene_buffer);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (surface->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);
 | 
							client_buffer_mark_next_can_damage(surface->buffer);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		struct wlr_linux_drm_syncobj_surface_v1_state *syncobj_surface_state =
 | 
							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:;
 | 
						case WLR_SCENE_NODE_BUFFER:;
 | 
				
			||||||
		struct wlr_scene_buffer *scene_buffer = wlr_scene_buffer_from_node(node);
 | 
							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,
 | 
							struct wlr_texture *texture = scene_buffer_get_texture(scene_buffer,
 | 
				
			||||||
			data->output->output->renderer);
 | 
								data->output->output->renderer);
 | 
				
			||||||
		if (texture == NULL) {
 | 
							if (texture == NULL) {
 | 
				
			||||||
| 
						 | 
					@ -1736,6 +1754,18 @@ struct render_list_constructor_data {
 | 
				
			||||||
	bool fractional_scale;
 | 
						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,
 | 
					static bool construct_render_list_iterator(struct wlr_scene_node *node,
 | 
				
			||||||
		int lx, int ly, void *_data) {
 | 
							int lx, int ly, void *_data) {
 | 
				
			||||||
	struct render_list_constructor_data *data = _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_t intersection;
 | 
				
			||||||
	pixman_region32_init(&intersection);
 | 
						pixman_region32_init(&intersection);
 | 
				
			||||||
	pixman_region32_intersect_rect(&intersection, &node->visible,
 | 
						pixman_region32_intersect_rect(&intersection, &node->visible,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue