[PATCH v3 14/23] drm/qxl: cover all crtcs in shadow bo.

Noralf Trønnes noralf at tronnes.org
Fri Jan 25 17:08:28 UTC 2019



Den 18.01.2019 13.20, skrev Gerd Hoffmann:
> The qxl device supports only a single active framebuffer ("primary
> surface" in spice terminology).  In multihead configurations are handled
> by defining rectangles within the primary surface for each head/crtc.
> 
> Userspace which uses the qxl ioctl interface (xorg qxl driver) is aware
> of this limitation and will setup framebuffers and crtcs accordingly.
> 
> Userspace which uses dumb framebuffers (xorg modesetting driver,
> wayland) is not aware of this limitation and tries to use two
> framebuffers (one for each crtc) instead.
> 
> The qxl kms driver already has the dumb bo separated from the primary
> surface, by using a (shared) shadow bo as primary surface.  This is
> needed to support pageflips without having to re-create the primary
> surface.  The qxl driver will blit from the dumb bo to the shadow bo
> instead.
> 
> So we can extend the shadow logic:  Maintain a global shadow bo (aka
> primary surface), make it big enough that dumb bo's for all crtcs fit in
> side-by-side.  Adjust the pageflip blits to place the heads next to each
> other in the shadow.
> 
> With this patch in place multihead qxl works with wayland.
> 
> Signed-off-by: Gerd Hoffmann <kraxel at redhat.com>
> ---
>  drivers/gpu/drm/qxl/qxl_drv.h     |   5 +-
>  drivers/gpu/drm/qxl/qxl_display.c | 119 +++++++++++++++++++++++++++++---------
>  drivers/gpu/drm/qxl/qxl_draw.c    |   9 ++-
>  3 files changed, 104 insertions(+), 29 deletions(-)
> 
> diff --git a/drivers/gpu/drm/qxl/qxl_drv.h b/drivers/gpu/drm/qxl/qxl_drv.h
> index 150b1a4f66..43c6df9cf9 100644
> --- a/drivers/gpu/drm/qxl/qxl_drv.h
> +++ b/drivers/gpu/drm/qxl/qxl_drv.h
> @@ -230,6 +230,8 @@ struct qxl_device {
>  	struct qxl_ram_header *ram_header;
>  
>  	struct qxl_bo *primary_bo;
> +	struct qxl_bo *dumb_shadow_bo;
> +	struct qxl_head *dumb_heads;
>  
>  	struct qxl_memslot main_slot;
>  	struct qxl_memslot surfaces_slot;
> @@ -437,7 +439,8 @@ void qxl_draw_dirty_fb(struct qxl_device *qdev,
>  		       struct qxl_bo *bo,
>  		       unsigned int flags, unsigned int color,
>  		       struct drm_clip_rect *clips,
> -		       unsigned int num_clips, int inc);
> +		       unsigned int num_clips, int inc,
> +		       uint32_t dumb_shadow_offset);
>  
>  void qxl_draw_fill(struct qxl_draw_fill *qxl_draw_fill_rec);
>  
> diff --git a/drivers/gpu/drm/qxl/qxl_display.c b/drivers/gpu/drm/qxl/qxl_display.c
> index ff13bc6a4a..d9de43e5fd 100644
> --- a/drivers/gpu/drm/qxl/qxl_display.c
> +++ b/drivers/gpu/drm/qxl/qxl_display.c
> @@ -323,6 +323,8 @@ static void qxl_crtc_update_monitors_config(struct drm_crtc *crtc,
>  		head.y = crtc->y;
>  		if (qdev->monitors_config->count < i + 1)
>  			qdev->monitors_config->count = i + 1;
> +		if (qdev->primary_bo == qdev->dumb_shadow_bo)
> +			head.x += qdev->dumb_heads[i].x;
>  	} else if (i > 0) {
>  		head.width = 0;
>  		head.height = 0;
> @@ -426,7 +428,7 @@ static int qxl_framebuffer_surface_dirty(struct drm_framebuffer *fb,
>  	}
>  
>  	qxl_draw_dirty_fb(qdev, fb, qobj, flags, color,
> -			  clips, num_clips, inc);
> +			  clips, num_clips, inc, 0);
>  
>  	drm_modeset_unlock_all(fb->dev);
>  
> @@ -535,6 +537,7 @@ static void qxl_primary_atomic_update(struct drm_plane *plane,
>  	    .x2 = plane->state->fb->width,
>  	    .y2 = plane->state->fb->height
>  	};
> +	uint32_t dumb_shadow_offset = 0;
>  
>  	if (old_state->fb) {
>  		bo_old = gem_to_qxl_bo(old_state->fb->obj[0]);
> @@ -551,7 +554,12 @@ static void qxl_primary_atomic_update(struct drm_plane *plane,
>  		qxl_primary_apply_cursor(plane);
>  	}
>  
> -	qxl_draw_dirty_fb(qdev, plane->state->fb, bo, 0, 0, &norect, 1, 1);
> +	if (bo->is_dumb)
> +		dumb_shadow_offset =
> +			qdev->dumb_heads[plane->state->crtc->index].x;
> +
> +	qxl_draw_dirty_fb(qdev, plane->state->fb, bo, 0, 0, &norect, 1, 1,
> +			  dumb_shadow_offset);
>  }
>  
>  static void qxl_primary_atomic_disable(struct drm_plane *plane,
> @@ -707,12 +715,68 @@ static void qxl_cursor_atomic_disable(struct drm_plane *plane,
>  	qxl_release_fence_buffer_objects(release);
>  }
>  
> +static void qxl_update_dumb_head(struct qxl_device *qdev,
> +				 int index, struct qxl_bo *bo)
> +{
> +	uint32_t width, height;
> +
> +	if (index >= qdev->monitors_config->max_allowed)
> +		return;
> +
> +	if (bo && bo->is_dumb) {
> +		width = bo->surf.width;
> +		height = bo->surf.height;
> +	} else {
> +		width = 0;
> +		height = 0;
> +	}
> +
> +	if (qdev->dumb_heads[index].width == width &&
> +	    qdev->dumb_heads[index].height == height)
> +		return;
> +
> +	DRM_DEBUG("#%d: %dx%d -> %dx%d\n", index,
> +		  qdev->dumb_heads[index].width,
> +		  qdev->dumb_heads[index].height,
> +		  width, height);
> +	qdev->dumb_heads[index].width = width;
> +	qdev->dumb_heads[index].height = height;
> +}
> +
> +static void qxl_calc_dumb_shadow(struct qxl_device *qdev,
> +				 struct qxl_surface *surf)
> +{
> +	struct qxl_head *head;
> +	int i;
> +
> +	memset(surf, 0, sizeof(*surf));
> +	for (i = 0; i < qdev->monitors_config->max_allowed; i++) {
> +		head = qdev->dumb_heads + i;
> +		head->x = surf->width;
> +		surf->width += head->width;
> +		if (surf->height < head->height)
> +			surf->height = head->height;
> +	}
> +	if (surf->width < 64)
> +		surf->width = 64;
> +	if (surf->height < 64)
> +		surf->height = 64;
> +	surf->format = SPICE_SURFACE_FMT_32_xRGB;
> +	surf->stride = surf->width * 4;
> +
> +	if (!qdev->dumb_shadow_bo ||
> +	    qdev->dumb_shadow_bo->surf.width != surf->width ||
> +	    qdev->dumb_shadow_bo->surf.height != surf->height)
> +		DRM_DEBUG("%dx%d\n", surf->width, surf->height);
> +}
> +
>  static int qxl_plane_prepare_fb(struct drm_plane *plane,
>  				struct drm_plane_state *new_state)
>  {
>  	struct qxl_device *qdev = plane->dev->dev_private;
>  	struct drm_gem_object *obj;
> -	struct qxl_bo *user_bo, *old_bo = NULL;
> +	struct qxl_bo *user_bo;
> +	struct qxl_surface surf;
>  	int ret;
>  
>  	if (!new_state->fb)
> @@ -722,29 +786,30 @@ static int qxl_plane_prepare_fb(struct drm_plane *plane,
>  	user_bo = gem_to_qxl_bo(obj);
>  
>  	if (plane->type == DRM_PLANE_TYPE_PRIMARY &&
> -	    user_bo->is_dumb && !user_bo->shadow) {
> -		if (plane->state->fb) {
> -			obj = plane->state->fb->obj[0];
> -			old_bo = gem_to_qxl_bo(obj);
> +	    user_bo->is_dumb) {
> +		qxl_update_dumb_head(qdev, new_state->crtc->index,
> +				     user_bo);
> +		qxl_calc_dumb_shadow(qdev, &surf);
> +		if (!qdev->dumb_shadow_bo ||
> +		    qdev->dumb_shadow_bo->surf.width  != surf.width ||
> +		    qdev->dumb_shadow_bo->surf.height != surf.height) {
> +			if (qdev->dumb_shadow_bo) {
> +				drm_gem_object_put_unlocked
> +					(&qdev->dumb_shadow_bo->gem_base);
> +				qdev->dumb_shadow_bo = NULL;
> +			}
> +			qxl_bo_create(qdev, surf.height * surf.stride,
> +				      true, true, QXL_GEM_DOMAIN_SURFACE, &surf,
> +				      &qdev->dumb_shadow_bo);
>  		}
> -		if (old_bo && old_bo->shadow &&
> -		    user_bo->gem_base.size == old_bo->gem_base.size &&
> -		    plane->state->crtc     == new_state->crtc &&
> -		    plane->state->crtc_w   == new_state->crtc_w &&
> -		    plane->state->crtc_h   == new_state->crtc_h &&
> -		    plane->state->src_x    == new_state->src_x &&
> -		    plane->state->src_y    == new_state->src_y &&
> -		    plane->state->src_w    == new_state->src_w &&
> -		    plane->state->src_h    == new_state->src_h &&
> -		    plane->state->rotation == new_state->rotation &&
> -		    plane->state->zpos     == new_state->zpos) {
> -			drm_gem_object_get(&old_bo->shadow->gem_base);
> -			user_bo->shadow = old_bo->shadow;
> -		} else {
> -			qxl_bo_create(qdev, user_bo->gem_base.size,
> -				      true, true, QXL_GEM_DOMAIN_SURFACE, NULL,
> -				      &user_bo->shadow);
> -			user_bo->shadow->surf = user_bo->surf;
> +		if (user_bo->shadow != qdev->dumb_shadow_bo) {
> +			if (user_bo->shadow) {
> +				drm_gem_object_put_unlocked
> +					(&user_bo->shadow->gem_base);
> +				user_bo->shadow = NULL;
> +			}
> +			drm_gem_object_get(&qdev->dumb_shadow_bo->gem_base);
> +			user_bo->shadow = qdev->dumb_shadow_bo;
>  		}
>  	}
>  
> @@ -773,7 +838,7 @@ static void qxl_plane_cleanup_fb(struct drm_plane *plane,
>  	user_bo = gem_to_qxl_bo(obj);
>  	qxl_bo_unpin(user_bo);
>  
> -	if (user_bo->shadow && !user_bo->shadow->is_primary) {
> +	if (old_state->fb != plane->state->fb && user_bo->shadow) {
>  		drm_gem_object_put_unlocked(&user_bo->shadow->gem_base);
>  		user_bo->shadow = NULL;
>  	}
> @@ -1106,6 +1171,8 @@ int qxl_create_monitors_object(struct qxl_device *qdev)
>  
>  	memset(qdev->monitors_config, 0, monitors_config_size);
>  	qdev->monitors_config->max_allowed = max_allowed;
> +
> +	qdev->dumb_heads = kcalloc(max_allowed, sizeof(qdev->dumb_heads[0]), GFP_KERNEL);

Needs an allocation failure check. With that:

Acked-by: Noralf Trønnes <noralf at tronnes.org>


>  	return 0;
>  }
>  
> diff --git a/drivers/gpu/drm/qxl/qxl_draw.c b/drivers/gpu/drm/qxl/qxl_draw.c
> index c408bb83c7..5313ad21c1 100644
> --- a/drivers/gpu/drm/qxl/qxl_draw.c
> +++ b/drivers/gpu/drm/qxl/qxl_draw.c
> @@ -267,7 +267,8 @@ void qxl_draw_dirty_fb(struct qxl_device *qdev,
>  		       struct qxl_bo *bo,
>  		       unsigned int flags, unsigned int color,
>  		       struct drm_clip_rect *clips,
> -		       unsigned int num_clips, int inc)
> +		       unsigned int num_clips, int inc,
> +		       uint32_t dumb_shadow_offset)
>  {
>  	/*
>  	 * TODO: if flags & DRM_MODE_FB_DIRTY_ANNOTATE_FILL then we should
> @@ -295,6 +296,9 @@ void qxl_draw_dirty_fb(struct qxl_device *qdev,
>  	if (ret)
>  		return;
>  
> +	clips->x1 += dumb_shadow_offset;
> +	clips->x2 += dumb_shadow_offset;
> +
>  	left = clips->x1;
>  	right = clips->x2;
>  	top = clips->y1;
> @@ -342,7 +346,8 @@ void qxl_draw_dirty_fb(struct qxl_device *qdev,
>  		goto out_release_backoff;
>  
>  	ret = qxl_image_init(qdev, release, dimage, surface_base,
> -			     left, top, width, height, depth, stride);
> +			     left - dumb_shadow_offset,
> +			     top, width, height, depth, stride);
>  	qxl_bo_kunmap(bo);
>  	if (ret)
>  		goto out_release_backoff;
> 


More information about the dri-devel mailing list