Skip to content

Commit ba1e3c2

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

File tree

3 files changed

+206
-20
lines changed

3 files changed

+206
-20
lines changed

virtio-gpu.c

Lines changed: 58 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 =
@@ -819,6 +844,27 @@ static void virtio_gpu_cmd_update_cursor_handler(virtio_gpu_state_t *vgpu,
819844
struct virtq_desc *vq_desc,
820845
uint32_t *plen)
821846
{
847+
/* Read request */
848+
struct virtio_gpu_update_cursor *cursor =
849+
vgpu_mem_host_to_guest(vgpu, vq_desc[0].addr);
850+
851+
/* Update cursor image */
852+
struct vgpu_resource_2d *res_2d =
853+
acquire_vgpu_resource_2d(cursor->resource_id);
854+
855+
if (res_2d != NULL) {
856+
window_lock(cursor->pos.scanout_id);
857+
cursor_update((struct gpu_resource *) res_2d, cursor->pos.scanout_id,
858+
cursor->pos.x, cursor->pos.y);
859+
window_unlock(cursor->pos.scanout_id);
860+
} else if (cursor->resource_id == 0) {
861+
window_lock(cursor->pos.scanout_id);
862+
cursor_clear(cursor->pos.scanout_id);
863+
window_unlock(cursor->pos.scanout_id);
864+
} else {
865+
fprintf(stderr, "Invalid resource ID %d.\n", cursor->resource_id);
866+
}
867+
822868
/* Write response */
823869
struct vgpu_ctrl_hdr *response =
824870
vgpu_mem_host_to_guest(vgpu, vq_desc[1].addr);
@@ -834,6 +880,15 @@ static void virtio_gpu_cmd_move_cursor_handler(virtio_gpu_state_t *vgpu,
834880
struct virtq_desc *vq_desc,
835881
uint32_t *plen)
836882
{
883+
/* Read request */
884+
struct virtio_gpu_update_cursor *cursor =
885+
vgpu_mem_host_to_guest(vgpu, vq_desc[0].addr);
886+
887+
/* Move cursor to new position */
888+
window_lock(cursor->pos.scanout_id);
889+
cursor_move(cursor->pos.scanout_id, cursor->pos.x, cursor->pos.y);
890+
window_unlock(cursor->pos.scanout_id);
891+
837892
/* Write response */
838893
struct vgpu_ctrl_hdr *response =
839894
vgpu_mem_host_to_guest(vgpu, vq_desc[1].addr);

window.c

Lines changed: 138 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,
@@ -223,6 +235,9 @@ static int window_thread(void *data)
223235
exit(2);
224236
}
225237

238+
/* FIXME */
239+
SDL_SetRenderDrawBlendMode(display->renderer, SDL_BLENDMODE_BLEND);
240+
226241
/* Render the whole screen with black color */
227242
SDL_SetRenderDrawColor(display->renderer, 0, 0, 0, 255);
228243
SDL_RenderClear(display->renderer);
@@ -232,24 +247,58 @@ static int window_thread(void *data)
232247
((struct display_info *) data)->ev_thread =
233248
SDL_CreateThread(event_thread, NULL, data);
234249

250+
SDL_Surface *surface;
251+
235252
while (1) {
253+
/* Mutex lock */
236254
SDL_LockMutex(display->img_mtx);
237255

238256
/* Wait until the image is arrived */
239257
while (SDL_CondWaitTimeout(display->img_cond, display->img_mtx,
240258
SDL_COND_TIMEOUT))
241259
;
242260

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);
261+
if (display->render_type == RENDER_PRIMARY_PLANE) {
262+
/* Generate primary plane texture */
263+
surface = SDL_CreateRGBSurfaceWithFormatFrom(
264+
resource->image, resource->width, resource->height,
265+
resource->bits_per_pixel, resource->stride,
266+
display->primary_sdl_format);
267+
268+
SDL_DestroyTexture(display->primary_texture);
269+
display->primary_texture =
270+
SDL_CreateTextureFromSurface(display->renderer, surface);
271+
SDL_FreeSurface(surface);
272+
} else if (display->render_type == UPDATE_CURSOR_IMAGE) {
273+
/* Generate cursor plane texture */
274+
surface = SDL_CreateRGBSurfaceWithFormatFrom(
275+
cursor->image, cursor->width, cursor->height,
276+
cursor->bits_per_pixel, cursor->stride,
277+
/*display->cursor_sdl_format*/
278+
SDL_PIXELFORMAT_RGBA32 /* FIXME */);
279+
280+
SDL_DestroyTexture(display->cursor_texture);
281+
display->cursor_texture =
282+
SDL_CreateTextureFromSurface(display->renderer, surface);
283+
SDL_FreeSurface(surface);
284+
} else if (CLEAR_CURSOR) {
285+
SDL_DestroyTexture(display->cursor_texture);
286+
}
287+
288+
/* Render primary and cursor planes */
289+
SDL_RenderClear(display->renderer);
290+
291+
if (display->primary_texture)
292+
SDL_RenderCopy(display->renderer, display->primary_texture, NULL,
293+
NULL);
294+
295+
if (display->cursor_texture)
296+
SDL_RenderCopy(display->renderer, display->cursor_texture, NULL,
297+
&display->cursor_rect);
298+
250299
SDL_RenderPresent(display->renderer);
251-
SDL_DestroyTexture(display->texture);
252300

301+
/* Mutex unlock */
253302
SDL_UnlockMutex(display->img_mtx);
254303
}
255304
}
@@ -314,15 +363,87 @@ static bool virtio_gpu_to_sdl_format(uint32_t virtio_gpu_format,
314363
}
315364
}
316365

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

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);
442+
/* Update primary plane resource */
443+
display->primary_sdl_format = sdl_format;
444+
memcpy(&display->resource, resource, sizeof(struct gpu_resource));
325445

326-
if (legal_format)
327-
SDL_CondSignal(displays[id].img_cond);
446+
/* Trigger primary plane rendering */
447+
displays[id].render_type = RENDER_PRIMARY_PLANE;
448+
SDL_CondSignal(display->img_cond);
328449
}

window.h

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

3+
enum {
4+
RENDER_PRIMARY_PLANE,
5+
UPDATE_CURSOR_IMAGE,
6+
MOVE_CURSOR,
7+
CLEAR_CURSOR,
8+
};
9+
310
#if SEMU_HAS(VIRTIOGPU)
411
/* Public interface to the vgpu_resource_2d structure */
512
struct gpu_resource {
@@ -15,6 +22,9 @@ struct gpu_resource {
1522
void window_init(void);
1623
void window_add(uint32_t width, uint32_t height);
1724
void window_render(struct gpu_resource *resource);
25+
void cursor_clear(int scanout_id);
26+
void cursor_update(struct gpu_resource *resource, int scanout_id, int x, int y);
27+
void cursor_move(int scanout_id, int x, int y);
1828
void window_lock(uint32_t id);
1929
void window_unlock(uint32_t id);
2030
#endif

0 commit comments

Comments
 (0)