From f2cfa761ae9ceb79abc79835b2ffec8f6c92b93b Mon Sep 17 00:00:00 2001 From: "Sun, Peng" Date: Fri, 25 Feb 2022 22:04:04 +0800 Subject: [PATCH] dm: virtio-gpu: cursor surpport Hardware cursor emulation of virtio-gpu video adapter. Guest vm can show its own cursor in virtual display, not share the cursor with service vm. It also accelerated by SDL(OpenGL ES 2.0 backend) API with hardware acceleration based on the GPU hardware own by service vm. Tracked-On: #7210 Signed-off-by: Sun, Peng Reviewed-by: Zhao, yakui Acked-by: Wang, Yu1 --- devicemodel/hw/pci/virtio/virtio_gpu.c | 89 +++++++++++++++++++++++++- devicemodel/hw/vdisplay_sdl.c | 80 +++++++++++++++++++++++ devicemodel/include/vdisplay.h | 15 +++++ 3 files changed, 181 insertions(+), 3 deletions(-) diff --git a/devicemodel/hw/pci/virtio/virtio_gpu.c b/devicemodel/hw/pci/virtio/virtio_gpu.c index 56942456c..ff8a84f1f 100644 --- a/devicemodel/hw/pci/virtio/virtio_gpu.c +++ b/devicemodel/hw/pci/virtio/virtio_gpu.c @@ -250,6 +250,26 @@ struct virtio_gpu_resource_flush { uint32_t padding; }; +/* + * Command: VIRTIO_GPU_CMD_UPDATE_CURSOR + * Command: VIRTIO_GPU_CMD_MOVE_CURSOR + */ +struct virtio_gpu_cursor_pos { + uint32_t scanout_id; + uint32_t x; + uint32_t y; + uint32_t padding; +}; + +struct virtio_gpu_update_cursor { + struct virtio_gpu_ctrl_hdr hdr; + struct virtio_gpu_cursor_pos pos; + uint32_t resource_id; + uint32_t hot_x; + uint32_t hot_y; + uint32_t padding; +}; + /* * Per-device struct */ @@ -261,6 +281,7 @@ struct virtio_gpu { int vdpy_handle; LIST_HEAD(,virtio_gpu_resource_2d) r2d_list; struct vdpy_display_bh ctrl_bh; + struct vdpy_display_bh cursor_bh; }; struct virtio_gpu_command { @@ -866,16 +887,62 @@ virtio_gpu_notify_controlq(void *vdev, struct virtio_vq_info *vq) } static void -virtio_gpu_notify_cursorq(void *vdev, struct virtio_vq_info *vq) +virtio_gpu_cmd_update_cursor(struct virtio_gpu_command *cmd) { + struct virtio_gpu_update_cursor req; + struct virtio_gpu_resource_2d *r2d; + struct cursor cur; + struct virtio_gpu *gpu; + + gpu = cmd->gpu; + memcpy(&req, cmd->iov[0].iov_base, sizeof(req)); + if (req.resource_id > 0) { + r2d = virtio_gpu_find_resource_2d(cmd->gpu, req.resource_id); + if (r2d == NULL) { + pr_err("%s: Illegal resource id %d\n", __func__, + req.resource_id); + return; + } + cur.x = req.pos.x; + cur.y = req.pos.y; + cur.hot_x = req.hot_x; + cur.hot_y = req.hot_y; + cur.width = r2d->width; + cur.height = r2d->height; + pixman_image_ref(r2d->image); + cur.data = pixman_image_get_data(r2d->image); + vdpy_cursor_define(gpu->vdpy_handle, &cur); + pixman_image_unref(r2d->image); + } +} + +static void +virtio_gpu_cmd_move_cursor(struct virtio_gpu_command *cmd) +{ + struct virtio_gpu_update_cursor req; + struct virtio_gpu *gpu; + + gpu = cmd->gpu; + memcpy(&req, cmd->iov[0].iov_base, sizeof(req)); + vdpy_cursor_move(gpu->vdpy_handle, req.pos.x, req.pos.y); +} + +static void +virtio_gpu_cursor_bh(void *data) +{ + struct virtio_gpu *vdev; + struct virtio_vq_info *vq; struct virtio_gpu_command cmd; struct virtio_gpu_ctrl_hdr hdr; struct iovec iov[VIRTIO_GPU_MAXSEGS]; int n; uint16_t idx; + vq = (struct virtio_vq_info *)data; + vdev = (struct virtio_gpu *)(vq->base); cmd.gpu = vdev; cmd.iolen = 0; + while (vq_has_descs(vq)) { n = vq_getchain(vq, &idx, iov, VIRTIO_GPU_MAXSEGS, NULL); if (n < 0) { @@ -890,8 +957,13 @@ virtio_gpu_notify_cursorq(void *vdev, struct virtio_vq_info *vq) cmd.iov = iov; memcpy(&hdr, iov[0].iov_base, sizeof(hdr)); switch (hdr.type) { + case VIRTIO_GPU_CMD_UPDATE_CURSOR: + virtio_gpu_cmd_update_cursor(&cmd); + break; + case VIRTIO_GPU_CMD_MOVE_CURSOR: + virtio_gpu_cmd_move_cursor(&cmd); + break; default: - virtio_gpu_cmd_unspec(&cmd); break; } @@ -900,6 +972,15 @@ virtio_gpu_notify_cursorq(void *vdev, struct virtio_vq_info *vq) vq_endchains(vq, 1); /* Generate interrupt if appropriate. */ } +static void +virtio_gpu_notify_cursorq(void *vdev, struct virtio_vq_info *vq) +{ + struct virtio_gpu *gpu; + + gpu = (struct virtio_gpu *)vdev; + vdpy_submit_bh(gpu->vdpy_handle, &gpu->cursor_bh); +} + static int virtio_gpu_init(struct vmctx *ctx, struct pci_vdev *dev, char *opts) { @@ -957,9 +1038,11 @@ virtio_gpu_init(struct vmctx *ctx, struct pci_vdev *dev, char *opts) gpu->vq[VIRTIO_GPU_CURSORQ].qsize = VIRTIO_GPU_RINGSZ; gpu->vq[VIRTIO_GPU_CURSORQ].notify = virtio_gpu_notify_cursorq; - /* Initialize the ctrl bh_task */ + /* Initialize the ctrl/cursor bh_task */ gpu->ctrl_bh.task_cb = virtio_gpu_ctrl_bh; gpu->ctrl_bh.data = &gpu->vq[VIRTIO_GPU_CONTROLQ]; + gpu->cursor_bh.task_cb = virtio_gpu_cursor_bh; + gpu->cursor_bh.data = &gpu->vq[VIRTIO_GPU_CURSORQ]; /* prepare the config space */ gpu->cfg.events_read = 0; diff --git a/devicemodel/hw/vdisplay_sdl.c b/devicemodel/hw/vdisplay_sdl.c index 891aa4ceb..48beddc2c 100644 --- a/devicemodel/hw/vdisplay_sdl.c +++ b/devicemodel/hw/vdisplay_sdl.c @@ -54,6 +54,8 @@ static struct display { int guest_width, guest_height; int screen; struct surface surf; + struct cursor cur; + SDL_Texture *cursor_tex; /* Add one UI_timer(33ms) to render the buffers from guest_vm */ struct acrn_timer ui_timer; struct vdpy_display_bh ui_timer_bh; @@ -630,9 +632,20 @@ vdpy_surface_set(int handle, struct surface *surf) vdpy.dpy_img = src_img; } +void +vdpy_cursor_position_transformation(struct display *vdpy, SDL_Rect *rect) +{ + rect->x = (vdpy->cur.x * vdpy->width) / vdpy->guest_width; + rect->y = (vdpy->cur.y * vdpy->height) / vdpy->guest_height; + rect->w = (vdpy->cur.width * vdpy->width) / vdpy->guest_width; + rect->h = (vdpy->cur.height * vdpy->height) / vdpy->guest_height; +} + void vdpy_surface_update(int handle, struct surface *surf) { + SDL_Rect cursor_rect; + if (handle != vdpy.s.n_connect) { return; } @@ -648,18 +661,71 @@ vdpy_surface_update(int handle, struct surface *surf) SDL_RenderClear(vdpy.dpy_renderer); SDL_RenderCopy(vdpy.dpy_renderer, vdpy.dpy_texture, NULL, NULL); + + /* This should be handled after rendering the surface_texture. + * Otherwise it will be hidden + */ + if (vdpy.cursor_tex) { + vdpy_cursor_position_transformation(&vdpy, &cursor_rect); + SDL_RenderCopy(vdpy.dpy_renderer, vdpy.cursor_tex, + NULL, &cursor_rect); + } + SDL_RenderPresent(vdpy.dpy_renderer); /* update the rendering time */ clock_gettime(CLOCK_MONOTONIC, &vdpy.last_time); } +void +vdpy_cursor_define(int handle, struct cursor *cur) +{ + if (handle != vdpy.s.n_connect) { + return; + } + + if (cur->data == NULL) + return; + + if (vdpy.cursor_tex) + SDL_DestroyTexture(vdpy.cursor_tex); + + vdpy.cursor_tex = SDL_CreateTexture( + vdpy.dpy_renderer, + SDL_PIXELFORMAT_ARGB8888, + SDL_TEXTUREACCESS_STREAMING, + cur->width, cur->height); + if (vdpy.cursor_tex == NULL) { + pr_err("Failed to create sdl_cursor surface for %p.\n", cur); + return; + } + + SDL_SetTextureBlendMode(vdpy.cursor_tex, SDL_BLENDMODE_BLEND); + vdpy.cur = *cur; + SDL_UpdateTexture(vdpy.cursor_tex, NULL, cur->data, cur->width * 4); +} + +void +vdpy_cursor_move(int handle, uint32_t x, uint32_t y) +{ + if (handle != vdpy.s.n_connect) { + return; + } + + /* Only move the position of the cursor. The cursor_texture + * will be handled in surface_update + */ + vdpy.cur.x = x; + vdpy.cur.y = y; +} + static void vdpy_sdl_ui_refresh(void *data) { struct display *ui_vdpy; struct timespec cur_time; uint64_t elapsed_time; + SDL_Rect cursor_rect; ui_vdpy = (struct display *)data; @@ -678,6 +744,16 @@ vdpy_sdl_ui_refresh(void *data) SDL_RenderClear(ui_vdpy->dpy_renderer); SDL_RenderCopy(ui_vdpy->dpy_renderer, ui_vdpy->dpy_texture, NULL, NULL); + + /* This should be handled after rendering the surface_texture. + * Otherwise it will be hidden + */ + if (ui_vdpy->cursor_tex) { + vdpy_cursor_position_transformation(ui_vdpy, &cursor_rect); + SDL_RenderCopy(ui_vdpy->dpy_renderer, ui_vdpy->cursor_tex, + NULL, &cursor_rect); + } + SDL_RenderPresent(ui_vdpy->dpy_renderer); } @@ -817,6 +893,10 @@ vdpy_sdl_display_thread(void *data) SDL_DestroyTexture(vdpy.dpy_texture); vdpy.dpy_texture = NULL; } + if (vdpy.cursor_tex) { + SDL_DestroyTexture(vdpy.cursor_tex); + vdpy.cursor_tex = NULL; + } sdl_fail: if (vdpy.dpy_renderer) { diff --git a/devicemodel/include/vdisplay.h b/devicemodel/include/vdisplay.h index 9a14866b4..79448ef6f 100644 --- a/devicemodel/include/vdisplay.h +++ b/devicemodel/include/vdisplay.h @@ -66,6 +66,19 @@ struct surface { void *pixel; }; +struct cursor { + enum surface_type surf_type; + /* use pixman_format as the intermediate-format */ + pixman_format_code_t surf_format; + uint32_t x; + uint32_t y; + uint32_t hot_x; + uint32_t hot_y; + uint32_t width; + uint32_t height; + void *data; +}; + int vdpy_parse_cmd_option(const char *opts); void gfx_ui_init(); int vdpy_init(); @@ -74,6 +87,8 @@ void vdpy_surface_set(int handle, struct surface *surf); void vdpy_surface_update(int handle, struct surface *surf); bool vdpy_submit_bh(int handle, struct vdpy_display_bh *bh); void vdpy_get_edid(int handle, uint8_t *edid, size_t size); +void vdpy_cursor_define(int handle, struct cursor *cur); +void vdpy_cursor_move(int handle, uint32_t x, uint32_t y); int vdpy_deinit(int handle); void gfx_ui_deinit();