fixed nan propogation error
[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 if( _vg_engine_status() == k_engine_status_crashed ){
44 assert( vg_thread_purpose() == k_thread_purpose_loader );
45 longjmp( vg.env_loader_exit, 1 );
46 }
47
48 SDL_AtomicLock( &vg_async.sl_index );
49
50 u32 total_allocation = vg_align8(size) + vg_align8(sizeof(vg_async_item)),
51 remaining = vg_linear_remaining( vg_async.buffer ),
52 capacity = vg_linear_get_capacity( vg_async.buffer );
53
54 if( total_allocation > capacity ){
55 SDL_AtomicUnlock( &vg_async.sl_index );
56 vg_error( "Requested: %umb. Buffer size: %umb\n",
57 (total_allocation/1024)/1024,
58 (capacity/1024)/1024 );
59
60 vg_fatal_error( "async alloc invalid size\n" );
61 }
62
63 if( total_allocation > remaining ){
64 SDL_AtomicUnlock( &vg_async.sl_index );
65 SDL_SemWait( vg_async.sem_wait_for_flush );
66 SDL_AtomicLock( &vg_async.sl_index );
67
68 remaining = vg_linear_remaining( vg_async.buffer );
69 capacity = vg_linear_get_capacity( vg_async.buffer );
70
71 assert( remaining == capacity );
72 assert( vg_async.start == NULL );
73 assert( vg_async.end == NULL );
74 }
75
76 void *block = vg_linear_alloc( vg_async.buffer, total_allocation );
77
78 vg_async_item *entry = block;
79 entry->next = NULL;
80
81 if( size ) entry->payload = ((u8*)block) + vg_align8(sizeof(vg_async_item));
82 else entry->payload = NULL;
83
84 entry->size = size;
85 entry->fn_runner = NULL;
86
87 if( vg_async.end ){
88 vg_async.end->next = entry;
89 vg_async.end = entry;
90 }else{
91 vg_async.start = entry;
92 vg_async.end = entry;
93 }
94
95 SDL_AtomicUnlock( &vg_async.sl_index );
96
97 return entry;
98 }
99
100 /*
101 * Wait until the current stack of async calls is completely flushed out
102 */
103 VG_STATIC void vg_async_stall(void)
104 {
105 vg_info( "async_stall: %d\n", SDL_SemValue( vg_async.sem_wait_for_flush ) );
106 SDL_SemWait( vg_async.sem_wait_for_flush );
107 }
108
109 /*
110 * Mark the call as being filled and ready to go
111 */
112 VG_STATIC void vg_async_dispatch( vg_async_item *item,
113 void (*runner)( void *payload, u32 size ) )
114 {
115 if( SDL_SemValue(vg_async.sem_wait_for_flush) )
116 SDL_SemWait(vg_async.sem_wait_for_flush);
117
118 SDL_AtomicLock( &vg_async.sl_index );
119 item->fn_runner = runner;
120 SDL_AtomicUnlock( &vg_async.sl_index );
121 }
122
123 /*
124 * Make a simple async call without allocating extra.
125 */
126 VG_STATIC void vg_async_call( void (*runner)( void *payload, u32 size ),
127 void *payload, u32 size )
128 {
129 vg_async_item *call = vg_async_alloc(0);
130 call->payload = payload;
131 call->size = size;
132 vg_async_dispatch( call, runner );
133 }
134
135 /*
136 * Run as much of the async buffer as possible
137 */
138 VG_STATIC void vg_run_async_checked(void)
139 {
140 SDL_AtomicLock( &vg_async.sl_index );
141
142 while( vg_async.start ){
143 vg_async_item *entry = vg_async.start;
144
145 if( entry->fn_runner ){
146 entry->fn_runner( entry->payload, entry->size );
147 vg_async.start = entry->next;
148
149 if( vg_async.start == NULL ){
150 vg_async.end = NULL;
151
152 vg_linear_clear( vg_async.buffer );
153
154 if( !SDL_SemValue( vg_async.sem_wait_for_flush ) ){
155 SDL_SemPost( vg_async.sem_wait_for_flush );
156 }
157 }
158 }
159 else{
160 SDL_AtomicUnlock( &vg_async.sl_index );
161 return;
162 }
163
164 /* TODO: if exceed max frametime.... */
165 }
166
167 if( !SDL_SemValue( vg_async.sem_wait_for_flush ) ){
168 SDL_SemPost( vg_async.sem_wait_for_flush );
169 }
170
171 SDL_AtomicUnlock( &vg_async.sl_index );
172 }
173
174 VG_STATIC void vg_async_init(void)
175 {
176 vg_async.sem_wait_for_flush = SDL_CreateSemaphore(0);
177 vg_async.buffer = vg_create_linear_allocator( NULL, 50*1024*1024,
178 VG_MEMORY_SYSTEM );
179 }
180
181 #endif /* VG_ASYNC_H */