Skip to content

Commit 8ea2e86

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

File tree

3 files changed

+219
-20
lines changed

3 files changed

+219
-20
lines changed

virtio-gpu.c

Lines changed: 68 additions & 3 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 =
@@ -708,6 +733,14 @@ static void virtio_gpu_cmd_transfer_to_host_2d_handler(
708733
(req->r.height < res_2d->height) ? req->r.height : res_2d->height;
709734
void *img_data = (void *) res_2d->image;
710735

736+
/* The cursor resource is tightly packed and contiguous */
737+
if (width == CURSOR_WIDTH && height == CURSOR_HEIGHT) {
738+
memcpy(res_2d->image, res_2d->iovec[0].iov_base,
739+
sizeof(uint32_t) * width * height);
740+
/* FIXME: This is ugly */
741+
goto write_response;
742+
}
743+
711744
for (uint32_t h = 0; h < height; h++) {
712745
size_t src_offset = req->offset + stride * h;
713746
size_t dest_offset = (req->r.y + h) * stride + (req->r.x * bpp);
@@ -753,6 +786,8 @@ static void virtio_gpu_cmd_transfer_to_host_2d_handler(
753786
}
754787
}
755788

789+
/* FIXME: This is ugly */
790+
write_response:
756791
/* Write response */
757792
struct vgpu_ctrl_hdr *response =
758793
vgpu_mem_host_to_guest(vgpu, vq_desc[1].addr);
@@ -819,6 +854,27 @@ static void virtio_gpu_cmd_update_cursor_handler(virtio_gpu_state_t *vgpu,
819854
struct virtq_desc *vq_desc,
820855
uint32_t *plen)
821856
{
857+
/* Read request */
858+
struct virtio_gpu_update_cursor *cursor =
859+
vgpu_mem_host_to_guest(vgpu, vq_desc[0].addr);
860+
861+
/* Update cursor image */
862+
struct vgpu_resource_2d *res_2d =
863+
acquire_vgpu_resource_2d(cursor->resource_id);
864+
865+
if (res_2d != NULL) {
866+
window_lock(cursor->pos.scanout_id);
867+
cursor_update((struct gpu_resource *) res_2d, cursor->pos.scanout_id,
868+
cursor->pos.x, cursor->pos.y);
869+
window_unlock(cursor->pos.scanout_id);
870+
} else if (cursor->resource_id == 0) {
871+
window_lock(cursor->pos.scanout_id);
872+
cursor_clear(cursor->pos.scanout_id);
873+
window_unlock(cursor->pos.scanout_id);
874+
} else {
875+
fprintf(stderr, "Invalid resource ID %d.\n", cursor->resource_id);
876+
}
877+
822878
/* Write response */
823879
struct vgpu_ctrl_hdr *response =
824880
vgpu_mem_host_to_guest(vgpu, vq_desc[1].addr);
@@ -834,6 +890,15 @@ static void virtio_gpu_cmd_move_cursor_handler(virtio_gpu_state_t *vgpu,
834890
struct virtq_desc *vq_desc,
835891
uint32_t *plen)
836892
{
893+
/* Read request */
894+
struct virtio_gpu_update_cursor *cursor =
895+
vgpu_mem_host_to_guest(vgpu, vq_desc[0].addr);
896+
897+
/* Move cursor to new position */
898+
window_lock(cursor->pos.scanout_id);
899+
cursor_move(cursor->pos.scanout_id, cursor->pos.x, cursor->pos.y);
900+
window_unlock(cursor->pos.scanout_id);
901+
837902
/* Write response */
838903
struct vgpu_ctrl_hdr *response =
839904
vgpu_mem_host_to_guest(vgpu, vq_desc[1].addr);

window.c

Lines changed: 134 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -127,16 +127,27 @@ struct key_map_entry key_map[] = {
127127
};
128128

129129
struct display_info {
130+
/* Request type: primary or cursor */
131+
int render_type;
132+
133+
/* Primary plane */
130134
struct gpu_resource resource;
131-
uint32_t sdl_format;
135+
uint32_t primary_sdl_format;
136+
SDL_Texture *primary_texture;
137+
138+
/* Cursor plane */
139+
struct gpu_resource cursor;
140+
uint32_t cursor_sdl_format;
141+
uint32_t *cursor_img;
142+
SDL_Rect cursor_rect; /* Cursor size and position */
143+
SDL_Texture *cursor_texture;
144+
132145
SDL_mutex *img_mtx;
133146
SDL_cond *img_cond;
134147
SDL_Thread *win_thread;
135148
SDL_Thread *ev_thread;
136149
SDL_Window *window;
137150
SDL_Renderer *renderer;
138-
SDL_Surface *surface;
139-
SDL_Texture *texture;
140151
};
141152

142153
static struct display_info displays[VIRTIO_GPU_MAX_SCANOUTS];
@@ -203,6 +214,7 @@ static int window_thread(void *data)
203214
{
204215
struct display_info *display = (struct display_info *) data;
205216
struct gpu_resource *resource = &display->resource;
217+
struct gpu_resource *cursor = &display->cursor;
206218

207219
/* Create SDL window */
208220
display->window = SDL_CreateWindow("semu", SDL_WINDOWPOS_UNDEFINED,
@@ -232,24 +244,57 @@ static int window_thread(void *data)
232244
((struct display_info *) data)->ev_thread =
233245
SDL_CreateThread(event_thread, NULL, data);
234246

247+
SDL_Surface *surface;
248+
235249
while (1) {
250+
/* Mutex lock */
236251
SDL_LockMutex(display->img_mtx);
237252

238253
/* Wait until the image is arrived */
239254
while (SDL_CondWaitTimeout(display->img_cond, display->img_mtx,
240255
SDL_COND_TIMEOUT))
241256
;
242257

243-
/* Render image */
244-
display->surface = SDL_CreateRGBSurfaceWithFormatFrom(
245-
resource->image, resource->width, resource->height,
246-
resource->bits_per_pixel, resource->stride, display->sdl_format);
247-
display->texture =
248-
SDL_CreateTextureFromSurface(display->renderer, display->surface);
249-
SDL_RenderCopy(display->renderer, display->texture, NULL, NULL);
258+
if (display->render_type == RENDER_PRIMARY_PLANE) {
259+
/* Generate primary plane texture */
260+
surface = SDL_CreateRGBSurfaceWithFormatFrom(
261+
resource->image, resource->width, resource->height,
262+
resource->bits_per_pixel, resource->stride,
263+
display->primary_sdl_format);
264+
265+
SDL_DestroyTexture(display->primary_texture);
266+
display->primary_texture =
267+
SDL_CreateTextureFromSurface(display->renderer, surface);
268+
SDL_FreeSurface(surface);
269+
} else if (display->render_type == UPDATE_CURSOR_RESOURCE) {
270+
/* Generate cursor plane texture */
271+
surface = SDL_CreateRGBSurfaceWithFormatFrom(
272+
cursor->image, cursor->width, cursor->height, CURSOR_BPP,
273+
CURSOR_STRIDE, SDL_PIXELFORMAT_ARGB8888);
274+
275+
SDL_DestroyTexture(display->cursor_texture);
276+
display->cursor_texture =
277+
SDL_CreateTextureFromSurface(display->renderer, surface);
278+
SDL_FreeSurface(surface);
279+
} else if (display->render_type == CLEAR_CURSOR_RESOURCE) {
280+
SDL_DestroyTexture(display->cursor_texture);
281+
display->cursor_texture = NULL;
282+
}
283+
284+
/* Render primary and cursor planes */
285+
SDL_RenderClear(display->renderer);
286+
287+
if (display->primary_texture)
288+
SDL_RenderCopy(display->renderer, display->primary_texture, NULL,
289+
NULL);
290+
291+
if (display->cursor_texture)
292+
SDL_RenderCopy(display->renderer, display->cursor_texture, NULL,
293+
&display->cursor_rect);
294+
250295
SDL_RenderPresent(display->renderer);
251-
SDL_DestroyTexture(display->texture);
252296

297+
/* Mutex unlock */
253298
SDL_UnlockMutex(display->img_mtx);
254299
}
255300
}
@@ -314,15 +359,87 @@ static bool virtio_gpu_to_sdl_format(uint32_t virtio_gpu_format,
314359
}
315360
}
316361

362+
void cursor_clear(int scanout_id)
363+
{
364+
/* Reset cursor information */
365+
struct display_info *display = &displays[scanout_id];
366+
memset(&display->cursor_rect, 0, sizeof(SDL_Rect));
367+
display->cursor_sdl_format = 0;
368+
369+
/* Reset cursor resource */
370+
memset(&display->cursor, 0, sizeof(struct gpu_resource));
371+
free(display->cursor_img);
372+
display->cursor_img = NULL;
373+
display->cursor.image = NULL;
374+
375+
/* Trigger plane rendering */
376+
display->render_type = CLEAR_CURSOR_RESOURCE;
377+
SDL_CondSignal(display->img_cond);
378+
}
379+
380+
void cursor_update(struct gpu_resource *resource, int scanout_id, int x, int y)
381+
{
382+
/* Convert virtio-gpu resource format to SDL format */
383+
uint32_t sdl_format;
384+
bool legal_format = virtio_gpu_to_sdl_format(resource->format, &sdl_format);
385+
386+
if (!legal_format) {
387+
fprintf(stderr, "Invalid resource format.\n");
388+
return;
389+
}
390+
391+
/* Update cursor information */
392+
struct display_info *display = &displays[scanout_id];
393+
display->cursor_rect.x = x;
394+
display->cursor_rect.y = y;
395+
display->cursor_rect.w = resource->width;
396+
display->cursor_rect.h = resource->height;
397+
display->cursor_sdl_format = sdl_format;
398+
399+
/* Cursor resource update */
400+
memcpy(&display->cursor, resource, sizeof(struct gpu_resource));
401+
size_t pixels_size = sizeof(uint32_t) * resource->width * resource->height;
402+
free(display->cursor_img);
403+
display->cursor_img = malloc(pixels_size);
404+
display->cursor.image = display->cursor_img;
405+
memcpy(display->cursor_img, resource->image, pixels_size);
406+
407+
/* Trigger cursor rendering */
408+
display->render_type = UPDATE_CURSOR_RESOURCE;
409+
SDL_CondSignal(display->img_cond);
410+
}
411+
412+
void cursor_move(int scanout_id, int x, int y)
413+
{
414+
/* Update cursor position */
415+
struct display_info *display = &displays[scanout_id];
416+
display->cursor_rect.x = x;
417+
display->cursor_rect.y = y;
418+
419+
/* Trigger cursor rendering */
420+
display->render_type = MOVE_CURSOR_POSITION;
421+
SDL_CondSignal(display->img_cond);
422+
}
423+
317424
void window_render(struct gpu_resource *resource)
318425
{
319426
int id = resource->scanout_id;
427+
struct display_info *display = &displays[id];
428+
429+
/* Convert virtio-gpu resource format to SDL format */
430+
uint32_t sdl_format;
431+
bool legal_format = virtio_gpu_to_sdl_format(resource->format, &sdl_format);
432+
433+
if (!legal_format) {
434+
fprintf(stderr, "Invalid resource format.\n");
435+
return;
436+
}
320437

321-
/* Resource update */
322-
memcpy(&displays[id].resource, resource, sizeof(struct gpu_resource));
323-
bool legal_format =
324-
virtio_gpu_to_sdl_format(resource->format, &displays[id].sdl_format);
438+
/* Update primary plane resource */
439+
display->primary_sdl_format = sdl_format;
440+
memcpy(&display->resource, resource, sizeof(struct gpu_resource));
325441

326-
if (legal_format)
327-
SDL_CondSignal(displays[id].img_cond);
442+
/* Trigger primary plane rendering */
443+
displays[id].render_type = RENDER_PRIMARY_PLANE;
444+
SDL_CondSignal(display->img_cond);
328445
}

window.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,19 @@
11
#pragma once
22

3+
/* Cursor size is always 64*64 in VirtIO GPU */
4+
#define CURSOR_WIDTH 64
5+
#define CURSOR_HEIGHT 64
6+
7+
#define CURSOR_BPP 4 /* Bytes per pixel, using ARGB */
8+
#define CURSOR_STRIDE (CURSOR_WIDTH * CURSOR_BPP)
9+
10+
enum {
11+
RENDER_PRIMARY_PLANE,
12+
UPDATE_CURSOR_RESOURCE,
13+
CLEAR_CURSOR_RESOURCE,
14+
MOVE_CURSOR_POSITION,
15+
};
16+
317
#if SEMU_HAS(VIRTIOGPU)
418
/* Public interface to the vgpu_resource_2d structure */
519
struct gpu_resource {
@@ -15,6 +29,9 @@ struct gpu_resource {
1529
void window_init(void);
1630
void window_add(uint32_t width, uint32_t height);
1731
void window_render(struct gpu_resource *resource);
32+
void cursor_clear(int scanout_id);
33+
void cursor_update(struct gpu_resource *resource, int scanout_id, int x, int y);
34+
void cursor_move(int scanout_id, int x, int y);
1835
void window_lock(uint32_t id);
1936
void window_unlock(uint32_t id);
2037
#endif

0 commit comments

Comments
 (0)