1 /* Copyright (C) 2021-2023 Harry Godden (hgn) - All Rights Reserved
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
12 static void vg_assert_thread( enum vg_thread_purpose required
);
14 typedef struct vg_async_item vg_async_item
;
22 void (*fn_runner
)( void *payload
, u32 size
);
28 vg_async_item
*start
, *end
;
30 SDL_sem
*sem_wait_for_flush
;
31 SDL_SpinLock sl_index
;
35 static enum vg_thread_purpose
vg_thread_purpose(void);
36 static enum engine_status
_vg_engine_status(void);
39 * Allocate an asynchronous call with a bit of memory
41 static vg_async_item
*vg_async_alloc( u32 size
)
43 /* ditch out here if engine crashed. this serves as the 'quit checking' */
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 );
49 SDL_AtomicLock( &vg_async
.sl_index
);
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
);
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 );
61 vg_fatal_error( "async alloc invalid size\n" );
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
);
69 remaining
= vg_linear_remaining( vg_async
.buffer
);
70 capacity
= vg_linear_get_capacity( vg_async
.buffer
);
72 assert( remaining
== capacity
);
73 assert( vg_async
.start
== NULL
);
74 assert( vg_async
.end
== NULL
);
77 void *block
= vg_linear_alloc( vg_async
.buffer
, total_allocation
);
79 vg_async_item
*entry
= block
;
82 if( size
) entry
->payload
= ((u8
*)block
) + vg_align8(sizeof(vg_async_item
));
83 else entry
->payload
= NULL
;
86 entry
->fn_runner
= NULL
;
89 vg_async
.end
->next
= entry
;
92 vg_async
.start
= entry
;
96 SDL_AtomicUnlock( &vg_async
.sl_index
);
102 * Wait until the current stack of async calls is completely flushed out
104 static void vg_async_stall(void)
106 vg_assert_thread(k_thread_purpose_loader
);
107 vg_info( "async_stall: %d\n", SDL_SemValue( vg_async
.sem_wait_for_flush
) );
108 SDL_SemWait( vg_async
.sem_wait_for_flush
);
112 * Mark the call as being filled and ready to go
114 static void vg_async_dispatch( vg_async_item
*item
,
115 void (*runner
)( void *payload
, u32 size
) )
117 vg_assert_thread(k_thread_purpose_loader
);
118 if( SDL_SemValue(vg_async
.sem_wait_for_flush
) )
119 SDL_SemWait(vg_async
.sem_wait_for_flush
);
121 SDL_AtomicLock( &vg_async
.sl_index
);
122 item
->fn_runner
= runner
;
123 SDL_AtomicUnlock( &vg_async
.sl_index
);
127 * Make a simple async call without allocating extra.
129 static void vg_async_call( void (*runner
)( void *payload
, u32 size
),
130 void *payload
, u32 size
)
132 vg_assert_thread(k_thread_purpose_loader
);
133 vg_async_item
*call
= vg_async_alloc(0);
134 call
->payload
= payload
;
136 vg_async_dispatch( call
, runner
);
140 * Run as much of the async buffer as possible
142 static void vg_run_async_checked(void)
144 SDL_AtomicLock( &vg_async
.sl_index
);
146 while( vg_async
.start
){
147 vg_async_item
*entry
= vg_async
.start
;
149 if( entry
->fn_runner
){
150 entry
->fn_runner( entry
->payload
, entry
->size
);
151 vg_async
.start
= entry
->next
;
153 if( vg_async
.start
== NULL
){
156 vg_linear_clear( vg_async
.buffer
);
158 if( !SDL_SemValue( vg_async
.sem_wait_for_flush
) ){
159 SDL_SemPost( vg_async
.sem_wait_for_flush
);
164 SDL_AtomicUnlock( &vg_async
.sl_index
);
168 /* TODO: if exceed max frametime.... */
171 if( !SDL_SemValue( vg_async
.sem_wait_for_flush
) ){
172 SDL_SemPost( vg_async
.sem_wait_for_flush
);
175 SDL_AtomicUnlock( &vg_async
.sl_index
);
178 static void vg_async_init(void)
180 vg_async
.sem_wait_for_flush
= SDL_CreateSemaphore(0);
181 vg_async
.buffer
= vg_create_linear_allocator( NULL
, 50*1024*1024,
185 #endif /* VG_ASYNC_H */