Skip to content

Commit

Permalink
vmwgfx: Clip cliprects against screen boundaries in present and dirty
Browse files Browse the repository at this point in the history
Signed-off-by: Jakob Bornecrantz <jakob@vmware.com>
Reviewed-by: Thomas Hellstrom <thellstrom@vmware.com>
Signed-off-by: Dave Airlie <airlied@redhat.com>
  • Loading branch information
Jakob Bornecrantz authored and Dave Airlie committed Dec 19, 2011
1 parent bfc2638 commit 6abff3c
Showing 1 changed file with 156 additions and 53 deletions.
209 changes: 156 additions & 53 deletions drivers/gpu/drm/vmwgfx/vmwgfx_kms.c
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,44 @@
/* Might need a hrtimer here? */
#define VMWGFX_PRESENT_RATE ((HZ / 60 > 0) ? HZ / 60 : 1)


struct vmw_clip_rect {
int x1, x2, y1, y2;
};

/**
* Clip @num_rects number of @rects against @clip storing the
* results in @out_rects and the number of passed rects in @out_num.
*/
void vmw_clip_cliprects(struct drm_clip_rect *rects,
int num_rects,
struct vmw_clip_rect clip,
SVGASignedRect *out_rects,
int *out_num)
{
int i, k;

for (i = 0, k = 0; i < num_rects; i++) {
int x1 = max_t(int, clip.x1, rects[i].x1);
int y1 = max_t(int, clip.y1, rects[i].y1);
int x2 = min_t(int, clip.x2, rects[i].x2);
int y2 = min_t(int, clip.y2, rects[i].y2);

if (x1 >= x2)
continue;
if (y1 >= y2)
continue;

out_rects[k].left = x1;
out_rects[k].top = y1;
out_rects[k].right = x2;
out_rects[k].bottom = y2;
k++;
}

*out_num = k;
}

void vmw_display_unit_cleanup(struct vmw_display_unit *du)
{
if (du->cursor_surface)
Expand Down Expand Up @@ -386,8 +424,9 @@ static int do_surface_dirty_sou(struct vmw_private *dev_priv,
struct drm_clip_rect *clips,
unsigned num_clips, int inc)
{
struct drm_clip_rect *clips_ptr;
struct vmw_display_unit *units[VMWGFX_NUM_DISPLAY_UNITS];
struct drm_clip_rect *clips_ptr;
struct drm_clip_rect *tmp;
struct drm_crtc *crtc;
size_t fifo_size;
int i, num_units;
Expand All @@ -400,7 +439,6 @@ static int do_surface_dirty_sou(struct vmw_private *dev_priv,
} *cmd;
SVGASignedRect *blits;


num_units = 0;
list_for_each_entry(crtc, &dev_priv->dev->mode_config.crtc_list,
head) {
Expand All @@ -411,13 +449,24 @@ static int do_surface_dirty_sou(struct vmw_private *dev_priv,

BUG_ON(!clips || !num_clips);

tmp = kzalloc(sizeof(*tmp) * num_clips, GFP_KERNEL);
if (unlikely(tmp == NULL)) {
DRM_ERROR("Temporary cliprect memory alloc failed.\n");
return -ENOMEM;
}

fifo_size = sizeof(*cmd) + sizeof(SVGASignedRect) * num_clips;
cmd = kzalloc(fifo_size, GFP_KERNEL);
if (unlikely(cmd == NULL)) {
DRM_ERROR("Temporary fifo memory alloc failed.\n");
return -ENOMEM;
ret = -ENOMEM;
goto out_free_tmp;
}

/* setup blits pointer */
blits = (SVGASignedRect *)&cmd[1];

/* initial clip region */
left = clips->x1;
right = clips->x2;
top = clips->y1;
Expand All @@ -443,53 +492,71 @@ static int do_surface_dirty_sou(struct vmw_private *dev_priv,
cmd->body.srcRect.bottom = bottom;

clips_ptr = clips;
blits = (SVGASignedRect *)&cmd[1];
for (i = 0; i < num_clips; i++, clips_ptr += inc) {
blits[i].left = clips_ptr->x1 - left;
blits[i].right = clips_ptr->x2 - left;
blits[i].top = clips_ptr->y1 - top;
blits[i].bottom = clips_ptr->y2 - top;
tmp[i].x1 = clips_ptr->x1 - left;
tmp[i].x2 = clips_ptr->x2 - left;
tmp[i].y1 = clips_ptr->y1 - top;
tmp[i].y2 = clips_ptr->y2 - top;
}

/* do per unit writing, reuse fifo for each */
for (i = 0; i < num_units; i++) {
struct vmw_display_unit *unit = units[i];
int clip_x1 = left - unit->crtc.x;
int clip_y1 = top - unit->crtc.y;
int clip_x2 = right - unit->crtc.x;
int clip_y2 = bottom - unit->crtc.y;
struct vmw_clip_rect clip;
int num;

clip.x1 = left - unit->crtc.x;
clip.y1 = top - unit->crtc.y;
clip.x2 = right - unit->crtc.x;
clip.y2 = bottom - unit->crtc.y;

/* skip any crtcs that misses the clip region */
if (clip_x1 >= unit->crtc.mode.hdisplay ||
clip_y1 >= unit->crtc.mode.vdisplay ||
clip_x2 <= 0 || clip_y2 <= 0)
if (clip.x1 >= unit->crtc.mode.hdisplay ||
clip.y1 >= unit->crtc.mode.vdisplay ||
clip.x2 <= 0 || clip.y2 <= 0)
continue;

/*
* In order for the clip rects to be correctly scaled
* the src and dest rects needs to be the same size.
*/
cmd->body.destRect.left = clip.x1;
cmd->body.destRect.right = clip.x2;
cmd->body.destRect.top = clip.y1;
cmd->body.destRect.bottom = clip.y2;

/* create a clip rect of the crtc in dest coords */
clip.x2 = unit->crtc.mode.hdisplay - clip.x1;
clip.y2 = unit->crtc.mode.vdisplay - clip.y1;
clip.x1 = 0 - clip.x1;
clip.y1 = 0 - clip.y1;

/* need to reset sid as it is changed by execbuf */
cmd->body.srcImage.sid = cpu_to_le32(framebuffer->user_handle);

cmd->body.destScreenId = unit->unit;

/*
* The blit command is a lot more resilient then the
* readback command when it comes to clip rects. So its
* okay to go out of bounds.
*/
/* clip and write blits to cmd stream */
vmw_clip_cliprects(tmp, num_clips, clip, blits, &num);

cmd->body.destRect.left = clip_x1;
cmd->body.destRect.right = clip_x2;
cmd->body.destRect.top = clip_y1;
cmd->body.destRect.bottom = clip_y2;
/* if no cliprects hit skip this */
if (num == 0)
continue;


/* recalculate package length */
fifo_size = sizeof(*cmd) + sizeof(SVGASignedRect) * num;
cmd->header.size = cpu_to_le32(fifo_size - sizeof(cmd->header));
ret = vmw_execbuf_process(file_priv, dev_priv, NULL, cmd,
fifo_size, 0, NULL);

if (unlikely(ret != 0))
break;
}


kfree(cmd);
out_free_tmp:
kfree(tmp);

return ret;
}
Expand Down Expand Up @@ -795,19 +862,29 @@ static int do_dmabuf_dirty_sou(struct drm_file *file_priv,
int clip_y1 = clips_ptr->y1 - unit->crtc.y;
int clip_x2 = clips_ptr->x2 - unit->crtc.x;
int clip_y2 = clips_ptr->y2 - unit->crtc.y;
int move_x, move_y;

/* skip any crtcs that misses the clip region */
if (clip_x1 >= unit->crtc.mode.hdisplay ||
clip_y1 >= unit->crtc.mode.vdisplay ||
clip_x2 <= 0 || clip_y2 <= 0)
continue;

/* clip size to crtc size */
clip_x2 = min_t(int, clip_x2, unit->crtc.mode.hdisplay);
clip_y2 = min_t(int, clip_y2, unit->crtc.mode.vdisplay);

/* translate both src and dest to bring clip into screen */
move_x = min_t(int, clip_x1, 0);
move_y = min_t(int, clip_y1, 0);

/* actual translate done here */
blits[hit_num].header = SVGA_CMD_BLIT_GMRFB_TO_SCREEN;
blits[hit_num].body.destScreenId = unit->unit;
blits[hit_num].body.srcOrigin.x = clips_ptr->x1;
blits[hit_num].body.srcOrigin.y = clips_ptr->y1;
blits[hit_num].body.destRect.left = clip_x1;
blits[hit_num].body.destRect.top = clip_y1;
blits[hit_num].body.srcOrigin.x = clips_ptr->x1 - move_x;
blits[hit_num].body.srcOrigin.y = clips_ptr->y1 - move_y;
blits[hit_num].body.destRect.left = clip_x1 - move_x;
blits[hit_num].body.destRect.top = clip_y1 - move_y;
blits[hit_num].body.destRect.right = clip_x2;
blits[hit_num].body.destRect.bottom = clip_y2;
hit_num++;
Expand Down Expand Up @@ -1094,6 +1171,7 @@ int vmw_kms_present(struct vmw_private *dev_priv,
uint32_t num_clips)
{
struct vmw_display_unit *units[VMWGFX_NUM_DISPLAY_UNITS];
struct drm_clip_rect *tmp;
struct drm_crtc *crtc;
size_t fifo_size;
int i, k, num_units;
Expand All @@ -1116,11 +1194,18 @@ int vmw_kms_present(struct vmw_private *dev_priv,
BUG_ON(surface == NULL);
BUG_ON(!clips || !num_clips);

tmp = kzalloc(sizeof(*tmp) * num_clips, GFP_KERNEL);
if (unlikely(tmp == NULL)) {
DRM_ERROR("Temporary cliprect memory alloc failed.\n");
return -ENOMEM;
}

fifo_size = sizeof(*cmd) + sizeof(SVGASignedRect) * num_clips;
cmd = kmalloc(fifo_size, GFP_KERNEL);
if (unlikely(cmd == NULL)) {
DRM_ERROR("Failed to allocate temporary fifo memory.\n");
return -ENOMEM;
ret = -ENOMEM;
goto out_free_tmp;
}

left = clips->x;
Expand All @@ -1138,50 +1223,66 @@ int vmw_kms_present(struct vmw_private *dev_priv,
/* only need to do this once */
memset(cmd, 0, fifo_size);
cmd->header.id = cpu_to_le32(SVGA_3D_CMD_BLIT_SURFACE_TO_SCREEN);
cmd->header.size = cpu_to_le32(fifo_size - sizeof(cmd->header));

blits = (SVGASignedRect *)&cmd[1];

cmd->body.srcRect.left = left;
cmd->body.srcRect.right = right;
cmd->body.srcRect.top = top;
cmd->body.srcRect.bottom = bottom;

blits = (SVGASignedRect *)&cmd[1];
for (i = 0; i < num_clips; i++) {
blits[i].left = clips[i].x - left;
blits[i].right = clips[i].x + clips[i].w - left;
blits[i].top = clips[i].y - top;
blits[i].bottom = clips[i].y + clips[i].h - top;
tmp[i].x1 = clips[i].x - left;
tmp[i].x2 = clips[i].x + clips[i].w - left;
tmp[i].y1 = clips[i].y - top;
tmp[i].y2 = clips[i].y + clips[i].h - top;
}

for (k = 0; k < num_units; k++) {
struct vmw_display_unit *unit = units[k];
int clip_x1 = left + destX - unit->crtc.x;
int clip_y1 = top + destY - unit->crtc.y;
int clip_x2 = right + destX - unit->crtc.x;
int clip_y2 = bottom + destY - unit->crtc.y;
struct vmw_clip_rect clip;
int num;

clip.x1 = left + destX - unit->crtc.x;
clip.y1 = top + destY - unit->crtc.y;
clip.x2 = right + destX - unit->crtc.x;
clip.y2 = bottom + destY - unit->crtc.y;

/* skip any crtcs that misses the clip region */
if (clip_x1 >= unit->crtc.mode.hdisplay ||
clip_y1 >= unit->crtc.mode.vdisplay ||
clip_x2 <= 0 || clip_y2 <= 0)
if (clip.x1 >= unit->crtc.mode.hdisplay ||
clip.y1 >= unit->crtc.mode.vdisplay ||
clip.x2 <= 0 || clip.y2 <= 0)
continue;

/*
* In order for the clip rects to be correctly scaled
* the src and dest rects needs to be the same size.
*/
cmd->body.destRect.left = clip.x1;
cmd->body.destRect.right = clip.x2;
cmd->body.destRect.top = clip.y1;
cmd->body.destRect.bottom = clip.y2;

/* create a clip rect of the crtc in dest coords */
clip.x2 = unit->crtc.mode.hdisplay - clip.x1;
clip.y2 = unit->crtc.mode.vdisplay - clip.y1;
clip.x1 = 0 - clip.x1;
clip.y1 = 0 - clip.y1;

/* need to reset sid as it is changed by execbuf */
cmd->body.srcImage.sid = sid;

cmd->body.destScreenId = unit->unit;

/*
* The blit command is a lot more resilient then the
* readback command when it comes to clip rects. So its
* okay to go out of bounds.
*/
/* clip and write blits to cmd stream */
vmw_clip_cliprects(tmp, num_clips, clip, blits, &num);

cmd->body.destRect.left = clip_x1;
cmd->body.destRect.right = clip_x2;
cmd->body.destRect.top = clip_y1;
cmd->body.destRect.bottom = clip_y2;
/* if no cliprects hit skip this */
if (num == 0)
continue;

/* recalculate package length */
fifo_size = sizeof(*cmd) + sizeof(SVGASignedRect) * num;
cmd->header.size = cpu_to_le32(fifo_size - sizeof(cmd->header));
ret = vmw_execbuf_process(file_priv, dev_priv, NULL, cmd,
fifo_size, 0, NULL);

Expand All @@ -1190,6 +1291,8 @@ int vmw_kms_present(struct vmw_private *dev_priv,
}

kfree(cmd);
out_free_tmp:
kfree(tmp);

return ret;
}
Expand Down

0 comments on commit 6abff3c

Please sign in to comment.