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