switch to async system
[vg.git] / vg_async.h
1 /* Copyright (C) 2021-2023 Harry Godden (hgn) - All Rights Reserved
2 *
3 * primateves that you use when you need to run something from another thread
4 * back in the main loop of vg, at the start of each frame
5 */
6
7 #ifndef VG_ASYNC_H
8 #define VG_ASYNC_H
9
10 #define VG_GAME
11 #include "vg/vg.h"
12
13 typedef struct vg_async_item vg_async_item;
14
15 struct vg_async_item{
16 vg_async_item *next;
17
18 void *payload;
19 u32 size;
20
21 void (*fn_runner)( void *payload, u32 size );
22 };
23
24 struct vg_async{
25 void *buffer;
26
27 vg_async_item *start, *end;
28
29 SDL_sem *sem_wait_for_flush;
30 SDL_SpinLock sl_index;
31 }
32 static vg_async;
33
34 VG_STATIC enum vg_thread_purpose vg_thread_purpose(void);
35 VG_STATIC enum engine_status _vg_engine_status(void);
36
37 /*
38 * Allocate an asynchronous call with a bit of memory
39 */
40 VG_STATIC vg_async_item *vg_async_alloc( u32 size )
41 {
42 /* ditch out here if engine crashed. this serves as the 'quit checking' */
43
44 if( _vg_engine_status() == k_engine_status_crashed ){
45 assert( vg_thread_purpose() == k_thread_purpose_loader );
46 longjmp( vg.env_loader_exit, 1 );
47 }
48
49 SDL_AtomicLock( &vg_async.sl_index );
50
51 u32 total_allocation = vg_align8(size) + vg_align8(sizeof(vg_async_item)),
52 remaining = vg_linear_remaining( vg_async.buffer ),
53 capacity = vg_linear_get_capacity( vg_async.buffer );
54
55 if( total_allocation > capacity ){
56 SDL_AtomicUnlock( &vg_async.sl_index );
57 vg_error( "Requested: %umb. Buffer size: %umb\n",
58 (total_allocation/1024)/1024,
59 (capacity/1024)/1024 );
60
61 vg_fatal_error( "async alloc invalid size\n" );
62 }
63
64 if( total_allocation > remaining ){
65 SDL_AtomicUnlock( &vg_async.sl_index );
66 SDL_SemWait( vg_async.sem_wait_for_flush );
67 SDL_AtomicLock( &vg_async.sl_index );
68
69 remaining = vg_linear_remaining( vg_async.buffer );
70 capacity = vg_linear_get_capacity( vg_async.buffer );
71
72 assert( remaining == capacity );
73 assert( vg_async.start == NULL );
74 assert( vg_async.end == NULL );
75 }
76
77 void *block = vg_linear_alloc( vg_async.buffer, total_allocation );
78
79 vg_async_item *entry = block;
80 entry->next = NULL;
81
82 if( size ) entry->payload = ((u8*)block) + vg_align8(sizeof(vg_async_item));
83 else entry->payload = NULL;
84
85 entry->size = size;
86 entry->fn_runner = NULL;
87
88 if( vg_async.end ){
89 vg_async.end->next = entry;
90 vg_async.end = entry;
91 }else{
92 vg_async.start = entry;
93 vg_async.end = entry;
94 }
95
96 SDL_AtomicUnlock( &vg_async.sl_index );
97
98 return entry;
99 }
100
101 VG_STATIC void vg_async_stall(void)
102 {
103 vg_info( "async_stall: %d\n", SDL_SemValue( vg_async.sem_wait_for_flush ) );
104 SDL_SemWait( vg_async.sem_wait_for_flush );
105 }
106
107 /*
108 * Mark the call as being filled and ready to go
109 */
110 VG_STATIC void vg_async_dispatch( vg_async_item *item,
111 void (*runner)( void *payload, u32 size ) )
112 {
113 if( SDL_SemValue(vg_async.sem_wait_for_flush) )
114 SDL_SemWait(vg_async.sem_wait_for_flush);
115
116 SDL_AtomicLock( &vg_async.sl_index );
117 item->fn_runner = runner;
118 SDL_AtomicUnlock( &vg_async.sl_index );
119 }
120
121 /*
122 * Run as much of the async buffer as possible
123 */
124 VG_STATIC void vg_run_async_checked(void)
125 {
126 SDL_AtomicLock( &vg_async.sl_index );
127
128 while( vg_async.start ){
129 vg_async_item *entry = vg_async.start;
130
131 if( entry->fn_runner ){
132 entry->fn_runner( entry->payload, entry->size );
133 vg_async.start = entry->next;
134
135 if( vg_async.start == NULL ){
136 vg_async.end = NULL;
137
138 vg_linear_clear( vg_async.buffer );
139
140 if( !SDL_SemValue( vg_async.sem_wait_for_flush ) ){
141 SDL_SemPost( vg_async.sem_wait_for_flush );
142 }
143 }
144 }
145 else{
146 SDL_AtomicUnlock( &vg_async.sl_index );
147 return;
148 }
149
150 /* TODO: if exceed max frametime.... */
151 }
152
153 if( !SDL_SemValue( vg_async.sem_wait_for_flush ) ){
154 SDL_SemPost( vg_async.sem_wait_for_flush );
155 }
156
157 SDL_AtomicUnlock( &vg_async.sl_index );
158 }
159
160 VG_STATIC void vg_async_init(void)
161 {
162 vg_async.sem_wait_for_flush = SDL_CreateSemaphore(0);
163 vg_async.buffer = vg_create_linear_allocator( NULL, 50*1024*1024,
164 VG_MEMORY_SYSTEM );
165 }
166
167 #endif /* VG_ASYNC_H */