master
c 1,515 lines 48.8 KB
Raw
1 /*
2 * Copyright (C) 2010 Red Hat, Inc.
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License as
6 * published by the Free Software Foundation; either version 2 or
7 * (at your option) version 3 of the License.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, see <http://www.gnu.org/licenses/>.
16 */
17
18 #include "qemu/osdep.h"
19 #include "ui/qemu-spice.h"
20 #include "qemu/error-report.h"
21 #include "qemu/timer.h"
22 #include "qemu/lockable.h"
23 #include "qemu/main-loop.h"
24 #include "qemu/option.h"
25 #include "qemu/queue.h"
26 #include "ui/console.h"
27 #include "trace.h"
28
29 #include "ui/spice-display.h"
30
31 #include "standard-headers/drm/drm_fourcc.h"
32
33 bool spice_opengl;
34 bool spice_remote_client;
35 int spice_max_refresh_rate;
36
37 static GPtrArray *spice_displays;
38
39 int qemu_spice_rect_is_empty(const QXLRect* r)
40 {
41 return r->top == r->bottom || r->left == r->right;
42 }
43
44 void qemu_spice_rect_union(QXLRect *dest, const QXLRect *r)
45 {
46 if (qemu_spice_rect_is_empty(r)) {
47 return;
48 }
49
50 if (qemu_spice_rect_is_empty(dest)) {
51 *dest = *r;
52 return;
53 }
54
55 dest->top = MIN(dest->top, r->top);
56 dest->left = MIN(dest->left, r->left);
57 dest->bottom = MAX(dest->bottom, r->bottom);
58 dest->right = MAX(dest->right, r->right);
59 }
60
61 QXLCookie *qxl_cookie_new(int type, uint64_t io)
62 {
63 QXLCookie *cookie;
64
65 cookie = g_malloc0(sizeof(*cookie));
66 cookie->type = type;
67 cookie->io = io;
68 return cookie;
69 }
70
71 void qemu_spice_add_memslot(SimpleSpiceDisplay *ssd, QXLDevMemSlot *memslot,
72 qxl_async_io async)
73 {
74 trace_qemu_spice_add_memslot(ssd->qxl.id, memslot->slot_id,
75 memslot->virt_start, memslot->virt_end,
76 async);
77
78 if (async != QXL_SYNC) {
79 spice_qxl_add_memslot_async(&ssd->qxl, memslot,
80 (uintptr_t)qxl_cookie_new(QXL_COOKIE_TYPE_IO,
81 QXL_IO_MEMSLOT_ADD_ASYNC));
82 } else {
83 spice_qxl_add_memslot(&ssd->qxl, memslot);
84 }
85 }
86
87 void qemu_spice_del_memslot(SimpleSpiceDisplay *ssd, uint32_t gid, uint32_t sid)
88 {
89 trace_qemu_spice_del_memslot(ssd->qxl.id, gid, sid);
90 spice_qxl_del_memslot(&ssd->qxl, gid, sid);
91 }
92
93 void qemu_spice_create_primary_surface(SimpleSpiceDisplay *ssd, uint32_t id,
94 QXLDevSurfaceCreate *surface,
95 qxl_async_io async)
96 {
97 trace_qemu_spice_create_primary_surface(ssd->qxl.id, id, surface, async);
98 if (async != QXL_SYNC) {
99 spice_qxl_create_primary_surface_async(&ssd->qxl, id, surface,
100 (uintptr_t)qxl_cookie_new(QXL_COOKIE_TYPE_IO,
101 QXL_IO_CREATE_PRIMARY_ASYNC));
102 } else {
103 spice_qxl_create_primary_surface(&ssd->qxl, id, surface);
104 }
105 }
106
107 void qemu_spice_destroy_primary_surface(SimpleSpiceDisplay *ssd,
108 uint32_t id, qxl_async_io async)
109 {
110 trace_qemu_spice_destroy_primary_surface(ssd->qxl.id, id, async);
111 if (async != QXL_SYNC) {
112 spice_qxl_destroy_primary_surface_async(&ssd->qxl, id,
113 (uintptr_t)qxl_cookie_new(QXL_COOKIE_TYPE_IO,
114 QXL_IO_DESTROY_PRIMARY_ASYNC));
115 } else {
116 spice_qxl_destroy_primary_surface(&ssd->qxl, id);
117 }
118 }
119
120 void qemu_spice_wakeup(SimpleSpiceDisplay *ssd)
121 {
122 trace_qemu_spice_wakeup(ssd->qxl.id);
123 spice_qxl_wakeup(&ssd->qxl);
124 }
125
126 static void qemu_spice_create_one_update(SimpleSpiceDisplay *ssd,
127 QXLRect *rect)
128 {
129 SimpleSpiceUpdate *update;
130 QXLDrawable *drawable;
131 QXLImage *image;
132 QXLCommand *cmd;
133 int bw, bh;
134 struct timespec time_space;
135 pixman_image_t *dest;
136
137 trace_qemu_spice_create_update(
138 rect->left, rect->right,
139 rect->top, rect->bottom);
140
141 update = g_malloc0(sizeof(*update));
142 drawable = &update->drawable;
143 image = &update->image;
144 cmd = &update->ext.cmd;
145
146 bw = rect->right - rect->left;
147 bh = rect->bottom - rect->top;
148 update->bitmap = g_malloc(bw * bh * 4);
149
150 drawable->bbox = *rect;
151 drawable->clip.type = SPICE_CLIP_TYPE_NONE;
152 drawable->effect = QXL_EFFECT_OPAQUE;
153 drawable->release_info.id = (uintptr_t)(&update->ext);
154 drawable->type = QXL_DRAW_COPY;
155 drawable->surfaces_dest[0] = -1;
156 drawable->surfaces_dest[1] = -1;
157 drawable->surfaces_dest[2] = -1;
158 clock_gettime(CLOCK_MONOTONIC, &time_space);
159 /* time in milliseconds from epoch. */
160 drawable->mm_time = time_space.tv_sec * 1000
161 + time_space.tv_nsec / 1000 / 1000;
162
163 drawable->u.copy.rop_descriptor = SPICE_ROPD_OP_PUT;
164 drawable->u.copy.src_bitmap = (uintptr_t)image;
165 drawable->u.copy.src_area.right = bw;
166 drawable->u.copy.src_area.bottom = bh;
167
168 QXL_SET_IMAGE_ID(image, QXL_IMAGE_GROUP_DEVICE, ssd->unique++);
169 image->descriptor.type = SPICE_IMAGE_TYPE_BITMAP;
170 image->bitmap.flags = QXL_BITMAP_DIRECT | QXL_BITMAP_TOP_DOWN;
171 image->bitmap.stride = bw * 4;
172 image->descriptor.width = image->bitmap.x = bw;
173 image->descriptor.height = image->bitmap.y = bh;
174 image->bitmap.data = (uintptr_t)(update->bitmap);
175 image->bitmap.palette = 0;
176 image->bitmap.format = SPICE_BITMAP_FMT_32BIT;
177
178 dest = pixman_image_create_bits(PIXMAN_LE_x8r8g8b8, bw, bh,
179 (void *)update->bitmap, bw * 4);
180 pixman_image_composite(PIXMAN_OP_SRC, ssd->surface, NULL, ssd->mirror,
181 rect->left, rect->top, 0, 0,
182 rect->left, rect->top, bw, bh);
183 pixman_image_composite(PIXMAN_OP_SRC, ssd->mirror, NULL, dest,
184 rect->left, rect->top, 0, 0,
185 0, 0, bw, bh);
186 pixman_image_unref(dest);
187
188 cmd->type = QXL_CMD_DRAW;
189 cmd->data = (uintptr_t)drawable;
190
191 QTAILQ_INSERT_TAIL(&ssd->updates, update, next);
192 }
193
194 static void qemu_spice_create_update(SimpleSpiceDisplay *ssd)
195 {
196 static const int blksize = 32;
197 int blocks = DIV_ROUND_UP(surface_width(ssd->ds), blksize);
198 g_autofree int *dirty_top = NULL;
199 int y, yoff1, yoff2, x, xoff, blk, bw;
200 int bpp = surface_bytes_per_pixel(ssd->ds);
201 uint8_t *guest, *mirror;
202
203 if (qemu_spice_rect_is_empty(&ssd->dirty)) {
204 return;
205 };
206
207 dirty_top = g_new(int, blocks);
208 for (blk = 0; blk < blocks; blk++) {
209 dirty_top[blk] = -1;
210 }
211
212 guest = surface_data(ssd->ds);
213 mirror = (void *)pixman_image_get_data(ssd->mirror);
214 for (y = ssd->dirty.top; y < ssd->dirty.bottom; y++) {
215 yoff1 = y * surface_stride(ssd->ds);
216 yoff2 = y * pixman_image_get_stride(ssd->mirror);
217 for (x = ssd->dirty.left; x < ssd->dirty.right; x += blksize) {
218 xoff = x * bpp;
219 blk = x / blksize;
220 bw = MIN(blksize, ssd->dirty.right - x);
221 if (memcmp(guest + yoff1 + xoff,
222 mirror + yoff2 + xoff,
223 bw * bpp) == 0) {
224 if (dirty_top[blk] != -1) {
225 QXLRect update = {
226 .top = dirty_top[blk],
227 .bottom = y,
228 .left = x,
229 .right = x + bw,
230 };
231 qemu_spice_create_one_update(ssd, &update);
232 dirty_top[blk] = -1;
233 }
234 } else {
235 if (dirty_top[blk] == -1) {
236 dirty_top[blk] = y;
237 }
238 }
239 }
240 }
241
242 for (x = ssd->dirty.left; x < ssd->dirty.right; x += blksize) {
243 blk = x / blksize;
244 bw = MIN(blksize, ssd->dirty.right - x);
245 if (dirty_top[blk] != -1) {
246 QXLRect update = {
247 .top = dirty_top[blk],
248 .bottom = ssd->dirty.bottom,
249 .left = x,
250 .right = x + bw,
251 };
252 qemu_spice_create_one_update(ssd, &update);
253 dirty_top[blk] = -1;
254 }
255 }
256
257 memset(&ssd->dirty, 0, sizeof(ssd->dirty));
258 }
259
260 static SimpleSpiceCursor*
261 qemu_spice_create_cursor_update(SimpleSpiceDisplay *ssd,
262 QEMUCursor *c,
263 bool on)
264 {
265 size_t size = c ? c->width * c->height * 4 : 0;
266 SimpleSpiceCursor *update;
267 QXLCursorCmd *ccmd;
268 QXLCursor *cursor;
269 QXLCommand *cmd;
270
271 update = g_malloc0(sizeof(*update) + size);
272 ccmd = &update->cmd;
273 cursor = &update->cursor;
274 cmd = &update->ext.cmd;
275
276 if (c) {
277 ccmd->type = QXL_CURSOR_SET;
278 ccmd->u.set.position.x = ssd->ptr_x + ssd->hot_x;
279 ccmd->u.set.position.y = ssd->ptr_y + ssd->hot_y;
280 ccmd->u.set.visible = true;
281 ccmd->u.set.shape = (uintptr_t)cursor;
282 cursor->header.unique = ssd->unique++;
283 cursor->header.type = SPICE_CURSOR_TYPE_ALPHA;
284 cursor->header.width = c->width;
285 cursor->header.height = c->height;
286 cursor->header.hot_spot_x = c->hot_x;
287 cursor->header.hot_spot_y = c->hot_y;
288 cursor->data_size = size;
289 cursor->chunk.data_size = size;
290 memcpy(cursor->chunk.data, c->data, size);
291 } else if (!on) {
292 ccmd->type = QXL_CURSOR_HIDE;
293 } else {
294 ccmd->type = QXL_CURSOR_MOVE;
295 ccmd->u.position.x = ssd->ptr_x + ssd->hot_x;
296 ccmd->u.position.y = ssd->ptr_y + ssd->hot_y;
297 }
298 ccmd->release_info.id = (uintptr_t)(&update->ext);
299
300 cmd->type = QXL_CMD_CURSOR;
301 cmd->data = (uintptr_t)ccmd;
302
303 return update;
304 }
305
306 /*
307 * Called from spice server thread context (via interface_release_resource)
308 * We do *not* hold the global qemu mutex here, so extra care is needed
309 * when calling qemu functions. QEMU interfaces used:
310 * - g_free (underlying glibc free is re-entrant).
311 */
312 void qemu_spice_destroy_update(SimpleSpiceDisplay *sdpy, SimpleSpiceUpdate *update)
313 {
314 g_free(update->bitmap);
315 g_free(update);
316 }
317
318 void qemu_spice_create_host_memslot(SimpleSpiceDisplay *ssd)
319 {
320 QXLDevMemSlot memslot;
321
322 memset(&memslot, 0, sizeof(memslot));
323 memslot.slot_group_id = MEMSLOT_GROUP_HOST;
324 memslot.virt_end = ~0;
325 qemu_spice_add_memslot(ssd, &memslot, QXL_SYNC);
326 }
327
328 void qemu_spice_create_host_primary(SimpleSpiceDisplay *ssd)
329 {
330 QXLDevSurfaceCreate surface;
331 uint64_t surface_size;
332
333 memset(&surface, 0, sizeof(surface));
334
335 surface_size = (uint64_t) surface_width(ssd->ds) *
336 surface_height(ssd->ds) * 4;
337 assert(surface_size > 0);
338 assert(surface_size < INT_MAX);
339 if (ssd->bufsize < surface_size) {
340 ssd->bufsize = surface_size;
341 g_free(ssd->buf);
342 ssd->buf = g_malloc(ssd->bufsize);
343 }
344
345 surface.format = SPICE_SURFACE_FMT_32_xRGB;
346 surface.width = surface_width(ssd->ds);
347 surface.height = surface_height(ssd->ds);
348 surface.stride = -surface.width * 4;
349 surface.mouse_mode = true;
350 surface.flags = 0;
351 surface.type = 0;
352 surface.mem = (uintptr_t)ssd->buf;
353 surface.group_id = MEMSLOT_GROUP_HOST;
354
355 qemu_spice_create_primary_surface(ssd, 0, &surface, QXL_SYNC);
356 }
357
358 void qemu_spice_destroy_host_primary(SimpleSpiceDisplay *ssd)
359 {
360 qemu_spice_destroy_primary_surface(ssd, 0, QXL_SYNC);
361 }
362
363 void qemu_spice_display_init_common(SimpleSpiceDisplay *ssd)
364 {
365 qemu_mutex_init(&ssd->lock);
366 QTAILQ_INIT(&ssd->updates);
367 ssd->mouse_x = -1;
368 ssd->mouse_y = -1;
369 if (ssd->num_surfaces == 0) {
370 ssd->num_surfaces = 1024;
371 }
372 }
373
374 /* display listener callbacks */
375
376 void qemu_spice_display_update(SimpleSpiceDisplay *ssd,
377 int x, int y, int w, int h)
378 {
379 QXLRect update_area;
380
381 trace_qemu_spice_display_update(ssd->qxl.id, x, y, w, h);
382 update_area.left = x,
383 update_area.right = x + w;
384 update_area.top = y;
385 update_area.bottom = y + h;
386
387 if (qemu_spice_rect_is_empty(&ssd->dirty)) {
388 ssd->notify++;
389 }
390 qemu_spice_rect_union(&ssd->dirty, &update_area);
391 }
392
393 void qemu_spice_display_switch(SimpleSpiceDisplay *ssd,
394 DisplaySurface *surface)
395 {
396 SimpleSpiceUpdate *update;
397 bool need_destroy;
398
399 if (ssd->surface &&
400 surface_width(surface) == pixman_image_get_width(ssd->surface) &&
401 surface_height(surface) == pixman_image_get_height(ssd->surface) &&
402 surface_format(surface) == pixman_image_get_format(ssd->surface)) {
403 /* no-resize fast path: just swap backing store */
404 trace_qemu_spice_display_surface(ssd->qxl.id,
405 surface_width(surface),
406 surface_height(surface),
407 true);
408 qemu_mutex_lock(&ssd->lock);
409 ssd->ds = surface;
410 pixman_image_unref(ssd->surface);
411 ssd->surface = pixman_image_ref(ssd->ds->image);
412 qemu_mutex_unlock(&ssd->lock);
413 qemu_spice_display_update(ssd, 0, 0,
414 surface_width(surface),
415 surface_height(surface));
416 return;
417 }
418
419 /* full mode switch */
420 trace_qemu_spice_display_surface(ssd->qxl.id,
421 surface_width(surface),
422 surface_height(surface),
423 false);
424
425 memset(&ssd->dirty, 0, sizeof(ssd->dirty));
426 if (ssd->surface) {
427 pixman_image_unref(ssd->surface);
428 ssd->surface = NULL;
429 pixman_image_unref(ssd->mirror);
430 ssd->mirror = NULL;
431 }
432
433 qemu_mutex_lock(&ssd->lock);
434 need_destroy = (ssd->ds != NULL);
435 ssd->ds = surface;
436 while ((update = QTAILQ_FIRST(&ssd->updates)) != NULL) {
437 QTAILQ_REMOVE(&ssd->updates, update, next);
438 qemu_spice_destroy_update(ssd, update);
439 }
440 qemu_mutex_unlock(&ssd->lock);
441 if (need_destroy) {
442 qemu_spice_destroy_host_primary(ssd);
443 }
444 if (ssd->ds) {
445 ssd->surface = pixman_image_ref(ssd->ds->image);
446 ssd->mirror = qemu_pixman_mirror_create(surface_format(ssd->ds),
447 ssd->ds->image);
448 qemu_spice_create_host_primary(ssd);
449 }
450
451 memset(&ssd->dirty, 0, sizeof(ssd->dirty));
452 ssd->notify++;
453
454 qemu_mutex_lock(&ssd->lock);
455 if (ssd->cursor) {
456 g_free(ssd->ptr_define);
457 ssd->ptr_define =
458 qemu_spice_create_cursor_update(ssd, ssd->cursor, false);
459 }
460 qemu_mutex_unlock(&ssd->lock);
461 }
462
463 void qemu_spice_cursor_refresh_bh(void *opaque)
464 {
465 SimpleSpiceDisplay *ssd = opaque;
466
467 qemu_mutex_lock(&ssd->lock);
468 if (ssd->cursor) {
469 QEMUCursor *c = ssd->cursor;
470 assert(ssd->dcl.con);
471 cursor_ref(c);
472 qemu_mutex_unlock(&ssd->lock);
473 qemu_console_set_cursor(ssd->dcl.con, c);
474 qemu_mutex_lock(&ssd->lock);
475 cursor_unref(c);
476 }
477
478 if (ssd->mouse_x != -1 && ssd->mouse_y != -1) {
479 int x, y;
480 assert(ssd->dcl.con);
481 x = ssd->mouse_x;
482 y = ssd->mouse_y;
483 ssd->mouse_x = -1;
484 ssd->mouse_y = -1;
485 qemu_mutex_unlock(&ssd->lock);
486 qemu_console_set_mouse(ssd->dcl.con, x, y, true);
487 } else {
488 qemu_mutex_unlock(&ssd->lock);
489 }
490 }
491
492 void qemu_spice_display_refresh(SimpleSpiceDisplay *ssd)
493 {
494 qemu_console_hw_update(ssd->dcl.con);
495
496 WITH_QEMU_LOCK_GUARD(&ssd->lock) {
497 if (QTAILQ_EMPTY(&ssd->updates) && ssd->ds) {
498 qemu_spice_create_update(ssd);
499 ssd->notify++;
500 }
501 }
502
503 trace_qemu_spice_display_refresh(ssd->qxl.id, ssd->notify);
504 if (ssd->notify) {
505 ssd->notify = 0;
506 qemu_spice_wakeup(ssd);
507 }
508 }
509
510 /* spice display interface callbacks */
511
512 static void interface_attached_worker(QXLInstance *sin)
513 {
514 /* nothing to do */
515 }
516
517 static void interface_set_compression_level(QXLInstance *sin, int level)
518 {
519 /* nothing to do */
520 }
521
522 static void interface_get_init_info(QXLInstance *sin, QXLDevInitInfo *info)
523 {
524 SimpleSpiceDisplay *ssd = container_of(sin, SimpleSpiceDisplay, qxl);
525
526 info->memslot_gen_bits = MEMSLOT_GENERATION_BITS;
527 info->memslot_id_bits = MEMSLOT_SLOT_BITS;
528 info->num_memslots = NUM_MEMSLOTS;
529 info->num_memslots_groups = NUM_MEMSLOTS_GROUPS;
530 info->internal_groupslot_id = 0;
531 info->qxl_ram_size = 16 * 1024 * 1024;
532 info->n_surfaces = ssd->num_surfaces;
533 }
534
535 static int interface_get_command(QXLInstance *sin, QXLCommandExt *ext)
536 {
537 SimpleSpiceDisplay *ssd = container_of(sin, SimpleSpiceDisplay, qxl);
538 SimpleSpiceUpdate *update;
539 int ret = false;
540
541 qemu_mutex_lock(&ssd->lock);
542 update = QTAILQ_FIRST(&ssd->updates);
543 if (update != NULL) {
544 QTAILQ_REMOVE(&ssd->updates, update, next);
545 *ext = update->ext;
546 ret = true;
547 }
548 qemu_mutex_unlock(&ssd->lock);
549
550 return ret;
551 }
552
553 static int interface_req_cmd_notification(QXLInstance *sin)
554 {
555 return 1;
556 }
557
558 static void interface_release_resource(QXLInstance *sin,
559 QXLReleaseInfoExt rext)
560 {
561 SimpleSpiceDisplay *ssd = container_of(sin, SimpleSpiceDisplay, qxl);
562 SimpleSpiceUpdate *update;
563 SimpleSpiceCursor *cursor;
564 QXLCommandExt *ext;
565
566 if (!rext.info) {
567 return;
568 }
569
570 ext = (void *)(intptr_t)(rext.info->id);
571 switch (ext->cmd.type) {
572 case QXL_CMD_DRAW:
573 update = container_of(ext, SimpleSpiceUpdate, ext);
574 qemu_spice_destroy_update(ssd, update);
575 break;
576 case QXL_CMD_CURSOR:
577 cursor = container_of(ext, SimpleSpiceCursor, ext);
578 g_free(cursor);
579 break;
580 default:
581 g_assert_not_reached();
582 }
583 }
584
585 static int interface_get_cursor_command(QXLInstance *sin, QXLCommandExt *ext)
586 {
587 SimpleSpiceDisplay *ssd = container_of(sin, SimpleSpiceDisplay, qxl);
588 int ret;
589
590 QEMU_LOCK_GUARD(&ssd->lock);
591 if (ssd->ptr_define) {
592 *ext = ssd->ptr_define->ext;
593 ssd->ptr_define = NULL;
594 ret = true;
595 } else if (ssd->ptr_move) {
596 *ext = ssd->ptr_move->ext;
597 ssd->ptr_move = NULL;
598 ret = true;
599 } else {
600 ret = false;
601 }
602 return ret;
603 }
604
605 static int interface_req_cursor_notification(QXLInstance *sin)
606 {
607 return 1;
608 }
609
610 static void interface_notify_update(QXLInstance *sin, uint32_t update_id)
611 {
612 fprintf(stderr, "%s: abort()\n", __func__);
613 abort();
614 }
615
616 static int interface_flush_resources(QXLInstance *sin)
617 {
618 fprintf(stderr, "%s: abort()\n", __func__);
619 abort();
620 return 0;
621 }
622
623 static void interface_update_area_complete(QXLInstance *sin,
624 uint32_t surface_id,
625 QXLRect *dirty, uint32_t num_updated_rects)
626 {
627 /* should never be called, used in qxl native mode only */
628 fprintf(stderr, "%s: abort()\n", __func__);
629 abort();
630 }
631
632 /* called from spice server thread context only */
633 static void interface_async_complete(QXLInstance *sin, uint64_t cookie_token)
634 {
635 QXLCookie *cookie = (QXLCookie *)(uintptr_t)cookie_token;
636
637 switch (cookie->type) {
638 #ifdef HAVE_SPICE_GL
639 case QXL_COOKIE_TYPE_GL_DRAW_DONE:
640 {
641 SimpleSpiceDisplay *ssd = container_of(sin, SimpleSpiceDisplay, qxl);
642 qemu_bh_schedule(ssd->gl_unblock_bh);
643 break;
644 }
645 case QXL_COOKIE_TYPE_IO:
646 if (cookie->io == QXL_IO_MONITORS_CONFIG_ASYNC) {
647 g_free(cookie->u.data);
648 }
649 break;
650 #endif
651 default:
652 /* should never be called, used in qxl native mode only */
653 fprintf(stderr, "%s: abort()\n", __func__);
654 abort();
655 }
656 g_free(cookie);
657 }
658
659 static void interface_set_client_capabilities(QXLInstance *sin,
660 uint8_t client_present,
661 uint8_t caps[58])
662 {
663 /* nothing to do */
664 }
665
666 static int interface_client_monitors_config(QXLInstance *sin,
667 VDAgentMonitorsConfig *mc)
668 {
669 SimpleSpiceDisplay *ssd = container_of(sin, SimpleSpiceDisplay, qxl);
670 QemuUIInfo info;
671 int head;
672
673 if (!qemu_console_ui_info_supported(ssd->dcl.con)) {
674 return 0; /* == not supported by guest */
675 }
676
677 if (!mc) {
678 return 1;
679 }
680
681 info = *qemu_console_get_ui_info(ssd->dcl.con);
682
683 head = qemu_console_get_index(ssd->dcl.con);
684 if (mc->num_of_monitors > head) {
685 info.width = mc->monitors[head].width;
686 info.height = mc->monitors[head].height;
687 if (mc->flags & VD_AGENT_CONFIG_MONITORS_FLAG_PHYSICAL_SIZE) {
688 VDAgentMonitorMM *mm = (void *)&mc->monitors[mc->num_of_monitors];
689 info.width_mm = mm[head].width;
690 info.height_mm = mm[head].height;
691 }
692 }
693
694 trace_qemu_spice_ui_info(ssd->qxl.id, info.width, info.height);
695 qemu_console_set_ui_info(ssd->dcl.con, &info, false);
696 return 1;
697 }
698
699 static const QXLInterface dpy_interface = {
700 .base.type = SPICE_INTERFACE_QXL,
701 .base.description = "qemu simple display",
702 .base.major_version = SPICE_INTERFACE_QXL_MAJOR,
703 .base.minor_version = SPICE_INTERFACE_QXL_MINOR,
704
705 .attached_worker = interface_attached_worker,
706 .set_compression_level = interface_set_compression_level,
707 .get_init_info = interface_get_init_info,
708
709 /* the callbacks below are called from spice server thread context */
710 .get_command = interface_get_command,
711 .req_cmd_notification = interface_req_cmd_notification,
712 .release_resource = interface_release_resource,
713 .get_cursor_command = interface_get_cursor_command,
714 .req_cursor_notification = interface_req_cursor_notification,
715 .notify_update = interface_notify_update,
716 .flush_resources = interface_flush_resources,
717 .async_complete = interface_async_complete,
718 .update_area_complete = interface_update_area_complete,
719 .set_client_capabilities = interface_set_client_capabilities,
720 .client_monitors_config = interface_client_monitors_config,
721 };
722
723 static void display_update(DisplayChangeListener *dcl,
724 int x, int y, int w, int h)
725 {
726 SimpleSpiceDisplay *ssd = container_of(dcl, SimpleSpiceDisplay, dcl);
727 qemu_spice_display_update(ssd, x, y, w, h);
728 }
729
730 static void display_switch(DisplayChangeListener *dcl,
731 DisplaySurface *surface)
732 {
733 SimpleSpiceDisplay *ssd = container_of(dcl, SimpleSpiceDisplay, dcl);
734 qemu_spice_display_switch(ssd, surface);
735 }
736
737 static void display_refresh(DisplayChangeListener *dcl)
738 {
739 SimpleSpiceDisplay *ssd = container_of(dcl, SimpleSpiceDisplay, dcl);
740 qemu_spice_display_refresh(ssd);
741 }
742
743 static void display_mouse_set(DisplayChangeListener *dcl,
744 int x, int y, bool on)
745 {
746 SimpleSpiceDisplay *ssd = container_of(dcl, SimpleSpiceDisplay, dcl);
747
748 qemu_mutex_lock(&ssd->lock);
749 ssd->ptr_x = x;
750 ssd->ptr_y = y;
751 g_free(ssd->ptr_move);
752 ssd->ptr_move = qemu_spice_create_cursor_update(ssd, NULL, on);
753 qemu_mutex_unlock(&ssd->lock);
754 qemu_spice_wakeup(ssd);
755 }
756
757 static void display_mouse_define(DisplayChangeListener *dcl,
758 QEMUCursor *c)
759 {
760 SimpleSpiceDisplay *ssd = container_of(dcl, SimpleSpiceDisplay, dcl);
761
762 qemu_mutex_lock(&ssd->lock);
763 cursor_ref(c);
764 cursor_unref(ssd->cursor);
765 ssd->cursor = c;
766 ssd->hot_x = c->hot_x;
767 ssd->hot_y = c->hot_y;
768 g_free(ssd->ptr_move);
769 ssd->ptr_move = NULL;
770 g_free(ssd->ptr_define);
771 ssd->ptr_define = qemu_spice_create_cursor_update(ssd, c, false);
772 qemu_mutex_unlock(&ssd->lock);
773 qemu_spice_wakeup(ssd);
774 }
775
776 static const DisplayChangeListenerOps display_listener_ops = {
777 .dpy_name = "spice",
778 .dpy_gfx_update = display_update,
779 .dpy_gfx_switch = display_switch,
780 .dpy_gfx_check_format = qemu_pixman_check_format,
781 .dpy_refresh = display_refresh,
782 .dpy_mouse_set = display_mouse_set,
783 .dpy_cursor_define = display_mouse_define,
784 };
785
786 #ifdef HAVE_SPICE_GL
787
788 static void qemu_spice_gl_monitor_config(SimpleSpiceDisplay *ssd,
789 int x, int y, int w, int h)
790 {
791 QXLMonitorsConfig *config;
792 QXLCookie *cookie;
793
794 config = g_malloc0(sizeof(QXLMonitorsConfig) + sizeof(QXLHead));
795 config->count = 1;
796 config->max_allowed = 1;
797 config->heads[0].x = x;
798 config->heads[0].y = y;
799 config->heads[0].width = w;
800 config->heads[0].height = h;
801 cookie = qxl_cookie_new(QXL_COOKIE_TYPE_IO,
802 QXL_IO_MONITORS_CONFIG_ASYNC);
803 cookie->u.data = config;
804
805 spice_qxl_monitors_config_async(&ssd->qxl,
806 (uintptr_t)config,
807 MEMSLOT_GROUP_HOST,
808 (uintptr_t)cookie);
809 }
810
811 static void qemu_spice_gl_block(SimpleSpiceDisplay *ssd, bool block)
812 {
813 uint64_t timeout;
814
815 if (block) {
816 timeout = qemu_clock_get_ms(QEMU_CLOCK_REALTIME);
817 timeout += 1000; /* one sec */
818 timer_mod(ssd->gl_unblock_timer, timeout);
819 } else {
820 timer_del(ssd->gl_unblock_timer);
821 }
822 qemu_console_hw_gl_block(ssd->dcl.con, block);
823 }
824
825 static void qemu_spice_gl_unblock_bh(void *opaque)
826 {
827 SimpleSpiceDisplay *ssd = opaque;
828
829 qemu_spice_gl_block(ssd, false);
830 }
831
832 static void qemu_spice_gl_block_timer(void *opaque)
833 {
834 warn_report("spice: no gl-draw-done within one second");
835 }
836
837 static void spice_gl_draw(SimpleSpiceDisplay *ssd,
838 uint32_t x, uint32_t y, uint32_t w, uint32_t h)
839 {
840 uint64_t cookie;
841
842 cookie = (uintptr_t)qxl_cookie_new(QXL_COOKIE_TYPE_GL_DRAW_DONE, 0);
843 spice_qxl_gl_draw_async(&ssd->qxl, x, y, w, h, cookie);
844 }
845
846 static void spice_gl_refresh(DisplayChangeListener *dcl)
847 {
848 SimpleSpiceDisplay *ssd = container_of(dcl, SimpleSpiceDisplay, dcl);
849
850 if (!ssd->ds) {
851 return;
852 }
853
854 if (qemu_console_is_gl_blocked(ssd->dcl.con)) {
855 if (spice_remote_client && ssd->gl_updates && ssd->have_scanout) {
856 glFlush();
857 spice_gl_draw(ssd, 0, 0,
858 surface_width(ssd->ds), surface_height(ssd->ds));
859 ssd->gl_updates = 0;
860 /* E.g, to achieve 60 FPS, update_interval needs to be ~16.66 ms */
861 dcl->update_interval = 1000 / spice_max_refresh_rate;
862 }
863 return;
864 }
865
866 qemu_console_hw_update(dcl->con);
867 if (ssd->gl_updates && ssd->have_surface) {
868 qemu_spice_gl_block(ssd, true);
869 glFlush();
870 spice_gl_draw(ssd, 0, 0,
871 surface_width(ssd->ds), surface_height(ssd->ds));
872 ssd->gl_updates = 0;
873 }
874 }
875
876 static void spice_gl_update(DisplayChangeListener *dcl,
877 int x, int y, int w, int h)
878 {
879 SimpleSpiceDisplay *ssd = container_of(dcl, SimpleSpiceDisplay, dcl);
880
881 surface_gl_update_texture(ssd->gls, ssd->ds, x, y, w, h);
882 ssd->gl_updates++;
883 }
884
885 static bool spice_gl_replace_fd_texture(SimpleSpiceDisplay *ssd,
886 int *fds, uint64_t *modifier,
887 int *num_planes)
888 {
889 uint32_t offsets[DMABUF_MAX_PLANES], strides[DMABUF_MAX_PLANES];
890 GLuint texture;
891 GLuint mem_obj;
892 int fourcc;
893 bool ret;
894
895 if (!spice_remote_client) {
896 return true;
897 }
898
899 if (*modifier == DRM_FORMAT_MOD_LINEAR) {
900 return true;
901 }
902
903 if (*num_planes > 1) {
904 error_report("spice: cannot replace texture with multiple planes");
905 return false;
906 }
907
908 /*
909 * We really want to ensure that the memory layout of the texture
910 * is linear; otherwise, the encoder's output may show corruption.
911 */
912 if (!surface_gl_create_texture_from_fd(ssd->ds, fds[0], &texture,
913 &mem_obj)) {
914 error_report("spice: cannot create new texture from fd");
915 return false;
916 }
917
918 /*
919 * A successful return after glImportMemoryFdEXT() means that
920 * the ownership of fd has been passed to GL. In other words,
921 * the fd we got above should not be used anymore.
922 */
923 ret = egl_dmabuf_export_texture(texture,
924 fds,
925 (EGLint *)offsets,
926 (EGLint *)strides,
927 &fourcc,
928 num_planes,
929 modifier);
930 if (!ret) {
931 glDeleteTextures(1, &texture);
932 #ifdef GL_EXT_memory_object_fd
933 glDeleteMemoryObjectsEXT(1, &mem_obj);
934 #endif
935
936 /*
937 * Since we couldn't export our newly create texture (or create,
938 * an fd associated with it) we need to backtrack and try to
939 * recreate the fd associated with the original texture.
940 */
941 ret = egl_dmabuf_export_texture(ssd->ds->texture,
942 fds,
943 (EGLint *)offsets,
944 (EGLint *)strides,
945 &fourcc,
946 num_planes,
947 modifier);
948 if (!ret) {
949 surface_gl_destroy_texture(ssd->gls, ssd->ds);
950 warn_report("spice: no texture available to display");
951 }
952 } else {
953 surface_gl_destroy_texture(ssd->gls, ssd->ds);
954 ssd->ds->texture = texture;
955
956 #ifdef GL_EXT_memory_object_fd
957 if (ssd->gl_surface_mem_obj) {
958 glDeleteMemoryObjectsEXT(1, &ssd->gl_surface_mem_obj);
959 }
960
961 ssd->gl_surface_mem_obj = mem_obj;
962 #endif
963 }
964 return ret;
965 }
966
967 static void spice_server_gl_scanout(QXLInstance *qxl,
968 const int *fd,
969 uint32_t width, uint32_t height,
970 const uint32_t *offset,
971 const uint32_t *stride,
972 uint32_t num_planes, uint32_t format,
973 uint64_t modifier, int y_0_top)
974 {
975 #ifdef HAVE_SPICE_QXL_GL_SCANOUT2
976 spice_qxl_gl_scanout2(qxl, fd, width, height, offset, stride,
977 num_planes, format, modifier, y_0_top);
978 #else
979 if (fd == NULL) {
980 spice_qxl_gl_scanout(qxl, -1, 0, 0, 0, 0, false);
981 } else if (num_planes <= 1) {
982 spice_qxl_gl_scanout(qxl, fd[0], width, height, stride[0], format, y_0_top);
983 } else {
984 error_report("SPICE server does not support multi plane GL scanout");
985 }
986 #endif
987 }
988
989 static void spice_gl_switch(DisplayChangeListener *dcl,
990 struct DisplaySurface *new_surface)
991 {
992 SimpleSpiceDisplay *ssd = container_of(dcl, SimpleSpiceDisplay, dcl);
993 bool ret;
994
995 if (ssd->ds) {
996 surface_gl_destroy_texture(ssd->gls, ssd->ds);
997 }
998 ssd->ds = new_surface;
999 if (ssd->ds) {
1000 uint32_t offset[DMABUF_MAX_PLANES], stride[DMABUF_MAX_PLANES];
1001 int fd[DMABUF_MAX_PLANES], num_planes, fourcc;
1002 uint64_t modifier;
1003
1004 surface_gl_create_texture(ssd->gls, ssd->ds);
1005 if (!egl_dmabuf_export_texture(ssd->ds->texture,
1006 fd,
1007 (EGLint *)offset,
1008 (EGLint *)stride,
1009 &fourcc,
1010 &num_planes,
1011 &modifier)) {
1012 surface_gl_destroy_texture(ssd->gls, ssd->ds);
1013 return;
1014 }
1015
1016 ret = spice_gl_replace_fd_texture(ssd, fd, &modifier, &num_planes);
1017 if (!ret) {
1018 surface_gl_destroy_texture(ssd->gls, ssd->ds);
1019 return;
1020 }
1021
1022 trace_qemu_spice_gl_surface(ssd->qxl.id,
1023 surface_width(ssd->ds),
1024 surface_height(ssd->ds),
1025 fourcc);
1026
1027 /* note: spice server will close the fd */
1028 spice_server_gl_scanout(&ssd->qxl, fd,
1029 surface_width(ssd->ds),
1030 surface_height(ssd->ds),
1031 offset, stride, num_planes,
1032 fourcc, modifier, false);
1033 ssd->have_surface = true;
1034 ssd->have_scanout = false;
1035
1036 qemu_spice_gl_monitor_config(ssd, 0, 0,
1037 surface_width(ssd->ds),
1038 surface_height(ssd->ds));
1039 }
1040 }
1041
1042 static QEMUGLContext qemu_spice_gl_create_context(DisplayGLCtx *dgc,
1043 QEMUGLParams *params)
1044 {
1045 return qemu_egl_create_context(dgc, params, qemu_egl_rn_ctx);
1046 }
1047
1048 static void qemu_spice_gl_scanout_disable(DisplayChangeListener *dcl)
1049 {
1050 SimpleSpiceDisplay *ssd = container_of(dcl, SimpleSpiceDisplay, dcl);
1051
1052 trace_qemu_spice_gl_scanout_disable(ssd->qxl.id);
1053
1054 /*
1055 * We need to check for the case of "lost" updates, where a gl_draw
1056 * was not submitted because the timer did not get a chance to run.
1057 * One case where this happens is when the Guest VM is getting
1058 * rebooted. If the console is blocked in this situation, we need
1059 * to unblock it. Otherwise, newer updates would not take effect.
1060 */
1061 if (qemu_console_is_gl_blocked(ssd->dcl.con)) {
1062 if (spice_remote_client && ssd->gl_updates && ssd->have_scanout) {
1063 ssd->gl_updates = 0;
1064 qemu_spice_gl_block(ssd, false);
1065 }
1066 }
1067 spice_server_gl_scanout(&ssd->qxl, NULL, 0, 0, NULL, NULL, 0, DRM_FORMAT_INVALID,
1068 DRM_FORMAT_MOD_INVALID, false);
1069 qemu_spice_gl_monitor_config(ssd, 0, 0, 0, 0);
1070 ssd->have_surface = false;
1071 ssd->have_scanout = false;
1072 }
1073
1074 static void qemu_spice_gl_scanout_texture(DisplayChangeListener *dcl,
1075 uint32_t tex_id,
1076 bool y_0_top,
1077 uint32_t backing_width,
1078 uint32_t backing_height,
1079 uint32_t x, uint32_t y,
1080 uint32_t w, uint32_t h,
1081 void *d3d_tex2d)
1082 {
1083 SimpleSpiceDisplay *ssd = container_of(dcl, SimpleSpiceDisplay, dcl);
1084 EGLint offset[DMABUF_MAX_PLANES], stride[DMABUF_MAX_PLANES], fourcc = 0;
1085 int fd[DMABUF_MAX_PLANES], num_planes, i;
1086 uint64_t modifier;
1087
1088 assert(tex_id);
1089 if (!egl_dmabuf_export_texture(tex_id, fd, offset, stride, &fourcc,
1090 &num_planes, &modifier)) {
1091 fprintf(stderr, "%s: failed to export dmabuf for texture\n", __func__);
1092 return;
1093 }
1094
1095 trace_qemu_spice_gl_scanout_texture(ssd->qxl.id, w, h, fourcc);
1096
1097 if (spice_remote_client && modifier != DRM_FORMAT_MOD_LINEAR) {
1098 egl_fb_destroy(&ssd->guest_fb);
1099 egl_fb_setup_for_tex(&ssd->guest_fb,
1100 backing_width, backing_height,
1101 tex_id, false);
1102 ssd->backing_y_0_top = y_0_top;
1103 ssd->blit_scanout_texture = true;
1104 ssd->new_scanout_texture = true;
1105
1106 for (i = 0; i < num_planes; i++) {
1107 close(fd[i]);
1108 }
1109 } else {
1110 /* note: spice server will close the fd */
1111 spice_server_gl_scanout(&ssd->qxl, fd, backing_width, backing_height,
1112 (uint32_t *)offset, (uint32_t *)stride,
1113 num_planes, fourcc, modifier, y_0_top);
1114 qemu_spice_gl_monitor_config(ssd, x, y, w, h);
1115 }
1116
1117 ssd->have_surface = false;
1118 ssd->have_scanout = true;
1119 }
1120
1121 static void qemu_spice_gl_scanout_dmabuf(DisplayChangeListener *dcl,
1122 QemuDmaBuf *dmabuf)
1123 {
1124 SimpleSpiceDisplay *ssd = container_of(dcl, SimpleSpiceDisplay, dcl);
1125
1126 ssd->guest_dmabuf = dmabuf;
1127 ssd->guest_dmabuf_refresh = true;
1128
1129 ssd->have_surface = false;
1130 ssd->have_scanout = true;
1131 }
1132
1133 static void qemu_spice_gl_cursor_dmabuf(DisplayChangeListener *dcl,
1134 QemuDmaBuf *dmabuf, bool have_hot,
1135 uint32_t hot_x, uint32_t hot_y)
1136 {
1137 SimpleSpiceDisplay *ssd = container_of(dcl, SimpleSpiceDisplay, dcl);
1138 uint32_t width, height, texture;
1139
1140 ssd->have_hot = have_hot;
1141 ssd->hot_x = hot_x;
1142 ssd->hot_y = hot_y;
1143
1144 trace_qemu_spice_gl_cursor(ssd->qxl.id, dmabuf != NULL, have_hot);
1145 if (dmabuf) {
1146 egl_dmabuf_import_texture(dmabuf);
1147 texture = qemu_dmabuf_get_texture(dmabuf);
1148 if (!texture) {
1149 return;
1150 }
1151 width = qemu_dmabuf_get_width(dmabuf);
1152 height = qemu_dmabuf_get_height(dmabuf);
1153 egl_fb_setup_for_tex(&ssd->cursor_fb, width, height, texture, false);
1154 } else {
1155 egl_fb_destroy(&ssd->cursor_fb);
1156 }
1157 }
1158
1159 static void qemu_spice_gl_cursor_position(DisplayChangeListener *dcl,
1160 uint32_t pos_x, uint32_t pos_y)
1161 {
1162 SimpleSpiceDisplay *ssd = container_of(dcl, SimpleSpiceDisplay, dcl);
1163
1164 qemu_mutex_lock(&ssd->lock);
1165 ssd->ptr_x = pos_x;
1166 ssd->ptr_y = pos_y;
1167 qemu_mutex_unlock(&ssd->lock);
1168 }
1169
1170 static void qemu_spice_gl_release_dmabuf(DisplayChangeListener *dcl,
1171 QemuDmaBuf *dmabuf)
1172 {
1173 SimpleSpiceDisplay *ssd = container_of(dcl, SimpleSpiceDisplay, dcl);
1174
1175 if (ssd->guest_dmabuf == dmabuf) {
1176 ssd->guest_dmabuf = NULL;
1177 ssd->guest_dmabuf_refresh = false;
1178 }
1179 egl_dmabuf_release_texture(dmabuf);
1180 }
1181
1182 static bool spice_gl_blit_scanout_texture(SimpleSpiceDisplay *ssd)
1183 {
1184 uint32_t offsets[DMABUF_MAX_PLANES], strides[DMABUF_MAX_PLANES];
1185 int fds[DMABUF_MAX_PLANES], num_planes, fourcc;
1186 egl_fb scanout_tex_fb = {};
1187 uint64_t modifier;
1188 bool ret;
1189
1190 egl_fb_setup_for_tex(&scanout_tex_fb,
1191 surface_width(ssd->ds), surface_height(ssd->ds),
1192 ssd->ds->texture, false);
1193 egl_fb_blit(&scanout_tex_fb, &ssd->guest_fb, false);
1194 glFlush();
1195 egl_fb_destroy(&scanout_tex_fb);
1196
1197 if (!ssd->new_scanout_texture) {
1198 return true;
1199 }
1200
1201 ret = egl_dmabuf_export_texture(ssd->ds->texture,
1202 fds,
1203 (EGLint *)offsets,
1204 (EGLint *)strides,
1205 &fourcc,
1206 &num_planes,
1207 &modifier);
1208 if (!ret) {
1209 error_report("spice: failed to get fd for texture");
1210 return false;
1211 }
1212
1213 spice_server_gl_scanout(&ssd->qxl, fds,
1214 surface_width(ssd->ds),
1215 surface_height(ssd->ds),
1216 (uint32_t *)offsets, (uint32_t *)strides,
1217 num_planes, fourcc, modifier,
1218 ssd->backing_y_0_top);
1219 qemu_spice_gl_monitor_config(ssd, 0, 0,
1220 surface_width(ssd->ds),
1221 surface_height(ssd->ds));
1222 ssd->new_scanout_texture = false;
1223 return true;
1224 }
1225
1226 static void qemu_spice_gl_update(DisplayChangeListener *dcl,
1227 uint32_t x, uint32_t y, uint32_t w, uint32_t h)
1228 {
1229 SimpleSpiceDisplay *ssd = container_of(dcl, SimpleSpiceDisplay, dcl);
1230 EGLint fourcc = 0;
1231 bool render_cursor = false;
1232 bool y_0_top = false; /* FIXME */
1233 bool ret;
1234 uint32_t width, height, texture;
1235
1236 if (!ssd->have_scanout) {
1237 return;
1238 }
1239
1240 if (ssd->cursor_fb.texture) {
1241 render_cursor = true;
1242 }
1243 if (ssd->render_cursor != render_cursor) {
1244 ssd->render_cursor = render_cursor;
1245 ssd->guest_dmabuf_refresh = true;
1246 egl_fb_destroy(&ssd->blit_fb);
1247 }
1248
1249 if (ssd->guest_dmabuf_refresh) {
1250 QemuDmaBuf *dmabuf = ssd->guest_dmabuf;
1251 width = qemu_dmabuf_get_width(dmabuf);
1252 height = qemu_dmabuf_get_height(dmabuf);
1253
1254 if (render_cursor) {
1255 egl_dmabuf_import_texture(dmabuf);
1256 texture = qemu_dmabuf_get_texture(dmabuf);
1257 if (!texture) {
1258 return;
1259 }
1260
1261 /* source framebuffer */
1262 egl_fb_setup_for_tex(&ssd->guest_fb, width, height,
1263 texture, false);
1264
1265 /* dest framebuffer */
1266 if (ssd->blit_fb.width != width ||
1267 ssd->blit_fb.height != height) {
1268 int fds[DMABUF_MAX_PLANES], num_planes;
1269 uint32_t offsets[DMABUF_MAX_PLANES], strides[DMABUF_MAX_PLANES];
1270 uint64_t modifier;
1271
1272 trace_qemu_spice_gl_render_dmabuf(ssd->qxl.id, width,
1273 height);
1274 egl_fb_destroy(&ssd->blit_fb);
1275 egl_fb_setup_new_tex(&ssd->blit_fb,
1276 width, height);
1277 if (!egl_dmabuf_export_texture(ssd->blit_fb.texture, fds,
1278 (EGLint *)offsets, (EGLint *)strides,
1279 &fourcc, &num_planes, &modifier)) {
1280 fprintf(stderr,
1281 "%s: failed to export dmabuf for texture\n", __func__);
1282 return;
1283 }
1284
1285 spice_server_gl_scanout(&ssd->qxl, fds, width, height, offsets, strides,
1286 num_planes, fourcc, modifier, false);
1287 }
1288 } else {
1289 int fds[DMABUF_MAX_PLANES];
1290 int noffsets, nstrides;
1291 const uint32_t *offsets = qemu_dmabuf_get_offsets(dmabuf, &noffsets);
1292 const uint32_t *strides = qemu_dmabuf_get_strides(dmabuf, &nstrides);
1293 uint32_t num_planes = qemu_dmabuf_get_num_planes(dmabuf);
1294
1295 assert(noffsets >= num_planes);
1296 assert(nstrides >= num_planes);
1297
1298 fourcc = qemu_dmabuf_get_fourcc(dmabuf);
1299 y_0_top = qemu_dmabuf_get_y0_top(dmabuf);
1300 qemu_dmabuf_dup_fds(dmabuf, fds, DMABUF_MAX_PLANES);
1301
1302 trace_qemu_spice_gl_forward_dmabuf(ssd->qxl.id, width, height);
1303 /* note: spice server will close the fd, so hand over a dup */
1304 spice_server_gl_scanout(&ssd->qxl, fds, width, height,
1305 offsets, strides, num_planes,
1306 fourcc,
1307 qemu_dmabuf_get_modifier(dmabuf),
1308 y_0_top);
1309 }
1310 qemu_spice_gl_monitor_config(ssd, 0, 0, width, height);
1311 ssd->guest_dmabuf_refresh = false;
1312 }
1313
1314 if (render_cursor) {
1315 int ptr_x, ptr_y;
1316
1317 qemu_mutex_lock(&ssd->lock);
1318 ptr_x = ssd->ptr_x;
1319 ptr_y = ssd->ptr_y;
1320 qemu_mutex_unlock(&ssd->lock);
1321 egl_texture_blit(ssd->gls, &ssd->blit_fb, &ssd->guest_fb,
1322 !y_0_top);
1323 egl_texture_blend(ssd->gls, &ssd->blit_fb, &ssd->cursor_fb,
1324 !y_0_top, ptr_x, ptr_y, 1.0, 1.0);
1325 glFlush();
1326 }
1327
1328 if (spice_remote_client && ssd->blit_scanout_texture) {
1329 ret = spice_gl_blit_scanout_texture(ssd);
1330 if (!ret) {
1331 return;
1332 }
1333 }
1334
1335 trace_qemu_spice_gl_update(ssd->qxl.id, w, h, x, y);
1336 qemu_spice_gl_block(ssd, true);
1337 glFlush();
1338
1339 /*
1340 * In the case of remote clients, the submission of gl_draw request is
1341 * deferred here, so that it can be submitted later (to spice server)
1342 * from spice_gl_refresh() timer callback. This is done to ensure that
1343 * Guest updates are submitted at a steady rate (e.g. 60 FPS) instead
1344 * of submitting them arbitrarily.
1345 */
1346 if (spice_remote_client) {
1347 ssd->gl_updates++;
1348 } else {
1349 spice_gl_draw(ssd, x, y, w, h);
1350 }
1351 }
1352
1353 static const DisplayChangeListenerOps display_listener_gl_ops = {
1354 .dpy_name = "spice-egl",
1355 .dpy_gfx_update = spice_gl_update,
1356 .dpy_gfx_switch = spice_gl_switch,
1357 .dpy_gfx_check_format = console_gl_check_format,
1358 .dpy_refresh = spice_gl_refresh,
1359 .dpy_mouse_set = display_mouse_set,
1360 .dpy_cursor_define = display_mouse_define,
1361
1362 .dpy_gl_scanout_disable = qemu_spice_gl_scanout_disable,
1363 .dpy_gl_scanout_texture = qemu_spice_gl_scanout_texture,
1364 .dpy_gl_scanout_dmabuf = qemu_spice_gl_scanout_dmabuf,
1365 .dpy_gl_cursor_dmabuf = qemu_spice_gl_cursor_dmabuf,
1366 .dpy_gl_cursor_position = qemu_spice_gl_cursor_position,
1367 .dpy_gl_release_dmabuf = qemu_spice_gl_release_dmabuf,
1368 .dpy_gl_update = qemu_spice_gl_update,
1369 };
1370
1371 static bool
1372 qemu_spice_is_compatible_dcl(DisplayGLCtx *dgc,
1373 DisplayChangeListener *dcl)
1374 {
1375 return dcl->ops == &display_listener_gl_ops;
1376 }
1377
1378 static const DisplayGLCtxOps gl_ctx_ops = {
1379 .dpy_gl_ctx_is_compatible_dcl = qemu_spice_is_compatible_dcl,
1380 .dpy_gl_ctx_create = qemu_spice_gl_create_context,
1381 .dpy_gl_ctx_destroy = qemu_egl_destroy_context,
1382 .dpy_gl_ctx_make_current = qemu_egl_make_context_current,
1383 };
1384
1385 #endif /* HAVE_SPICE_GL */
1386
1387 static void qemu_spice_display_init_one(QemuConsole *con)
1388 {
1389 SimpleSpiceDisplay *ssd = g_new0(SimpleSpiceDisplay, 1);
1390 Error *err = NULL;
1391 char device_address[256] = "";
1392 const DisplayChangeListenerOps *ops = &display_listener_ops;
1393
1394 qemu_spice_display_init_common(ssd);
1395
1396 #ifdef HAVE_SPICE_GL
1397 if (spice_opengl) {
1398 ops = &display_listener_gl_ops;
1399 ssd->dgc.ops = &gl_ctx_ops;
1400 ssd->gl_unblock_bh = qemu_bh_new(qemu_spice_gl_unblock_bh, ssd);
1401 ssd->gl_unblock_timer = timer_new_ms(QEMU_CLOCK_REALTIME,
1402 qemu_spice_gl_block_timer, ssd);
1403 ssd->gls = qemu_gl_init_shader();
1404 ssd->have_surface = false;
1405 ssd->have_scanout = false;
1406 }
1407 #endif
1408 ssd->qxl.base.sif = &dpy_interface.base;
1409 qemu_spice_add_display_interface(&ssd->qxl, con);
1410
1411 if (qemu_console_fill_device_address(con, device_address, 256, &err)) {
1412 spice_qxl_set_device_info(&ssd->qxl,
1413 device_address,
1414 qemu_console_get_head(con),
1415 1);
1416 } else {
1417 error_report_err(err);
1418 }
1419
1420 qemu_spice_create_host_memslot(ssd);
1421
1422 if (spice_opengl) {
1423 qemu_console_set_display_gl_ctx(con, &ssd->dgc);
1424 }
1425 qemu_console_register_listener(con, &ssd->dcl, ops);
1426 g_ptr_array_add(spice_displays, ssd);
1427 }
1428
1429 void qemu_spice_display_cleanup(void)
1430 {
1431 if (!spice_displays) {
1432 return;
1433 }
1434
1435 for (guint i = 0; i < spice_displays->len; i++) {
1436 SimpleSpiceDisplay *ssd = g_ptr_array_index(spice_displays, i);
1437 SimpleSpiceUpdate *update;
1438
1439 qemu_console_unregister_listener(&ssd->dcl);
1440 #ifdef HAVE_SPICE_GL
1441 if (spice_opengl) {
1442 qemu_console_set_display_gl_ctx(ssd->dcl.con, NULL);
1443 }
1444 #endif
1445
1446 if (ssd->ds) {
1447 qemu_spice_destroy_host_primary(ssd);
1448 }
1449 qemu_spice_del_memslot(ssd, MEMSLOT_GROUP_HOST, 0);
1450 spice_server_remove_interface(&ssd->qxl.base);
1451
1452 while ((update = QTAILQ_FIRST(&ssd->updates)) != NULL) {
1453 QTAILQ_REMOVE(&ssd->updates, update, next);
1454 qemu_spice_destroy_update(ssd, update);
1455 }
1456 g_clear_pointer(&ssd->ptr_define, g_free);
1457 g_clear_pointer(&ssd->ptr_move, g_free);
1458 g_clear_pointer(&ssd->cursor, cursor_unref);
1459 g_clear_pointer(&ssd->surface, pixman_image_unref);
1460 g_clear_pointer(&ssd->mirror, pixman_image_unref);
1461 g_clear_pointer(&ssd->buf, g_free);
1462 #ifdef HAVE_SPICE_GL
1463 g_clear_pointer(&ssd->gl_unblock_bh, qemu_bh_delete);
1464 g_clear_pointer(&ssd->gl_unblock_timer, timer_free);
1465 g_clear_pointer(&ssd->gls, qemu_gl_fini_shader);
1466 egl_fb_destroy(&ssd->guest_fb);
1467 egl_fb_destroy(&ssd->blit_fb);
1468 egl_fb_destroy(&ssd->cursor_fb);
1469 #endif
1470 qemu_mutex_destroy(&ssd->lock);
1471 g_free(ssd);
1472 }
1473 g_clear_pointer(&spice_displays, g_ptr_array_unref);
1474 }
1475
1476 void qemu_spice_display_init(void)
1477 {
1478 QemuOptsList *olist = qemu_find_opts("spice");
1479 QemuOpts *opts = QTAILQ_FIRST(&olist->head);
1480 QemuConsole *spice_con, *con;
1481 const char *str;
1482 int i;
1483
1484 spice_displays = g_ptr_array_new();
1485
1486 str = qemu_opt_get(opts, "display");
1487 if (str) {
1488 int head = qemu_opt_get_number(opts, "head", 0);
1489 Error *err = NULL;
1490
1491 spice_con = qemu_console_lookup_by_device_name(str, head, &err);
1492 if (err) {
1493 error_report("Failed to lookup display/head");
1494 exit(1);
1495 }
1496 } else {
1497 spice_con = NULL;
1498 }
1499
1500 for (i = 0;; i++) {
1501 con = qemu_console_lookup_by_index(i);
1502 if (!con || !qemu_console_is_graphic(con)) {
1503 break;
1504 }
1505 if (qemu_spice_have_display_interface(con)) {
1506 continue;
1507 }
1508 if (spice_con != NULL && spice_con != con) {
1509 continue;
1510 }
1511 qemu_spice_display_init_one(con);
1512 }
1513
1514 qemu_spice_display_init_done();
1515 }