xref: /openbmc/qemu/ui/spice-display.c (revision 0c0729b46a3680c233e0d45647d5193c5c5083f9)
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