1 /* 2 * Copyright (C) 2010 Red Hat, Inc. 3 * 4 * This program is free software; you can redistribute it and/or 5 * modify it under the terms of the GNU General Public License as 6 * published by the Free Software Foundation; either version 2 or 7 * (at your option) version 3 of the License. 8 * 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License 15 * along with this program; if not, see <http://www.gnu.org/licenses/>. 16 */ 17 18 #include "qemu/osdep.h" 19 #include "ui/qemu-spice.h" 20 #include "qemu/error-report.h" 21 #include "qemu/timer.h" 22 #include "qemu/lockable.h" 23 #include "qemu/main-loop.h" 24 #include "qemu/option.h" 25 #include "qemu/queue.h" 26 #include "ui/console.h" 27 #include "trace.h" 28 29 #include "ui/spice-display.h" 30 31 #include "standard-headers/drm/drm_fourcc.h" 32 33 bool spice_opengl; 34 bool spice_remote_client; 35 int spice_max_refresh_rate; 36 37 int qemu_spice_rect_is_empty(const QXLRect* r) 38 { 39 return r->top == r->bottom || r->left == r->right; 40 } 41 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 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 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 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 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 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 118 void qemu_spice_wakeup(SimpleSpiceDisplay *ssd) 119 { 120 trace_qemu_spice_wakeup(ssd->qxl.id); 121 spice_qxl_wakeup(&ssd->qxl); 122 } 123 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 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* 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 */ 310 void qemu_spice_destroy_update(SimpleSpiceDisplay *sdpy, SimpleSpiceUpdate *update) 311 { 312 g_free(update->bitmap); 313 g_free(update); 314 } 315 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 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 356 void qemu_spice_destroy_host_primary(SimpleSpiceDisplay *ssd) 357 { 358 qemu_spice_destroy_primary_surface(ssd, 0, QXL_SYNC); 359 } 360 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 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 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 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 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 511 static void interface_attached_worker(QXLInstance *sin) 512 { 513 /* nothing to do */ 514 } 515 #else 516 static void interface_attach_worker(QXLInstance *sin, QXLWorker *qxl_worker) 517 { 518 /* nothing to do */ 519 } 520 #endif 521 522 static void interface_set_compression_level(QXLInstance *sin, int level) 523 { 524 /* nothing to do */ 525 } 526 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 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 558 static int interface_req_cmd_notification(QXLInstance *sin) 559 { 560 return 1; 561 } 562 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 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 610 static int interface_req_cursor_notification(QXLInstance *sin) 611 { 612 return 1; 613 } 614 615 static void interface_notify_update(QXLInstance *sin, uint32_t update_id) 616 { 617 fprintf(stderr, "%s: abort()\n", __func__); 618 abort(); 619 } 620 621 static int interface_flush_resources(QXLInstance *sin) 622 { 623 fprintf(stderr, "%s: abort()\n", __func__); 624 abort(); 625 return 0; 626 } 627 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 */ 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 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 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 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 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 748 static void display_refresh(DisplayChangeListener *dcl) 749 { 750 SimpleSpiceDisplay *ssd = container_of(dcl, SimpleSpiceDisplay, dcl); 751 qemu_spice_display_refresh(ssd); 752 } 753 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 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 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 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 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 843 static void qemu_spice_gl_block_timer(void *opaque) 844 { 845 warn_report("spice: no gl-draw-done within one second"); 846 } 847 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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