libspice显示命令调用流程分析

1、spice通过spice_server_add_interface接口提供给qemu相应的接口,包括qxl显示接口,迁移接口,键盘接口,鼠标接口等。

cpp 复制代码
SPICE_GNUC_VISIBLE int spice_server_add_interface(SpiceServer *reds,
                                                  SpiceBaseInstance *sin)
{
    ...
    } else if (strcmp(base_interface->type, SPICE_INTERFACE_QXL) == 0) {
        QXLInstance *qxl;
        spice_debug("SPICE_INTERFACE_QXL");
        if (base_interface->major_version != SPICE_INTERFACE_QXL_MAJOR ||
            base_interface->minor_version > SPICE_INTERFACE_QXL_MINOR) {
            spice_warning("unsupported qxl interface");
            return -1;
        }
        qxl = SPICE_UPCAST(QXLInstance, sin);
        if (qxl->id < 0) {
            spice_warning("invalid QXL ID");
            return -1;
        }
        if (reds_find_channel(reds, SPICE_CHANNEL_DISPLAY, qxl->id)) {
            spice_warning("QXL ID already allocated");
            return -1;
        }
        red_qxl_init(reds, qxl);
        reds->qxl_instances.push_front(qxl);

        /* this function has to be called after the qxl is on the list
         * as QXLInstance clients expect the qxl to be on the list when
         * this callback is called. This as clients assume they can start the
         * qxl_instances. Also note that this should be the first callback to
         * be called. */
        red_qxl_attach_worker(qxl);
        red_qxl_set_compression_level(qxl, calc_compression_level(reds));
    ...
}

其中qemu源码的调用逻辑 https://blog.csdn.net/cai742925624/article/details/139607523?spm=1001.2014.3001.5502
2、当显示提供显示接口时,会初始spice的qxl模块,创建RedWorker处理类。

cpp 复制代码
void red_qxl_init(RedsState *reds, QXLInstance *qxl)
{
    QXLState *qxl_state;
    spice_return_if_fail(qxl != nullptr);
    qxl_state = new QXLState();
    qxl_state->reds = reds;
    qxl_state->qxl = qxl;
    pthread_mutex_init(&qxl_state->scanout_mutex, nullptr);
    qxl_state->scanout.drm_dma_buf_fd = -1;
    qxl_state->gl_draw_cookie = GL_DRAW_COOKIE_INVALID;
    qxl_state->dispatcher = red::make_shared<Dispatcher>(RED_WORKER_MESSAGE_COUNT);
    qxl_state->max_monitors = UINT_MAX;
    qxl->st = qxl_state;
    qxl_state->worker = red_worker_new(qxl);
    red_worker_run(qxl_state->worker);
}

3、RedWorker会实现调度器dispatcher并注册各种处理的回调函数,并在此时创建显示通道。

cpp 复制代码
RedWorker* red_worker_new(QXLInstance *qxl)
{
    QXLDevInitInfo init_info;
    RedWorker *worker;
    Dispatcher *dispatcher;
    RedsState *reds = red_qxl_get_server(qxl->st);
    RedChannel *channel;

    red_qxl_get_init_info(qxl, &init_info);
    worker = g_new0(RedWorker, 1);
    worker->core = event_loop_core;
    worker->core.main_context = g_main_context_new();
    worker->record = reds_get_record(reds);
    dispatcher = red_qxl_get_dispatcher(qxl);
    dispatcher->set_opaque(worker);
    worker->qxl = qxl;
    register_callbacks(dispatcher);
    if (worker->record) {
        dispatcher->register_universal_handler(worker_dispatcher_record);
    }

    worker->driver_cap_monitors_config = false;
    char worker_str[SPICE_STAT_NODE_NAME_MAX];
    snprintf(worker_str, sizeof(worker_str), "display[%d]", worker->qxl->id & 0xff);
    stat_init_node(&worker->stat, reds, nullptr, worker_str, TRUE);
    stat_init_counter(&worker->wakeup_counter, reds, &worker->stat, "wakeups", TRUE);
    stat_init_counter(&worker->command_counter, reds, &worker->stat, "commands", TRUE);
    stat_init_counter(&worker->full_loop_counter, reds, &worker->stat, "full_loops", TRUE);
    stat_init_counter(&worker->total_loop_counter, reds, &worker->stat, "total_loops", TRUE);
    worker->dispatch_watch = dispatcher->create_watch(&worker->core);
    spice_assert(worker->dispatch_watch != nullptr);
    GSource *source = g_source_new(&worker_source_funcs, sizeof(RedWorkerSource));
    SPICE_CONTAINEROF(source, RedWorkerSource, source)->worker = worker;
    g_source_attach(source, worker->core.main_context);
    g_source_unref(source);
    memslot_info_init(&worker->mem_slots,init_info.num_memslots_groups,init_info.num_memslots,
        init_info.memslot_gen_bits,init_info.memslot_id_bits,init_info.internal_groupslot_id);
    worker->event_timeout = INF_EVENT_WAIT;
    worker->cursor_channel = cursor_channel_new(reds, qxl->id,&worker->core, dispatcher).get(); // XXX
    channel = worker->cursor_channel;
    channel->init_stat_node(&worker->stat, "cursor_channel");

    // TODO: handle seamless migration. Temp, setting migrate to FALSE
    worker->display_channel = display_channel_new(reds, qxl, &worker->core, dispatcher,FALSE,reds_get_streaming_video(reds),
        eds_get_video_codecs(reds),init_info.n_surfaces).get(); // XXX
    channel = worker->display_channel;
    channel->init_stat_node(&worker->stat, "display_channel");
    display_channel_set_image_compression(worker->display_channel,spice_server_get_image_compression(reds));
    return worker;
}

3、RedWorker主要处理qemu发过来的命令并找到调用回调函数处理,比如刷新显存图像 handle_dev_flush_surfaces_async

cpp 复制代码
static void register_callbacks(Dispatcher *dispatcher)
{
    /* TODO: register cursor & display specific msg in respective channel files */
    register_handler(dispatcher,
                     handle_dev_update,
                     true);
    register_handler(dispatcher,
                     handle_dev_update_async,
                     false);
    register_handler(dispatcher,
                     handle_dev_add_memslot,
                     true);
    register_handler(dispatcher,
                     handle_dev_add_memslot_async,
                     false);
    register_handler(dispatcher,
                     handle_dev_del_memslot,
                     false);
    register_handler(dispatcher,
                     handle_dev_destroy_surfaces,
                     true);
    register_handler(dispatcher,
                     handle_dev_destroy_surfaces_async,
                     false);
    register_handler(dispatcher,
                     handle_dev_destroy_primary_surface,
                     true);
    register_handler(dispatcher,
                     handle_dev_destroy_primary_surface_async,
                     false);
    register_handler(dispatcher,
                     handle_dev_create_primary_surface_async,
                     false);
    register_handler(dispatcher,
                     handle_dev_create_primary_surface,
                     true);
    register_handler(dispatcher,
                     handle_dev_reset_image_cache,
                     true);
    register_handler(dispatcher,
                     handle_dev_reset_cursor,
                     true);
    register_handler(dispatcher,
                     handle_dev_wakeup,
                     false);
    register_handler(dispatcher,
                     handle_dev_oom,
                     false);
    register_handler(dispatcher,
                     handle_dev_start,
                     false);
    register_handler(dispatcher,
                     handle_dev_flush_surfaces_async,
                     false);
    register_handler(dispatcher,
                     handle_dev_stop,
                     true);
    register_handler(dispatcher,
                     handle_dev_loadvm_commands,
                     true);
    register_handler(dispatcher,
                     handle_dev_set_compression,
                     false);
    register_handler(dispatcher,
                     handle_dev_set_streaming_video,
                     false);
    register_handler(dispatcher,
                     handle_dev_set_video_codecs,
                     false);
    register_handler(dispatcher,
                     handle_dev_set_mouse_mode,
                     false);
    register_handler(dispatcher,
                     handle_dev_destroy_surface_wait,
                     true);
    register_handler(dispatcher,
                     handle_dev_destroy_surface_wait_async,
                     false);
    register_handler(dispatcher,
                     handle_dev_reset_memslots,
                     false);
    register_handler(dispatcher,
                     handle_dev_monitors_config_async,
                     false);
    register_handler(dispatcher,
                     handle_dev_driver_unload,
                     false);
    register_handler(dispatcher,
                     handle_dev_gl_scanout,
                     false);
    register_handler(dispatcher,
                     handle_dev_gl_draw_async,
                     false);
    register_handler(dispatcher,
                     handle_dev_close,
                     false);
}

4、handle_dev_update_async会处理显示命令。

cpp 复制代码
static void
handle_dev_update_async(RedWorker* worker, RedWorkerMessageUpdateAsync* msg)
{
    QXLRect *qxl_dirty_rects = nullptr;
    uint32_t num_dirty_rects = 0;
    spice_return_if_fail(red_qxl_is_running(worker->qxl));
    spice_return_if_fail(qxl_get_interface(worker->qxl)->update_area_complete);
    flush_display_commands(worker);
    display_channel_update(worker->display_channel,msg->surface_id, &msg->qxl_area, msg->clear_dirty_region,&qxl_dirty_rects, &num_dirty_rects);
    red_qxl_update_area_complete(worker->qxl, msg->surface_id,qxl_dirty_rects, num_dirty_rects);
    g_free(qxl_dirty_rects);
    red_qxl_async_complete(worker->qxl, msg->base.cookie);
}

static void flush_display_commands(RedWorker *worker)
{
    flush_commands(worker, worker->display_channel, red_process_display);
}

5、将命令发送给各个显示通道客户端。

cpp 复制代码
static void flush_commands(RedWorker *worker, RedChannel *red_channel, red_process_t process)
{
    for (;;) {
        uint64_t end_time;
        int ring_is_empty;
        process(worker, &ring_is_empty); //red_process_display先处理完成
        if (ring_is_empty) {
            break;
        }
        while (process(worker, &ring_is_empty)) {
            red_channel->push();
        }
        if (ring_is_empty) {
            break;
        }
        end_time = spice_get_monotonic_time_ns() + COMMON_CLIENT_TIMEOUT;
        for (;;) {
            red_channel->push();
            if (red_channel->max_pipe_size() <= MAX_PIPE_SIZE) {
                break;
            }
            red_channel->receive();
            red_channel->send();
            // TODO: MC: the whole timeout will break since it takes lowest timeout, should do it client by client.
            if (spice_get_monotonic_time_ns() >= end_time) {
                // TODO: we need to record the client that actually causes the timeout. So we need to check the locations of the various pipe heads when counting, and disconnect only those/that.
                spice_warning("flush timeout");
                red_channel->disconnect();
            } else {
                usleep(DISPLAY_CLIENT_RETRY_INTERVAL);
            }
        }
    }
}

其中显示命令处理

cpp 复制代码
int red_qxl_get_command(QXLInstance *qxl, struct QXLCommandExt *cmd)
{
    QXLInterface *qxl_interface = qxl_get_interface(qxl);
    return qxl_interface->get_command(qxl, cmd);
}

static int red_process_display(RedWorker *worker, int *ring_is_empty)
{
    QXLCommandExt ext_cmd;
    int n = 0;
    uint64_t start = spice_get_monotonic_time_ns();
    if (!red_qxl_is_running(worker->qxl)) {
        *ring_is_empty = TRUE;
        return n;
    }
    stat_inc_counter(worker->total_loop_counter, 1);
    worker->process_display_generation++;
    *ring_is_empty = FALSE;
    while (worker->display_channel->max_pipe_size() <= MAX_PIPE_SIZE) {
        if (!red_qxl_get_command(worker->qxl, &ext_cmd)) {
            *ring_is_empty = TRUE;
            if (worker->display_poll_tries < CMD_RING_POLL_RETRIES) {
                worker->event_timeout = MIN(worker->event_timeout, CMD_RING_POLL_TIMEOUT);
            } else if (worker->display_poll_tries == CMD_RING_POLL_RETRIES && !red_qxl_req_cmd_notification(worker->qxl)) {
                continue;
            }
            worker->display_poll_tries++;
            return n;
        }
        if (worker->record) {
            red_record_qxl_command(worker->record, &worker->mem_slots, ext_cmd);
        }

        stat_inc_counter(worker->command_counter, 1);
        worker->display_poll_tries = 0;
        switch (ext_cmd.cmd.type) {
        case QXL_CMD_DRAW: {
            auto red_drawable = red_drawable_new(worker->qxl, &worker->mem_slots, ext_cmd.group_id, ext_cmd.cmd.data,ext_cmd.flags); // returns with 1 ref
            if (red_drawable) {
                display_channel_process_draw(worker->display_channel, std::move(red_drawable),worker->process_display_generation);
            }
            break;
        }
        case QXL_CMD_UPDATE: {
            auto update = red_update_cmd_new(worker->qxl, &worker->mem_slots,ext_cmd.group_id, ext_cmd.cmd.data);
            if (!update) {
                break;
            }
            if (!display_channel_validate_surface(worker->display_channel, update->surface_id)) {
                spice_warning("Invalid surface in QXL_CMD_UPDATE");
            } else {
                display_channel_draw(worker->display_channel, &update->area, update->surface_id);
                red_qxl_notify_update(worker->qxl, update->update_id);
            }
            break;
        }
        case QXL_CMD_MESSAGE: {
            auto message = red_message_new(worker->qxl, &worker->mem_slots,ext_cmd.group_id, ext_cmd.cmd.data);
            if (!message) {
                break;
            }
#ifdef DEBUG
            spice_warning("MESSAGE: %.*s", message->len, message->data);
#endif
            break;
        }
        case QXL_CMD_SURFACE:
            red_process_surface_cmd(worker, &ext_cmd, FALSE);
            break;
        default:
            spice_error("bad command type");
        }
        n++;
        if (worker->display_channel->all_blocked() || spice_get_monotonic_time_ns() - start > NSEC_PER_SEC / 100) {
            worker->event_timeout = 0;
            return n;
        }
    }
    worker->was_blocked = TRUE;
    stat_inc_counter(worker->full_loop_counter, 1);
    return n;
}
通过qemu中spice-display.c的dpy_interface获取数据
static const QXLInterface dpy_interface = {
    .base.type               = SPICE_INTERFACE_QXL,
    .base.description        = "qemu simple display",
    .base.major_version      = SPICE_INTERFACE_QXL_MAJOR,
    .base.minor_version      = SPICE_INTERFACE_QXL_MINOR,
    .attache_worker          = interface_attach_worker,
    .set_compression_level   = interface_set_compression_level,
#if SPICE_NEEDS_SET_MM_TIME
    .set_mm_time             = interface_set_mm_time,
#endif
    .get_init_info           = interface_get_init_info,
    /* the callbacks below are called from spice server thread context */
    .get_command             = interface_get_command,
    .req_cmd_notification    = interface_req_cmd_notification,
    .release_resource        = interface_release_resource,
    .get_cursor_command      = interface_get_cursor_command,
    .req_cursor_notification = interface_req_cursor_notification,
    .notify_update           = interface_notify_update,
    .flush_resources         = interface_flush_resources,
    .async_complete          = interface_async_complete,
    .update_area_complete    = interface_update_area_complete,
    .set_client_capabilities = interface_set_client_capabilities,
    .client_monitors_config  = interface_client_monitors_config,
};

static int interface_get_command(QXLInstance *sin, QXLCommandExt *ext)
{
    SimpleSpiceDisplay *ssd = container_of(sin, SimpleSpiceDisplay, qxl);
    SimpleSpiceUpdate *update;
    int ret = false;
    qemu_mutex_lock(&ssd->lock);
    update = QTAILQ_FIRST(&ssd->updates);
    if (update != NULL) {
        QTAILQ_REMOVE(&ssd->updates, update, next);
        *ext = update->ext;
        ret = true;
    }
    qemu_mutex_unlock(&ssd->lock);
    return ret;
}

//qemu中的qxl.c
static void display_refresh(DisplayChangeListener *dcl)
{
    PCIQXLDevice *qxl = container_of(dcl, PCIQXLDevice, ssd.dcl);
    if (qxl->mode == QXL_MODE_VGA) {
        qemu_spice_display_refresh(&qxl->ssd);
    }
}


static const QXLInterface qxl_interface = {
    .base.type               = SPICE_INTERFACE_QXL,
    .base.description        = "qxl gpu",
    .base.major_version      = SPICE_INTERFACE_QXL_MAJOR,
    .base.minor_version      = SPICE_INTERFACE_QXL_MINOR,
    .attache_worker          = interface_attach_worker,
    .set_compression_level   = interface_set_compression_level,
#if SPICE_NEEDS_SET_MM_TIME
    .set_mm_time             = interface_set_mm_time,
#endif
    .get_init_info           = interface_get_init_info,
    /* the callbacks below are called from spice server thread context */
    .get_command             = interface_get_command,
    .req_cmd_notification    = interface_req_cmd_notification,
    .release_resource        = interface_release_resource,
    .get_cursor_command      = interface_get_cursor_command,
    .req_cursor_notification = interface_req_cursor_notification,
    .notify_update           = interface_notify_update,
    .flush_resources         = interface_flush_resources,
    .async_complete          = interface_async_complete,
    .update_area_complete    = interface_update_area_complete,
    .set_client_capabilities = interface_set_client_capabilities,
    .client_monitors_config = interface_client_monitors_config,
};

/* called from spice server thread context only */
static int interface_get_command(QXLInstance *sin, struct QXLCommandExt *ext)
{
    PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl);
    SimpleSpiceUpdate *update;
    QXLCommandRing *ring;
    QXLCommand *cmd;
    int notify, ret;
    trace_qxl_ring_command_check(qxl->id, qxl_mode_to_string(qxl->mode));
    switch (qxl->mode) {
    case QXL_MODE_VGA:
        ret = false;
        qemu_mutex_lock(&qxl->ssd.lock);
        update = QTAILQ_FIRST(&qxl->ssd.updates);
        if (update != NULL) {
            QTAILQ_REMOVE(&qxl->ssd.updates, update, next);
            *ext = update->ext;
            ret = true;
        }
        qemu_mutex_unlock(&qxl->ssd.lock);
        if (ret) {
            trace_qxl_ring_command_get(qxl->id, qxl_mode_to_string(qxl->mode));
            qxl_log_command(qxl, "vga", ext);
        }
        return ret;
    case QXL_MODE_COMPAT:
    case QXL_MODE_NATIVE:
    case QXL_MODE_UNDEFINED:
        ring = &qxl->ram->cmd_ring;
        if (qxl->guest_bug || SPICE_RING_IS_EMPTY(ring)) {
            return false;
        }
        SPICE_RING_CONS_ITEM(qxl, ring, cmd);
        if (!cmd) {
            return false;
        }
        ext->cmd      = *cmd;
        ext->group_id = MEMSLOT_GROUP_GUEST;
        ext->flags    = qxl->cmdflags;
        SPICE_RING_POP(ring, notify);
        qxl_ring_set_dirty(qxl);
        if (notify) {
            qxl_send_events(qxl, QXL_INTERRUPT_DISPLAY);
        }
        qxl->guest_primary.commands++;
        qxl_track_command(qxl, ext);
        qxl_log_command(qxl, "cmd", ext);
        trace_qxl_ring_command_get(qxl->id, qxl_mode_to_string(qxl->mode));
        return true;
    default:
        return false;
    }
}

6、遍历显示通道的TCP客户端并将处理后的数据发送

cpp 复制代码
void RedChannel::push()
{
    red_channel_foreach_client(this, &RedChannelClient::push);
}

void RedChannelClient::push()
{
    if (priv->during_send) {
        return;
    }

    priv->during_send = TRUE;
    red::shared_ptr<RedChannelClient> hold_rcc(this);
    if (is_blocked()) {
        send();
    }

    if (!no_item_being_sent() && !is_blocked()) {
        priv->set_blocked();
        red_channel_warning(get_channel(),"ERROR: an item waiting to be sent and not blocked");
    }

    while (auto pipe_item = priv->pipe_item_get()) {
        send_any_item(pipe_item.get());
    }
    /* prepare_pipe_add() will reenable WRITE events when the priv->pipe is empty
     * ack_zero_messages_window() will reenable WRITE events
     * if we were waiting for acks to be received
     * If we don't remove WRITE if we are waiting for ack we will be keep
     * notified that we can write and we then exit (see pipe_item_get) as we
     * are waiting for the ack consuming CPU in a tight loop
     */
    if ((no_item_being_sent() && priv->pipe.empty()) ||
        priv->waiting_for_ack()) {
        priv->watch_update_mask(SPICE_WATCH_EVENT_READ);
        /* channel has no pending data to send so now we can flush data in
         * order to avoid data stall into buffers in case of manual
         * flushing
         * We need to flush also in case of ack as it is possible
         * that for a long train of small messages the message that would
         * cause the client to send the ack is still in the queue
         */
        red_stream_flush(priv->stream);
    }
    priv->during_send = FALSE;
}

7、先判断是不是父类RedChannelClient的命令类型。

cpp 复制代码
void RedChannelClient::send_any_item(RedPipeItem *item)
{
    spice_assert(no_item_being_sent());
    priv->reset_send_data();
    switch (item->type) {
        case RED_PIPE_ITEM_TYPE_SET_ACK:
            send_set_ack();
            break;
        case RED_PIPE_ITEM_TYPE_MIGRATE:
            send_migrate();
            break;
        case RED_PIPE_ITEM_TYPE_EMPTY_MSG:
            send_empty_msg(item);
            break;
        case RED_PIPE_ITEM_TYPE_PING:
            send_ping();
            break;
        case RED_PIPE_ITEM_TYPE_MARKER:
            static_cast<MarkerPipeItem*>(item)->item_sent = true;
            break;
        default:
            send_item(item);
            break;
    }
}

8、处理子类(如显示通道)的命令。

cpp 复制代码
void DisplayChannelClient::send_item(RedPipeItem *pipe_item)
{
    DisplayChannelClient *dcc = this;
    SpiceMarshaller *m = get_marshaller();
    ::reset_send_data(dcc);
    switch (pipe_item->type) {
    case RED_PIPE_ITEM_TYPE_DRAW: {
        auto dpi = static_cast<RedDrawablePipeItem*>(pipe_item);
        marshall_qxl_drawable(this, m, dpi);
        break;
    }
    case RED_PIPE_ITEM_TYPE_INVAL_ONE:
        marshall_inval_palette(this, m, static_cast<RedCachePipeItem*>(pipe_item));
        break;
    case RED_PIPE_ITEM_TYPE_STREAM_CREATE: {
        auto item = static_cast<StreamCreateDestroyItem*>(pipe_item);
        marshall_stream_start(this, m, item->agent);
        break;
    }
    case RED_PIPE_ITEM_TYPE_STREAM_CLIP:
        marshall_stream_clip(this, m, static_cast<VideoStreamClipItem*>(pipe_item));
        break;
    case RED_PIPE_ITEM_TYPE_STREAM_DESTROY: {
        auto item = static_cast<StreamCreateDestroyItem*>(pipe_item);
        marshall_stream_end(this, m, item->agent);
        break;
    }
    case RED_PIPE_ITEM_TYPE_UPGRADE:
        marshall_upgrade(this, m, static_cast<RedUpgradeItem*>(pipe_item));
        break;
    case RED_PIPE_ITEM_TYPE_MIGRATE_DATA:
        display_channel_marshall_migrate_data(this, m);
        break;
    case RED_PIPE_ITEM_TYPE_IMAGE:
        red_marshall_image(this, m, static_cast<RedImageItem*>(pipe_item));
        break;
    case RED_PIPE_ITEM_TYPE_PIXMAP_SYNC:
        display_channel_marshall_pixmap_sync(this, m);
        break;
    case RED_PIPE_ITEM_TYPE_PIXMAP_RESET:
        display_channel_marshall_reset_cache(this, m);
        break;
    case RED_PIPE_ITEM_TYPE_INVAL_PALETTE_CACHE:
        dcc_palette_cache_reset(dcc);
        init_send_data(SPICE_MSG_DISPLAY_INVAL_ALL_PALETTES);
        break;
    case RED_PIPE_ITEM_TYPE_CREATE_SURFACE: {
        auto surface_create = static_cast<RedSurfaceCreateItem*>(pipe_item);
        marshall_surface_create(this, m, &surface_create->surface_create);
        break;
    }
    case RED_PIPE_ITEM_TYPE_DESTROY_SURFACE: {
        auto surface_destroy = static_cast<RedSurfaceDestroyItem*>(pipe_item);
        marshall_surface_destroy(this, m, surface_destroy->surface_destroy.surface_id);
        break;
    }
    case RED_PIPE_ITEM_TYPE_MONITORS_CONFIG: {
        auto monconf_item = static_cast<RedMonitorsConfigItem*>(pipe_item);
        marshall_monitors_config(this, m, monconf_item->monitors_config);
        break;
    }
    case RED_PIPE_ITEM_TYPE_STREAM_ACTIVATE_REPORT: {
        auto report_item =
            static_cast<RedStreamActivateReportItem*>(pipe_item);
        marshall_stream_activate_report(this, m, report_item);
        break;
    }
    case RED_PIPE_ITEM_TYPE_GL_SCANOUT:
        marshall_gl_scanout(this, m, pipe_item);
        break;
    case RED_PIPE_ITEM_TYPE_GL_DRAW:
        marshall_gl_draw(this, m, pipe_item);
        break;
    default:
        spice_warn_if_reached();
    }
    // a message is pending
    if (send_message_pending()) {
        ::begin_send_message(this);
    }
}

9、序列化命令,如以draw命令为例。

cpp 复制代码
static void marshall_qxl_drawable(DisplayChannelClient *dcc,
                                  SpiceMarshaller *m,
                                  RedDrawablePipeItem *dpi)
{
    spice_return_if_fail(dcc);
    Drawable *item = dpi->drawable;
    DisplayChannel *display = DCC_TO_DC(dcc);
    spice_return_if_fail(display);
    /* allow sized frames to be streamed, even if they where replaced by another frame, since
     * newer frames might not cover sized frames completely if they are bigger */
    if (item->stream && red_marshall_stream_data(dcc, m, item)) {
        return;
    }
    if (display->priv->enable_jpeg)
        marshall_lossy_qxl_drawable(dcc, m, dpi);
    else
        marshall_lossless_qxl_drawable(dcc, m, dpi);
}

static void marshall_lossy_qxl_drawable(DisplayChannelClient *dcc,
                                        SpiceMarshaller *base_marshaller,
                                        RedDrawablePipeItem *dpi)
{
    Drawable *item = dpi->drawable;
    switch (item->red_drawable->type) {
    case QXL_DRAW_FILL:
        red_lossy_marshall_qxl_draw_fill(dcc, base_marshaller, dpi);
        break;
    case QXL_DRAW_OPAQUE:
        red_lossy_marshall_qxl_draw_opaque(dcc, base_marshaller, dpi);
        break;
    case QXL_DRAW_COPY:
        red_lossy_marshall_qxl_draw_copy(dcc, base_marshaller, dpi);
        break;
    case QXL_DRAW_TRANSPARENT:
        red_lossy_marshall_qxl_draw_transparent(dcc, base_marshaller, dpi);
        break;
    case QXL_DRAW_ALPHA_BLEND:
        red_lossy_marshall_qxl_draw_alpha_blend(dcc, base_marshaller, dpi);
        break;
    case QXL_COPY_BITS:
        red_lossy_marshall_qxl_copy_bits(dcc, base_marshaller, dpi);
        break;
    case QXL_DRAW_BLEND:
        red_lossy_marshall_qxl_draw_blend(dcc, base_marshaller, dpi);
        break;
    case QXL_DRAW_BLACKNESS:
        red_lossy_marshall_qxl_draw_blackness(dcc, base_marshaller, dpi);
        break;
    case QXL_DRAW_WHITENESS:
        red_lossy_marshall_qxl_draw_whiteness(dcc, base_marshaller, dpi);
        break;
    case QXL_DRAW_INVERS:
        red_lossy_marshall_qxl_draw_inverse(dcc, base_marshaller, item);
        break;
    case QXL_DRAW_ROP3:
        red_lossy_marshall_qxl_draw_rop3(dcc, base_marshaller, dpi);
        break;
    case QXL_DRAW_COMPOSITE:
        red_lossy_marshall_qxl_draw_composite(dcc, base_marshaller, dpi);
        break;
    case QXL_DRAW_STROKE:
        red_lossy_marshall_qxl_draw_stroke(dcc, base_marshaller, dpi);
        break;
    case QXL_DRAW_TEXT:
        red_lossy_marshall_qxl_draw_text(dcc, base_marshaller, dpi);
        break;
    default:
        spice_warn_if_reached();
    }
}

static void red_lossy_marshall_qxl_draw_copy(DisplayChannelClient *dcc,
                                             SpiceMarshaller *base_marshaller,
                                             RedDrawablePipeItem *dpi)
{
    Drawable *item = dpi->drawable;
    RedDrawable *drawable = item->red_drawable.get();
    int has_mask = !!drawable->u.copy.mask.bitmap;
    int src_is_lossy;
    BitmapData src_bitmap_data;
    FillBitsType src_send_type;
    src_is_lossy = is_bitmap_lossy(dcc, item, drawable->u.copy.src_bitmap,&drawable->u.copy.src_area, &src_bitmap_data);
    src_send_type = red_marshall_qxl_draw_copy(dcc, base_marshaller, dpi, TRUE);
    if (src_send_type == FILL_BITS_TYPE_COMPRESS_LOSSY) {
        src_is_lossy = TRUE;
    } else if (src_send_type == FILL_BITS_TYPE_COMPRESS_LOSSLESS) {
        src_is_lossy = FALSE;
    }
    surface_lossy_region_update(dcc, item, has_mask,src_is_lossy);
}

static FillBitsType red_marshall_qxl_draw_copy(DisplayChannelClient *dcc,
                                               SpiceMarshaller *base_marshaller,
                                               RedDrawablePipeItem *dpi,
                                               int src_allowed_lossy)
{
    Drawable *item = dpi->drawable;
    RedDrawable *drawable = item->red_drawable.get();
    SpiceMarshaller *src_bitmap_out;
    SpiceMarshaller *mask_bitmap_out;
    SpiceCopy copy;
    FillBitsType src_send_type;
    dcc->init_send_data(SPICE_MSG_DISPLAY_DRAW_COPY);
    fill_base(base_marshaller, item);
    copy = drawable->u.copy;
    spice_marshall_Copy(base_marshaller,&copy,&src_bitmap_out, &mask_bitmap_out);
    src_send_type = fill_bits(dcc, src_bitmap_out, copy.src_bitmap, item, src_allowed_lossy);
    fill_mask(dcc, mask_bitmap_out, copy.mask.bitmap, item);
    return src_send_type;
}
相关推荐
冰山一脚20136 个月前
libusb注意事项笔记
spice
冰山一脚20131 年前
spice VDAgent简介
spice