fibers wip

This commit is contained in:
jacob 2025-07-03 19:53:59 -05:00
parent ca94dbec3e
commit 514c2a6496
16 changed files with 521 additions and 3076 deletions

View File

@ -843,7 +843,6 @@ void OnBuild(StringList cli_args)
StringBeginsWith(name, Lit("ttf_"))) {
if (PlatformWindows) {
ignore = !(StringEqual(name, Lit("sys_win32.c")) ||
StringEqual(name, Lit("sys_win32-old.c")) ||
StringEqual(name, Lit("sock_win32.c")) ||
StringEqual(name, Lit("gp_dx12.c")) ||
StringEqual(name, Lit("playback_wasapi.c")) ||

View File

@ -1,63 +0,0 @@
@echo off
setlocal enabledelayedexpansion
:: Unpack arguments
for %%a in (%*) do set "%%a=1"
set app_path=%1
if NOT "%escalate%" == "1" goto skipEscalate
:: This enables tracy sampling by running the executable in administrator mode.
:: BatchGotAdmin
:: https://stackoverflow.com/a/10052222
:-------------------------------------
:: --> Check for permissions
if "%PROCESSOR_ARCHITECTURE%" EQU "amd64" (
>nul 2>&1 "%SYSTEMROOT%\SysWOW64\cacls.exe" "%SYSTEMROOT%\SysWOW64\config\system"
) else (
>nul 2>&1 "%SYSTEMROOT%\system32\cacls.exe" "%SYSTEMROOT%\system32\config\system"
)
:: --> If error flag set, we do not have admin.
if '%errorlevel%' NEQ '0' (
echo Requesting administrative privileges...
goto UACPrompt
) else ( goto gotAdmin )
:UACPrompt
echo Set UAC = CreateObject^("Shell.Application"^) > "%temp%\getadmin.vbs"
set params= %*
echo UAC.ShellExecute "cmd.exe", "/c ""%~s0"" %params:"=""%", "", "runas", 1 >> "%temp%\getadmin.vbs"
"%temp%\getadmin.vbs"
del "%temp%\getadmin.vbs"
exit /B
:gotAdmin
pushd "%CD%"
CD /D "%~dp0"
:--------------------------------------
:skipEscalate
:: `ping` is being used in place of `TIMEOUT`
:: https://www.ibm.com/support/pages/timeout-command-run-batch-job-exits-immediately-and-returns-error-input-redirection-not-supported-exiting-process-immediately
taskkill /im tracy-profiler.exe /f 2> nul
start tracy-capture.exe -o .tracy -f
echo Launching app "%app_path%"...
%app_path%
if NOT %errorlevel% == 0 (
echo.
echo Program failed
pause
exit /b 1
)
:: Give time for trace file to finish before opening tracy
ping -n 2 127.0.0.1 >NUL
echo Launching tracy...
start "" "tracy-profiler.exe" ".tracy"

View File

@ -247,17 +247,16 @@ void sys_app_entry(struct string args_str)
struct string *worker_names = arena_push_array(scratch.arena, struct string, worker_count);
for (i32 i = 0; i < worker_count; ++i) {
struct string prefix = string_from_int(scratch.arena, worker_count - i, 10, 2); /* For profiler sorting order */
struct string id = string_from_int(scratch.arena, i, 10, 2);
struct string *name = &worker_names[i];
if (i == APP_DEDICATED_WORKER_ID_USER) {
*name = string_format(scratch.arena, LIT("[W%F] Worker #%F (User)"), FMT_STR(prefix), FMT_STR(id));
*name = string_format(scratch.arena, LIT("Worker #%F (User)"), FMT_STR(id));
} else if (i == APP_DEDICATED_WORKER_ID_SIM) {
*name = string_format(scratch.arena, LIT("[W%F] Worker #%F (Sim)"), FMT_STR(prefix), FMT_STR(id));
*name = string_format(scratch.arena, LIT("Worker #%F (Sim)"), FMT_STR(id));
} else if (i == APP_DEDICATED_WORKER_ID_AUDIO) {
*name = string_format(scratch.arena, LIT("[W%F] Worker #%F (Audio)"), FMT_STR(prefix), FMT_STR(id));
*name = string_format(scratch.arena, LIT("Worker #%F (Audio)"), FMT_STR(id));
} else {
*name = string_format(scratch.arena, LIT("[W%F] Worker #%F"), FMT_STR(prefix), FMT_STR(id));
*name = string_format(scratch.arena, LIT("Worker #%F"), FMT_STR(id));
}
}

View File

@ -656,6 +656,15 @@ INLINE f64 clamp_f64(f64 v, f64 min, f64 max) { return v < min ? min : v > max ?
#include "prof_tracy.h"
#define PROF_THREAD_GROUP_RUNNERS -8000
#define PROF_THREAD_GROUP_FIBERS -7000
#define PROF_THREAD_GROUP_WORKERS -6000
#define PROF_THREAD_GROUP_IO -5
#define PROF_THREAD_GROUP_WINDOW -4
#define PROF_THREAD_GROUP_EVICTORS -3
#define PROF_THREAD_GROUP_APP -2
#define PROF_THREAD_GROUP_MAIN -1
#ifdef __cplusplus
}
#endif

View File

@ -76,22 +76,6 @@
/* If enabled, things like network writes & memory allocations will be tracked in a global statistics struct */
#define GSTAT_ENABLED 1
#define FIBERS_TEST 1
/* ========================== *
* Settings
* ========================== */

View File

@ -390,7 +390,7 @@ struct gp_startup_receipt gp_startup(void)
/* Start evictor thread */
G.evictor_thread_wake_event = CreateEvent(NULL, false, false, NULL);
G.evictor_thread = sys_thread_alloc(evictor_thread_entry_point, NULL, LIT("[P2] GPU evictor thread"));
G.evictor_thread = sys_thread_alloc(evictor_thread_entry_point, NULL, LIT("GPU resource evictor thread"), PROF_THREAD_GROUP_EVICTORS);
struct gp_startup_receipt res = ZI;
return res;

View File

@ -199,7 +199,7 @@ struct host *host_alloc(u16 listen_port)
host->sock = sock_alloc(listen_port, MEGABYTE(2), MEGABYTE(2));
host->rcv_buffer_write_mutex = sys_mutex_alloc();
host->receiver_thread = sys_thread_alloc(&host_receiver_thread_entry_point, host, LIT("[P5] Host receiver"));
host->receiver_thread = sys_thread_alloc(&host_receiver_thread_entry_point, host, LIT("Host receiver"), PROF_THREAD_GROUP_IO);
return host;
}

View File

@ -95,7 +95,7 @@ void job_startup(i32 num_workers, struct string *worker_names)
G.num_worker_threads = num_workers;
for (i32 i = 0; i < G.num_worker_threads; ++i) {
struct string name = worker_names[i];
G.worker_threads[i] = sys_thread_alloc(worker_thread_entry_point, (void *)(i64)i, name);
G.worker_threads[i] = sys_thread_alloc(worker_thread_entry_point, (void *)(i64)i, name, PROF_THREAD_GROUP_WORKERS + i);
}
atomic_i32_fetch_set(&G.num_idle_worker_threads, num_workers);

View File

@ -39,7 +39,7 @@ INLINE void __prof_zone_cleanup_func(TracyCZoneCtx *ctx) { TracyCZoneEnd(*ctx) }
#define __proffree(ptr) TracyCFree((ptr))
#define __profmsg(txt, len, col) TracyCMessageC((txt), (len), BGR32(col))
#define __profframe(name) TracyCFrameMarkNamed((name))
#define __profthread(name) TracyCSetThreadName((name))
#define __profthread(name, group_hint) TracyCSetThreadNameWithHint((name), (group_hint))
enum __prof_plot_type {
__prof_plot_type_number = TracyPlotFormatNumber,
@ -64,7 +64,7 @@ enum __prof_plot_type {
#define __proffree(ptr)
#define __profmsg(txt, len, col)
#define __profframe(name)
#define __profthread(name)
#define __profthread(name, group_hint)
#define __prof_plot_init(name, type, step, fill, color)
#define __prof_plot(name, val)
#define __prof_plot_i(name, val)
@ -136,10 +136,10 @@ INLINE void __prof_dx12_zone_cleanup_func(TracyCD3D12ZoneCtx *ctx) { ___tracy_d3
#endif /* PROFILING_CAPTURE_FRAME_IMAGE */
#ifdef TRACY_FIBERS
# define __prof_fiber_enter(fiber_name) ___tracy_fiber_enter(fiber_name)
# define __prof_fiber_leave ___tracy_fiber_leave()
# define __prof_fiber_enter(fiber_name, profiler_group) TracyCFiberEnterWithHint(fiber_name, profiler_group)
# define __prof_fiber_leave TracyCFiberLeave
#else
# define __prof_fiber_enter(fiber_name)
# define __prof_fiber_enter(fiber_name, profiler_group)
# define __prof_fiber_leave
#endif

View File

@ -77,8 +77,8 @@ struct resource_startup_receipt resource_startup(void)
G.watch_dispatcher_cv = sys_condition_variable_alloc();
app_register_exit_callback(&resource_shutdown);
G.resource_watch_monitor_thread = sys_thread_alloc(resource_watch_monitor_thread_entry_point, NULL, LIT("[P2] Resource watch monitor"));
G.resource_watch_dispatch_thread = sys_thread_alloc(resource_watch_dispatcher_thread_entry_point, NULL, LIT("[P2] Resource watch dispatcher"));
G.resource_watch_monitor_thread = sys_thread_alloc(resource_watch_monitor_thread_entry_point, NULL, LIT("Resource watch monitor"), PROF_THREAD_GROUP_IO);
G.resource_watch_dispatch_thread = sys_thread_alloc(resource_watch_dispatcher_thread_entry_point, NULL, LIT("Resource watch dispatcher"), PROF_THREAD_GROUP_IO);
#endif

View File

@ -257,7 +257,7 @@ struct sprite_startup_receipt sprite_startup(struct gp_startup_receipt *gp_sr,
G.evictor_scheduler_mutex = sys_mutex_alloc();
G.evictor_scheduler_shutdown_cv = sys_condition_variable_alloc();
G.evictor_scheduler_thread = sys_thread_alloc(sprite_evictor_scheduler_thread_entry_point, NULL, LIT("[P2] Sprite evictor scheduler"));
G.evictor_scheduler_thread = sys_thread_alloc(sprite_evictor_scheduler_thread_entry_point, NULL, LIT("Sprite evictor scheduler"), PROF_THREAD_GROUP_EVICTORS);
app_register_exit_callback(&sprite_shutdown);
resource_register_watch_callback(&sprite_resource_watch_callback);

View File

@ -396,8 +396,6 @@ void sys_condition_variable_broadcast(struct sys_condition_variable *sys_cv);
* Threads
* ========================== */
#define SYS_THREAD_STACK_SIZE MEGABYTE(4)
#define SYS_THREAD_DEF(name, arg_name) void name(void *arg_name)
typedef SYS_THREAD_DEF(sys_thread_func, data);
@ -405,7 +403,8 @@ typedef SYS_THREAD_DEF(sys_thread_func, data);
struct sys_thread *sys_thread_alloc(
sys_thread_func *entry_point,
void *thread_data, /* Passed as arg to `entry_point` */
struct string thread_name
struct string thread_name,
i32 profiler_group
);
void sys_thread_wait_release(struct sys_thread *thread);
@ -489,6 +488,30 @@ b32 sys_run_command(struct string cmd);
i32 sys_current_fiber_id(void);
void sys_yield(void);
/* ========================== *
* Job
* ========================== */
enum sys_job_priority {
SYS_JOB_PRIORITY_HIGH = 0,
SYS_JOB_PRIORITY_NORMAL = 1,
SYS_JOB_PRIORITY_BACKGROUND = 2,
NUM_SYS_JOB_PRIORITIES
};
struct sys_job_data {
i32 id;
void *sig;
};
#define SYS_JOB_DEF(job_name, arg_name) void job_name(struct sys_job_data arg_name)
typedef SYS_JOB_DEF(sys_job_func, job_data);
void sys_run(i32 count, sys_job_func *func, enum sys_job_priority priority);
/* ========================== *
* Scratch context
* ========================== */
@ -501,19 +524,6 @@ struct sys_scratch_ctx {
struct sys_scratch_ctx *sys_scratch_ctx_from_fiber_id(i32 fiber_id);
/* ========================== *
* Job
* ========================== */
struct sys_job_data {
i32 id;
void *sig;
};
#define SYS_JOB_DEF(job_name, arg_name) void job_name(struct sys_job_data arg_name)
typedef SYS_JOB_DEF(sys_job_func, job_data);
/* ========================== *
* App entry point
* ========================== */

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,3 @@
#if FIBERS_TEST
#include "sys.h"
#include "memory.h"
#include "arena.h"
@ -32,6 +30,9 @@
#define SYS_WINDOW_EVENT_LISTENERS_MAX 512
#define WINDOW_CLASS_NAME L"power_play_window_class"
#define THREAD_STACK_SIZE MEGABYTE(4)
#define FIBER_STACK_SIZE MEGABYTE(4)
struct win32_mutex {
SRWLOCK srwlock;
struct win32_mutex *next_free;
@ -59,6 +60,7 @@ struct win32_thread {
void *thread_data;
char thread_name_cstr[256];
wchar_t thread_name_wstr[256];
i32 profiler_group;
struct win32_thread *next;
struct win32_thread *prev;
@ -112,18 +114,84 @@ struct win32_window {
#define FIBER_NAME_PREFIX_CSTR "[F] Fiber "
#define FIBER_NAME_MAX_SIZE 64
enum yield_kind {
YIELD_KIND_NONE,
YIELD_KIND_DONE,
YIELD_KIND_COOPERATIVE,
YIELD_KIND_SLEEP,
NUM_YIELD_KINDS
};
struct alignas(64) fiber {
char *name_cstr; /* 8 bytes */
/* ========================================================================= */
i32 id; /* 4 bytes */
i32 parent_id; /* 4 bytes */
/* ========================================================================= */
void *addr; /* 8 bytes */
/* ========================================================================= */
sys_job_func *job_func; /* 8 bytes */
/* ========================================================================= */
void *job_sig; /* 8 bytes */
/* ========================================================================= */
i32 job_id; /* 4 bytes */
u8 pad0[4]; /* 4 bytes (padding) */
/* ========================================================================= */
enum yield_kind yield_kind; /* 4 bytes */
u8 pad1[4]; /* 4 bytes (padding) */
/* ========================================================================= */
u8 pad_end[8]; /* 8 bytes (padding) */
};
STATIC_ASSERT(sizeof(struct fiber) == 64); /* Assume fiber fits in one cache line (increase if necessary) */
STATIC_ASSERT(alignof(struct fiber) == 64); /* Avoid false sharing */
#define FIBER_CTX_SLEEP_TIMER_INIT_MAGIC ((HANDLE)0x48e87857650169c8)
struct alignas(64) fiber_ctx {
HANDLE sleep_timer; /* 8 bytes */
/* ========================================================================= */
struct sys_scratch_ctx scratch_ctx; /* 16 bytes */
u8 pad[40]; /* 40 bytes */
/* ========================================================================= */
u8 pad[40]; /* 40 bytes (padding) */
};
STATIC_ASSERT(sizeof(struct fiber_ctx) == 64); /* Assume ctx fits in one cache line (increase if necessary) */
STATIC_ASSERT(alignof(struct fiber_ctx) == 64); /* Avoid false sharing */
struct alignas(64) runner_ctx {
i32 id;
HANDLE sleep_timer;
};
struct job_info {
i32 num_dispatched;
i32 count;
sys_job_func *func;
void *sig;
struct job_info *next;
};
struct alignas(64) job_queue {
struct atomic_i32 lock;
struct arena *arena;
struct job_info *first;
struct job_info *last;
struct job_info *first_free;
};
enum job_queue_kind {
JOB_QUEUE_KIND_HIGH_PRIORITY,
JOB_QUEUE_KIND_NORMAL_PRIORITY,
JOB_QUEUE_KIND_BACKGROUND,
NUM_JOB_QUEUE_KINDS
};
/* ========================== *
@ -180,30 +248,123 @@ GLOBAL struct {
/* Fibers */
struct atomic_i32 num_fibers;
i32 num_fibers;
i32 first_free_fiber_id;
struct arena *fiber_names_arena;
alignas(64) struct atomic_i32 fibers_lock;
struct fiber fibers[SYS_MAX_FIBERS];
struct fiber_ctx fiber_contexts[SYS_MAX_FIBERS];
/* Jobs */
struct job_queue job_queues[NUM_JOB_QUEUE_KINDS];
/* Runners */
struct atomic_i32 runners_shutdown;
i32 num_runner_threads;
struct arena *runner_threads_arena;
struct sys_thread **runner_threads;
struct runner_ctx *runner_contexts;
struct atomic_i32 runner_wake_gen;
} G = ZI, DEBUG_ALIAS(G, G_sys_win32);
/* ========================== *
* Fibers
* ========================== */
INTERNAL i32 fiber_ctx_init(void)
enum fiber_kind {
FIBER_KIND_CONVERTED_THREAD,
FIBER_KIND_JOB_RUNNER
};
INTERNAL void job_fiber_entry(void *id_ptr);
INTERNAL struct fiber *fiber_alloc(enum fiber_kind kind)
{
i32 id = atomic_i32_fetch_add(&G.num_fibers, 1);
if (id >= SYS_MAX_FIBERS) {
i32 fiber_id = 0;
struct fiber *fiber = NULL;
char *new_name_cstr = NULL;
{
while (atomic_i32_fetch_test_set(&G.fibers_lock, 0, 1) != 0) ix_pause();
{
fiber_id = G.first_free_fiber_id;
if (fiber_id && kind == FIBER_KIND_JOB_RUNNER) {
fiber = &G.fibers[fiber_id];
G.first_free_fiber_id = fiber->parent_id;
} else {
fiber_id = G.num_fibers++;
if (fiber_id >= SYS_MAX_FIBERS) {
sys_panic(LIT("Max fibers reached"));
}
struct fiber_ctx *ctx = &G.fiber_contexts[id];
{
ctx->sleep_timer = FIBER_CTX_SLEEP_TIMER_INIT_MAGIC;
fiber = &G.fibers[fiber_id];
new_name_cstr = arena_push_array(G.fiber_names_arena, char, FIBER_NAME_MAX_SIZE);
}
return id;
}
atomic_i32_fetch_set(&G.fibers_lock, 0);
}
if (new_name_cstr != NULL) {
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 a = id_chars[rev_start];
char b = id_chars[rev_end];
id_chars[rev_start] = b;
id_chars[rev_end] = a;
++rev_start;
--rev_end;
}
/* Concat fiber name */
i32 name_size = 1;
STATIC_ASSERT(sizeof(sizeof(FIBER_NAME_PREFIX_CSTR)) <= FIBER_NAME_MAX_SIZE);
MEMCPY(new_name_cstr, FIBER_NAME_PREFIX_CSTR, sizeof(FIBER_NAME_PREFIX_CSTR));
name_size += sizeof(FIBER_NAME_PREFIX_CSTR) - 2;
MEMCPY(new_name_cstr + name_size, id_chars, id_chars_len);
fiber->name_cstr = new_name_cstr;
/* Init win32 fiber */
if (kind == FIBER_KIND_JOB_RUNNER) {
fiber->addr = CreateFiber(FIBER_STACK_SIZE, job_fiber_entry, (void *)(i64)fiber_id);
} else {
fiber->addr = ConvertThreadToFiber((void *)(i64)fiber_id);
}
}
fiber->job_func = 0;
fiber->job_sig = 0;
fiber->job_id = 0;
fiber->yield_kind = 0;
fiber->parent_id = 0;
return fiber;
}
INTERNAL void fiber_release(struct fiber *fiber, i32 fiber_id)
{
while (atomic_i32_fetch_test_set(&G.fibers_lock, 0, 1) != 0) ix_pause();
{
fiber->parent_id = G.first_free_fiber_id;
G.first_free_fiber_id = fiber_id;
}
atomic_i32_fetch_set(&G.fibers_lock, 0);
}
INTERNAL struct fiber *fiber_from_id(i32 id)
{
ASSERT(id >= 0 && id < SYS_MAX_FIBERS);
return &G.fibers[id];
}
INTERNAL struct fiber_ctx *fiber_ctx_from_id(i32 id)
@ -212,11 +373,248 @@ INTERNAL struct fiber_ctx *fiber_ctx_from_id(i32 id)
return &G.fiber_contexts[id];
}
/* ========================== *
* Test job
* ========================== */
i32 sys_current_fiber_id(void)
{
return (i32)(i64)GetFiberData();
}
INTERNAL void yield(enum yield_kind kind)
{
struct fiber *fiber = fiber_from_id(sys_current_fiber_id());
i32 parent_fiber_id = fiber->parent_id;
if (parent_fiber_id <= 0) {
sys_panic(LIT("A top level fiber tried to yield"));
}
struct fiber *parent_fiber = fiber_from_id(parent_fiber_id);
{
__prof_fiber_leave;
fiber->yield_kind = kind;
SwitchToFiber(parent_fiber->addr);
__prof_fiber_enter(fiber->name_cstr, PROF_THREAD_GROUP_FIBERS + fiber->id);
}
}
void sys_yield(void)
{
yield(YIELD_KIND_COOPERATIVE);
}
void sys_run(i32 count, sys_job_func *func, enum sys_job_priority priority)
{
//priority = min_i32(priority, current_fiber_job_priority); /* Jobs can't create higher priority jobs */
if (count >= 1 && func && priority >= 0 && priority < NUM_SYS_JOB_PRIORITIES) {
STATIC_ASSERT((i32)NUM_SYS_JOB_PRIORITIES == (i32)NUM_JOB_QUEUE_KINDS); /* Enums must have 1:1 mapping */
enum job_queue_kind queue_kind = (enum job_queue_kind)priority;
struct job_queue *queue = &G.job_queues[queue_kind];
while (atomic_i32_fetch_test_set(&queue->lock, 0, 1) != 0) ix_pause();
{
struct job_info *info = NULL;
if (queue->first_free) {
info = queue->first_free;
queue->first_free = info->next;
} else {
info = arena_push(queue->arena, struct job_info);
}
info->count = count;
info->func = func;
if (queue->last) {
queue->last->next = info;
} else {
queue->first = info;
}
queue->last = info;
}
atomic_i32_fetch_set(&queue->lock, 0);
} else {
/* Invalid job parameters */
sys_panic(LIT("Invalid job parameters"));
}
}
struct bla_job_sig {
i32 _;
};
INTERNAL SYS_JOB_DEF(bla_job, job)
{
__prof;
(UNUSED)job;
Sleep(20);
{
__profscope(Do tha yield);
yield(YIELD_KIND_SLEEP);
}
Sleep(20);
}
/* ========================== *
* Job fiber func
* ========================== */
INTERNAL void job_fiber_entry(void *id_ptr)
{
i32 id = (i32)(i64)id_ptr;
struct fiber *fiber = fiber_from_id(id);
while (true) {
__prof_fiber_enter(fiber->name_cstr, PROF_THREAD_GROUP_FIBERS + fiber->id);
i32 parent_id = fiber->parent_id;
struct fiber *parent_fiber = fiber_from_id(parent_id);
void *parent_fiber_addr = parent_fiber->addr;
{
/* Run job */
fiber->yield_kind = YIELD_KIND_NONE;
struct sys_job_data data = ZI;
data.id = fiber->job_id;
data.sig = fiber->job_sig;
fiber->job_func(data);
fiber->yield_kind = YIELD_KIND_DONE;
}
__prof_fiber_leave;
SwitchToFiber(parent_fiber_addr);
}
}
/* ========================== *
* Test runners
* ========================== */
INTERNAL SYS_THREAD_DEF(runner_entry, runner_ctx_arg)
{
struct runner_ctx *ctx = runner_ctx_arg;
(UNUSED)ctx;
i32 runner_fiber_id = sys_current_fiber_id();
struct job_queue *queues[countof(G.job_queues)] = ZI;
for (u32 i = 0; i < countof(G.job_queues); ++i) {
queues[i] = &G.job_queues[i];
}
while (!atomic_i32_fetch(&G.runners_shutdown)) {
/* Pull job from queue */
i32 job_id = 0;
sys_job_func *job_func = 0;
void *job_sig = 0;
for (u32 queue_index = 0; queue_index < countof(queues) && !job_func; ++queue_index) {
struct job_queue *queue = queues[queue_index];
if (queue) {
while (atomic_i32_fetch_test_set(&queue->lock, 0, 1) != 0) ix_pause();
struct job_info *info = queue->first;
while (info && !job_func) {
struct job_info *next = info->next;
job_id = info->num_dispatched++;
if (job_id < info->count) {
/* Pick job */
job_func = info->func;
job_sig = info->sig;
if (job_id == (info->count - 1)) {
/* We're picking up the last dispatch, so dequeue the job */
if (!next) {
queue->last = NULL;
}
queue->first = next;
info->next = queue->first_free;
queue->first_free = info;
}
}
info = next;
}
atomic_i32_fetch_set(&queue->lock, 0);
}
}
/* Run job */
if (job_func) {
__profscope(Run job);
struct fiber *fiber = fiber_alloc(FIBER_KIND_JOB_RUNNER);
fiber->job_func = job_func;
fiber->job_sig = job_sig;
fiber->job_id = job_id;
fiber->parent_id = runner_fiber_id;
b32 done = false;
while (!done) {
SwitchToFiber(fiber->addr);
enum yield_kind yield_kind = fiber->yield_kind;
switch (yield_kind) {
default:
{
/* Invalid yield kind */
sys_panic(LIT("Fiber yielded with unknown yield kind"));
} break;
case YIELD_KIND_SLEEP:
{
__profscope(Job sleep);
Sleep(100);
} break;
case YIELD_KIND_DONE:
{
fiber_release(fiber, fiber->id);
done = true;
} break;
}
}
}
}
}
INTERNAL SYS_THREAD_DEF(test_entry, _)
{
struct arena_temp scratch = scratch_begin_no_conflict();
(UNUSED)_;
/* Init job queues */
for (u32 i = 0; i < countof(G.job_queues); ++i) {
struct job_queue *queue = &G.job_queues[i];
queue->arena = arena_alloc(GIGABYTE(64));
}
/* Start runners */
G.num_runner_threads = 6;
G.runner_threads_arena = arena_alloc(GIGABYTE(64));
G.runner_threads = arena_push_array(G.runner_threads_arena, struct sys_thread *, G.num_runner_threads);
G.runner_contexts = arena_push_array(G.runner_threads_arena, struct runner_ctx, G.num_runner_threads);
for (i32 i = 0; i < G.num_runner_threads; ++i) {
struct runner_ctx *ctx = &G.runner_contexts[i];
ctx->id = i;
ctx->sleep_timer = CreateWaitableTimerExW(NULL, NULL, CREATE_WAITABLE_TIMER_HIGH_RESOLUTION, TIMER_ALL_ACCESS);
struct string id_str = string_from_int(scratch.arena, i, 10, 2);
struct string name = string_format(scratch.arena, LIT("Runner %F"), FMT_STR(id_str));
G.runner_threads[i] = sys_thread_alloc(runner_entry, ctx, name, PROF_THREAD_GROUP_RUNNERS + i);
}
/* Push test job */
Sleep(300);
sys_run(2, bla_job, SYS_JOB_PRIORITY_NORMAL);
/* Wait on runners */
for (i32 i = 0; i < G.num_runner_threads; ++i) {
struct sys_thread *runner_thread = G.runner_threads[i];
sys_thread_wait_release(runner_thread);
}
scratch_end(scratch);
}
/* ========================== *
* Scratch context
* ========================== */
@ -249,6 +647,7 @@ struct sys_scratch_ctx *sys_scratch_ctx_from_fiber_id(i32 id)
/* ========================== *
* Events
* ========================== */
@ -1143,7 +1542,7 @@ INTERNAL struct win32_window *win32_window_alloc(void)
window->event_callbacks_mutex = sys_mutex_alloc();
/* Start window thread for processing events */
window->event_thread = sys_thread_alloc(&window_thread_entry_point, window, LIT("[P4] Window thread"));
window->event_thread = sys_thread_alloc(&window_thread_entry_point, window, LIT("Window thread"), PROF_THREAD_GROUP_WINDOW);
/* Wait for event thread to create actual window */
sync_flag_wait(&window->ready_sf);
@ -1983,10 +2382,9 @@ INTERNAL void win32_thread_release(struct win32_thread *t)
INTERNAL DWORD WINAPI win32_thread_proc(LPVOID vt)
{
struct win32_thread *t = (struct win32_thread *)vt;
__profthread(t->thread_name_cstr);
__profthread(t->thread_name_cstr, t->profiler_group);
i32 fiber_id = fiber_ctx_init();
ConvertThreadToFiber((void *)(i64)fiber_id);
fiber_alloc(FIBER_KIND_CONVERTED_THREAD);
/* Initialize COM */
CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
@ -2007,7 +2405,7 @@ INTERNAL DWORD WINAPI win32_thread_proc(LPVOID vt)
return 0;
}
struct sys_thread *sys_thread_alloc(sys_thread_func *entry_point, void *thread_data, struct string thread_name)
struct sys_thread *sys_thread_alloc(sys_thread_func *entry_point, void *thread_data, struct string thread_name, i32 profiler_group)
{
__prof;
struct arena_temp scratch = scratch_begin_no_conflict();
@ -2019,6 +2417,7 @@ struct sys_thread *sys_thread_alloc(sys_thread_func *entry_point, void *thread_d
struct win32_thread *t = win32_thread_alloc();
t->entry_point = entry_point;
t->thread_data = thread_data;
t->profiler_group = profiler_group;
/* Copy thread name to params */
{
@ -2035,7 +2434,7 @@ struct sys_thread *sys_thread_alloc(sys_thread_func *entry_point, void *thread_d
t->handle = CreateThread(
NULL,
SYS_THREAD_STACK_SIZE,
THREAD_STACK_SIZE,
win32_thread_proc,
t,
0,
@ -2345,13 +2744,9 @@ INTERNAL void win32_precise_sleep_legacy(f64 seconds)
void sys_sleep_precise(f64 seconds)
{
__prof;
struct fiber_ctx *ctx = fiber_ctx_from_id(sys_current_fiber_id());
/* FIXME: Enable this */
#if 0
HANDLE timer = ctx->sleep_timer;
if (timer == FIBER_CTX_SLEEP_TIMER_INIT_MAGIC) {
__profscope(Create high resolution timer);
timer = CreateWaitableTimerExW(NULL, NULL, CREATE_WAITABLE_TIMER_HIGH_RESOLUTION, TIMER_ALL_ACCESS);
ctx->sleep_timer = timer;
}
if (timer) {
/* Use newer sleeping method */
win32_precise_sleep_timer(timer, seconds);
@ -2360,6 +2755,10 @@ void sys_sleep_precise(f64 seconds)
* is not available due to older windows version */
win32_precise_sleep_legacy(seconds);
}
#else
(UNUSED)win32_precise_sleep_timer;
win32_precise_sleep_legacy(seconds);
#endif
}
void sys_sleep(f64 seconds)
@ -2409,7 +2808,6 @@ int CALLBACK wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev_instance,
(UNUSED)show_code;
#if PROFILING
/* Launch profiler */
{
__profscope(Launch profiler);
STARTUPINFO si = ZI;
@ -2420,14 +2818,19 @@ int CALLBACK wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev_instance,
DeleteFileW(PROFILING_FILE_WSTR);
b32 success = CreateProcessW(NULL, cmd, NULL, NULL, FALSE, DETACHED_PROCESS, NULL, NULL, &si, &pi);
if (!success) {
MessageBoxExW(NULL, L"Failed to launch capture using command '" PROFILING_CMD_WSTR L"'. Is the app in your path?", L"Error", MB_ICONSTOP | MB_SETFOREGROUND | MB_TOPMOST, 0);
MessageBoxExW(NULL, L"Failed to launch profiler using command '" PROFILING_CMD_WSTR L"'.", L"Error", MB_ICONSTOP | MB_SETFOREGROUND | MB_TOPMOST, 0);
}
}
#endif
__profthread("Main thread", PROF_THREAD_GROUP_MAIN);
/* Init fibers */
G.num_fibers = 1; /* Fiber at index 0 always nil */
G.fiber_names_arena = arena_alloc(GIGABYTE(64));
/* Convert main thread to fiber */
i32 fiber_id = fiber_ctx_init();
ConvertThreadToFiber((void *)(i64)fiber_id);
fiber_alloc(FIBER_KIND_CONVERTED_THREAD);
u64 cmdline_len = wstr_len(cmdline_wstr, countof(G.cmdline_args_wstr) - 1);
MEMCPY(G.cmdline_args_wstr, cmdline_wstr, cmdline_len * sizeof(*cmdline_wstr));
@ -2535,7 +2938,10 @@ int CALLBACK wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev_instance,
/* Call app thread and wait for return */
{
/* Start app thread */
struct sys_thread *app_thread = sys_thread_alloc(&win32_app_thread_entry_point, NULL, LIT("[P0] App thread"));
struct sys_thread *app_thread = sys_thread_alloc(&win32_app_thread_entry_point, NULL, LIT("App thread"), PROF_THREAD_GROUP_APP);
/* Start test thread */
struct sys_thread *test_thread = sys_thread_alloc(test_entry, NULL, LIT("Test thread"), PROF_THREAD_GROUP_APP);
/* Get app thread handle */
HANDLE app_thread_handle = 0;
@ -2546,6 +2952,7 @@ int CALLBACK wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev_instance,
}
sys_mutex_unlock(&lock);
/* Wait for either app thread exit or panic */
if (app_thread_handle) {
HANDLE wait_handles[] = {
@ -2559,6 +2966,10 @@ int CALLBACK wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev_instance,
ASSERT(res != WAIT_FAILED);
(UNUSED)res;
}
/* Shutdown test thread */
atomic_i32_fetch_set(&G.runners_shutdown, 1);
sys_thread_wait_release(test_thread);
}
/* Find any dangling threads that haven't exited gracefully by now */
@ -2629,6 +3040,3 @@ void __stdcall wWinMainCRTStartup(void)
#pragma clang diagnostic pop
#endif /* !CRTLIB */
#endif

View File

@ -204,8 +204,6 @@ struct ttf_decode_result ttf_decode(struct arena *arena, struct string encoded,
/* Compute glyph metrics */
DWRITE_GLYPH_METRICS glyph_metrics = ZI;
error = font_face->GetDesignGlyphMetrics(&i, 1, &glyph_metrics, false);
f32 off_x = (f32)bounding_box.left - raster_target_x;

View File

@ -1689,29 +1689,32 @@ INTERNAL void user_update(void)
if (!G.debug_camera) {
__profscope(Draw crosshair);
struct v2 crosshair_pos = G.user_cursor;
struct sprite_tag crosshair_tag = sprite_tag_from_path(LIT("sprite/crosshair.ase"));
struct sprite_texture *t = sprite_texture_from_tag_async(sprite_frame_scope, crosshair_tag);
struct sprite_tag crosshair = sprite_tag_from_path(LIT("sprite/crosshair.ase"));
struct sprite_texture *t = sprite_texture_from_tag_async(sprite_frame_scope, crosshair);
struct v2 size = V2(t->width, t->height);
struct xform xf = XFORM_TRS(.t = crosshair_pos, .s = size);
draw_texture(G.ui_gp_flow, DRAW_TEXTURE_PARAMS(.xf = xf, .sprite = crosshair_tag));
draw_texture(G.ui_gp_flow, DRAW_TEXTURE_PARAMS(.xf = xf, .sprite = crosshair));
}
/* FIXME: Enable this */
#if 0
{
__profscope(Update window cursor);
if (G.debug_camera) {
sys_window_cursor_disable_clip(G.window);
sys_window_cursor_show(G.window);
} else {
struct sprite_texture *t = sprite_texture_from_tag_async(sprite_frame_scope, sprite_tag_from_path(LIT("sprite/crosshair.ase")));
struct v2 size = V2(t->width, t->height);
struct rect cursor_clip = RECT_FROM_V2(G.user_screen_offset, G.user_size);
cursor_clip.pos = v2_add(cursor_clip.pos, v2_mul(size, 0.5f));
cursor_clip.pos = v2_add(cursor_clip.pos, V2(1, 1));
cursor_clip.size = v2_sub(cursor_clip.size, size);
sys_window_cursor_hide(G.window);
sys_window_cursor_enable_clip(G.window, cursor_clip);
#endif
} else {
__profscope(Update windows cursor);
#if 0
sys_window_cursor_disable_clip(G.window);
sys_window_cursor_show(G.window);
#endif
}
}
#endif
/* ========================== *
* Create user sim cmd