From bb98469f47b09324561970e91c49a58ab038a176 Mon Sep 17 00:00:00 2001 From: Shengwen Cheng Date: Sun, 8 Sep 2024 23:56:34 +0800 Subject: [PATCH] [WIP] Support VirGL Follow the installation guide from: https://gitlab.freedesktop.org/virgl/virglrenderer Prerequisite: `sudo apt install libepoxy-dev` --- Makefile | 9 ++ device.h | 2 + feature.h | 5 + main.c | 2 + virtio-gpu.c | 326 ++++++++++++++++++++++++++++++++++++++++++++++++++- virtio.h | 12 ++ 6 files changed, 355 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index c1bdee5..c8f2622 100644 --- a/Makefile +++ b/Makefile @@ -60,6 +60,15 @@ ifneq ($(UNAME_S),Linux) ENABLE_VIRTIOGPU := 0 endif +# VirGL +ENABLE_VIRGL ?= 1 +ifneq (ENABLE_VIRTIOGPU,0) + CFLAGS += $(shell pkg-config virglrenderer --cflags) + LDFLAGS += $(shell pkg-config virglrenderer --libs) +endif + +$(call set-feature, VIRGL) + # SDL2 ENABLE_SDL ?= 1 ifeq (, $(shell which sdl2-config)) diff --git a/device.h b/device.h index 724e60e..7ef4884 100644 --- a/device.h +++ b/device.h @@ -219,6 +219,8 @@ void virtio_gpu_write(hart_t *vm, uint8_t width, uint32_t value); +void semu_virgl_init(void); + void virtio_gpu_init(virtio_gpu_state_t *vgpu); void virtio_gpu_add_scanout(virtio_gpu_state_t *vgpu, uint32_t width, diff --git a/feature.h b/feature.h index 0405753..64e3457 100644 --- a/feature.h +++ b/feature.h @@ -17,6 +17,11 @@ #define SEMU_FEATURE_VIRTIOGPU 1 #endif +/* VirGL */ +#ifndef SEMU_FEATURE_VIRGL +#define SEMU_FEATURE_VIRGL 1 +#endif + /* virtio-input */ #ifndef SEMU_FEATURE_VIRTIOINPUT #define SEMU_FEATURE_VIRTIOINPUT 1 diff --git a/main.c b/main.c index 55e4603..f5d0854 100644 --- a/main.c +++ b/main.c @@ -660,6 +660,8 @@ static int semu_start(int argc, char **argv) virtio_input_init(&(emu.vmouse)); #endif #if SEMU_HAS(VIRTIOGPU) + semu_virgl_init(); + emu.vgpu.ram = emu.ram; virtio_gpu_init(&(emu.vgpu)); virtio_gpu_add_scanout(&(emu.vgpu), 1024, 768); diff --git a/virtio-gpu.c b/virtio-gpu.c index 40b11be..cfb9f63 100644 --- a/virtio-gpu.c +++ b/virtio-gpu.c @@ -9,6 +9,7 @@ #include #include #include +#include #include "common.h" #include "device.h" @@ -21,6 +22,7 @@ #define VIRTIO_F_VERSION_1 1 #define VIRTIO_GPU_EVENT_DISPLAY (1 << 0) +#define VIRTIO_GPU_F_VIRGL (1 << 0) #define VIRTIO_GPU_F_EDID (1 << 1) #define VIRTIO_GPU_FLAG_FENCE (1 << 0) @@ -154,6 +156,72 @@ PACKED(struct vgpu_resp_capset_info { uint32_t padding; }); +PACKED(struct virtio_gpu_ctx_create { + struct vgpu_ctrl_hdr hdr; + uint32_t nlen; + uint32_t context_init; + char debug_name[64]; +}); + +/* clang-format off */ +PACKED(struct virtio_gpu_ctx_destroy { + struct vgpu_ctrl_hdr hdr; +}); +/* clang-format on */ + +PACKED(struct virtio_gpu_resource_create_3d { + struct vgpu_ctrl_hdr hdr; + uint32_t resource_id; + uint32_t target; + uint32_t format; + uint32_t bind; + uint32_t width; + uint32_t height; + uint32_t depth; + uint32_t array_size; + uint32_t last_level; + uint32_t nr_samples; + uint32_t flags; + uint32_t padding; +}); + +PACKED(struct virtio_gpu_ctx_resource { + struct vgpu_ctrl_hdr hdr; + uint32_t resource_id; + uint32_t padding; +}); + +PACKED(struct virtio_gpu_box { + uint32_t x; + uint32_t y; + uint32_t z; + uint32_t w; + uint32_t h; + uint32_t d; +}); + +PACKED(struct virtio_gpu_transfer_host_3d { + struct vgpu_ctrl_hdr hdr; + struct virtio_gpu_box box; + uint64_t offset; + uint32_t resource_id; + uint32_t level; + uint32_t stride; + uint32_t layer_stride; +}); + +PACKED(struct virtio_gpu_cmd_submit { + struct vgpu_ctrl_hdr hdr; + uint32_t size; + uint32_t num_in_fences; +}); + +PACKED(struct virtio_gpu_resp_map_info { + struct vgpu_ctrl_hdr hdr; + uint32_t map_info; + uint32_t padding; +}); + static struct vgpu_config vgpu_configs; static LIST_HEAD(vgpu_res_2d_list); @@ -737,6 +805,210 @@ static void virtio_gpu_cmd_move_cursor_handler(virtio_gpu_state_t *vgpu, *plen = sizeof(*response); } +static void virtio_gpu_cmd_ctx_create_handler(virtio_gpu_state_t *vgpu, + struct virtq_desc *vq_desc, + uint32_t *plen) +{ + /* Read request */ + struct virtio_gpu_ctx_create *request = + vgpu_mem_host_to_guest(vgpu, vq_desc[0].addr); + + /* Create 3D context with VirGL */ + virgl_renderer_context_create(request->hdr.ctx_id, request->nlen, + request->debug_name); + + /* Write response */ + struct vgpu_ctrl_hdr *response = + vgpu_mem_host_to_guest(vgpu, vq_desc[1].addr); + + memset(response, 0, sizeof(*response)); + response->type = VIRTIO_GPU_RESP_OK_NODATA; + + /* Return write length */ + *plen = sizeof(*response); +} + +static void virtio_gpu_cmd_ctx_destroy_handler(virtio_gpu_state_t *vgpu, + struct virtq_desc *vq_desc, + uint32_t *plen) +{ + /* Read request */ + struct virtio_gpu_ctx_destroy *request = + vgpu_mem_host_to_guest(vgpu, vq_desc[0].addr); + + /* Destroy 3D context with VirGL */ + virgl_renderer_context_destroy(request->hdr.ctx_id); + + /* Write response */ + struct vgpu_ctrl_hdr *response = + vgpu_mem_host_to_guest(vgpu, vq_desc[1].addr); + + memset(response, 0, sizeof(*response)); + response->type = VIRTIO_GPU_RESP_OK_NODATA; + + /* Return write length */ + *plen = sizeof(*response); +} + +static void virtio_gpu_cmd_attach_resource_handler(virtio_gpu_state_t *vgpu, + struct virtq_desc *vq_desc, + uint32_t *plen) +{ + /* Read request */ + struct virtio_gpu_ctx_resource *request = + vgpu_mem_host_to_guest(vgpu, vq_desc[0].addr); + + /* Attach 3D context to resource with VirGL */ + virgl_renderer_ctx_attach_resource(request->hdr.ctx_id, + request->resource_id); + + /* Write response */ + struct vgpu_ctrl_hdr *response = + vgpu_mem_host_to_guest(vgpu, vq_desc[1].addr); + + memset(response, 0, sizeof(*response)); + response->type = VIRTIO_GPU_RESP_OK_NODATA; + + /* Return write length */ + *plen = sizeof(*response); +} + +static void virtio_gpu_cmd_detach_resource_handler(virtio_gpu_state_t *vgpu, + struct virtq_desc *vq_desc, + uint32_t *plen) +{ + /* Read request */ + struct virtio_gpu_ctx_resource *request = + vgpu_mem_host_to_guest(vgpu, vq_desc[0].addr); + + /* Detach 3D context and resource with VirGL */ + virgl_renderer_ctx_detach_resource(request->hdr.ctx_id, + request->resource_id); + + /* Write response */ + struct vgpu_ctrl_hdr *response = + vgpu_mem_host_to_guest(vgpu, vq_desc[1].addr); + + memset(response, 0, sizeof(*response)); + response->type = VIRTIO_GPU_RESP_OK_NODATA; + + /* Return write length */ + *plen = sizeof(*response); +} + +static void virtio_gpu_cmd_resource_create_3d_handler( + virtio_gpu_state_t *vgpu, + struct virtq_desc *vq_desc, + uint32_t *plen) +{ + /* Read request */ + struct virtio_gpu_resource_create_3d *request = + vgpu_mem_host_to_guest(vgpu, vq_desc[0].addr); + + /* Create 3D resource with VirGL */ + struct virgl_renderer_resource_create_args args = { + .handle = request->resource_id, + .target = request->target, + .format = request->format, + .bind = request->bind, + .width = request->width, + .height = request->height, + .depth = request->depth, + .array_size = request->array_size, + .last_level = request->last_level, + .nr_samples = request->nr_samples, + .flags = request->flags, + }; + virgl_renderer_resource_create(&args, NULL, 0); + + /* Write response */ + struct vgpu_ctrl_hdr *response = + vgpu_mem_host_to_guest(vgpu, vq_desc[1].addr); + + memset(response, 0, sizeof(*response)); + response->type = VIRTIO_GPU_RESP_OK_NODATA; + + /* Return write length */ + *plen = sizeof(*response); +} + +static void virtio_gpu_cmd_transfer_to_host_3d_handler( + virtio_gpu_state_t *vgpu, + struct virtq_desc *vq_desc, + uint32_t *plen) +{ + /* Read request */ + struct virtio_gpu_transfer_host_3d *req = + vgpu_mem_host_to_guest(vgpu, vq_desc[0].addr); + + /* Transfer data from target to host with VirGL */ + virgl_renderer_transfer_read_iov(req->resource_id, req->hdr.ctx_id, + req->level, req->stride, req->layer_stride, + (struct virgl_box *) &req->box, + req->offset, NULL, 0); + + /* Write response */ + struct vgpu_ctrl_hdr *response = + vgpu_mem_host_to_guest(vgpu, vq_desc[1].addr); + + struct vgpu_ctrl_hdr res_no_data = {.type = VIRTIO_GPU_RESP_OK_NODATA}; + memcpy(response, &res_no_data, sizeof(struct vgpu_ctrl_hdr)); + + /* Update write length */ + *plen = sizeof(*response); +} + +static void virtio_gpu_cmd_transfer_from_host_3d_handler( + virtio_gpu_state_t *vgpu, + struct virtq_desc *vq_desc, + uint32_t *plen) +{ + /* Read request */ + struct virtio_gpu_transfer_host_3d *req = + vgpu_mem_host_to_guest(vgpu, vq_desc[0].addr); + + /* Transfer data from host to target with VirGL */ + virgl_renderer_transfer_write_iov( + req->resource_id, req->hdr.ctx_id, req->level, req->stride, + req->layer_stride, (struct virgl_box *) &req->box, req->offset, NULL, + 0); + + /* Write response */ + struct vgpu_ctrl_hdr *response = + vgpu_mem_host_to_guest(vgpu, vq_desc[1].addr); + + struct vgpu_ctrl_hdr res_no_data = {.type = VIRTIO_GPU_RESP_OK_NODATA}; + memcpy(response, &res_no_data, sizeof(struct vgpu_ctrl_hdr)); + + /* Update write length */ + *plen = sizeof(*response); +} + +static void virtio_gpu_cmd_submit_3d_handler(virtio_gpu_state_t *vgpu, + struct virtq_desc *vq_desc, + uint32_t *plen) +{ + /* Read request */ + struct virtio_gpu_cmd_submit *req = + vgpu_mem_host_to_guest(vgpu, vq_desc[0].addr); + + /* Submit 3D command with VirGL */ + // TODO + void *buffer = NULL; + int ndw = 0; + virgl_renderer_submit_cmd(buffer, req->hdr.ctx_id, ndw); + + /* Write response */ + struct vgpu_ctrl_hdr *response = + vgpu_mem_host_to_guest(vgpu, vq_desc[1].addr); + + struct vgpu_ctrl_hdr res_no_data = {.type = VIRTIO_GPU_RESP_OK_NODATA}; + memcpy(response, &res_no_data, sizeof(struct vgpu_ctrl_hdr)); + + /* Update write length */ + *plen = sizeof(*response); +} + static int virtio_gpu_desc_handler(virtio_gpu_state_t *vgpu, const virtio_gpu_queue_t *queue, uint32_t desc_idx, @@ -800,6 +1072,38 @@ static int virtio_gpu_desc_handler(virtio_gpu_state_t *vgpu, case VIRTIO_GPU_CMD_MOVE_CURSOR: virtio_gpu_cmd_move_cursor_handler(vgpu, vq_desc, plen); break; + case VIRTIO_GPU_CMD_CTX_CREATE: + printf("@@@VIRTIO_GPU_CMD_CTX_CREATE\n"); + virtio_gpu_cmd_ctx_create_handler(vgpu, vq_desc, plen); + break; + case VIRTIO_GPU_CMD_CTX_DESTROY: + printf("@@@VIRTIO_GPU_CMD_CTX_DESTROY\n"); + virtio_gpu_cmd_ctx_destroy_handler(vgpu, vq_desc, plen); + break; + case VIRTIO_GPU_CMD_CTX_ATTACH_RESOURCE: + printf("@@@VIRTIO_GPU_CMD_CTX_ATTACH_RESOURCE\n"); + virtio_gpu_cmd_attach_resource_handler(vgpu, vq_desc, plen); + break; + case VIRTIO_GPU_CMD_CTX_DETACH_RESOURCE: + printf("@@@VIRTIO_GPU_CMD_CTX_DETACH_RESOURCE\n"); + virtio_gpu_cmd_detach_resource_handler(vgpu, vq_desc, plen); + break; + case VIRTIO_GPU_CMD_RESOURCE_CREATE_3D: + printf("@@@VIRTIO_GPU_CMD_RESOURCE_CREATE_3D\n"); + virtio_gpu_cmd_resource_create_3d_handler(vgpu, vq_desc, plen); + break; + case VIRTIO_GPU_CMD_TRANSFER_TO_HOST_3D: + printf("@@@VIRTIO_GPU_CMD_TRANSFER_TO_HOST_3D\n"); + virtio_gpu_cmd_transfer_to_host_3d_handler(vgpu, vq_desc, plen); + break; + case VIRTIO_GPU_CMD_TRANSFER_FROM_HOST_3D: + printf("@@@VIRTIO_GPU_CMD_TRANSFER_FROM_HOST_3D\n"); + virtio_gpu_cmd_transfer_from_host_3d_handler(vgpu, vq_desc, plen); + break; + case VIRTIO_GPU_CMD_SUBMIT_3D: + printf("@@@VIRTIO_GPU_CMD_SUBMIT_3D\n"); + virtio_gpu_cmd_submit_3d_handler(vgpu, vq_desc, plen); + break; default: fprintf(stderr, "%s(): unknown command %d\n", __func__, header->type); virtio_gpu_set_fail(vgpu); @@ -894,6 +1198,9 @@ static bool virtio_gpu_reg_read(virtio_gpu_state_t *vgpu, *value = VIRTIO_F_VERSION_1; } else { /* [31:0] */ *value = VIRTIO_GPU_F_EDID; +#if SEMU_HAS(VIRGL) + *value |= VIRTIO_GPU_F_VIRGL; +#endif } return true; case _(QueueNumMax): @@ -939,7 +1246,7 @@ static bool virtio_gpu_reg_read(virtio_gpu_state_t *vgpu, return true; } case offsetof(struct vgpu_config, num_capsets): { - *value = 0; /* TODO: Add at least one capset to support VirGl */ + *value = 1; /* TODO: Add at least one capset to support VirGl */ return true; } default: @@ -1085,6 +1392,23 @@ void virtio_gpu_init(virtio_gpu_state_t *vgpu) calloc(sizeof(struct vgpu_scanout_info), VIRTIO_GPU_MAX_SCANOUTS); } +void semu_virgl_init(void) +{ + struct virgl_renderer_callbacks cb; + memset(&cb, 0, sizeof(cb)); + cb.version = VIRGL_RENDERER_CALLBACKS_VERSION; + + int flags = VIRGL_RENDERER_USE_EGL | VIRGL_RENDERER_THREAD_SYNC; + static int cookie; // FIXME: This is for passing user-defined data + + int ret = virgl_renderer_init(&cookie, flags, &cb); + if (ret) { + fprintf(stderr, "failed to initialize virgl renderer: %s\n", + strerror(ret)); + exit(2); + } +} + void virtio_gpu_add_scanout(virtio_gpu_state_t *vgpu, uint32_t width, uint32_t height) diff --git a/virtio.h b/virtio.h index d9947b5..b23b414 100644 --- a/virtio.h +++ b/virtio.h @@ -87,6 +87,18 @@ enum virtio_gpu_ctrl_type { VIRTIO_GPU_CMD_GET_CAPSET, VIRTIO_GPU_CMD_GET_EDID, + /* 3d commands */ + VIRTIO_GPU_CMD_CTX_CREATE = 0x0200, + VIRTIO_GPU_CMD_CTX_DESTROY, + VIRTIO_GPU_CMD_CTX_ATTACH_RESOURCE, + VIRTIO_GPU_CMD_CTX_DETACH_RESOURCE, + VIRTIO_GPU_CMD_RESOURCE_CREATE_3D, + VIRTIO_GPU_CMD_TRANSFER_TO_HOST_3D, + VIRTIO_GPU_CMD_TRANSFER_FROM_HOST_3D, + VIRTIO_GPU_CMD_SUBMIT_3D, + VIRTIO_GPU_CMD_RESOURCE_MAP_BLOB, + VIRTIO_GPU_CMD_RESOURCE_UNMAP_BLOB, + /* cursor commands */ VIRTIO_GPU_CMD_UPDATE_CURSOR = 0x0300, VIRTIO_GPU_CMD_MOVE_CURSOR,