Skip to content

Commit c7140de

Browse files
committed
[WIP] Implement cursor plane rendering for VirtIO GPU
1 parent 7e2c711 commit c7140de

File tree

3 files changed

+259
-49
lines changed

3 files changed

+259
-49
lines changed

virtio-gpu.c

Lines changed: 108 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ struct vgpu_resource_2d {
4949
uint32_t stride;
5050
uint32_t bits_per_pixel;
5151
uint32_t *image;
52+
bool scanout_attached;
5253
/* Private: */
5354
uint32_t resource_id;
5455
size_t page_cnt;
@@ -169,6 +170,22 @@ PACKED(struct virtio_gpu_ctx_create {
169170
char debug_name[64];
170171
});
171172

173+
PACKED(struct virtio_gpu_cursor_pos {
174+
uint32_t scanout_id;
175+
uint32_t x;
176+
uint32_t y;
177+
uint32_t padding;
178+
});
179+
180+
PACKED(struct virtio_gpu_update_cursor {
181+
struct vgpu_ctrl_hdr hdr;
182+
struct virtio_gpu_cursor_pos pos;
183+
uint32_t resource_id;
184+
uint32_t hot_x;
185+
uint32_t hot_y;
186+
uint32_t padding;
187+
});
188+
172189
/* clang-format off */
173190
PACKED(struct virtio_gpu_ctx_destroy {
174191
struct vgpu_ctrl_hdr hdr;
@@ -244,6 +261,7 @@ static struct vgpu_resource_2d *create_vgpu_resource_2d(int resource_id)
244261
return NULL;
245262

246263
res->resource_id = resource_id;
264+
res->scanout_attached = false;
247265
list_add(&res->list, &vgpu_res_2d_list);
248266
return res;
249267
}
@@ -267,15 +285,18 @@ static int destroy_vgpu_resource_2d(uint32_t resource_id)
267285
if (!res_2d)
268286
return -1;
269287

270-
window_lock(resource_id);
288+
int scanout_id = res_2d->scanout_id;
289+
if (res_2d->scanout_attached)
290+
window_lock(scanout_id);
271291

272292
/* Release the resource */
273293
free(res_2d->image);
274294
list_del(&res_2d->list);
275295
free(res_2d->iovec);
276296
free(res_2d);
277297

278-
window_unlock(resource_id);
298+
if (res_2d->scanout_attached)
299+
window_unlock(scanout_id);
279300

280301
return 0;
281302
}
@@ -629,9 +650,11 @@ static void virtio_gpu_cmd_set_scanout_handler(virtio_gpu_state_t *vgpu,
629650

630651
/* Linux's virtio-gpu driver may send scanout command
631652
* even if the resource does not exist */
632-
if (res_2d)
653+
if (res_2d) {
633654
/* Set scanout ID to proper 2D resource */
634655
res_2d->scanout_id = request->scanout_id;
656+
res_2d->scanout_attached = true;
657+
}
635658

636659
/* Write response */
637660
struct vgpu_ctrl_hdr *response =
@@ -657,7 +680,9 @@ static void virtio_gpu_cmd_resource_flush_handler(virtio_gpu_state_t *vgpu,
657680
acquire_vgpu_resource_2d(request->resource_id);
658681

659682
/* Trigger display window rendering */
683+
window_lock(res_2d->scanout_id);
660684
window_render((struct gpu_resource *) res_2d);
685+
window_unlock(res_2d->scanout_id);
661686

662687
/* Write response */
663688
struct vgpu_ctrl_hdr *response =
@@ -670,36 +695,9 @@ static void virtio_gpu_cmd_resource_flush_handler(virtio_gpu_state_t *vgpu,
670695
*plen = sizeof(*response);
671696
}
672697

673-
static void virtio_gpu_cmd_transfer_to_host_2d_handler(
674-
virtio_gpu_state_t *vgpu,
675-
struct virtq_desc *vq_desc,
676-
uint32_t *plen)
698+
static void virtio_gpu_copy_image_from_pages(struct vgpu_trans_to_host_2d *req,
699+
struct vgpu_resource_2d *res_2d)
677700
{
678-
/* Read request */
679-
struct vgpu_trans_to_host_2d *req =
680-
vgpu_mem_host_to_guest(vgpu, vq_desc[0].addr);
681-
682-
/* Acquire 2D resource */
683-
struct vgpu_resource_2d *res_2d =
684-
acquire_vgpu_resource_2d(req->resource_id);
685-
686-
if (!res_2d) {
687-
fprintf(stderr, "%s(): Failed to find 2D resource\n", __func__);
688-
virtio_gpu_set_fail(vgpu);
689-
return;
690-
}
691-
692-
/* Check image boundary */
693-
if (req->r.x > res_2d->width || req->r.y > res_2d->height ||
694-
req->r.width > res_2d->width || req->r.height > res_2d->height ||
695-
req->r.x + req->r.width > res_2d->width ||
696-
req->r.y + req->r.height > res_2d->height) {
697-
fprintf(stderr, "%s(): Invalid image size\n", __func__);
698-
virtio_gpu_set_fail(vgpu);
699-
return;
700-
}
701-
702-
/* Transfer frame data from guest to host */
703701
uint32_t stride = res_2d->stride;
704702
uint32_t bpp = res_2d->bits_per_pixel / 8; /* Bytes per pixel */
705703
uint32_t width =
@@ -752,6 +750,54 @@ static void virtio_gpu_cmd_transfer_to_host_2d_handler(
752750
}
753751
}
754752
}
753+
}
754+
755+
static void virtio_gpu_cursor_image_copy(struct vgpu_resource_2d *res_2d)
756+
{
757+
/* The cursor resource is tightly packed and contiguous */
758+
memcpy(res_2d->image, res_2d->iovec[0].iov_base,
759+
sizeof(uint32_t) * CURSOR_WIDTH * CURSOR_HEIGHT);
760+
}
761+
762+
static void virtio_gpu_cmd_transfer_to_host_2d_handler(
763+
virtio_gpu_state_t *vgpu,
764+
struct virtq_desc *vq_desc,
765+
uint32_t *plen)
766+
{
767+
/* Read request */
768+
struct vgpu_trans_to_host_2d *req =
769+
vgpu_mem_host_to_guest(vgpu, vq_desc[0].addr);
770+
771+
/* Acquire 2D resource */
772+
struct vgpu_resource_2d *res_2d =
773+
acquire_vgpu_resource_2d(req->resource_id);
774+
775+
if (!res_2d) {
776+
fprintf(stderr, "%s(): Failed to find 2D resource\n", __func__);
777+
virtio_gpu_set_fail(vgpu);
778+
return;
779+
}
780+
781+
/* Check image boundary */
782+
if (req->r.x > res_2d->width || req->r.y > res_2d->height ||
783+
req->r.width > res_2d->width || req->r.height > res_2d->height ||
784+
req->r.x + req->r.width > res_2d->width ||
785+
req->r.y + req->r.height > res_2d->height) {
786+
fprintf(stderr, "%s(): Invalid image size\n", __func__);
787+
virtio_gpu_set_fail(vgpu);
788+
return;
789+
}
790+
791+
uint32_t width =
792+
(req->r.width < res_2d->width) ? req->r.width : res_2d->width;
793+
uint32_t height =
794+
(req->r.height < res_2d->height) ? req->r.height : res_2d->height;
795+
796+
/* Transfer frame data from guest to host */
797+
if (width == CURSOR_WIDTH && height == CURSOR_HEIGHT)
798+
virtio_gpu_cursor_image_copy(res_2d);
799+
else
800+
virtio_gpu_copy_image_from_pages(req, res_2d);
755801

756802
/* Write response */
757803
struct vgpu_ctrl_hdr *response =
@@ -819,6 +865,27 @@ static void virtio_gpu_cmd_update_cursor_handler(virtio_gpu_state_t *vgpu,
819865
struct virtq_desc *vq_desc,
820866
uint32_t *plen)
821867
{
868+
/* Read request */
869+
struct virtio_gpu_update_cursor *cursor =
870+
vgpu_mem_host_to_guest(vgpu, vq_desc[0].addr);
871+
872+
/* Update cursor image */
873+
struct vgpu_resource_2d *res_2d =
874+
acquire_vgpu_resource_2d(cursor->resource_id);
875+
876+
if (res_2d != NULL) {
877+
window_lock(cursor->pos.scanout_id);
878+
cursor_update((struct gpu_resource *) res_2d, cursor->pos.scanout_id,
879+
cursor->pos.x, cursor->pos.y);
880+
window_unlock(cursor->pos.scanout_id);
881+
} else if (cursor->resource_id == 0) {
882+
window_lock(cursor->pos.scanout_id);
883+
cursor_clear(cursor->pos.scanout_id);
884+
window_unlock(cursor->pos.scanout_id);
885+
} else {
886+
fprintf(stderr, "Invalid resource ID %d.\n", cursor->resource_id);
887+
}
888+
822889
/* Write response */
823890
struct vgpu_ctrl_hdr *response =
824891
vgpu_mem_host_to_guest(vgpu, vq_desc[1].addr);
@@ -834,6 +901,15 @@ static void virtio_gpu_cmd_move_cursor_handler(virtio_gpu_state_t *vgpu,
834901
struct virtq_desc *vq_desc,
835902
uint32_t *plen)
836903
{
904+
/* Read request */
905+
struct virtio_gpu_update_cursor *cursor =
906+
vgpu_mem_host_to_guest(vgpu, vq_desc[0].addr);
907+
908+
/* Move cursor to new position */
909+
window_lock(cursor->pos.scanout_id);
910+
cursor_move(cursor->pos.scanout_id, cursor->pos.x, cursor->pos.y);
911+
window_unlock(cursor->pos.scanout_id);
912+
837913
/* Write response */
838914
struct vgpu_ctrl_hdr *response =
839915
vgpu_mem_host_to_guest(vgpu, vq_desc[1].addr);

0 commit comments

Comments
 (0)