Merge remote-tracking branch 'spice/spice.v60' into staging
* spice/spice.v60: hw/qxl: support client monitor configuration via device qxl: add trace-event for QXL_IO_LOG hw/qxl: tracing fixes qxl: better cleanup for surface destroy qxl: Ignore set_client_capabilities pre/post migrate qxl: dont update invalid area spice: send updates only for changed screen content spice: add screen mirror spice: split qemu_spice_create_update spice: switch to queue for vga mode updates
This commit is contained in:
		
						commit
						cd6dcc7105
					
				| 
						 | 
					@ -2738,6 +2738,9 @@ EOF
 | 
				
			||||||
    if $pkg_config --atleast-version=0.12.0 spice-protocol >/dev/null 2>&1; then
 | 
					    if $pkg_config --atleast-version=0.12.0 spice-protocol >/dev/null 2>&1; then
 | 
				
			||||||
        spice_qxl_io_monitors_config_async="yes"
 | 
					        spice_qxl_io_monitors_config_async="yes"
 | 
				
			||||||
    fi
 | 
					    fi
 | 
				
			||||||
 | 
					    if $pkg_config --atleast-version=0.12.2 spice-protocol > /dev/null 2>&1; then
 | 
				
			||||||
 | 
					        spice_qxl_client_monitors_config="yes"
 | 
				
			||||||
 | 
					    fi
 | 
				
			||||||
  else
 | 
					  else
 | 
				
			||||||
    if test "$spice" = "yes" ; then
 | 
					    if test "$spice" = "yes" ; then
 | 
				
			||||||
      feature_not_found "spice"
 | 
					      feature_not_found "spice"
 | 
				
			||||||
| 
						 | 
					@ -3485,6 +3488,10 @@ if test "$spice_qxl_io_monitors_config_async" = "yes" ; then
 | 
				
			||||||
  echo "CONFIG_QXL_IO_MONITORS_CONFIG_ASYNC=y" >> $config_host_mak
 | 
					  echo "CONFIG_QXL_IO_MONITORS_CONFIG_ASYNC=y" >> $config_host_mak
 | 
				
			||||||
fi
 | 
					fi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if test "$spice_qxl_client_monitors_config" = "yes" ; then
 | 
				
			||||||
 | 
					  echo "CONFIG_QXL_CLIENT_MONITORS_CONFIG=y" >> $config_host_mak
 | 
				
			||||||
 | 
					fi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
if test "$smartcard" = "yes" ; then
 | 
					if test "$smartcard" = "yes" ; then
 | 
				
			||||||
  echo "CONFIG_SMARTCARD=y" >> $config_host_mak
 | 
					  echo "CONFIG_SMARTCARD=y" >> $config_host_mak
 | 
				
			||||||
fi
 | 
					fi
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										107
									
								
								hw/qxl.c
								
								
								
								
							
							
						
						
									
										107
									
								
								hw/qxl.c
								
								
								
								
							| 
						 | 
					@ -18,6 +18,8 @@
 | 
				
			||||||
 * along with this program; if not, see <http://www.gnu.org/licenses/>.
 | 
					 * along with this program; if not, see <http://www.gnu.org/licenses/>.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <zlib.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "qemu-common.h"
 | 
					#include "qemu-common.h"
 | 
				
			||||||
#include "qemu-timer.h"
 | 
					#include "qemu-timer.h"
 | 
				
			||||||
#include "qemu-queue.h"
 | 
					#include "qemu-queue.h"
 | 
				
			||||||
| 
						 | 
					@ -141,6 +143,7 @@ static void qxl_ring_set_dirty(PCIQXLDevice *qxl);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void qxl_set_guest_bug(PCIQXLDevice *qxl, const char *msg, ...)
 | 
					void qxl_set_guest_bug(PCIQXLDevice *qxl, const char *msg, ...)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
					    trace_qxl_set_guest_bug(qxl->id);
 | 
				
			||||||
    qxl_send_events(qxl, QXL_INTERRUPT_ERROR);
 | 
					    qxl_send_events(qxl, QXL_INTERRUPT_ERROR);
 | 
				
			||||||
    qxl->guest_bug = 1;
 | 
					    qxl->guest_bug = 1;
 | 
				
			||||||
    if (qxl->guestdebug) {
 | 
					    if (qxl->guestdebug) {
 | 
				
			||||||
| 
						 | 
					@ -201,6 +204,7 @@ static void qxl_spice_destroy_surface_wait(PCIQXLDevice *qxl, uint32_t id,
 | 
				
			||||||
        spice_qxl_destroy_surface_async(&qxl->ssd.qxl, id, (uintptr_t)cookie);
 | 
					        spice_qxl_destroy_surface_async(&qxl->ssd.qxl, id, (uintptr_t)cookie);
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
        qxl->ssd.worker->destroy_surface_wait(qxl->ssd.worker, id);
 | 
					        qxl->ssd.worker->destroy_surface_wait(qxl->ssd.worker, id);
 | 
				
			||||||
 | 
					        qxl_spice_destroy_surface_wait_complete(qxl, id);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -597,9 +601,9 @@ static int interface_get_command(QXLInstance *sin, struct QXLCommandExt *ext)
 | 
				
			||||||
    case QXL_MODE_VGA:
 | 
					    case QXL_MODE_VGA:
 | 
				
			||||||
        ret = false;
 | 
					        ret = false;
 | 
				
			||||||
        qemu_mutex_lock(&qxl->ssd.lock);
 | 
					        qemu_mutex_lock(&qxl->ssd.lock);
 | 
				
			||||||
        if (qxl->ssd.update != NULL) {
 | 
					        update = QTAILQ_FIRST(&qxl->ssd.updates);
 | 
				
			||||||
            update = qxl->ssd.update;
 | 
					        if (update != NULL) {
 | 
				
			||||||
            qxl->ssd.update = NULL;
 | 
					            QTAILQ_REMOVE(&qxl->ssd.updates, update, next);
 | 
				
			||||||
            *ext = update->ext;
 | 
					            *ext = update->ext;
 | 
				
			||||||
            ret = true;
 | 
					            ret = true;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
| 
						 | 
					@ -953,6 +957,11 @@ static void interface_set_client_capabilities(QXLInstance *sin,
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl);
 | 
					    PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (runstate_check(RUN_STATE_INMIGRATE) ||
 | 
				
			||||||
 | 
					        runstate_check(RUN_STATE_POSTMIGRATE)) {
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    qxl->shadow_rom.client_present = client_present;
 | 
					    qxl->shadow_rom.client_present = client_present;
 | 
				
			||||||
    memcpy(qxl->shadow_rom.client_capabilities, caps, sizeof(caps));
 | 
					    memcpy(qxl->shadow_rom.client_capabilities, caps, sizeof(caps));
 | 
				
			||||||
    qxl->rom->client_present = client_present;
 | 
					    qxl->rom->client_present = client_present;
 | 
				
			||||||
| 
						 | 
					@ -964,6 +973,79 @@ static void interface_set_client_capabilities(QXLInstance *sin,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#if defined(CONFIG_QXL_CLIENT_MONITORS_CONFIG) \
 | 
				
			||||||
 | 
					    && SPICE_SERVER_VERSION >= 0x000b05
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static uint32_t qxl_crc32(const uint8_t *p, unsigned len)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    /*
 | 
				
			||||||
 | 
					     * zlib xors the seed with 0xffffffff, and xors the result
 | 
				
			||||||
 | 
					     * again with 0xffffffff; Both are not done with linux's crc32,
 | 
				
			||||||
 | 
					     * which we want to be compatible with, so undo that.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    return crc32(0xffffffff, p, len) ^ 0xffffffff;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* called from main context only */
 | 
				
			||||||
 | 
					static int interface_client_monitors_config(QXLInstance *sin,
 | 
				
			||||||
 | 
					                                        VDAgentMonitorsConfig *monitors_config)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl);
 | 
				
			||||||
 | 
					    QXLRom *rom = memory_region_get_ram_ptr(&qxl->rom_bar);
 | 
				
			||||||
 | 
					    int i;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /*
 | 
				
			||||||
 | 
					     * Older windows drivers set int_mask to 0 when their ISR is called,
 | 
				
			||||||
 | 
					     * then later set it to ~0. So it doesn't relate to the actual interrupts
 | 
				
			||||||
 | 
					     * handled. However, they are old, so clearly they don't support this
 | 
				
			||||||
 | 
					     * interrupt
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    if (qxl->ram->int_mask == 0 || qxl->ram->int_mask == ~0 ||
 | 
				
			||||||
 | 
					        !(qxl->ram->int_mask & QXL_INTERRUPT_CLIENT_MONITORS_CONFIG)) {
 | 
				
			||||||
 | 
					        trace_qxl_client_monitors_config_unsupported_by_guest(qxl->id,
 | 
				
			||||||
 | 
					                                                            qxl->ram->int_mask,
 | 
				
			||||||
 | 
					                                                            monitors_config);
 | 
				
			||||||
 | 
					        return 0;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (!monitors_config) {
 | 
				
			||||||
 | 
					        return 1;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    memset(&rom->client_monitors_config, 0,
 | 
				
			||||||
 | 
					           sizeof(rom->client_monitors_config));
 | 
				
			||||||
 | 
					    rom->client_monitors_config.count = monitors_config->num_of_monitors;
 | 
				
			||||||
 | 
					    /* monitors_config->flags ignored */
 | 
				
			||||||
 | 
					    if (rom->client_monitors_config.count >=
 | 
				
			||||||
 | 
					            ARRAY_SIZE(rom->client_monitors_config.heads)) {
 | 
				
			||||||
 | 
					        trace_qxl_client_monitors_config_capped(qxl->id,
 | 
				
			||||||
 | 
					                                monitors_config->num_of_monitors,
 | 
				
			||||||
 | 
					                                ARRAY_SIZE(rom->client_monitors_config.heads));
 | 
				
			||||||
 | 
					        rom->client_monitors_config.count =
 | 
				
			||||||
 | 
					            ARRAY_SIZE(rom->client_monitors_config.heads);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    for (i = 0 ; i < rom->client_monitors_config.count ; ++i) {
 | 
				
			||||||
 | 
					        VDAgentMonConfig *monitor = &monitors_config->monitors[i];
 | 
				
			||||||
 | 
					        QXLURect *rect = &rom->client_monitors_config.heads[i];
 | 
				
			||||||
 | 
					        /* monitor->depth ignored */
 | 
				
			||||||
 | 
					        rect->left = monitor->x;
 | 
				
			||||||
 | 
					        rect->top = monitor->y;
 | 
				
			||||||
 | 
					        rect->right = monitor->x + monitor->width;
 | 
				
			||||||
 | 
					        rect->bottom = monitor->y + monitor->height;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    rom->client_monitors_config_crc = qxl_crc32(
 | 
				
			||||||
 | 
					            (const uint8_t *)&rom->client_monitors_config,
 | 
				
			||||||
 | 
					            sizeof(rom->client_monitors_config));
 | 
				
			||||||
 | 
					    trace_qxl_client_monitors_config_crc(qxl->id,
 | 
				
			||||||
 | 
					            sizeof(rom->client_monitors_config),
 | 
				
			||||||
 | 
					            rom->client_monitors_config_crc);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    trace_qxl_interrupt_client_monitors_config(qxl->id,
 | 
				
			||||||
 | 
					                        rom->client_monitors_config.count,
 | 
				
			||||||
 | 
					                        rom->client_monitors_config.heads);
 | 
				
			||||||
 | 
					    qxl_send_events(qxl, QXL_INTERRUPT_CLIENT_MONITORS_CONFIG);
 | 
				
			||||||
 | 
					    return 1;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static const QXLInterface qxl_interface = {
 | 
					static const QXLInterface qxl_interface = {
 | 
				
			||||||
    .base.type               = SPICE_INTERFACE_QXL,
 | 
					    .base.type               = SPICE_INTERFACE_QXL,
 | 
				
			||||||
    .base.description        = "qxl gpu",
 | 
					    .base.description        = "qxl gpu",
 | 
				
			||||||
| 
						 | 
					@ -988,6 +1070,10 @@ static const QXLInterface qxl_interface = {
 | 
				
			||||||
#if SPICE_SERVER_VERSION >= 0x000b04
 | 
					#if SPICE_SERVER_VERSION >= 0x000b04
 | 
				
			||||||
    .set_client_capabilities = interface_set_client_capabilities,
 | 
					    .set_client_capabilities = interface_set_client_capabilities,
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					#if SPICE_SERVER_VERSION >= 0x000b05 && \
 | 
				
			||||||
 | 
					    defined(CONFIG_QXL_CLIENT_MONITORS_CONFIG)
 | 
				
			||||||
 | 
					    .client_monitors_config = interface_client_monitors_config,
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void qxl_enter_vga_mode(PCIQXLDevice *d)
 | 
					static void qxl_enter_vga_mode(PCIQXLDevice *d)
 | 
				
			||||||
| 
						 | 
					@ -1402,7 +1488,7 @@ static void ioport_write(void *opaque, target_phys_addr_t addr,
 | 
				
			||||||
            break;
 | 
					            break;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        trace_qxl_io_unexpected_vga_mode(d->id,
 | 
					        trace_qxl_io_unexpected_vga_mode(d->id,
 | 
				
			||||||
            io_port, io_port_to_string(io_port));
 | 
					            addr, val, io_port_to_string(io_port));
 | 
				
			||||||
        /* be nice to buggy guest drivers */
 | 
					        /* be nice to buggy guest drivers */
 | 
				
			||||||
        if (io_port >= QXL_IO_UPDATE_AREA_ASYNC &&
 | 
					        if (io_port >= QXL_IO_UPDATE_AREA_ASYNC &&
 | 
				
			||||||
            io_port < QXL_IO_RANGE_SIZE) {
 | 
					            io_port < QXL_IO_RANGE_SIZE) {
 | 
				
			||||||
| 
						 | 
					@ -1470,6 +1556,13 @@ async_common:
 | 
				
			||||||
            return;
 | 
					            return;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (update.left < 0 || update.top < 0 || update.left >= update.right ||
 | 
				
			||||||
 | 
					            update.top >= update.bottom) {
 | 
				
			||||||
 | 
					            qxl_set_guest_bug(d, "QXL_IO_UPDATE_AREA: "
 | 
				
			||||||
 | 
					                              "invalid area(%d,%d,%d,%d)\n", update.left,
 | 
				
			||||||
 | 
					                              update.right, update.top, update.bottom);
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
        if (async == QXL_ASYNC) {
 | 
					        if (async == QXL_ASYNC) {
 | 
				
			||||||
            cookie = qxl_cookie_new(QXL_COOKIE_TYPE_IO,
 | 
					            cookie = qxl_cookie_new(QXL_COOKIE_TYPE_IO,
 | 
				
			||||||
                                    QXL_IO_UPDATE_AREA_ASYNC);
 | 
					                                    QXL_IO_UPDATE_AREA_ASYNC);
 | 
				
			||||||
| 
						 | 
					@ -1501,6 +1594,7 @@ async_common:
 | 
				
			||||||
        qxl_set_mode(d, val, 0);
 | 
					        qxl_set_mode(d, val, 0);
 | 
				
			||||||
        break;
 | 
					        break;
 | 
				
			||||||
    case QXL_IO_LOG:
 | 
					    case QXL_IO_LOG:
 | 
				
			||||||
 | 
					        trace_qxl_io_log(d->id, d->ram->log_buf);
 | 
				
			||||||
        if (d->guestdebug) {
 | 
					        if (d->guestdebug) {
 | 
				
			||||||
            fprintf(stderr, "qxl/guest-%d: %" PRId64 ": %s", d->id,
 | 
					            fprintf(stderr, "qxl/guest-%d: %" PRId64 ": %s", d->id,
 | 
				
			||||||
                    qemu_get_clock_ns(vm_clock), d->ram->log_buf);
 | 
					                    qemu_get_clock_ns(vm_clock), d->ram->log_buf);
 | 
				
			||||||
| 
						 | 
					@ -1594,9 +1688,9 @@ cancel_async:
 | 
				
			||||||
static uint64_t ioport_read(void *opaque, target_phys_addr_t addr,
 | 
					static uint64_t ioport_read(void *opaque, target_phys_addr_t addr,
 | 
				
			||||||
                            unsigned size)
 | 
					                            unsigned size)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    PCIQXLDevice *d = opaque;
 | 
					    PCIQXLDevice *qxl = opaque;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    trace_qxl_io_read_unexpected(d->id);
 | 
					    trace_qxl_io_read_unexpected(qxl->id);
 | 
				
			||||||
    return 0xff;
 | 
					    return 0xff;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1626,6 +1720,7 @@ static void qxl_send_events(PCIQXLDevice *d, uint32_t events)
 | 
				
			||||||
    uint32_t old_pending;
 | 
					    uint32_t old_pending;
 | 
				
			||||||
    uint32_t le_events = cpu_to_le32(events);
 | 
					    uint32_t le_events = cpu_to_le32(events);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    trace_qxl_send_events(d->id, events);
 | 
				
			||||||
    assert(qemu_spice_display_is_running(&d->ssd));
 | 
					    assert(qemu_spice_display_is_running(&d->ssd));
 | 
				
			||||||
    old_pending = __sync_fetch_and_or(&d->ram->int_pending, le_events);
 | 
					    old_pending = __sync_fetch_and_or(&d->ram->int_pending, le_events);
 | 
				
			||||||
    if ((old_pending & le_events) == le_events) {
 | 
					    if ((old_pending & le_events) == le_events) {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										11
									
								
								trace-events
								
								
								
								
							
							
						
						
									
										11
									
								
								trace-events
								
								
								
								
							| 
						 | 
					@ -932,8 +932,9 @@ qxl_interface_update_area_complete_rest(int qid, uint32_t num_updated_rects) "%d
 | 
				
			||||||
qxl_interface_update_area_complete_overflow(int qid, int max) "%d max=%d"
 | 
					qxl_interface_update_area_complete_overflow(int qid, int max) "%d max=%d"
 | 
				
			||||||
qxl_interface_update_area_complete_schedule_bh(int qid, uint32_t num_dirty) "%d #dirty=%d"
 | 
					qxl_interface_update_area_complete_schedule_bh(int qid, uint32_t num_dirty) "%d #dirty=%d"
 | 
				
			||||||
qxl_io_destroy_primary_ignored(int qid, const char *mode) "%d %s"
 | 
					qxl_io_destroy_primary_ignored(int qid, const char *mode) "%d %s"
 | 
				
			||||||
 | 
					qxl_io_log(int qid, const uint8_t *log_buf) "%d %s"
 | 
				
			||||||
qxl_io_read_unexpected(int qid) "%d"
 | 
					qxl_io_read_unexpected(int qid) "%d"
 | 
				
			||||||
qxl_io_unexpected_vga_mode(int qid, uint32_t io_port, const char *desc) "%d 0x%x (%s)"
 | 
					qxl_io_unexpected_vga_mode(int qid, uint64_t addr, uint64_t val, const char *desc) "%d 0x%"PRIx64"=%"PRIu64" (%s)"
 | 
				
			||||||
qxl_io_write(int qid, const char *mode, uint64_t addr, uint64_t val, unsigned size, int async) "%d %s addr=%"PRIu64 " val=%"PRIu64" size=%u async=%d"
 | 
					qxl_io_write(int qid, const char *mode, uint64_t addr, uint64_t val, unsigned size, int async) "%d %s addr=%"PRIu64 " val=%"PRIu64" size=%u async=%d"
 | 
				
			||||||
qxl_memslot_add_guest(int qid, uint32_t slot_id, uint64_t guest_start, uint64_t guest_end) "%d %u: guest phys 0x%"PRIx64 " - 0x%" PRIx64
 | 
					qxl_memslot_add_guest(int qid, uint32_t slot_id, uint64_t guest_start, uint64_t guest_end) "%d %u: guest phys 0x%"PRIx64 " - 0x%" PRIx64
 | 
				
			||||||
qxl_post_load(int qid, const char *mode) "%d %s"
 | 
					qxl_post_load(int qid, const char *mode) "%d %s"
 | 
				
			||||||
| 
						 | 
					@ -964,7 +965,7 @@ qxl_spice_destroy_surfaces(int qid, int async) "%d async=%d"
 | 
				
			||||||
qxl_spice_destroy_surface_wait_complete(int qid, uint32_t id) "%d sid=%d"
 | 
					qxl_spice_destroy_surface_wait_complete(int qid, uint32_t id) "%d sid=%d"
 | 
				
			||||||
qxl_spice_destroy_surface_wait(int qid, uint32_t id, int async) "%d sid=%d async=%d"
 | 
					qxl_spice_destroy_surface_wait(int qid, uint32_t id, int async) "%d sid=%d async=%d"
 | 
				
			||||||
qxl_spice_flush_surfaces_async(int qid, uint32_t surface_count, uint32_t num_free_res) "%d s#=%d, res#=%d"
 | 
					qxl_spice_flush_surfaces_async(int qid, uint32_t surface_count, uint32_t num_free_res) "%d s#=%d, res#=%d"
 | 
				
			||||||
qxl_spice_monitors_config(int id) "%d"
 | 
					qxl_spice_monitors_config(int qid) "%d"
 | 
				
			||||||
qxl_spice_loadvm_commands(int qid, void *ext, uint32_t count) "%d ext=%p count=%d"
 | 
					qxl_spice_loadvm_commands(int qid, void *ext, uint32_t count) "%d ext=%p count=%d"
 | 
				
			||||||
qxl_spice_oom(int qid) "%d"
 | 
					qxl_spice_oom(int qid) "%d"
 | 
				
			||||||
qxl_spice_reset_cursor(int qid) "%d"
 | 
					qxl_spice_reset_cursor(int qid) "%d"
 | 
				
			||||||
| 
						 | 
					@ -973,6 +974,12 @@ qxl_spice_reset_memslots(int qid) "%d"
 | 
				
			||||||
qxl_spice_update_area(int qid, uint32_t surface_id, uint32_t left, uint32_t right, uint32_t top, uint32_t bottom) "%d sid=%d [%d,%d,%d,%d]"
 | 
					qxl_spice_update_area(int qid, uint32_t surface_id, uint32_t left, uint32_t right, uint32_t top, uint32_t bottom) "%d sid=%d [%d,%d,%d,%d]"
 | 
				
			||||||
qxl_spice_update_area_rest(int qid, uint32_t num_dirty_rects, uint32_t clear_dirty_region) "%d #d=%d clear=%d"
 | 
					qxl_spice_update_area_rest(int qid, uint32_t num_dirty_rects, uint32_t clear_dirty_region) "%d #d=%d clear=%d"
 | 
				
			||||||
qxl_surfaces_dirty(int qid, int surface, int offset, int size) "%d surface=%d offset=%d size=%d"
 | 
					qxl_surfaces_dirty(int qid, int surface, int offset, int size) "%d surface=%d offset=%d size=%d"
 | 
				
			||||||
 | 
					qxl_send_events(int qid, uint32_t events) "%d %d"
 | 
				
			||||||
 | 
					qxl_set_guest_bug(int qid) "%d"
 | 
				
			||||||
 | 
					qxl_interrupt_client_monitors_config(int qid, int num_heads, void *heads) "%d %d %p"
 | 
				
			||||||
 | 
					qxl_client_monitors_config_unsupported_by_guest(int qid, uint32_t int_mask, void *client_monitors_config) "%d %X %p"
 | 
				
			||||||
 | 
					qxl_client_monitors_config_capped(int qid, int requested, int limit) "%d %d %d"
 | 
				
			||||||
 | 
					qxl_client_monitors_config_crc(int qid, unsigned size, uint32_t crc32) "%d %u %u"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# hw/qxl-render.c
 | 
					# hw/qxl-render.c
 | 
				
			||||||
qxl_render_blit_guest_primary_initialized(void) ""
 | 
					qxl_render_blit_guest_primary_initialized(void) ""
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -164,34 +164,31 @@ int qemu_spice_display_is_running(SimpleSpiceDisplay *ssd)
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static SimpleSpiceUpdate *qemu_spice_create_update(SimpleSpiceDisplay *ssd)
 | 
					static void qemu_spice_create_one_update(SimpleSpiceDisplay *ssd,
 | 
				
			||||||
 | 
					                                         QXLRect *rect)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    SimpleSpiceUpdate *update;
 | 
					    SimpleSpiceUpdate *update;
 | 
				
			||||||
    QXLDrawable *drawable;
 | 
					    QXLDrawable *drawable;
 | 
				
			||||||
    QXLImage *image;
 | 
					    QXLImage *image;
 | 
				
			||||||
    QXLCommand *cmd;
 | 
					    QXLCommand *cmd;
 | 
				
			||||||
    uint8_t *src, *dst;
 | 
					    uint8_t *src, *mirror, *dst;
 | 
				
			||||||
    int by, bw, bh;
 | 
					    int by, bw, bh, offset, bytes;
 | 
				
			||||||
    struct timespec time_space;
 | 
					    struct timespec time_space;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (qemu_spice_rect_is_empty(&ssd->dirty)) {
 | 
					 | 
				
			||||||
        return NULL;
 | 
					 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    trace_qemu_spice_create_update(
 | 
					    trace_qemu_spice_create_update(
 | 
				
			||||||
           ssd->dirty.left, ssd->dirty.right,
 | 
					           rect->left, rect->right,
 | 
				
			||||||
           ssd->dirty.top, ssd->dirty.bottom);
 | 
					           rect->top, rect->bottom);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    update   = g_malloc0(sizeof(*update));
 | 
					    update   = g_malloc0(sizeof(*update));
 | 
				
			||||||
    drawable = &update->drawable;
 | 
					    drawable = &update->drawable;
 | 
				
			||||||
    image    = &update->image;
 | 
					    image    = &update->image;
 | 
				
			||||||
    cmd      = &update->ext.cmd;
 | 
					    cmd      = &update->ext.cmd;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    bw       = ssd->dirty.right - ssd->dirty.left;
 | 
					    bw       = rect->right - rect->left;
 | 
				
			||||||
    bh       = ssd->dirty.bottom - ssd->dirty.top;
 | 
					    bh       = rect->bottom - rect->top;
 | 
				
			||||||
    update->bitmap = g_malloc(bw * bh * 4);
 | 
					    update->bitmap = g_malloc(bw * bh * 4);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    drawable->bbox            = ssd->dirty;
 | 
					    drawable->bbox            = *rect;
 | 
				
			||||||
    drawable->clip.type       = SPICE_CLIP_TYPE_NONE;
 | 
					    drawable->clip.type       = SPICE_CLIP_TYPE_NONE;
 | 
				
			||||||
    drawable->effect          = QXL_EFFECT_OPAQUE;
 | 
					    drawable->effect          = QXL_EFFECT_OPAQUE;
 | 
				
			||||||
    drawable->release_info.id = (uintptr_t)update;
 | 
					    drawable->release_info.id = (uintptr_t)update;
 | 
				
			||||||
| 
						 | 
					@ -219,27 +216,99 @@ static SimpleSpiceUpdate *qemu_spice_create_update(SimpleSpiceDisplay *ssd)
 | 
				
			||||||
    image->bitmap.palette = 0;
 | 
					    image->bitmap.palette = 0;
 | 
				
			||||||
    image->bitmap.format = SPICE_BITMAP_FMT_32BIT;
 | 
					    image->bitmap.format = SPICE_BITMAP_FMT_32BIT;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (ssd->conv == NULL) {
 | 
					    offset =
 | 
				
			||||||
        PixelFormat dst = qemu_default_pixelformat(32);
 | 
					        rect->top * ds_get_linesize(ssd->ds) +
 | 
				
			||||||
        ssd->conv = qemu_pf_conv_get(&dst, &ssd->ds->surface->pf);
 | 
					        rect->left * ds_get_bytes_per_pixel(ssd->ds);
 | 
				
			||||||
        assert(ssd->conv);
 | 
					    bytes = ds_get_bytes_per_pixel(ssd->ds) * bw;
 | 
				
			||||||
    }
 | 
					    src = ds_get_data(ssd->ds) + offset;
 | 
				
			||||||
 | 
					    mirror = ssd->ds_mirror + offset;
 | 
				
			||||||
    src = ds_get_data(ssd->ds) +
 | 
					 | 
				
			||||||
        ssd->dirty.top * ds_get_linesize(ssd->ds) +
 | 
					 | 
				
			||||||
        ssd->dirty.left * ds_get_bytes_per_pixel(ssd->ds);
 | 
					 | 
				
			||||||
    dst = update->bitmap;
 | 
					    dst = update->bitmap;
 | 
				
			||||||
    for (by = 0; by < bh; by++) {
 | 
					    for (by = 0; by < bh; by++) {
 | 
				
			||||||
        qemu_pf_conv_run(ssd->conv, dst, src, bw);
 | 
					        memcpy(mirror, src, bytes);
 | 
				
			||||||
 | 
					        qemu_pf_conv_run(ssd->conv, dst, mirror, bw);
 | 
				
			||||||
        src += ds_get_linesize(ssd->ds);
 | 
					        src += ds_get_linesize(ssd->ds);
 | 
				
			||||||
 | 
					        mirror += ds_get_linesize(ssd->ds);
 | 
				
			||||||
        dst += image->bitmap.stride;
 | 
					        dst += image->bitmap.stride;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    cmd->type = QXL_CMD_DRAW;
 | 
					    cmd->type = QXL_CMD_DRAW;
 | 
				
			||||||
    cmd->data = (uintptr_t)drawable;
 | 
					    cmd->data = (uintptr_t)drawable;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    QTAILQ_INSERT_TAIL(&ssd->updates, update, next);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void qemu_spice_create_update(SimpleSpiceDisplay *ssd)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    static const int blksize = 32;
 | 
				
			||||||
 | 
					    int blocks = (ds_get_width(ssd->ds) + blksize - 1) / blksize;
 | 
				
			||||||
 | 
					    int dirty_top[blocks];
 | 
				
			||||||
 | 
					    int y, yoff, x, xoff, blk, bw;
 | 
				
			||||||
 | 
					    int bpp = ds_get_bytes_per_pixel(ssd->ds);
 | 
				
			||||||
 | 
					    uint8_t *guest, *mirror;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (qemu_spice_rect_is_empty(&ssd->dirty)) {
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (ssd->conv == NULL) {
 | 
				
			||||||
 | 
					        PixelFormat dst = qemu_default_pixelformat(32);
 | 
				
			||||||
 | 
					        ssd->conv = qemu_pf_conv_get(&dst, &ssd->ds->surface->pf);
 | 
				
			||||||
 | 
					        assert(ssd->conv);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (ssd->ds_mirror == NULL) {
 | 
				
			||||||
 | 
					        int size = ds_get_height(ssd->ds) * ds_get_linesize(ssd->ds);
 | 
				
			||||||
 | 
					        ssd->ds_mirror = g_malloc0(size);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for (blk = 0; blk < blocks; blk++) {
 | 
				
			||||||
 | 
					        dirty_top[blk] = -1;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    guest = ds_get_data(ssd->ds);
 | 
				
			||||||
 | 
					    mirror = ssd->ds_mirror;
 | 
				
			||||||
 | 
					    for (y = ssd->dirty.top; y < ssd->dirty.bottom; y++) {
 | 
				
			||||||
 | 
					        yoff = y * ds_get_linesize(ssd->ds);
 | 
				
			||||||
 | 
					        for (x = ssd->dirty.left; x < ssd->dirty.right; x += blksize) {
 | 
				
			||||||
 | 
					            xoff = x * bpp;
 | 
				
			||||||
 | 
					            blk = x / blksize;
 | 
				
			||||||
 | 
					            bw = MIN(blksize, ssd->dirty.right - x);
 | 
				
			||||||
 | 
					            if (memcmp(guest + yoff + xoff,
 | 
				
			||||||
 | 
					                       mirror + yoff + xoff,
 | 
				
			||||||
 | 
					                       bw * bpp) == 0) {
 | 
				
			||||||
 | 
					                if (dirty_top[blk] != -1) {
 | 
				
			||||||
 | 
					                    QXLRect update = {
 | 
				
			||||||
 | 
					                        .top    = dirty_top[blk],
 | 
				
			||||||
 | 
					                        .bottom = y,
 | 
				
			||||||
 | 
					                        .left   = x,
 | 
				
			||||||
 | 
					                        .right  = x + bw,
 | 
				
			||||||
 | 
					                    };
 | 
				
			||||||
 | 
					                    qemu_spice_create_one_update(ssd, &update);
 | 
				
			||||||
 | 
					                    dirty_top[blk] = -1;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                if (dirty_top[blk] == -1) {
 | 
				
			||||||
 | 
					                    dirty_top[blk] = y;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for (x = ssd->dirty.left; x < ssd->dirty.right; x += blksize) {
 | 
				
			||||||
 | 
					        blk = x / blksize;
 | 
				
			||||||
 | 
					        bw = MIN(blksize, ssd->dirty.right - x);
 | 
				
			||||||
 | 
					        if (dirty_top[blk] != -1) {
 | 
				
			||||||
 | 
					            QXLRect update = {
 | 
				
			||||||
 | 
					                .top    = dirty_top[blk],
 | 
				
			||||||
 | 
					                .bottom = ssd->dirty.bottom,
 | 
				
			||||||
 | 
					                .left   = x,
 | 
				
			||||||
 | 
					                .right  = x + bw,
 | 
				
			||||||
 | 
					            };
 | 
				
			||||||
 | 
					            qemu_spice_create_one_update(ssd, &update);
 | 
				
			||||||
 | 
					            dirty_top[blk] = -1;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    memset(&ssd->dirty, 0, sizeof(ssd->dirty));
 | 
					    memset(&ssd->dirty, 0, sizeof(ssd->dirty));
 | 
				
			||||||
    return update;
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
| 
						 | 
					@ -315,6 +384,7 @@ void qemu_spice_display_init_common(SimpleSpiceDisplay *ssd, DisplayState *ds)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    ssd->ds = ds;
 | 
					    ssd->ds = ds;
 | 
				
			||||||
    qemu_mutex_init(&ssd->lock);
 | 
					    qemu_mutex_init(&ssd->lock);
 | 
				
			||||||
 | 
					    QTAILQ_INIT(&ssd->updates);
 | 
				
			||||||
    ssd->mouse_x = -1;
 | 
					    ssd->mouse_x = -1;
 | 
				
			||||||
    ssd->mouse_y = -1;
 | 
					    ssd->mouse_y = -1;
 | 
				
			||||||
    if (ssd->num_surfaces == 0) {
 | 
					    if (ssd->num_surfaces == 0) {
 | 
				
			||||||
| 
						 | 
					@ -345,16 +415,20 @@ void qemu_spice_display_update(SimpleSpiceDisplay *ssd,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void qemu_spice_display_resize(SimpleSpiceDisplay *ssd)
 | 
					void qemu_spice_display_resize(SimpleSpiceDisplay *ssd)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
					    SimpleSpiceUpdate *update;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    dprint(1, "%s:\n", __FUNCTION__);
 | 
					    dprint(1, "%s:\n", __FUNCTION__);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    memset(&ssd->dirty, 0, sizeof(ssd->dirty));
 | 
					    memset(&ssd->dirty, 0, sizeof(ssd->dirty));
 | 
				
			||||||
    qemu_pf_conv_put(ssd->conv);
 | 
					    qemu_pf_conv_put(ssd->conv);
 | 
				
			||||||
    ssd->conv = NULL;
 | 
					    ssd->conv = NULL;
 | 
				
			||||||
 | 
					    g_free(ssd->ds_mirror);
 | 
				
			||||||
 | 
					    ssd->ds_mirror = NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    qemu_mutex_lock(&ssd->lock);
 | 
					    qemu_mutex_lock(&ssd->lock);
 | 
				
			||||||
    if (ssd->update != NULL) {
 | 
					    while ((update = QTAILQ_FIRST(&ssd->updates)) != NULL) {
 | 
				
			||||||
        qemu_spice_destroy_update(ssd, ssd->update);
 | 
					        QTAILQ_REMOVE(&ssd->updates, update, next);
 | 
				
			||||||
        ssd->update = NULL;
 | 
					        qemu_spice_destroy_update(ssd, update);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    qemu_mutex_unlock(&ssd->lock);
 | 
					    qemu_mutex_unlock(&ssd->lock);
 | 
				
			||||||
    qemu_spice_destroy_host_primary(ssd);
 | 
					    qemu_spice_destroy_host_primary(ssd);
 | 
				
			||||||
| 
						 | 
					@ -384,8 +458,8 @@ void qemu_spice_display_refresh(SimpleSpiceDisplay *ssd)
 | 
				
			||||||
    vga_hw_update();
 | 
					    vga_hw_update();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    qemu_mutex_lock(&ssd->lock);
 | 
					    qemu_mutex_lock(&ssd->lock);
 | 
				
			||||||
    if (ssd->update == NULL) {
 | 
					    if (QTAILQ_EMPTY(&ssd->updates)) {
 | 
				
			||||||
        ssd->update = qemu_spice_create_update(ssd);
 | 
					        qemu_spice_create_update(ssd);
 | 
				
			||||||
        ssd->notify++;
 | 
					        ssd->notify++;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    qemu_spice_cursor_refresh_unlocked(ssd);
 | 
					    qemu_spice_cursor_refresh_unlocked(ssd);
 | 
				
			||||||
| 
						 | 
					@ -442,9 +516,9 @@ static int interface_get_command(QXLInstance *sin, struct QXLCommandExt *ext)
 | 
				
			||||||
    dprint(3, "%s:\n", __FUNCTION__);
 | 
					    dprint(3, "%s:\n", __FUNCTION__);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    qemu_mutex_lock(&ssd->lock);
 | 
					    qemu_mutex_lock(&ssd->lock);
 | 
				
			||||||
    if (ssd->update != NULL) {
 | 
					    update = QTAILQ_FIRST(&ssd->updates);
 | 
				
			||||||
        update = ssd->update;
 | 
					    if (update != NULL) {
 | 
				
			||||||
        ssd->update = NULL;
 | 
					        QTAILQ_REMOVE(&ssd->updates, update, next);
 | 
				
			||||||
        *ext = update->ext;
 | 
					        *ext = update->ext;
 | 
				
			||||||
        ret = true;
 | 
					        ret = true;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -72,6 +72,7 @@ typedef struct SimpleSpiceUpdate SimpleSpiceUpdate;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct SimpleSpiceDisplay {
 | 
					struct SimpleSpiceDisplay {
 | 
				
			||||||
    DisplayState *ds;
 | 
					    DisplayState *ds;
 | 
				
			||||||
 | 
					    uint8_t *ds_mirror;
 | 
				
			||||||
    void *buf;
 | 
					    void *buf;
 | 
				
			||||||
    int bufsize;
 | 
					    int bufsize;
 | 
				
			||||||
    QXLWorker *worker;
 | 
					    QXLWorker *worker;
 | 
				
			||||||
| 
						 | 
					@ -92,7 +93,7 @@ struct SimpleSpiceDisplay {
 | 
				
			||||||
     * to them must be protected by the lock.
 | 
					     * to them must be protected by the lock.
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    QemuMutex lock;
 | 
					    QemuMutex lock;
 | 
				
			||||||
    SimpleSpiceUpdate *update;
 | 
					    QTAILQ_HEAD(, SimpleSpiceUpdate) updates;
 | 
				
			||||||
    QEMUCursor *cursor;
 | 
					    QEMUCursor *cursor;
 | 
				
			||||||
    int mouse_x, mouse_y;
 | 
					    int mouse_x, mouse_y;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
| 
						 | 
					@ -102,6 +103,7 @@ struct SimpleSpiceUpdate {
 | 
				
			||||||
    QXLImage image;
 | 
					    QXLImage image;
 | 
				
			||||||
    QXLCommandExt ext;
 | 
					    QXLCommandExt ext;
 | 
				
			||||||
    uint8_t *bitmap;
 | 
					    uint8_t *bitmap;
 | 
				
			||||||
 | 
					    QTAILQ_ENTRY(SimpleSpiceUpdate) next;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
int qemu_spice_rect_is_empty(const QXLRect* r);
 | 
					int qemu_spice_rect_is_empty(const QXLRect* r);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue