threading refactor progress
This commit is contained in:
parent
94210da6be
commit
4a787ee618
@ -112,23 +112,11 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
//~ Platform headers
|
//~ C headers
|
||||||
|
|
||||||
//- Windows headers
|
#if IsLanguageC
|
||||||
#if IsPlatformWindows
|
#include <stdint.h>
|
||||||
#define COBJMACROS
|
#include <stdarg.h>
|
||||||
#define WIN32_LEAN_AND_MEAN
|
|
||||||
#define UNICODE
|
|
||||||
#pragma warning(push, 0)
|
|
||||||
#include <Windows.h>
|
|
||||||
#include <combaseapi.h>
|
|
||||||
#include <dcommon.h>
|
|
||||||
#include <initguid.h>
|
|
||||||
#include <unknwn.h>
|
|
||||||
#include <objbase.h>
|
|
||||||
#include <uuids.h>
|
|
||||||
#include <Knownfolders.h>
|
|
||||||
#pragma warning(pop)
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
@ -207,7 +195,14 @@
|
|||||||
#define Readonly __attribute((section(".rodata")))
|
#define Readonly __attribute((section(".rodata")))
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
//- Barriers
|
//- Thread-local
|
||||||
|
|
||||||
|
#if IsCompilerMsvc
|
||||||
|
#define ThreadLocal __declspec(thread)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
//- Memory barriers
|
||||||
#if IsCompilerMsvc
|
#if IsCompilerMsvc
|
||||||
#define WriteBarrier() _WriteBarrier()
|
#define WriteBarrier() _WriteBarrier()
|
||||||
#define ReadBarrier() _ReadBarrier()
|
#define ReadBarrier() _ReadBarrier()
|
||||||
@ -482,7 +477,6 @@
|
|||||||
//~ Scalar types
|
//~ Scalar types
|
||||||
|
|
||||||
#if IsLanguageC
|
#if IsLanguageC
|
||||||
#include <stdint.h>
|
|
||||||
typedef int8_t i8;
|
typedef int8_t i8;
|
||||||
typedef int16_t i16;
|
typedef int16_t i16;
|
||||||
typedef int32_t i32;
|
typedef int32_t i32;
|
||||||
@ -747,20 +741,6 @@ Struct(SamplerStateHandle) { u32 v; };
|
|||||||
#define ShaderConstant(type, name, slot) cbuffer name : register(b##slot) { type name; }
|
#define ShaderConstant(type, name, slot) cbuffer name : register(b##slot) { type name; }
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////
|
|
||||||
//~ Fibers
|
|
||||||
|
|
||||||
#define MaxFibers 4096
|
|
||||||
StaticAssert(MaxFibers < I16Max); /* MaxFibers should fit in FiberId */
|
|
||||||
|
|
||||||
#if IsLanguageC
|
|
||||||
#if IsPlatformWindows
|
|
||||||
#define FiberId() (*(volatile i16 *)__readgsqword(0x20))
|
|
||||||
#else
|
|
||||||
#error FiberId not implemented
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
//~ Exit callback types
|
//~ Exit callback types
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
SharedArenaCtx shared_arena_ctx = ZI;
|
ThreadLocal ThreadArenasCtx t_arena_ctx = ZI;
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
//~ Arena management
|
//~ Arena management
|
||||||
@ -228,24 +228,6 @@ void EndTempArena(TempArena temp)
|
|||||||
PopTo(temp.arena, temp.start_pos);
|
PopTo(temp.arena, temp.start_pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////
|
|
||||||
//~ Fiber arena helpers
|
|
||||||
|
|
||||||
FiberArenaCtx *FiberArenaCtxFromId(i16 fiber_id)
|
|
||||||
{
|
|
||||||
SharedArenaCtx *g = &shared_arena_ctx;
|
|
||||||
FiberArenaCtx *ctx = &g->arena_contexts[fiber_id];
|
|
||||||
if (!ctx->perm_arena)
|
|
||||||
{
|
|
||||||
ctx->perm_arena = AcquireArena(Gibi(64));
|
|
||||||
for (i32 i = 0; i < (i32)countof(ctx->scratch_arenas); ++i)
|
|
||||||
{
|
|
||||||
ctx->scratch_arenas[i] = AcquireArena(Gibi(64));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ctx;
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
//~ Scratch arena helpers
|
//~ Scratch arena helpers
|
||||||
|
|
||||||
@ -257,11 +239,10 @@ TempArena BeginScratch(Arena *potential_conflict)
|
|||||||
/* Use `BeginScratchNoConflict` if no conflicts are present */
|
/* Use `BeginScratchNoConflict` if no conflicts are present */
|
||||||
Assert(potential_conflict != 0);
|
Assert(potential_conflict != 0);
|
||||||
|
|
||||||
FiberArenaCtx *ctx = FiberArenaCtxFromId(FiberId());
|
Arena *scratch_arena = t_arena_ctx.scratch_arenas[0];
|
||||||
Arena *scratch_arena = ctx->scratch_arenas[0];
|
|
||||||
if (scratch_arena == potential_conflict)
|
if (scratch_arena == potential_conflict)
|
||||||
{
|
{
|
||||||
scratch_arena = ctx->scratch_arenas[1];
|
scratch_arena = t_arena_ctx.scratch_arenas[1];
|
||||||
}
|
}
|
||||||
TempArena temp = BeginTempArena(scratch_arena);
|
TempArena temp = BeginTempArena(scratch_arena);
|
||||||
return temp;
|
return temp;
|
||||||
@ -269,8 +250,7 @@ TempArena BeginScratch(Arena *potential_conflict)
|
|||||||
|
|
||||||
TempArena BeginScratchNoConflict_(void)
|
TempArena BeginScratchNoConflict_(void)
|
||||||
{
|
{
|
||||||
FiberArenaCtx *ctx = FiberArenaCtxFromId(FiberId());
|
Arena *scratch_arena = t_arena_ctx.scratch_arenas[0];
|
||||||
Arena *scratch_arena = ctx->scratch_arenas[0];
|
|
||||||
TempArena temp = BeginTempArena(scratch_arena);
|
TempArena temp = BeginTempArena(scratch_arena);
|
||||||
return temp;
|
return temp;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -22,16 +22,13 @@ Struct(TempArena)
|
|||||||
|
|
||||||
#define ScratchArenasPerCtx 2
|
#define ScratchArenasPerCtx 2
|
||||||
|
|
||||||
Struct(FiberArenaCtx)
|
Struct(ThreadArenasCtx)
|
||||||
{
|
{
|
||||||
Arena *perm_arena;
|
Arena *perm_arena;
|
||||||
Arena *scratch_arenas[ScratchArenasPerCtx];
|
Arena *scratch_arenas[ScratchArenasPerCtx];
|
||||||
};
|
};
|
||||||
|
|
||||||
Struct(SharedArenaCtx)
|
extern ThreadLocal ThreadArenasCtx t_arena_ctx;
|
||||||
{
|
|
||||||
FiberArenaCtx arena_contexts[MaxFibers];
|
|
||||||
} extern shared_arena_ctx;
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
//~ Arena management
|
//~ Arena management
|
||||||
@ -76,16 +73,12 @@ void *ArenaNext_(Arena *arena, u64 align);
|
|||||||
void *ArenaFirst_(Arena *arena, u64 align);
|
void *ArenaFirst_(Arena *arena, u64 align);
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
//~ Temp arena helpers
|
//~ Thread arena helpers
|
||||||
|
|
||||||
TempArena BeginTempArena(Arena *arena);
|
TempArena BeginTempArena(Arena *arena);
|
||||||
void EndTempArena(TempArena temp);
|
void EndTempArena(TempArena temp);
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////
|
#define PermArena() (t_arena_ctx.perm_arena)
|
||||||
//~ Fiber arena helpers
|
|
||||||
|
|
||||||
FiberArenaCtx *FiberArenaCtxFromId(i16 fiber_id);
|
|
||||||
#define PermArena() (FiberArenaCtxFromId(FiberId())->perm_arena)
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
//~ Scratch arena helpers
|
//~ Scratch arena helpers
|
||||||
|
|||||||
@ -1,187 +0,0 @@
|
|||||||
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;
|
|
||||||
u64 bin_index = RandU64FromSeed((u64)addr) % countof(g->neq_bins);
|
|
||||||
FutexNeqListBin *bin = &g->neq_bins[bin_index];
|
|
||||||
|
|
||||||
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;
|
|
||||||
u64 bin_index = RandU64FromSeed((u64)addr) % countof(g->neq_bins);
|
|
||||||
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 = ArenaNext(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);
|
|
||||||
}
|
|
||||||
@ -1,50 +1,5 @@
|
|||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
//~ Neq futex types
|
//~ @hookdecl Not-equal futex operations
|
||||||
|
|
||||||
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
|
/* Similar to Win32 WaitOnAddress & WakeByAddressAll
|
||||||
* i.e. - Suprious wait until value at address != cmp */
|
* i.e. - Suprious wait until value at address != cmp */
|
||||||
@ -53,7 +8,7 @@ void FutexYieldNeq(volatile void *addr, void *cmp, u8 cmp_size);
|
|||||||
void FutexWakeNeq(void *addr);
|
void FutexWakeNeq(void *addr);
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
//~ Greater-than-or-equal futex operations
|
//~ @hookdecl Greater-than-or-equal futex operations
|
||||||
|
|
||||||
/* Similar to Win32 WaitOnAddress & WakeByAddressAll
|
/* Similar to Win32 WaitOnAddress & WakeByAddressAll
|
||||||
* i.e. - Spurious wait until monotonically increasing value at address >= cmp (used for fences)
|
* i.e. - Spurious wait until monotonically increasing value at address >= cmp (used for fences)
|
||||||
@ -61,7 +16,7 @@ void FutexWakeNeq(void *addr);
|
|||||||
* NOTE: This API is offered for fence-like semantics, where waiters only want to
|
* 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 when the futex progresses past the specified target value, rather than
|
||||||
* wake every time the futex is modified.
|
* wake every time the futex is modified.
|
||||||
**/
|
*/
|
||||||
|
|
||||||
void FutexYieldGte(volatile void *addr, void *cmp, u8 cmp_size);
|
void FutexYieldGte(volatile void *addr, void *cmp, u8 cmp_size);
|
||||||
void FutexWakeGte(void *addr);
|
void FutexWakeGte(void *addr);
|
||||||
|
|||||||
@ -9,8 +9,8 @@
|
|||||||
# include "base_memory.h"
|
# include "base_memory.h"
|
||||||
# include "base_arena.h"
|
# include "base_arena.h"
|
||||||
# include "base_futex.h"
|
# include "base_futex.h"
|
||||||
# include "base_snc.h"
|
# include "base_sync.h"
|
||||||
# include "base_job.h"
|
# include "base_wave.h"
|
||||||
# include "base_time.h"
|
# include "base_time.h"
|
||||||
# include "base_uid.h"
|
# include "base_uid.h"
|
||||||
# include "base_string.h"
|
# include "base_string.h"
|
||||||
@ -34,8 +34,8 @@
|
|||||||
#if IsLanguageC
|
#if IsLanguageC
|
||||||
# include "base_memory.c"
|
# include "base_memory.c"
|
||||||
# include "base_arena.c"
|
# include "base_arena.c"
|
||||||
# include "base_futex.c"
|
# include "base_sync.c"
|
||||||
# include "base_snc.c"
|
# include "base_wave.c"
|
||||||
# include "base_uid.c"
|
# include "base_uid.c"
|
||||||
# include "base_string.c"
|
# include "base_string.c"
|
||||||
# include "base_cmdline.c"
|
# include "base_cmdline.c"
|
||||||
|
|||||||
@ -1,121 +0,0 @@
|
|||||||
////////////////////////////////////////////////////////////
|
|
||||||
//~ Job pool types
|
|
||||||
|
|
||||||
typedef i32 JobPoolId;
|
|
||||||
|
|
||||||
Enum(JobPoolPriority)
|
|
||||||
{
|
|
||||||
JobPoolPriority_Background,
|
|
||||||
JobPoolPriority_Async,
|
|
||||||
JobPoolPriority_Graphics,
|
|
||||||
JobPoolPriority_Simulation,
|
|
||||||
JobPoolPriority_Critical,
|
|
||||||
JobPoolPriority_Audio,
|
|
||||||
};
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////
|
|
||||||
//~ Job types
|
|
||||||
|
|
||||||
Struct(Job);
|
|
||||||
|
|
||||||
typedef void JobFunc(void *, i32);
|
|
||||||
|
|
||||||
Enum(JobFlag)
|
|
||||||
{
|
|
||||||
JobFlag_None = (0),
|
|
||||||
};
|
|
||||||
|
|
||||||
Struct(Job)
|
|
||||||
{
|
|
||||||
/* Internal */
|
|
||||||
Job *next;
|
|
||||||
Atomic32Padded num_tasks_completed;
|
|
||||||
|
|
||||||
/* Initialized & constant after OpenJob */
|
|
||||||
Arena *arena;
|
|
||||||
JobFunc *func;
|
|
||||||
JobPoolId pool;
|
|
||||||
|
|
||||||
/* Configurable between OpenJob & CloseJob */
|
|
||||||
i32 count;
|
|
||||||
Fence *fence;
|
|
||||||
JobFlag flags;
|
|
||||||
void *sig;
|
|
||||||
};
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////
|
|
||||||
//~ @hookdecl Init
|
|
||||||
|
|
||||||
void InitJobSystem(void);
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////
|
|
||||||
//~ @hookdecl Fiber suspend/resume operations
|
|
||||||
|
|
||||||
void SuspendFiber(void);
|
|
||||||
void ResumeFibers(i16 fiber_ids_count, i16 *fiber_ids); /* NOTE: Must only be called on fibers suspended via SuspendFiber */
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////
|
|
||||||
//~ @hookdecl Job pool operations
|
|
||||||
|
|
||||||
JobPoolId InitJobPool(u32 thread_count, String name, JobPoolPriority priority);
|
|
||||||
|
|
||||||
//- Default job pools
|
|
||||||
|
|
||||||
/* The current job pool */
|
|
||||||
JobPoolId CurrentPool(void);
|
|
||||||
|
|
||||||
/* Contains high-priority workers that span the entire CPU.
|
|
||||||
* Meant to take on temporary high-throughput work that is allowed to
|
|
||||||
* steal performance from all other pools (e.g. program startup, loading a level,
|
|
||||||
* etc). */
|
|
||||||
JobPoolId HyperPool(void);
|
|
||||||
|
|
||||||
/* Contains lower-priority workers affinitized to not interfere with workers in other pools.
|
|
||||||
* Meant to consume asynchronous work from higher priority pools. */
|
|
||||||
JobPoolId AsyncPool(void);
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////
|
|
||||||
//~ @hookdecl Job declaration operations
|
|
||||||
|
|
||||||
#define EmptySig { i32 _; }
|
|
||||||
|
|
||||||
#define JobDecl(job, sigdef) \
|
|
||||||
typedef struct job##_Sig sigdef job##_Sig; \
|
|
||||||
Struct(job##_Desc) { JobFunc *func; JobPoolId pool; u32 count; Fence *fence; JobFlag flags; job##_Sig sig; }; \
|
|
||||||
void job(job##_Sig *, i32); \
|
|
||||||
StaticAssert(1)
|
|
||||||
|
|
||||||
#define JobImpl(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
|
|
||||||
* pool, copying 'sprite' into the job signature. 'fence' is also passed
|
|
||||||
* and then immediately yielded on in this example, effectively making
|
|
||||||
* the operation synchronous as the caller will block until the job completes:
|
|
||||||
* {
|
|
||||||
* Fence job_fence = {0};
|
|
||||||
* u32 job_count = 0;
|
|
||||||
* job_count += RunJob(LoadTextureJob, .pool = JobPool_Background, .fence = &job_fence, .sig = { .resource = sprite });
|
|
||||||
* YieldOnFence(&job_fence, job_count);
|
|
||||||
* }
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#define RunJob(job_func, ...) (1); \
|
|
||||||
do { \
|
|
||||||
job_func##_Desc __desc = { .count = 1, .pool = CurrentPool(), .func = job_func, __VA_ARGS__ }; \
|
|
||||||
Job *__job = OpenJob(__desc.func, __desc.pool); \
|
|
||||||
__job->count = __desc.count; \
|
|
||||||
__job->fence = __desc.fence; \
|
|
||||||
__job->flags = __desc.flags; \
|
|
||||||
__job->sig = PushStructNoZero(__job->arena, job_func##_Sig); \
|
|
||||||
CopyBytes(__job->sig, &__desc.sig, sizeof(__desc.sig)); \
|
|
||||||
CloseJob(__job); \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
Job *OpenJob(JobFunc *func, JobPoolId pool_kind);
|
|
||||||
u32 CloseJob(Job *job);
|
|
||||||
@ -13,7 +13,6 @@ Struct(LogEvent)
|
|||||||
i32 level;
|
i32 level;
|
||||||
|
|
||||||
i32 thread_id;
|
i32 thread_id;
|
||||||
i32 fiber_id;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Struct(LogEventsArray)
|
Struct(LogEventsArray)
|
||||||
|
|||||||
@ -1,49 +1,3 @@
|
|||||||
////////////////////////////////////////////////////////////
|
|
||||||
//~ Win32 memory allocation
|
|
||||||
|
|
||||||
#if IsPlatformWindows
|
|
||||||
|
|
||||||
//- Reserve
|
|
||||||
void *ReserveMemory(u64 size)
|
|
||||||
{
|
|
||||||
void *ptr = VirtualAlloc(0, size, MEM_RESERVE, PAGE_NOACCESS);
|
|
||||||
return ptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ReleaseMemory(void *address)
|
|
||||||
{
|
|
||||||
VirtualFree(address, 0, MEM_RELEASE);
|
|
||||||
}
|
|
||||||
|
|
||||||
//- Commit
|
|
||||||
void *CommitMemory(void *address, u64 size)
|
|
||||||
{
|
|
||||||
void *ptr = VirtualAlloc(address, size, MEM_COMMIT, PAGE_READWRITE);
|
|
||||||
return ptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
void DecommitMemory(void *address, u64 size)
|
|
||||||
{
|
|
||||||
VirtualFree(address, size, MEM_DECOMMIT);
|
|
||||||
}
|
|
||||||
|
|
||||||
//- Protect
|
|
||||||
void SetMemoryReadonly(void *address, u64 size)
|
|
||||||
{
|
|
||||||
DWORD old;
|
|
||||||
VirtualProtect(address, size, PAGE_READONLY, &old);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SetMemoryReadWrite(void *address, u64 size)
|
|
||||||
{
|
|
||||||
DWORD old;
|
|
||||||
VirtualProtect(address, size, PAGE_READWRITE, &old);
|
|
||||||
}
|
|
||||||
|
|
||||||
#else
|
|
||||||
# error Memory allocation not implemented for this platform
|
|
||||||
#endif /* IsPlatformWindows */
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
//~ Crtlib mem op stubs
|
//~ Crtlib mem op stubs
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
//~ Memory allocation
|
//~ @hookdecl Memory allocation
|
||||||
|
|
||||||
//- Reserve
|
//- Reserve
|
||||||
void *ReserveMemory(u64 size);
|
void *ReserveMemory(u64 size);
|
||||||
|
|||||||
@ -56,7 +56,7 @@ Lock LockSpinE(Mutex *m, i32 spin)
|
|||||||
}
|
}
|
||||||
|
|
||||||
#if IsRtcEnabled
|
#if IsRtcEnabled
|
||||||
Atomic32Set(&m->exclusive_fiber_id, FiberId());
|
Atomic32Set(&m->exclusive_thread_id, ThreadId());
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
Lock lock = ZI;
|
Lock lock = ZI;
|
||||||
@ -123,7 +123,7 @@ void Unlock(Lock *l)
|
|||||||
if (l->exclusive)
|
if (l->exclusive)
|
||||||
{
|
{
|
||||||
#if IsRtcEnabled
|
#if IsRtcEnabled
|
||||||
Atomic32Set(&m->exclusive_fiber_id, 0);
|
Atomic32Set(&m->exclusive_thread_id, 0);
|
||||||
#endif
|
#endif
|
||||||
Atomic32Set(&m->v, 0);
|
Atomic32Set(&m->v, 0);
|
||||||
}
|
}
|
||||||
@ -12,7 +12,7 @@ AlignedStruct(Mutex, CachelineSize)
|
|||||||
Atomic32 v;
|
Atomic32 v;
|
||||||
|
|
||||||
#if IsRtcEnabled
|
#if IsRtcEnabled
|
||||||
Atomic32 exclusive_fiber_id;
|
Atomic32 exclusive_thread_id;
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
StaticAssert(alignof(Mutex) == CachelineSize && sizeof(Mutex) % CachelineSize == 0);
|
StaticAssert(alignof(Mutex) == CachelineSize && sizeof(Mutex) % CachelineSize == 0);
|
||||||
7
src/base/base_wave.c
Normal file
7
src/base/base_wave.c
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
//~ Wave sync ops
|
||||||
|
|
||||||
|
void WaveSyncBroadcast_(WaveLaneCtx *lane_ctx, i32 broadcast_lane_idx, void *broadcast_ptr, u64 broadcast_size)
|
||||||
|
{
|
||||||
|
/* FIXME: Impl */
|
||||||
|
}
|
||||||
31
src/base/base_wave.h
Normal file
31
src/base/base_wave.h
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
//~ Wave types
|
||||||
|
|
||||||
|
Struct(WaveCtx)
|
||||||
|
{
|
||||||
|
u32 lanes_count;
|
||||||
|
};
|
||||||
|
|
||||||
|
Struct(WaveLaneCtx)
|
||||||
|
{
|
||||||
|
u32 idx;
|
||||||
|
WaveCtx *wave;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef void WaveLaneEntryFunc(WaveLaneCtx *lane, void *udata);
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
//~ Wave sync ops
|
||||||
|
|
||||||
|
#define WaveSyncBroadcast(lane_ctx, broadcast_lane_idx, broadcast_ptr) WaveSyncBroadcast_((lane_ctx), (broadcast_lane_idx), (broadcast_ptr), sizeof(*(broadcast_ptr)))
|
||||||
|
void WaveSyncBroadcast_(WaveLaneCtx *lane_ctx, i32 broadcast_lane_idx, void *broadcast_ptr, u64 broadcast_size);
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
//~ @hookdecl Dispatch
|
||||||
|
|
||||||
|
void DispatchWave(u32 num_lanes, WaveLaneEntryFunc *entry, void *udata);
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
//~ @hookdecl Thread
|
||||||
|
|
||||||
|
i32 ThreadId(void);
|
||||||
@ -196,32 +196,6 @@ void ExitNow(i32 code)
|
|||||||
ExitProcess(code);
|
ExitProcess(code);
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////
|
|
||||||
//~ Startup / shutdown jobs
|
|
||||||
|
|
||||||
JobImpl(W32_StartupLayers, sig, id)
|
|
||||||
{
|
|
||||||
W32_SharedState *g = &W32_shared_state;
|
|
||||||
TempArena scratch = BeginScratchNoConflict();
|
|
||||||
{
|
|
||||||
StartupLayers();
|
|
||||||
SetEvent(g->startup_end_event);
|
|
||||||
}
|
|
||||||
EndScratch(scratch);
|
|
||||||
}
|
|
||||||
|
|
||||||
JobImpl(W32_ShutdownLayers, sig, id)
|
|
||||||
{
|
|
||||||
W32_SharedState *g = &W32_shared_state;
|
|
||||||
i32 num_funcs = Atomic32Fetch(&g->num_exit_funcs);
|
|
||||||
for (i32 i = num_funcs - 1; i >= 0; --i)
|
|
||||||
{
|
|
||||||
ExitFunc *func = g->exit_funcs[i];
|
|
||||||
func();
|
|
||||||
}
|
|
||||||
SetEvent(g->exit_end_event);
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
//~ Main
|
//~ Main
|
||||||
|
|
||||||
@ -241,11 +215,9 @@ i32 W32_Main(void)
|
|||||||
g->timer_start_qpc = qpc.QuadPart;
|
g->timer_start_qpc = qpc.QuadPart;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Set up exit events */
|
/* Setup events */
|
||||||
g->panic_event = CreateEventW(0, 1, 0, 0);
|
g->panic_event = CreateEventW(0, 1, 0, 0);
|
||||||
g->startup_end_event = CreateEventW(0, 1, 0, 0);
|
|
||||||
g->exit_begin_event = CreateEventW(0, 1, 0, 0);
|
g->exit_begin_event = CreateEventW(0, 1, 0, 0);
|
||||||
g->exit_end_event = CreateEventW(0, 1, 0, 0);
|
|
||||||
|
|
||||||
g->main_thread_id = GetCurrentThreadId();
|
g->main_thread_id = GetCurrentThreadId();
|
||||||
SetThreadDescription(GetCurrentThread(), L"Main thread");
|
SetThreadDescription(GetCurrentThread(), L"Main thread");
|
||||||
@ -253,11 +225,8 @@ i32 W32_Main(void)
|
|||||||
/* Query system info */
|
/* Query system info */
|
||||||
GetSystemInfo(&g->info);
|
GetSystemInfo(&g->info);
|
||||||
|
|
||||||
/* Init job system */
|
/* Init main thread */
|
||||||
InitJobSystem();
|
W32_InitCurrentThread(Lit("Main thread"));
|
||||||
|
|
||||||
/* Init futex system */
|
|
||||||
InitFutexSystem();
|
|
||||||
|
|
||||||
/* Get raw args from command line */
|
/* Get raw args from command line */
|
||||||
{
|
{
|
||||||
@ -296,14 +265,13 @@ i32 W32_Main(void)
|
|||||||
/* Startup layers */
|
/* Startup layers */
|
||||||
if (!Atomic32Fetch(&g->panicking))
|
if (!Atomic32Fetch(&g->panicking))
|
||||||
{
|
{
|
||||||
RunJob(W32_StartupLayers);
|
StartupLayers();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Wait for startup end or panic */
|
/* Wait for panic */
|
||||||
if (!Atomic32Fetch(&g->panicking))
|
if (!Atomic32Fetch(&g->panicking))
|
||||||
{
|
{
|
||||||
HANDLE handles[] = {
|
HANDLE handles[] = {
|
||||||
g->startup_end_event,
|
|
||||||
g->panic_event,
|
g->panic_event,
|
||||||
};
|
};
|
||||||
WaitForMultipleObjects(countof(handles), handles, 0, INFINITE);
|
WaitForMultipleObjects(countof(handles), handles, 0, INFINITE);
|
||||||
@ -321,17 +289,21 @@ i32 W32_Main(void)
|
|||||||
|
|
||||||
//- App shutdown
|
//- App shutdown
|
||||||
|
|
||||||
/* Run exit callbacks job */
|
/* Run exit callbacks */
|
||||||
if (!Atomic32Fetch(&g->panicking))
|
if (!Atomic32Fetch(&g->panicking))
|
||||||
{
|
{
|
||||||
RunJob(W32_ShutdownLayers);
|
i32 num_funcs = Atomic32Fetch(&g->num_exit_funcs);
|
||||||
|
for (i32 idx = num_funcs - 1; idx >= 0; --idx)
|
||||||
|
{
|
||||||
|
ExitFunc *func = g->exit_funcs[idx];
|
||||||
|
func();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Wait for exit end or panic */
|
/* Wait for exit end or panic */
|
||||||
if (!Atomic32Fetch(&g->panicking))
|
if (!Atomic32Fetch(&g->panicking))
|
||||||
{
|
{
|
||||||
HANDLE handles[] = {
|
HANDLE handles[] = {
|
||||||
g->exit_end_event,
|
|
||||||
g->panic_event
|
g->panic_event
|
||||||
};
|
};
|
||||||
WaitForMultipleObjects(countof(handles), handles, 0, INFINITE);
|
WaitForMultipleObjects(countof(handles), handles, 0, INFINITE);
|
||||||
|
|||||||
@ -1,14 +1,48 @@
|
|||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
//~ Win32 libs
|
//~ Win32 libs
|
||||||
|
|
||||||
|
//- Windows headers
|
||||||
|
|
||||||
|
#define COBJMACROS
|
||||||
|
#define WIN32_LEAN_AND_MEAN
|
||||||
|
#define UNICODE
|
||||||
|
#pragma warning(push, 0)
|
||||||
|
#include <Windows.h>
|
||||||
|
#include <combaseapi.h>
|
||||||
|
#include <dcommon.h>
|
||||||
|
#include <initguid.h>
|
||||||
|
#include <unknwn.h>
|
||||||
|
#include <objbase.h>
|
||||||
|
#include <uuids.h>
|
||||||
|
#include <Knownfolders.h>
|
||||||
|
#include <WinSock2.h>
|
||||||
|
#include <TlHelp32.h>
|
||||||
|
#include <WS2tcpip.h>
|
||||||
|
#include <windowsx.h>
|
||||||
|
#include <ShlObj_core.h>
|
||||||
|
#include <fileapi.h>
|
||||||
|
#include <dwmapi.h>
|
||||||
|
#include <avrt.h>
|
||||||
|
#include <shellapi.h>
|
||||||
|
#pragma warning(pop)
|
||||||
|
|
||||||
#ifndef BCRYPT_RNG_ALG_HANDLE
|
#ifndef BCRYPT_RNG_ALG_HANDLE
|
||||||
#define BCRYPT_RNG_ALG_HANDLE ((void *)0x00000081)
|
#define BCRYPT_RNG_ALG_HANDLE ((void *)0x00000081)
|
||||||
u32 BCryptGenRandom(void *algorithm, u8 *buffer, u32 buffer_size, u32 flags);
|
u32 BCryptGenRandom(void *algorithm, u8 *buffer, u32 buffer_size, u32 flags);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
//- Windows libs
|
||||||
|
|
||||||
#pragma comment(lib, "kernel32")
|
#pragma comment(lib, "kernel32")
|
||||||
#pragma comment(lib, "user32")
|
#pragma comment(lib, "user32")
|
||||||
#pragma comment(lib, "bcrypt")
|
#pragma comment(lib, "bcrypt")
|
||||||
|
#pragma comment(lib, "shell32")
|
||||||
|
#pragma comment(lib, "ole32")
|
||||||
|
#pragma comment(lib, "winmm")
|
||||||
|
#pragma comment(lib, "dwmapi")
|
||||||
|
#pragma comment(lib, "synchronization")
|
||||||
|
#pragma comment(lib, "avrt")
|
||||||
|
#pragma comment(lib, "ws2_32.lib")
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
//~ Embedded data iter types
|
//~ Embedded data iter types
|
||||||
@ -40,9 +74,7 @@ Struct(W32_SharedState)
|
|||||||
Atomic32 panicking;
|
Atomic32 panicking;
|
||||||
wchar_t panic_wstr[4096];
|
wchar_t panic_wstr[4096];
|
||||||
HANDLE panic_event;
|
HANDLE panic_event;
|
||||||
HANDLE startup_end_event;
|
|
||||||
HANDLE exit_begin_event;
|
HANDLE exit_begin_event;
|
||||||
HANDLE exit_end_event;
|
|
||||||
|
|
||||||
//- Exit funcs
|
//- Exit funcs
|
||||||
Atomic32 num_exit_funcs;
|
Atomic32 num_exit_funcs;
|
||||||
@ -55,12 +87,6 @@ Struct(W32_SharedState)
|
|||||||
#define W32_EmbeddedDataPrefix EMBEDDED_RESOURCE_DATA__
|
#define W32_EmbeddedDataPrefix EMBEDDED_RESOURCE_DATA__
|
||||||
BOOL W32_FindEmbeddedRcData(HMODULE module, LPCWSTR type, LPWSTR wstr_entry_name, LONG_PTR udata);
|
BOOL W32_FindEmbeddedRcData(HMODULE module, LPCWSTR type, LPWSTR wstr_entry_name, LONG_PTR udata);
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////
|
|
||||||
//~ Startup / shutdown jobs
|
|
||||||
|
|
||||||
JobDecl(W32_StartupLayers, EmptySig);
|
|
||||||
JobDecl(W32_ShutdownLayers, EmptySig);
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
//~ Main
|
//~ Main
|
||||||
|
|
||||||
|
|||||||
27
src/base/base_win32/base_win32_futex.c
Normal file
27
src/base/base_win32/base_win32_futex.c
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
//~ @hookimpl Not-equal futex operations
|
||||||
|
|
||||||
|
void FutexYieldNeq(volatile void *addr, void *cmp, u8 cmp_size)
|
||||||
|
{
|
||||||
|
WaitOnAddress(addr, cmp, cmp_size, INFINITE);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FutexWakeNeq(void *addr)
|
||||||
|
{
|
||||||
|
WakeByAddressSingle(addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
//~ @hookimpl 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);
|
||||||
|
}
|
||||||
@ -1,10 +1,12 @@
|
|||||||
//- Api
|
//- Api
|
||||||
#include "base_win32.h"
|
#include "base_win32.h"
|
||||||
#include "base_win32_job.h"
|
#include "base_win32_wave.h"
|
||||||
#include "base_win32_log.h"
|
#include "base_win32_log.h"
|
||||||
|
|
||||||
//- Impl
|
//- Impl
|
||||||
#include "base_win32.c"
|
#include "base_win32.c"
|
||||||
#include "base_win32_job.c"
|
#include "base_win32_futex.c"
|
||||||
|
#include "base_win32_memory.c"
|
||||||
|
#include "base_win32_wave.c"
|
||||||
#include "base_win32_time.c"
|
#include "base_win32_time.c"
|
||||||
#include "base_win32_log.c"
|
#include "base_win32_log.c"
|
||||||
|
|||||||
@ -1,721 +0,0 @@
|
|||||||
W32_SharedJobState W32_shared_job_state = ZI;
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////
|
|
||||||
//~ @hookimpl Startup
|
|
||||||
|
|
||||||
void InitJobSystem(void)
|
|
||||||
{
|
|
||||||
/* Init fibers */
|
|
||||||
W32_SharedJobState *g = &W32_shared_job_state;
|
|
||||||
g->num_fibers = 1; /* Fiber at index 0 always nil */
|
|
||||||
W32_AcquireFiber(0); /* Convert main thread to fiber */
|
|
||||||
Arena *perm = PermArena();
|
|
||||||
|
|
||||||
/* TODO: Dynamic thread counts */
|
|
||||||
g->hyper_pool = InitJobPool(8, Lit("Hyper"), JobPoolPriority_Critical);
|
|
||||||
g->async_pool = InitJobPool(8, Lit("Async"), JobPoolPriority_Async);
|
|
||||||
|
|
||||||
/* Ensure hyper pool has id 0.
|
|
||||||
* Pool ID 0 will be used when pushing a job from outside a pool (e.g. during startup) */
|
|
||||||
Assert(g->hyper_pool == 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////
|
|
||||||
//~ Win32 thread
|
|
||||||
|
|
||||||
JobImpl(W32_DummyJob, sig, id)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
DWORD WINAPI W32_Win32ThreadProc(LPVOID vt)
|
|
||||||
{
|
|
||||||
/* Convert thread to fiber */
|
|
||||||
W32_AcquireFiber(0);
|
|
||||||
Arena *perm = PermArena();
|
|
||||||
|
|
||||||
|
|
||||||
W32_Thread *t = (W32_Thread *)vt;
|
|
||||||
String thread_name_desc = StringF(perm, "[%F] %F", FmtSint(FiberId()), FmtString(t->thread_name));
|
|
||||||
char *thread_name_desc_cstr = CstrFromString(perm, thread_name_desc);
|
|
||||||
wchar_t *thread_name_desc_wstr = WstrFromString(perm, thread_name_desc);
|
|
||||||
|
|
||||||
/* Initialize COM */
|
|
||||||
CoInitializeEx(0, COINIT_MULTITHREADED);
|
|
||||||
|
|
||||||
/* Set thread name */
|
|
||||||
SetThreadDescription(GetCurrentThread(), thread_name_desc_wstr);
|
|
||||||
|
|
||||||
//LogInfoF("New thread \"%F\" created with ID %F", FmtString(StringFromCstrNoLimit(t->thread_name_cstr)), FmtUint(ThreadId()));
|
|
||||||
|
|
||||||
/* Enter thread entry point */
|
|
||||||
t->entry_point(t->thread_udata);
|
|
||||||
|
|
||||||
/* Uninitialize COM */
|
|
||||||
CoUninitialize();
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
W32_Thread *W32_StartThread(W32_ThreadFunc *entry_point, void *thread_udata, String thread_name)
|
|
||||||
{
|
|
||||||
W32_SharedJobState *g = &W32_shared_job_state;
|
|
||||||
TempArena scratch = BeginScratchNoConflict();
|
|
||||||
Arena *perm = PermArena();
|
|
||||||
Assert(entry_point != 0);
|
|
||||||
//LogInfoF("Creating thread \"%F\"", FmtString(thread_name));
|
|
||||||
|
|
||||||
W32_Thread *t = PushStruct(perm, W32_Thread);
|
|
||||||
t->thread_name = PushString(perm, thread_name);
|
|
||||||
t->entry_point = entry_point;
|
|
||||||
t->thread_udata = thread_udata;
|
|
||||||
|
|
||||||
t->handle = CreateThread(0, W32_FiberStackSize, W32_Win32ThreadProc, t, 0, 0);
|
|
||||||
if (!t->handle)
|
|
||||||
{
|
|
||||||
Panic(Lit("Failed to create thread"));
|
|
||||||
}
|
|
||||||
|
|
||||||
EndScratch(scratch);
|
|
||||||
return (W32_Thread *)t;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Returns 0 if the thread could not release in specified timeout (e.g. because it is still running) */
|
|
||||||
b32 W32_TryEndThread(W32_Thread *thread, f32 timeout_seconds)
|
|
||||||
{
|
|
||||||
W32_SharedJobState *g = &W32_shared_job_state;
|
|
||||||
b32 ok = 0;
|
|
||||||
W32_Thread *t = (W32_Thread *)thread;
|
|
||||||
HANDLE handle = t->handle;
|
|
||||||
if (handle)
|
|
||||||
{
|
|
||||||
/* Wait for thread to stop */
|
|
||||||
DWORD timeout_ms = (timeout_seconds > 10000000) ? INFINITE : RoundF32ToI32(timeout_seconds * 1000);
|
|
||||||
DWORD wait_result = WaitForSingleObject(handle, timeout_ms);
|
|
||||||
if (wait_result == WAIT_OBJECT_0)
|
|
||||||
{
|
|
||||||
/* Release thread */
|
|
||||||
ok = 1;
|
|
||||||
CloseHandle(handle);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ok;
|
|
||||||
}
|
|
||||||
|
|
||||||
void W32_WaitEndThread(W32_Thread *thread)
|
|
||||||
{
|
|
||||||
b32 ok = W32_TryEndThread(thread, F32Infinity);
|
|
||||||
Assert(ok);
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////
|
|
||||||
//~ Win32 fiber
|
|
||||||
|
|
||||||
//- Acquire fiber
|
|
||||||
/* If `pool` is 0, then the currently running thread will be converted into a fiber */
|
|
||||||
W32_Fiber *W32_AcquireFiber(W32_JobPool *pool)
|
|
||||||
{
|
|
||||||
W32_SharedJobState *g = &W32_shared_job_state;
|
|
||||||
i16 fiber_id = 0;
|
|
||||||
W32_Fiber *fiber = 0;
|
|
||||||
char *new_name_cstr = 0;
|
|
||||||
{
|
|
||||||
if (pool != 0)
|
|
||||||
{
|
|
||||||
LockTicketMutex(&pool->free_fibers_tm);
|
|
||||||
if (pool->first_free_fiber_id)
|
|
||||||
{
|
|
||||||
fiber_id = pool->first_free_fiber_id;
|
|
||||||
fiber = &g->fibers[fiber_id];
|
|
||||||
pool->first_free_fiber_id = fiber->return_id;
|
|
||||||
}
|
|
||||||
UnlockTicketMutex(&pool->free_fibers_tm);
|
|
||||||
}
|
|
||||||
if (!fiber_id)
|
|
||||||
{
|
|
||||||
LockTicketMutex(&g->fibers_tm);
|
|
||||||
{
|
|
||||||
{
|
|
||||||
fiber_id = g->num_fibers++;
|
|
||||||
if (fiber_id >= MaxFibers)
|
|
||||||
{
|
|
||||||
Panic(Lit("Max fibers reached"));
|
|
||||||
}
|
|
||||||
fiber = &g->fibers[fiber_id];
|
|
||||||
if (!g->fiber_names_arena)
|
|
||||||
{
|
|
||||||
g->fiber_names_arena = AcquireArena(Gibi(64));
|
|
||||||
}
|
|
||||||
new_name_cstr = PushStructs(g->fiber_names_arena, char, W32_FiberNameMaxSize);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
UnlockTicketMutex(&g->fibers_tm);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
if (new_name_cstr != 0)
|
|
||||||
{
|
|
||||||
fiber->id = fiber_id;
|
|
||||||
|
|
||||||
/* Id to ASCII */
|
|
||||||
i32 id_div = fiber_id;
|
|
||||||
char id_chars[64] = ZI;
|
|
||||||
i32 id_chars_len = 0;
|
|
||||||
do
|
|
||||||
{
|
|
||||||
i32 digit = id_div % 10;
|
|
||||||
id_div /= 10;
|
|
||||||
id_chars[id_chars_len] = ("0123456789")[digit];
|
|
||||||
++id_chars_len;
|
|
||||||
} while (id_div > 0);
|
|
||||||
i32 rev_start = 0;
|
|
||||||
i32 rev_end = id_chars_len - 1;
|
|
||||||
while (rev_start < rev_end)
|
|
||||||
{
|
|
||||||
char swp = id_chars[rev_start];
|
|
||||||
id_chars[rev_start] = id_chars[rev_end];
|
|
||||||
id_chars[rev_end] = swp;
|
|
||||||
++rev_start;
|
|
||||||
--rev_end;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Concat fiber name */
|
|
||||||
i32 name_size = 1;
|
|
||||||
Assert(sizeof(sizeof(W32_FiberNamePrefixCstr)) <= W32_FiberNameMaxSize);
|
|
||||||
CopyBytes(new_name_cstr, W32_FiberNamePrefixCstr, sizeof(W32_FiberNamePrefixCstr));
|
|
||||||
name_size += sizeof(W32_FiberNamePrefixCstr) - 2;
|
|
||||||
CopyBytes(new_name_cstr + name_size, id_chars, id_chars_len);
|
|
||||||
name_size += id_chars_len;
|
|
||||||
CopyBytes(new_name_cstr + name_size, W32_FiberNameSuffixCstr, sizeof(W32_FiberNameSuffixCstr));
|
|
||||||
name_size += sizeof(W32_FiberNameSuffixCstr) - 2;
|
|
||||||
|
|
||||||
fiber->name_cstr = new_name_cstr;
|
|
||||||
|
|
||||||
/* Init win32 fiber */
|
|
||||||
if (pool != 0)
|
|
||||||
{
|
|
||||||
fiber->pool = pool->id;
|
|
||||||
#if VIRTUAL_FIBERS
|
|
||||||
fiber->addr = CreateThread(0, W32_FiberStackSize, W32_VirtualFiberEntryPoint, (void *)(i64)fiber_id, 0, 0);
|
|
||||||
#else
|
|
||||||
fiber->addr = CreateFiber(W32_FiberStackSize, W32_FiberEntryPoint, (void *)(i64)fiber_id);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* Fiber is not a part of a job pool, convert thread to fiber */
|
|
||||||
fiber->addr = ConvertThreadToFiber((void *)(i64)fiber_id);
|
|
||||||
#if VIRTUAL_FIBERS
|
|
||||||
fiber->addr = GetCurrentThread();
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fiber->task = 0;
|
|
||||||
fiber->return_id = 0;
|
|
||||||
return fiber;
|
|
||||||
}
|
|
||||||
|
|
||||||
//- Release fiber
|
|
||||||
void W32_ReleaseFiber(W32_JobPool *pool, W32_Fiber *fiber)
|
|
||||||
{
|
|
||||||
LockTicketMutex(&pool->free_fibers_tm);
|
|
||||||
{
|
|
||||||
i16 fiber_id = fiber->id;
|
|
||||||
fiber->return_id = pool->first_free_fiber_id;
|
|
||||||
pool->first_free_fiber_id = fiber_id;
|
|
||||||
}
|
|
||||||
UnlockTicketMutex(&pool->free_fibers_tm);
|
|
||||||
}
|
|
||||||
|
|
||||||
//- Fiber id
|
|
||||||
ForceInline W32_Fiber *W32_FiberFromId(i16 id)
|
|
||||||
{
|
|
||||||
return id > 0 ? &W32_shared_job_state.fibers[id] : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void W32_SwitchToFiber(W32_Fiber *target)
|
|
||||||
{
|
|
||||||
#if VIRTUAL_FIBERS
|
|
||||||
W32_Fiber *self = W32_FiberFromId(FiberId());
|
|
||||||
Atomic8Set(&self->virtual_yield, 1);
|
|
||||||
/* Signal virtual target */
|
|
||||||
{
|
|
||||||
Atomic8Set(&target->virtual_yield, 0);
|
|
||||||
WakeByAddressSingle(&target->virtual_yield);
|
|
||||||
}
|
|
||||||
/* Wait for return */
|
|
||||||
{
|
|
||||||
i8 vswitch = 1;
|
|
||||||
while (vswitch != 0)
|
|
||||||
{
|
|
||||||
WaitOnAddress(&self->virtual_yield, &vswitch, sizeof(vswitch), INFINITE);
|
|
||||||
vswitch = Atomic8Fetch(&self->virtual_yield);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
SwitchToFiber(target->addr);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////
|
|
||||||
//~ Win32 fiber entry
|
|
||||||
|
|
||||||
void W32_FiberEntryPoint(void *_)
|
|
||||||
{
|
|
||||||
i16 fiber_id = FiberId();
|
|
||||||
volatile W32_Fiber *fiber = W32_FiberFromId(fiber_id);
|
|
||||||
W32_JobPool *pool = &W32_shared_job_state.job_pools[fiber->pool];
|
|
||||||
JobPoolId pool_id = fiber->pool;
|
|
||||||
char *fiber_name_cstr = fiber->name_cstr;
|
|
||||||
for (;;)
|
|
||||||
{
|
|
||||||
W32_Task *task = fiber->task;
|
|
||||||
Job *job = task->job;
|
|
||||||
|
|
||||||
/* Run task */
|
|
||||||
job->func(job->sig, task->task_id);
|
|
||||||
|
|
||||||
/* Check if we've completed the last task in the job */
|
|
||||||
if (Atomic32FetchAdd(&job->num_tasks_completed.v, 1) + 1 >= job->count)
|
|
||||||
{
|
|
||||||
/* Increment fence */
|
|
||||||
if (job->fence)
|
|
||||||
{
|
|
||||||
FetchAddFence(job->fence, 1);
|
|
||||||
}
|
|
||||||
/* Free job */
|
|
||||||
LockTicketMutex(&pool->free_jobs_tm);
|
|
||||||
{
|
|
||||||
SllStackPush(pool->first_free_job, job);
|
|
||||||
}
|
|
||||||
UnlockTicketMutex(&pool->free_jobs_tm);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Free task */
|
|
||||||
{
|
|
||||||
LockTicketMutex(&pool->free_tasks_tm);
|
|
||||||
{
|
|
||||||
SllStackPush(pool->first_free_task, task);
|
|
||||||
}
|
|
||||||
UnlockTicketMutex(&pool->free_tasks_tm);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Yield to worker */
|
|
||||||
{
|
|
||||||
W32_Fiber *parent_fiber = W32_FiberFromId(fiber->return_id);
|
|
||||||
W32_SwitchToFiber(parent_fiber);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
DWORD WINAPI W32_VirtualFiberEntryPoint(LPVOID arg)
|
|
||||||
{
|
|
||||||
ConvertThreadToFiber(arg);
|
|
||||||
|
|
||||||
Arena *perm = PermArena();
|
|
||||||
char *fiber_name_cstr = W32_FiberFromId(FiberId())->name_cstr;
|
|
||||||
wchar_t *fiber_name_wstr = WstrFromString(perm, StringFromCstrNoLimit(fiber_name_cstr));
|
|
||||||
SetThreadDescription(GetCurrentThread(), fiber_name_wstr);
|
|
||||||
|
|
||||||
CoInitializeEx(0, COINIT_MULTITHREADED);
|
|
||||||
W32_FiberEntryPoint(0);
|
|
||||||
CoUninitialize();
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////
|
|
||||||
//~ Win32 job worker entry
|
|
||||||
|
|
||||||
W32_ThreadDef(W32_JobWorkerEntryPoint, worker_ctx_arg)
|
|
||||||
{
|
|
||||||
W32_WorkerCtx *ctx = worker_ctx_arg;
|
|
||||||
JobPoolId pool_id = ctx->pool_id;
|
|
||||||
W32_JobPool *pool = &W32_shared_job_state.job_pools[pool_id];
|
|
||||||
i16 worker_fiber_id = FiberId();
|
|
||||||
|
|
||||||
|
|
||||||
{
|
|
||||||
/* TODO: Set win32 IO priority as well */
|
|
||||||
HANDLE thread_handle = GetCurrentThread();
|
|
||||||
switch (pool->priority)
|
|
||||||
{
|
|
||||||
case JobPoolPriority_Background:
|
|
||||||
{
|
|
||||||
SetThreadPriority(thread_handle, THREAD_PRIORITY_BELOW_NORMAL);
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case JobPoolPriority_Async:
|
|
||||||
{
|
|
||||||
SetThreadPriority(thread_handle, THREAD_PRIORITY_NORMAL);
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case JobPoolPriority_Graphics:
|
|
||||||
{
|
|
||||||
SetThreadPriority(thread_handle, THREAD_PRIORITY_ABOVE_NORMAL);
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case JobPoolPriority_Simulation:
|
|
||||||
{
|
|
||||||
SetThreadPriority(thread_handle, THREAD_PRIORITY_HIGHEST);
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case JobPoolPriority_Critical:
|
|
||||||
{
|
|
||||||
SetThreadPriority(thread_handle, THREAD_PRIORITY_HIGHEST);
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case JobPoolPriority_Audio:
|
|
||||||
{
|
|
||||||
/* https://learn.microsoft.com/en-us/windows/win32/procthread/multimedia-class-scheduler-service#registry-settings */
|
|
||||||
DWORD task = 0;
|
|
||||||
AvSetMmThreadCharacteristics(L"Pro Audio", &task);
|
|
||||||
SetThreadPriority(thread_handle, THREAD_PRIORITY_TIME_CRITICAL);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
W32_Fiber *task_fiber = 0;
|
|
||||||
i64 tasks_count = 0;
|
|
||||||
while (tasks_count >= 0)
|
|
||||||
{
|
|
||||||
W32_Task *task = 0;
|
|
||||||
{
|
|
||||||
LockTicketMutex(&pool->tasks_tm);
|
|
||||||
{
|
|
||||||
task = pool->first_task;
|
|
||||||
if (task)
|
|
||||||
{
|
|
||||||
SllQueuePop(pool->first_task, pool->last_task);
|
|
||||||
Atomic64FetchAdd(&pool->tasks_count.v, -1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
UnlockTicketMutex(&pool->tasks_tm);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (task)
|
|
||||||
{
|
|
||||||
if (task->fiber_id != 0)
|
|
||||||
{
|
|
||||||
/* Task is resuming with an existing fiber */
|
|
||||||
if (task_fiber)
|
|
||||||
{
|
|
||||||
W32_ReleaseFiber(pool, task_fiber);
|
|
||||||
}
|
|
||||||
task_fiber = W32_FiberFromId(task->fiber_id);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* Task is unstarted, wrap it in a fiber */
|
|
||||||
if (!task_fiber)
|
|
||||||
{
|
|
||||||
task_fiber = W32_AcquireFiber(pool);
|
|
||||||
}
|
|
||||||
task_fiber->task = task;
|
|
||||||
task->fiber_id = task_fiber->id;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Run task fiber */
|
|
||||||
task_fiber->return_id = worker_fiber_id;
|
|
||||||
W32_SwitchToFiber(task_fiber);
|
|
||||||
|
|
||||||
if (Atomic8Fetch(&task_fiber->status) == W32_FiberStatus_Suspending)
|
|
||||||
{
|
|
||||||
/* Fiber suspended during execution */
|
|
||||||
Atomic8Set(&task_fiber->status, W32_FiberStatus_Suspended);
|
|
||||||
task_fiber = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Park worker */
|
|
||||||
tasks_count = Atomic64Fetch(&pool->tasks_count.v);
|
|
||||||
while (tasks_count == 0)
|
|
||||||
{
|
|
||||||
/* TODO: Add toggleable flag that allows for workers to skip parking
|
|
||||||
* for high-throughput workloads, and instead just spin until work is available */
|
|
||||||
WaitOnAddress(&pool->tasks_count, &tasks_count, sizeof(tasks_count), INFINITE);
|
|
||||||
tasks_count = Atomic64Fetch(&pool->tasks_count.v);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Worker shutdown */
|
|
||||||
if (task_fiber)
|
|
||||||
{
|
|
||||||
W32_ReleaseFiber(pool, task_fiber);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////
|
|
||||||
//~ @hookimpl Fiber suspend/resume operations
|
|
||||||
|
|
||||||
void SuspendFiber(void)
|
|
||||||
{
|
|
||||||
i16 fiber_id = FiberId();
|
|
||||||
W32_Fiber *fiber = W32_FiberFromId(FiberId());
|
|
||||||
{
|
|
||||||
Atomic8Set(&fiber->status, W32_FiberStatus_Suspending);
|
|
||||||
W32_Fiber *parent_fiber = W32_FiberFromId(fiber->return_id);
|
|
||||||
W32_SwitchToFiber(parent_fiber);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* NOTE: Must only be called on fibers suspended via SuspendFiber */
|
|
||||||
void ResumeFibers(i16 fiber_ids_count, i16 *fiber_ids)
|
|
||||||
{
|
|
||||||
TempArena scratch = BeginScratchNoConflict();
|
|
||||||
W32_SharedJobState *g = &W32_shared_job_state;
|
|
||||||
i32 num_pools = Atomic32Fetch(&g->num_pools);
|
|
||||||
|
|
||||||
/* Group tasks by pool */
|
|
||||||
W32_TaskList *tasks_by_pool = PushStructs(scratch.arena, W32_TaskList, num_pools);
|
|
||||||
for (i16 id_index = 0; id_index < fiber_ids_count; ++id_index)
|
|
||||||
{
|
|
||||||
i16 fiber_id = fiber_ids[id_index];
|
|
||||||
W32_Fiber *fiber = W32_FiberFromId(fiber_id);
|
|
||||||
|
|
||||||
/* Wait for fiber to complete suspending */
|
|
||||||
W32_FiberStatus status = Atomic8Fetch(&fiber->status);
|
|
||||||
while (status != W32_FiberStatus_Suspended)
|
|
||||||
{
|
|
||||||
_mm_pause();
|
|
||||||
status = Atomic8Fetch(&fiber->status);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Update fiber status */
|
|
||||||
Atomic8Set(&fiber->status, W32_FiberStatus_None);
|
|
||||||
|
|
||||||
W32_Task *task = fiber->task;
|
|
||||||
// if (task->job->flags & JobFlag_Dedicated)
|
|
||||||
if (0)
|
|
||||||
{
|
|
||||||
/* TODO: Wake dedicated fiber right now */
|
|
||||||
WakeByAddressSingle(&fiber->status);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* Group task based on pool */
|
|
||||||
JobPoolId pool_id = fiber->pool;
|
|
||||||
W32_TaskList *pool_tasks = &tasks_by_pool[pool_id];
|
|
||||||
SllQueuePush(pool_tasks->first, pool_tasks->last, task);
|
|
||||||
++pool_tasks->count;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Submit tasks */
|
|
||||||
for (JobPoolId pool_id = 0; pool_id < num_pools; ++pool_id)
|
|
||||||
{
|
|
||||||
W32_TaskList *tasks = &tasks_by_pool[pool_id];
|
|
||||||
i16 count = tasks->count;
|
|
||||||
if (count > 0)
|
|
||||||
{
|
|
||||||
W32_JobPool *pool = &g->job_pools[pool_id];
|
|
||||||
|
|
||||||
/* Push tasks to front of queue */
|
|
||||||
LockTicketMutex(&pool->tasks_tm);
|
|
||||||
{
|
|
||||||
tasks->last->next = pool->first_task;
|
|
||||||
pool->first_task = tasks->first;
|
|
||||||
if (!pool->last_task)
|
|
||||||
{
|
|
||||||
pool->last_task = tasks->last;
|
|
||||||
}
|
|
||||||
Atomic64FetchAdd(&pool->tasks_count.v, count);
|
|
||||||
}
|
|
||||||
UnlockTicketMutex(&pool->tasks_tm);
|
|
||||||
|
|
||||||
/* Wake workers */
|
|
||||||
if (count >= W32_WakeAllWorkersThreshold)
|
|
||||||
{
|
|
||||||
WakeByAddressAll(&pool->tasks_count);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
for (i16 i = 0; i < count; ++i)
|
|
||||||
{
|
|
||||||
WakeByAddressSingle(&pool->tasks_count);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
EndScratch(scratch);
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////
|
|
||||||
//~ @hookimpl Job pool operations
|
|
||||||
|
|
||||||
JobPoolId InitJobPool(u32 thread_count, String name, JobPoolPriority priority)
|
|
||||||
{
|
|
||||||
W32_SharedJobState *g = &W32_shared_job_state;
|
|
||||||
JobPoolId pool_id = Atomic32FetchAdd(&g->num_pools, 1);
|
|
||||||
W32_JobPool *pool = &g->job_pools[pool_id];
|
|
||||||
pool->num_worker_threads = thread_count;
|
|
||||||
pool->id = pool_id;
|
|
||||||
pool->name = name;
|
|
||||||
pool->priority = priority;
|
|
||||||
|
|
||||||
Arena *perm = PermArena();
|
|
||||||
pool->worker_threads = PushStructs(perm, W32_Thread *, pool->num_worker_threads);
|
|
||||||
pool->worker_contexts = PushStructs(perm, W32_WorkerCtx, pool->num_worker_threads);
|
|
||||||
for (i32 i = 0; i < pool->num_worker_threads; ++i)
|
|
||||||
{
|
|
||||||
W32_WorkerCtx *ctx = &pool->worker_contexts[i];
|
|
||||||
ctx->pool_id = pool_id;
|
|
||||||
ctx->id = i;
|
|
||||||
String worker_name = StringF(perm, "%F [%F]", FmtString(name), FmtSint(i));
|
|
||||||
pool->worker_threads[i] = W32_StartThread(W32_JobWorkerEntryPoint, ctx, worker_name);
|
|
||||||
}
|
|
||||||
return pool_id;
|
|
||||||
}
|
|
||||||
|
|
||||||
JobPoolId CurrentPool(void)
|
|
||||||
{
|
|
||||||
W32_Fiber *fiber = W32_FiberFromId(FiberId());
|
|
||||||
return fiber->pool;
|
|
||||||
}
|
|
||||||
|
|
||||||
JobPoolId AsyncPool(void)
|
|
||||||
{
|
|
||||||
return W32_shared_job_state.async_pool;
|
|
||||||
}
|
|
||||||
|
|
||||||
JobPoolId HyperPool(void)
|
|
||||||
{
|
|
||||||
return W32_shared_job_state.hyper_pool;
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////
|
|
||||||
//~ @hookimpl Job operations
|
|
||||||
|
|
||||||
Job *OpenJob(JobFunc *func, JobPoolId pool_id)
|
|
||||||
{
|
|
||||||
W32_JobPool *pool = &W32_shared_job_state.job_pools[pool_id];
|
|
||||||
Job *job = 0;
|
|
||||||
{
|
|
||||||
Arena *job_arena = 0;
|
|
||||||
{
|
|
||||||
{
|
|
||||||
LockTicketMutex(&pool->free_jobs_tm);
|
|
||||||
Job *free_job = pool->first_free_job;
|
|
||||||
if (free_job)
|
|
||||||
{
|
|
||||||
pool->first_free_job = free_job->next;
|
|
||||||
job_arena = free_job->arena;
|
|
||||||
}
|
|
||||||
UnlockTicketMutex(&pool->free_jobs_tm);
|
|
||||||
}
|
|
||||||
if (job_arena)
|
|
||||||
{
|
|
||||||
ResetArena(job_arena);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
job_arena = AcquireArena(Mebi(4));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
job = PushStruct(job_arena, Job);
|
|
||||||
job->arena = job_arena;
|
|
||||||
}
|
|
||||||
job->pool = pool_id;
|
|
||||||
job->func = func;
|
|
||||||
job->count = 1;
|
|
||||||
return job;
|
|
||||||
}
|
|
||||||
|
|
||||||
u32 CloseJob(Job *job)
|
|
||||||
{
|
|
||||||
TempArena scratch = BeginScratchNoConflict();
|
|
||||||
|
|
||||||
JobPoolId pool_id = job->pool;
|
|
||||||
W32_JobPool *pool = &W32_shared_job_state.job_pools[pool_id];
|
|
||||||
u32 num_tasks = job->count;
|
|
||||||
|
|
||||||
if (num_tasks == 0)
|
|
||||||
{
|
|
||||||
job->func = W32_DummyJob;
|
|
||||||
num_tasks = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Allocate tasks from free list */
|
|
||||||
u32 num_tasks_allocated = 0;
|
|
||||||
W32_Task **tasks_array = PushStructsNoZero(scratch.arena, W32_Task *, num_tasks);
|
|
||||||
{
|
|
||||||
LockTicketMutex(&pool->free_tasks_tm);
|
|
||||||
{
|
|
||||||
while (num_tasks_allocated < num_tasks)
|
|
||||||
{
|
|
||||||
W32_Task *task = pool->first_free_task;
|
|
||||||
if (task)
|
|
||||||
{
|
|
||||||
tasks_array[num_tasks_allocated++] = task;
|
|
||||||
SllStackPop(pool->first_free_task);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
UnlockTicketMutex(&pool->free_tasks_tm);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Allocate new tasks from memory */
|
|
||||||
u32 remaining = num_tasks - num_tasks_allocated;
|
|
||||||
if (remaining > 0)
|
|
||||||
{
|
|
||||||
Arena *perm = PermArena();
|
|
||||||
PushAlign(perm, CachelineSize);
|
|
||||||
W32_Task *pushed_tasks = PushStructsNoZero(perm, W32_Task, remaining);
|
|
||||||
for (u32 i = 0; i < remaining; ++i)
|
|
||||||
{
|
|
||||||
tasks_array[num_tasks_allocated + i] = &pushed_tasks[i];
|
|
||||||
}
|
|
||||||
num_tasks_allocated += remaining;
|
|
||||||
PushAlign(perm, CachelineSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* FIXME: Handle dedicated jobs separately */
|
|
||||||
|
|
||||||
/* Generate task list */
|
|
||||||
W32_TaskList tasks = ZI;
|
|
||||||
for (u32 i = 0; i < num_tasks; ++i)
|
|
||||||
{
|
|
||||||
W32_Task *task = tasks_array[i];
|
|
||||||
ZeroStruct(task);
|
|
||||||
task->job = job;
|
|
||||||
task->task_id = tasks.count++;
|
|
||||||
SllQueuePush(tasks.first, tasks.last, task);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Push tasks to back of pool */
|
|
||||||
{
|
|
||||||
LockTicketMutex(&pool->tasks_tm);
|
|
||||||
{
|
|
||||||
if (pool->last_task)
|
|
||||||
{
|
|
||||||
pool->last_task->next = tasks.first;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
pool->first_task = tasks.first;
|
|
||||||
}
|
|
||||||
pool->last_task = tasks.last;
|
|
||||||
Atomic64FetchAdd(&pool->tasks_count.v, num_tasks);
|
|
||||||
}
|
|
||||||
UnlockTicketMutex(&pool->tasks_tm);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Wake workers */
|
|
||||||
if (num_tasks >= W32_WakeAllWorkersThreshold)
|
|
||||||
{
|
|
||||||
WakeByAddressAll(&pool->tasks_count);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
for (u32 i = 0; i < num_tasks; ++i)
|
|
||||||
{
|
|
||||||
WakeByAddressSingle(&pool->tasks_count);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
EndScratch(scratch);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
@ -1,205 +0,0 @@
|
|||||||
////////////////////////////////////////////////////////////
|
|
||||||
//~ Win32 libs
|
|
||||||
|
|
||||||
#pragma warning(push, 0)
|
|
||||||
# include <WinSock2.h>
|
|
||||||
# include <TlHelp32.h>
|
|
||||||
# include <WS2tcpip.h>
|
|
||||||
# include <windowsx.h>
|
|
||||||
# include <ShlObj_core.h>
|
|
||||||
# include <fileapi.h>
|
|
||||||
# include <dwmapi.h>
|
|
||||||
# include <avrt.h>
|
|
||||||
# include <shellapi.h>
|
|
||||||
#pragma warning(pop)
|
|
||||||
|
|
||||||
#pragma comment(lib, "kernel32")
|
|
||||||
#pragma comment(lib, "user32")
|
|
||||||
#pragma comment(lib, "shell32")
|
|
||||||
#pragma comment(lib, "ole32")
|
|
||||||
#pragma comment(lib, "winmm")
|
|
||||||
#pragma comment(lib, "dwmapi")
|
|
||||||
#pragma comment(lib, "synchronization")
|
|
||||||
#pragma comment(lib, "avrt")
|
|
||||||
#pragma comment(lib, "ws2_32.lib")
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////
|
|
||||||
//~ Thread types
|
|
||||||
|
|
||||||
#define W32_ThreadDef(name, arg_name) void name(void *arg_name)
|
|
||||||
typedef W32_ThreadDef(W32_ThreadFunc, data);
|
|
||||||
|
|
||||||
Struct(W32_Thread)
|
|
||||||
{
|
|
||||||
String thread_name;
|
|
||||||
W32_ThreadFunc *entry_point;
|
|
||||||
void *thread_udata;
|
|
||||||
|
|
||||||
HANDLE handle;
|
|
||||||
};
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////
|
|
||||||
//~ Fiber types
|
|
||||||
|
|
||||||
#define W32_FiberStackSize Mebi(1)
|
|
||||||
#define W32_FiberNamePrefixCstr "["
|
|
||||||
#define W32_FiberNameSuffixCstr "]"
|
|
||||||
#define W32_FiberNameMaxSize 64
|
|
||||||
|
|
||||||
Enum(W32_FiberStatus)
|
|
||||||
{
|
|
||||||
W32_FiberStatus_None = 0,
|
|
||||||
W32_FiberStatus_Suspending = 1,
|
|
||||||
W32_FiberStatus_Suspended = 2,
|
|
||||||
};
|
|
||||||
|
|
||||||
AlignedStruct(W32_Fiber, CachelineSize)
|
|
||||||
{
|
|
||||||
/* ---------------------------------------------------- */
|
|
||||||
void *addr; /* 08 bytes */
|
|
||||||
/* ---------------------------------------------------- */
|
|
||||||
char *name_cstr; /* 08 bytes */
|
|
||||||
/* ---------------------------------------------------- */
|
|
||||||
Atomic8 status; /* 01 bytes */
|
|
||||||
Atomic8 virtual_yield; /* 01 bytes */
|
|
||||||
i16 id; /* 02 bytes */
|
|
||||||
i16 pool; /* 02 bytes */
|
|
||||||
i16 return_id; /* 02 bytes */
|
|
||||||
/* ---------------------------------------------------- */
|
|
||||||
struct W32_Task *task; /* 08 bytes */
|
|
||||||
/* ---------------------------------------------------- */
|
|
||||||
};
|
|
||||||
StaticAssert(alignof(W32_Fiber) == CachelineSize && sizeof(W32_Fiber) % CachelineSize == 0); /* False sharing validation */
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////
|
|
||||||
//~ Job pool types
|
|
||||||
|
|
||||||
//- Worker ctx
|
|
||||||
AlignedStruct(W32_WorkerCtx, CachelineSize)
|
|
||||||
{
|
|
||||||
JobPoolId pool_id;
|
|
||||||
i32 id;
|
|
||||||
};
|
|
||||||
|
|
||||||
//- Task
|
|
||||||
Struct(W32_Task)
|
|
||||||
{
|
|
||||||
W32_Task *next;
|
|
||||||
Job *job;
|
|
||||||
u32 task_id;
|
|
||||||
i16 fiber_id;
|
|
||||||
};
|
|
||||||
|
|
||||||
Struct(W32_TaskList)
|
|
||||||
{
|
|
||||||
W32_Task *first;
|
|
||||||
W32_Task *last;
|
|
||||||
u64 count;
|
|
||||||
};
|
|
||||||
|
|
||||||
//- Job pool
|
|
||||||
Struct(W32_JobPool)
|
|
||||||
{
|
|
||||||
JobPoolId id;
|
|
||||||
String name;
|
|
||||||
JobPoolPriority priority;
|
|
||||||
i32 num_worker_threads;
|
|
||||||
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 types
|
|
||||||
|
|
||||||
/* Arbitrary threshold for determining when to use a looped WakeByAddressSingle vs a WakeByAddressAll to wake parked workers */
|
|
||||||
#define W32_WakeAllWorkersThreshold 8
|
|
||||||
#define W32_MaxJobPools 1024
|
|
||||||
|
|
||||||
Struct(W32_SharedJobState)
|
|
||||||
{
|
|
||||||
JobPoolId async_pool;
|
|
||||||
JobPoolId hyper_pool;
|
|
||||||
|
|
||||||
//- Fibers
|
|
||||||
AlignedBlock(CachelineSize)
|
|
||||||
{
|
|
||||||
TicketMutex fibers_tm;
|
|
||||||
i16 num_fibers;
|
|
||||||
W32_Fiber fibers[MaxFibers];
|
|
||||||
Arena *fiber_names_arena;
|
|
||||||
};
|
|
||||||
|
|
||||||
//- Job pools
|
|
||||||
AlignedBlock(CachelineSize)
|
|
||||||
{
|
|
||||||
Atomic32 num_pools;
|
|
||||||
W32_JobPool job_pools[W32_MaxJobPools];
|
|
||||||
};
|
|
||||||
|
|
||||||
} extern W32_shared_job_state;
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////
|
|
||||||
//~ Startup
|
|
||||||
|
|
||||||
void StartupJobs(void);
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////
|
|
||||||
//~ Thread operations
|
|
||||||
|
|
||||||
DWORD WINAPI W32_Win32ThreadProc(LPVOID vt);
|
|
||||||
W32_Thread *W32_StartThread(W32_ThreadFunc *entry_point, void *thread_udata, String thread_name);
|
|
||||||
b32 W32_TryEndThread(W32_Thread *thread, f32 timeout_seconds);
|
|
||||||
void W32_WaitEndThread(W32_Thread *thread);
|
|
||||||
JobDecl(W32_DummyJob, EmptySig);
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////
|
|
||||||
//~ Fiber operations
|
|
||||||
|
|
||||||
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_SwitchToFiber(W32_Fiber *target);
|
|
||||||
void W32_YieldFiber(W32_Fiber *fiber, W32_Fiber *parent_fiber);
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////
|
|
||||||
//~ Fiber entry
|
|
||||||
|
|
||||||
void W32_FiberEntryPoint(void *wi32_fiber_data);
|
|
||||||
DWORD WINAPI W32_VirtualFiberEntryPoint(LPVOID arg);
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////
|
|
||||||
//~ Job worker entry
|
|
||||||
|
|
||||||
W32_ThreadDef(W32_JobWorkerEntryPoint, worker_ctx_arg);
|
|
||||||
@ -45,7 +45,6 @@ void W32_Log(i32 level, String msg)
|
|||||||
DateTime datetime = LocalDateTime();
|
DateTime datetime = LocalDateTime();
|
||||||
i64 now_ns = TimeNs();
|
i64 now_ns = TimeNs();
|
||||||
i32 thread_id = GetCurrentThreadId();
|
i32 thread_id = GetCurrentThreadId();
|
||||||
i32 fiber_id = FiberId();
|
|
||||||
|
|
||||||
//- Log message to file
|
//- Log message to file
|
||||||
/* TODO: Log asynchronously */
|
/* TODO: Log asynchronously */
|
||||||
@ -53,7 +52,7 @@ void W32_Log(i32 level, String msg)
|
|||||||
String shorthand = settings.shorthand;
|
String shorthand = settings.shorthand;
|
||||||
String msg_formatted = StringF(
|
String msg_formatted = StringF(
|
||||||
scratch.arena,
|
scratch.arena,
|
||||||
"[%F:%F:%F.%F] |%F| <%F> [%F] %F\n",
|
"[%F:%F:%F.%F] <%F> [%F] %F\n",
|
||||||
|
|
||||||
/* Time */
|
/* Time */
|
||||||
FmtUintZ(datetime.hour, 2),
|
FmtUintZ(datetime.hour, 2),
|
||||||
@ -64,9 +63,6 @@ void W32_Log(i32 level, String msg)
|
|||||||
/* Thread id */
|
/* Thread id */
|
||||||
FmtUintZ(thread_id, 5),
|
FmtUintZ(thread_id, 5),
|
||||||
|
|
||||||
/* Fiber id */
|
|
||||||
FmtUintZ(fiber_id, 4),
|
|
||||||
|
|
||||||
/* Level */
|
/* Level */
|
||||||
FmtString(shorthand),
|
FmtString(shorthand),
|
||||||
|
|
||||||
@ -87,7 +83,6 @@ void W32_Log(i32 level, String msg)
|
|||||||
ev->time_ns = now_ns;
|
ev->time_ns = now_ns;
|
||||||
ev->level = level;
|
ev->level = level;
|
||||||
ev->thread_id = thread_id;
|
ev->thread_id = thread_id;
|
||||||
ev->fiber_id = fiber_id;
|
|
||||||
ev->id = g->logs_count++;
|
ev->id = g->logs_count++;
|
||||||
ev->level_id = g->log_level_counts[level]++;
|
ev->level_id = g->log_level_counts[level]++;
|
||||||
Atomic64Set(&g->readable_logs_count, g->logs_count);
|
Atomic64Set(&g->readable_logs_count, g->logs_count);
|
||||||
|
|||||||
42
src/base/base_win32/base_win32_memory.c
Normal file
42
src/base/base_win32/base_win32_memory.c
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
//~ @hookimpl Memory allocation
|
||||||
|
|
||||||
|
//- Reserve
|
||||||
|
|
||||||
|
void *ReserveMemory(u64 size)
|
||||||
|
{
|
||||||
|
void *ptr = VirtualAlloc(0, size, MEM_RESERVE, PAGE_NOACCESS);
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ReleaseMemory(void *address)
|
||||||
|
{
|
||||||
|
VirtualFree(address, 0, MEM_RELEASE);
|
||||||
|
}
|
||||||
|
|
||||||
|
//- Commit
|
||||||
|
|
||||||
|
void *CommitMemory(void *address, u64 size)
|
||||||
|
{
|
||||||
|
void *ptr = VirtualAlloc(address, size, MEM_COMMIT, PAGE_READWRITE);
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DecommitMemory(void *address, u64 size)
|
||||||
|
{
|
||||||
|
VirtualFree(address, size, MEM_DECOMMIT);
|
||||||
|
}
|
||||||
|
|
||||||
|
//- Protect
|
||||||
|
|
||||||
|
void SetMemoryReadonly(void *address, u64 size)
|
||||||
|
{
|
||||||
|
DWORD old;
|
||||||
|
VirtualProtect(address, size, PAGE_READONLY, &old);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetMemoryReadWrite(void *address, u64 size)
|
||||||
|
{
|
||||||
|
DWORD old;
|
||||||
|
VirtualProtect(address, size, PAGE_READWRITE, &old);
|
||||||
|
}
|
||||||
79
src/base/base_win32/base_win32_wave.c
Normal file
79
src/base/base_win32/base_win32_wave.c
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
//~ Thread
|
||||||
|
|
||||||
|
void W32_InitCurrentThread(String name)
|
||||||
|
{
|
||||||
|
/* Init thread arenas */
|
||||||
|
{
|
||||||
|
ThreadArenasCtx *tctx = &t_arena_ctx;
|
||||||
|
tctx->perm_arena = AcquireArena(Gibi(64));
|
||||||
|
for (i32 i = 0; i < (i32)countof(tctx->scratch_arenas); ++i)
|
||||||
|
{
|
||||||
|
tctx->scratch_arenas[i] = AcquireArena(Gibi(64));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Arena *perm = PermArena();
|
||||||
|
|
||||||
|
/* Fixme: Set thread name */
|
||||||
|
// SetThreadDescription(GetCurrentThread(), thread_name_wstr);
|
||||||
|
//LogInfoF("New thread \"%F\" created with ID %F", FmtString(StringFromCstrNoLimit(t->thread_name_cstr)), FmtUint(ThreadId()));
|
||||||
|
|
||||||
|
/* Initialize COM */
|
||||||
|
CoInitializeEx(0, COINIT_MULTITHREADED);
|
||||||
|
}
|
||||||
|
|
||||||
|
DWORD WINAPI W32_ThreadProc(LPVOID thread_args_vp)
|
||||||
|
{
|
||||||
|
W32_ThreadArgs *thread_args = (W32_ThreadArgs *)thread_args_vp;
|
||||||
|
W32_InitCurrentThread(thread_args->name);
|
||||||
|
thread_args->entry(thread_args->lane, thread_args->udata);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
//~ @hookimpl Dispatch
|
||||||
|
|
||||||
|
void DispatchWave(u32 num_lanes, WaveLaneEntryFunc *entry, void *udata)
|
||||||
|
{
|
||||||
|
/* FIXME: Impl */
|
||||||
|
|
||||||
|
Arena *perm = PermArena();
|
||||||
|
|
||||||
|
WaveCtx *wave_ctx = 0;
|
||||||
|
{
|
||||||
|
PushAlign(perm, CachelineSize);
|
||||||
|
wave_ctx = PushStruct(perm, WaveCtx);
|
||||||
|
PushAlign(perm, CachelineSize);
|
||||||
|
}
|
||||||
|
wave_ctx->lanes_count = num_lanes;
|
||||||
|
|
||||||
|
for (u32 lane_idx = 0; lane_idx < num_lanes; ++lane_idx)
|
||||||
|
{
|
||||||
|
PushAlign(perm, CachelineSize);
|
||||||
|
WaveLaneCtx *lane_ctx = PushStruct(perm, WaveLaneCtx);
|
||||||
|
PushAlign(perm, CachelineSize);
|
||||||
|
|
||||||
|
lane_ctx->idx = lane_idx;
|
||||||
|
lane_ctx->wave = wave_ctx;
|
||||||
|
|
||||||
|
W32_ThreadArgs *thread_args = PushStruct(perm, W32_ThreadArgs);
|
||||||
|
thread_args->lane = lane_ctx;
|
||||||
|
thread_args->udata = udata;
|
||||||
|
thread_args->entry = entry;
|
||||||
|
|
||||||
|
HANDLE handle = CreateThread(0, Mebi(4), W32_ThreadProc, thread_args, 0, 0);
|
||||||
|
if (!handle)
|
||||||
|
{
|
||||||
|
Panic(Lit("Failed to create thread"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
//~ @hookimpl Thread
|
||||||
|
|
||||||
|
i32 ThreadId(void)
|
||||||
|
{
|
||||||
|
return GetCurrentThreadId();
|
||||||
|
}
|
||||||
17
src/base/base_win32/base_win32_wave.h
Normal file
17
src/base/base_win32/base_win32_wave.h
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
//~ Thread types
|
||||||
|
|
||||||
|
Struct(W32_ThreadArgs)
|
||||||
|
{
|
||||||
|
void *udata;
|
||||||
|
WaveLaneCtx *lane;
|
||||||
|
WaveLaneEntryFunc *entry;
|
||||||
|
String name;
|
||||||
|
};
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
//~ Thread
|
||||||
|
|
||||||
|
void W32_InitCurrentThread(String name);
|
||||||
|
|
||||||
|
DWORD WINAPI W32_ThreadProc(LPVOID thread_args_vp);
|
||||||
636
src/meta/meta.c
636
src/meta/meta.c
@ -103,26 +103,6 @@ LineCol LineColFromPos(String data, i64 pos)
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////
|
|
||||||
//~ OS command job
|
|
||||||
|
|
||||||
Struct(RunCommandResult)
|
|
||||||
{
|
|
||||||
OS_CommandResult cmd_result;
|
|
||||||
i64 elapsed_ns;
|
|
||||||
};
|
|
||||||
|
|
||||||
JobDecl(RunCommand, { String *cmds; RunCommandResult *results; });
|
|
||||||
JobImpl(RunCommand, sig, id)
|
|
||||||
{
|
|
||||||
i64 start_ns = TimeNs();
|
|
||||||
Arena *arena = PermArena();
|
|
||||||
String cmd = sig->cmds[id];
|
|
||||||
RunCommandResult *result = &sig->results[id];
|
|
||||||
result->cmd_result = OS_RunCommand(arena, cmd);
|
|
||||||
result->elapsed_ns = TimeNs() - start_ns;
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
//~ Compiler params
|
//~ Compiler params
|
||||||
|
|
||||||
@ -148,6 +128,8 @@ Struct(CompilerParams)
|
|||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
//~ Build step job
|
//~ Build step job
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
|
||||||
Enum(StepParamsFlag)
|
Enum(StepParamsFlag)
|
||||||
{
|
{
|
||||||
StepParamsFlag_None = 0,
|
StepParamsFlag_None = 0,
|
||||||
@ -155,6 +137,7 @@ Enum(StepParamsFlag)
|
|||||||
StepParamsFlag_CompileShaders = (1 << 1),
|
StepParamsFlag_CompileShaders = (1 << 1),
|
||||||
StepParamsFlag_CompileEmbeddedDirs = (1 << 2),
|
StepParamsFlag_CompileEmbeddedDirs = (1 << 2),
|
||||||
StepParamsFlag_CompileArc = (1 << 3),
|
StepParamsFlag_CompileArc = (1 << 3),
|
||||||
|
StepParamsFlag_RunCommand = (1 << 4),
|
||||||
};
|
};
|
||||||
|
|
||||||
Struct(StepParams)
|
Struct(StepParams)
|
||||||
@ -166,6 +149,8 @@ Struct(StepParams)
|
|||||||
|
|
||||||
String arc_store;
|
String arc_store;
|
||||||
String arc_dir;
|
String arc_dir;
|
||||||
|
|
||||||
|
String cmd;
|
||||||
};
|
};
|
||||||
|
|
||||||
Struct(StepResult)
|
Struct(StepResult)
|
||||||
@ -173,6 +158,7 @@ Struct(StepResult)
|
|||||||
StringList obj_files;
|
StringList obj_files;
|
||||||
StringList output_lines;
|
StringList output_lines;
|
||||||
M_ErrorList meta_errors;
|
M_ErrorList meta_errors;
|
||||||
|
OS_CommandResult cmd_result;
|
||||||
i32 return_code;
|
i32 return_code;
|
||||||
i64 elapsed_ns;
|
i64 elapsed_ns;
|
||||||
};
|
};
|
||||||
@ -198,8 +184,7 @@ void InheritStepResults(Arena *arena, StepResult *dst, u64 srcs_count, StepResul
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
JobDecl(Step, { StepParams *params; StepResult *results; });
|
void Step(void)
|
||||||
JobImpl(Step, sig, id)
|
|
||||||
{
|
{
|
||||||
StepParams *params = &sig->params[id];
|
StepParams *params = &sig->params[id];
|
||||||
StepParamsFlag flags = params->flags;
|
StepParamsFlag flags = params->flags;
|
||||||
@ -409,12 +394,12 @@ JobImpl(Step, sig, id)
|
|||||||
|
|
||||||
if (flags & StepParamsFlag_CompileShaders)
|
if (flags & StepParamsFlag_CompileShaders)
|
||||||
{
|
{
|
||||||
String shaders_out_dir = shader_store_name;
|
String shader_store_name = shader_store_name;
|
||||||
OS_Mkdir(shaders_out_dir);
|
OS_Mkdir(shader_store_name);
|
||||||
{
|
{
|
||||||
/* Remove all old shaders */
|
/* Remove all old shaders */
|
||||||
StringList files = ZI;
|
StringList files = ZI;
|
||||||
F_FilesFromDir(arena, &files, shaders_out_dir, F_IterFlag_None);
|
F_FilesFromDir(arena, &files, shader_store_name, F_IterFlag_None);
|
||||||
for (StringListNode *n = files.first; n; n = n->next)
|
for (StringListNode *n = files.first; n; n = n->next)
|
||||||
{
|
{
|
||||||
String file = n->s;
|
String file = n->s;
|
||||||
@ -427,7 +412,7 @@ JobImpl(Step, sig, id)
|
|||||||
}
|
}
|
||||||
|
|
||||||
//- Generate GPU file & shader entries
|
//- Generate GPU file & shader entries
|
||||||
String gpu_out_file = F_GetFull(arena, StringF(arena, "%F_gen_gpu.gpu", FmtString(cmdline.leaf_layer_name)));
|
String gpu_out_file = F_GetFull(arena, StringF(arena, "%F_gen_gpu.hlsl", FmtString(cmdline.leaf_layer_name)));
|
||||||
Enum(ShaderEntryKind) { ShaderEntryKind_VS, ShaderEntryKind_PS, ShaderEntryKind_CS, };
|
Enum(ShaderEntryKind) { ShaderEntryKind_VS, ShaderEntryKind_PS, ShaderEntryKind_CS, };
|
||||||
Struct(ShaderEntry) { ShaderEntry *next; ShaderEntryKind kind; String name; };
|
Struct(ShaderEntry) { ShaderEntry *next; ShaderEntryKind kind; String name; };
|
||||||
ShaderEntry *first_shader_entry = 0;
|
ShaderEntry *first_shader_entry = 0;
|
||||||
@ -538,7 +523,7 @@ JobImpl(Step, sig, id)
|
|||||||
"cmd /c dxc.exe -T %F -E %F -Fo %F/%F %F %F %F",
|
"cmd /c dxc.exe -T %F -E %F -Fo %F/%F %F %F %F",
|
||||||
FmtString(target),
|
FmtString(target),
|
||||||
FmtString(e->name),
|
FmtString(e->name),
|
||||||
FmtString(shaders_out_dir),
|
FmtString(shader_store_name),
|
||||||
FmtString(e->name),
|
FmtString(e->name),
|
||||||
FmtString(gpu_out_file),
|
FmtString(gpu_out_file),
|
||||||
FmtString(StringFromList(arena, cp.defs, Lit(" "))),
|
FmtString(StringFromList(arena, cp.defs, Lit(" "))),
|
||||||
@ -817,23 +802,76 @@ JobImpl(Step, sig, id)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//////////////////////////////
|
||||||
|
//- Run command
|
||||||
|
|
||||||
|
if (flags & StepParamsFlag_RunCommand)
|
||||||
|
{
|
||||||
|
result->cmd_result = OS_RunCommand(arena, cmd);
|
||||||
|
}
|
||||||
|
|
||||||
result->elapsed_ns = TimeNs() - start_ns;
|
result->elapsed_ns = TimeNs() - start_ns;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
//~ Stub funcs
|
||||||
|
|
||||||
|
|
||||||
|
/* FIXME: Move and implement these */
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Struct(CpuTopologyInfo)
|
||||||
|
{
|
||||||
|
i32 num_logical_cores;
|
||||||
|
};
|
||||||
|
|
||||||
|
CpuTopologyInfo GetCpuTopologyInfo(void)
|
||||||
|
{
|
||||||
|
/* FIXME: Implement this */
|
||||||
|
CpuTopologyInfo res = ZI;
|
||||||
|
res.num_logical_cores = 8;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
//~ Startup
|
//~ Startup
|
||||||
|
|
||||||
JobDecl(Build, EmptySig);
|
void BuildEntryPoint(WaveLaneCtx *lane, void *udata)
|
||||||
JobImpl(Build, _, __)
|
|
||||||
{
|
{
|
||||||
Arena *arena = PermArena();
|
Arena *arena = PermArena();
|
||||||
M_ErrorList errors = ZI;
|
M_ErrorList meta_parse_errors = ZI;
|
||||||
i32 ret = 0;
|
i32 ret = 0;
|
||||||
|
|
||||||
//////////////////////////////
|
//////////////////////////////
|
||||||
//- Dirty check
|
//- Dirty check
|
||||||
|
|
||||||
/* Return rebuild code if metaprogram is dirty */
|
/* Return rebuild code if metaprogram is dirty */
|
||||||
|
if (lane->idx == 0)
|
||||||
{
|
{
|
||||||
/* Read old metahash */
|
/* Read old metahash */
|
||||||
u64 old_metahash = 0;
|
u64 old_metahash = 0;
|
||||||
@ -873,10 +911,10 @@ JobImpl(Build, _, __)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//////////////////////////////
|
//////////////////////////////
|
||||||
//- Command line
|
//- Command line
|
||||||
|
|
||||||
|
if (lane->idx == 0)
|
||||||
{
|
{
|
||||||
String layer_name = ZI;
|
String layer_name = ZI;
|
||||||
CommandlineArg arg = CommandlineArgFromName(Lit("layer"));
|
CommandlineArg arg = CommandlineArgFromName(Lit("layer"));
|
||||||
@ -993,6 +1031,9 @@ JobImpl(Build, _, __)
|
|||||||
//////////////////////////////
|
//////////////////////////////
|
||||||
//- Parse
|
//- Parse
|
||||||
|
|
||||||
|
M_Layer flattened = ZI;
|
||||||
|
if (lane->idx == 0)
|
||||||
|
{
|
||||||
//- Lex layers
|
//- Lex layers
|
||||||
M_TokenFileList lexed = ZI;
|
M_TokenFileList lexed = ZI;
|
||||||
{
|
{
|
||||||
@ -1008,19 +1049,534 @@ JobImpl(Build, _, __)
|
|||||||
}
|
}
|
||||||
|
|
||||||
//- Flatten layers
|
//- Flatten layers
|
||||||
M_Layer flattened = ZI;
|
|
||||||
{
|
{
|
||||||
StringList starting_layer_names = ZI;
|
StringList starting_layer_names = ZI;
|
||||||
PushStringToList(arena, &starting_layer_names, cmdline.leaf_layer_name);
|
PushStringToList(arena, &starting_layer_names, cmdline.leaf_layer_name);
|
||||||
flattened = M_GetFlattenedEntries(arena, parsed, starting_layer_names);
|
flattened = M_GetFlattenedEntries(arena, parsed, starting_layer_names);
|
||||||
}
|
}
|
||||||
M_AppendErrors(arena, &errors, flattened.errors);
|
M_AppendErrors(arena, &meta_parse_errors, flattened.errors);
|
||||||
ret = errors.count > 0;
|
}
|
||||||
|
|
||||||
|
//////////////////////////////
|
||||||
|
//- Prep
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ## Phase 1
|
||||||
|
*
|
||||||
|
* <C compile oscmd> <shader compile oscmds>
|
||||||
|
* |
|
||||||
|
* <FULL BARRIER> -------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* ## Phase 2
|
||||||
|
* <shader resource arcs> <embedded resource arcs>
|
||||||
|
* | |
|
||||||
|
* <FULL BARRIER> -------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* ## Phase 3
|
||||||
|
* <shader resource oscmd> <embedded resource oscmds>
|
||||||
|
* <FULL BARRIER> -------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* ## Phase 4
|
||||||
|
*
|
||||||
|
* <link oscmd>
|
||||||
|
*/
|
||||||
|
|
||||||
|
Struct(SharedResults)
|
||||||
|
{
|
||||||
|
/* C compilation */
|
||||||
|
AlignedBlock(CachelineSize)
|
||||||
|
{
|
||||||
|
M_ErrorList errors;
|
||||||
|
StringList obj_files;
|
||||||
|
StringList output;
|
||||||
|
i32 return_code;
|
||||||
|
} c_comp;
|
||||||
|
|
||||||
|
/* HLSL compilation */
|
||||||
|
AlignedBlock(CachelineSize)
|
||||||
|
{
|
||||||
|
M_ErrorList errors;
|
||||||
|
StringList obj_files;
|
||||||
|
StringList output;
|
||||||
|
i32 return_code;
|
||||||
|
} gpu_comp;
|
||||||
|
|
||||||
|
/* Resource generation */
|
||||||
|
AlignedBlock(CachelineSize)
|
||||||
|
{
|
||||||
|
M_ErrorList errors;
|
||||||
|
StringList obj_files;
|
||||||
|
StringList output;
|
||||||
|
i32 return_code;
|
||||||
|
} res_comp;
|
||||||
|
};
|
||||||
|
|
||||||
|
SharedResults *shared = 0;
|
||||||
|
if (lane->idx == 0)
|
||||||
|
{
|
||||||
|
shared = PushStruct(arena, SharedResults);
|
||||||
|
}
|
||||||
|
WaveSyncBroadcast(lane, 0, &shared);
|
||||||
|
|
||||||
|
String shader_store_name = Lit("ShadersStore");
|
||||||
|
|
||||||
|
//////////////////////////////
|
||||||
|
//- Compile C
|
||||||
|
|
||||||
|
{
|
||||||
|
/* Compile C */
|
||||||
|
{
|
||||||
|
//- Generate C file
|
||||||
|
String c_out_file = F_GetFull(arena, StringF(arena, "%F_gen_c.c", FmtString(cmdline.leaf_layer_name)));
|
||||||
|
{
|
||||||
|
StringList c_store_lines = ZI;
|
||||||
|
StringList c_shader_lines = ZI;
|
||||||
|
StringList c_include_lines = ZI;
|
||||||
|
StringList c_startup_lines = ZI;
|
||||||
|
{
|
||||||
|
for (M_Entry *entry = flattened.first; entry->valid; entry = entry->next)
|
||||||
|
{
|
||||||
|
M_EntryKind kind = entry->kind;
|
||||||
|
M_Token *entry_tok = entry->name_token;
|
||||||
|
M_Token *arg0_tok = entry->arg_tokens[0];
|
||||||
|
M_Token *arg1_tok = entry->arg_tokens[1];
|
||||||
|
switch (kind)
|
||||||
|
{
|
||||||
|
default: break;
|
||||||
|
case M_EntryKind_EmbedDir:
|
||||||
|
{
|
||||||
|
if (arg0_tok->valid && arg1_tok->valid)
|
||||||
|
{
|
||||||
|
String store_name = arg0_tok->s;
|
||||||
|
String token_file = arg1_tok->file->name;
|
||||||
|
String token_parent_dir = F_GetParentDir(token_file);
|
||||||
|
String arg_dir = arg1_tok->s;
|
||||||
|
String full = F_GetFullCrossPlatform(arena, StringF(arena, "%F/%F", FmtString(token_parent_dir), FmtString(arg_dir)));
|
||||||
|
if (F_IsDir(full))
|
||||||
|
{
|
||||||
|
u64 hash = HashFnv64(Fnv64Basis, StringF(arena, "%F/", FmtString(store_name)));
|
||||||
|
String line = StringF(arena, "ResourceStore %F = { 0x%F };", FmtString(store_name), FmtHex(hash));
|
||||||
|
PushStringToList(arena, &c_store_lines, line);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
String err = StringF(arena, "Directory '%F' not found", FmtString(full));
|
||||||
|
M_PushError(arena, &shared->c_comp.errors, arg1_tok, err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
M_PushError(arena, &shared->c_comp.errors, entry_tok, Lit("Expected resource store & directory name"));
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
case M_EntryKind_VertexShader:
|
||||||
|
case M_EntryKind_PixelShader:
|
||||||
|
case M_EntryKind_ComputeShader:
|
||||||
|
{
|
||||||
|
if (arg0_tok->valid)
|
||||||
|
{
|
||||||
|
String shader_type = kind == M_EntryKind_VertexShader ? Lit("VertexShader")
|
||||||
|
: kind == M_EntryKind_PixelShader ? Lit("PixelShader")
|
||||||
|
: kind == M_EntryKind_ComputeShader ? Lit("ComputeShader")
|
||||||
|
: Lit("");
|
||||||
|
String shader_name = arg0_tok->s;
|
||||||
|
u64 hash = HashFnv64(Fnv64Basis, StringF(arena, "%F/%F", FmtString(shader_store_name), FmtString(shader_name)));
|
||||||
|
String line = StringF(arena, "%F %F = { 0x%F };", FmtString(shader_type), FmtString(shader_name), FmtHex(hash));
|
||||||
|
PushStringToList(arena, &c_shader_lines, line);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
M_PushError(arena, &shared->c_comp.errors, entry_tok, Lit("Expected shader name"));
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
case M_EntryKind_IncludeC:
|
||||||
|
{
|
||||||
|
if (arg0_tok->valid)
|
||||||
|
{
|
||||||
|
String token_file = arg0_tok->file->name;
|
||||||
|
String token_parent_dir = F_GetParentDir(token_file);
|
||||||
|
String arg_file = arg0_tok->s;
|
||||||
|
String full = F_GetFull(arena, StringF(arena, "%F/%F", FmtString(token_parent_dir), FmtString(arg_file)));
|
||||||
|
if (F_IsFile(full))
|
||||||
|
{
|
||||||
|
String line = StringF(arena, "#include \"%F\"", FmtString(full));
|
||||||
|
PushStringToList(arena, &c_include_lines, line);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
String err = StringF(arena, "File '%F' not found", FmtString(full));
|
||||||
|
M_PushError(arena, &shared->c_comp.errors, arg0_tok, err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
M_PushError(arena, &shared->c_comp.errors, entry_tok, Lit("Expected file name"));
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
case M_EntryKind_Startup:
|
||||||
|
{
|
||||||
|
if (arg0_tok->valid)
|
||||||
|
{
|
||||||
|
String startup = arg0_tok->s;
|
||||||
|
String line = StringF(arena, " %F();", FmtString(startup));
|
||||||
|
PushStringToList(arena, &c_startup_lines, line);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
M_PushError(arena, &shared->c_comp.errors, entry_tok, Lit("Expected startup function name"));
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (&shared->c_comp.errors.count == 0)
|
||||||
|
{
|
||||||
|
StringList c_out_lines = ZI;
|
||||||
|
PushStringToList(arena, &c_out_lines, Lit("// Auto generated file"));
|
||||||
|
/* Include base layer */
|
||||||
|
{
|
||||||
|
String base_inc_path = F_GetFull(arena, Lit("../src/base/base_inc.h"));
|
||||||
|
PushStringToList(arena, &c_out_lines, Lit(""));
|
||||||
|
PushStringToList(arena, &c_out_lines, Lit("//- Base layer includes"));
|
||||||
|
PushStringToList(arena, &c_out_lines, StringF(arena, "#include \"%F\"", FmtString(base_inc_path)));
|
||||||
|
}
|
||||||
|
/* Define resource stores */
|
||||||
|
if (c_store_lines.count > 0)
|
||||||
|
{
|
||||||
|
PushStringToList(arena, &c_out_lines, Lit(""));
|
||||||
|
PushStringToList(arena, &c_out_lines, Lit("//- Resource stores"));
|
||||||
|
for (StringListNode *n = c_store_lines.first; n; n = n->next)
|
||||||
|
{
|
||||||
|
PushStringToList(arena, &c_out_lines, n->s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* Define shaders */
|
||||||
|
if (c_shader_lines.count > 0)
|
||||||
|
{
|
||||||
|
PushStringToList(arena, &c_out_lines, Lit(""));
|
||||||
|
PushStringToList(arena, &c_out_lines, Lit("//- Shaders"));
|
||||||
|
for (StringListNode *n = c_shader_lines.first; n; n = n->next)
|
||||||
|
{
|
||||||
|
PushStringToList(arena, &c_out_lines, n->s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* Include dependency layers */
|
||||||
|
if (c_include_lines.count > 0)
|
||||||
|
{
|
||||||
|
PushStringToList(arena, &c_out_lines, Lit(""));
|
||||||
|
PushStringToList(arena, &c_out_lines, Lit("//- Dependency graph includes"));
|
||||||
|
for (StringListNode *n = c_include_lines.first; n; n = n->next)
|
||||||
|
{
|
||||||
|
PushStringToList(arena, &c_out_lines, n->s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* Define StartupLayers */
|
||||||
|
{
|
||||||
|
PushStringToList(arena, &c_out_lines, Lit(""));
|
||||||
|
PushStringToList(arena, &c_out_lines, Lit("//- Startup"));
|
||||||
|
PushStringToList(arena, &c_out_lines, Lit("void StartupLayers(void)"));
|
||||||
|
PushStringToList(arena, &c_out_lines, Lit("{"));
|
||||||
|
for (StringListNode *n = c_startup_lines.first; n; n = n->next)
|
||||||
|
{
|
||||||
|
PushStringToList(arena, &c_out_lines, n->s);
|
||||||
|
}
|
||||||
|
PushStringToList(arena, &c_out_lines, Lit("}"));
|
||||||
|
}
|
||||||
|
/* Write to file */
|
||||||
|
PushStringToList(arena, &c_out_lines, Lit(""));
|
||||||
|
String c_out = StringFromList(arena, c_out_lines, Lit("\n"));
|
||||||
|
F_ClearWrite(c_out_file, c_out);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//- Compile C
|
||||||
|
String c_out_obj_file = StringF(arena, "%F_gen_c.obj", FmtString(cmdline.leaf_layer_name));
|
||||||
|
PushStringToList(arena, &shared->c_comp.obj_files, c_out_obj_file);
|
||||||
|
if (shared->c_comp.errors.count == 0)
|
||||||
|
{
|
||||||
|
String cmd = StringF(arena,
|
||||||
|
"cmd /c cl.exe /c %F -Fo:%F %F %F %F %F",
|
||||||
|
FmtString(c_out_file),
|
||||||
|
FmtString(c_out_obj_file),
|
||||||
|
FmtString(StringFromList(arena, cp.flags_msvc, Lit(" "))),
|
||||||
|
FmtString(StringFromList(arena, cp.compiler_only_flags_msvc, Lit(" "))),
|
||||||
|
FmtString(StringFromList(arena, cp.warnings_msvc, Lit(" "))),
|
||||||
|
FmtString(StringFromList(arena, cp.defs, Lit(" "))));
|
||||||
|
OS_CommandResult cmd_result = OS_RunCommand(arena, cmd);
|
||||||
|
String cmd_output = TrimWhitespace(cmd_result.output);
|
||||||
|
if (cmd_output.len > 0)
|
||||||
|
{
|
||||||
|
PushStringToList(arena, &shared->c_comp.output, cmd_output);
|
||||||
|
}
|
||||||
|
shared->c_comp.return_code = cmd_result.code;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////
|
||||||
|
//- Compile shaders
|
||||||
|
|
||||||
|
{
|
||||||
|
OS_Mkdir(shader_store_name);
|
||||||
|
{
|
||||||
|
/* Remove all old shaders */
|
||||||
|
StringList files = ZI;
|
||||||
|
F_FilesFromDir(arena, &files, shader_store_name, F_IterFlag_None);
|
||||||
|
for (StringListNode *n = files.first; n; n = n->next)
|
||||||
|
{
|
||||||
|
String file = n->s;
|
||||||
|
/* Safety check to prevent non-shader files from being removed */
|
||||||
|
if (StringEndsWith(file, Lit("VS")) || StringEndsWith(file, Lit("CS")) || StringEndsWith(file, Lit("PS")))
|
||||||
|
{
|
||||||
|
OS_Rm(n->s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//- Generate GPU file & shader entries
|
||||||
|
String gpu_out_file = F_GetFull(arena, StringF(arena, "%F_gen_gpu.hlsl", FmtString(cmdline.leaf_layer_name)));
|
||||||
|
Enum(ShaderEntryKind) { ShaderEntryKind_VS, ShaderEntryKind_PS, ShaderEntryKind_CS, };
|
||||||
|
Struct(ShaderEntry) { ShaderEntry *next; ShaderEntryKind kind; String name; };
|
||||||
|
ShaderEntry *first_shader_entry = 0;
|
||||||
|
ShaderEntry *last_shader_entry = 0;
|
||||||
|
u64 shader_entries_count = 0;
|
||||||
|
{
|
||||||
|
StringList gpu_include_lines = ZI;
|
||||||
|
{
|
||||||
|
for (M_Entry *entry = flattened.first; entry->valid; entry = entry->next)
|
||||||
|
{
|
||||||
|
M_EntryKind kind = entry->kind;
|
||||||
|
M_Token *entry_tok = entry->name_token;
|
||||||
|
M_Token *arg0_tok = entry->arg_tokens[0];
|
||||||
|
M_Token *arg1_tok = entry->arg_tokens[1];
|
||||||
|
switch (kind)
|
||||||
|
{
|
||||||
|
default: break;
|
||||||
|
case M_EntryKind_IncludeGpu:
|
||||||
|
{
|
||||||
|
if (arg0_tok->valid)
|
||||||
|
{
|
||||||
|
String token_file = arg0_tok->file->name;
|
||||||
|
String token_parent_dir = F_GetParentDir(token_file);
|
||||||
|
String arg_file = arg0_tok->s;
|
||||||
|
String full = F_GetFull(arena, StringF(arena, "%F/%F", FmtString(token_parent_dir), FmtString(arg_file)));
|
||||||
|
if (F_IsFile(full))
|
||||||
|
{
|
||||||
|
String line = StringF(arena, "#include \"%F\"", FmtString(full));
|
||||||
|
PushStringToList(arena, &gpu_include_lines, line);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
String err = StringF(arena, "File '%F' not found", FmtString(full));
|
||||||
|
M_PushError(arena, &shared->gpu_comp.errors, arg0_tok, err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
M_PushError(arena, &shared->gpu_comp.errors, entry_tok, Lit("Expected file name"));
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
case M_EntryKind_VertexShader:
|
||||||
|
case M_EntryKind_PixelShader:
|
||||||
|
case M_EntryKind_ComputeShader:
|
||||||
|
{
|
||||||
|
if (arg0_tok->valid)
|
||||||
|
{
|
||||||
|
ShaderEntryKind shader_kind = kind == M_EntryKind_VertexShader ? ShaderEntryKind_VS
|
||||||
|
: kind == M_EntryKind_PixelShader ? ShaderEntryKind_PS
|
||||||
|
: kind == M_EntryKind_ComputeShader ? ShaderEntryKind_CS
|
||||||
|
: ShaderEntryKind_VS;
|
||||||
|
String shader_name = arg0_tok->s;
|
||||||
|
ShaderEntry *e = PushStruct(arena, ShaderEntry);
|
||||||
|
e->kind = shader_kind;
|
||||||
|
e->name = shader_name;
|
||||||
|
SllQueuePush(first_shader_entry, last_shader_entry, e);
|
||||||
|
++shader_entries_count;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
M_PushError(arena, &shared->gpu_comp.errors, entry_tok, Lit("Expected shader name"));
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (shared->gpu_comp.errors.count == 0)
|
||||||
|
{
|
||||||
|
StringList gpu_out_lines = ZI;
|
||||||
|
PushStringToList(arena, &gpu_out_lines, Lit("// Auto generated file"));
|
||||||
|
/* Include base layer */
|
||||||
|
{
|
||||||
|
String base_inc_path = F_GetFull(arena, Lit("../src/base/base_inc.h"));
|
||||||
|
PushStringToList(arena, &gpu_out_lines, Lit(""));
|
||||||
|
PushStringToList(arena, &gpu_out_lines, Lit("//- Base layer includes"));
|
||||||
|
PushStringToList(arena, &gpu_out_lines, StringF(arena, "#include \"%F\"", FmtString(base_inc_path)));
|
||||||
|
}
|
||||||
|
/* Include dependency layers */
|
||||||
|
if (gpu_out_lines.count > 0)
|
||||||
|
{
|
||||||
|
PushStringToList(arena, &gpu_out_lines, Lit(""));
|
||||||
|
PushStringToList(arena, &gpu_out_lines, Lit("//- Dependency graph includes"));
|
||||||
|
for (StringListNode *n = gpu_include_lines.first; n; n = n->next)
|
||||||
|
{
|
||||||
|
PushStringToList(arena, &gpu_out_lines, n->s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* Write to file */
|
||||||
|
PushStringToList(arena, &gpu_out_lines, Lit(""));
|
||||||
|
String c_out = StringFromList(arena, gpu_out_lines, Lit("\n"));
|
||||||
|
F_ClearWrite(gpu_out_file, c_out);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//- Compile shaders
|
||||||
|
String *compile_cmds = PushStructs(arena, String, shader_entries_count);
|
||||||
|
{
|
||||||
|
i32 i = 0;
|
||||||
|
for (ShaderEntry *e = first_shader_entry; e; e = e->next)
|
||||||
|
{
|
||||||
|
String target = e->kind == ShaderEntryKind_VS ? Lit("vs_6_6")
|
||||||
|
: e->kind == ShaderEntryKind_PS ? Lit("ps_6_6")
|
||||||
|
: e->kind == ShaderEntryKind_CS ? Lit("cs_6_6")
|
||||||
|
: Lit("vs_6_6");
|
||||||
|
String *compile_cmd = &compile_cmds[i];
|
||||||
|
*compile_cmd = StringF(arena,
|
||||||
|
"cmd /c dxc.exe -T %F -E %F -Fo %F/%F %F %F %F",
|
||||||
|
FmtString(target),
|
||||||
|
FmtString(e->name),
|
||||||
|
FmtString(shader_store_name),
|
||||||
|
FmtString(e->name),
|
||||||
|
FmtString(gpu_out_file),
|
||||||
|
FmtString(StringFromList(arena, cp.defs, Lit(" "))),
|
||||||
|
FmtString(StringFromList(arena, cp.flags_dxc, Lit(" "))));
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (ShaderEntry *e = first_shader_entry; e; e = e->next)
|
||||||
|
{
|
||||||
|
String target = e->kind == ShaderEntryKind_VS ? Lit("vs_6_6")
|
||||||
|
: e->kind == ShaderEntryKind_PS ? Lit("ps_6_6")
|
||||||
|
: e->kind == ShaderEntryKind_CS ? Lit("cs_6_6")
|
||||||
|
: Lit("vs_6_6");
|
||||||
|
String compile_cmd = StringF(arena,
|
||||||
|
"cmd /c dxc.exe -T %F -E %F -Fo %F/%F %F %F %F",
|
||||||
|
FmtString(target),
|
||||||
|
FmtString(e->name),
|
||||||
|
FmtString(shader_store_name),
|
||||||
|
FmtString(e->name),
|
||||||
|
FmtString(gpu_out_file),
|
||||||
|
FmtString(StringFromList(arena, cp.defs, Lit(" "))),
|
||||||
|
FmtString(StringFromList(arena, cp.flags_dxc, Lit(" "))));
|
||||||
|
|
||||||
|
OS_CommandResult cmd_result = OS_RunCommand(arena, compile_cmd);
|
||||||
|
|
||||||
|
if (cmd_result.code == 0)
|
||||||
|
{
|
||||||
|
// f64 elapsed = SecondsFromNs(cmd_result.elapsed_ns);
|
||||||
|
f64 elapsed = 0;
|
||||||
|
PushStringToList(arena, &shared->gpu_comp.output, StringF(arena, "%F:%F %Fs", FmtString(F_GetFileName(gpu_out_file)), FmtString(e->name), FmtFloat(elapsed)));
|
||||||
|
if (cmd_result.output.len > 0)
|
||||||
|
{
|
||||||
|
PushStringToList(arena, &shared->gpu_comp.output, cmd_result.output);
|
||||||
|
}
|
||||||
|
cmd_result.code = cmd_result.code;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//- Build embedded shader archive
|
||||||
|
// if (result->code == 0)
|
||||||
|
// {
|
||||||
|
// StepParams arc_params = *params;
|
||||||
|
// StepResult arc_results = ZI;
|
||||||
|
|
||||||
|
// arc_params.flags = StepParamsFlag_CompileArc;
|
||||||
|
// arc_params.arc_store = shader_store_name;
|
||||||
|
// arc_params.arc_dir = shader_store_name;
|
||||||
|
|
||||||
|
// u32 job_count = 0; Fence job_fence = ZI;
|
||||||
|
// job_count += RunJob(Step, .fence = &job_fence, .sig.params = &arc_params, .sig.results = &arc_results);
|
||||||
|
// YieldOnFence(&job_fence, job_count);
|
||||||
|
// InheritStepResults(arena, result, 1, &arc_results);
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////
|
||||||
|
//- Compile resources
|
||||||
|
|
||||||
|
|
||||||
|
//////////////////////////////
|
||||||
|
//- Link
|
||||||
|
|
||||||
|
{
|
||||||
|
String exe_file = StringF(arena, "%F.exe", FmtString(cmdline.leaf_layer_name));
|
||||||
|
{
|
||||||
|
/* Wait for exe to become writable (wait for program to close) */
|
||||||
|
f32 timeout = 1.0;
|
||||||
|
OS_File file = OS_OpenFile(exe_file, OS_FileFlag_Write, NsFromSeconds(timeout));
|
||||||
|
OS_CloseFile(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
i64 link_elapsed_ns = 0;
|
||||||
|
if (ret == 0)
|
||||||
|
{
|
||||||
|
i64 start_ns = TimeNs();
|
||||||
|
String program_obj_files_str = StringFromList(arena, shared->c_comp.obj_files, Lit(" "));
|
||||||
|
String shader_obj_files_str = StringFromList(arena, shared->gpu_comp.obj_files, Lit(" "));
|
||||||
|
String resource_obj_files_str = StringFromList(arena, shared->res_comp.obj_files, Lit(" "));
|
||||||
|
|
||||||
|
String cmd = StringF(arena,
|
||||||
|
"cmd /c link.exe %F %F %F /OUT:%F %F %F",
|
||||||
|
FmtString(program_obj_files_str),
|
||||||
|
FmtString(shader_obj_files_str),
|
||||||
|
FmtString(resource_obj_files_str),
|
||||||
|
FmtString(exe_file),
|
||||||
|
FmtString(StringFromList(arena, cp.flags_msvc, Lit(" "))),
|
||||||
|
FmtString(StringFromList(arena, cp.linker_only_flags_msvc, Lit(" "))));
|
||||||
|
OS_CommandResult result = OS_RunCommand(arena, cmd);
|
||||||
|
String output = TrimWhitespace(result.output);
|
||||||
|
if (output.len > 0)
|
||||||
|
{
|
||||||
|
EchoLine(output);
|
||||||
|
}
|
||||||
|
ret = result.code;
|
||||||
|
link_elapsed_ns = TimeNs() - start_ns;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ret == 0)
|
||||||
|
{
|
||||||
|
EchoLine(StringF(arena, ">>>>> Linked in %Fs", FmtFloat(SecondsFromNs(link_elapsed_ns))));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//////////////////////////////
|
//////////////////////////////
|
||||||
//- Compile & link
|
//- Compile & link
|
||||||
|
|
||||||
if (errors.count == 0)
|
#if 0
|
||||||
|
ret = meta_parse_errors.count > 0;
|
||||||
|
if (ret == 0)
|
||||||
{
|
{
|
||||||
//- Compile
|
//- Compile
|
||||||
StepParams params_array[3] = ZI;
|
StepParams params_array[3] = ZI;
|
||||||
@ -1115,12 +1671,13 @@ JobImpl(Build, _, __)
|
|||||||
EchoLine(StringF(arena, ">>>>> Linked in %Fs", FmtFloat(SecondsFromNs(link_elapsed_ns))));
|
EchoLine(StringF(arena, ">>>>> Linked in %Fs", FmtFloat(SecondsFromNs(link_elapsed_ns))));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
//////////////////////////////
|
//////////////////////////////
|
||||||
//- Echo errors
|
//- Echo meta errors
|
||||||
|
|
||||||
for (M_Error *e = errors.first; e; e = e->next)
|
for (M_Error *e = meta_parse_errors.first; e; e = e->next)
|
||||||
{
|
{
|
||||||
String msg = ZI;
|
String msg = ZI;
|
||||||
M_Token *token = e->token;
|
M_Token *token = e->token;
|
||||||
@ -1165,5 +1722,8 @@ JobImpl(Build, _, __)
|
|||||||
void StartupLayers(void)
|
void StartupLayers(void)
|
||||||
{
|
{
|
||||||
OS_Startup();
|
OS_Startup();
|
||||||
RunJob(Build, .pool = HyperPool());
|
|
||||||
|
CpuTopologyInfo cpu_info = GetCpuTopologyInfo();
|
||||||
|
// DispatchWave(cpu_info.num_logical_cores - 1, BuildEntryPoint, 0);
|
||||||
|
DispatchWave(1, BuildEntryPoint, 0);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1 +0,0 @@
|
|||||||
|
|
||||||
Loading…
Reference in New Issue
Block a user