Support for hardware cursor sizes other than 64x64 on wayland

Use the new DRM capabilities to figure out the correct cursor size, and
make sure that matches instead of hardcoding 64x64. This fixes incorrect
rendering on some newer AMD cards that support 256x256 cursors.

Based heavily on a patch by:
Alvaro Fernando García <alvarofernandogarcia@gmail.com>
This commit is contained in:
Jasper St. Pierre 2014-09-24 15:40:09 -06:00
parent 6565bca210
commit 488dd0b402
3 changed files with 86 additions and 32 deletions

View File

@ -139,35 +139,56 @@ load_cursor_on_client (MetaCursor cursor)
meta_prefs_get_cursor_size ()); meta_prefs_get_cursor_size ());
} }
static void
get_hardware_cursor_size (uint64_t *cursor_width, uint64_t *cursor_height)
{
#ifdef HAVE_NATIVE_BACKEND
MetaBackend *meta_backend = meta_get_backend ();
MetaCursorRenderer *renderer = meta_backend_get_cursor_renderer (meta_backend);
if (META_IS_CURSOR_RENDERER_NATIVE (renderer))
{
meta_cursor_renderer_native_get_cursor_size (META_CURSOR_RENDERER_NATIVE (renderer), cursor_width, cursor_height);
return;
}
#endif
g_assert_not_reached ();
}
static void static void
meta_cursor_image_load_gbm_buffer (struct gbm_device *gbm, meta_cursor_image_load_gbm_buffer (struct gbm_device *gbm,
MetaCursorImage *image, MetaCursorImage *image,
uint8_t *pixels, uint8_t *pixels,
int width, uint width,
int height, uint height,
int rowstride, int rowstride,
uint32_t gbm_format) uint32_t gbm_format)
{ {
if (width > 64 || height > 64) uint64_t cursor_width, cursor_height;
get_hardware_cursor_size (&cursor_width, &cursor_height);
if (width > cursor_width || height > cursor_height)
{ {
meta_warning ("Invalid theme cursor size (must be at most 64x64)\n"); meta_warning ("Invalid theme cursor size (must be at most %ux%u)\n",
(unsigned int)cursor_width, (unsigned int)cursor_height);
return; return;
} }
if (gbm_device_is_format_supported (gbm, gbm_format, if (gbm_device_is_format_supported (gbm, gbm_format,
GBM_BO_USE_CURSOR_64X64 | GBM_BO_USE_WRITE)) GBM_BO_USE_CURSOR | GBM_BO_USE_WRITE))
{ {
uint8_t buf[4 * 64 * 64]; uint8_t buf[4 * cursor_width * cursor_height];
int i; uint i;
image->bo = gbm_bo_create (gbm, 64, 64, image->bo = gbm_bo_create (gbm, cursor_width, cursor_height,
gbm_format, GBM_BO_USE_CURSOR_64X64 | GBM_BO_USE_WRITE); gbm_format, GBM_BO_USE_CURSOR | GBM_BO_USE_WRITE);
memset (buf, 0, sizeof(buf)); memset (buf, 0, sizeof(buf));
for (i = 0; i < height; i++) for (i = 0; i < height; i++)
memcpy (buf + i * 4 * 64, pixels + i * rowstride, width * 4); memcpy (buf + i * 4 * cursor_width, pixels + i * rowstride, width * 4);
gbm_bo_write (image->bo, buf, 64 * 64 * 4); gbm_bo_write (image->bo, buf, cursor_width * cursor_height * 4);
} }
else else
meta_warning ("HW cursor for format %d not supported\n", gbm_format); meta_warning ("HW cursor for format %d not supported\n", gbm_format);
@ -191,7 +212,7 @@ static void
meta_cursor_image_load_from_xcursor_image (MetaCursorImage *image, meta_cursor_image_load_from_xcursor_image (MetaCursorImage *image,
XcursorImage *xc_image) XcursorImage *xc_image)
{ {
int width, height, rowstride; uint width, height, rowstride;
CoglPixelFormat cogl_format; CoglPixelFormat cogl_format;
uint32_t gbm_format; uint32_t gbm_format;
ClutterBackend *clutter_backend; ClutterBackend *clutter_backend;
@ -262,7 +283,8 @@ meta_cursor_image_load_from_buffer (MetaCursorImage *image,
CoglContext *cogl_context; CoglContext *cogl_context;
struct wl_shm_buffer *shm_buffer; struct wl_shm_buffer *shm_buffer;
uint32_t gbm_format; uint32_t gbm_format;
int width, height; uint64_t cursor_width, cursor_height;
uint width, height;
image->hot_x = hot_x; image->hot_x = hot_x;
image->hot_y = hot_y; image->hot_y = hot_y;
@ -313,22 +335,23 @@ meta_cursor_image_load_from_buffer (MetaCursorImage *image,
} }
else else
{ {
/* HW cursors must be 64x64, but 64x64 is huge, and no cursor theme actually uses
that, so themed cursors must be padded with transparent pixels to fill the
overlay. This is trivial if we have CPU access to the data, but it's not
possible if the buffer is in GPU memory (and possibly tiled too), so if we
don't get the right size, we fallback to GL.
*/
if (width != 64 || height != 64)
{
meta_warning ("Invalid cursor size (must be 64x64), falling back to software (GL) cursors\n");
return;
}
if (gbm) if (gbm)
{ {
image->bo = gbm_bo_import (gbm, GBM_BO_IMPORT_WL_BUFFER, /* HW cursors have a predefined size (at least 64x64), which usually is bigger than cursor theme
buffer, GBM_BO_USE_CURSOR_64X64); size, so themed cursors must be padded with transparent pixels to fill the
overlay. This is trivial if we have CPU access to the data, but it's not
possible if the buffer is in GPU memory (and possibly tiled too), so if we
don't get the right size, we fallback to GL.
*/
get_hardware_cursor_size (&cursor_width, &cursor_height);
if (width != cursor_width || height != cursor_height)
{
meta_warning ("Invalid cursor size (must be 64x64), falling back to software (GL) cursors\n");
return;
}
image->bo = gbm_bo_import (gbm, GBM_BO_IMPORT_WL_BUFFER, buffer, GBM_BO_USE_CURSOR);
if (!image->bo) if (!image->bo)
meta_warning ("Importing HW cursor from wl_buffer failed\n"); meta_warning ("Importing HW cursor from wl_buffer failed\n");
} }

View File

@ -27,16 +27,27 @@
#include "meta-cursor-renderer-native.h" #include "meta-cursor-renderer-native.h"
#include <gbm.h> #include <gbm.h>
#include <xf86drm.h>
#include "meta-cursor-private.h" #include "meta-cursor-private.h"
#include "meta-monitor-manager.h" #include "meta-monitor-manager.h"
#ifndef DRM_CAP_CURSOR_WIDTH
#define DRM_CAP_CURSOR_WIDTH 0x8
#endif
#ifndef DRM_CAP_CURSOR_HEIGHT
#define DRM_CAP_CURSOR_HEIGHT 0x9
#endif
struct _MetaCursorRendererNativePrivate struct _MetaCursorRendererNativePrivate
{ {
gboolean has_hw_cursor; gboolean has_hw_cursor;
int drm_fd; int drm_fd;
struct gbm_device *gbm; struct gbm_device *gbm;
uint64_t cursor_width;
uint64_t cursor_height;
}; };
typedef struct _MetaCursorRendererNativePrivate MetaCursorRendererNativePrivate; typedef struct _MetaCursorRendererNativePrivate MetaCursorRendererNativePrivate;
@ -71,17 +82,13 @@ set_crtc_cursor (MetaCursorRendererNative *native,
{ {
struct gbm_bo *bo; struct gbm_bo *bo;
union gbm_bo_handle handle; union gbm_bo_handle handle;
int width, height;
int hot_x, hot_y; int hot_x, hot_y;
bo = meta_cursor_reference_get_gbm_bo (cursor, &hot_x, &hot_y); bo = meta_cursor_reference_get_gbm_bo (cursor, &hot_x, &hot_y);
handle = gbm_bo_get_handle (bo); handle = gbm_bo_get_handle (bo);
width = gbm_bo_get_width (bo);
height = gbm_bo_get_height (bo);
drmModeSetCursor2 (priv->drm_fd, crtc->crtc_id, handle.u32, drmModeSetCursor2 (priv->drm_fd, crtc->crtc_id, handle.u32,
width, height, hot_x, hot_y); priv->cursor_width, priv->cursor_height, hot_x, hot_y);
} }
else else
{ {
@ -186,6 +193,19 @@ meta_cursor_renderer_native_init (MetaCursorRendererNative *native)
CoglRenderer *cogl_renderer = cogl_display_get_renderer (cogl_context_get_display (ctx)); CoglRenderer *cogl_renderer = cogl_display_get_renderer (cogl_context_get_display (ctx));
priv->drm_fd = cogl_kms_renderer_get_kms_fd (cogl_renderer); priv->drm_fd = cogl_kms_renderer_get_kms_fd (cogl_renderer);
priv->gbm = gbm_create_device (priv->drm_fd); priv->gbm = gbm_create_device (priv->drm_fd);
uint64_t width, height;
if (drmGetCap (priv->drm_fd, DRM_CAP_CURSOR_WIDTH, &width) == 0 &&
drmGetCap (priv->drm_fd, DRM_CAP_CURSOR_HEIGHT, &height) == 0)
{
priv->cursor_width = width;
priv->cursor_height = height;
}
else
{
priv->cursor_width = 64;
priv->cursor_height = 64;
}
} }
#endif #endif
} }
@ -198,6 +218,16 @@ meta_cursor_renderer_native_get_gbm_device (MetaCursorRendererNative *native)
return priv->gbm; return priv->gbm;
} }
void
meta_cursor_renderer_native_get_cursor_size (MetaCursorRendererNative *native,
uint64_t *width, uint64_t *height)
{
MetaCursorRendererNativePrivate *priv = meta_cursor_renderer_native_get_instance_private (native);
*width = priv->cursor_width;
*height = priv->cursor_height;
}
void void
meta_cursor_renderer_native_force_update (MetaCursorRendererNative *native) meta_cursor_renderer_native_force_update (MetaCursorRendererNative *native)
{ {

View File

@ -50,6 +50,7 @@ struct _MetaCursorRendererNativeClass
GType meta_cursor_renderer_native_get_type (void) G_GNUC_CONST; GType meta_cursor_renderer_native_get_type (void) G_GNUC_CONST;
struct gbm_device * meta_cursor_renderer_native_get_gbm_device (MetaCursorRendererNative *renderer); struct gbm_device * meta_cursor_renderer_native_get_gbm_device (MetaCursorRendererNative *renderer);
void meta_cursor_renderer_native_get_cursor_size (MetaCursorRendererNative *native, uint64_t *width, uint64_t *height);
void meta_cursor_renderer_native_force_update (MetaCursorRendererNative *renderer); void meta_cursor_renderer_native_force_update (MetaCursorRendererNative *renderer);
#endif /* META_CURSOR_RENDERER_NATIVE_H */ #endif /* META_CURSOR_RENDERER_NATIVE_H */