job refactor to move futexes out of platform layer
This commit is contained in:
parent
0518513478
commit
1abf58d45b
@ -9,6 +9,10 @@ set program_build_cmd=meta.exe %*
|
||||
set meta_build_cmd=cl.exe ../src/meta/meta.c -Od -Z7 -nologo -diagnostics:column -WX -link -DEBUG:FULL -INCREMENTAL:NO
|
||||
set meta_rebuild_code=1317212284
|
||||
|
||||
if "%force_meta_build%"=="1" (
|
||||
if exist meta.exe del meta.exe
|
||||
)
|
||||
|
||||
::- Meta build
|
||||
:meta_build
|
||||
if not exist meta.exe (
|
||||
@ -22,7 +26,7 @@ if not exist meta.exe (
|
||||
)
|
||||
|
||||
::- Program build
|
||||
if not "%nobuild%"=="1" (
|
||||
if not "%no_program_build%"=="1" (
|
||||
echo ======== Build ========
|
||||
%program_build_cmd%
|
||||
set "rc=!errorlevel!"
|
||||
|
||||
@ -16,15 +16,13 @@ Struct(AppArgList)
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ State
|
||||
//~ State types
|
||||
|
||||
Struct(SharedAppState)
|
||||
{
|
||||
Arena *arena;
|
||||
String write_path;
|
||||
};
|
||||
|
||||
extern SharedAppState shared_app_state;
|
||||
} extern shared_app_state;
|
||||
|
||||
////////////////////////////////
|
||||
//~ App functions
|
||||
|
||||
@ -38,7 +38,7 @@ Struct(AC_Store)
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ State
|
||||
//~ State types
|
||||
|
||||
#define AC_MaxAssets 1024
|
||||
#define AC_AssetLookupTableCapacity (AC_MaxAssets * 4)
|
||||
@ -58,9 +58,7 @@ Struct(AC_SharedState)
|
||||
u64 dbg_table_count;
|
||||
Mutex dbg_table_mutex;
|
||||
#endif
|
||||
};
|
||||
|
||||
extern AC_SharedState AC_shared_state;
|
||||
} extern AC_shared_state;
|
||||
|
||||
////////////////////////////////
|
||||
//~ Startup
|
||||
|
||||
@ -95,6 +95,10 @@
|
||||
# error Unknown architecture
|
||||
# endif
|
||||
|
||||
//- Cache line size
|
||||
/* TODO: Just hard-code to something like 128 or 256 if Apple silicon is ever supported */
|
||||
#define CachelineSize 64
|
||||
|
||||
//- Windows NTDDI version
|
||||
/* TODO: Remove this */
|
||||
#if 0
|
||||
@ -139,7 +143,7 @@
|
||||
#if RtcIsEnabled
|
||||
# if CompilerIsMsvc
|
||||
# define Assert(cond) ((cond) ? 1 : (IsRunningInDebugger() ? (*(volatile i32 *)0 = 0) : Panic(Lit(__FILE__ ":" Stringize(__LINE__) ":0: assertion failed: "#cond""))))
|
||||
# define DEBUGBREAK __debugbreak
|
||||
# define DEBUGBREAK __debugbreak()
|
||||
# else
|
||||
# define Assert(cond) ((cond) ? 1 : (__builtin_trap(), 0))
|
||||
# define DEBUGBREAK __builtin_debugtrap()
|
||||
@ -405,6 +409,7 @@ void __asan_unpoison_memory_region(void const volatile *add, size_t);
|
||||
//- Struct
|
||||
#define Struct(name) typedef struct name name; struct name
|
||||
#define AlignedStruct(name, n) typedef struct name name; struct alignas(n) name
|
||||
#define AlignedBlock(n) struct alignas(n)
|
||||
|
||||
//- Enum
|
||||
#define Enum(name) typedef enum name name; enum name
|
||||
@ -530,14 +535,14 @@ Struct(Atomic32) { volatile i32 _v; };
|
||||
Struct(Atomic64) { volatile i64 _v; };
|
||||
|
||||
//- Cache-line isolated aligned atomic types
|
||||
AlignedStruct(Atomic8Padded, 64) { Atomic8 v; u8 _pad[63]; };
|
||||
AlignedStruct(Atomic16Padded, 64) { Atomic16 v; u8 _pad[62]; };
|
||||
AlignedStruct(Atomic32Padded, 64) { Atomic32 v; u8 _pad[60]; };
|
||||
AlignedStruct(Atomic64Padded, 64) { Atomic64 v; u8 _pad[56]; };
|
||||
StaticAssert(sizeof(Atomic8Padded) == 64 && alignof(Atomic8Padded) == 64);
|
||||
StaticAssert(sizeof(Atomic16Padded) == 64 && alignof(Atomic16Padded) == 64);
|
||||
StaticAssert(sizeof(Atomic32Padded) == 64 && alignof(Atomic32Padded) == 64);
|
||||
StaticAssert(sizeof(Atomic64Padded) == 64 && alignof(Atomic64Padded) == 64);
|
||||
AlignedStruct(Atomic8Padded, CachelineSize) { Atomic8 v; };
|
||||
AlignedStruct(Atomic16Padded, CachelineSize) { Atomic16 v; };
|
||||
AlignedStruct(Atomic32Padded, CachelineSize) { Atomic32 v; };
|
||||
AlignedStruct(Atomic64Padded, CachelineSize) { Atomic64 v; };
|
||||
StaticAssert(alignof(Atomic8Padded) == CachelineSize && sizeof(Atomic8Padded) % CachelineSize == 0);
|
||||
StaticAssert(alignof(Atomic16Padded) == CachelineSize && sizeof(Atomic16Padded) % CachelineSize == 0);
|
||||
StaticAssert(alignof(Atomic32Padded) == CachelineSize && sizeof(Atomic32Padded) % CachelineSize == 0);
|
||||
StaticAssert(alignof(Atomic64Padded) == CachelineSize && sizeof(Atomic64Padded) % CachelineSize == 0);
|
||||
|
||||
#if PlatformIsWindows && ArchIsX64
|
||||
//- 8 bit atomic operations
|
||||
@ -550,8 +555,8 @@ StaticAssert(sizeof(Atomic64Padded) == 64 && alignof(Atomic64Padded) == 64);
|
||||
#define Atomic16Fetch(x) (x)->_v
|
||||
#define Atomic16FetchSet(x, e) (i16)_InterlockedExchange16(&(x)->_v, (e))
|
||||
#define Atomic16FetchTestSet(x, c, e) (i16)_InterlockedCompareExchange16(&(x)->_v, (e), (c))
|
||||
#define Atomic16FetchTestXor(x, c) (i16)_InterlockedXor16(&(x)->_v, (c))
|
||||
#define Atomic16FetchTestAdd(x, a) (i16)_InterlockedExchangeAdd16(&(x)->_v, (a))
|
||||
#define Atomic16FetchXor(x, c) (i16)_InterlockedXor16(&(x)->_v, (c))
|
||||
#define Atomic16FetchAdd(x, a) (i16)_InterlockedExchangeAdd16(&(x)->_v, (a))
|
||||
//- 32 bit atomic operations
|
||||
#define Atomic32Fetch(x) (x)->_v
|
||||
#define Atomic32FetchSet(x, e) (i32)_InterlockedExchange((volatile long *)&(x)->_v, (e))
|
||||
@ -576,14 +581,14 @@ StaticAssert(sizeof(Atomic64Padded) == 64 && alignof(Atomic64Padded) == 64);
|
||||
#if LanguageIsC
|
||||
Struct(TicketMutex)
|
||||
{
|
||||
Atomic64Padded ticket;
|
||||
Atomic64Padded serving;
|
||||
Atomic16Padded ticket;
|
||||
Atomic16Padded serving;
|
||||
};
|
||||
|
||||
ForceInline void LockTicketMutex(TicketMutex *tm)
|
||||
{
|
||||
i64 ticket = Atomic64FetchAdd(&tm->ticket.v, 1);
|
||||
while (Atomic64Fetch(&tm->serving.v) != ticket)
|
||||
u16 ticket = Atomic16FetchAdd(&tm->ticket.v, 1);
|
||||
while (Atomic16Fetch(&tm->serving.v) != ticket)
|
||||
{
|
||||
_mm_pause();
|
||||
}
|
||||
@ -591,7 +596,7 @@ ForceInline void LockTicketMutex(TicketMutex *tm)
|
||||
|
||||
ForceInline void UnlockTicketMutex(TicketMutex *tm)
|
||||
{
|
||||
Atomic64FetchAdd(&tm->serving.v, 1);
|
||||
Atomic16FetchAdd(&tm->serving.v, 1);
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -703,7 +708,7 @@ Struct(ComputeShader) { Resource resource; };
|
||||
|
||||
#if LanguageIsC
|
||||
# if PlatformIsWindows
|
||||
# define FiberId() (*(volatile i16 *)__readgsqword(32))
|
||||
# define FiberId() (*(volatile i16 *)__readgsqword(0x20))
|
||||
# else
|
||||
# error FiberId not implemented
|
||||
# endif
|
||||
@ -727,8 +732,11 @@ typedef ExitFuncDef(ExitFunc);
|
||||
|
||||
//- Core hooks
|
||||
StringList GetCommandLineArgs(void);
|
||||
void Echo(String msg);
|
||||
b32 Panic(String msg);
|
||||
b32 IsRunningInDebugger(void);
|
||||
i64 TimeNs(void);
|
||||
u32 GetNumHardwareThreads(void);
|
||||
void TrueRand(String buffer);
|
||||
void OnExit(ExitFunc *func);
|
||||
void SignalExit(i32 code);
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
////////////////////////////////
|
||||
//~ Arena types
|
||||
|
||||
#define ArenaHeaderSize 64
|
||||
#define ArenaBlockSize 16384
|
||||
#define ArenaHeaderSize CachelineSize
|
||||
#define ArenaBlockSize 16384
|
||||
|
||||
Struct(Arena)
|
||||
{
|
||||
@ -25,7 +25,7 @@ Struct(TempArena)
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ State
|
||||
//~ State types
|
||||
|
||||
#define ScratchArenasPerCtx 2
|
||||
|
||||
@ -38,9 +38,7 @@ Struct(FiberArenaCtx)
|
||||
Struct(SharedArenaCtx)
|
||||
{
|
||||
FiberArenaCtx arena_contexts[MaxFibers];
|
||||
};
|
||||
|
||||
extern SharedArenaCtx shared_arena_ctx;
|
||||
} extern shared_arena_ctx;
|
||||
|
||||
////////////////////////////////
|
||||
//~ Arena push/pop
|
||||
@ -125,7 +123,7 @@ void ShrinkArena(Arena *arena);
|
||||
void SetArenaReadonly(Arena *arena);
|
||||
void SetArenaReadWrite(Arena *arena);
|
||||
|
||||
Inline void *AlignArena(Arena *arena, u64 align)
|
||||
Inline void *PushAlign(Arena *arena, u64 align)
|
||||
{
|
||||
Assert(!arena->readonly);
|
||||
if (align > 0)
|
||||
|
||||
185
src/base/base_futex.c
Normal file
185
src/base/base_futex.c
Normal file
@ -0,0 +1,185 @@
|
||||
SharedFutexState shared_futex_state = ZI;
|
||||
|
||||
////////////////////////////////
|
||||
//~ Startup
|
||||
|
||||
void InitFutexSystem(void)
|
||||
{
|
||||
SharedFutexState *g = &shared_futex_state;
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
//~ State helpers
|
||||
|
||||
FiberNeqFutexState *FiberNeqFutexStateFromId(i16 fiber_id)
|
||||
{
|
||||
return &shared_futex_state.fiber_neq_states[fiber_id];
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
//~ Not-equal futex operations
|
||||
|
||||
void FutexYieldNeq(volatile void *addr, void *cmp, u8 cmp_size)
|
||||
{
|
||||
SharedFutexState *g = &shared_futex_state;
|
||||
FutexNeqListBin *bin = &g->neq_bins[RandU64FromSeed((u64)addr) % countof(g->neq_bins)];
|
||||
|
||||
b32 cancel = 0;
|
||||
|
||||
LockTicketMutex(&bin->tm);
|
||||
{
|
||||
/* Now that bin is locked, check if we should cancel insertion based on current value at address */
|
||||
{
|
||||
union
|
||||
{
|
||||
volatile void *v;
|
||||
Atomic8 *v8;
|
||||
Atomic16 *v16;
|
||||
Atomic32 *v32;
|
||||
Atomic64 *v64;
|
||||
} addr_atomic;
|
||||
union
|
||||
{
|
||||
void *v;
|
||||
i8 *v8;
|
||||
i16 *v16;
|
||||
i32 *v32;
|
||||
i64 *v64;
|
||||
} cmp_int;
|
||||
addr_atomic.v = addr;
|
||||
cmp_int.v = cmp;
|
||||
switch(cmp_size)
|
||||
{
|
||||
default: Assert(0); cancel = 1; break; /* Invalid futex size */
|
||||
case 1: cancel = Atomic8Fetch(addr_atomic.v8) != *cmp_int.v8; break;
|
||||
case 2: cancel = Atomic16Fetch(addr_atomic.v16) != *cmp_int.v16; break;
|
||||
case 4: cancel = Atomic32Fetch(addr_atomic.v32) != *cmp_int.v32; break;
|
||||
case 8: cancel = Atomic64Fetch(addr_atomic.v64) != *cmp_int.v64; break;
|
||||
}
|
||||
}
|
||||
if (!cancel)
|
||||
{
|
||||
/* Grab futex list from address */
|
||||
FutexNeqList *list = 0;
|
||||
{
|
||||
list = bin->first;
|
||||
for (; list; list = list->next)
|
||||
{
|
||||
if (list->addr == addr) break;
|
||||
}
|
||||
if (!list)
|
||||
{
|
||||
if (bin->first_free)
|
||||
{
|
||||
list = bin->first_free;
|
||||
bin->first_free = list->next;
|
||||
ZeroStruct(list);
|
||||
}
|
||||
else
|
||||
{
|
||||
Arena *perm = PermArena();
|
||||
PushAlign(perm, CachelineSize);
|
||||
list = PushStruct(perm, FutexNeqList);
|
||||
PushAlign(perm, CachelineSize);
|
||||
list->addr = addr;
|
||||
}
|
||||
list->next = bin->first;
|
||||
bin->first = list;
|
||||
}
|
||||
}
|
||||
|
||||
/* Insert */
|
||||
{
|
||||
i16 fiber_id = FiberId();
|
||||
FiberNeqFutexState *f = FiberNeqFutexStateFromId(fiber_id);
|
||||
f->next = list->first;
|
||||
list->first = fiber_id;
|
||||
}
|
||||
}
|
||||
}
|
||||
UnlockTicketMutex(&bin->tm);
|
||||
|
||||
/* Suspend */
|
||||
if (!cancel)
|
||||
{
|
||||
SuspendFiber();
|
||||
}
|
||||
}
|
||||
|
||||
void FutexWakeNeq(void *addr)
|
||||
{
|
||||
SharedFutexState *g = &shared_futex_state;
|
||||
FutexNeqListBin *bin = &g->neq_bins[RandU64FromSeed((u64)addr) % countof(g->neq_bins)];
|
||||
|
||||
/* Pull waiting ids */
|
||||
i16 first_id = 0;
|
||||
{
|
||||
LockTicketMutex(&bin->tm);
|
||||
{
|
||||
FutexNeqList *list = bin->first;
|
||||
for (; list; list = list->next)
|
||||
{
|
||||
if (list->addr == addr) break;
|
||||
}
|
||||
if (list)
|
||||
{
|
||||
first_id = list->first;
|
||||
/* Free futex list */
|
||||
{
|
||||
FutexNeqList *prev = list->prev;
|
||||
FutexNeqList *next = list->next;
|
||||
if (prev)
|
||||
{
|
||||
prev->next = next;
|
||||
}
|
||||
else
|
||||
{
|
||||
bin->first = next;
|
||||
}
|
||||
if (next)
|
||||
{
|
||||
next->prev = prev;
|
||||
}
|
||||
list->next = bin->first_free;
|
||||
bin->first_free = list;
|
||||
}
|
||||
}
|
||||
}
|
||||
UnlockTicketMutex(&bin->tm);
|
||||
}
|
||||
|
||||
/* Resume fibers */
|
||||
if (first_id != 0)
|
||||
{
|
||||
TempArena scratch = BeginScratchNoConflict();
|
||||
i16 *ids = PushDry(scratch.arena, i16);
|
||||
i16 ids_count = 0;
|
||||
{
|
||||
i16 id = first_id;
|
||||
while (id != 0)
|
||||
{
|
||||
FiberNeqFutexState *f = FiberNeqFutexStateFromId(id);
|
||||
*PushStructNoZero(scratch.arena, i16) = id;
|
||||
++ids_count;
|
||||
id = FiberNeqFutexStateFromId(id)->next;
|
||||
}
|
||||
}
|
||||
ResumeFibers(ids_count, ids);
|
||||
EndScratch(scratch);
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
//~ Greater-than-or-equal futex operations
|
||||
|
||||
void FutexYieldGte(volatile void *addr, void *cmp, u8 cmp_size)
|
||||
{
|
||||
/* TODO: Actually implement this. Just emulating via neq for now. */
|
||||
FutexYieldNeq(addr, cmp, cmp_size);
|
||||
}
|
||||
|
||||
void FutexWakeGte(void *addr)
|
||||
{
|
||||
/* TODO: Actually implement this. Just emulating via neq for now. */
|
||||
FutexWakeNeq(addr);
|
||||
}
|
||||
67
src/base/base_futex.h
Normal file
67
src/base/base_futex.h
Normal file
@ -0,0 +1,67 @@
|
||||
////////////////////////////////
|
||||
//~ Neq futex types
|
||||
|
||||
Struct(FutexNeqList)
|
||||
{
|
||||
FutexNeqList *prev;
|
||||
FutexNeqList *next;
|
||||
volatile void *addr;
|
||||
i16 first;
|
||||
};
|
||||
|
||||
Struct(FutexNeqListBin)
|
||||
{
|
||||
TicketMutex tm;
|
||||
FutexNeqList *first;
|
||||
FutexNeqList *first_free;
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ State types
|
||||
|
||||
#define FutexNeqBinsCount 16384
|
||||
#define FutexGteBinsCount 16384
|
||||
|
||||
AlignedStruct(FiberNeqFutexState, CachelineSize)
|
||||
{
|
||||
i16 next;
|
||||
};
|
||||
|
||||
Struct(SharedFutexState)
|
||||
{
|
||||
FiberNeqFutexState fiber_neq_states[MaxFibers];
|
||||
FutexNeqListBin neq_bins[FutexNeqBinsCount];
|
||||
} extern shared_futex_state;
|
||||
|
||||
////////////////////////////////
|
||||
//~ Startup
|
||||
|
||||
void InitFutexSystem(void);
|
||||
|
||||
////////////////////////////////
|
||||
//~ State helpers
|
||||
|
||||
FiberNeqFutexState *FiberNeqFutexStateFromId(i16 fiber_id);
|
||||
|
||||
////////////////////////////////
|
||||
//~ Not-equal futex operations
|
||||
|
||||
/* Similar to Win32 WaitOnAddress & WakeByAddressAll
|
||||
* i.e. - Suprious wait until value at address != cmp */
|
||||
|
||||
void FutexYieldNeq(volatile void *addr, void *cmp, u8 cmp_size);
|
||||
void FutexWakeNeq(void *addr);
|
||||
|
||||
////////////////////////////////
|
||||
//~ Greater-than-or-equal futex operations
|
||||
|
||||
/* Similar to Win32 WaitOnAddress & WakeByAddressAll
|
||||
* i.e. - Spurious wait until monotonically increasing value at address >= cmp (used for fences)
|
||||
*
|
||||
* NOTE: This API is offered for fence-like semantics, where waiters only want to
|
||||
* wake when the futex progresses past the specified target value, rather than
|
||||
* wake every time the futex is modified.
|
||||
**/
|
||||
|
||||
void FutexYieldGte(volatile void *addr, void *cmp, u8 cmp_size);
|
||||
void FutexWakeGte(void *addr);
|
||||
@ -8,6 +8,7 @@
|
||||
# include "base_intrinsics.h"
|
||||
# include "base_memory.h"
|
||||
# include "base_arena.h"
|
||||
# include "base_futex.h"
|
||||
# include "base_snc.h"
|
||||
# include "base_job.h"
|
||||
# include "base_uid.h"
|
||||
@ -29,6 +30,7 @@
|
||||
#if LanguageIsC
|
||||
# include "base_memory.c"
|
||||
# include "base_arena.c"
|
||||
# include "base_futex.c"
|
||||
# include "base_snc.c"
|
||||
# include "base_uid.c"
|
||||
# include "base_string.c"
|
||||
|
||||
@ -1,99 +1,91 @@
|
||||
////////////////////////////////
|
||||
//~ Job queue types
|
||||
//~ Opaque types
|
||||
|
||||
/* Work pools contain their own worker threads with their own thread priorities
|
||||
Struct(Job);
|
||||
|
||||
////////////////////////////////
|
||||
//~ Job pool types
|
||||
|
||||
/* Job pools contain worker threads with their own thread priorities
|
||||
* and affinities based on the intended context of the pool. */
|
||||
Enum(JobPool)
|
||||
{
|
||||
JobPool_Inherit = -1,
|
||||
|
||||
/* Contains critical-priority worker threads affinitized over the entire CPU.
|
||||
/* Contains worker threads affinitized over the entire CPU.
|
||||
* Meant to take on high-bandwidth temporary work (e.g. loading a level). */
|
||||
JobPool_Hyper = 0,
|
||||
JobPool_Hyper = 0, /* High-priority */
|
||||
|
||||
/* Contains low-priority worker threads affinitized over the entire CPU.
|
||||
* Meant to take on blocking work that higher-priority workers can continue doing actual work. */
|
||||
JobPool_Blocking = 1,
|
||||
/* Contains worker threads affinitized over the entire CPU.
|
||||
* Meant to take on blocking work so that affinitized workers workers can continue doing actual work. */
|
||||
JobPool_Blocking = 1, /* Normal priority */
|
||||
|
||||
/* Contains low-priority worker threads affinitized to cores that don't interfere with workers in specialized pools.
|
||||
* Meant to take on asynchronous work from higher priority pools. */
|
||||
JobPool_Background = 2,
|
||||
/* Contains worker threads affinitized to cores that don't interfere with workers in specialized pools.
|
||||
* Meant to consume asynchronous work from higher priority pools. */
|
||||
JobPool_Background = 2, /* Normal priority */
|
||||
|
||||
/* Contains high-priority worker threads affinitized to cores that don't interfere with workers in other specialized pools.
|
||||
* These pools are meant to only have work pushed onto them from jobs within the same pool (e.g. pushing 100 rendering
|
||||
* jobs will not interfere with cores running workers on the Sim pool as long as the jobs are pushed onto the User pool). */
|
||||
JobPool_Audio = 3,
|
||||
JobPool_User = 4,
|
||||
JobPool_Sim = 5,
|
||||
/* Contains worker threads affinitized to cores that don't interfere with workers in other specialized pools.
|
||||
* These pools are meant to only have work pushed onto them from jobs within the same pool (e.g. 100 jobs pushed onto the
|
||||
* User pool will not interfere with cores running workers on the Sim pool). */
|
||||
JobPool_Audio = 3, /* Critical priority */
|
||||
JobPool_User = 4, /* Above-normal priority */
|
||||
JobPool_Sim = 5, /* Above-normal priority */
|
||||
|
||||
JobPool_Count
|
||||
};
|
||||
|
||||
/* Job execution order within a pool is based on priority. */
|
||||
Enum(JobPriority)
|
||||
{
|
||||
JobPriority_Inherit = -1,
|
||||
JobPriority_High = 0,
|
||||
JobPriority_Normal = 1,
|
||||
JobPriority_Low = 2,
|
||||
////////////////////////////////
|
||||
//~ Job types
|
||||
|
||||
JobPriority_Count
|
||||
Struct(JobCounter)
|
||||
{
|
||||
Fence jobs_completed_fence;
|
||||
Atomic64Padded num_jobs_dispatched;
|
||||
};
|
||||
|
||||
typedef void JobFunc(void *, i32);
|
||||
|
||||
Struct(Job)
|
||||
{
|
||||
Job *next;
|
||||
|
||||
Arena *arena;
|
||||
JobFunc *func;
|
||||
JobPool pool;
|
||||
i32 count;
|
||||
JobCounter *counter;
|
||||
void *sig;
|
||||
|
||||
Atomic64Padded num_tasks_completed;
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ @hookdecl Init
|
||||
|
||||
void InitJobWorkers(void);
|
||||
void InitJobSystem(void);
|
||||
|
||||
////////////////////////////////
|
||||
//~ @hookdecl Futex
|
||||
//~ @hookdecl Fiber suspend/resume operations
|
||||
|
||||
/* Futex-like wait & wake */
|
||||
void FutexYield(volatile void *addr, void *cmp, u32 size, i64 timeout_ns);
|
||||
void FutexWake(void *addr, i32 count);
|
||||
void SuspendFiber(void);
|
||||
void ResumeFibers(i16 fiber_ids_count, i16 *fiber_ids);
|
||||
|
||||
////////////////////////////////
|
||||
//~ @hookdecl Job operations
|
||||
|
||||
typedef void GenericJobFunc(void *, i32);
|
||||
|
||||
Struct(GenericJobDesc)
|
||||
{
|
||||
Arena *arena;
|
||||
void *sig;
|
||||
GenericJobFunc *func;
|
||||
i32 count;
|
||||
JobPool pool;
|
||||
JobPriority priority;
|
||||
Counter *counter;
|
||||
|
||||
/* Internal */
|
||||
Atomic32 num_completed;
|
||||
GenericJobDesc *next_free;
|
||||
};
|
||||
|
||||
Struct(JobDescParams)
|
||||
{
|
||||
i32 count;
|
||||
JobPool pool;
|
||||
JobPriority priority;
|
||||
Counter *counter;
|
||||
};
|
||||
//~ @hookdecl Job declaration operations
|
||||
|
||||
#define EmptySig { i32 _; }
|
||||
|
||||
#define PushJobDesc(job, ...) (job##_Desc *)PushJobDesc_(sizeof(job##_Sig), alignof(job##_Sig), job##_Generic, (JobDescParams) { .count = 1, .pool = JobPool_Inherit, .priority = JobPriority_Inherit, .counter = 0, __VA_ARGS__ })
|
||||
GenericJobDesc *PushJobDesc_(u64 sig_size, u64 sig_align, GenericJobFunc *func, JobDescParams params);
|
||||
|
||||
#define JobDecl(job, sigdef) \
|
||||
typedef struct job##_Sig sigdef job##_Sig; \
|
||||
Struct(job##_Desc) { Arena *arena; job##_Sig *sig; GenericJobFunc *func; i32 count; JobPool pool; JobPriority priority; Counter *counter; }; \
|
||||
void job(job##_Sig *, i32); \
|
||||
inline void job##_Generic(void *sig, i32 id) { job((job##_Sig *)sig, id); } \
|
||||
#define JobDecl(job, sigdef) \
|
||||
typedef struct job##_Sig sigdef job##_Sig; \
|
||||
Struct(job##_Desc) { JobFunc *func; JobPool pool; u32 count; JobCounter *counter; job##_Sig sig; }; \
|
||||
void job(job##_Sig *, i32); \
|
||||
StaticAssert(1)
|
||||
|
||||
#define JobDef(job, sig_arg, id_arg) void job(job##_Sig *sig_arg, i32 id_arg)
|
||||
|
||||
////////////////////////////////
|
||||
//~ @hookdecl Job dispatch operations
|
||||
|
||||
/* RunJob example usage:
|
||||
*
|
||||
* This example pushes a single 'LoadTextureJob' onto the background job
|
||||
@ -101,24 +93,23 @@ GenericJobDesc *PushJobDesc_(u64 sig_size, u64 sig_align, GenericJobFunc *func,
|
||||
* and then immediately yielded on in this example, effectively making
|
||||
* the operation synchronous as the caller will block until the job completes:
|
||||
* {
|
||||
* Counter counter = {0};
|
||||
* RunJob(1, LoadTextureJob, JobPool_Background, JobPriority_Low, &counter, .resource = sprite);
|
||||
* YieldOnCounter(&counter);
|
||||
* JobCounter counter = {0};
|
||||
* RunJob(LoadTextureJob, .pool = JobPool_Background, .counter = &counter, .sig = { .resource = sprite });
|
||||
* YieldOnJobs(&counter);
|
||||
* }
|
||||
*
|
||||
*/
|
||||
#define RunJob(_count, job, _pool, _priority, _counter, ...) \
|
||||
do { \
|
||||
job##_Desc *__job_desc = (job##_Desc *)PushJobDesc_(sizeof(job##_Sig), alignof(job##_Sig), job##_Generic, (JobDescParams) { .count = _count, .pool = _pool, .priority = _priority, .counter = _counter,}); \
|
||||
*__job_desc->sig = (job##_Sig) { __VA_ARGS__ }; \
|
||||
RunJobEx((GenericJobDesc *)__job_desc); \
|
||||
#define RunJob(name, ...) \
|
||||
do { \
|
||||
name##_Desc __desc = { .count = 1, .pool = JobPool_Inherit, .func = name, __VA_ARGS__ }; \
|
||||
Job *__job = OpenJob(__desc.func, __desc.pool); \
|
||||
__job->count = __desc.count; \
|
||||
__job->counter = __desc.counter; \
|
||||
__job->sig = PushStructNoZero(__job->arena, name##_Sig); \
|
||||
CopyBytes(__job->sig, &__desc.sig, sizeof(__desc.sig)); \
|
||||
CloseJob(__job); \
|
||||
} while (0)
|
||||
|
||||
void RunJobEx(GenericJobDesc *desc);
|
||||
|
||||
////////////////////////////////
|
||||
//~ @hookdecl Helpers
|
||||
|
||||
i64 TimeNs(void);
|
||||
u32 GetLogicalProcessorCount(void);
|
||||
i64 GetCurrentSchedulerPeriodNs(void);
|
||||
Job *OpenJob(JobFunc *func, JobPool pool_kind);
|
||||
void CloseJob(Job *job);
|
||||
#define YieldOnJobs(counter) (YieldOnFence(&(counter)->jobs_completed_fence, Atomic64Fetch(&(counter)->num_jobs_dispatched.v)))
|
||||
|
||||
@ -3,7 +3,7 @@ SharedResourceState shared_resource_state = ZI;
|
||||
////////////////////////////////
|
||||
//~ Startup
|
||||
|
||||
void InitBaseResources(u64 archive_strings_count, String *archive_strings)
|
||||
void InitResourceSystem(u64 archive_strings_count, String *archive_strings)
|
||||
{
|
||||
SharedResourceState *g = &shared_resource_state;
|
||||
Arena *perm = PermArena();
|
||||
|
||||
@ -18,7 +18,7 @@ Struct(ResourceEntryBin)
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ State
|
||||
//~ State types
|
||||
|
||||
#define NumResourceEntryBins 4096
|
||||
|
||||
@ -28,14 +28,12 @@ Struct(SharedResourceState)
|
||||
ResourceEntry *last_entry;
|
||||
u64 entries_count;
|
||||
ResourceEntryBin bins[NumResourceEntryBins];
|
||||
};
|
||||
|
||||
extern SharedResourceState shared_resource_state;
|
||||
} extern shared_resource_state;
|
||||
|
||||
////////////////////////////////
|
||||
//~ Startup
|
||||
|
||||
void InitBaseResources(u64 archive_strings_count, String *archive_strings);
|
||||
void InitResourceSystem(u64 archive_strings_count, String *archive_strings);
|
||||
|
||||
////////////////////////////////
|
||||
//~ Resource operations
|
||||
|
||||
@ -49,7 +49,7 @@ Lock LockSpinE(Mutex *m, i32 spin)
|
||||
}
|
||||
else
|
||||
{
|
||||
FutexYield(&m->v, &v, 4, I64Max);
|
||||
FutexYieldNeq(&m->v, &v, 4);
|
||||
spin_cnt = 0;
|
||||
}
|
||||
}
|
||||
@ -96,7 +96,7 @@ Lock LockSpinS(Mutex *m, i32 spin)
|
||||
}
|
||||
else
|
||||
{
|
||||
FutexYield(&m->v, &v, 4, I64Max);
|
||||
FutexYieldNeq(&m->v, &v, 4);
|
||||
spin_cnt = 0;
|
||||
}
|
||||
}
|
||||
@ -131,7 +131,7 @@ void Unlock(Lock *l)
|
||||
{
|
||||
Atomic32FetchAdd(&m->v, -1);
|
||||
}
|
||||
FutexWake(&m->v, I32Max);
|
||||
FutexWakeNeq(&m->v);
|
||||
ZeroStruct(l);
|
||||
}
|
||||
|
||||
@ -139,11 +139,6 @@ void Unlock(Lock *l)
|
||||
//~ Condition variable
|
||||
|
||||
void YieldOnCv(Cv *cv, Lock *l)
|
||||
{
|
||||
YieldOnCvTime(cv, l, I64Max);
|
||||
}
|
||||
|
||||
void YieldOnCvTime(Cv *cv, Lock *l, i64 timeout_ns)
|
||||
{
|
||||
u64 old_wake_gen = Atomic64Fetch(&cv->wake_gen);
|
||||
Mutex *mutex = l->mutex;
|
||||
@ -151,7 +146,7 @@ void YieldOnCvTime(Cv *cv, Lock *l, i64 timeout_ns)
|
||||
{
|
||||
Unlock(l);
|
||||
{
|
||||
FutexYield(&cv->wake_gen, &old_wake_gen, sizeof(old_wake_gen), timeout_ns);
|
||||
FutexYieldNeq(&cv->wake_gen, &old_wake_gen, sizeof(old_wake_gen));
|
||||
}
|
||||
if (exclusive)
|
||||
{
|
||||
@ -164,10 +159,10 @@ void YieldOnCvTime(Cv *cv, Lock *l, i64 timeout_ns)
|
||||
}
|
||||
}
|
||||
|
||||
void SignalCv(Cv *cv, i32 count)
|
||||
void SignalCv(Cv *cv)
|
||||
{
|
||||
Atomic64FetchAdd(&cv->wake_gen, 1);
|
||||
FutexWake(&cv->wake_gen, count);
|
||||
FutexWakeNeq(&cv->wake_gen);
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
@ -184,7 +179,7 @@ void AddCounter(Counter *counter, i64 x)
|
||||
i64 new_v = old_v + x;
|
||||
if (old_v > 0 && new_v <= 0)
|
||||
{
|
||||
FutexWake(&counter->v, I32Max);
|
||||
FutexWakeNeq(&counter->v);
|
||||
}
|
||||
}
|
||||
|
||||
@ -193,7 +188,42 @@ void YieldOnCounter(Counter *counter)
|
||||
i64 v = Atomic64Fetch(&counter->v);
|
||||
while (v > 0)
|
||||
{
|
||||
FutexYield(&counter->v, &v, sizeof(v), I64Max);
|
||||
FutexYieldNeq(&counter->v, &v, sizeof(v));
|
||||
v = Atomic64Fetch(&counter->v);
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
//~ Fence
|
||||
|
||||
u64 FetchFence(Fence *fence)
|
||||
{
|
||||
return Atomic64Fetch(&fence->v.v);
|
||||
}
|
||||
|
||||
u64 FetchSetFence(Fence *fence, u64 x)
|
||||
{
|
||||
u64 fetch = Atomic64FetchSet(&fence->v.v, x);
|
||||
if (x > fetch)
|
||||
{
|
||||
FutexWakeGte(&fence->v.v);
|
||||
}
|
||||
return fetch;
|
||||
}
|
||||
|
||||
u64 FetchAddFence(Fence *fence, u64 x)
|
||||
{
|
||||
u64 fetch = Atomic64FetchAdd(&fence->v.v, x);
|
||||
FutexWakeGte(&fence->v.v);
|
||||
return fetch;
|
||||
}
|
||||
|
||||
void YieldOnFence(Fence *fence, u64 target)
|
||||
{
|
||||
u64 v = Atomic64Fetch(&fence->v.v);
|
||||
while (v < target)
|
||||
{
|
||||
FutexYieldGte(&fence->v.v, &v, sizeof(v));
|
||||
v = Atomic64Fetch(&fence->v.v);
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
|
||||
#define DefaultMutexSpin 4000
|
||||
|
||||
AlignedStruct(Mutex, 64)
|
||||
AlignedStruct(Mutex, CachelineSize)
|
||||
{
|
||||
/* Bit 31 = Exclusive lock is held
|
||||
* Bit 30 = Exclusive lock is pending
|
||||
@ -13,13 +13,9 @@ AlignedStruct(Mutex, 64)
|
||||
|
||||
#if RtcIsEnabled
|
||||
Atomic32 exclusive_fiber_id;
|
||||
u8 _pad[56];
|
||||
#else
|
||||
u8 _pad[60];
|
||||
#endif
|
||||
};
|
||||
StaticAssert(sizeof(Mutex) == 64); /* Padding validation */
|
||||
StaticAssert(alignof(Mutex) == 64); /* Prevent false sharing */
|
||||
StaticAssert(alignof(Mutex) == CachelineSize && sizeof(Mutex) % CachelineSize == 0);
|
||||
|
||||
Struct(Lock)
|
||||
{
|
||||
@ -30,24 +26,28 @@ Struct(Lock)
|
||||
////////////////////////////////
|
||||
//~ Condition variable types
|
||||
|
||||
AlignedStruct(Cv, 64)
|
||||
AlignedStruct(Cv, CachelineSize)
|
||||
{
|
||||
Atomic64 wake_gen;
|
||||
u8 _pad[56];
|
||||
};
|
||||
StaticAssert(sizeof(Cv) == 64); /* Padding validation */
|
||||
StaticAssert(alignof(Cv) == 64); /* Prevent false sharing */
|
||||
StaticAssert(alignof(Cv) == CachelineSize && sizeof(Cv) % CachelineSize == 0);
|
||||
|
||||
////////////////////////////////
|
||||
//~ Counter types
|
||||
|
||||
AlignedStruct(Counter, 64)
|
||||
AlignedStruct(Counter, CachelineSize)
|
||||
{
|
||||
Atomic64 v;
|
||||
u8 _pad[56];
|
||||
};
|
||||
StaticAssert(sizeof(Counter) == 64); /* Padding validation */
|
||||
StaticAssert(alignof(Counter) == 64); /* Prevent false sharing */
|
||||
StaticAssert(alignof(Counter) == CachelineSize && sizeof(Counter) % CachelineSize == 0);
|
||||
|
||||
////////////////////////////////
|
||||
//~ Fence types
|
||||
|
||||
Struct(Fence)
|
||||
{
|
||||
Atomic64Padded v;
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ Mutex operations
|
||||
@ -73,8 +73,7 @@ void Unlock(Lock *lock);
|
||||
//~ Condition variable operations
|
||||
|
||||
void YieldOnCv(Cv *cv, Lock *lock);
|
||||
void YieldOnCvTime(Cv *cv, Lock *l, i64 timeout_ns);
|
||||
void SignalCv(Cv *cv, i32 count);
|
||||
void SignalCv(Cv *cv);
|
||||
|
||||
////////////////////////////////
|
||||
//~ Counter operations
|
||||
@ -82,3 +81,12 @@ void SignalCv(Cv *cv, i32 count);
|
||||
i64 ValueFromCounter(Counter *counter);
|
||||
void AddCounter(Counter *counter, i64 x);
|
||||
void YieldOnCounter(Counter *counter);
|
||||
|
||||
////////////////////////////////
|
||||
//~ Fence operations
|
||||
|
||||
u64 FetchFence(Fence *fence);
|
||||
u64 FetchSetFence(Fence *fence, u64 x);
|
||||
u64 FetchAddFence(Fence *fence, u64 x);
|
||||
|
||||
void YieldOnFence(Fence *fence, u64 target);
|
||||
|
||||
@ -44,6 +44,15 @@ StringList GetCommandLineArgs(void)
|
||||
return result;
|
||||
}
|
||||
|
||||
void Echo(String msg)
|
||||
{
|
||||
HANDLE console_handle = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
if (console_handle != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
WriteFile(console_handle, msg.text, msg.len, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
b32 Panic(String msg)
|
||||
{
|
||||
char msg_cstr[4096];
|
||||
@ -73,6 +82,20 @@ b32 IsRunningInDebugger(void)
|
||||
return IsDebuggerPresent();
|
||||
}
|
||||
|
||||
i64 TimeNs(void)
|
||||
{
|
||||
struct W32_SharedState *g = &W32_shared_state;
|
||||
LARGE_INTEGER qpc;
|
||||
QueryPerformanceCounter(&qpc);
|
||||
i64 result = (qpc.QuadPart - g->timer_start_qpc) * g->ns_per_qpc;
|
||||
return result;
|
||||
}
|
||||
|
||||
u32 GetNumHardwareThreads(void)
|
||||
{
|
||||
return GetActiveProcessorCount(ALL_PROCESSOR_GROUPS);
|
||||
}
|
||||
|
||||
void TrueRand(String buffer)
|
||||
{
|
||||
BCryptGenRandom(BCRYPT_RNG_ALG_HANDLE, (u8 *)buffer.text, buffer.len, 0);
|
||||
@ -216,20 +239,35 @@ i32 W32_Main(void)
|
||||
g->exit_begin_event = CreateEventW(0, 1, 0, 0);
|
||||
g->exit_end_event = CreateEventW(0, 1, 0, 0);
|
||||
|
||||
/* Query performance frequency */
|
||||
{
|
||||
LARGE_INTEGER qpf;
|
||||
QueryPerformanceFrequency(&qpf);
|
||||
g->ns_per_qpc = 1000000000 / qpf.QuadPart;
|
||||
}
|
||||
{
|
||||
LARGE_INTEGER qpc;
|
||||
QueryPerformanceCounter(&qpc);
|
||||
g->timer_start_qpc = qpc.QuadPart;
|
||||
}
|
||||
|
||||
g->main_thread_id = GetCurrentThreadId();
|
||||
SetThreadDescription(GetCurrentThread(), L"Main thread");
|
||||
|
||||
/* Query system info */
|
||||
GetSystemInfo(&g->info);
|
||||
|
||||
/* Startup workers */
|
||||
InitJobWorkers();
|
||||
/* Init job system */
|
||||
InitJobSystem();
|
||||
|
||||
/* Init futex system */
|
||||
InitFutexSystem();
|
||||
|
||||
/* Init resources */
|
||||
{
|
||||
W32_FindEmbeddedDataCtx ctx = ZI;
|
||||
EnumResourceNamesW(0, RT_RCDATA, &W32_FindEmbeddedRcData, (LONG_PTR)&ctx);
|
||||
InitBaseResources(ctx.embedded_strings_count, ctx.embedded_strings);
|
||||
InitResourceSystem(ctx.embedded_strings_count, ctx.embedded_strings);
|
||||
}
|
||||
|
||||
//- App startup
|
||||
@ -237,7 +275,7 @@ i32 W32_Main(void)
|
||||
/* Startup layers */
|
||||
if (!Atomic32Fetch(&g->panicking))
|
||||
{
|
||||
RunJob(1, W32_StartupLayersJob, JobPool_Hyper, JobPriority_High, 0, 0);
|
||||
RunJob(W32_StartupLayersJob);
|
||||
}
|
||||
|
||||
/* Wait for startup end or panic */
|
||||
@ -265,7 +303,7 @@ i32 W32_Main(void)
|
||||
/* Run exit callbacks job */
|
||||
if (!Atomic32Fetch(&g->panicking))
|
||||
{
|
||||
RunJob(1, W32_ShutdownLayersJob, JobPool_Hyper, JobPriority_High, 0, 0);
|
||||
RunJob(W32_ShutdownLayersJob);
|
||||
}
|
||||
|
||||
/* Wait for exit end or panic */
|
||||
@ -293,19 +331,13 @@ i32 W32_Main(void)
|
||||
|
||||
#if CrtlibIsEnabled
|
||||
# if IsConsoleApp
|
||||
int main(char **argc, int argv)
|
||||
int main(UNUSED char **argc, UNUSED int argv)
|
||||
{
|
||||
LAX argc;
|
||||
LAX argv;
|
||||
return W32_Main();
|
||||
}
|
||||
# else
|
||||
int CALLBACK wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev_instance, _In_ LPWSTR cmdline_wstr, _In_ int show_code)
|
||||
int CALLBACK wWinMain(UNUSED _In_ HINSTANCE instance, UNUSED _In_opt_ HINSTANCE prev_instance, UNUSED _In_ LPWSTR cmdline_wstr, UNUSED _In_ int show_code)
|
||||
{
|
||||
LAX instance;
|
||||
LAX prev_instance;
|
||||
LAX cmdline_wstr;
|
||||
LAX show_code;
|
||||
return W32_Main();
|
||||
}
|
||||
# endif /* IsConsoleApp */
|
||||
|
||||
@ -20,7 +20,7 @@ Struct(W32_FindEmbeddedDataCtx)
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ State
|
||||
//~ State types
|
||||
|
||||
#define W32_MaxOnExitFuncs 4096
|
||||
|
||||
@ -31,6 +31,9 @@ Struct(W32_SharedState)
|
||||
Atomic32 shutdown;
|
||||
Atomic32 exit_code;
|
||||
|
||||
i64 timer_start_qpc;
|
||||
i64 ns_per_qpc;
|
||||
|
||||
//- Application control flow
|
||||
Atomic32 panicking;
|
||||
wchar_t panic_wstr[4096];
|
||||
@ -42,9 +45,7 @@ Struct(W32_SharedState)
|
||||
//- Exit funcs
|
||||
Atomic32 num_exit_funcs;
|
||||
ExitFunc *exit_funcs[W32_MaxOnExitFuncs];
|
||||
};
|
||||
|
||||
extern W32_SharedState W32_shared_state;
|
||||
} extern W32_shared_state;
|
||||
|
||||
////////////////////////////////
|
||||
//~ Embedded data initialization
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -32,42 +32,14 @@ typedef W32_ThreadDef(W32_ThreadFunc, data);
|
||||
|
||||
Struct(W32_Thread)
|
||||
{
|
||||
String thread_name;
|
||||
W32_ThreadFunc *entry_point;
|
||||
void *thread_data;
|
||||
char thread_name_cstr[256];
|
||||
wchar_t thread_name_wstr[256];
|
||||
void *thread_udata;
|
||||
i32 profiler_group;
|
||||
|
||||
W32_Thread *next;
|
||||
W32_Thread *prev;
|
||||
|
||||
HANDLE handle;
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ Wait list types
|
||||
|
||||
AlignedStruct(W32_WaitList, 64)
|
||||
{
|
||||
u64 value;
|
||||
i16 first_waiter;
|
||||
i16 last_waiter;
|
||||
i32 num_waiters;
|
||||
W32_WaitList *next_in_bin;
|
||||
W32_WaitList *prev_in_bin;
|
||||
u8 pad[32];
|
||||
};
|
||||
StaticAssert(alignof(W32_WaitList) == 64); /* Avoid false sharing */
|
||||
|
||||
AlignedStruct(W32_WaitBin, 64)
|
||||
{
|
||||
W32_WaitList *first_wait_list;
|
||||
W32_WaitList *last_wait_list;
|
||||
W32_WaitList *first_free_wait_list;
|
||||
TicketMutex lock;
|
||||
};
|
||||
StaticAssert(alignof(W32_WaitBin) == 64); /* Avoid false sharing */
|
||||
|
||||
////////////////////////////////
|
||||
//~ Fiber types
|
||||
|
||||
@ -76,211 +48,140 @@ StaticAssert(alignof(W32_WaitBin) == 64); /* Avoid false sharing */
|
||||
#define W32_FiberNameSuffixCstr "]"
|
||||
#define W32_FiberNameMaxSize 64
|
||||
|
||||
//- Yield param
|
||||
Enum(W32_YieldKind)
|
||||
{
|
||||
W32_YieldKind_None,
|
||||
W32_YieldKind_Done,
|
||||
W32_YieldKind_Wait,
|
||||
|
||||
W32_YieldKind_Count
|
||||
};
|
||||
|
||||
Struct(W32_YieldParam)
|
||||
{
|
||||
W32_YieldKind kind;
|
||||
union
|
||||
{
|
||||
struct
|
||||
{
|
||||
volatile void *addr;
|
||||
void *cmp;
|
||||
u32 size;
|
||||
i64 timeout_ns;
|
||||
} wait;
|
||||
};
|
||||
};
|
||||
|
||||
//- Fiber
|
||||
AlignedStruct(W32_Fiber, 64)
|
||||
AlignedStruct(W32_Fiber, CachelineSize)
|
||||
{
|
||||
/* ---------------------------------------------------- */
|
||||
void *addr; /* 08 bytes */
|
||||
/* ---------------------------------------------------- */
|
||||
char *name_cstr; /* 08 bytes */
|
||||
/* ---------------------------------------------------- */
|
||||
Atomic32 wake_lock; /* 04 bytes (4 byte alignment) */
|
||||
Atomic8 is_suspending; /* 01 bytes */
|
||||
u8 _pad0[1]; /* 01 bytes (padding) */
|
||||
i16 id; /* 02 bytes */
|
||||
i16 pool; /* 02 bytes */
|
||||
i16 parent_id; /* 02 bytes */
|
||||
/* ---------------------------------------------------- */
|
||||
u64 wait_addr; /* 08 bytes */
|
||||
struct W32_Task *task; /* 08 bytes */
|
||||
/* ---------------------------------------------------- */
|
||||
u64 wait_time; /* 08 bytes */
|
||||
/* ---------------------------------------------------- */
|
||||
i16 next_addr_waiter; /* 02 bytes */
|
||||
i16 prev_addr_waiter; /* 02 bytes */
|
||||
i16 next_time_waiter; /* 02 bytes */
|
||||
i16 prev_time_waiter; /* 02 bytes */
|
||||
/* ---------------------------------------------------- */
|
||||
GenericJobDesc *job_desc; /* 08 bytes */
|
||||
/* ---------------------------------------------------- */
|
||||
u8 _pad0[8]; /* 08 bytes (padding) */
|
||||
/* ---------------------------------------------------- */
|
||||
/* -------------------- Cache line -------------------- */
|
||||
/* ---------------------------------------------------- */
|
||||
i32 job_id; /* 04 bytes */
|
||||
i16 job_pool; /* 02 bytes */
|
||||
i16 job_priority; /* 02 bytes */
|
||||
/* ---------------------------------------------------- */
|
||||
W32_YieldParam *yield_param; /* 08 bytes */
|
||||
/* ---------------------------------------------------- */
|
||||
u8 _pad1[48]; /* 48 bytes (padding) */
|
||||
|
||||
#if 0
|
||||
u8 _pad1[32]; /* 32 bytes (padding) */
|
||||
#endif
|
||||
};
|
||||
StaticAssert(sizeof(W32_Fiber) == 128); /* Padding validation (increase if necessary) */
|
||||
StaticAssert(alignof(W32_Fiber) == 64); /* Verify alignment to avoid false sharing */
|
||||
StaticAssert(offsetof(W32_Fiber, wake_lock) % 4 == 0); /* Atomic must be aligned */
|
||||
StaticAssert(alignof(W32_Fiber) == CachelineSize && sizeof(W32_Fiber) % CachelineSize == 0); /* False sharing validation */
|
||||
StaticAssert(offsetof(W32_Fiber, is_suspending) % 4 == 0); /* Atomic alignment validation */
|
||||
|
||||
////////////////////////////////
|
||||
//~ Job queue types
|
||||
//~ Job pool types
|
||||
|
||||
//- Worker ctx
|
||||
AlignedStruct(W32_WorkerCtx, 64)
|
||||
AlignedStruct(W32_WorkerCtx, CachelineSize)
|
||||
{
|
||||
JobPool pool_kind;
|
||||
i32 id;
|
||||
};
|
||||
|
||||
//- Job info
|
||||
Struct(W32_JobInfo)
|
||||
//- Task
|
||||
Struct(W32_Task)
|
||||
{
|
||||
GenericJobDesc *desc;
|
||||
|
||||
i32 count;
|
||||
i32 num_dispatched;
|
||||
i16 fiber_id; /* If the job is being resumed from a yield */
|
||||
|
||||
W32_JobInfo *next;
|
||||
W32_Task *next;
|
||||
Job *job;
|
||||
u32 task_id;
|
||||
i16 fiber_id;
|
||||
};
|
||||
|
||||
//- Job queue
|
||||
AlignedStruct(W32_JobQueue, 64)
|
||||
Struct(W32_TaskList)
|
||||
{
|
||||
TicketMutex lock;
|
||||
Arena *arena;
|
||||
|
||||
W32_JobInfo *first;
|
||||
W32_JobInfo *last;
|
||||
|
||||
W32_JobInfo *first_free;
|
||||
W32_Task *first;
|
||||
W32_Task *last;
|
||||
u64 count;
|
||||
};
|
||||
|
||||
//- Job pool
|
||||
AlignedStruct(W32_JobPool, 64)
|
||||
AlignedStruct(W32_JobPool, CachelineSize)
|
||||
{
|
||||
/* Jobs */
|
||||
W32_JobQueue job_queues[JobPriority_Count];
|
||||
|
||||
TicketMutex free_fibers_tm;
|
||||
i16 first_free_fiber_id;
|
||||
|
||||
/* Workers */
|
||||
Atomic32Padded workers_shutdown;
|
||||
Atomic64Padded num_jobs_in_queue;
|
||||
TicketMutex workers_wake_tm;
|
||||
|
||||
JobPool kind;
|
||||
i32 num_worker_threads;
|
||||
i32 thread_priority;
|
||||
u64 thread_affinity_mask;
|
||||
b32 thread_is_audio;
|
||||
Arena *worker_threads_arena;
|
||||
W32_Thread **worker_threads;
|
||||
W32_WorkerCtx *worker_contexts;
|
||||
|
||||
/* Tasks */
|
||||
AlignedBlock(CachelineSize)
|
||||
{
|
||||
TicketMutex tasks_tm;
|
||||
Atomic64Padded tasks_count;
|
||||
W32_Task *first_task;
|
||||
W32_Task *last_task;
|
||||
};
|
||||
|
||||
/* TODO: Make lists wait-free */
|
||||
|
||||
/* Free fibers */
|
||||
AlignedBlock(CachelineSize)
|
||||
{
|
||||
TicketMutex free_fibers_tm;
|
||||
i16 first_free_fiber_id;
|
||||
};
|
||||
|
||||
/* Free jobs */
|
||||
AlignedBlock(CachelineSize)
|
||||
{
|
||||
TicketMutex free_jobs_tm;
|
||||
Job *first_free_job;
|
||||
};
|
||||
|
||||
/* Free tasks */
|
||||
AlignedBlock(CachelineSize)
|
||||
{
|
||||
TicketMutex free_tasks_tm;
|
||||
W32_Task *first_free_task;
|
||||
};
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ State
|
||||
//~ State types
|
||||
|
||||
/* Assume scheduler cycle is 20hz at start to be conservative */
|
||||
#define W32_DefaultSchedulerPeriodNs 50000000
|
||||
#define W32_NumRollingSchedulerPeriods 1000
|
||||
/* Arbitrary threshold for determining when to use a looped WakeByAddressSingle vs a WakeByAddressAll to wake parked workers */
|
||||
#define W32_WakeAllWorkersThreshold 8
|
||||
|
||||
#define W32_NumWaitAddrBins 16384
|
||||
#define W32_NumWaitTimeBins 1024
|
||||
|
||||
/* Arbitrary threshold for determining when to fall back from a looped WakeByAddressSingle to WakeByAddressAll */
|
||||
#define W32_WakeAllThreshold 16
|
||||
|
||||
Struct(W32_SharedJobCtx)
|
||||
Struct(W32_SharedJobState)
|
||||
{
|
||||
i64 timer_start_qpc;
|
||||
i64 ns_per_qpc;
|
||||
Atomic32 shutdown;
|
||||
|
||||
//- Worker thread pool
|
||||
TicketMutex threads_tm;
|
||||
Arena *threads_arena;
|
||||
W32_Thread *first_thread;
|
||||
W32_Thread *last_thread;
|
||||
W32_Thread *first_free_thread;
|
||||
|
||||
//- Scheduler
|
||||
Atomic64Padded current_scheduler_cycle;
|
||||
Atomic64Padded current_scheduler_cycle_period_ns;
|
||||
|
||||
//- Fibers
|
||||
TicketMutex fibers_tm;
|
||||
i16 num_fibers;
|
||||
Arena *fiber_names_arena;
|
||||
W32_Fiber fibers[MaxFibers];
|
||||
|
||||
//- Job descs
|
||||
Arena *job_descs_arena;
|
||||
TicketMutex job_descs_tm;
|
||||
GenericJobDesc *first_free_job_desc;
|
||||
|
||||
//- Wait lists
|
||||
Atomic64Padded waiter_wake_gen;
|
||||
TicketMutex wait_lists_arena_tm;
|
||||
Arena *wait_lists_arena;
|
||||
|
||||
//- Wait tables
|
||||
W32_WaitBin wait_addr_bins[W32_NumWaitAddrBins];
|
||||
W32_WaitBin wait_time_bins[W32_NumWaitTimeBins];
|
||||
AlignedBlock(CachelineSize)
|
||||
{
|
||||
TicketMutex fibers_tm;
|
||||
i16 num_fibers;
|
||||
W32_Fiber fibers[MaxFibers];
|
||||
Arena *fiber_names_arena;
|
||||
};
|
||||
|
||||
//- Job pools
|
||||
W32_JobPool job_pools[JobPool_Count];
|
||||
};
|
||||
|
||||
extern W32_SharedJobCtx W32_shared_job_ctx;
|
||||
AlignedBlock(CachelineSize)
|
||||
{
|
||||
W32_JobPool job_pools[JobPool_Count];
|
||||
};
|
||||
} extern W32_shared_job_state;
|
||||
|
||||
////////////////////////////////
|
||||
//~ Startup
|
||||
|
||||
void StartupJobs(void);
|
||||
|
||||
////////////////////////////////
|
||||
//~ Shutdown
|
||||
|
||||
void ShutdownJobs(void);
|
||||
|
||||
////////////////////////////////
|
||||
//~ Thread operations
|
||||
|
||||
DWORD WINAPI W32_Win32ThreadProc(LPVOID vt);
|
||||
W32_Thread *W32_AcquireThread(W32_ThreadFunc *entry_point, void *thread_data, String thread_name, i32 profiler_group);
|
||||
b32 W32_TryReleaseThread(W32_Thread *thread, f32 timeout_seconds);
|
||||
void W32_WaitReleaseThread(W32_Thread *thread);
|
||||
W32_Thread *W32_StartThread(W32_ThreadFunc *entry_point, void *thread_udata, String thread_name, i32 profiler_group);
|
||||
b32 W32_TryEndThread(W32_Thread *thread, f32 timeout_seconds);
|
||||
void W32_WaitEndThread(W32_Thread *thread);
|
||||
|
||||
////////////////////////////////
|
||||
//~ Wait list operations
|
||||
//~ Pool operations
|
||||
|
||||
W32_WaitBin *W32_WaitBinFromAddr(u64 addr);
|
||||
W32_WaitBin *W32_WaitBinFromTime(u64 time);
|
||||
|
||||
void W32_WakeLockedFibers(i32 num_fibers, W32_Fiber **fibers);
|
||||
void W32_WakeByAddress(void *addr, i32 count);
|
||||
void W32_WakeByTime(u64 time);
|
||||
W32_JobPool *W32_JobPoolFromKind(JobPool pool_kind);
|
||||
|
||||
////////////////////////////////
|
||||
//~ Fiber operations
|
||||
@ -288,20 +189,15 @@ void W32_WakeByTime(u64 time);
|
||||
W32_Fiber *W32_AcquireFiber(W32_JobPool *pool);
|
||||
void W32_ReleaseFiber(W32_JobPool *pool, W32_Fiber *fiber);
|
||||
ForceInline W32_Fiber *W32_FiberFromId(i16 id);
|
||||
void W32_FiberResume(W32_Fiber *fiber);
|
||||
void W32_SwitchToFiber(W32_Fiber *fiber);
|
||||
void W32_YieldFiber(W32_Fiber *fiber, W32_Fiber *parent_fiber);
|
||||
|
||||
////////////////////////////////
|
||||
//~ Fiber entry
|
||||
|
||||
void W32_FiberEntryPoint(void *id_ptr);
|
||||
void W32_FiberEntryPoint(void *wi32_fiber_data);
|
||||
|
||||
////////////////////////////////
|
||||
//~ Job worker entry
|
||||
|
||||
W32_ThreadDef(W32_JobWorkerEntryFunc, worker_ctx_arg);
|
||||
|
||||
////////////////////////////////
|
||||
//~ Job scheduler entry
|
||||
|
||||
W32_ThreadDef(W32_JobSchedulerEntryFunc, _);
|
||||
|
||||
@ -99,14 +99,12 @@ Struct(D_TextParams)
|
||||
})
|
||||
|
||||
////////////////////////////////
|
||||
//~ State
|
||||
//~ State types
|
||||
|
||||
Struct(D_SharedState)
|
||||
{
|
||||
GPU_Resource *solid_white_texture;
|
||||
};
|
||||
|
||||
extern D_SharedState D_shared_state;
|
||||
} extern D_shared_state;
|
||||
|
||||
////////////////////////////////
|
||||
//~ Startup
|
||||
|
||||
@ -122,11 +122,14 @@ AC_Asset *F_LoadAsset(Resource resource, f32 point_size, b32 wait)
|
||||
if (is_first_touch)
|
||||
{
|
||||
AC_MarkLoading(asset);
|
||||
F_LoadJob_Desc *desc = PushJobDesc(F_LoadJob, .pool = JobPool_Background, .priority = JobPriority_Low);
|
||||
desc->sig->asset = asset;
|
||||
desc->sig->resource = resource;
|
||||
desc->sig->point_size = point_size;
|
||||
RunJobEx((GenericJobDesc *)desc);
|
||||
{
|
||||
Job *job = OpenJob(F_LoadJob, JobPool_Background);
|
||||
F_LoadJob_Sig *sig = PushStruct(job->arena, F_LoadJob_Sig);
|
||||
sig->asset = asset;
|
||||
sig->resource = resource;
|
||||
sig->point_size = point_size;
|
||||
CloseJob(job);
|
||||
}
|
||||
if (wait)
|
||||
{
|
||||
AC_YieldOnAssetReady(asset);
|
||||
|
||||
@ -145,7 +145,7 @@ Struct(GPU_D12_Swapchain)
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ State
|
||||
//~ State types
|
||||
|
||||
Struct(GPU_D12_FiberState)
|
||||
{
|
||||
@ -156,7 +156,7 @@ Struct(GPU_D12_FiberState)
|
||||
Struct(GPU_D12_SharedState)
|
||||
{
|
||||
i32 _;
|
||||
};
|
||||
} extern GPU_D12_shared_state;
|
||||
|
||||
////////////////////////////////
|
||||
//~ State operations
|
||||
|
||||
@ -62,16 +62,10 @@
|
||||
////////////////////////////////
|
||||
//~ Util
|
||||
|
||||
/* TODO: Move this to OS layer */
|
||||
void Echo(String msg)
|
||||
{
|
||||
OS_Echo(msg);
|
||||
}
|
||||
|
||||
void EchoLine(String msg)
|
||||
{
|
||||
OS_Echo(msg);
|
||||
OS_Echo(Lit("\n"));
|
||||
Echo(msg);
|
||||
Echo(Lit("\n"));
|
||||
}
|
||||
|
||||
Struct(LineCol)
|
||||
@ -535,9 +529,12 @@ JobDef(StepJob, sig, id)
|
||||
++i;
|
||||
}
|
||||
}
|
||||
Counter counter = ZI;
|
||||
RunJob(shader_entries_count, RunCommandJob, JobPool_Hyper, JobPriority_Normal, &counter, .cmds = compile_cmds, .results = compile_results);
|
||||
YieldOnCounter(&counter);
|
||||
JobCounter counter = ZI;
|
||||
RunJob(RunCommandJob,
|
||||
.count = shader_entries_count,
|
||||
.counter = &counter,
|
||||
.sig = { .cmds = compile_cmds, .results = compile_results });
|
||||
YieldOnJobs(&counter);
|
||||
|
||||
//- Process shader compilation results
|
||||
{
|
||||
@ -569,9 +566,9 @@ JobDef(StepJob, sig, id)
|
||||
arc_params.arc_store = shader_store_name;
|
||||
arc_params.arc_dir = shader_store_name;
|
||||
|
||||
Counter counter = ZI;
|
||||
RunJob(1, StepJob, JobPool_Hyper, JobPriority_Normal, &counter, .params = &arc_params, .results = &arc_results);
|
||||
YieldOnCounter(&counter);
|
||||
JobCounter counter = ZI;
|
||||
RunJob(StepJob, .counter = &counter, .sig.params = &arc_params, .sig.results = &arc_results);
|
||||
YieldOnJobs(&counter);
|
||||
InheritStepResults(arena, result, 1, &arc_results);
|
||||
}
|
||||
}
|
||||
@ -645,9 +642,13 @@ JobDef(StepJob, sig, id)
|
||||
++i;
|
||||
}
|
||||
}
|
||||
Counter counter = ZI;
|
||||
RunJob(dir_embeds_count, StepJob, JobPool_Hyper, JobPriority_Normal, &counter, .params = arc_params_array, .results = arc_results_array);
|
||||
YieldOnCounter(&counter);
|
||||
JobCounter counter = ZI;
|
||||
RunJob(StepJob,
|
||||
.counter = &counter,
|
||||
.count = dir_embeds_count,
|
||||
.sig.params = arc_params_array,
|
||||
.sig.results = arc_results_array);
|
||||
YieldOnJobs(&counter);
|
||||
InheritStepResults(arena, result, dir_embeds_count, arc_results_array);
|
||||
}
|
||||
}
|
||||
@ -804,7 +805,8 @@ JobDef(StepJob, sig, id)
|
||||
////////////////////////////////
|
||||
//~ Startup
|
||||
|
||||
void StartupMeta(void)
|
||||
JobDecl(BuildJob, EmptySig);
|
||||
JobDef(BuildJob, _, __)
|
||||
{
|
||||
Arena *arena = PermArena();
|
||||
M_ErrorList errors = ZI;
|
||||
@ -991,9 +993,9 @@ void StartupMeta(void)
|
||||
resource_params->compiler_params = cp;
|
||||
resource_params->flattened = flattened;
|
||||
|
||||
Counter step_counter = ZI;
|
||||
RunJob(countof(params_array), StepJob, JobPool_Hyper, JobPriority_Normal, &step_counter, .params = params_array, .results = results_array);
|
||||
YieldOnCounter(&step_counter);
|
||||
JobCounter step_counter = ZI;
|
||||
RunJob(StepJob, .count = countof(params_array), .counter = &step_counter, .sig.params = params_array, .sig.results = results_array);
|
||||
YieldOnJobs(&step_counter);
|
||||
|
||||
////////////////////////////////
|
||||
//~ Process compile step results
|
||||
@ -1106,5 +1108,5 @@ void StartupMeta(void)
|
||||
void StartupLayers(void)
|
||||
{
|
||||
OS_Startup();
|
||||
StartupMeta();
|
||||
RunJob(BuildJob, .pool = JobPool_Hyper);
|
||||
}
|
||||
|
||||
@ -53,5 +53,4 @@ void OS_Rm(String path);
|
||||
////////////////////////////////
|
||||
//~ @hookdecl Shell operations
|
||||
|
||||
void OS_Echo(String msg);
|
||||
OS_CommandResult OS_RunCommand(Arena *arena, String cmd);
|
||||
|
||||
@ -208,15 +208,6 @@ void OS_Rm(String path)
|
||||
////////////////////////////////
|
||||
//~ @hookdef Shell hooks
|
||||
|
||||
void OS_Echo(String msg)
|
||||
{
|
||||
HANDLE console_handle = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
if (console_handle != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
WriteFile(console_handle, msg.text, msg.len, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
OS_CommandResult OS_RunCommand(Arena *arena, String cmd)
|
||||
{
|
||||
TempArena scratch = BeginScratch(arena);
|
||||
|
||||
@ -75,7 +75,7 @@ Struct(MIX_Track){
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ State
|
||||
//~ State types
|
||||
|
||||
Struct(MIX_SharedState)
|
||||
{
|
||||
@ -91,9 +91,7 @@ Struct(MIX_SharedState)
|
||||
MIX_Track *track_last_playing;
|
||||
u64 track_playing_count;
|
||||
MIX_Track *track_first_free;
|
||||
};
|
||||
|
||||
extern MIX_SharedState M_shared_state;
|
||||
} extern M_shared_state;
|
||||
|
||||
////////////////////////////////
|
||||
//~ Startup
|
||||
|
||||
@ -279,12 +279,12 @@ Enum(P_MessageBoxKind)
|
||||
void P_Startup(void);
|
||||
|
||||
////////////////////////////////
|
||||
//~ @hookdecl Time helper operations
|
||||
//~ @hookdecl Time helper hooks
|
||||
|
||||
P_DateTime P_LocalTime(void);
|
||||
|
||||
////////////////////////////////
|
||||
//~ @hookdecl File system operations
|
||||
//~ @hookdecl File system hooks
|
||||
|
||||
/* NOTE: File paths use forward slash '/' as delimiter */
|
||||
|
||||
@ -310,14 +310,14 @@ u64 P_GetFileSize(P_File file);
|
||||
P_FileTime P_GetFileTime(P_File file);
|
||||
|
||||
////////////////////////////////
|
||||
//~ @hookdecl File map operations
|
||||
//~ @hookdecl File map hooks
|
||||
|
||||
P_FileMap P_OpenFileMap(P_File file);
|
||||
void P_CloseFileMap(P_FileMap map);
|
||||
String P_GetFileMapData(P_FileMap map);
|
||||
|
||||
////////////////////////////////
|
||||
//~ @hookdecl Window operations
|
||||
//~ @hookdecl Window hooks
|
||||
|
||||
P_Window *P_AcquireWindow(void);
|
||||
void P_ReleaseWindow(P_Window *window);
|
||||
@ -342,7 +342,7 @@ Vec2I32 P_GetWindowMonitorSize(P_Window *window);
|
||||
u64 P_GetInternalWindowHandle(P_Window *window);
|
||||
|
||||
////////////////////////////////
|
||||
//~ @hookdecl Address helpers
|
||||
//~ @hookdecl Address helper hooks
|
||||
|
||||
P_Address P_AddressFromString(String str);
|
||||
P_Address P_AddressFromIpPortCstr(char *ip_cstr, char *port_cstr);
|
||||
@ -351,7 +351,7 @@ String P_StringFromAddress(Arena *arena, P_Address address);
|
||||
b32 P_AddressIsEqual(P_Address a, P_Address b);
|
||||
|
||||
////////////////////////////////
|
||||
//~ @hookdecl Sock operations
|
||||
//~ @hookdecl Sock hooks
|
||||
|
||||
P_Sock *P_AcquireSock(u16 listen_port, u64 sndbuf_size, u64 rcvbuf_size);
|
||||
void P_ReleaseSock(P_Sock *sock);
|
||||
@ -359,14 +359,20 @@ P_SockReadResult P_ReadSock(Arena *arena, P_Sock *sock);
|
||||
void P_WriteSock(P_Sock *sock, P_Address address, String data);
|
||||
|
||||
////////////////////////////////
|
||||
//~ @hookdecl Utils
|
||||
//~ @hookdecl Util hooks
|
||||
|
||||
void P_MessageBox(P_MessageBoxKind kind, String message);
|
||||
void P_SetClipboardText(String str);
|
||||
String P_GetClipboardText(Arena *arena);
|
||||
|
||||
////////////////////////////////
|
||||
//~ @hookdecl Sleep
|
||||
//~ @hookdecl Timer hooks
|
||||
|
||||
Fence *P_GetTimerFence();
|
||||
i64 P_GetCurrentTimerPeriodNs();
|
||||
|
||||
////////////////////////////////
|
||||
//~ @hookdecl Sleep hooks
|
||||
|
||||
void P_SleepPrecise(i64 sleep_time_ns);
|
||||
void P_SleepFrame(i64 last_frame_time_ns, i64 target_dt_ns);
|
||||
|
||||
@ -1,6 +1,3 @@
|
||||
////////////////////////////////
|
||||
//~ State
|
||||
|
||||
P_SharedLogState P_shared_log_state = ZI;
|
||||
|
||||
////////////////////////////////
|
||||
@ -184,9 +181,9 @@ void P_Log_(i32 level, String msg)
|
||||
FmtString(msg)
|
||||
);
|
||||
#else
|
||||
String msg_formatted = FormatString(
|
||||
String msg_formatted = StringF(
|
||||
scratch.arena,
|
||||
Lit("[%F:%F:%F.%F] |%F| [%F] %F"),
|
||||
"[%F:%F:%F.%F] |%F| [%F] %F",
|
||||
|
||||
/* Time */
|
||||
FmtUintZ(datetime.hour, 2),
|
||||
|
||||
@ -56,9 +56,8 @@ Struct(LogEventCallback)
|
||||
#define P_LogLevel_Count 6
|
||||
|
||||
////////////////////////////////
|
||||
//~ State
|
||||
//~ State types
|
||||
|
||||
//- Shared context
|
||||
Struct(P_SharedLogState)
|
||||
{
|
||||
Atomic32 initialized;
|
||||
@ -70,11 +69,11 @@ Struct(P_SharedLogState)
|
||||
|
||||
P_File file;
|
||||
b32 file_valid;
|
||||
};
|
||||
} P_shared_log_state;
|
||||
|
||||
extern P_SharedLogState P_shared_log_state;
|
||||
////////////////////////////////
|
||||
//~ Log level types
|
||||
|
||||
//-- Log level settings
|
||||
Struct(P_LogLevelSettings)
|
||||
{
|
||||
String shorthand;
|
||||
|
||||
@ -99,6 +99,9 @@ void P_Startup(void)
|
||||
//- Init winsock
|
||||
WSAStartup(MAKEWORD(2, 2), &g->wsa_data);
|
||||
g->socks_arena = AcquireArena(Gibi(64));
|
||||
|
||||
//- Init timer
|
||||
RunJob(P_W32_TimerJob);
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
@ -181,7 +184,7 @@ P_W32_Window *P_W32_AcquireWindow(void)
|
||||
* created and receive a HWND, because on Windows a the event proc must run on
|
||||
* the same thread that created the window. */
|
||||
AddCounter(&window->ready_fence, 1);
|
||||
window->window_thread = W32_AcquireThread(&P_W32_WindowThreadEntryFunc, window, Lit("Window thread"), PROF_THREAD_GROUP_WINDOW);
|
||||
window->window_thread = W32_StartThread(&P_W32_WindowThreadEntryFunc, window, Lit("Window thread"), PROF_THREAD_GROUP_WINDOW);
|
||||
YieldOnCounter(&window->ready_fence);
|
||||
|
||||
return window;
|
||||
@ -193,7 +196,7 @@ void P_W32_ReleaseWindow(P_W32_Window *window)
|
||||
Atomic32FetchSet(&window->shutdown, 1);
|
||||
P_W32_SharedState *g = &P_W32_shared_state;
|
||||
P_W32_WakeWindow(window);
|
||||
W32_WaitReleaseThread(window->window_thread);
|
||||
W32_WaitEndThread(window->window_thread);
|
||||
|
||||
Lock lock = LockE(&g->windows_mutex);
|
||||
{
|
||||
@ -862,6 +865,69 @@ P_Address P_W32_PlatformAddressFromWin32Address(P_W32_Address ws_addr)
|
||||
return result;
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
//~ Timer job
|
||||
|
||||
JobDef(P_W32_TimerJob, _, __)
|
||||
{
|
||||
P_W32_SharedState *g = &P_W32_shared_state;
|
||||
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);
|
||||
|
||||
/* Create high resolution timer */
|
||||
HANDLE timer = CreateWaitableTimerExW(0, 0, CREATE_WAITABLE_TIMER_HIGH_RESOLUTION, TIMER_ALL_ACCESS);
|
||||
if (!timer)
|
||||
{
|
||||
Panic(Lit("Failed to create high resolution timer"));
|
||||
}
|
||||
|
||||
i32 periods_index = 0;
|
||||
i64 periods[P_W32_NumRollingTimerPeriods] = ZI;
|
||||
for (i32 i = 0; i < (i32)countof(periods); ++i)
|
||||
{
|
||||
periods[i] = P_W32_DefaultTimerPeriodNs;
|
||||
}
|
||||
|
||||
i64 last_cycle_ns = 0;
|
||||
/* FIXME: shutdown */
|
||||
for (;;)
|
||||
{
|
||||
__profn("Job scheduler cycle");
|
||||
{
|
||||
__profn("Job scheduler wait");
|
||||
LARGE_INTEGER due = ZI;
|
||||
due.QuadPart = -1;
|
||||
//due.QuadPart = -10000;
|
||||
//due.QuadPart = -32000;
|
||||
//due.QuadPart = -12000;
|
||||
//due.QuadPart = -8000;
|
||||
SetWaitableTimerEx(timer, &due, 0, 0, 0, 0, 0);
|
||||
WaitForSingleObject(timer, INFINITE);
|
||||
}
|
||||
i64 now_ns = TimeNs();
|
||||
i64 period_ns = last_cycle_ns == 0 ? P_W32_DefaultTimerPeriodNs : now_ns - last_cycle_ns;
|
||||
last_cycle_ns = now_ns;
|
||||
|
||||
/* Calculate mean period */
|
||||
{
|
||||
periods[periods_index++] = period_ns;
|
||||
if (periods_index == countof(periods))
|
||||
{
|
||||
periods_index = 0;
|
||||
}
|
||||
f64 periods_sum_ns = 0;
|
||||
for (i32 i = 0; i < (i32)countof(periods); ++i)
|
||||
{
|
||||
periods_sum_ns += (f64)periods[i];
|
||||
}
|
||||
f64 mean_ns = periods_sum_ns / (f64)countof(periods);
|
||||
Atomic64FetchSet(&g->average_timer_period_ns.v, RoundF64ToI64(mean_ns));
|
||||
}
|
||||
|
||||
/* Update fence */
|
||||
FetchSetFence(&g->timer_fence, now_ns);
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
//~ @hookdef Time hooks
|
||||
|
||||
@ -1083,7 +1149,7 @@ String P_ReadFile(Arena *arena, P_File file)
|
||||
{
|
||||
/* ReadFile returns non-zero on success */
|
||||
/* TODO: error checking */
|
||||
AlignArena(arena, 16);
|
||||
PushAlign(arena, CachelineSize);
|
||||
s.text = PushStructsNoZero(arena, u8, size);
|
||||
LAX ReadFile(
|
||||
(HANDLE)file.handle,
|
||||
@ -1774,6 +1840,21 @@ String P_GetClipboardText(Arena *arena)
|
||||
return result;
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
//~ @hookdecl Timer hooks
|
||||
|
||||
Fence *P_GetTimerFence()
|
||||
{
|
||||
P_W32_SharedState *g = &P_W32_shared_state;
|
||||
return &g->timer_fence;
|
||||
}
|
||||
|
||||
i64 P_GetCurrentTimerPeriodNs()
|
||||
{
|
||||
P_W32_SharedState *g = &P_W32_shared_state;
|
||||
return Atomic64Fetch(&g->average_timer_period_ns.v);
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
//~ @hookdef Sleep hooks
|
||||
|
||||
@ -1781,29 +1862,26 @@ void P_SleepPrecise(i64 sleep_time_ns)
|
||||
{
|
||||
__prof;
|
||||
|
||||
i64 big_sleep = GetCurrentSchedulerPeriodNs();
|
||||
i64 tolerance = (f64)big_sleep * 0.5;
|
||||
//i64 tolerance = 1000000000;
|
||||
|
||||
i64 now_ns = TimeNs();
|
||||
i64 target_ns = now_ns + sleep_time_ns;
|
||||
|
||||
/* Sleep */
|
||||
while (now_ns < target_ns - big_sleep - tolerance)
|
||||
/* Sleep on timer to conserve power */
|
||||
{
|
||||
__profn("Sleep part");
|
||||
FutexYield(0, 0, 0, big_sleep);
|
||||
now_ns = TimeNs();
|
||||
__profn("Sleep timer");
|
||||
Fence *timer_fence = P_GetTimerFence();
|
||||
i64 timer_period_ns = P_GetCurrentTimerPeriodNs();
|
||||
i64 timer_tolerance_ns = timer_period_ns * 0.5;
|
||||
i64 target_timer_sleep_ns = target_ns - timer_period_ns - timer_tolerance_ns;
|
||||
YieldOnFence(timer_fence, target_timer_sleep_ns);
|
||||
}
|
||||
|
||||
/* Spin */
|
||||
now_ns = TimeNs();
|
||||
while (now_ns < target_ns)
|
||||
{
|
||||
__profn("Sleep spin");
|
||||
while (now_ns < target_ns)
|
||||
{
|
||||
_mm_pause();
|
||||
now_ns = TimeNs();
|
||||
}
|
||||
_mm_pause();
|
||||
now_ns = TimeNs();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -110,9 +110,11 @@ Struct(P_W32_Sock)
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ State
|
||||
//~ State types
|
||||
|
||||
#define P_W32_WindowClassName L"power_play_window_class"
|
||||
#define P_W32_NumRollingTimerPeriods 500
|
||||
#define P_W32_DefaultTimerPeriodNs 50000000
|
||||
|
||||
Struct(P_W32_SharedState)
|
||||
{
|
||||
@ -135,9 +137,11 @@ Struct(P_W32_SharedState)
|
||||
Arena *socks_arena;
|
||||
Mutex socks_mutex;
|
||||
P_W32_Sock *first_free_sock;
|
||||
};
|
||||
|
||||
extern P_W32_SharedState P_W32_shared_state;
|
||||
//- Timer
|
||||
Fence timer_fence;
|
||||
Atomic64Padded average_timer_period_ns;
|
||||
} extern P_W32_shared_state;
|
||||
|
||||
////////////////////////////////
|
||||
//~ Time operations
|
||||
@ -172,3 +176,8 @@ LRESULT CALLBACK P_W32_Win32WindowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARA
|
||||
P_W32_Address P_W32_Win32AddressFromPlatformAddress(P_Address addr);
|
||||
P_W32_Address P_W32_ConvertAnyaddrToLocalhost(P_W32_Address addr);
|
||||
P_Address P_W32_PlatformAddressFromWin32Address(P_W32_Address ws_addr);
|
||||
|
||||
////////////////////////////////
|
||||
//~ Timer job
|
||||
|
||||
JobDecl(P_W32_TimerJob, EmptySig);
|
||||
|
||||
@ -15,7 +15,7 @@ void PB_Startup(void)
|
||||
PB_WSP_SharedState *g = &PB_WSP_shared_state;
|
||||
PB_WSP_InitializeWasapi();
|
||||
/* Start playback job */
|
||||
RunJob(1, PB_WSP_PlaybackJob, JobPool_Audio, JobPriority_High, &g->PB_WSP_PlaybackJob_counter, 0);
|
||||
RunJob(PB_WSP_PlaybackJob, .pool = JobPool_Audio, .counter = &g->shutdown_job_counter);
|
||||
OnExit(&PB_WSP_Shutdown);
|
||||
}
|
||||
|
||||
@ -24,7 +24,7 @@ ExitFuncDef(PB_WSP_Shutdown)
|
||||
__prof;
|
||||
PB_WSP_SharedState *g = &PB_WSP_shared_state;
|
||||
Atomic32FetchSet(&g->shutdown, 1);
|
||||
YieldOnCounter(&g->PB_WSP_PlaybackJob_counter);
|
||||
YieldOnJobs(&g->shutdown_job_counter);
|
||||
}
|
||||
|
||||
void PB_WSP_InitializeWasapi(void)
|
||||
|
||||
@ -22,7 +22,7 @@ Struct(PB_WSP_Buff)
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ State
|
||||
//~ State types
|
||||
|
||||
Struct(PB_WSP_SharedState)
|
||||
{
|
||||
@ -32,10 +32,8 @@ Struct(PB_WSP_SharedState)
|
||||
IAudioRenderClient *playback;
|
||||
WAVEFORMATEX *buffer_format;
|
||||
u32 buffer_frames;
|
||||
Counter PB_WSP_PlaybackJob_counter;
|
||||
};
|
||||
|
||||
extern PB_WSP_SharedState PB_WSP_shared_state;
|
||||
JobCounter shutdown_job_counter;
|
||||
} extern PB_WSP_shared_state;
|
||||
|
||||
////////////////////////////////
|
||||
//~ Wasapi startup
|
||||
|
||||
10
src/pp/pp.c
10
src/pp/pp.c
@ -48,8 +48,8 @@ void StartupUser(void)
|
||||
P_ShowWindow(g->window);
|
||||
|
||||
/* Start jobs */
|
||||
RunJob(1, UpdateUserJob, JobPool_User, JobPriority_High, &g->shutdown_job_counters, 0);
|
||||
RunJob(1, SimJob, JobPool_Sim, JobPriority_High, &g->shutdown_job_counters, 0);
|
||||
RunJob(UpdateUserJob, .pool = JobPool_User, .counter = &g->shutdown_job_counter);
|
||||
RunJob(SimJob, .pool = JobPool_Sim, .counter = &g->shutdown_job_counter);
|
||||
OnExit(&ShutdownUser);
|
||||
}
|
||||
|
||||
@ -61,7 +61,7 @@ ExitFuncDef(ShutdownUser)
|
||||
__prof;
|
||||
SharedUserState *g = &shared_user_state;
|
||||
Atomic32FetchSet(&g->shutdown, 1);
|
||||
YieldOnCounter(&g->shutdown_job_counters);
|
||||
YieldOnJobs(&g->shutdown_job_counter);
|
||||
P_ReleaseWindow(g->window);
|
||||
}
|
||||
|
||||
@ -2310,10 +2310,10 @@ void UpdateUser(P_Window *window)
|
||||
__profn("Shade pass");
|
||||
GPU_ProfN(cl, Lit("Shade pass"));
|
||||
|
||||
u32 shade_flags = K_SHADE_FLAG_NONE;
|
||||
u32 shade_flags = ShadeFlag_None;
|
||||
if (effects_disabled)
|
||||
{
|
||||
shade_flags |= K_SHADE_FLAG_DISABLE_EFFECTS;
|
||||
shade_flags |= ShadeFlag_DisableEffects;
|
||||
}
|
||||
ShadeSig sig = ZI;
|
||||
sig.flags = shade_flags;
|
||||
|
||||
@ -139,7 +139,7 @@ Struct(DecodeQueue)
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ State
|
||||
//~ State types
|
||||
|
||||
Struct(BindState)
|
||||
{
|
||||
@ -153,7 +153,7 @@ Struct(BindState)
|
||||
Struct(SharedUserState)
|
||||
{
|
||||
Atomic32 shutdown;
|
||||
Counter shutdown_job_counters;
|
||||
JobCounter shutdown_job_counter;
|
||||
P_Window *window;
|
||||
GPU_Swapchain *swapchain;
|
||||
|
||||
@ -266,9 +266,7 @@ Struct(SharedUserState)
|
||||
Vec2 world_cursor;
|
||||
|
||||
Vec2 focus_send;
|
||||
};
|
||||
|
||||
extern SharedUserState shared_user_state;
|
||||
} extern shared_user_state;
|
||||
|
||||
////////////////////////////////
|
||||
//~ Startup
|
||||
|
||||
@ -277,7 +277,7 @@ void CSDef(ShadeCS, Semantic(uint3, SV_DispatchThreadID))
|
||||
color *= albedo_tex[id];
|
||||
|
||||
/* Apply lighting */
|
||||
if (!(sig.flags & K_SHADE_FLAG_DISABLE_EFFECTS))
|
||||
if (!(sig.flags & ShadeFlag_DisableEffects))
|
||||
{
|
||||
color.rgb *= ColorFromPos(id);
|
||||
}
|
||||
|
||||
@ -60,8 +60,8 @@ AssertRootConst(FloodSig, 8);
|
||||
////////////////////////////////
|
||||
//~ Shade types
|
||||
|
||||
#define K_SHADE_FLAG_NONE (0 << 0)
|
||||
#define K_SHADE_FLAG_DISABLE_EFFECTS (1 << 0)
|
||||
#define ShadeFlag_None (0 << 0)
|
||||
#define ShadeFlag_DisableEffects (1 << 0)
|
||||
|
||||
Struct(ShadeSig)
|
||||
{
|
||||
|
||||
@ -227,7 +227,7 @@ Inline Snapshot *NilSnapshot(void)
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
//~ State
|
||||
//~ State types
|
||||
|
||||
#define ClientLookupBinsCount 127
|
||||
#define TickLookupBinsCount 127
|
||||
|
||||
@ -98,11 +98,14 @@ AC_Asset *SND_LoadAsset(Resource resource, SND_SoundFlag flags, b32 wait)
|
||||
if (is_first_touch)
|
||||
{
|
||||
AC_MarkLoading(asset);
|
||||
SND_LoadJob_Desc *desc = PushJobDesc(SND_LoadJob, .pool = JobPool_Background, .priority = JobPriority_Low, .counter = &asset->counter);
|
||||
desc->sig->resource = resource;
|
||||
desc->sig->asset = asset;
|
||||
desc->sig->flags = flags;
|
||||
RunJobEx((GenericJobDesc *)desc);
|
||||
{
|
||||
Job *job = OpenJob(SND_LoadJob, JobPool_Background);
|
||||
SND_LoadJob_Sig *sig = PushStruct(job->arena, SND_LoadJob_Sig);
|
||||
sig->resource = resource;
|
||||
sig->asset = asset;
|
||||
sig->flags = flags;
|
||||
CloseJob(job);
|
||||
}
|
||||
if (wait)
|
||||
{
|
||||
AC_YieldOnAssetReady(asset);
|
||||
|
||||
@ -286,13 +286,13 @@ S_Entry *S_FetchEntry(Resource resource, JobPool pool, S_FetchFlag flags)
|
||||
&& !Atomic32Fetch(&entry->texture_touched)
|
||||
&& !Atomic32FetchTestSet(&entry->texture_touched, 0, 1))
|
||||
{
|
||||
RunJob(1, S_LoadTextureJob, pool, JobPriority_Inherit, 0, .entry = entry);
|
||||
RunJob(S_LoadTextureJob, .pool = pool, .sig.entry = entry);
|
||||
}
|
||||
if ((flags & S_FetchFlag_Sheet)
|
||||
&& !Atomic32Fetch(&entry->sheet_touched)
|
||||
&& !Atomic32FetchTestSet(&entry->sheet_touched, 0, 1))
|
||||
{
|
||||
RunJob(1, S_LoadSheetJob, pool, JobPriority_Inherit, 0, .entry = entry);
|
||||
RunJob(S_LoadSheetJob, .pool = pool, .sig.entry = entry);
|
||||
}
|
||||
return entry;
|
||||
}
|
||||
|
||||
@ -121,16 +121,14 @@ Struct(S_EntryBin)
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ State
|
||||
//~ State types
|
||||
|
||||
#define S_EntryBinsCount 1024
|
||||
|
||||
Struct(S_SharedState)
|
||||
{
|
||||
S_EntryBin entry_bins[S_EntryBinsCount];
|
||||
};
|
||||
|
||||
extern S_SharedState S_shared_state;
|
||||
} extern S_shared_state;
|
||||
|
||||
////////////////////////////////
|
||||
//~ Load jobs
|
||||
|
||||
@ -12,7 +12,7 @@ Struct(TAR_Entry)
|
||||
b32 is_dir;
|
||||
TAR_Entry *next;
|
||||
TAR_Entry *next_child; /* If entry is dir, points to first child. Otherwise points to next sibling. */
|
||||
};
|
||||
} extern Readonly TAR_nil_entry;
|
||||
|
||||
Struct(TAR_Archive)
|
||||
{
|
||||
@ -20,8 +20,6 @@ Struct(TAR_Archive)
|
||||
TAR_Entry *head;
|
||||
};
|
||||
|
||||
extern Readonly TAR_Entry TAR_nil_entry;
|
||||
|
||||
////////////////////////////////
|
||||
//~ Header types
|
||||
|
||||
|
||||
@ -30,8 +30,7 @@ void TTF_Startup(void)
|
||||
#endif
|
||||
if (error != S_OK)
|
||||
{
|
||||
/* FIXME: Enable this */
|
||||
//Panic(Lit("Error creating DWrite factory"));
|
||||
Panic(Lit("Error creating DWrite factory"));
|
||||
(*(volatile int *)0) = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@ -140,7 +140,7 @@ static inline UINT32 IDWriteGdiInterop_Release
|
||||
EXTERN_C HRESULT DECLSPEC_IMPORT WINAPI DWriteCreateFactory (DWRITE_FACTORY_TYPE factoryType, const GUID* iid, void** factory) WIN_NOEXCEPT;
|
||||
|
||||
////////////////////////////////
|
||||
//~ State
|
||||
//~ State types
|
||||
|
||||
/* TODO: Determine font dpi dynamically */
|
||||
#define TTF_DW_Dpi (96.0f)
|
||||
@ -148,6 +148,4 @@ EXTERN_C HRESULT DECLSPEC_IMPORT WINAPI DWriteCreateFactory (DWRITE_FACTORY_TYPE
|
||||
Struct(TTF_DW_SharedState)
|
||||
{
|
||||
struct IDWriteFactory5 *factory;
|
||||
};
|
||||
|
||||
extern TTF_DW_SharedState TTF_DW_shared_state;
|
||||
} extern TTF_DW_shared_state;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user