master
c 474 lines 16.4 KB
Raw
1 /*
2 * QEMU ATI SVGA emulation
3 * 2D engine functions
4 *
5 * Copyright (c) 2019 BALATON Zoltan
6 *
7 * This work is licensed under the GNU GPL license version 2 or later.
8 */
9
10 #include "qemu/osdep.h"
11 #include "ati_int.h"
12 #include "ati_regs.h"
13 #include "qemu/log.h"
14 #include "ui/pixel_ops.h"
15 #include "ui/console.h"
16 #include "ui/rect.h"
17
18 /*
19 * NOTE:
20 * This is 2D _acceleration_ and supposed to be fast. Therefore, don't try to
21 * reinvent the wheel (unlikely to get better with a naive implementation than
22 * existing libraries) and avoid (poorly) reimplementing gfx primitives.
23 * That is unnecessary and would become a performance problem. Instead, try to
24 * map to and reuse existing optimised facilities (e.g. pixman) wherever
25 * possible.
26 */
27
28 static int ati_bpp_from_datatype(const ATIVGAState *s)
29 {
30 switch (s->regs.dp_datatype & 0xf) {
31 case 2:
32 return 8;
33 case 3:
34 case 4:
35 return 16;
36 case 5:
37 return 24;
38 case 6:
39 return 32;
40 default:
41 qemu_log_mask(LOG_UNIMP, "Unknown dst datatype %d\n",
42 s->regs.dp_datatype & 0xf);
43 return 0;
44 }
45 }
46
47 typedef struct {
48 VGACommonState *vga;
49 int bpp;
50 uint32_t rop3;
51 bool host_data_active;
52 bool left_to_right;
53 bool top_to_bottom;
54 bool need_swap;
55 uint32_t frgd_clr;
56 QemuRect scissor;
57
58 QemuRect dst;
59 int dst_stride;
60 uint8_t *dst_bits;
61 uint32_t dst_offset;
62
63 QemuRect src;
64 int src_stride;
65 const uint8_t *src_bits;
66 } ATI2DCtx;
67
68 static void ati_set_dirty(const ATI2DCtx *ctx)
69 {
70 VGACommonState *vga = ctx->vga;
71 DisplaySurface *ds = qemu_console_surface(vga->con);
72 unsigned int bypp = ctx->bpp / 8;
73 hwaddr dirty_start = ctx->dst_offset + ctx->dst.x * bypp +
74 ctx->dst.y * ctx->dst_stride;
75 hwaddr dirty_end = dirty_start + ctx->dst.width * bypp +
76 (ctx->dst.height - 1) * ctx->dst_stride;
77 /*
78 * The blit may be outside of the visible screen (e.g. virtual desktops.)
79 * Dirty only the intersection of the visible screen and the blit.
80 */
81 hwaddr vis_start = vga->vbe_start_addr * 4;
82 hwaddr vis_end = vis_start + vga->vbe_regs[VBE_DISPI_INDEX_YRES] *
83 vga->vbe_line_offset;
84 hwaddr start = MAX(vis_start, dirty_start);
85 hwaddr end = MIN(vis_end, dirty_end);
86
87 (void)ds;
88 DPRINTF("%p %u ds: %p %d %d rop: %x\n", vga->vram_ptr, vga->vbe_start_addr,
89 surface_data(ds), surface_stride(ds), surface_bits_per_pixel(ds),
90 ctx->rop3 >> 16);
91
92 if (start < end) {
93 memory_region_set_dirty(&vga->vram, start, end - start);
94 }
95 }
96
97 static void setup_2d_blt_ctx(ATIVGAState *s, ATI2DCtx *ctx)
98 {
99 ctx->vga = &s->vga;
100 ctx->bpp = ati_bpp_from_datatype(s);
101 ctx->rop3 = s->regs.dp_mix & GMC_ROP3_MASK;
102 ctx->host_data_active = s->host_data.active;
103 ctx->left_to_right = s->regs.dp_cntl & DST_X_LEFT_TO_RIGHT;
104 ctx->top_to_bottom = s->regs.dp_cntl & DST_Y_TOP_TO_BOTTOM;
105 ctx->need_swap = (HOST_BIG_ENDIAN != s->vga.big_endian_fb);
106 ctx->frgd_clr = s->regs.dp_brush_frgd_clr;
107 ctx->dst_offset = s->regs.dst_offset;
108
109 ctx->scissor.width = s->regs.sc_right - s->regs.sc_left + 1;
110 ctx->scissor.height = s->regs.sc_bottom - s->regs.sc_top + 1;
111 ctx->scissor.x = s->regs.sc_left;
112 ctx->scissor.y = s->regs.sc_top;
113
114 ctx->dst.width = s->regs.dst_width;
115 ctx->dst.height = s->regs.dst_height;
116 ctx->dst.x = (ctx->left_to_right ?
117 s->regs.dst_x : s->regs.dst_x + 1 - ctx->dst.width);
118 ctx->dst.y = (ctx->top_to_bottom ?
119 s->regs.dst_y : s->regs.dst_y + 1 - ctx->dst.height);
120 ctx->dst_stride = s->regs.dst_pitch;
121 ctx->dst_bits = s->vga.vram_ptr + s->regs.dst_offset;
122 if (s->dev_id == PCI_DEVICE_ID_ATI_RAGE128_PF) {
123 ctx->dst_stride *= ctx->bpp;
124 }
125
126 ctx->src.x = (ctx->left_to_right ?
127 s->regs.src_x : s->regs.src_x + 1 - ctx->dst.width);
128 ctx->src.y = (ctx->top_to_bottom ?
129 s->regs.src_y : s->regs.src_y + 1 - ctx->dst.height);
130 ctx->src_stride = s->regs.src_pitch;
131 ctx->src_bits = s->vga.vram_ptr + s->regs.src_offset;
132 if (s->dev_id == PCI_DEVICE_ID_ATI_RAGE128_PF) {
133 ctx->src_stride *= ctx->bpp;
134 }
135 DPRINTF("%d %d %d, %d %d %d, (%d,%d) -> (%d,%d) %dx%d %c %c\n",
136 s->regs.src_offset, s->regs.dst_offset, s->regs.default_offset,
137 ctx->src_stride, ctx->dst_stride, s->regs.default_pitch,
138 ctx->src.x, ctx->src.y, ctx->dst.x, ctx->dst.y,
139 ctx->dst.width, ctx->dst.height,
140 (ctx->left_to_right ? '>' : '<'),
141 (ctx->top_to_bottom ? 'v' : '^'));
142 }
143
144 static uint32_t make_filler(int bpp, uint32_t color)
145 {
146 if (bpp < 24) {
147 color |= color << 16;
148 if (bpp < 15) {
149 color |= color << 8;
150 }
151 }
152 return color;
153 }
154
155 static bool ati_2d_do_blt(const ATI2DCtx *ctx, uint8_t use_pixman)
156 {
157 QemuRect vis_src, vis_dst;
158 unsigned int x, y, i, j, bypp = ctx->bpp / 8;
159 const uint8_t *vram_end = ctx->vga->vram_ptr + ctx->vga->vram_size;
160
161 if (!ctx->bpp) {
162 qemu_log_mask(LOG_GUEST_ERROR, "Invalid bpp\n");
163 return false;
164 }
165 if (!ctx->dst_stride) {
166 qemu_log_mask(LOG_GUEST_ERROR, "Zero dest pitch\n");
167 return false;
168 }
169 if (ctx->dst.x > 0x3fff || ctx->dst.y > 0x3fff ||
170 ctx->dst_bits >= vram_end - bypp || ctx->dst_bits + ctx->dst.x * bypp +
171 (ctx->dst.y + ctx->dst.height) * ctx->dst_stride >= vram_end - bypp) {
172 qemu_log_mask(LOG_UNIMP, "blt outside vram not implemented\n");
173 return false;
174 }
175 qemu_rect_intersect(&ctx->dst, &ctx->scissor, &vis_dst);
176 if (!vis_dst.height || !vis_dst.width) {
177 /* Nothing is visible, completely clipped */
178 return false;
179 }
180 /*
181 * The src must be offset if clipping is applied to the dst.
182 * This is so that when the source is blit to a dst clipped
183 * on the top or left the src image is not shifted into the
184 * clipped region but actually clipped.
185 */
186 vis_src.x = ctx->src.x + (vis_dst.x - ctx->dst.x);
187 vis_src.y = ctx->src.y + (vis_dst.y - ctx->dst.y);
188 vis_src.width = vis_dst.width;
189 vis_src.height = vis_dst.height;
190
191 DPRINTF("dst: (%d,%d) %dx%d -> vis_dst: (%d,%d) %dx%d\n",
192 ctx->dst.x, ctx->dst.y, ctx->dst.width, ctx->dst.height,
193 vis_dst.x, vis_dst.y, vis_dst.width, vis_dst.height);
194 DPRINTF("src: (%d,%d) %dx%d -> vis_src: (%d,%d) %dx%d\n",
195 ctx->src.x, ctx->src.y, ctx->dst.width, ctx->dst.height,
196 vis_src.x, vis_src.y, vis_src.width, vis_src.height);
197
198 switch (ctx->rop3) {
199 case ROP3_SRCCOPY:
200 {
201 bool fallback = false;
202 if (!ctx->src_stride) {
203 qemu_log_mask(LOG_GUEST_ERROR, "Zero source pitch\n");
204 return false;
205 }
206 if (!ctx->host_data_active &&
207 (vis_src.x > 0x3fff || vis_src.y > 0x3fff ||
208 ctx->src_bits >= vram_end - bypp ||
209 ctx->src_bits + vis_src.x * bypp + (vis_src.y + vis_dst.height) *
210 ctx->src_stride >= vram_end - bypp)) {
211 qemu_log_mask(LOG_UNIMP, "blt outside vram not implemented\n");
212 return false;
213 }
214
215 DPRINTF("pixman_blt(%p, %p, %ld, %ld, %d, %d, %d, %d, %d, %d, %d, %d)\n",
216 ctx->src_bits, ctx->dst_bits,
217 ctx->src_stride / sizeof(uint32_t),
218 ctx->dst_stride / sizeof(uint32_t),
219 ctx->bpp, ctx->bpp, vis_src.x, vis_src.y, vis_dst.x, vis_dst.y,
220 vis_dst.width, vis_dst.height);
221 #ifdef CONFIG_PIXMAN
222 int src_stride_words = ctx->src_stride / sizeof(uint32_t);
223 int dst_stride_words = ctx->dst_stride / sizeof(uint32_t);
224 if ((use_pixman & BIT(1)) &&
225 ctx->left_to_right && ctx->top_to_bottom) {
226 fallback = !pixman_blt((uint32_t *)ctx->src_bits,
227 (uint32_t *)ctx->dst_bits, src_stride_words,
228 dst_stride_words, ctx->bpp, ctx->bpp,
229 vis_src.x, vis_src.y, vis_dst.x, vis_dst.y,
230 vis_dst.width, vis_dst.height);
231 } else if (use_pixman & BIT(1)) {
232 /* FIXME: We only really need a temporary if src and dst overlap */
233 int llb = vis_dst.width * (ctx->bpp / 8);
234 int tmp_stride_words = DIV_ROUND_UP(llb, sizeof(uint32_t));
235 uint32_t *tmp = g_malloc(tmp_stride_words * sizeof(uint32_t) *
236 vis_dst.height);
237 fallback = !pixman_blt((uint32_t *)ctx->src_bits, tmp,
238 src_stride_words, tmp_stride_words, ctx->bpp,
239 ctx->bpp, vis_src.x, vis_src.y, 0, 0,
240 vis_dst.width, vis_dst.height);
241 if (!fallback) {
242 fallback = !pixman_blt(tmp, (uint32_t *)ctx->dst_bits,
243 tmp_stride_words, dst_stride_words,
244 ctx->bpp, ctx->bpp, 0, 0,
245 vis_dst.x, vis_dst.y,
246 vis_dst.width, vis_dst.height);
247 }
248 g_free(tmp);
249 } else
250 #endif
251 {
252 fallback = true;
253 }
254 if (fallback) {
255 for (y = 0; y < vis_dst.height; y++) {
256 i = vis_dst.x * bypp;
257 j = vis_src.x * bypp;
258 if (ctx->top_to_bottom) {
259 i += (vis_dst.y + y) * ctx->dst_stride;
260 j += (vis_src.y + y) * ctx->src_stride;
261 } else {
262 i += (vis_dst.y + vis_dst.height - 1 - y)
263 * ctx->dst_stride;
264 j += (vis_src.y + vis_dst.height - 1 - y)
265 * ctx->src_stride;
266 }
267 memmove(&ctx->dst_bits[i], &ctx->src_bits[j],
268 vis_dst.width * bypp);
269 }
270 }
271 break;
272 }
273 case ROP3_PATCOPY:
274 case ROP3_BLACKNESS:
275 case ROP3_WHITENESS:
276 {
277 const uint8_t *palette = ctx->vga->palette;
278 uint32_t filler = 0;
279
280 if (ctx->bpp == 24) {
281 qemu_log_mask(LOG_UNIMP, "Fill blt unsupported in 24 bits\n");
282 return false;
283 }
284 switch (ctx->rop3) {
285 case ROP3_PATCOPY:
286 filler = make_filler(ctx->bpp, ctx->frgd_clr);
287 break;
288 case ROP3_BLACKNESS:
289 filler = 0xffUL << 24 | rgb_to_pixel32(palette[0], palette[1],
290 palette[2]);
291 break;
292 case ROP3_WHITENESS:
293 filler = 0xffUL << 24 | rgb_to_pixel32(palette[3], palette[4],
294 palette[5]);
295 break;
296 }
297 DPRINTF("pixman_fill(%p, %ld, %d, %d, %d, %d, %d, %x)\n",
298 ctx->dst_bits, ctx->dst_stride / sizeof(uint32_t), ctx->bpp,
299 vis_dst.x, vis_dst.y, vis_dst.width, vis_dst.height, filler);
300 if (ctx->need_swap) {
301 bswap32s(&filler);
302 }
303 #ifdef CONFIG_PIXMAN
304 if (!(use_pixman & BIT(0)) ||
305 !pixman_fill((uint32_t *)ctx->dst_bits,
306 ctx->dst_stride / sizeof(uint32_t), ctx->bpp,
307 vis_dst.x, vis_dst.y, vis_dst.width, vis_dst.height,
308 filler))
309 #endif
310 {
311 /* fallback when pixman failed or we don't want to call it */
312 for (y = 0; y < vis_dst.height; y++) {
313 i = vis_dst.x * bypp + (vis_dst.y + y) * ctx->dst_stride;
314 for (x = 0; x < vis_dst.width; x++, i += bypp) {
315 stn_he_p(&ctx->dst_bits[i], bypp, filler);
316 }
317 }
318 }
319 break;
320 }
321 default:
322 qemu_log_mask(LOG_UNIMP, "Unimplemented ati_2d blt op %x\n",
323 ctx->rop3 >> 16);
324 return false;
325 }
326
327 return true;
328 }
329
330 void ati_2d_blt(ATIVGAState *s)
331 {
332 ATI2DCtx ctx;
333 uint32_t src_source = s->regs.dp_mix & DP_SRC_SOURCE;
334
335 /* Finish any active HOST_DATA blits before starting a new blit */
336 ati_host_data_finish(s);
337
338 if (src_source == DP_SRC_HOST || src_source == DP_SRC_HOST_BYTEALIGN) {
339 /* Begin a HOST_DATA blit */
340 s->host_data.active = true;
341 s->host_data.next = 0;
342 s->host_data.col = 0;
343 s->host_data.row = 0;
344 return;
345 }
346 setup_2d_blt_ctx(s, &ctx);
347 if (ati_2d_do_blt(&ctx, s->use_pixman)) {
348 ati_set_dirty(&ctx);
349 }
350 }
351
352 bool ati_host_data_flush(ATIVGAState *s)
353 {
354 ATI2DCtx ctx, chunk;
355 unsigned bypp, pix_count, row, col, idx;
356 uint8_t pix_buf[ATI_HOST_DATA_ACC_BITS * sizeof(uint32_t)];
357 uint32_t src_source = s->regs.dp_mix & DP_SRC_SOURCE;
358 uint32_t src_datatype = s->regs.dp_datatype & DP_SRC_DATATYPE;
359
360 if (!s->host_data.active) {
361 return false;
362 }
363 if (src_source != DP_SRC_HOST) {
364 qemu_log_mask(LOG_GUEST_ERROR,
365 "host_data_blt: unsupported src_source %x\n", src_source);
366 return false;
367 }
368 if (src_datatype != SRC_MONO_FRGD_BKGD && src_datatype != SRC_MONO_FRGD &&
369 src_datatype != SRC_COLOR) {
370 qemu_log_mask(LOG_GUEST_ERROR,
371 "host_data_blt: undefined src_datatype %x\n",
372 src_datatype);
373 return false;
374 }
375
376 setup_2d_blt_ctx(s, &ctx);
377
378 if (!ctx.bpp) {
379 qemu_log_mask(LOG_GUEST_ERROR,
380 "host_data_blt: invalid bpp from datatype\n");
381 return false;
382 }
383 if (ctx.bpp == 24) {
384 qemu_log_mask(LOG_UNIMP,
385 "host_data_blt: unsupported in 24 bits mode\n");
386 return false;
387 }
388 if (!ctx.left_to_right || !ctx.top_to_bottom) {
389 qemu_log_mask(LOG_UNIMP,
390 "host_data_blt: unsupported blit direction %c%c\n",
391 ctx.left_to_right ? '>' : '<',
392 ctx.top_to_bottom ? 'v' : '^');
393 return false;
394 }
395
396 bypp = ctx.bpp / 8;
397 pix_count = ATI_HOST_DATA_ACC_BITS;
398 if (src_datatype == SRC_COLOR) {
399 pix_count /= ctx.bpp;
400 memcpy(pix_buf, s->host_data.acc, sizeof(s->host_data.acc));
401 } else {
402 /* Expand monochrome bits to color pixels */
403 uint32_t byte_pix_order = s->regs.dp_datatype & DP_BYTE_PIX_ORDER;
404 uint32_t fg = make_filler(ctx.bpp, s->regs.dp_src_frgd_clr);
405 uint32_t bg = make_filler(ctx.bpp, s->regs.dp_src_bkgd_clr);
406
407 if (ctx.need_swap) {
408 bswap32s(&fg);
409 bswap32s(&bg);
410 }
411 idx = 0;
412 for (int word = 0; word < 4; word++) {
413 for (int byte = 0; byte < 4; byte++) {
414 uint8_t byte_val = s->host_data.acc[word] >> (byte * 8);
415 for (int i = 0; i < 8; i++) {
416 bool is_fg = byte_val & BIT(byte_pix_order ? i : 7 - i);
417 stn_he_p(&pix_buf[idx], bypp, is_fg ? fg : bg);
418 idx += bypp;
419 }
420 }
421 }
422 }
423
424 /* Copy and then modify blit ctx for use in a chunked blit */
425 chunk = ctx;
426 chunk.src_bits = pix_buf;
427 chunk.src.y = 0;
428 chunk.src_stride = ATI_HOST_DATA_ACC_BITS * bypp;
429
430 /* Blit one scanline chunk at a time */
431 row = s->host_data.row;
432 col = s->host_data.col;
433 idx = 0;
434 DPRINTF("blt %dpx @ row: %d, col: %d\n", pix_count, row, col);
435 while (idx < pix_count && row < ctx.dst.height) {
436 unsigned pix_in_scanline = MIN(pix_count - idx,
437 ctx.dst.width - col);
438 chunk.src.x = idx;
439 /* Build a rect for this scanline chunk */
440 chunk.dst.x = ctx.dst.x + col;
441 chunk.dst.y = ctx.dst.y + row;
442 chunk.dst.width = pix_in_scanline;
443 chunk.dst.height = 1;
444 DPRINTF("blt %dpx span @ row: %d, col: %d to dst (%d,%d)\n",
445 pix_in_scanline, row, col, chunk.dst.x, chunk.dst.y);
446 if (ati_2d_do_blt(&chunk, s->use_pixman)) {
447 ati_set_dirty(&chunk);
448 }
449 idx += pix_in_scanline;
450 col += pix_in_scanline;
451 if (col >= ctx.dst.width) {
452 col = 0;
453 row += 1;
454 }
455 }
456
457 /* Track state of the overall blit for use by the next flush */
458 s->host_data.row = row;
459 s->host_data.col = col;
460 if (s->host_data.row >= ctx.dst.height) {
461 s->host_data.active = false;
462 }
463
464 return s->host_data.active;
465 }
466
467 void ati_host_data_finish(ATIVGAState *s)
468 {
469 if (ati_host_data_flush(s)) {
470 qemu_log_mask(LOG_GUEST_ERROR,
471 "HOST_DATA blit ended before all data was written\n");
472 }
473 s->host_data.active = false;
474 }