Skip to content

Commit 3195a6e

Browse files
committed
Apply bidirectional queue design to the input system
This commit splits the input system into two parts, event and submission, and introduces two system calls, setup_queue and submit, the former is used to assist in the creation of queues, which is part of the communication bridge between the user application code and the emulator, and the latter is used to notify the emulator that a submission (or a command) should be processed. The design of these system calls is heavily based on Linux io_uring interface.
1 parent 09f4e65 commit 3195a6e

File tree

3 files changed

+110
-62
lines changed

3 files changed

+110
-62
lines changed

docs/syscall.md

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -27,15 +27,32 @@ These system calls are solely for the convenience of accessing the [SDL library]
2727

2828
If a window does not already exist, one will be created with the specified `width` and `height`. The `screen` buffer will replace the content of the framebuffer, passing a different `width` or `height` compared to the size of the window is undefined behavior. This system call additionally polls events from the SDL library, and, if necessary, update the internal input specific event queue.
2929

30-
### `poll_event(event)` - Poll a input specific event from SDL
30+
### `setup_queue` - Setup input system's dedicated event and submission queue
3131

3232
**system call number**: `0xC0DE`
3333

34-
**synopsis**: `int poll_event(void *event)`
34+
**synopsis**: `void *setup_queue(int capacity, unsigned int* event_count)`
3535

36-
`event` will be filled with the polled event data, it should have a 32-bit `type` field, and associated with an appropriately sized value buffer. The internal event queue will be updated everytime `draw_frame` is called.
36+
Allocate a continuous memory chunk that has the requested capacity and two closely packed queues, the event queue and the submission queue. The base address of the event queue is located in the returned address, and the submission queue is immediately after the event queue's last element, which is the event queue's base address plus the size of each event element times the given capacity. The capacity must be a power of 2; if it is not, it will be rounded up to the next highest power of 2. It is crucial to initialize the event counter variable before supplying its address to this system call because it serves as an interaction to the user that an event has been added to the event queue.
3737

38-
Currently accepted event types include the following:
39-
* `KEY_EVENT` - `0x0`: Triggered when the status of keyboard changes, either when a key is pressed or released, and it returns a 32-bit keycode and a 8-bit key state, the values of the hexadecimal keycodes are listed in [SDL Keycode Lookup Table](https://wiki.libsdl.org/SDLKeycodeLookup)
40-
* `MOUSE_MOTION_EVENT` - `0x1`: A mouse move event, with relative position information, two 32-bit signed integers that correspond to the x and y delta value, the mouse is continually wrapped in the window border by default.
41-
* `MOUSE_BUTTON_EVENT` - `0x2`: the user code receives this event whenever the state of a mouse button changes, whether a button is pressed or released, and it returns a 8-bit value that indicates which button is updated(1 is left, 2 is middle, 3 is right and so on), as well as a 8-bit state value that indicates whether the button is pressed.
38+
#### Events
39+
40+
An event entry is made up of a 32-bit value representing the event's type and a `union` buffer containing t1he event's parameters.
41+
42+
* `KEY_EVENT`: Either a key is pressed or released. Its value buffer is made up of a 32-bit universal key code and an 8-bit state flag; if the corresponding character of the pressed key is not printable, the bit right after the most significant bit is set; for example, the "a" key's correspoding character is printable, so its keycode is the ASCII code of the "a" character, which is `0x61`. However, because the left shift key doesn't have a corresponding printable key, its hexadecimal value is `0x400000E1`, with the 31 bit is set.
43+
* `MOUSE_MOTION_EVENT`: The cursor is moved during the current frame. This event contains two signed integer value, which is the delta of x position and y position respectively. If the relative mouse mode is enabled, the mouse movement will never be 0 because the cursor is wrapped within the canvas and is repeated whenever the cursor reaches the border.
44+
* `MOUSE_BUTTON_EVENT`: The state of a mouse button has been changed. Its value buffer contains a 8-bit button value(1 is left, 2 is middle, 3 is right and so on) and an 8-bit boolean flag that indicates whether the mouse button is pressed.
45+
46+
### `submit_queue` - Notify the emulator a submission has been pushed into the submission queue
47+
48+
**system call number**: `0xFEED`
49+
50+
**synopsis**: `void submit_queue(int count)`
51+
52+
To inform the emulator that a batch of submissions should be processed, the application code should push several submissions into the queue first, and then pass the size of the submissions batch to this system call; the submissions will be processed and executed sequentially and immediately.
53+
54+
#### Submissions
55+
56+
The submission entry is structured similarly to an event entry, with a 32-bit type field and an associated dynamic-sized value buffer whose width depends on the type of submission.
57+
58+
* `RELATIVE_MODE_SUBMISSION`: Enable or disable the mouse relative mode. If the mouse relative mode is enabled, the mouse cursor is wrapped within the window border, it's associated with an 8-bit wide boolean value that indicates whether the relative mouse mode should be enbled.

src/syscall.c

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,8 @@
3737
#if RV32_HAS(SDL)
3838
#define __SYSCALL_LIST_EXT \
3939
_(draw_frame, 0xBEEF) \
40-
_(poll_event, 0xC0DE)
40+
_(setup_queue, 0xC0DE) \
41+
_(submit_queue, 0xFEED)
4142
#else
4243
#define __SYSCALL_LIST_EXT
4344
#endif
@@ -316,7 +317,8 @@ static void syscall_open(struct riscv_t *rv)
316317

317318
#if RV32_HAS(SDL)
318319
extern void syscall_draw_frame(struct riscv_t *rv);
319-
extern void syscall_poll_event(struct riscv_t *rv);
320+
extern void syscall_setup_queue(struct riscv_t *rv);
321+
extern void syscall_submit_queue(struct riscv_t *rv);
320322
#endif
321323

322324
void syscall_handler(struct riscv_t *rv)

src/syscall_sdl.c

Lines changed: 82 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,6 @@
99

1010
#include "state.h"
1111

12-
/* For optimization, the capcity of event queues must be the power of two to
13-
* avoid the expensive modulo operation, the details are explained here:
14-
* https://stackoverflow.com/questions/10527581/why-must-a-ring-buffer-size-be-a-power-of-2
15-
*/
16-
#define EVENT_QUEUE_CAPACITY 128
17-
1812
enum {
1913
KEY_EVENT = 0,
2014
MOUSE_MOTION_EVENT = 1,
@@ -47,38 +41,59 @@ typedef struct {
4741
} event_t;
4842

4943
typedef struct {
50-
event_t events[EVENT_QUEUE_CAPACITY];
51-
size_t start, end;
52-
bool full;
44+
event_t *base;
45+
size_t end;
5346
} event_queue_t;
5447

48+
enum {
49+
RELATIVE_MODE_SUBMISSION = 0,
50+
};
51+
52+
typedef struct {
53+
uint32_t type;
54+
union {
55+
union {
56+
uint8_t enabled;
57+
} mouse;
58+
};
59+
} submission_t;
60+
61+
typedef struct {
62+
submission_t *base;
63+
size_t start;
64+
} submission_queue_t;
65+
5566
static SDL_Window *window = NULL;
5667
static SDL_Renderer *renderer;
5768
static SDL_Texture *texture;
69+
static uint32_t queues_capacity = 0;
70+
static uint32_t event_count;
5871
static event_queue_t event_queue = {
59-
.events = {},
60-
.start = 0,
72+
.base = NULL,
6173
.end = 0,
62-
.full = false,
74+
};
75+
static submission_queue_t submission_queue = {
76+
.base = NULL,
77+
.start = 0,
6378
};
6479

65-
static bool event_pop(event_t *event)
80+
static submission_t submission_pop(void)
6681
{
67-
if (event_queue.start == event_queue.end)
68-
return false;
69-
*event = event_queue.events[event_queue.start++];
70-
event_queue.start &= EVENT_QUEUE_CAPACITY - 1;
71-
event_queue.full = false;
72-
return true;
82+
submission_t submission = submission_queue.base[submission_queue.start++];
83+
submission_queue.start &= queues_capacity - 1;
84+
return submission;
7385
}
7486

75-
static void event_push(event_t event)
87+
static void event_push(struct riscv_t *rv, event_t event)
7688
{
77-
if (event_queue.full)
78-
return;
79-
event_queue.events[event_queue.end++] = event;
80-
event_queue.end &= EVENT_QUEUE_CAPACITY - 1;
81-
event_queue.full = (event_queue.start == event_queue.end);
89+
event_queue.base[event_queue.end++] = event;
90+
event_queue.end &= queues_capacity - 1;
91+
92+
state_t *s = rv_userdata(rv);
93+
uint32_t count;
94+
memory_read(s->mem, (void *) &count, event_count, sizeof(uint32_t));
95+
count += 1;
96+
memory_write(s->mem, event_count, (void *) &count, sizeof(uint32_t));
8297
}
8398

8499
/* check if we need to setup SDL and run event loop */
@@ -128,7 +143,7 @@ static bool check_sdl(struct riscv_t *rv, uint32_t width, uint32_t height)
128143
.state = (bool) (event.key.state == SDL_PRESSED),
129144
};
130145
memcpy(&new_event.key_event, &key_event, sizeof(key_event));
131-
event_push(new_event);
146+
event_push(rv, new_event);
132147
break;
133148
}
134149
case SDL_MOUSEMOTION: {
@@ -141,7 +156,7 @@ static bool check_sdl(struct riscv_t *rv, uint32_t width, uint32_t height)
141156
};
142157
memcpy(&new_event.mouse.motion, &mouse_motion,
143158
sizeof(mouse_motion));
144-
event_push(new_event);
159+
event_push(rv, new_event);
145160
break;
146161
}
147162
case SDL_MOUSEBUTTONDOWN:
@@ -161,7 +176,7 @@ static bool check_sdl(struct riscv_t *rv, uint32_t width, uint32_t height)
161176
};
162177
memcpy(&new_event.mouse.button, &mouse_button,
163178
sizeof(mouse_button));
164-
event_push(new_event);
179+
event_push(rv, new_event);
165180
break;
166181
}
167182
}
@@ -193,34 +208,48 @@ void syscall_draw_frame(struct riscv_t *rv)
193208
SDL_RenderPresent(renderer);
194209
}
195210

196-
void syscall_poll_event(struct riscv_t *rv)
211+
void syscall_setup_queue(struct riscv_t *rv)
197212
{
198-
state_t *s = rv_userdata(rv); /* access userdata */
199-
200-
/* poll_event(event) */
201-
const uint32_t base = rv_get_reg(rv, rv_reg_a0);
202-
203-
event_t event;
204-
if (!event_pop(&event)) {
213+
/* setup_queue(capacity, event_count) */
214+
queues_capacity = rv_get_reg(rv, rv_reg_a0);
215+
event_count = rv_get_reg(rv, rv_reg_a1);
216+
if (queues_capacity == 0) {
205217
rv_set_reg(rv, rv_reg_a0, 0);
206218
return;
207219
}
208220

209-
memory_write(s->mem, base + 0, (const uint8_t *) &event.type, 4);
210-
switch (event.type) {
211-
case KEY_EVENT:
212-
memory_write(s->mem, base + 4, (const uint8_t *) &event.key_event,
213-
sizeof(key_event_t));
214-
break;
215-
case MOUSE_MOTION_EVENT:
216-
memory_write(s->mem, base + 4, (const uint8_t *) &event.mouse.motion,
217-
sizeof(mouse_motion_t));
218-
break;
219-
case MOUSE_BUTTON_EVENT:
220-
memory_write(s->mem, base + 4, (const uint8_t *) &event.mouse.button,
221-
sizeof(mouse_button_t));
222-
break;
223-
}
224-
225-
rv_set_reg(rv, rv_reg_a0, 1);
221+
/* Bit Twiddling Hacks: Round up to the next highest power of 2 */
222+
queues_capacity--;
223+
queues_capacity |= queues_capacity >> 1;
224+
queues_capacity |= queues_capacity >> 2;
225+
queues_capacity |= queues_capacity >> 4;
226+
queues_capacity |= queues_capacity >> 8;
227+
queues_capacity |= queues_capacity >> 16;
228+
queues_capacity++;
229+
230+
void *base = malloc(sizeof(event_t) * queues_capacity +
231+
sizeof(submission_t) * queues_capacity);
232+
event_queue.base = base;
233+
submission_queue.base = base + sizeof(event_t) * queues_capacity;
234+
235+
rv_set_reg(
236+
rv, rv_reg_a0,
237+
(uint32_t) (uintptr_t) base); /* eliminate the "cast from pointer to
238+
integer of different size" warning*/
226239
}
240+
241+
void syscall_submit_queue(struct riscv_t *rv)
242+
{
243+
/* submit(count) */
244+
uint32_t count = rv_get_reg(rv, rv_reg_a0);
245+
246+
while (count--) {
247+
submission_t submission = submission_pop();
248+
249+
switch (submission.type) {
250+
case RELATIVE_MODE_SUBMISSION:
251+
SDL_SetRelativeMouseMode(submission.mouse.enabled);
252+
break;
253+
}
254+
}
255+
}

0 commit comments

Comments
 (0)