runtime job pools

This commit is contained in:
jacob 2025-10-25 12:02:55 -05:00
parent 5f7de288ac
commit c724b79c2c
14 changed files with 184 additions and 1139 deletions

View File

@ -1,63 +1,28 @@
////////////////////////////////////////////////////////////
//~ Opaque types
Struct(Job);
////////////////////////////////////////////////////////////
//~ Job pool types
/* Job pools contain worker threads with their own thread priorities
* and affinities based on the intended context of the pool. */
Enum(JobPool)
typedef i32 JobPoolId;
Enum(JobPoolPriority)
{
JobPool_Inherit = -1,
/* Contains un-affinitized worker threads.
* Meant to take on temporary high-throughput work that is allowed to
* interfere with all other pools (e.g. program startup, loading a level,
* etc). */
JobPool_Hyper = 0,
/* Contains worker threads affinitized to cores that don't interfere with workers in specialized pools.
* Meant to consume asynchronous work from higher priority pools. */
JobPool_Background = 1,
/* Contains worker threads affinitized to cores that don't interfere with workers in other specialized pools.
* These pools are meant to only have work pushed onto them from jobs within the same pool (e.g. 100 jobs pushed onto the
* User pool will not interfere with cores running the Sim pool). */
JobPool_Audio = 2,
JobPool_User = 3,
JobPool_Sim = 4,
JobPool_Count
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,
/* A dedicated job is a heavy weight job that will receive its own OS
* thread and will never yield. When the fiber running the job suspends
* itself, the dedicated thread will perform a blocking wait rather than
* yielding the thread to another fiber. This is mainly useful for
* long-running dispatcher-esque jobs that block on OS primitives, since
* occupying a worker thread (and thereby preventing non-blocking jobs from
* running on that worker) is unwanted.
*
* For example, Win32 window message processing is required by the OS to
* occur on the same thread that initially created the window, which means
* it actually must run inside a dedicated job to prevent message processing
* from yielding & resuming on another thread. The message processing loop
* can block until messages are received from the OS without having to
* occupy a job worker while it blocks, and can then schedule yielded
* jobs onto job worker pools based on the processed messages.
*/
JobFlag_Dedicated = (1 << 0),
JobFlag_None = (0),
};
Struct(Job)
@ -69,12 +34,12 @@ Struct(Job)
/* Initialized & constant after OpenJob */
Arena *arena;
JobFunc *func;
JobPool pool;
JobPoolId pool;
/* Configurable between OpenJob & CloseJob */
JobFlag flags;
i32 count;
Fence *fence;
JobFlag flags;
void *sig;
};
@ -89,6 +54,26 @@ void InitJobSystem(void);
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
@ -96,7 +81,7 @@ void ResumeFibers(i16 fiber_ids_count, i16 *fiber_ids); /* NOTE: Must only be c
#define JobDecl(job, sigdef) \
typedef struct job##_Sig sigdef job##_Sig; \
Struct(job##_Desc) { JobFunc *func; JobPool pool; u32 count; Fence *fence; JobFlag flags; job##_Sig sig; }; \
Struct(job##_Desc) { JobFunc *func; JobPoolId pool; u32 count; Fence *fence; JobFlag flags; job##_Sig sig; }; \
void job(job##_Sig *, i32); \
StaticAssert(1)
@ -121,7 +106,7 @@ void ResumeFibers(i16 fiber_ids_count, i16 *fiber_ids); /* NOTE: Must only be c
*/
#define RunJob(job_func, ...) (1); \
do { \
job_func##_Desc __desc = { .count = 1, .pool = JobPool_Inherit, .func = job_func, __VA_ARGS__ }; \
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; \
@ -131,5 +116,5 @@ do {
CloseJob(__job); \
} while (0)
Job *OpenJob(JobFunc *func, JobPool pool_kind);
Job *OpenJob(JobFunc *func, JobPoolId pool_kind);
u32 CloseJob(Job *job);

View File

@ -11,78 +11,13 @@ void InitJobSystem(void)
W32_AcquireFiber(0); /* Convert main thread to fiber */
Arena *perm = PermArena();
/* Init job pools */
for (JobPool pool_kind = 0; pool_kind < (i32)countof(g->job_pools); ++pool_kind)
{
W32_JobPool *pool = &g->job_pools[pool_kind];
/* FIXME */
}
/* TODO: Dynamic thread counts */
g->hyper_pool = InitJobPool(8, Lit("Hyper"), JobPoolPriority_Critical);
g->async_pool = InitJobPool(8, Lit("Async"), JobPoolPriority_Async);
//- Start job workers
/* TODO: Heuristic worker counts & affinities */
{
__profn("Start job workers");
for (JobPool pool_kind = 0; pool_kind < (i32)countof(g->job_pools); ++pool_kind)
{
W32_JobPool *pool = &g->job_pools[pool_kind];
String name_fmt = ZI;
i32 prof_group = PROF_THREAD_GROUP_FIBERS - Mebi(pool_kind);
switch (pool_kind)
{
case JobPool_Sim:
{
name_fmt = Lit("Sim worker #%F");
pool->num_worker_threads = 4;
pool->thread_affinity_mask = 0x000000000000000Full;
pool->thread_priority = THREAD_PRIORITY_ABOVE_NORMAL;
} break;
case JobPool_User:
{
name_fmt = Lit("User worker #%F");
pool->num_worker_threads = 4;
pool->thread_affinity_mask = 0x00000000000000F0ull;
pool->thread_priority = THREAD_PRIORITY_ABOVE_NORMAL;
} break;
case JobPool_Audio:
{
name_fmt = Lit("Audio worker #%F");
pool->num_worker_threads = 2;
pool->thread_affinity_mask = 0x0000000000000300ull;
pool->thread_priority = THREAD_PRIORITY_TIME_CRITICAL;
pool->thread_is_audio = 1;
} break;
case JobPool_Background:
{
name_fmt = Lit("Background worker #%F");
pool->num_worker_threads = 2;
pool->thread_affinity_mask = 0x0000000000000C00ull;
pool->thread_priority = THREAD_PRIORITY_NORMAL;
} break;
case JobPool_Hyper:
{
name_fmt = Lit("Hyper worker #%F");
pool->num_worker_threads = 8;
pool->thread_priority = THREAD_PRIORITY_HIGHEST;
} break;
}
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_kind = pool_kind;
ctx->id = i;
String name = FormatString(perm, name_fmt, FmtSint(i));
pool->worker_threads[i] = W32_StartThread(W32_JobWorkerEntryPoint, ctx, name, prof_group + i);
}
}
}
//OnExit(ShutdownJobs);
/* 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);
}
////////////////////////////////////////////////////////////
@ -266,7 +201,7 @@ W32_Fiber *W32_AcquireFiber(W32_JobPool *pool)
if (pool != 0)
{
__profn("CreateFiber");
fiber->pool = pool->kind;
fiber->pool = pool->id;
#if VIRTUAL_FIBERS
fiber->addr = CreateThread(0, W32_FiberStackSize, W32_VirtualFiberEntryPoint, (void *)(i64)fiber_id, 0, 0);
#else
@ -338,9 +273,9 @@ 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];
JobPool pool_kind = fiber->pool;
JobPoolId pool_id = fiber->pool;
char *fiber_name_cstr = fiber->name_cstr;
__prof_fiber_enter(fiber_name_cstr, PROF_THREAD_GROUP_FIBERS - Mebi(pool_kind) + Kibi(1) + fiber->id);
__prof_fiber_enter(fiber_name_cstr, PROF_THREAD_GROUP_FIBERS - Mebi(pool_id) + Kibi(1) + fiber->id);
for (;;)
{
W32_Task *task = fiber->task;
@ -404,54 +339,49 @@ DWORD WINAPI W32_VirtualFiberEntryPoint(LPVOID arg)
W32_ThreadDef(W32_JobWorkerEntryPoint, worker_ctx_arg)
{
W32_WorkerCtx *ctx = worker_ctx_arg;
JobPool pool_kind = ctx->pool_kind;
W32_JobPool *pool = &W32_shared_job_state.job_pools[pool_kind];
JobPoolId pool_id = ctx->pool_id;
W32_JobPool *pool = &W32_shared_job_state.job_pools[pool_id];
i16 worker_fiber_id = FiberId();
{
/* TODO: Heuristic pinning */
/* TODO: Pin non-worker threads to other cores */
/* 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;
#if 0
if (pool->thread_priority != 0)
case JobPoolPriority_Async:
{
__profn("Set priority");
b32 ok = SetThreadPriority(thread_handle, pool->thread_priority) != 0;
Assert(ok);
}
#endif
SetThreadPriority(thread_handle, THREAD_PRIORITY_NORMAL);
} break;
#if 0
if (pool->thread_affinity_mask)
case JobPoolPriority_Graphics:
{
__profn("Set affinity");
b32 ok = SetThreadAffinityMask(thread_handle, pool->thread_affinity_mask) != 0;
#if RtcIsEnabled || ProfilingIsEnabled
{
/* Retry until external tools can set correct process affinity */
i32 delay_ms = 16;
while (!ok && delay_ms <= 1024)
{
__profn("Affinity retry");
Sleep(delay_ms);
ok = SetThreadAffinityMask(thread_handle, pool->thread_affinity_mask) != 0;
delay_ms *= 2;
}
}
#endif
Assert(ok);
}
#endif
SetThreadPriority(thread_handle, THREAD_PRIORITY_ABOVE_NORMAL);
} break;
if (pool->thread_is_audio)
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 */
__profn("Set mm thread characteristics");
DWORD task = 0;
HANDLE mmc_handle = AvSetMmThreadCharacteristics(L"Pro Audio", &task);
Assert(mmc_handle);
AvSetMmThreadCharacteristics(L"Pro Audio", &task);
SetThreadPriority(thread_handle, THREAD_PRIORITY_TIME_CRITICAL);
}
}
}
@ -546,8 +476,12 @@ void SuspendFiber(void)
void ResumeFibers(i16 fiber_ids_count, i16 *fiber_ids)
{
__prof;
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[JobPool_Count] = ZI;
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];
@ -565,7 +499,8 @@ void ResumeFibers(i16 fiber_ids_count, i16 *fiber_ids)
Atomic8Set(&fiber->status, W32_FiberStatus_None);
W32_Task *task = fiber->task;
if (task->job->flags & JobFlag_Dedicated)
// if (task->job->flags & JobFlag_Dedicated)
if (0)
{
/* TODO: Wake dedicated fiber right now */
WakeByAddressSingle(&fiber->status);
@ -573,21 +508,21 @@ void ResumeFibers(i16 fiber_ids_count, i16 *fiber_ids)
else
{
/* Group task based on pool */
JobPool pool_kind = fiber->pool;
W32_TaskList *pool_tasks = &tasks_by_pool[pool_kind];
JobPoolId pool_id = fiber->pool;
W32_TaskList *pool_tasks = &tasks_by_pool[pool_id];
QueuePush(pool_tasks->first, pool_tasks->last, task);
++pool_tasks->count;
}
}
/* Submit tasks */
for (JobPool pool_kind = 0; pool_kind < JobPool_Count; ++pool_kind)
for (JobPoolId pool_id = 0; pool_id < num_pools; ++pool_id)
{
W32_TaskList *tasks = &tasks_by_pool[pool_kind];
W32_TaskList *tasks = &tasks_by_pool[pool_id];
i16 count = tasks->count;
if (count > 0)
{
W32_JobPool *pool = &W32_shared_job_state.job_pools[pool_kind];
W32_JobPool *pool = &g->job_pools[pool_id];
/* Push tasks to front of queue */
LockTicketMutex(&pool->tasks_tm);
@ -616,19 +551,62 @@ void ResumeFibers(i16 fiber_ids_count, i16 *fiber_ids)
}
}
}
EndScratch(scratch);
}
////////////////////////////////////////////////////////////
//~ @hoodef Job operations
//~ @hookdef Job pool operations
Job *OpenJob(JobFunc *func, JobPool pool_kind)
JobPoolId InitJobPool(u32 thread_count, String name, JobPoolPriority priority)
{
if (pool_kind == JobPool_Inherit)
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;
/* TODO: Real profiler group */
i32 profiler_group = 0;
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_Fiber *fiber = W32_FiberFromId(FiberId());
pool_kind = fiber->pool;
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, profiler_group + i);
}
W32_JobPool *pool = &W32_shared_job_state.job_pools[pool_kind];
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;
}
////////////////////////////////////////////////////////////
//~ @hookdef 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;
@ -655,7 +633,7 @@ Job *OpenJob(JobFunc *func, JobPool pool_kind)
job = PushStruct(job_arena, Job);
job->arena = job_arena;
}
job->pool = pool_kind;
job->pool = pool_id;
job->func = func;
job->count = 1;
return job;
@ -665,8 +643,8 @@ u32 CloseJob(Job *job)
{
TempArena scratch = BeginScratchNoConflict();
JobPool pool_kind = job->pool;
W32_JobPool *pool = &W32_shared_job_state.job_pools[pool_kind];
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)

View File

@ -78,7 +78,7 @@ StaticAssert(alignof(W32_Fiber) == CachelineSize && sizeof(W32_Fiber) % Cachelin
//- Worker ctx
AlignedStruct(W32_WorkerCtx, CachelineSize)
{
JobPool pool_kind;
JobPoolId pool_id;
i32 id;
};
@ -99,13 +99,12 @@ Struct(W32_TaskList)
};
//- Job pool
AlignedStruct(W32_JobPool, CachelineSize)
Struct(W32_JobPool)
{
JobPool kind;
JobPoolId id;
String name;
JobPoolPriority priority;
i32 num_worker_threads;
i32 thread_priority;
u64 thread_affinity_mask;
b32 thread_is_audio;
W32_Thread **worker_threads;
W32_WorkerCtx *worker_contexts;
@ -147,9 +146,13 @@ AlignedStruct(W32_JobPool, CachelineSize)
/* 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)
{
@ -162,8 +165,10 @@ Struct(W32_SharedJobState)
//- Job pools
AlignedBlock(CachelineSize)
{
W32_JobPool job_pools[JobPool_Count];
Atomic32 num_pools;
W32_JobPool job_pools[W32_MaxJobPools];
};
} extern W32_shared_job_state;
////////////////////////////////////////////////////////////

View File

@ -182,7 +182,7 @@ AC_Asset *F_LoadAsset(ResourceKey resource, f32 point_size, b32 wait)
{
AC_MarkLoading(asset);
{
Job *job = OpenJob(F_Load, JobPool_Background);
Job *job = OpenJob(F_Load, AsyncPool());
F_Load_Sig *sig = PushStruct(job->arena, F_Load_Sig);
job->sig = sig;
sig->asset = asset;

View File

@ -111,7 +111,8 @@ void GPU_D12_Startup(void)
GPU_D12_InitRootsig();
/* Start queue sync job */
RunJob(GPU_D12_StartQueueSync, .pool = JobPool_Hyper, .flags = JobFlag_Dedicated);
JobPoolId sync_pool = InitJobPool(1, Lit("Dx12 queue sync"), JobPoolPriority_Critical);
RunJob(GPU_D12_StartQueueSync, .pool = sync_pool);
}
////////////////////////////////////////////////////////////

View File

@ -1142,5 +1142,5 @@ JobDef(Build, _, __)
void StartupLayers(void)
{
OS_Startup();
RunJob(Build, .pool = JobPool_Hyper);
RunJob(Build, .pool = HyperPool());
}

View File

@ -7,101 +7,16 @@ void P_Startup(void)
{
P_W32_SharedState *g = &P_W32_shared_state;
//- Initialize btn table
{
ZeroArray(g->vk_btn_table);
for (u32 i = 'A', j = P_Btn_A; i <= 'Z'; ++i, ++j)
{
g->vk_btn_table[i] = (P_Btn)j;
}
for (u32 i = '0', j = P_Btn_0; i <= '9'; ++i, ++j)
{
g->vk_btn_table[i] = (P_Btn)j;
}
for (u32 i = VK_F1, j = P_Btn_F1; i <= VK_F24; ++i, ++j)
{
g->vk_btn_table[i] = (P_Btn)j;
}
g->vk_btn_table[VK_ESCAPE] = P_Btn_ESC;
g->vk_btn_table[VK_OEM_3] = P_Btn_GraveAccent;
g->vk_btn_table[VK_OEM_MINUS] = P_Btn_Minus;
g->vk_btn_table[VK_OEM_PLUS] = P_Btn_Equal;
g->vk_btn_table[VK_BACK] = P_Btn_Backspace;
g->vk_btn_table[VK_TAB] = P_Btn_Tab;
g->vk_btn_table[VK_SPACE] = P_Btn_Space;
g->vk_btn_table[VK_RETURN] = P_Btn_Enter;
g->vk_btn_table[VK_CONTROL] = P_Btn_Ctrl;
g->vk_btn_table[VK_SHIFT] = P_Btn_Shift;
g->vk_btn_table[VK_MENU] = P_Btn_Alt;
g->vk_btn_table[VK_UP] = P_Btn_Up;
g->vk_btn_table[VK_LEFT] = P_Btn_Left;
g->vk_btn_table[VK_DOWN] = P_Btn_Down;
g->vk_btn_table[VK_RIGHT] = P_Btn_Right;
g->vk_btn_table[VK_DELETE] = P_Btn_Delete;
g->vk_btn_table[VK_PRIOR] = P_Btn_PageUp;
g->vk_btn_table[VK_NEXT] = P_Btn_PageDown;
g->vk_btn_table[VK_HOME] = P_Btn_Home;
g->vk_btn_table[VK_END] = P_Btn_End;
g->vk_btn_table[VK_OEM_2] = P_Btn_ForwardSlash;
g->vk_btn_table[VK_OEM_PERIOD] = P_Btn_Period;
g->vk_btn_table[VK_OEM_COMMA] = P_Btn_Comma;
g->vk_btn_table[VK_OEM_7] = P_Btn_Quote;
g->vk_btn_table[VK_OEM_4] = P_Btn_LeftBracket;
g->vk_btn_table[VK_OEM_6] = P_Btn_RightBracket;
g->vk_btn_table[VK_INSERT] = P_Btn_Insert;
g->vk_btn_table[VK_OEM_1] = P_Btn_Semicolon;
}
//- Create window class
{
HMODULE instance = GetModuleHandle(0);
/* Register the window class */
WNDCLASSEXW *wc = &g->window_class;
wc->cbSize = sizeof(WNDCLASSEX);
wc->lpszClassName = P_W32_WindowClassName;
wc->hCursor = LoadCursor(0, IDC_ARROW);
wc->style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
// wc->hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
wc->lpfnWndProc = P_W32_Win32WindowProc;
wc->hInstance = instance;
/* Use first icon resource as window icon (same as explorer) */
wchar_t path[4096] = ZI;
GetModuleFileNameW(instance, path, countof(path));
ExtractIconExW(path, 0, &wc->hIcon, &wc->hIconSm, 1);
if (!RegisterClassExW(wc))
{
Panic(Lit("Failed to register window class"));
}
}
//- Register raw input
{
RAWINPUTDEVICE rid = (RAWINPUTDEVICE) {
.usUsagePage = 0x01, /* HID_USAGE_PAGE_GENERIC */
.usUsage = 0x02, /* HID_USAGE_GENERIC_MOUSE */
//.dwFlags = RIDEV_NOLEGACY /* Adds mouse and also ignores legacy mouse messages */
};
b32 ok = RegisterRawInputDevices(&rid, 1, sizeof(rid));
Assert(ok);
LAX ok;
}
//- Init watches pool
g->watches_arena = AcquireArena(Gibi(64));
//- Init windows pool
g->windows_arena = AcquireArena(Gibi(64));
//- Init winsock
WSAStartup(MAKEWORD(2, 2), &g->wsa_data);
g->socks_arena = AcquireArena(Gibi(64));
//- Init timer
RunJob(P_W32_StartTimerSync, .pool = JobPool_Hyper, .flags = JobFlag_Dedicated);
JobPoolId timer_pool = InitJobPool(1, Lit("Timer sync"), JobPoolPriority_Critical);
RunJob(P_W32_StartTimerSync, .pool = timer_pool);
}
////////////////////////////////////////////////////////////
@ -154,627 +69,6 @@ String P_W32_StringFromWin32Path(Arena *arena, wchar_t *src)
return result;
}
////////////////////////////////////////////////////////////
//~ Win32 window
P_W32_Window *P_W32_AcquireWindow(void)
{
P_W32_SharedState *g = &P_W32_shared_state;
P_W32_Window *window = 0;
{
Lock lock = LockE(&g->windows_mutex);
if (g->first_free_window)
{
window = g->first_free_window;
g->first_free_window = window->next_free;
}
else
{
window = PushStructNoZero(g->windows_arena, P_W32_Window);
}
Unlock(&lock);
}
ZeroStruct(window);
window->event_arenas[0] = AcquireArena(Gibi(64));
window->event_arenas[1] = AcquireArena(Gibi(64));
/* Start window event job */
/* NOTE: This job must finish starting for the window to actually be
* created and receive a HWND, because on Windows a the event proc must run on
* the same thread that created the window. */
RunJob(P_W32_StartWindowMsgProcessing, .pool = JobPool_Hyper, .flags = JobFlag_Dedicated, .sig.window = window);
YieldOnFence(&window->ready_fence, 1);
return window;
}
void P_W32_ReleaseWindow(P_W32_Window *window)
{
/* Stop window threads */
Atomic32Set(&window->shutdown, 1);
P_W32_SharedState *g = &P_W32_shared_state;
P_W32_WakeWindow(window);
YieldOnFence(&window->finished_fence, 1);
Lock lock = LockE(&g->windows_mutex);
{
window->next_free = g->first_free_window;
g->first_free_window = window;
}
Unlock(&lock);
}
HWND P_W32_InitWindow(P_W32_Window *window)
{
struct P_W32_SharedState *g = &P_W32_shared_state;
/*
* From martins (https://gist.github.com/mmozeiko/5e727f845db182d468a34d524508ad5f#file-win32_d3d11-c-L66-L70):
* WS_EX_NOREDIRECTIONBITMAP flag here is needed to fix ugly bug with Windows 10
* when window is resized and DXGI swap chain uses FLIP presentation model
* DO NOT use it if you choose to use non-FLIP presentation model
* read about the bug here: https://stackoverflow.com/q/63096226 and here: https://stackoverflow.com/q/53000291
*/
DWORD exstyle = WS_EX_APPWINDOW | WS_EX_NOREDIRECTIONBITMAP;
/* TODO: Check for hwnd success */
HWND hwnd = CreateWindowExW(
exstyle,
g->window_class.lpszClassName,
L"",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
0,
0,
g->window_class.hInstance,
0
);
/* Dark mode */
BOOL dark_mode = 1;
DwmSetWindowAttribute(hwnd, DWMWA_USE_IMMERSIVE_DARK_MODE, (LPCVOID)&dark_mode, sizeof(dark_mode));
/* Set window as userdata */
SetWindowLongPtrW(hwnd, GWLP_USERDATA, (LONG_PTR)window);
return hwnd;
}
////////////////////////////////////////////////////////////
//~ Win32 window settings
void P_W32_UpdateWindowFromSystem(P_W32_Window *window)
{
HWND hwnd = window->hwnd;
RECT window_rect = ZI;
GetWindowRect(hwnd, &window_rect);
RECT client_rect = ZI;
GetClientRect(hwnd, (LPRECT)&client_rect);
ClientToScreen(hwnd, (LPPOINT)&client_rect.left);
ClientToScreen(hwnd, (LPPOINT)&client_rect.right);
/* TODO: Error if we can't get monitor info */
/* Screen dimensions */
MONITORINFO monitor_info = { .cbSize = sizeof(monitor_info) };
GetMonitorInfo(MonitorFromWindow(hwnd, MONITOR_DEFAULTTOPRIMARY), &monitor_info);
RECT monitor_rect = monitor_info.rcMonitor;
window->monitor_width = monitor_rect.right - monitor_rect.left;
window->monitor_height = monitor_rect.bottom - monitor_rect.top;
/* Minimized / maximized */
if (window->flags & P_WindowFlag_Showing)
{
WINDOWPLACEMENT placement = { .length = sizeof(placement) };
GetWindowPlacement(hwnd, &placement);
if (placement.showCmd == SW_SHOWMINIMIZED)
{
window->settings.flags |= P_WindowSettingsFlag_Minimized;
}
else
{
window->settings.flags &= ~P_WindowSettingsFlag_Minimized;
}
if (placement.showCmd == SW_SHOWMAXIMIZED
|| ((window->settings.flags & P_WindowSettingsFlag_Minimized) && ((placement.flags & WPF_RESTORETOMAXIMIZED) != 0)))
{
window->settings.flags |= P_WindowSettingsFlag_Maximized;
}
else
{
window->settings.flags &= ~P_WindowSettingsFlag_Maximized;
}
}
/* Window dimensions */
i32 x = client_rect.left;
i32 y = client_rect.top;
i32 width = client_rect.right - client_rect.left;
i32 height = client_rect.bottom - client_rect.top;
if (!(window->settings.flags & P_WindowSettingsFlag_Minimized))
{
window->x = x;
window->y = y;
window->width = width;
window->height = height;
if (!(window->settings.flags & (P_WindowSettingsFlag_Maximized | P_WindowSettingsFlag_Fullscreen)))
{
/* Treat a window resize in non maximized/fullscreen mode as a
* settings change.
*
* TODO: make sure we check for fullscreen here too if we ever
* allow it. */
window->settings.floating_x = x;
window->settings.floating_y = y;
window->settings.floating_width = width;
window->settings.floating_height = height;
}
}
}
void P_W32_UpdateWindowFromSettings(P_W32_Window *window, P_WindowSettings *settings)
{
HWND hwnd = window->hwnd;
P_WindowSettings old_settings = window->settings;
window->settings = *settings;
i32 show_cmd = SW_HIDE;
if (window->flags & P_WindowFlag_Showing)
{
show_cmd = SW_NORMAL;
if (settings->flags & P_WindowSettingsFlag_Maximized)
{
show_cmd = SW_SHOWMAXIMIZED;
}
else if (settings->flags & P_WindowSettingsFlag_Minimized)
{
show_cmd = SW_MINIMIZE;
}
}
RECT rect = ZI;
b32 old_fullscreen = old_settings.flags & P_WindowSettingsFlag_Fullscreen;
b32 fullscreen = settings->flags & P_WindowSettingsFlag_Fullscreen;
if (fullscreen)
{
if (!old_fullscreen)
{
/* Entering fullscreen */
SetWindowLongPtrW(hwnd, GWL_STYLE, WS_POPUP);
}
rect = (RECT) {
.left = 0,
.top = 0,
.right = window->monitor_width,
.bottom = window->monitor_height
};
}
else
{
if (old_fullscreen)
{
/* Leaving fullscreen */
SetWindowLongPtrW(hwnd, GWL_STYLE, WS_OVERLAPPEDWINDOW);
}
rect = (RECT) {
.left = settings->floating_x,
.top = settings->floating_y,
.right = settings->floating_x + settings->floating_width,
.bottom = settings->floating_y + settings->floating_height
};
AdjustWindowRect(&rect, WS_OVERLAPPEDWINDOW, 0);
}
WINDOWPLACEMENT wp = {
.length = sizeof(WINDOWPLACEMENT),
.showCmd = show_cmd,
.rcNormalPosition = rect
};
SetWindowPlacement(hwnd, &wp);
{
TempArena scratch = BeginScratchNoConflict();
wchar_t *title_wstr = WstrFromString(scratch.arena, StringFromCstrNoLimit(settings->title));
SetWindowTextW(hwnd, title_wstr);
EndScratch(scratch);
}
}
////////////////////////////////////////////////////////////
//~ Win32 window message processing
JobDef(P_W32_StartWindowMsgProcessing, sig, id)
{
P_W32_Window *window = sig->window;
/* Win32 limitation: Window must be initialized on same thread that processes events */
window->hwnd = P_W32_InitWindow(window);
P_W32_UpdateWindowFromSystem(window);
SetFence(&window->ready_fence, 1);
SetForegroundWindow(window->hwnd);
while (!Atomic32Fetch(&window->shutdown))
{
SetFocus(window->hwnd);
MSG msg = ZI;
{
GetMessageW(&msg, 0, 0, 0);
}
{
__profn("Process window message");
if (!Atomic32Fetch(&window->shutdown))
{
TranslateMessage(&msg);
DispatchMessageW(&msg);
}
}
}
/* Destroy window hwnd */
DestroyWindow(window->hwnd);
SetFence(&window->finished_fence, 1);
}
void P_W32_ProcessWindowEvent(P_W32_Window *window, P_WindowEvent event)
{
__prof;
Lock lock = LockE(&window->event_arena_swp_mutex);
{
*PushStruct(window->event_arenas[window->current_event_arena_index], P_WindowEvent) = event;
}
Unlock(&lock);
}
void P_W32_WakeWindow(P_W32_Window *window)
{
/* Post a blank message to the window's thread message queue to wake it. */
PostMessageW(window->hwnd, WM_NULL, 0, 0);
}
LRESULT CALLBACK P_W32_Win32WindowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
__prof;
P_W32_SharedState *g = &P_W32_shared_state;
P_W32_Window *window = (P_W32_Window *)GetWindowLongPtrW(hwnd, GWLP_USERDATA);
if (!window)
{
return DefWindowProcW(hwnd, msg, wparam, lparam);
}
/* Update cursor */
if (GetFocus() == window->hwnd)
{
u32 cursor_flags = window->cursor_set_flags;
/* Hide cursor */
if (cursor_flags & P_W32_CursorFlag_Hide)
{
while (ShowCursor(0) >= 0);
}
/* Show cursor */
if (cursor_flags & P_W32_CursorFlag_Show)
{
while (ShowCursor(1) < 0);
}
/* Update position */
if (cursor_flags & P_W32_CursorFlag_Position)
{
Vec2 window_space_pos = window->cursor_set_position;
POINT p = { window_space_pos.x, window_space_pos.y };
ClientToScreen(window->hwnd, &p);
SetCursorPos(p.x, p.y);
}
/* Stop clipping cursor */
if (cursor_flags & P_W32_CursorFlag_DisableClip)
{
ClipCursor(0);
}
/* Clip cursor in window window */
if (cursor_flags & P_W32_CursorFlag_EnableClip)
{
i32 left = window->x + RoundF32ToI32(window->cursor_clip_bounds.x);
i32 right = left + RoundF32ToI32(window->cursor_clip_bounds.width);
i32 top = window->y + RoundF32ToI32(window->cursor_clip_bounds.y);
i32 bottom = top + RoundF32ToI32(window->cursor_clip_bounds.height);
RECT clip = {
.left = ClampI32(left, window->x, window->x + window->width),
.right = ClampI32(right, window->x, window->x + window->width),
.top = ClampI32(top, window->y, window->y + window->height),
.bottom = ClampI32(bottom, window->y, window->y + window->height)
};
ClipCursor(&clip);
}
window->cursor_set_flags = 0;
}
/* Update always on top */
{
u32 toggles = Atomic32FetchSet(&window->topmost_toggles, 0);
if (toggles % 2 != 0)
{
b32 new_topmost = !window->is_topmost;
if (new_topmost)
{
SetWindowText(hwnd, L"============================= TOP =============================");
}
else
{
SetWindowText(hwnd, L"");
}
SetWindowPos(hwnd, new_topmost ? HWND_TOPMOST : HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
window->is_topmost = new_topmost;
}
}
LRESULT result = 0;
b32 is_release = 0;
switch (msg)
{
default:
{
result = DefWindowProcW(hwnd, msg, wparam, lparam);
} break;
case WM_QUIT:
case WM_CLOSE:
case WM_DESTROY:
{
P_W32_ProcessWindowEvent(window, (P_WindowEvent) { .kind = P_WindowEventKind_Quit });
} break;
case WM_PAINT:
{
result = DefWindowProcW(hwnd, msg, wparam, lparam);
} break;
case WM_ENTERSIZEMOVE:
case WM_MOVE:
case WM_MOVING:
case WM_SIZE:
case WM_SIZING:
{
P_W32_UpdateWindowFromSystem(window);
result = DefWindowProcW(hwnd, msg, wparam, lparam);
} break;
/* Keyboard buttons */
case WM_SYSKEYUP:
case WM_SYSKEYDOWN:
{
if (LOWORD(wparam) != VK_MENU)
{
result = DefWindowProcW(hwnd, msg, wparam, lparam);
}
} FALLTHROUGH;
case WM_KEYUP:
case WM_KEYDOWN:
{
WORD vk_code = LOWORD(wparam);
b32 is_repeat = 0;
P_WindowEventKind event_kind = P_WindowEventKind_None;
if (msg == WM_KEYDOWN || msg == WM_SYSKEYDOWN)
{
event_kind = P_WindowEventKind_ButtonDown;
is_repeat = (lparam & 0x40000000) != 0;
}
else if (msg == WM_KEYUP || msg == WM_SYSKEYUP)
{
event_kind = P_WindowEventKind_ButtonUp;
}
P_Btn button = P_Btn_None;
if (vk_code < countof(g->vk_btn_table))
{
button = g->vk_btn_table[vk_code];
}
P_W32_ProcessWindowEvent(
window,
(P_WindowEvent)
{
.kind = event_kind,
.button = button,
.is_repeat = is_repeat
});
} break;
/* Text */
case WM_SYSCHAR:
case WM_CHAR:
{
u16 utf16_char = (u32)wparam;
/* Decode */
u32 codepoint = 0;
if (IsUtf16HighSurrogate(utf16_char))
{
window->utf16_high_surrogate_last_input = utf16_char;
}
else if (IsUtf16LowSurrogate(utf16_char))
{
u16 high = window->utf16_high_surrogate_last_input;
u16 low = utf16_char;
if (high)
{
u16 utf16_pair_bytes[2] = { high, low };
Utf16DecodeResult decoded = DecodeUtf16((String16) { .len = countof(utf16_pair_bytes), .text = utf16_pair_bytes });
if (decoded.advance16 == 2 && decoded.codepoint < U32Max)
{
codepoint = decoded.codepoint;
}
}
window->utf16_high_surrogate_last_input = 0;
}
else
{
window->utf16_high_surrogate_last_input = 0;
codepoint = utf16_char;
}
if (codepoint)
{
if (codepoint == '\r')
{
codepoint = '\n'; /* Just treat all \r as newline */
}
if ((codepoint >= 32 && codepoint != 127) || codepoint == '\t' || codepoint == '\n')
{
P_W32_ProcessWindowEvent(
window,
(P_WindowEvent)
{
.kind = P_WindowEventKind_Text,
.text_codepoint = codepoint
});
}
}
} break;
/* Mouse buttons */
case WM_LBUTTONUP:
case WM_MBUTTONUP:
case WM_RBUTTONUP:
case WM_XBUTTONUP:
{
ReleaseCapture();
is_release = 1;
} FALLTHROUGH;
case WM_LBUTTONDOWN:
case WM_MBUTTONDOWN:
case WM_RBUTTONDOWN:
case WM_XBUTTONDOWN:
{
if (!is_release)
{
SetCapture(hwnd);
}
P_WindowEventKind event_kind = is_release ? P_WindowEventKind_ButtonUp : P_WindowEventKind_ButtonDown;
P_Btn button = 0;
switch (msg)
{
case WM_LBUTTONUP: case WM_LBUTTONDOWN: button = P_Btn_M1; break;
case WM_RBUTTONUP: case WM_RBUTTONDOWN: button = P_Btn_M2; break;
case WM_MBUTTONUP: case WM_MBUTTONDOWN: button = P_Btn_M3; break;
case WM_XBUTTONUP: case WM_XBUTTONDOWN:
{
u32 wparam_xbutton = GET_XBUTTON_WPARAM(wparam);
if (wparam_xbutton == XBUTTON1)
{
button = P_Btn_M4;
}
else if (wparam_xbutton == XBUTTON2)
{
button = P_Btn_M5;
}
} break;
}
if (button)
{
P_W32_ProcessWindowEvent(
window,
(P_WindowEvent)
{
.kind = event_kind,
.button = button
});
}
} break;
/* Mouse wheel */
case WM_MOUSEWHEEL:
{
int delta = GET_WHEEL_DELTA_WPARAM(wparam);
i32 dir = delta >= 0 ? 1 : -1;
P_Btn button = dir >= 0 ? P_Btn_MWheelUp : P_Btn_MWheelDown;
for (i32 i = 0; i < (dir * delta); i += WHEEL_DELTA)
{
/* Send a button down & button up event simultaneously */
P_W32_ProcessWindowEvent(window, (P_WindowEvent) { .kind = P_WindowEventKind_ButtonDown, .button = button });
P_W32_ProcessWindowEvent(window, (P_WindowEvent) { .kind = P_WindowEventKind_ButtonUp, .button = button });
}
} break;
/* Mouse move */
case WM_MOUSEMOVE:
{
i32 x = GET_X_LPARAM(lparam);
i32 y = GET_Y_LPARAM(lparam);
P_W32_ProcessWindowEvent(
window,
(P_WindowEvent)
{
.kind = P_WindowEventKind_CursorMove,
.cursor_position = VEC2(x, y)
});
} break;
/* Raw mouse move */
case WM_INPUT:
{
TempArena scratch = BeginScratchNoConflict();
/* Read raw input buffer */
UINT buff_size;
GetRawInputData((HRAWINPUT)lparam, RID_INPUT, 0, &buff_size, sizeof(RAWINPUTHEADER));
u8 *buff = PushStructs(scratch.arena, u8, buff_size);
if (GetRawInputData((HRAWINPUT)lparam, RID_INPUT, buff, &buff_size, sizeof(RAWINPUTHEADER)) != buff_size)
{
P_LogErrorF("GetRawInputData did not return correct size");
break;
}
RAWINPUT raw = ZI;
CopyBytes(&raw, buff, sizeof(RAWINPUT));
if (raw.header.dwType == RIM_TYPEMOUSE)
{
i32 x = raw.data.mouse.lLastX;
i32 y = raw.data.mouse.lLastY;
Vec2 delta = VEC2(x, y);
P_W32_ProcessWindowEvent(
window,
(P_WindowEvent)
{
.kind = P_WindowEventKind_MouseMove,
.mouse_delta = delta
});
}
EndScratch(scratch);
} break;
/* Minmax info */
case WM_GETMINMAXINFO:
{
/* Set minimum window size */
LPMINMAXINFO mmi = (LPMINMAXINFO)lparam;
mmi->ptMinTrackSize.x = 100;
mmi->ptMinTrackSize.y = 100;
} break;
}
return result;
}
////////////////////////////////////////////////////////////
//~ Win32 Address
@ -889,7 +183,7 @@ JobDef(P_W32_StartTimerSync, _, __)
{
__profn("Job scheduler cycle");
{
/* TODO: Minimum timer frequency in case windows timers ever become ultra precise in the future */
/* TODO: Minimum timer frequency in case timers ever become ultra precise in the future */
__profn("Job scheduler wait");
LARGE_INTEGER due = ZI;
due.QuadPart = -1;
@ -1297,150 +591,6 @@ String P_GetFileMapData(P_FileMap map)
return map.mapped_memory;
}
////////////////////////////////////////////////////////////
//~ @hookdef Window hooks
P_Window *P_AcquireWindow(void)
{
__prof;
return (P_Window *)P_W32_AcquireWindow();
}
void P_ReleaseWindow(P_Window *p_window)
{
__prof;
P_W32_Window *window = (P_W32_Window *)p_window;
P_W32_ReleaseWindow(window);
}
//- Window events
P_WindowEventArray P_PopWindowEvents(Arena *arena, P_Window *p_window)
{
__prof;
P_W32_Window *window = (P_W32_Window *)p_window;
i32 event_arena_index = 0;
{
/* Swap event buffers */
Lock lock = LockE(&window->event_arena_swp_mutex);
event_arena_index = window->current_event_arena_index;
window->current_event_arena_index = 1 - window->current_event_arena_index;
Unlock(&lock);
}
Arena *events_arena = window->event_arenas[event_arena_index];
P_WindowEventArray events = ZI;
events.count = ArenaCount(events_arena, P_WindowEvent);
events.events = PushStructsNoZero(arena, P_WindowEvent, events.count);
CopyBytes(events.events, ArenaBase(events_arena), events_arena->pos);
ResetArena(events_arena);
return events;
}
//- Window settings
void P_UpdateWindowSettings(P_Window *p_window, P_WindowSettings *settings)
{
__prof;
P_W32_Window *window = (P_W32_Window *)p_window;
Lock lock = LockE(&window->settings_mutex);
{
P_W32_UpdateWindowFromSettings(window, settings);
}
Unlock(&lock);
}
/* FIXME: Lock settings mutex for these functions */
P_WindowSettings P_GetWindowSettings(P_Window *p_window)
{
P_W32_Window *window = (P_W32_Window *)p_window;
return window->settings;
}
void P_ShowWindow(P_Window *p_window)
{
P_W32_Window *window = (P_W32_Window *)p_window;
HWND hwnd = window->hwnd;
Lock lock = LockE(&window->settings_mutex);
{
i32 show_cmd = SW_NORMAL;
P_WindowSettings *settings = &window->settings;
if (settings->flags & P_WindowSettingsFlag_Maximized)
{
show_cmd = SW_SHOWMAXIMIZED;
}
else if (settings->flags & P_WindowSettingsFlag_Minimized)
{
show_cmd = SW_MINIMIZE;
}
window->flags |= P_WindowFlag_Showing;
ShowWindow(hwnd, show_cmd);
BringWindowToTop(hwnd);
}
Unlock(&lock);
}
void P_SetWindowCursorPos(P_Window *p_window, Vec2 pos)
{
P_W32_Window *window = (P_W32_Window *)p_window;
window->cursor_set_position = pos;
window->cursor_set_flags |= P_W32_CursorFlag_Position;
P_W32_WakeWindow(window);
}
void P_ShowWindowCursor(P_Window *p_window)
{
P_W32_Window *window = (P_W32_Window *)p_window;
window->cursor_set_flags |= P_W32_CursorFlag_Show;
P_W32_WakeWindow(window);
}
void P_HideWindowCursor(P_Window *p_window)
{
P_W32_Window *window = (P_W32_Window *)p_window;
window->cursor_set_flags |= P_W32_CursorFlag_Hide;
P_W32_WakeWindow(window);
}
void P_EnableWindoweCursorClip(P_Window *p_window, Rect bounds)
{
P_W32_Window *window = (P_W32_Window *)p_window;
window->cursor_clip_bounds = bounds;
window->cursor_set_flags |= P_W32_CursorFlag_EnableClip;
P_W32_WakeWindow(window);
}
void P_DisableWindoweCursorClip(P_Window *p_window)
{
P_W32_Window *window = (P_W32_Window *)p_window;
window->cursor_set_flags |= P_W32_CursorFlag_DisableClip;
P_W32_WakeWindow(window);
}
void P_ToggleWindowTopmost(P_Window *p_window)
{
P_W32_Window *window = (P_W32_Window *)p_window;
Atomic32FetchAdd(&window->topmost_toggles, 1);
P_W32_WakeWindow(window);
}
//- Window info
Vec2I32 P_GetWindowSize(P_Window *p_window)
{
P_W32_Window *window = (P_W32_Window *)p_window;
return VEC2I32(window->width, window->height);
}
Vec2I32 P_GetWindowMonitorSize(P_Window *p_window)
{
P_W32_Window *window = (P_W32_Window *)p_window;
return VEC2I32(window->monitor_width, window->monitor_height);
}
u64 P_GetInternalWindowHandle(P_Window *p_window)
{
P_W32_Window *window = (P_W32_Window *)p_window;
return (u64)window->hwnd;
}
////////////////////////////////////////////////////////////
//~ @hookdef Address helper hooks

View File

@ -23,55 +23,6 @@
#pragma comment(lib, "avrt")
#pragma comment(lib, "ws2_32.lib")
////////////////////////////////////////////////////////////
//~ Window types
Enum(P_W32_CursorFlag)
{
P_W32_CursorFlag_None = (0 << 0),
P_W32_CursorFlag_Position = (1 << 0),
P_W32_CursorFlag_Hide = (1 << 1),
P_W32_CursorFlag_Show = (1 << 2),
P_W32_CursorFlag_EnableClip = (1 << 3),
P_W32_CursorFlag_DisableClip = (1 << 4)
};
Struct(P_W32_Window)
{
u32 flags;
HWND hwnd;
Fence ready_fence;
Fence finished_fence;
u16 utf16_high_surrogate_last_input;
Mutex settings_mutex;
P_WindowSettings settings;
i32 monitor_width;
i32 monitor_height;
/* NOTE: width & height are unaffected by window minimization (they retain
* their pre-minimized values) */
i32 x, y, width, height;
/* FIXME: Use a cmd buffer for updating cursor (and maybe also settings).
* Current setup means cursor_set calls can be applied out of order */
u32 cursor_set_flags;
Vec2 cursor_set_position;
Rect cursor_clip_bounds;
Atomic32 topmost_toggles;
b32 is_topmost;
Mutex event_arena_swp_mutex;
i32 current_event_arena_index;
Arena *event_arenas[2];
Atomic32 shutdown;
P_W32_Window *next_free;
};
////////////////////////////////////////////////////////////
//~ Watch types
@ -111,26 +62,16 @@ Struct(P_W32_Sock)
////////////////////////////////////////////////////////////
//~ State types
#define P_W32_WindowClassName L"power_play_window_class"
#define P_W32_NumRollingTimerPeriods 500
#define P_W32_DefaultTimerPeriodNs 50000000
Struct(P_W32_SharedState)
{
//- Key lookup table
P_Btn vk_btn_table[256];
//- Watches pool
Mutex watches_mutex;
Arena *watches_arena;
P_W32_Watch *watches_first_free;
//- Windows pool
WNDCLASSEXW window_class;
Mutex windows_mutex;
Arena *windows_arena;
P_W32_Window *first_free_window;
//- Socket pool
WSADATA wsa_data;
Arena *socks_arena;
@ -152,27 +93,6 @@ P_DateTime P_W32_DateTimeFromWin32SystemTime(SYSTEMTIME st);
String P_W32_StringFromWin32Path(Arena *arena, wchar_t *src);
////////////////////////////////////////////////////////////
//~ Window operations
P_W32_Window *P_W32_AcquireWindow(void);
void P_W32_ReleaseWindow(P_W32_Window *window);
HWND P_W32_InitWindow(P_W32_Window *window);
////////////////////////////////////////////////////////////
//~ Window settings
void P_W32_UpdateWindowFromSystem(P_W32_Window *window);
void P_W32_UpdateWindowFromSettings(P_W32_Window *window, P_WindowSettings *settings);
////////////////////////////////////////////////////////////
//~ Window message processing
JobDecl(P_W32_StartWindowMsgProcessing, { P_W32_Window *window; });
void P_W32_ProcessWindowEvent(P_W32_Window *window, P_WindowEvent event);
void P_W32_WakeWindow(P_W32_Window *window);
LRESULT CALLBACK P_W32_Win32WindowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam);
////////////////////////////////////////////////////////////
//~ Address operations

View File

@ -15,7 +15,8 @@ void PB_Startup(void)
PB_WSP_SharedState *g = &PB_WSP_shared_state;
PB_WSP_InitializeWasapi();
/* Start playback job */
g->shutdown_jobs_count += RunJob(PB_WSP_Playback, .pool = JobPool_Audio, .fence = &g->shutdown_jobs_fence);
JobPoolId playback_pool = InitJobPool(1, Lit("Playback"), JobPoolPriority_Audio);
g->shutdown_jobs_count += RunJob(PB_WSP_Playback, .pool = playback_pool, .fence = &g->shutdown_jobs_fence);
OnExit(&PB_WSP_Shutdown);
}

View File

@ -51,9 +51,13 @@ void StartupUser(void)
//P_RegisterLogCallback(ConsoleLogCallback, P_LogLevel_Success);
P_RegisterLogCallback(ConsoleLogCallback, P_LogLevel_Debug);
/* Create job pools */
JobPoolId user_pool = InitJobPool(1, Lit("User"), JobPoolPriority_Graphics);
JobPoolId sim_pool = InitJobPool(4, Lit("Simulation"), JobPoolPriority_Simulation);
/* Start jobs */
g->shutdown_jobs_count += RunJob(UpdateUserOrSleep, .pool = JobPool_User, .fence = &g->shutdown_jobs_fence);
g->shutdown_jobs_count += RunJob(UpdateSim, .pool = JobPool_Sim, .fence = &g->shutdown_jobs_fence);
g->shutdown_jobs_count += RunJob(UpdateUserOrSleep, .pool = user_pool, .fence = &g->shutdown_jobs_fence);
g->shutdown_jobs_count += RunJob(UpdateSim, .pool = sim_pool, .fence = &g->shutdown_jobs_fence);
OnExit(&ShutdownUser);
}

View File

@ -99,7 +99,7 @@ AC_Asset *SND_LoadAsset(ResourceKey resource, SND_SoundFlag flags, b32 wait)
{
AC_MarkLoading(asset);
{
Job *job = OpenJob(SND_Load, JobPool_Background);
Job *job = OpenJob(SND_Load, AsyncPool());
SND_Load_Sig *sig = PushStruct(job->arena, SND_Load_Sig);
job->sig = sig;
sig->resource = resource;

View File

@ -283,7 +283,7 @@ JobDef(S_LoadSheet, sig, _)
//~ Cache
/* TODO: Per-fiber L1 cache */
S_Entry *S_FetchEntry(ResourceKey resource, JobPool pool, S_FetchFlag flags)
S_Entry *S_FetchEntry(ResourceKey resource, JobPoolId pool, S_FetchFlag flags)
{
S_SharedState *g = &S_shared_state;
S_Entry *entry = 0;
@ -351,7 +351,7 @@ S_Entry *S_FetchEntry(ResourceKey resource, JobPool pool, S_FetchFlag flags)
S_Texture *S_TextureFromResource(ResourceKey resource)
{
S_Entry *entry = S_FetchEntry(resource, JobPool_Inherit, S_FetchFlag_Texture);
S_Entry *entry = S_FetchEntry(resource, CurrentPool(), S_FetchFlag_Texture);
YieldOnFence(&entry->texture_ready_fence, 1);
return &entry->texture;
}
@ -359,7 +359,7 @@ S_Texture *S_TextureFromResource(ResourceKey resource)
S_Texture *S_TextureFromResourceAsync(ResourceKey resource)
{
S_Texture *result = &S_NilTexture;
S_Entry *entry = S_FetchEntry(resource, JobPool_Inherit, S_FetchFlag_Texture);
S_Entry *entry = S_FetchEntry(resource, AsyncPool(), S_FetchFlag_Texture);
if (FetchFence(&entry->texture_ready_fence) >= 1)
{
result = &entry->texture;
@ -369,7 +369,7 @@ S_Texture *S_TextureFromResourceAsync(ResourceKey resource)
S_Sheet *S_SheetFromResource(ResourceKey resource)
{
S_Entry *entry = S_FetchEntry(resource, JobPool_Inherit, S_FetchFlag_Sheet);
S_Entry *entry = S_FetchEntry(resource, CurrentPool(), S_FetchFlag_Sheet);
YieldOnFence(&entry->sheet_ready_fence, 1);
return &entry->sheet;
}
@ -377,7 +377,7 @@ S_Sheet *S_SheetFromResource(ResourceKey resource)
S_Sheet *S_SheetFromResourceAsync(ResourceKey resource)
{
S_Sheet *result = &S_NilSheet;
S_Entry *entry = S_FetchEntry(resource, JobPool_Inherit, S_FetchFlag_Sheet);
S_Entry *entry = S_FetchEntry(resource, AsyncPool(), S_FetchFlag_Sheet);
if (FetchFence(&entry->sheet_ready_fence) >= 1)
{
result = &entry->sheet;

View File

@ -146,7 +146,7 @@ Enum(S_FetchFlag)
S_FetchFlag_Sheet = (1 << 1),
};
S_Entry *S_FetchEntry(ResourceKey resource, JobPool pool, S_FetchFlag flags);
S_Entry *S_FetchEntry(ResourceKey resource, JobPoolId pool, S_FetchFlag flags);
////////////////////////////////////////////////////////////
//~ Sprite data retrieval operations

View File

@ -86,7 +86,8 @@ void WND_Startup(void)
}
//- Start message processing job
RunJob(WND_W32_ProcessMessagesForever, .pool = JobPool_Hyper, .flags = JobFlag_Dedicated);
JobPoolId message_job_pool = InitJobPool(1, Lit("Win32 message loop"), JobPoolPriority_Background);
RunJob(WND_W32_ProcessMessagesForever, .pool = message_job_pool);
}
////////////////////////////////////////////////////////////