366f448108221c18b0a910f1d8d66a84d31c117d
[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 #include "vg/vg.h"
11
12 typedef struct vg_async_item vg_async_item;
13
14 struct vg_async_item{
15 vg_async_item *next;
16
17 void *payload;
18 u32 size;
19
20 void (*fn_runner)( void *payload, u32 size );
21 };
22
23 struct vg_async{
24 void *buffer;
25
26 vg_async_item *start, *end;
27
28 SDL_sem *sem_wait_for_flush;
29 SDL_SpinLock sl_index;
30 }
31 static vg_async;
32
33 /*
34 * Allocate an asynchronous call with a bit of memory
35 */
36 VG_STATIC vg_async_item *vg_async_alloc( u32 size )
37 {
38 SDL_AtomicLock( &vg_async.sl_index );
39
40 u32 total_allocation = vg_align8(size) + vg_align8(sizeof(vg_async_item)),
41 remaining = vg_linear_remaining( vg_async.buffer ),
42 capacity = vg_linear_get_capacity( vg_async.buffer );
43
44 if( total_allocation > capacity ){
45 SDL_AtomicUnlock( &vg_async.sl_index );
46 vg_error( "Requested: %umb. Buffer size: %umb\n",
47 (total_allocation/1024)/1024,
48 (capacity/1024)/1024 );
49 vg_fatal_exit_loop( "async alloc invalid size\n" );
50 }
51
52 if( total_allocation > remaining ){
53 SDL_AtomicUnlock( &vg_async.sl_index );
54 SDL_SemWait( vg_async.sem_wait_for_flush );
55 SDL_AtomicLock( &vg_async.sl_index );
56
57 remaining = vg_linear_remaining( vg_async.buffer );
58 capacity = vg_linear_get_capacity( vg_async.buffer );
59
60 assert( remaining == capacity );
61 assert( vg_async.start == NULL );
62 assert( vg_async.end == NULL );
63 }
64
65 void *block = vg_linear_alloc( vg_async.buffer, total_allocation );
66
67 vg_async_item *entry = block;
68 entry->next = NULL;
69 entry->payload = ((u8*)block) + vg_align8(sizeof(vg_async_item));
70 entry->size = size;
71 entry->fn_runner = NULL;
72
73 if( vg_async.end ){
74 vg_async.end->next = entry;
75 vg_async.end = entry;
76 }else{
77 vg_async.start = entry;
78 vg_async.end = entry;
79 }
80
81 SDL_AtomicUnlock( &vg_async.sl_index );
82
83 return entry;
84 }
85
86 /*
87 * Mark the call as being filled and ready to go
88 */
89 VG_STATIC void vg_async_dispatch( vg_async_item *item,
90 void (*runner)( void *payload, u32 size ) )
91 {
92 SDL_AtomicLock( &vg_async.sl_index );
93 item->fn_runner = runner;
94 SDL_AtomicUnlock( &vg_async.sl_index );
95 }
96
97 /*
98 * Run as much of the async buffer as possible
99 */
100 VG_STATIC void vg_run_async_checked(void)
101 {
102 SDL_AtomicLock( &vg_async.sl_index );
103
104 while( vg_async.start ){
105 vg_async_item *entry = vg_async.start;
106
107 if( entry->fn_runner ){
108 entry->fn_runner( entry->payload, entry->size );
109 vg_async.start = entry->next;
110
111 if( vg_async.start == NULL ){
112 vg_async.end = NULL;
113
114 vg_linear_clear( vg_async.buffer );
115
116 if( !SDL_SemValue( vg_async.sem_wait_for_flush ) ){
117 SDL_SemPost( vg_async.sem_wait_for_flush );
118 }
119 }
120 }
121 else{
122 break;
123 }
124
125 /* TODO: if exceed max frametime.... */
126 }
127
128 SDL_AtomicUnlock( &vg_async.sl_index );
129 }
130
131 VG_STATIC void vg_async_init(void)
132 {
133 vg_async.sem_wait_for_flush = SDL_CreateSemaphore(0);
134 vg_async.buffer = vg_create_linear_allocator( NULL, 50*1024*1024,
135 VG_MEMORY_REALTIME );
136 }
137
138 #endif /* VG_ASYNC_H */