Use scissor registers to clip blit operations. This is required
for text rendering in X using the r128 driver. Without it overly-wide
glyphs are drawn and create all sorts of chaos.
Use QemuRect helpers for calculating the intersection of the
destination and scissor rectangles. Source coordinates are
also updated to reflect clipping. The original destination dimensions
are stored in 'dst' while the clipped rectangle is in 'visible' for
clear distinction between the two.
Signed-off-by: Chad Jablonski <chad@jablonski.xyz>
---
hw/display/ati_2d.c | 110 +++++++++++++++++++++++++++-----------------
1 file changed, 68 insertions(+), 42 deletions(-)
diff --git a/hw/display/ati_2d.c b/hw/display/ati_2d.c
index b8df549474..145eb487c4 100644
--- a/hw/display/ati_2d.c
+++ b/hw/display/ati_2d.c
@@ -13,6 +13,7 @@
#include "qemu/log.h"
#include "ui/pixel_ops.h"
#include "ui/console.h"
+#include "ui/rect.h"
/*
* NOTE:
@@ -43,6 +44,19 @@ static int ati_bpp_from_datatype(ATIVGAState *s)
}
}
+static QemuRect dst_rect(ATIVGAState *s)
+{
+ QemuRect dst;
+ unsigned dst_x = (s->regs.dp_cntl & DST_X_LEFT_TO_RIGHT ?
+ s->regs.dst_x :
+ s->regs.dst_x + 1 - s->regs.dst_width);
+ unsigned dst_y = (s->regs.dp_cntl & DST_Y_TOP_TO_BOTTOM ?
+ s->regs.dst_y :
+ s->regs.dst_y + 1 - s->regs.dst_height);
+ qemu_rect_init(&dst, dst_x, dst_y, s->regs.dst_width, s->regs.dst_height);
+ return dst;
+}
+
void ati_2d_blt(ATIVGAState *s)
{
/* FIXME it is probably more complex than this and may need to be */
@@ -51,11 +65,21 @@ void ati_2d_blt(ATIVGAState *s)
DPRINTF("%p %u ds: %p %d %d rop: %x\n", s->vga.vram_ptr,
s->vga.vbe_start_addr, surface_data(ds), surface_stride(ds),
surface_bits_per_pixel(ds),
- (s->regs.dp_rop3));
- unsigned dst_x = (s->regs.dp_cntl & DST_X_LEFT_TO_RIGHT ?
- s->regs.dst_x : s->regs.dst_x + 1 - s->regs.dst_width);
- unsigned dst_y = (s->regs.dp_cntl & DST_Y_TOP_TO_BOTTOM ?
- s->regs.dst_y : s->regs.dst_y + 1 - s->regs.dst_height);
+ s->regs.dp_rop3);
+
+ QemuRect dst = dst_rect(s);
+ QemuRect scissor;
+ qemu_rect_init(&scissor,
+ s->regs.sc_left, s->regs.sc_top,
+ s->regs.sc_right - s->regs.sc_left + 1,
+ s->regs.sc_bottom - s->regs.sc_top + 1);
+ QemuRect visible;
+ if (!qemu_rect_intersect(&dst, &scissor, &visible)) {
+ return;
+ }
+ uint32_t src_left_offset = visible.x - dst.x;
+ uint32_t src_top_offset = visible.y - dst.y;
+
int bpp = ati_bpp_from_datatype(s);
if (!bpp) {
qemu_log_mask(LOG_GUEST_ERROR, "Invalid bpp\n");
@@ -73,17 +97,16 @@ void ati_2d_blt(ATIVGAState *s)
dst_stride *= bpp;
}
uint8_t *end = s->vga.vram_ptr + s->vga.vram_size;
- if (dst_x > 0x3fff || dst_y > 0x3fff || dst_bits >= end
- || dst_bits + dst_x
- + (dst_y + s->regs.dst_height) * dst_stride >= end) {
+ if (visible.x > 0x3fff || visible.y > 0x3fff || dst_bits >= end
+ || dst_bits + visible.x
+ + (visible.y + visible.height) * dst_stride >= end) {
qemu_log_mask(LOG_UNIMP, "blt outside vram not implemented\n");
return;
}
DPRINTF("%d %d %d, %d %d %d, (%d,%d) -> (%d,%d) %dx%d %c %c\n",
s->regs.src_offset, s->regs.dst_offset, s->regs.default_offset,
s->regs.src_pitch, s->regs.dst_pitch, s->regs.default_pitch,
- s->regs.src_x, s->regs.src_y, dst_x, dst_y,
- s->regs.dst_width, s->regs.dst_height,
+ s->regs.src_x, s->regs.src_y, dst.x, dst.y, dst.width, dst.height,
(s->regs.dp_cntl & DST_X_LEFT_TO_RIGHT ? '>' : '<'),
(s->regs.dp_cntl & DST_Y_TOP_TO_BOTTOM ? 'v' : '^'));
switch (s->regs.dp_rop3) {
@@ -91,9 +114,11 @@ void ati_2d_blt(ATIVGAState *s)
{
bool fallback = false;
unsigned src_x = (s->regs.dp_cntl & DST_X_LEFT_TO_RIGHT ?
- s->regs.src_x : s->regs.src_x + 1 - s->regs.dst_width);
+ s->regs.src_x + src_left_offset :
+ s->regs.src_x + 1 - dst.width + src_left_offset);
unsigned src_y = (s->regs.dp_cntl & DST_Y_TOP_TO_BOTTOM ?
- s->regs.src_y : s->regs.src_y + 1 - s->regs.dst_height);
+ s->regs.src_y + src_top_offset :
+ s->regs.src_y + 1 - dst.height + src_top_offset);
int src_stride = s->regs.src_pitch;
if (!src_stride) {
qemu_log_mask(LOG_GUEST_ERROR, "Zero source pitch\n");
@@ -107,7 +132,7 @@ void ati_2d_blt(ATIVGAState *s)
}
if (src_x > 0x3fff || src_y > 0x3fff || src_bits >= end
|| src_bits + src_x
- + (src_y + s->regs.dst_height) * src_stride >= end) {
+ + (src_y + visible.height) * src_stride >= end) {
qemu_log_mask(LOG_UNIMP, "blt outside vram not implemented\n");
return;
}
@@ -116,31 +141,31 @@ void ati_2d_blt(ATIVGAState *s)
dst_stride /= sizeof(uint32_t);
DPRINTF("pixman_blt(%p, %p, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d)\n",
src_bits, dst_bits, src_stride, dst_stride, bpp, bpp,
- src_x, src_y, dst_x, dst_y,
- s->regs.dst_width, s->regs.dst_height);
+ src_x, src_y, visible.x, visible.y,
+ visible.width, visible.height);
#ifdef CONFIG_PIXMAN
if ((s->use_pixman & BIT(1)) &&
s->regs.dp_cntl & DST_X_LEFT_TO_RIGHT &&
s->regs.dp_cntl & DST_Y_TOP_TO_BOTTOM) {
fallback = !pixman_blt((uint32_t *)src_bits, (uint32_t *)dst_bits,
src_stride, dst_stride, bpp, bpp,
- src_x, src_y, dst_x, dst_y,
- s->regs.dst_width, s->regs.dst_height);
+ src_x, src_y, visible.x, visible.y,
+ visible.width, visible.height);
} else if (s->use_pixman & BIT(1)) {
/* FIXME: We only really need a temporary if src and dst overlap */
- int llb = s->regs.dst_width * (bpp / 8);
+ int llb = visible.width * (bpp / 8);
int tmp_stride = DIV_ROUND_UP(llb, sizeof(uint32_t));
uint32_t *tmp = g_malloc(tmp_stride * sizeof(uint32_t) *
- s->regs.dst_height);
+ visible.height);
fallback = !pixman_blt((uint32_t *)src_bits, tmp,
src_stride, tmp_stride, bpp, bpp,
src_x, src_y, 0, 0,
- s->regs.dst_width, s->regs.dst_height);
+ visible.width, visible.height);
if (!fallback) {
fallback = !pixman_blt(tmp, (uint32_t *)dst_bits,
tmp_stride, dst_stride, bpp, bpp,
- 0, 0, dst_x, dst_y,
- s->regs.dst_width, s->regs.dst_height);
+ 0, 0, visible.x, visible.y,
+ visible.width, visible.height);
}
g_free(tmp);
} else
@@ -153,17 +178,17 @@ void ati_2d_blt(ATIVGAState *s)
unsigned int src_pitch = src_stride * sizeof(uint32_t);
unsigned int dst_pitch = dst_stride * sizeof(uint32_t);
- for (y = 0; y < s->regs.dst_height; y++) {
- i = dst_x * bypp;
+ for (y = 0; y < visible.height; y++) {
+ i = visible.x * bypp;
j = src_x * bypp;
if (s->regs.dp_cntl & DST_Y_TOP_TO_BOTTOM) {
- i += (dst_y + y) * dst_pitch;
+ i += (visible.y + y) * dst_pitch;
j += (src_y + y) * src_pitch;
} else {
- i += (dst_y + s->regs.dst_height - 1 - y) * dst_pitch;
- j += (src_y + s->regs.dst_height - 1 - y) * src_pitch;
+ i += (visible.y + visible.height - 1 - y) * dst_pitch;
+ j += (src_y + visible.height - 1 - y) * src_pitch;
}
- memmove(&dst_bits[i], &src_bits[j], s->regs.dst_width * bypp);
+ memmove(&dst_bits[i], &src_bits[j], visible.width * bypp);
}
}
if (dst_bits >= s->vga.vram_ptr + s->vga.vbe_start_addr &&
@@ -171,13 +196,13 @@ void ati_2d_blt(ATIVGAState *s)
s->vga.vbe_regs[VBE_DISPI_INDEX_YRES] * s->vga.vbe_line_offset) {
memory_region_set_dirty(&s->vga.vram, s->vga.vbe_start_addr +
s->regs.dst_offset +
- dst_y * surface_stride(ds),
- s->regs.dst_height * surface_stride(ds));
+ visible.y * surface_stride(ds),
+ visible.height * surface_stride(ds));
}
s->regs.dst_x = (s->regs.dp_cntl & DST_X_LEFT_TO_RIGHT ?
- dst_x + s->regs.dst_width : dst_x);
+ visible.x + visible.width : visible.x);
s->regs.dst_y = (s->regs.dp_cntl & DST_Y_TOP_TO_BOTTOM ?
- dst_y + s->regs.dst_height : dst_y);
+ visible.y + visible.height : visible.y);
break;
}
case ROP3_PATCOPY:
@@ -202,20 +227,21 @@ void ati_2d_blt(ATIVGAState *s)
dst_stride /= sizeof(uint32_t);
DPRINTF("pixman_fill(%p, %d, %d, %d, %d, %d, %d, %x)\n",
- dst_bits, dst_stride, bpp, dst_x, dst_y,
- s->regs.dst_width, s->regs.dst_height, filler);
+ dst_bits, dst_stride, bpp, visible.x, visible.y,
+ visible.width, visible.height, filler);
#ifdef CONFIG_PIXMAN
if (!(s->use_pixman & BIT(0)) ||
- !pixman_fill((uint32_t *)dst_bits, dst_stride, bpp, dst_x, dst_y,
- s->regs.dst_width, s->regs.dst_height, filler))
+ !pixman_fill((uint32_t *)dst_bits, dst_stride, bpp,
+ visible.x, visible.y, visible.width, visible.height,
+ filler))
#endif
{
/* fallback when pixman failed or we don't want to call it */
unsigned int x, y, i, bypp = bpp / 8;
unsigned int dst_pitch = dst_stride * sizeof(uint32_t);
- for (y = 0; y < s->regs.dst_height; y++) {
- i = dst_x * bypp + (dst_y + y) * dst_pitch;
- for (x = 0; x < s->regs.dst_width; x++, i += bypp) {
+ for (y = 0; y < visible.height; y++) {
+ i = visible.x * bypp + (visible.y + y) * dst_pitch;
+ for (x = 0; x < visible.width; x++, i += bypp) {
stn_he_p(&dst_bits[i], bypp, filler);
}
}
@@ -225,11 +251,11 @@ void ati_2d_blt(ATIVGAState *s)
s->vga.vbe_regs[VBE_DISPI_INDEX_YRES] * s->vga.vbe_line_offset) {
memory_region_set_dirty(&s->vga.vram, s->vga.vbe_start_addr +
s->regs.dst_offset +
- dst_y * surface_stride(ds),
- s->regs.dst_height * surface_stride(ds));
+ visible.y * surface_stride(ds),
+ visible.height * surface_stride(ds));
}
s->regs.dst_y = (s->regs.dp_cntl & DST_Y_TOP_TO_BOTTOM ?
- dst_y + s->regs.dst_height : dst_y);
+ visible.y + visible.height : visible.y);
break;
}
default:
--
2.51.2
© 2016 - 2026 Red Hat, Inc.