mirror of
				https://gitlab.freedesktop.org/wlroots/wlroots.git
				synced 2025-10-29 05:40:12 -04:00 
			
		
		
		
	backend/drm: add support for SIZE_HINTS property
This property allows the driver to advertise support for multiple cursor sizes. On Intel, using a smaller buffer size reduces power consumption. References: https://lore.kernel.org/dri-devel/20240227193523.5601-2-ville.syrjala@linux.intel.com/
This commit is contained in:
		
							parent
							
								
									6f63f55ace
								
							
						
					
					
						commit
						a35b4f059d
					
				
					 7 changed files with 106 additions and 17 deletions
				
			
		|  | @ -135,6 +135,26 @@ bool check_drm_features(struct wlr_drm_backend *drm) { | ||||||
| 	return true; | 	return true; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | static bool init_plane_cursor_sizes(struct wlr_drm_plane *plane, | ||||||
|  | 		const struct drm_plane_size_hint *hints, size_t hints_len) { | ||||||
|  | 	assert(hints_len > 0); | ||||||
|  | 	plane->cursor_sizes = calloc(hints_len, sizeof(plane->cursor_sizes[0])); | ||||||
|  | 	if (plane->cursor_sizes == NULL) { | ||||||
|  | 		return false; | ||||||
|  | 	} | ||||||
|  | 	plane->cursor_sizes_len = hints_len; | ||||||
|  | 
 | ||||||
|  | 	for (size_t i = 0; i < hints_len; i++) { | ||||||
|  | 		const struct drm_plane_size_hint hint = hints[i]; | ||||||
|  | 		plane->cursor_sizes[i] = (struct wlr_output_cursor_size){ | ||||||
|  | 			.width = hint.width, | ||||||
|  | 			.height = hint.height, | ||||||
|  | 		}; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| static bool init_plane(struct wlr_drm_backend *drm, | static bool init_plane(struct wlr_drm_backend *drm, | ||||||
| 		struct wlr_drm_plane *p, const drmModePlane *drm_plane) { | 		struct wlr_drm_plane *p, const drmModePlane *drm_plane) { | ||||||
| 	uint32_t id = drm_plane->plane_id; | 	uint32_t id = drm_plane->plane_id; | ||||||
|  | @ -186,6 +206,37 @@ static bool init_plane(struct wlr_drm_backend *drm, | ||||||
| 		drmModeFreePropertyBlob(blob); | 		drmModeFreePropertyBlob(blob); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	uint64_t size_hints_blob_id = 0; | ||||||
|  | 	if (p->props.size_hints) { | ||||||
|  | 		if (!get_drm_prop(drm->fd, p->id, p->props.size_hints, &size_hints_blob_id)) { | ||||||
|  | 			wlr_log(WLR_ERROR, "Failed to read SIZE_HINTS property"); | ||||||
|  | 			return false; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	if (size_hints_blob_id != 0) { | ||||||
|  | 		drmModePropertyBlobRes *blob = drmModeGetPropertyBlob(drm->fd, size_hints_blob_id); | ||||||
|  | 		if (!blob) { | ||||||
|  | 			wlr_log(WLR_ERROR, "Failed to read SIZE_HINTS blob"); | ||||||
|  | 			return false; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		const struct drm_plane_size_hint *size_hints = blob->data; | ||||||
|  | 		size_t size_hints_len = blob->length / sizeof(size_hints[0]); | ||||||
|  | 		if (!init_plane_cursor_sizes(p, size_hints, size_hints_len)) { | ||||||
|  | 			return false; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		drmModeFreePropertyBlob(blob); | ||||||
|  | 	} else { | ||||||
|  | 		const struct drm_plane_size_hint size_hint = { | ||||||
|  | 			.width = drm->cursor_width, | ||||||
|  | 			.height = drm->cursor_height, | ||||||
|  | 		}; | ||||||
|  | 		if (!init_plane_cursor_sizes(p, &size_hint, 1)) { | ||||||
|  | 			return false; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	assert(drm->num_crtcs <= 32); | 	assert(drm->num_crtcs <= 32); | ||||||
| 	for (size_t j = 0; j < drm->num_crtcs; j++) { | 	for (size_t j = 0; j < drm->num_crtcs; j++) { | ||||||
| 		uint32_t crtc_bit = 1 << j; | 		uint32_t crtc_bit = 1 << j; | ||||||
|  | @ -1003,8 +1054,15 @@ static bool drm_connector_set_cursor(struct wlr_output *output, | ||||||
| 	conn->cursor_enabled = false; | 	conn->cursor_enabled = false; | ||||||
| 	drm_fb_clear(&conn->cursor_pending_fb); | 	drm_fb_clear(&conn->cursor_pending_fb); | ||||||
| 	if (buffer != NULL) { | 	if (buffer != NULL) { | ||||||
| 		if ((uint64_t)buffer->width != drm->cursor_width || | 		bool found = false; | ||||||
| 				(uint64_t)buffer->height != drm->cursor_height) { | 		for (size_t i = 0; i < plane->cursor_sizes_len; i++) { | ||||||
|  | 			struct wlr_output_cursor_size size = plane->cursor_sizes[i]; | ||||||
|  | 			if (size.width == buffer->width && size.height == buffer->height) { | ||||||
|  | 				found = true; | ||||||
|  | 				break; | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		if (!found) { | ||||||
| 			wlr_drm_conn_log(conn, WLR_DEBUG, "Cursor buffer size mismatch"); | 			wlr_drm_conn_log(conn, WLR_DEBUG, "Cursor buffer size mismatch"); | ||||||
| 			return false; | 			return false; | ||||||
| 		} | 		} | ||||||
|  | @ -1130,11 +1188,18 @@ static const struct wlr_drm_format_set *drm_connector_get_cursor_formats( | ||||||
| 	return &plane->formats; | 	return &plane->formats; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void drm_connector_get_cursor_size(struct wlr_output *output, | static const struct wlr_output_cursor_size *drm_connector_get_cursor_sizes(struct wlr_output *output, | ||||||
| 		int *width, int *height) { | 		size_t *len) { | ||||||
| 	struct wlr_drm_backend *drm = get_drm_backend_from_backend(output->backend); | 	struct wlr_drm_connector *conn = get_drm_connector_from_output(output); | ||||||
| 	*width = (int)drm->cursor_width; | 	if (!drm_connector_alloc_crtc(conn)) { | ||||||
| 	*height = (int)drm->cursor_height; | 		return NULL; | ||||||
|  | 	} | ||||||
|  | 	struct wlr_drm_plane *plane = conn->crtc->cursor; | ||||||
|  | 	if (!plane) { | ||||||
|  | 		return NULL; | ||||||
|  | 	} | ||||||
|  | 	*len = plane->cursor_sizes_len; | ||||||
|  | 	return plane->cursor_sizes; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static const struct wlr_drm_format_set *drm_connector_get_primary_formats( | static const struct wlr_drm_format_set *drm_connector_get_primary_formats( | ||||||
|  | @ -1160,7 +1225,7 @@ static const struct wlr_output_impl output_impl = { | ||||||
| 	.commit = drm_connector_commit, | 	.commit = drm_connector_commit, | ||||||
| 	.get_gamma_size = drm_connector_get_gamma_size, | 	.get_gamma_size = drm_connector_get_gamma_size, | ||||||
| 	.get_cursor_formats = drm_connector_get_cursor_formats, | 	.get_cursor_formats = drm_connector_get_cursor_formats, | ||||||
| 	.get_cursor_size = drm_connector_get_cursor_size, | 	.get_cursor_sizes = drm_connector_get_cursor_sizes, | ||||||
| 	.get_primary_formats = drm_connector_get_primary_formats, | 	.get_primary_formats = drm_connector_get_primary_formats, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -56,6 +56,7 @@ static const struct prop_info plane_info[] = { | ||||||
| 	{ "HOTSPOT_X", INDEX(hotspot_x) }, | 	{ "HOTSPOT_X", INDEX(hotspot_x) }, | ||||||
| 	{ "HOTSPOT_Y", INDEX(hotspot_y) }, | 	{ "HOTSPOT_Y", INDEX(hotspot_y) }, | ||||||
| 	{ "IN_FORMATS", INDEX(in_formats) }, | 	{ "IN_FORMATS", INDEX(in_formats) }, | ||||||
|  | 	{ "SIZE_HINTS", INDEX(size_hints) }, | ||||||
| 	{ "SRC_H", INDEX(src_h) }, | 	{ "SRC_H", INDEX(src_h) }, | ||||||
| 	{ "SRC_W", INDEX(src_w) }, | 	{ "SRC_W", INDEX(src_w) }, | ||||||
| 	{ "SRC_X", INDEX(src_x) }, | 	{ "SRC_X", INDEX(src_x) }, | ||||||
|  |  | ||||||
|  | @ -29,6 +29,9 @@ struct wlr_drm_plane { | ||||||
| 
 | 
 | ||||||
| 	struct wlr_drm_format_set formats; | 	struct wlr_drm_format_set formats; | ||||||
| 
 | 
 | ||||||
|  | 	struct wlr_output_cursor_size *cursor_sizes; | ||||||
|  | 	size_t cursor_sizes_len; | ||||||
|  | 
 | ||||||
| 	struct wlr_drm_plane_props props; | 	struct wlr_drm_plane_props props; | ||||||
| 
 | 
 | ||||||
| 	uint32_t initial_crtc_id; | 	uint32_t initial_crtc_id; | ||||||
|  |  | ||||||
|  | @ -44,6 +44,7 @@ struct wlr_drm_plane_props { | ||||||
| 	uint32_t type; | 	uint32_t type; | ||||||
| 	uint32_t rotation; // Not guaranteed to exist
 | 	uint32_t rotation; // Not guaranteed to exist
 | ||||||
| 	uint32_t in_formats; // Not guaranteed to exist
 | 	uint32_t in_formats; // Not guaranteed to exist
 | ||||||
|  | 	uint32_t size_hints; // Not guaranteed to exist
 | ||||||
| 
 | 
 | ||||||
| 	// atomic-modesetting only
 | 	// atomic-modesetting only
 | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -25,6 +25,10 @@ | ||||||
| 	WLR_OUTPUT_STATE_SUBPIXEL | \ | 	WLR_OUTPUT_STATE_SUBPIXEL | \ | ||||||
| 	WLR_OUTPUT_STATE_LAYERS) | 	WLR_OUTPUT_STATE_LAYERS) | ||||||
| 
 | 
 | ||||||
|  | struct wlr_output_cursor_size { | ||||||
|  | 	int width, height; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
| /**
 | /**
 | ||||||
|  * A backend implementation of struct wlr_output. |  * A backend implementation of struct wlr_output. | ||||||
|  * |  * | ||||||
|  | @ -82,10 +86,11 @@ struct wlr_output_impl { | ||||||
| 	const struct wlr_drm_format_set *(*get_cursor_formats)( | 	const struct wlr_drm_format_set *(*get_cursor_formats)( | ||||||
| 		struct wlr_output *output, uint32_t buffer_caps); | 		struct wlr_output *output, uint32_t buffer_caps); | ||||||
| 	/**
 | 	/**
 | ||||||
| 	 * Get the size suitable for the cursor buffer. Attempts to use a different | 	 * Get the list of sizes suitable for the cursor buffer. Attempts to use a | ||||||
| 	 * size for the cursor may fail. | 	 * different size for the cursor may fail. | ||||||
| 	 */ | 	 */ | ||||||
| 	void (*get_cursor_size)(struct wlr_output *output, int *width, int *height); | 	const struct wlr_output_cursor_size *(*get_cursor_sizes)(struct wlr_output *output, | ||||||
|  | 		size_t *len); | ||||||
| 	/**
 | 	/**
 | ||||||
| 	 * Get the list of DRM formats suitable for the primary buffer, | 	 * Get the list of DRM formats suitable for the primary buffer, | ||||||
| 	 * assuming a buffer with the specified capabilities. | 	 * assuming a buffer with the specified capabilities. | ||||||
|  |  | ||||||
|  | @ -111,7 +111,7 @@ wayland_server = dependency('wayland-server', | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| drm = dependency('libdrm', | drm = dependency('libdrm', | ||||||
| 	version: '>=2.4.120', | 	version: '>=2.4.122', | ||||||
| 	fallback: 'libdrm', | 	fallback: 'libdrm', | ||||||
| 	default_options: [ | 	default_options: [ | ||||||
| 		'auto_features=disabled', | 		'auto_features=disabled', | ||||||
|  |  | ||||||
|  | @ -196,13 +196,27 @@ static struct wlr_buffer *render_cursor_buffer(struct wlr_output_cursor *cursor) | ||||||
| 
 | 
 | ||||||
| 	int width = cursor->width; | 	int width = cursor->width; | ||||||
| 	int height = cursor->height; | 	int height = cursor->height; | ||||||
| 	if (output->impl->get_cursor_size) { | 	if (output->impl->get_cursor_sizes) { | ||||||
| 		// Apply hardware limitations on buffer size
 | 		// Apply hardware limitations on buffer size
 | ||||||
| 		output->impl->get_cursor_size(cursor->output, &width, &height); | 		size_t sizes_len = 0; | ||||||
| 		if ((int)texture->width > width || (int)texture->height > height) { | 		const struct wlr_output_cursor_size *sizes = | ||||||
|  | 			output->impl->get_cursor_sizes(cursor->output, &sizes_len); | ||||||
|  | 
 | ||||||
|  | 		bool found = false; | ||||||
|  | 		for (size_t i = 0; i < sizes_len; i++) { | ||||||
|  | 			struct wlr_output_cursor_size size = sizes[i]; | ||||||
|  | 			if ((int)texture->width <= size.width && (int)texture->height <= size.height) { | ||||||
|  | 				width = size.width; | ||||||
|  | 				height = size.height; | ||||||
|  | 				found = true; | ||||||
|  | 				break; | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		if (!found) { | ||||||
| 			wlr_log(WLR_DEBUG, "Cursor texture too large (%dx%d), " | 			wlr_log(WLR_DEBUG, "Cursor texture too large (%dx%d), " | ||||||
| 				"exceeds hardware limitations (%dx%d)", texture->width, | 				"exceeds hardware limitations", texture->width, | ||||||
| 				texture->height, width, height); | 				texture->height); | ||||||
| 			return NULL; | 			return NULL; | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Simon Ser
						Simon Ser