diff --git a/build.bat b/build.bat new file mode 100644 index 00000000..965b3c0c --- /dev/null +++ b/build.bat @@ -0,0 +1,57 @@ +@echo off +setlocal enabledelayedexpansion +cd /D "%~dp0" +if not exist build mkdir build + +:::::::::::::::::::::::::::::::: +::~ Unpack args + +for %%a in (%*) do set "%%a=1" + +if "%~1"=="" ( + echo [No arguments provided, assuming default `pp` build] + set meta=1 + set pp=1 +) + +if "%~1"=="release" if "%~2"=="" ( + echo [No arguments provided, assuming default 'pp' build] + set meta=1 + set pp=1 +) + +if not "%release%"=="1" set debug=1 + +if "%release%"=="1" echo [Release] +if "%debug%"=="1" echo [Debug] + +:::::::::::::::::::::::::::::::: +::~ Build meta program + +if "%meta%"=="1" ( + echo [Building meta] + + :: cl.exe /Fe"build/" /Fo"build/" /c src\\meta\\meta.c /link build/meta.obj + "cl" /Zi /DEBUG src/meta/meta.c /Fo:build/meta.obj /Fe:build/meta.exe /nologo + + if not "!errorlevel!" == "0" ( + echo . + echo Failed to build meta program + exit /b 1 + ) +) + +:::::::::::::::::::::::::::::::: +::~ Build power play + +if "%pp%"=="1" ( + echo [Building power play] + + "build/meta.exe" pp.lay + + if not "!errorlevel!" == "0" ( + echo. + echo Failed to run meta program + exit /b 1 + ) +) diff --git a/build.c b/build.c index 3ade0cc0..bb64caa2 100644 --- a/build.c +++ b/build.c @@ -1,1124 +1,2 @@ - -#include "buildit.h" -typedef struct StepList StepList; - //////////////////////////////// -//~ Globals - -Bool force_rebuild = 0; - -Arena perm = { 0 }; -D_Store store = { 0 }; -D_Hist hist = { 0 }; -StepList *sl = { 0 }; - -/* Args */ -String arg_outdir = Lit("build"); -Bool arg_msvc = 0; -Bool arg_rtc = 0; -Bool arg_asan = 0; -Bool arg_crtlib = 0; -Bool arg_debinfo = 0; -Bool arg_developer = 0; -Bool arg_profiling = 0; -Bool arg_unoptimized = 0; - -//////////////////////////////// -//~ Utilities - -void Error(String msg) -{ - SH_PrintF(Lit("ERROR: %F\n"), FmtStr(msg)); -} - -Bool IsDirty(D_Tag tag) -{ - Bool res = force_rebuild ? 1 : D_IsDirty(&store, &hist, tag); - if (!res) - { - if (tag.kind == D_TagKind_File || tag.kind == D_TagKind_Dir) - { - res = !D_Exists(tag); - } - } - - return res; -} - -SH_CommandResult RunCommand(Arena *arena, String command) -{ - SH_CommandResult res = SH_RunCommandCaptureOutput(arena, command, 1); - return res; -} - -void KillRunningProcesses(void) -{ - SH_RunCommand(Lit("taskkill /im PowerPlay.exe /f /fi \"STATUS eq RUNNING\""), 1); -} - -//////////////////////////////// -//~ Steplist operations - -typedef struct StepList StepList; - -typedef I32 StepStatus; enum -{ - StepStatus_None, - StepStatus_Success, - StepStatus_Failure, - StepStatus_Skipped -}; - -typedef struct Step Step; -struct Step -{ - String name; - void *arg; - - OS_Mutex res_mutex; - OS_ConditionVariable res_cv; - StepStatus res_status; - String res_output; - - Step *next; -}; - -typedef struct StepList StepList; -struct StepList -{ - OS_Mutex mutex; - Arena arena; - - T_Queue tq; - - Step *first; - Step *last; - I64 count; -}; - -void AddStep(String name, T_TaskFunc *func, void *arg) -{ - OS_Lock lock = OS_MutexLockE(&sl->mutex); - Step *s = ArenaPush(&sl->arena, Step); - s->res_mutex = OS_MutexAlloc(); - s->res_cv = OS_ConditionVariableAlloc(); - s->name = name; - s->arg = arg; - SllQueuePush(sl->first, sl->last, s); - ++sl->count; - T_AddTask(&sl->tq, func, s); - OS_MutexUnlock(&lock); -} - -void AddSyncPoint(void) -{ - OS_Lock lock = OS_MutexLockE(&sl->mutex); - T_AddSyncPoint(&sl->tq); - OS_MutexUnlock(&lock); -} - -void ExecuteSteps(void) -{ - T_Execute(&sl->tq); -} - -//////////////////////////////// -//~ Output cleaning operations - -/* TODO: Move to buildit */ -Size StringFind(String s, String pattern) -{ - Size pos = -1; - for (Size i = 0; i < s.len; ++i) - { - if ((s.len - i) >= pattern.len) - { - String cmp = { 0 }; - cmp.text = s.text + i; - cmp.len = pattern.len; - if (StringEqual(cmp, pattern)) - { - pos = i; - break; - } - } - else - { - break; - } - } - return pos; -} - -String CleanResultOutput(Arena *arena, String output) -{ - TempArena scratch = ScratchBegin(arena); - StringList res_lines = { 0 }; - - StringList patterns = { 0 }; - StringListAppend(scratch.arena, &patterns, Lit("\r\n")); - StringListAppend(scratch.arena, &patterns, Lit("\r\n")); - StringListAppend(scratch.arena, &patterns, Lit("\n")); - StringList lines = StringSplit(scratch.arena, output, patterns); - for (StringListNode *n = lines.first; n; n = n->next) - { - String line = n->string; - String line_cleaned = line; - - /* Ignore MSVC header include messages */ - if (StringContains(line, Lit("Note: including file"))) - { - line_cleaned.len = 0; - } - - /* Truncate note src locations. - * This is a hack to get around visual studio's "GoToNextLocation" - * picking up noise from clang & msvc notes since src location - * info is present, rather than going to the next error. */ - if (StringContains(line_cleaned, Lit(": note: "))) - { - String trunc = Lit("power_play\\src\\"); - Size pos = StringFind(line_cleaned, trunc); - if (pos < 0) - { - trunc = Lit("power_play\\src/"); - pos = StringFind(line_cleaned, trunc); - } - String line_trunced = line_cleaned; - if (pos >= 0) - { - line_trunced.text += pos + trunc.len; - line_trunced.len -= pos + trunc.len; - line_cleaned = StringCopy(scratch.arena, Lit("[note]: ")); - line_cleaned.len += StringCopy(scratch.arena, line_trunced).len; - } - } - - if (line_cleaned.len > 0) - { - StringListAppend(scratch.arena, &res_lines, line_cleaned); - } - } - - String res = StringFromStringList(arena, Lit("\n"), res_lines); - ScratchEnd(scratch); - - return res; -} - -//////////////////////////////// -//~ Build step operations - -typedef struct BuildStepSimpleCommandArg BuildStepSimpleCommandArg; -struct BuildStepSimpleCommandArg -{ - String cmd; - Atomic32 *skip_flag; - Atomic32 *failure_flag; - D_Tag delete_file_on_failure; - D_Tag dxc_depfile_dependent; - D_Tag dxc_depfile; -}; - -typedef struct BuildStepMsvcCompileCommandArg BuildStepMsvcCompileCommandArg; -struct BuildStepMsvcCompileCommandArg -{ - String cmd; - Atomic32 *skip_flag; - Atomic32 *failure_flag; - D_Tag delete_file_on_failure; - D_Tag depfile_dependent; - D_Tag output_depfile; - D_TagList depfile_force_includes; -}; - -void BuildStepSimpleCommand(void *arg_raw) -{ - TempArena scratch = ScratchBeginNoConflict(); - Step *s = arg_raw; - BuildStepSimpleCommandArg *arg = s->arg; - - Atomic32 *skip_flag = arg->skip_flag; - Atomic32 *failure_flag = arg->failure_flag; - if (!skip_flag || !Atomic32Fetch(skip_flag)) - { - SH_CommandResult result = RunCommand(scratch.arena, arg->cmd); - String result_output_cleaned = CleanResultOutput(scratch.arena, result.output); - { - OS_Lock res_lock = OS_MutexLockE(&s->res_mutex); - if (result_output_cleaned.len > 0) - { - OS_Lock sl_lock = OS_MutexLockE(&sl->mutex); - { - s->res_output.text = ArenaPushArrayNoZero(&sl->arena, Byte, result_output_cleaned.len); - } - OS_MutexUnlock(&sl_lock); - s->res_output.len = result_output_cleaned.len; - MemoryCopy(s->res_output.text, result_output_cleaned.text, result_output_cleaned.len); - } - s->res_status = result.error ? StepStatus_Failure : StepStatus_Success; - if (!D_IsNil(arg->dxc_depfile)) - { - String depfile_data = D_DepfileTextFromDxcOutput(scratch.arena, arg->dxc_depfile_dependent, result.output); - D_ClearWrite(arg->dxc_depfile, depfile_data); - } - if (result.error) - { - if (failure_flag) - { - Atomic32FetchSet(failure_flag, 1); - } - if (!D_IsNil(arg->delete_file_on_failure)) - { - D_Delete(arg->delete_file_on_failure); - } - } - OS_ConditionVariableBroadcast(&s->res_cv); - OS_MutexUnlock(&res_lock); - } - } - else - { - OS_Lock res_lock = OS_MutexLockE(&s->res_mutex); - s->res_status = StepStatus_Skipped; - if (failure_flag) - { - Atomic32FetchSet(failure_flag, 1); - } - OS_ConditionVariableBroadcast(&s->res_cv); - OS_MutexUnlock(&res_lock); - } - - - ScratchEnd(scratch); -} - -void BuildStepMsvcCompileCommand(void *arg_raw) -{ - TempArena scratch = ScratchBeginNoConflict(); - Step *s = arg_raw; - BuildStepMsvcCompileCommandArg *arg = s->arg; - - Atomic32 *skip_flag = arg->skip_flag; - Atomic32 *failure_flag = arg->failure_flag; - if (!skip_flag || !Atomic32Fetch(skip_flag)) - { - SH_CommandResult result = RunCommand(scratch.arena, arg->cmd); - if (!result.error && !D_IsNil(arg->depfile_dependent) && !D_IsNil(arg->output_depfile)) - { - String depfile_data = D_DepfileTextFromMsvcOutput(scratch.arena, arg->depfile_dependent, arg->depfile_force_includes, result.output); - D_ClearWrite(arg->output_depfile, depfile_data); - } - String result_output_cleaned = CleanResultOutput(scratch.arena, result.output); - { - OS_Lock res_lock = OS_MutexLockE(&s->res_mutex); - if (result_output_cleaned.len > 0) - { - OS_Lock sl_lock = OS_MutexLockE(&sl->mutex); - { - s->res_output.text = ArenaPushArrayNoZero(&sl->arena, Byte, result_output_cleaned.len); - } - OS_MutexUnlock(&sl_lock); - s->res_output.len = result_output_cleaned.len; - MemoryCopy(s->res_output.text, result_output_cleaned.text, result_output_cleaned.len); - } - s->res_status = result.error ? StepStatus_Failure : StepStatus_Success; - if (result.error) - { - if (failure_flag) - { - Atomic32FetchSet(failure_flag, 1); - } - if (!D_IsNil(arg->delete_file_on_failure)) - { - D_Delete(arg->delete_file_on_failure); - } - } - OS_ConditionVariableBroadcast(&s->res_cv); - OS_MutexUnlock(&res_lock); - } - } - else - { - OS_Lock res_lock = OS_MutexLockE(&s->res_mutex); - s->res_status = StepStatus_Skipped; - if (failure_flag) - { - Atomic32FetchSet(failure_flag, 1); - } - OS_ConditionVariableBroadcast(&s->res_cv); - OS_MutexUnlock(&res_lock); - } - - ScratchEnd(scratch); -} - -//////////////////////////////// -//~ Build - -void OnBuild(StringList cli_args) -{ - I64 worker_count = OS_GetProcessorCount(); - T_StartupWorkers(worker_count); - - perm = ArenaAlloc(Gigabyte(64)); - - store = D_StoreAlloc(); - - sl = ArenaPush(&perm, StepList); - sl->arena = ArenaAlloc(Gigabyte(64)); - sl->mutex = OS_MutexAlloc(); - sl->tq = T_QueueAlloc(); - - - //////////////////////////////// - //~ Read args - - String tracy_env_var_name = Lit("TRACY_SRC_PATH"); - String tracy_src_dir_path = OS_GetEnvVar(&perm, tracy_env_var_name); - String tracy_client_header_path = OS_GetAbsPath(&perm, StringF(&perm, Lit("%F%F"), FmtStr(tracy_src_dir_path), FmtStr(Lit("/public/tracy/TracyC.h")))); - String tracy_client_src_path = OS_GetAbsPath(&perm, StringF(&perm, Lit("%F%F"), FmtStr(tracy_src_dir_path), FmtStr(Lit("/public/TracyClient.cpp")))); - { - typedef I32 ArgState; enum - { - ArgState_None, - ArgState_OutputDir - }; - - ArgState arg_state = ArgState_None; - for (StringListNode *n = cli_args.first; n; n = n->next) - { - String arg = n->string; - if (n != cli_args.first) - { - switch (arg_state) - { - case ArgState_OutputDir: - { - arg_outdir = arg; - arg_state = ArgState_None; - } break; - - default: - { - if (StringEqual(arg, Lit("-O"))) arg_state = ArgState_OutputDir; - if (StringEqual(arg, Lit("-clang"))) arg_msvc = 0; - if (StringEqual(arg, Lit("-msvc"))) arg_msvc = 1; - if (StringEqual(arg, Lit("-rtc"))) arg_rtc = 1; - if (StringEqual(arg, Lit("-asan"))) arg_asan = 1; - if (StringEqual(arg, Lit("-crtlib"))) arg_crtlib = 1; - if (StringEqual(arg, Lit("-debinfo"))) arg_debinfo = 1; - if (StringEqual(arg, Lit("-developer"))) arg_developer = 1; - if (StringEqual(arg, Lit("-profiling"))) arg_profiling = 1; - if (StringEqual(arg, Lit("-unoptimized"))) arg_unoptimized = 1; - } break; - } - } - } - } - - String hist_path = OS_GetAbsPath(&perm, StringF(&perm, Lit("%F/.dephist"), FmtStr(arg_outdir))); - String build_hash_path = OS_GetAbsPath(&perm, StringF(&perm, Lit("%F/.pp_build_hash"), FmtStr(arg_outdir))); - String out_dep_dir_path = OS_GetAbsPath(&perm, StringF(&perm, Lit("%F/dep/"), FmtStr(arg_outdir))); - String out_obj_dir_path = OS_GetAbsPath(&perm, StringF(&perm, Lit("%F/obj/"), FmtStr(arg_outdir))); - String out_dxc_dir_path = OS_GetAbsPath(&perm, StringF(&perm, Lit("%F/dxc/"), FmtStr(arg_outdir))); - String out_inc_dir_path = OS_GetAbsPath(&perm, StringF(&perm, Lit("%F/inc/"), FmtStr(arg_outdir))); - String out_bin_dir_path = OS_GetAbsPath(&perm, StringF(&perm, Lit("%F/bin/"), FmtStr(arg_outdir))); - - if (!OS_DirExists(out_obj_dir_path)) - { - OS_CreateDirAtAbsPath(out_obj_dir_path); - } - if (!OS_DirExists(out_dxc_dir_path)) - { - OS_CreateDirAtAbsPath(out_dxc_dir_path); - } - if (!OS_DirExists(out_inc_dir_path)) - { - OS_CreateDirAtAbsPath(out_inc_dir_path); - } - if (!OS_DirExists(out_bin_dir_path)) - { - OS_CreateDirAtAbsPath(out_bin_dir_path); - } - - { - SH_Print(Lit("------------------------------\n")); - String compiler = { 0 }; - String compiler_loc = { 0 }; - { - SH_CommandResult where_res = { 0 }; - if (arg_msvc) - { - compiler = Lit("Msvc"); - where_res = RunCommand(&perm, Lit("where cl.exe")); - } - else - { - compiler = Lit("Clang"); - where_res = RunCommand(&perm, Lit("where clang.exe")); - } - compiler_loc = where_res.error ? Lit("Not found") : StringReplace(&perm, where_res.output, Lit("\n"), Lit("")); - } - SH_PrintF(Lit("Compiler path: %F\n"), FmtStr(compiler_loc)); - SH_PrintF(Lit("Build path: \"%F\"\n"), FmtStr(out_bin_dir_path)); - SH_PrintF(Lit("[%F]\n"), FmtStr(compiler)); - if (arg_asan) SH_Print(Lit("[Asan Enabled]\n")); - if (arg_profiling) SH_Print(Lit("[Profiling]\n")); - if (arg_developer) SH_Print(Lit("[Developer build]\n")); - SH_Print(Lit("------------------------------\n\n")); - } - - //- Constants - String dep_file_extension = Lit("d"); - String obj_file_extension = Lit("obj"); - Bool should_embed_res_dir = !arg_developer; - Bool should_embed_in_rc = !!arg_msvc; - - D_Tag executable_file = D_TagFromPath(&perm, StringF(&perm, Lit("%F/PowerPlay.exe"), FmtStr(out_bin_dir_path)), D_TagKind_File); - D_Tag dxc_dir = D_TagFromPath(&perm, out_dxc_dir_path, D_TagKind_Dir); - D_Tag res_dir = D_TagFromPath(&perm, Lit("res"), D_TagKind_Dir); - D_Tag icon_file = D_TagFromPath(&perm, Lit("icon.ico"), D_TagKind_File); - - D_Tag inc_src_file = D_TagFromPath(&perm, Lit("src/inc/inc_core.c"), D_TagKind_File); - D_Tag rc_res_file = D_TagFromPath(&perm, StringF(&perm, Lit("%F/rc.res"), FmtStr(out_obj_dir_path)), D_TagKind_File); - - //////////////////////////////// - //~ Determine compiler args - - String dxc_args_format = { 0 }; - - StringList c_compile_args = { 0 }; - StringList cpp_compile_args = { 0 }; - StringList compile_and_link_args = { 0 }; - StringList compile_args = { 0 }; - StringList compile_warnings = { 0 }; - StringList link_warnings = { 0 }; - StringList link_args = { 0 }; - StringList rc_compile_args = { 0 }; - StringList dxc_compile_args = { 0 }; - { - if (arg_msvc) - { - //- Msvc - StringListAppend(&perm, &c_compile_args, Lit("cl.exe /nologo /c \"%F\" /Fo\"%F\" /FS /showIncludes")); - StringListAppend(&perm, &cpp_compile_args, Lit("cl.exe /nologo /std:c++20 /c \"%F\" /Fo\"%F\" /FS /showIncludes")); - StringListAppend(&perm, &rc_compile_args, Lit("rc /fo\"%F\" \"%F\"")); - - StringListAppend(&perm, &link_args, Lit("link.exe /nologo %F /OUT:\"%F\" /OPT:REF /OPT:ICF")); - - String warnings = Lit("/WX /Wall " - "/options:strict " - "/wd4820 /wd4201 /wd5220 /wd4514 /wd4244 /wd5045 /wd4242 /wd4061 /wd4189 /wd4723 /wd5246 /wd4324 /wd4464 /wd4577 /wd4100"); - StringListAppend(&perm, &compile_warnings, warnings); - StringListAppend(&perm, &link_warnings, Lit("/WX")); - - StringListAppend(&perm, &compile_args, StringF(&perm, Lit("/Fd\"%F\\\""), FmtStr(out_bin_dir_path))); - } - else - { - //- Clang - StringListAppend(&perm, &c_compile_args, Lit("clang -xc -std=c99 -c \"%F\" -o \"%F\" -MD")); - StringListAppend(&perm, &cpp_compile_args, Lit("clang -xc++ -std=c++20 -c \"%F\" -o \"%F\" -MD")); - StringListAppend(&perm, &rc_compile_args, Lit("llvm-rc /fo\"%F\" \"%F\"")); - - StringListAppend(&perm, &link_args, Lit("clang %F -o \"%F\"")); - - StringListAppend(&perm, - &compile_and_link_args, - Lit("-fuse-ld=lld-link " - "-fno-strict-aliasing " - "-fno-finite-loops " - "-fwrapv " - "-msse4.1 " - "-msse4.2 ")); - - String warnings = Lit("-Weverything -Werror " - "-Wframe-larger-than=65536 " - "-Wno-unused-macros -Wno-gnu-zero-variadic-macro-arguments -Wno-documentation " - "-Wno-old-style-cast -Wno-conversion -Wno-double-promotion " - "-Wno-declaration-after-statement " - "-Wno-bad-function-cast -Wno-class-varargs -Wno-unreachable-code-break " - "-Wno-cast-align -Wno-float-equal -Wno-zero-as-null-pointer-constant " - "-Wno-cast-qual -Wno-missing-noreturn " - "-Wno-initializer-overrides " - "-Wno-c99-extensions -Wno-c++98-compat-pedantic -Wno-c++98-compat " - "-Wno-switch-enum -Wno-assign-enum -Wno-switch-default " - "-Wno-reserved-identifier -Wno-reserved-macro-identifier " - "-Wno-missing-designated-field-initializers " - "-Wno-unsafe-buffer-usage " - "-Wno-c11-extensions -Wno-gnu-anonymous-struct -Wno-nested-anon-types "); - - /* -Wno-unused-variable -Wno-unused-but-set-variable -Wno-unused-parameter */ - - StringListAppend(&perm, &compile_warnings, warnings); - StringListAppend(&perm, &link_warnings, warnings); - } - - //- Dxc - { - StringListAppend(&perm, &dxc_compile_args, Lit("-I src/ -H -WX -Ges")); - String dxc_args_define = StringFromStringList(&perm, Lit(" "), dxc_compile_args); - StringListAppend(&perm, &compile_args, StringF(&perm, Lit("-DDXC_ARGS=\"%F\""), FmtStr(dxc_args_define))); - } - - //- Rtc - if (arg_rtc) - { - if (!arg_crtlib) - { - Error(Lit("CrtlibIsEnabled (C runtime library) Must be enabled when compiling with RtcIsEnabled (runtime checks)")); - OS_Exit(1); - } - StringListAppend(&perm, &compile_args, Lit("-DRtcIsEnabled=1")); - if (arg_msvc) - { - if (!arg_asan) - { - /* Enable /RtcIsEnabled option (not compatible with AsanIsEnabled) */ - StringListAppend(&perm, &compile_args, Lit("/RTCcsu")); - } - } - else - { - /* Enable UBSan */ - StringListAppend(&perm, &compile_and_link_args, Lit("-fsanitize=undefined -fsanitize-trap=all")); - //StringListAppend(&perm, &compile_and_link_args, Lit("-fsanitize=undefined")); - } - } - - //- Crtlib - if (arg_crtlib) - { - StringListAppend(&perm, &compile_args, Lit("-DCrtlibIsEnabled=1")); - } - else - { - if (arg_msvc) - { - /* TODO */ - Error(Lit("TODO\n")); - OS_Exit(1); - } - else - { - StringListAppend(&perm, &compile_and_link_args, Lit("-mno-stack-arg-probe -fno-builtin -nostdlib")); - } - } - - //- Optimization - if (arg_unoptimized) - { - StringListAppend(&perm, &compile_args, Lit("-DUnoptimizedIsEnabled=1")); - if (arg_msvc) - { - StringListAppend(&perm, &compile_args, Lit("/Od")); - } - else - { - StringListAppend(&perm, &compile_and_link_args, Lit("-O0")); - } - StringListAppend(&perm, &dxc_compile_args, Lit("-Od")); - } - else - { - if (arg_msvc) - { - StringListAppend(&perm, &compile_args, Lit("/O2 /GL ")); - StringListAppend(&perm, &link_args, Lit("/LTCG")); - } - else - { - StringListAppend(&perm, &compile_and_link_args, Lit("-O3 -flto")); - } - StringListAppend(&perm, &dxc_compile_args, Lit("-O3")); - } - - //- Debug info - if (arg_debinfo) - { - StringListAppend(&perm, &compile_args, Lit("-DDebinfoEnabled=1")); - if (arg_msvc) - { - StringListAppend(&perm, &compile_args, Lit("/JMC /Zi")); - StringListAppend(&perm, &link_args, Lit("/DEBUG:FULL")); - } - else - { - StringListAppend(&perm, &compile_and_link_args, Lit("-g")); - } - StringListAppend(&perm, &dxc_compile_args, Lit("-Zi -Qembed_debug")); - } - - //- Address sanitizer - if (arg_asan) - { - if (!arg_crtlib) - { - Error(Lit("CrtlibIsEnabled (C runtime library) Must be enabled when compiling with asan enabled")); - OS_Exit(1); - } - StringListAppend(&perm, &compile_args, Lit("-DAsanIsEnabled=1")); - if (arg_msvc) - { - StringListAppend(&perm, &compile_args, Lit("/fsanitize=address")); - } - else - { - StringListAppend(&perm, &compile_and_link_args, Lit("-fsanitize=address")); - } - } - - //- Developer mode - if (arg_developer) - { - StringListAppend(&perm, &compile_args, Lit("-DDeveloperIsEnabled=1")); - } - - //- Profiling - if (arg_profiling) - { - if (!arg_crtlib) - { - Error(Lit("CrtlibIsEnabled (C runtime library) must be enabled when compiling with profiling enabled")); - OS_Exit(1); - } - if (arg_msvc) - { - Error(Lit("MSVC not supported with profiling enabled (Profiling relies on Clang attributes)")); - OS_Exit(1); - } - StringListAppend(&perm, &compile_args, Lit("-DProfilingIsEnabled=1")); - - /* Tracy include path */ - if (tracy_src_dir_path.len == 0 || !OS_DirExists(tracy_src_dir_path)) - { - Error(StringF(&perm, Lit("Profiling is enabled but tracy directory \"%F\" does not exist (set by environment variable \"%F\")"), FmtStr(tracy_src_dir_path), FmtStr(tracy_env_var_name))); - OS_Exit(1); - } - StringListAppend(&perm, &compile_args, StringF(&perm, Lit("-DTracyClientHeaderPath=\\\"%F\\\""), FmtStr(tracy_client_header_path))); - StringListAppend(&perm, &compile_args, StringF(&perm, Lit("-DTracyClientSrcPath=\\\"%F\\\""), FmtStr(tracy_client_src_path))); - } - - //- Incbin - if (!arg_msvc) - { - String incbin_dir = StringReplace(&perm, out_inc_dir_path, Lit("\\"), Lit("/")); - StringListAppend(&perm, &compile_args, StringF(&perm, Lit("-DIncbinRawDir=\"%F\""), FmtStr(incbin_dir))); - } - } - - //////////////////////////////// - //~ Examine build hash - - { - D_Tag build_hash_file = D_TagFromPath(&perm, build_hash_path, D_TagKind_File); - - U64 build_hash = D_HashString64Basis; - { - String args_str = StringFromStringList(&perm, Lit(" "), cli_args); - String compile_time_str = Lit(__DATE__ __TIME__); - build_hash = D_HashString64(build_hash, args_str); - build_hash = D_HashString64(build_hash, compile_time_str); - } - - U64 old_build_hash = 0; - { - String build_hash_file_data = D_ReadAll(&perm, build_hash_file); - if (build_hash_file_data.len >= 8) - { - MemoryCopy((Byte *)&old_build_hash, build_hash_file_data.text, 8); - } - } - - if (build_hash != old_build_hash) - { - SH_Print(Lit("Builder exe or build args have changed, forcing complete rebuild.\n")); - force_rebuild = 1; - String data = StringFromStruct(&build_hash); - D_ClearWrite(build_hash_file, data); - } - } - - //////////////////////////////// - //~ Build steps - - hist = D_HistFromPath(&perm, hist_path); - D_TagList link_files = { 0 }; - - //- Build step: Compile shaders - Atomic32 shader_success_flag = { 0 }; - { - D_TagList src_input_files = { 0 }; - { - D_Tag src_dir = D_TagFromPath(&perm, Lit("src"), D_TagKind_Dir); - D_TagList src_files = { 0 }; - D_GetDirContents(&perm, &src_files, src_dir, 1); - - for (D_TagListNode *n = src_files.first; n; n = n->next) - { - D_Tag file = n->tag; - String path = file.full_path; - String name = D_GetName(file); - String extension = StringPathExtension(name); - Bool is_dir = file.kind == D_TagKind_Dir; - Bool is_rst = !is_dir && StringEqual(extension, Lit("rst")); - Bool is_knl = !is_dir && StringEqual(extension, Lit("knl")); - if (is_rst || is_knl) - { - D_TagListAppend(&perm, &src_input_files, file); - } - } - } - - { - AddSyncPoint(); - - String dxc_args_bare = StringFromStringLists(&perm, Lit(" "), dxc_compile_args); - String dxc_compile_args_fmt = StringF(&perm, Lit("dxc %%F -E %%F -T %%F -Fo %%F %F"), FmtStr(dxc_args_bare)); - for (D_TagListNode *n = src_input_files.first; n; n = n->next) - { - D_Tag file = n->tag; - String path = file.full_path; - String name = D_GetName(file); - String name_no_extension = StringPathNoExtension(name); - String extension = StringPathExtension(name); - Bool is_rst = StringEqual(extension, Lit("rst")); - Bool is_knl = !is_rst && StringEqual(extension, Lit("knl")); - for (I32 kind = 0; kind < 3; ++kind) - { - String out_file_extension = { 0 }; - String entry = { 0 }; - String profile = { 0 }; - if (kind == 0 && is_rst) - { - /* Vertex shader */ - out_file_extension = Lit("vs"); - entry = Lit("vs"); - profile = Lit("vs_6_6"); - } - else if (kind == 1 && is_rst) - { - /* Pixel shader */ - out_file_extension = Lit("ps"); - entry = Lit("ps"); - profile = Lit("ps_6_6"); - } - else if (kind == 2 && is_knl) - { - /* Compute shader */ - out_file_extension = Lit("cs"); - entry = Lit("cs"); - profile = Lit("cs_6_6"); - } - if (entry.len > 0) - { - D_Tag dep_file; - { - String dep_file_path = StringF(&perm, Lit("%F/%F.%F"), FmtStr(out_obj_dir_path), FmtStr(name_no_extension), FmtStr(dep_file_extension)); - dep_file = D_TagFromPath(&perm, dep_file_path, D_TagKind_DepFile); - } - - D_Tag dxc_file; - { - String dxc_file_path = StringF(&perm, Lit("%F/%F.%F"), FmtStr(out_dxc_dir_path), FmtStr(name_no_extension), FmtStr(out_file_extension)); - dxc_file = D_TagFromPath(&perm, dxc_file_path, D_TagKind_File); - } - D_AddDependency(&store, dxc_file, file); - D_AddDependency(&store, dxc_file, dep_file); - - if (IsDirty(dxc_file)) - { - String step_name = StringF(&perm, Lit("%F -> %F"), FmtStr(name), FmtStr(D_GetName(dxc_file))); - { - BuildStepSimpleCommandArg *bs_arg = ArenaPush(&perm, BuildStepSimpleCommandArg); - bs_arg->cmd = StringF(&perm, dxc_compile_args_fmt, FmtStr(file.full_path), FmtStr(entry), FmtStr(profile), FmtStr(dxc_file.full_path)); - bs_arg->failure_flag = &shader_success_flag; - bs_arg->delete_file_on_failure = dxc_file; - bs_arg->dxc_depfile_dependent = dxc_file; - bs_arg->dxc_depfile = dep_file; - AddStep(step_name, &BuildStepSimpleCommand, bs_arg); - } - } - } - } - } - } - } - - //- Build step: Tar archives - Atomic32 tar_success_flag = { 0 }; - { - AddSyncPoint(); - - D_TagList tar_input_dirs = { 0 }; - D_TagListAppend(&perm, &tar_input_dirs, dxc_dir); - if (should_embed_res_dir) - { - D_TagListAppend(&perm, &tar_input_dirs, res_dir); - } - - for (D_TagListNode *n = tar_input_dirs.first; n; n = n->next) - { - D_Tag input_dir = n->tag; - D_Tag tar_file = D_TagFromPath(&perm, StringF(&perm, Lit("%F/%F.tar"), FmtStr(out_inc_dir_path), FmtStr(D_GetName(input_dir))), D_TagKind_File); - D_AddDependency(&store, tar_file, input_dir); - if (should_embed_in_rc) - { - D_AddDependency(&store, rc_res_file, tar_file); - } - else - { - D_AddDependency(&store, inc_src_file, tar_file); - } - if (IsDirty(tar_file)) - { - BuildStepSimpleCommandArg *bs_arg = ArenaPush(&perm, BuildStepSimpleCommandArg); - bs_arg->cmd = StringF(&perm, Lit("cd %F && tar cvf %F ."), FmtStr(input_dir.full_path), FmtStr(tar_file.full_path)); - bs_arg->failure_flag = &tar_success_flag; - bs_arg->skip_flag = &shader_success_flag; - String step_name = StringF(&perm, Lit("%F -> %F"), FmtStr(D_GetName(input_dir)), FmtStr(D_GetName(tar_file))); - AddStep(step_name, &BuildStepSimpleCommand, bs_arg); - } - } - } - - //- Build step: Compile RC files - Atomic32 rc_success_flag = { 0 }; - if (PlatformWindows) - { - AddSyncPoint(); - - D_Tag rc_input_file = D_TagFromPath(&perm, StringF(&perm, Lit("%F/rc.rc"), FmtStr(out_inc_dir_path)), D_TagKind_File); - { - D_Tag res_tar_file = D_TagFromPath(&perm, StringF(&perm, Lit("%F/%F.tar"), FmtStr(out_inc_dir_path), FmtStr(D_GetName(res_dir))), D_TagKind_File); - D_Tag dxc_tar_file = D_TagFromPath(&perm, StringF(&perm, Lit("%F/%F.tar"), FmtStr(out_inc_dir_path), FmtStr(D_GetName(dxc_dir))), D_TagKind_File); - D_AddDependency(&store, rc_input_file, icon_file); - D_AddDependency(&store, rc_input_file, dxc_tar_file); - if (should_embed_in_rc && should_embed_res_dir) - { - D_AddDependency(&store, rc_input_file, res_tar_file); - } - if (IsDirty(rc_input_file)) - { - D_ClearWrite(rc_input_file, Lit("")); - D_AppendWrite(rc_input_file, StringF(&perm, Lit("%F %F DISCARDABLE %F\n"), FmtStr(D_GetName(icon_file)), FmtStr(Lit("ICON")), FmtStr(D_GetName(icon_file)))); - D_AppendWrite(rc_input_file, StringF(&perm, Lit("%F %F DISCARDABLE %F\n"), FmtStr(D_GetName(dxc_tar_file)), FmtStr(Lit("RCDATA")), FmtStr(D_GetName(dxc_tar_file)))); - if (should_embed_in_rc && should_embed_res_dir) - { - D_AppendWrite(rc_input_file, StringF(&perm, Lit("%F %F DISCARDABLE %F\n"), FmtStr(D_GetName(res_tar_file)), FmtStr(Lit("RCDATA")), FmtStr(D_GetName(res_tar_file)))); - } - } - } - - { - String rc_compile_args_fmt = StringFromStringLists(&perm, Lit(" "), rc_compile_args); - D_AddDependency(&store, rc_res_file, rc_input_file); - - if (IsDirty(rc_res_file)) - { - BuildStepSimpleCommandArg *bs_arg = ArenaPush(&perm, BuildStepSimpleCommandArg); - bs_arg->cmd = StringF(&perm, rc_compile_args_fmt, FmtStr(rc_res_file.full_path), FmtStr(rc_input_file.full_path)); - bs_arg->skip_flag = &tar_success_flag; - bs_arg->failure_flag = &rc_success_flag; - String step_name = StringF(&perm, Lit("%F -> %F"), FmtStr(D_GetName(rc_input_file)), FmtStr(D_GetName(rc_res_file))); - AddStep(step_name, &BuildStepSimpleCommand, bs_arg); - } - - D_TagListAppend(&perm, &link_files, rc_res_file); - } - } - - //- Build step: Compile src files - Atomic32 src_success_flag = { 0 }; - { - /* src_input_files will contain .c & .cpp files with names that match their parent directory */ - D_TagList src_input_files = { 0 }; - { - D_Tag top_level_dir = D_TagFromPath(&perm, Lit("src"), D_TagKind_Dir); - D_TagList top_level_files = { 0 }; - D_GetDirContents(&perm, &top_level_files, top_level_dir, 0); - for (D_TagListNode *top_level_node = top_level_files.first; top_level_node; top_level_node = top_level_node->next) - { - D_Tag top_level_item = top_level_node->tag; - String top_level_item_name = D_GetName(top_level_item); - if (top_level_item.kind == D_TagKind_Dir) - { - D_TagList sub_files = { 0 }; - D_GetDirContents(&perm, &sub_files, top_level_item, 0); - for (D_TagListNode *n = sub_files.first; n; n = n->next) - { - D_Tag file = n->tag; - String name = D_GetName(file); - String extension = StringPathExtension(name); - String name_no_extension = StringPathNoExtension(name); - if (StringEqual(name_no_extension, top_level_item_name)) - { - Bool is_c = StringEqual(extension, Lit("c")); - Bool is_cpp = !is_c && StringEqual(extension, Lit("cpp")); - if (is_c || is_cpp) - { - D_TagListAppend(&perm, &src_input_files, file); - } - } - } - } - } - } - - /* Build src files */ - { - AddSyncPoint(); - - String c_compile_args_fmt = StringFromStringLists(&perm, Lit(" "), c_compile_args, compile_warnings, compile_and_link_args, compile_args); - String cpp_compile_args_fmt = StringFromStringLists(&perm, Lit(" "), cpp_compile_args, compile_warnings, compile_and_link_args, compile_args); - for (D_TagListNode *n = src_input_files.first; n; n = n->next) - { - D_Tag file = n->tag; - String path = file.full_path; - String name = D_GetName(file); - String extension = StringPathExtension(name); - Bool is_c = StringEqual(extension, Lit("c")); - Bool is_cpp = !is_c && StringEqual(extension, Lit("cpp")); - - D_Tag dep_file; - { - String name_no_extension = StringPathNoExtension(name); - String dep_file_path = StringF(&perm, Lit("%F/%F.%F"), FmtStr(out_obj_dir_path), FmtStr(name_no_extension), FmtStr(dep_file_extension)); - dep_file = D_TagFromPath(&perm, dep_file_path, D_TagKind_DepFile); - } - - D_Tag obj_file; - { - String name_no_extension = StringPathNoExtension(name); - String obj_file_path = StringF(&perm, Lit("%F/%F.%F"), FmtStr(out_obj_dir_path), FmtStr(name_no_extension), FmtStr(obj_file_extension)); - obj_file = D_TagFromPath(&perm, obj_file_path, D_TagKind_File); - } - D_AddDependency(&store, obj_file, file); - D_AddDependency(&store, obj_file, dep_file); - - if (IsDirty(obj_file)) - { - String comp_cmd_fmt = is_c ? c_compile_args_fmt : cpp_compile_args_fmt; - String step_name = StringF(&perm, Lit("%F -> %F"), FmtStr(name), FmtStr(D_GetName(obj_file))); - D_TagList depfile_force_includes = { 0 }; - if (arg_msvc) - { - BuildStepMsvcCompileCommandArg *bs_arg = ArenaPush(&perm, BuildStepMsvcCompileCommandArg); - bs_arg->cmd = StringF(&perm, comp_cmd_fmt, FmtStr(file.full_path), FmtStr(obj_file.full_path)); - bs_arg->depfile_dependent = obj_file; - bs_arg->output_depfile = dep_file; - bs_arg->depfile_force_includes = depfile_force_includes; - bs_arg->skip_flag = &rc_success_flag; - bs_arg->failure_flag = &src_success_flag; - bs_arg->delete_file_on_failure = obj_file; - AddStep(step_name, &BuildStepMsvcCompileCommand, bs_arg); - } - else - { - BuildStepSimpleCommandArg *bs_arg = ArenaPush(&perm, BuildStepSimpleCommandArg); - bs_arg->cmd = StringF(&perm, comp_cmd_fmt, FmtStr(file.full_path), FmtStr(obj_file.full_path)); - bs_arg->skip_flag = &rc_success_flag; - bs_arg->failure_flag = &src_success_flag; - AddStep(step_name, &BuildStepSimpleCommand, bs_arg); - } - } - - D_TagListAppend(&perm, &link_files, obj_file); - } - } - } - - //- Build step: Link - { - AddSyncPoint(); - - String link_files_str = { 0 }; - { - StringList link_files_quoted_list = { 0 }; - for (D_TagListNode *n = link_files.first; n; n = n->next) - { - D_Tag file = n->tag; - String path = StringF(&perm, Lit("\"%F\""), FmtStr(file.full_path)); - StringListAppend(&perm, &link_files_quoted_list, path); - D_AddDependency(&store, executable_file, file); - } - link_files_str = StringFromStringList(&perm, Lit(" "), link_files_quoted_list); - } - if (link_files_str.len > 0 && IsDirty(executable_file)) - { - String link_args_fmt = StringFromStringLists(&perm, Lit(" "), link_args, link_warnings, compile_and_link_args); - BuildStepSimpleCommandArg *bs_arg = ArenaPush(&perm, BuildStepSimpleCommandArg); - bs_arg->cmd = StringF(&perm, link_args_fmt, FmtStr(link_files_str), FmtStr(executable_file.full_path)); - bs_arg->skip_flag = &src_success_flag; - bs_arg->delete_file_on_failure = executable_file; - String step_name = Lit("Link"); - AddStep(step_name, &BuildStepSimpleCommand, bs_arg); - } - } - - //////////////////////////////// - //~ Execute build steps - - Bool success = 1; - I64 step_count = sl->count; - if (step_count > 0) - { - KillRunningProcesses(); - ExecuteSteps(); - - Step *s = sl->first; - I64 step_i = 0; - while (s) - { - ++step_i; - - { - OS_Lock lock = OS_MutexLockS(&s->res_mutex); - while (s->res_status == StepStatus_None) - { - OS_ConditionVariableWait(&s->res_cv, &lock); - } - OS_MutexUnlock(&lock); - } - - String output = s->res_output; - if (s->res_status == StepStatus_Success) - { - SH_PrintF(Lit("[%F/%F] %F\n"), FmtI64(step_i), FmtI64(step_count), FmtStr(s->name)); - } - else if (s->res_status == StepStatus_Failure) - { - success = 0; - SH_PrintF(Lit("[%F/%F] %F\n\n%F\n\n"), FmtI64(step_i), FmtI64(step_count), FmtStr(s->name), FmtStr(output)); - } - - s = s->next; - skip_cont: - continue; - } - } - else - { - SH_Print(Lit("No work to do\n")); - } - - if (success) - { - T_ShutdownWorkers(); - D_WriteStoreToHistFile(&store, hist_path); - } - else - { - D_WriteStoreToHistFile(&store, hist_path); - Error(Lit("Build failed\n")); - OS_Exit(1); - } - - if (!D_Exists(executable_file)) - { - /* Create blank executible if build fails (since Visual Studio can get - * confused if no build target exists) */ - D_ClearWrite(executable_file, Lit("")); - } -} +//~ Includes diff --git a/src/app/app_core.c b/src/app/app_core.c index 092dfb27..d83134c5 100644 --- a/src/app/app_core.c +++ b/src/app/app_core.c @@ -251,7 +251,7 @@ void Startup(void) P_LogInfoF("Settings file not found, loading default"); window_settings = GetDefaultAppWindowSettings(window); } - PushStringToBuffer(StringFromArray(window_settings.title), Lit(WINDOW_TITLE)); + PushStringToBuff(StringFromArray(window_settings.title), Lit(WINDOW_TITLE)); P_UpdateWindowSettings(window, &window_settings); EndTempArena(temp); diff --git a/src/base/base.lay b/src/base/base.lay index 3efbba4b..8493f3cf 100644 --- a/src/base/base.lay +++ b/src/base/base.lay @@ -1,5 +1,10 @@ @Layer base +//////////////////////////////// +//~ Deps + +@Dep prof + //////////////////////////////// //~ Api diff --git a/src/base/base_arena.h b/src/base/base_arena.h index c7cb54c4..df70a906 100644 --- a/src/base/base_arena.h +++ b/src/base/base_arena.h @@ -157,7 +157,7 @@ Inline void EndTempArena(TempArena temp) } //////////////////////////////// -//~ Arena ctx from operations +//~ Arena ctx operations Inline ArenaCtx *ArenaCtxFromFiberId(i16 fiber_id) { diff --git a/src/base/base_memory.c b/src/base/base_memory.c index b4b4ef75..c6f78c5f 100644 --- a/src/base/base_memory.c +++ b/src/base/base_memory.c @@ -1,15 +1,13 @@ +//////////////////////////////// +//~ Win32 memory allocation + #if PlatformIsWindows -//////////////////////////////// -//~ Windows headers - +//- Windows headers #define WIN32_LEAN_AND_MEAN #define UNICODE #include -//////////////////////////////// -//~ Memory allocation - //- Reserve void *ReserveMemory(u64 size) { diff --git a/src/base/base_string.c b/src/base/base_string.c index 2b35b023..bf0c4593 100644 --- a/src/base/base_string.c +++ b/src/base/base_string.c @@ -208,7 +208,7 @@ String PushString(Arena *arena, String src) return str; } -String PushStringToBuffer(String dst, String src) +String PushStringToBuff(String dst, String src) { String result = ZI; result.len = MinU64(dst.len, src.len); @@ -477,6 +477,7 @@ b32 StringEndsWith(String str, String substring) * FmtHex: Format a u64 in hexadecimal notation * FmtPtr: Format a pointer in hexadecimal notation prefixed by "0x" * FmtHandle: Format a 128 bit handle + * FmtUid: Format a 128 bit uid * * FmtEnd (internal): Denote the end of the va_list * @@ -798,7 +799,7 @@ char *CstrFromString(Arena *arena, String src) return (char *)text; } -char *CstrBuffFromStringToBuff(String dst_buff, String src) +char *CstrFromStringToBuff(String dst_buff, String src) { if (dst_buff.len > 0) { diff --git a/src/base/base_string.h b/src/base/base_string.h index e1a25035..4ebb7077 100644 --- a/src/base/base_string.h +++ b/src/base/base_string.h @@ -85,9 +85,6 @@ Struct(CodepointIter) //////////////////////////////// //~ String utils -#define STRING(size, data) (CppCompatInitListType(String) { (size), (data) }) -#define Lit(cstr_lit) CppCompatInitListType(String) { (sizeof((cstr_lit)) - 1), (u8 *)(cstr_lit) } -#define LitNoCast(cstr_lit) { .len = (sizeof((cstr_lit)) - 1), .text = (u8 *)(cstr_lit) } #define StringFromPointers(p0, p1) (CppCompatInitListType(String) { (u8 *)(p1) - (u8 *)(p0), (u8 *)p0 }) #define StringFromStruct(ptr) (CppCompatInitListType(String) { sizeof(*(ptr)), (u8 *)(ptr) }) #define StringFromArena(arena) (STRING((arena)->pos, ArenaBase(arena))) @@ -100,9 +97,8 @@ Struct(CodepointIter) ) //////////////////////////////// -//~ String operartions +//~ Conversion operations -//- Conversion String StringFromChar(Arena *arena, char c); String StringFromU64(Arena *arena, u64 n, u64 base, u64 zfill); String StringFromI64(Arena *arena, i64 n, u64 base, u64 zfill); @@ -111,9 +107,11 @@ String StringFromF64(Arena *arena, f64 f, u32 precision); String StringFromhandle(Arena *arena, u64 v0, u64 v1); String StringFromUid(Arena *arena, Uid uid); -//- Modification +//////////////////////////////// +//~ String operations + String PushString(Arena *arena, String src); -String PushStringToBuffer(String dst, String src); +String PushStringToBuff(String dst, String src); String RepeatString(Arena *arena, String src, u64 count); String CatString(Arena *arena, String str1, String str2); StringArray SplitString(Arena *arena, String str, String delim); @@ -125,7 +123,10 @@ b32 StringContains(String str, String substring); b32 StringStartsWith(String str, String substring); b32 StringEndsWith(String str, String substring); -//- Formatting helpers +//////////////////////////////// +//~ Formatting + +//- Format arg helpers #define FmtChar(v) (FmtArg) {.kind = FmtKind_Char, .value.c = (v)} #define FmtString(v) (FmtArg) {.kind = FmtKind_String, .value.string = (v)} #define FmtUint(v) (FmtArg) {.kind = FmtKind_Uint, .value.uint = (v)} @@ -139,7 +140,7 @@ b32 StringEndsWith(String str, String substring); #define FmtUid(v) (FmtArg) {.kind = FmtKind_Uid, .value.uid = (v) } #define FmtEnd (FmtArg) {.kind = FmtKind_End} -//- Formatting +//- Format functions #define StringFormat(arena, fmt, ...) _StringFormat((arena), (fmt), __VA_ARGS__, FmtEnd) String _StringFormat(Arena *arena, String fmt, ...); String StringFormatV(Arena *arena, String fmt, va_list args); @@ -167,7 +168,7 @@ String32 String32FromString(Arena *arena, String str8); u64 CstrLenNoLimit(char *cstr); u64 CstrLen(char *cstr, u64 limit); char *CstrFromString(Arena *arena, String src); -char *CstrBuffFromStringToBuff(String dest_buff, String src); +char *CstrFromStringToBuff(String dest_buff, String src); String StringFromCstrNoLimit(char *cstr); String StringFromCstr(char *cstr, u64 limit); diff --git a/src/kernel/kernel_blit.rst b/src/kernel/kernel_blit.rst index 7b69c688..4e079d3d 100644 --- a/src/kernel/kernel_blit.rst +++ b/src/kernel/kernel_blit.rst @@ -1,5 +1,3 @@ -#include "kernel.h" - ConstantBuffer sig : register(b0); /* ========================== * diff --git a/src/kernel/kernel_flood.knl b/src/kernel/kernel_flood.knl index 6708d64d..90d8ac04 100644 --- a/src/kernel/kernel_flood.knl +++ b/src/kernel/kernel_flood.knl @@ -1,5 +1,3 @@ -#include "kernel.h" - ConstantBuffer sig : register(b0); /* ========================== * diff --git a/src/kernel/kernel_material.rst b/src/kernel/kernel_material.rst index 9865ec35..fc8bc09d 100644 --- a/src/kernel/kernel_material.rst +++ b/src/kernel/kernel_material.rst @@ -1,5 +1,3 @@ -#include "kernel.h" - ConstantBuffer sig : register(b0); /* ========================== * diff --git a/src/meta/meta.c b/src/meta/meta.c new file mode 100644 index 00000000..5e7dbf09 --- /dev/null +++ b/src/meta/meta.c @@ -0,0 +1,68 @@ +//////////////////////////////// +//~ Includes + +//- Header files +#include "meta_core.h" +#include "meta.h" +#include "meta_intrinsics.h" +#include "meta_memory.h" +#include "meta_math.h" +#include "meta_arena.h" +#include "meta_string.h" +#include "meta_uni.h" +#include "meta_os.h" +#if PlatformIsWindows +# include "win32/meta_win32.h" +#endif + +//- Source files +#include "meta_core.c" +#include "meta_memory.c" +#include "meta_math.c" +#include "meta_arena.c" +#include "meta_string.c" +#include "meta_uni.c" +#if PlatformIsWindows +# include "win32/meta_win32.c" +#endif + +//////////////////////////////// +//~ Globals + +static Arena *perm = 0; +static StringList args = ZI; + +//////////////////////////////// +//~ Util + +/* TODO: Remove printf */ +#include + +void Echo(String msg) +{ + TempArena scratch = BeginScratchNoConflict(); + char *msg_cstr = CstrFromString(scratch.arena, msg); + printf(msg_cstr); + printf("\n"); + fflush(stdout); + EndScratch(scratch); +} + +//////////////////////////////// +//~ Entry point + +i32 main(i32 argc, u8 **argv) +{ + //- Startup + StartupOs(); + perm = AcquireArena(Gibi(64)); + + //- Unpack args + for (i32 i = 1; i < argc; ++i) + { + String arg = StringFromCstrNoLimit(argv[i]); + PushStringNode(perm, &args, arg); + } + + return 0; +} diff --git a/src/meta/meta.h b/src/meta/meta.h new file mode 100644 index 00000000..44700c99 --- /dev/null +++ b/src/meta/meta.h @@ -0,0 +1 @@ +i32 main(i32 argc, u8 **argv); diff --git a/src/meta/meta_arena.c b/src/meta/meta_arena.c new file mode 100644 index 00000000..4efc4226 --- /dev/null +++ b/src/meta/meta_arena.c @@ -0,0 +1,139 @@ +SharedArenaCtx shared_arena_ctx = ZI; + +/* NOTE: Application will exit if arena fails to reserve or commit initial memory. */ +Arena *AcquireArena(u64 reserve) +{ + __prof; + reserve += ArenaHeaderSize; + + /* Round up to nearest block size */ + u64 block_remainder = reserve % ArenaBlockSize; + if (block_remainder > 0) + { + reserve += ArenaBlockSize - block_remainder; + } + + u8 *base = ReserveMemory(reserve); + if (!base) + { + /* Hard fail on memory reserve failure for now */ + Panic(Lit("Failed to reserve memory")); + } + u64 reserved = reserve; + + /* Commit initial block */ + base = CommitMemory(base, ArenaBlockSize); + if (!base) + { + /* Hard fail on commit failure */ + Panic(Lit("Failed to commit initial memory block: System may be out of memory")); + } + + Assert(((u64)base & 0xFFF) == 0); /* Base should be 4k aligned */ + StaticAssert(ArenaHeaderSize <= ArenaBlockSize); /* Header must fit in first block */ + StaticAssert(sizeof(Arena) <= ArenaHeaderSize); /* Arena struct must fit in header */ + + __profalloc(base, ArenaBlockSize); + AsanPoison(base + sizeof(Arena), ArenaBlockSize - sizeof(Arena)); + + /* Create & return arena header at beginning of block */ + Arena *arena = (Arena *)base; + ZeroStruct(arena); + arena->committed = ArenaBlockSize - ArenaHeaderSize; + arena->reserved = reserved; + return arena; +} + +void ReleaseArena(Arena *arena) +{ + AsanUnpoison(arena, arena->committed + ArenaHeaderSize); + __prof; + __proffree(arena); + ReleaseMemory(arena); +} + +/* NOTE: Application will exit if arena fails to commit memory */ +void *PushBytesNoZero(Arena *arena, u64 size, u64 align) +{ + Assert(align > 0); + Assert(!arena->readonly); + + void *ptr = 0; + u8 *base = ArenaBase(arena); + + /* Check to avoid aligning when size = 0 */ + if (size > 0) + { + u64 aligned_start_pos = (arena->pos + (align - 1)); + aligned_start_pos -= aligned_start_pos % align; + + u64 new_pos = aligned_start_pos + size; + if (new_pos > arena->committed) + { + __profn("Arena commit"); + /* Commit new block(s) */ + u64 blocks_needed = (new_pos - arena->committed + ArenaBlockSize - 1) / ArenaBlockSize; + u64 commit_bytes = blocks_needed * ArenaBlockSize; + u64 new_capacity = arena->committed + commit_bytes; + if (new_capacity > arena->reserved) + { + /* Hard fail if we overflow reserved memory for now */ + Panic(Lit("Failed to commit new memory block: Overflow of reserved memory")); + } + void *commit_address = base + arena->committed; + if (!CommitMemory(commit_address, commit_bytes)) + { + /* Hard fail on memory allocation failure for now */ + Panic(Lit("Failed to commit new memory block: System may be out of memory")); + } + arena->committed += commit_bytes; + __proffree(arena); + __profalloc(arena, arena->committed + ArenaHeaderSize); + AsanPoison(commit_address, commit_bytes); + } + + ptr = base + aligned_start_pos; + AsanUnpoison(ptr, new_pos - aligned_start_pos); + arena->pos = new_pos; + } + else + { + ptr = base + arena->pos; + } + + return ptr; +} + +/* Copies the memory from the source arena into the destination arena, + * replacing old contents. Destination arena will be expanded if necessary. */ +void CopyArena(Arena *dst, Arena *src) +{ + ResetArena(dst); + u64 data_size = src->pos; + u8 *data_src = ArenaBase(src); + u8 *data_dst = PushBytesNoZero(dst, data_size, 1); + CopyBytes(data_dst, data_src, data_size); +} + +void ShrinkArena(Arena *arena) +{ + /* Not implemented */ + Assert(0); + LAX arena; +} + +void SetArenaReadonly(Arena *arena) +{ +#if RtcIsEnabled + arena->readonly = 1; +#endif + SetMemoryReadonly(arena, arena->committed + ArenaHeaderSize); +} + +void SetArenaReadWrite(Arena *arena) +{ + SetMemoryReadWrite(arena, arena->committed + ArenaHeaderSize); +#if RtcIsEnabled + arena->readonly = 0; +#endif +} diff --git a/src/meta/meta_arena.h b/src/meta/meta_arena.h new file mode 100644 index 00000000..c83fa066 --- /dev/null +++ b/src/meta/meta_arena.h @@ -0,0 +1,238 @@ +//////////////////////////////// +//~ Arena types + +#define ArenaHeaderSize 64 +#define ArenaBlockSize 16384 + +Struct(Arena) +{ + u64 pos; + u64 committed; + u64 reserved; +#if RtcIsEnabled + b32 readonly; +#endif +}; + +Struct(TempArena) { + Arena *arena; + u64 start_pos; + +#if RtcIsEnabled + u64 scratch_id; +#endif +}; + +//////////////////////////////// +//~ Per-thread arena ctx types + +#define ScratchArenasPerCtx 2 + +Struct(ArenaCtx) +{ + Arena *scratch_arenas[ScratchArenasPerCtx]; + Arena *perm_arena; +}; + +//////////////////////////////// +//~ Shared state + +Struct(SharedArenaCtx) +{ + ArenaCtx arena_contexts[MaxThreads]; +}; + +extern SharedArenaCtx shared_arena_ctx; + +//////////////////////////////// +//~ Arena push/pop + +#define PushStruct(a, type) ((type *)PushBytes((a), sizeof(type), alignof(type))) +#define PushStructNoZero(a, type) ((type *)PushBytesNoZero((a), sizeof(type), alignof(type))) + +#define PushStructs(a, type, n) ((type *)PushBytes((a), (sizeof(type) * (n)), alignof(type))) +#define PushStructsNoZero(a, type, n) ((type *)PushBytesNoZero((a), (sizeof(type) * (n)), alignof(type))) + +#define PopStruct(a, type, dst) PopBytes((a), sizeof(type), dst) +#define PopStructs(a, type, n, dst) PopBytes((a), sizeof(type) * (n), dst) + +/* Returns a pointer to where the next push would be (at alignment of type). + * Equivalent to PushStruct but without actually allocating anything or modifying the arena. */ +#define PushDry(a, type) (type *)(_PushDry((a), alignof(type))) + +void *PushBytesNoZero(Arena *arena, u64 size, u64 align); + +Inline void *PushBytes(Arena *arena, u64 size, u64 align) +{ + void *p = PushBytesNoZero(arena, size, align); + ZeroBytes(p, size); + return p; +} + +Inline u8 *ArenaBase(Arena *arena) +{ + return (u8 *)arena + ArenaHeaderSize; +} + +Inline void PopTo(Arena *arena, u64 pos) +{ + Assert(arena->pos >= pos); + Assert(!arena->readonly); + + AsanPoison(ArenaBase(arena) + pos, arena->pos - pos); + arena->pos = pos; +} + +Inline void PopBytes(Arena *arena, u64 size, void *copy_dst) +{ + Assert(arena->pos >= size); + Assert(!arena->readonly); + + u64 new_pos = arena->pos - size; + void *src = (void *)(ArenaBase(arena) + new_pos); + CopyBytes(copy_dst, src, size); + + AsanPoison(ArenaBase(arena) + new_pos, arena->pos - new_pos); + arena->pos = new_pos; +} + +Inline void *_PushDry(Arena *arena, u64 align) +{ + u64 aligned_start_pos = (arena->pos + (align - 1)); + aligned_start_pos -= aligned_start_pos % align; + void *ptr = ArenaBase(arena) + aligned_start_pos; + return ptr; +} + +//////////////////////////////// +//~ Arena management + +Arena *AcquireArena(u64 reserve); +void ReleaseArena(Arena *arena); + +void CopyArena(Arena *dst, Arena *src); +void ShrinkArena(Arena *arena); +void SetArenaReadonly(Arena *arena); +void SetArenaReadWrite(Arena *arena); + +Inline void *AlignArena(Arena *arena, u64 align) +{ + Assert(!arena->readonly); + if (align > 0) { + u64 aligned_start_pos = (arena->pos + (align - 1)); + aligned_start_pos -= aligned_start_pos % align; + u64 align_bytes = aligned_start_pos - (u64)arena->pos; + if (align_bytes > 0) { + return (void *)PushStructsNoZero(arena, u8, align_bytes); + } else { + return (void *)(ArenaBase(arena) + arena->pos); + } + } else { + /* 0 alignment */ + Assert(0); + return (void *)(ArenaBase(arena) + arena->pos); + } +} + +Inline void *ResetArena(Arena *arena) +{ + PopTo(arena, 0); + return (void *)ArenaBase(arena); +} + +//////////////////////////////// +//~ Temp arena operations + +Inline TempArena BeginTempArena(Arena *arena) +{ + TempArena t = ZI; + t.arena = arena; + t.start_pos = arena->pos; + return t; +} + +Inline void EndTempArena(TempArena temp) +{ + PopTo(temp.arena, temp.start_pos); +} + +//////////////////////////////// +//~ Arena ctx operations + +Inline ArenaCtx *ArenaCtxFromThreadId(i16 thread_id) +{ + SharedArenaCtx *shared = &shared_arena_ctx; + ArenaCtx *ctx = &shared->arena_contexts[thread_id]; + if (!ctx->scratch_arenas[0]) { + __profn("Initialize thread arena ctx"); + for (i32 i = 0; i < (i32)countof(ctx->scratch_arenas); ++i) { + ctx->scratch_arenas[i] = AcquireArena(Gibi(64)); + } + ctx->perm_arena = AcquireArena(Gibi(64)); + } + return ctx; +} + +//////////////////////////////// +//~ Scratch helpers + +/* Any parameterized arenas in the caller's scope should be passed into this + * function as a potential "conflict". This is to prevent friction in case the + * passed arena is itself a scratch arena from another scope (since + * parameterized arenas are often used to allocate persistent results for the + * caller). + * + * Use `BeginScratchNoConflict` instead if there is no arena in the current + * scope that could potentially be a scratch arena from another scope. */ +#define BeginScratch(potential_conflict) _BeginScratch(potential_conflict) + +Inline TempArena _BeginScratch(Arena *potential_conflict) +{ + /* This function is currently hard-coded to support 2 scratch arenas */ + StaticAssert(ScratchArenasPerCtx == 2); + + /* Use `BeginScratchNoConflict` if no conflicts are present */ + Assert(potential_conflict != 0); + + ArenaCtx *ctx = ArenaCtxFromThreadId(ThreadId()); + Arena *scratch_arena = ctx->scratch_arenas[0]; + if (potential_conflict && scratch_arena == potential_conflict) { + scratch_arena = ctx->scratch_arenas[1]; + } + TempArena temp = BeginTempArena(scratch_arena); + return temp; +} + +/* This macro declares an unused "arena" variable that will error if an existing "arena" + * variable is present (due to shadowing). This is for catching obvious cases of + * `BeginScratchNoConflict` getting called when an `arena` variable already + * exists in the caller's scope (`BeginScratch(arena)` should be called + * instead). */ +#define BeginScratchNoConflict() \ + BeginScratchNoConflict_(); \ + do { \ + u8 arena = 0; \ + LAX arena; \ + } while (0) + +Inline TempArena BeginScratchNoConflict_(void) +{ + ArenaCtx *ctx = ArenaCtxFromThreadId(ThreadId()); + Arena *scratch_arena = ctx->scratch_arenas[0]; + TempArena temp = BeginTempArena(scratch_arena); + return temp; +} + +Inline void EndScratch(TempArena scratch_temp) +{ + EndTempArena(scratch_temp); +} + +//////////////////////////////// +//~ Perm arena helpers + +Inline Arena *GetPermArena(void) +{ + ArenaCtx *ctx = ArenaCtxFromThreadId(ThreadId()); + return ctx->perm_arena; +} diff --git a/src/meta/meta_core.c b/src/meta/meta_core.c new file mode 100644 index 00000000..e69de29b diff --git a/src/meta/meta_core.h b/src/meta/meta_core.h new file mode 100644 index 00000000..8015134c --- /dev/null +++ b/src/meta/meta_core.h @@ -0,0 +1,588 @@ +/* TODO: Remove this */ +#define RtcIsEnabled 1 +#define UnoptimizedIsEnabled 1 + +//////////////////////////////// +//~ Compiler feature flags + +/* Compile definition defaults */ +#ifndef RtcIsEnabled +# define RtcIsEnabled 0 +#endif + +#ifndef AsanIsEnabled +# define AsanIsEnabled 0 +#endif + +#ifndef CrtlibIsEnabled +# define CrtlibIsEnabled 0 +#endif + +#ifndef DebinfoEnabled +# define DebinfoEnabled 0 +#endif + +#ifndef DeveloperIsEnabled +# define DeveloperIsEnabled 0 +#endif + +#ifndef ProfilingIsEnabled +# define ProfilingIsEnabled 0 +#endif + +#ifndef UnoptimizedIsEnabled +# define UnoptimizedIsEnabled 0 +#endif + +#ifndef TestsAreEnabled +# define TestsAreEnabled 0 +#endif + +#ifndef IncbinRawDir +# define IncbinDir "" +#else +# define IncbinDir Stringize(IncbinRawDir) +#endif + +//////////////////////////////// +//~ Machine context + +//- Compiler +#if defined(__clang__) +# define CompilerIsClang 1 +# define CompilerIsMsvc 0 +#elif defined(_MSC_VER) +# define CompilerIsClang 0 +# define CompilerIsMsvc 1 +#else +# error Unknown compiler +#endif + +//- Language +#if defined(__cplusplus) +# define LanguageIsCpp 1 +# define LanguageIsC 0 +# define LanguageIsGpu 0 +#elif defined(__HLSL_VERSION) +# define LanguageIsCpp 0 +# define LanguageIsC 0 +# define LanguageIsGpu 1 +#else +# define LanguageIsCpp 0 +# define LanguageIsC 1 +# define LanguageIsGpu 0 +#endif + +//- Operating system +#if defined(_WIN32) +# define PlatformIsWindows 1 +# define PlatformIsMac 0 +# define PlatformIsLinux 0 +#elif defined(__APPLE__) && defined(__MACH__) +# define PlatformIsWindows 0 +# define PlatformIsMac 1 +# define PlatformIsLinux 0 +#elif defined(__gnu_linux__) +# define PlatformIsWindows 0 +# define PlatformIsMac 0 +# define PlatformIsLinux 1 +#elif LanguageIsGpu +# define PlatformIsWindows 0 +# define PlatformIsMac 0 +# define PlatformIsLinux 0 +#else +# error Unknown platform +#endif + +//- Windows NTDDI version +/* FIXME: Remove this */ +#if 0 +#if CompilerIsMsvc +# define NTDDI_WIN11_DT 0x0C0A0000 +# define NTDDI_VERSION 0x0A000000 +# if RtcIsEnabled +# define _ALLOW_RTCc_IN_STL 1 +# endif +#endif +#endif + +//////////////////////////////// +//~ Debug + +//- Static assert +#if CompilerIsMsvc || (LanguageIsC && __STDC_VERSION__ < 202311L) || LanguageIsGpu +# if CompilerIsMsvc +# define StaticAssert2(cond, line) struct STATIC_ASSERT_____##line {int foo[(cond) ? 1 : -1];} +# define StaticAssert1(cond, line) StaticAssert2(cond, line) +# define StaticAssert(cond) StaticAssert1(cond, __LINE__) +# else +# define StaticAssert(cond) _Static_assert(cond, "") +# endif +#else +# define StaticAssert(c) static_assert(c, "") +#endif + +//- Debug assert +#if RtcIsEnabled +# if CompilerIsMsvc +// # define Assert(cond) ((cond) ? 1 : (IsRunningInDebugger() ? (*(volatile i32 *)0 = 0) : Panic(Lit("Assert(" #cond ") failed at " __FILE__ ":" Stringize(__LINE__))))) +# define Assert(cond) ((cond) ? 1 : (IsRunningInDebugger() ? (*(volatile i32 *)0 = 0) : Panic(Lit(__FILE__ "(" Stringize(__LINE__) "): error Assert("#cond")")))) +# define DEBUGBREAK __debugbreak +# else +# define Assert(cond) ((cond) ? 1 : (__builtin_trap(), 0)) +# define DEBUGBREAK __builtin_debugtrap() +# endif +# define DEBUGBREAKABLE { volatile i32 __DEBUGBREAKABLE_VAR = 0; LAX __DEBUGBREAKABLE_VAR; } (void)0 +#else +# define Assert(cond) (void)(0) +#endif + +//- Root constant assert +#define AssertRootConst(s, n) StaticAssert((sizeof(s) % 16 == 0) && /* Root constant struct should pad to 16 byte alignment */ \ + ((sizeof(s) / 4) == n) && /* Root constant struct size should match the specified 32-bit-constant count */ \ + (sizeof(s) <= 256)) /* Root constant struct can only fit 64 DWORDS */ + +//- Debug alias +/* TODO: Remove this */ +#if CompilerIsMsvc +# if DebinfoEnabled +# define DebugAlias(var, alias) *(alias) = &(var) +# else +# define DebugAlias(var, alias) *(alias) = &(var) +# endif +#else +# if DebinfoEnabled +# define DebugAlias(var, alias) __attribute((used)) *(alias) = &(var) +# else +# define DebugAlias(var, alias) __attribute((unused)) *(alias) = &(var) +# endif +#endif + +//- Address sanitization +#if AsanIsEnabled +void __asan_poison_memory_region(void const volatile *, size_t); +void __asan_unpoison_memory_region(void const volatile *add, size_t); +# define AsanPoison(addr, size) __asan_poison_memory_region((addr), (size)) +# define AsanUnpoison(addr, size) __asan_unpoison_memory_region((addr), (size)) +#else +# define AsanPoison(addr, size) +# define AsanUnpoison(addr, size) +#endif + +//////////////////////////////// +//~ Common utility macros + +//- Initlist compatibility +#if CompilerIsMsvc && LanguageIsCpp +# define CppCompatInitListType(type) +#else +# define CppCompatInitListType(type) (type) +#endif + +//- ZeroStruct initialization macro +#if LanguageIsC +# define ZI { 0 } +#else +# define ZI { } +#endif + +//- Inline +#define Inline static inline + +#if CompilerIsMsvc +# define ForceInline Inline __forceinline +#else +# define ForceInline Inline __attribute((always_inline)) +#endif + +#if CompilerIsMsvc +# define ForceNoInline __declspec(noinline) +#else +# define ForceNoInline __attribute__((noinline)) +#endif + +//- Static +#define LocalPersist static +#define Global static +/* TODO: Remove this */ +#define internal static + +//- Read-only +#if PlatformIsWindows +# if CompilerIsMsvc +# pragma section(".rdata$", read) +# define Readonly __declspec(allocate(".rdata$")) +# else +# define Readonly __declspec(allocate(".rdata")) +# endif +#elif PlatformIsMac +# define Readonly __attribute((section("__TEXT,__const"))) +#else +# define Readonly __attribute((section(".rodata"))) +#endif + +//- Barriers +#if CompilerIsMsvc +# define WriteBarrier() _WriteBarrier() +# define ReadBarrier() _ReadBarrier() +#elif defined(__x86_64) || defined(__i386__) +# define WriteBarrier() __asm__ volatile("" ::: "memory") +# define ReadBarrier() __asm__ volatile("" ::: "memory") +#elif LanguageIsGpu +# define WriteBarrier() +# define ReadBarrier() +#else +# error Memory barriers not implemented +#endif + +//- Unused markup +/* Strict unused markup */ +#if CompilerIsClang +# define UNUSED __attribute((unused)) +#else +# define UNUSED +#endif + +/* Relaxed unused markup */ +#define LAX (void) + +//- Fallthrough +#if CompilerIsMsvc +# if LanguageIsCpp +# define FALLTHROUGH [[fallthrough]] +# else +# define FALLTHROUGH +# endif +#elif CompilerIsClang +# define FALLTHROUGH __attribute((fallthrough)) +#else +# define FALLTHROUGH +#endif + +//- Preprocessor concatenation +#define Cat1(a, b) a ## b +#define Cat(a, b) Cat1(a, b) + +//- Preprocessor stringization +#define Stringize1(x) #x +#define Stringize(x) Stringize1(x) + +//- Sizes +#define Kibi(n) (n*1024ULL) +#define Mebi(n) (n*Kibi(1024ULL)) +#define Gibi(n) (n*Mebi(1024ULL)) +#define Tebi(n) (n*Gibi(1024ULL)) + +//- Time +#define NsFromSeconds(s) ((i64)((s) * 1000000000.0)) +#define SecondsFromNs(ns) ((f64)(ns) / 1000000000.0) + +//////////////////////////////// +//~ Type helper macros + +//- typeof +#if CompilerIsMsvc +/* Typeof not supported in MSVC */ +# define TypeofIsDefined 0 +# define typeof(type) Assert(0) +#else +# define TypeofIsDefined 1 +# if LanguageIsCpp || (__STDC_VERSION__ < 202311L) +# define typeof(type) __typeof__(type) +# endif +#endif + +//- alignof +#if (CompilerIsMsvc && LanguageIsC) || (LanguageIsC && (__STDC_VERSION__ < 202311L)) +# define alignof(type) __alignof(type) +#endif + +//- sizeof_field +#define sizeof_field(type, field) sizeof(((type *)0)->field) + +//- offsetof +#if 0 +#if !CompilerIsMsvc +# if !defined _CRT_USE_BUILTIN_OFFSETOF +# define offsetof(type, field) ((u64)&(((type *)0)->field)) +# else +# define offsetof(type, field) __builtin_offsetof(type, field) +# endif +#endif +#endif + +//////////////////////////////// +//~ Array helper macros + +//- countof +#define countof(a) (sizeof(a) / sizeof((a)[0])) + +//- IsArray +#define IsIndexable(a) (sizeof(a[0]) != 0) +#define IsArray(a) (IsIndexable(a) && (((void *)&a) == ((void *)a))) + +//////////////////////////////// +//~ Struct alignment / padding macros + +//- Pack +#if CompilerIsMsvc +# define Packed(s) __pragma(pack(push, 1)) s __pragma(pack(pop)) +#elif CompilerIsClang +# define Packed(s) s __attribute((__packed__)) +#elif LanguageIsGpu +# define Packed(s) s +#endif + +//- alignas +#if (CompilerIsMsvc && LanguageIsC) || (LanguageIsC && __STDC_VERSION__ < 202311L) +# if CompilerIsMsvc +# define alignas(n) __declspec(align(n)) +# else +# define alignas(n) __attribute__((aligned(n))) +# endif +#endif + +//////////////////////////////// +//~ Color helper macros + +//- Rgba 32 bit helpers +#define Rgb32(r, g, b) Rgba32((r), (g), (b), 0xFF) +#define Rgba32(r, g, b, a) (u32)((u32)(r) | ((u32)(g) << 8) | ((u32)(b) << 16) | ((u32)(a) << 24)) +#define Bgr32(rgb) ((((rgb >> 0) & 0xFF) << 16) | (((rgb >> 8) & 0xFF) << 8) | (((rgb >> 16) & 0xFF) << 0)) + +//- Rgba 32 bit float float helpers +#define _Rgb32U8FromF(fl) ((u8)((fl * 255.0) + 0.5)) +#define Rgba32F(r, g, b, a) Rgba32(_Rgb32U8FromF((r)), _Rgb32U8FromF((g)), _Rgb32U8FromF((b)), _Rgb32U8FromF((a))) +#define Rgb32F(r, g, b) Rgba32F((r), (g), (b), 1.f) + +#define Alpha32F(color, a) ((color) & 0x00FFFFFF) | (_Rgb32U8FromF((a)) << 24) + +//- Pre-defined colors +#define ColorWhite Rgb32(0xFF, 0xFF, 0xFF) +#define ColorBlack Rgb32(0x00, 0x00, 0x00) +#define ColorRed Rgb32(0xFF, 0x00, 0x00) +#define ColorGreen Rgb32(0x00, 0xFF, 0x00) +#define ColorBlue Rgb32(0x00, 0x00, 0xFF) +#define ColorYellow Rgb32(0xFF, 0xFF, 0x00) +#define ColorOrange Rgb32(0xFF, 0xA5, 0x00) +#define ColorPurple Rgb32(0xFF, 0x00, 0XFF) + +//////////////////////////////// +//~ Gpu helpers + +#if LanguageIsGpu +//- Resource heap index +# define GpuResourceFromUrid(urid) ResourceDescriptorHeap[urid] +# define GpuResourceFromNurid(nurid) ResourceDescriptorHeap[NonUniformResourceIndex(nurid)] + +//- Semantic declaration +# define Semantic(t, n) t n : n +#endif + +//////////////////////////////// +//~ Intrinsic headers + +#if !LanguageIsGpu +/* Intrinsic header info: + * _v, 0, 0); } +ForceInline i8 Atomic8FetchSet(Atomic8 *x, i8 e) { return (i8)_InterlockedExchange8((char *)&x->_v, e); } +ForceInline i8 Atomic8FetchTestSet(Atomic8 *x, i8 c, i8 e) { return (i8)_InterlockedCompareExchange8((char *)&x->_v, e, c); } +ForceInline i8 Atomic8FetchXor(Atomic8 *x, i8 c) { return (i8)_InterlockedXor8((char *)&x->_v, c); } +ForceInline i8 Atomic8FetchAdd(Atomic8 *x, i8 a) { return (i8)_InterlockedExchangeAdd8((char *)&x->_v, a); } +//- 16 bit atomic operations +ForceInline i16 Atomic16Fetch(Atomic16 *x) { return (i16)_InterlockedCompareExchange16(&x->_v, 0, 0); } +ForceInline i16 Atomic16FetchSet(Atomic16 *x, i16 e) { return (i16)_InterlockedExchange16(&x->_v, e); } +ForceInline i16 Atomic16FetchTestSet(Atomic16 *x, i16 c, i16 e) { return (i16)_InterlockedCompareExchange16(&x->_v, e, c); } +ForceInline i16 Atomic16FetchTestXor(Atomic16 *x, i16 c) { return (i16)_InterlockedXor16(&x->_v, c); } +ForceInline i16 Atomic16FetchTestAdd(Atomic16 *x, i16 a) { return (i16)_InterlockedExchangeAdd16(&x->_v, a); } +//- 32 bit atomic operations +ForceInline i32 Atomic32Fetch(Atomic32 *x) { return (i32)_InterlockedCompareExchange((volatile long *)&x->_v, 0, 0); } +ForceInline i32 Atomic32FetchSet(Atomic32 *x, i32 e) { return (i32)_InterlockedExchange((volatile long *)&x->_v, e); } +ForceInline i32 Atomic32FetchTestSet(Atomic32 *x, i32 c, i32 e) { return (i32)_InterlockedCompareExchange((volatile long *)&x->_v, e, c); } +ForceInline i32 Atomic32FetchXor(Atomic32 *x, i32 c) { return (i32)_InterlockedXor((volatile long *)&x->_v, c); } +ForceInline i32 Atomic32FetchAdd(Atomic32 *x, i32 a) { return (i32)_InterlockedExchangeAdd((volatile long *)&x->_v, a); } +//- 64 bit atomic operations +ForceInline i64 Atomic64Fetch(Atomic64 *x) { return (i64)_InterlockedCompareExchange64(&x->_v, 0, 0); } +ForceInline i64 Atomic64FetchSet(Atomic64 *x, i64 e) { return (i64)_InterlockedExchange64(&x->_v, e); } +ForceInline i64 Atomic64FetchTestSet(Atomic64 *x, i64 c, i64 e) { return (i64)_InterlockedCompareExchange64(&x->_v, e, c); } +ForceInline i64 Atomic64FetchXor(Atomic64 *x, i64 c) { return (i64)_InterlockedXor64(&x->_v, c); } +ForceInline i64 Atomic64FetchAdd(Atomic64 *x, i64 a) { return (i64)_InterlockedExchangeAdd64(&x->_v, a); } +#else +# error Atomics not implemented +#endif + +#endif + +//////////////////////////////// +//~ Ticket mutex + +#if !LanguageIsGpu + +Struct(TicketMutex) +{ + Atomic64Padded ticket; + Atomic64Padded serving; +}; + +ForceInline void LockTicketMutex(TicketMutex *tm) +{ + i64 ticket = Atomic64FetchAdd(&tm->ticket.v, 1); + while (Atomic64Fetch(&tm->serving.v) != ticket) + { + _mm_pause(); + } +} + +ForceInline void UnlockTicketMutex(TicketMutex *tm) +{ + Atomic64FetchAdd(&tm->serving.v, 1); +} +#endif + +//////////////////////////////// +//~ String types + +#define STRING(size, data) (CppCompatInitListType(String) { (size), (data) }) +#define Lit(cstr_lit) CppCompatInitListType(String) { (sizeof((cstr_lit)) - 1), (u8 *)(cstr_lit) } +#define LitNoCast(cstr_lit) { .len = (sizeof((cstr_lit)) - 1), .text = (u8 *)(cstr_lit) } + +Struct(String) +{ + u64 len; + u8 *text; +}; + +Struct(String16) +{ + u64 len; + u16 *text; +}; + +Struct(String32) +{ + u64 len; + u32 *text; +}; + +//////////////////////////////// +//~ @hookdecl Core hooks + +void Panic(String msg); +b32 IsRunningInDebugger(void); +i16 ThreadId(void); + +#define MaxThreads 1024 +StaticAssert(MaxThreads < I16Max); /* Thread id type should fit max threads */ + +//////////////////////////////// +//~ Prof + +#include "../prof/prof_tracy.h" diff --git a/src/meta/meta_intrinsics.h b/src/meta/meta_intrinsics.h new file mode 100644 index 00000000..eb0f3213 --- /dev/null +++ b/src/meta/meta_intrinsics.h @@ -0,0 +1,105 @@ +//////////////////////////////// +//~ Sqrt intrinsics + +Inline f32 IxSqrtF32(f32 f) +{ + __m128 n = _mm_set_ss(f); + n = _mm_sqrt_ss(n); + return _mm_cvtss_f32(n); +} + +Inline f64 IxSqrtF64(f64 f) +{ + __m128d n = _mm_set_sd(f); + n = _mm_sqrt_sd(_mm_setzero_pd(), n); + return _mm_cvtsd_f64(n); +} + +Inline f32 IxRsqrtF32(f32 f) +{ + __m128 n = _mm_set_ss(f); + n = _mm_rsqrt_ss(n); + return _mm_cvtss_f32(n); +} + +//////////////////////////////// +//~ Round intrinsics + +Inline i32 IxRoundF32ToI32(f32 f) +{ + return _mm_cvtss_si32(_mm_round_ss(_mm_setzero_ps(), _mm_set_ss(f), _MM_FROUND_TO_NEAREST_INT | _MM_FROUND_NO_EXC)); +} + +Inline f32 IxRoundF32ToF32(f32 f) +{ + return _mm_cvtss_f32(_mm_round_ss(_mm_setzero_ps(), _mm_set_ss(f), _MM_FROUND_TO_NEAREST_INT | _MM_FROUND_NO_EXC)); +} + +Inline i64 IxRoundF64ToI64(f64 f) +{ + return _mm_cvtsd_si64(_mm_round_sd(_mm_setzero_pd(), _mm_set_sd(f), _MM_FROUND_TO_NEAREST_INT | _MM_FROUND_NO_EXC)); +} + +Inline f64 IxRoundF64ToF64(f64 f) +{ + return _mm_cvtsd_f64(_mm_round_sd(_mm_setzero_pd(), _mm_set_sd(f), _MM_FROUND_TO_NEAREST_INT | _MM_FROUND_NO_EXC)); +} + +//////////////////////////////// +//~ Floor intrinsics + +Inline i32 IxFloorF32ToI32(f32 f) +{ + return _mm_cvtss_si32(_mm_floor_ss(_mm_setzero_ps(), _mm_set_ss(f))); +} + +Inline f32 IxFloorF32ToF32(f32 f) +{ + return _mm_cvtss_f32(_mm_floor_ss(_mm_setzero_ps(), _mm_set_ss(f))); +} + +Inline i64 IxFloorF64ToI64(f64 f) +{ + return _mm_cvtsd_si64(_mm_floor_sd(_mm_setzero_pd(), _mm_set_sd(f))); +} + +Inline f64 IxFloorF64ToF64(f64 f) +{ + return _mm_cvtsd_f64(_mm_floor_sd(_mm_setzero_pd(), _mm_set_sd(f))); +} + +//////////////////////////////// +//~ Ceil intrinsics + +Inline i32 IxCeilF32ToI32(f32 f) +{ + return _mm_cvtss_si32(_mm_ceil_ss(_mm_setzero_ps(), _mm_set_ss(f))); +} + +Inline f32 IxCeilF32ToF32(f32 f) +{ + return _mm_cvtss_f32(_mm_ceil_ss(_mm_setzero_ps(), _mm_set_ss(f))); +} + +Inline i64 IxCeilF64ToI64(f64 f) +{ + return _mm_cvtsd_si64(_mm_ceil_sd(_mm_setzero_pd(), _mm_set_sd(f))); +} + +Inline f64 IxCeilF64ToF64(f64 f) +{ + return _mm_cvtsd_f64(_mm_ceil_sd(_mm_setzero_pd(), _mm_set_sd(f))); +} + +//////////////////////////////// +//~ Truncate intrinsics + +Inline f32 IxTruncF32ToF32(f32 f) +{ + return _mm_cvtss_f32(_mm_round_ss(_mm_setzero_ps(), _mm_set_ss(f), _MM_FROUND_TO_ZERO | _MM_FROUND_NO_EXC)); +} + +Inline f64 IxTruncF64ToF64(f64 f) +{ + return _mm_cvtsd_f64(_mm_round_sd(_mm_setzero_pd(), _mm_set_sd(f), _MM_FROUND_TO_ZERO | _MM_FROUND_NO_EXC)); +} diff --git a/src/meta/meta_math.c b/src/meta/meta_math.c new file mode 100644 index 00000000..cb05a245 --- /dev/null +++ b/src/meta/meta_math.c @@ -0,0 +1,1447 @@ +/* Math functions are default 32 bit (f32, i32, etc) unless specified */ + +//////////////////////////////// +//~ Min / max + +//- Min +u8 MinU8(u8 a, u8 b) { return a <= b ? a : b; } +i8 MinI8(i8 a, i8 b) { return a <= b ? a : b; } +u32 MinU32(u32 a, u32 b) { return a <= b ? a : b; } +i32 MinI32(i32 a, i32 b) { return a <= b ? a : b; } +f32 MinF32(f32 a, f32 b) { return a <= b ? a : b; } +u64 MinU64(u64 a, u64 b) { return a <= b ? a : b; } +i64 MinI64(i64 a, i64 b) { return a <= b ? a : b; } +f64 MinF64(f64 a, f64 b) { return a <= b ? a : b; } + +//- Max +u8 MaxU8(u8 a, u8 b) { return a >= b ? a : b; } +i8 MaxI8(i8 a, i8 b) { return a >= b ? a : b; } +u32 MaxU32(u32 a, u32 b) { return a >= b ? a : b; } +i32 MaxI32(i32 a, i32 b) { return a >= b ? a : b; } +f32 MaxF32(f32 a, f32 b) { return a >= b ? a : b; } +u64 MaxU64(u64 a, u64 b) { return a >= b ? a : b; } +i64 MaxI64(i64 a, i64 b) { return a >= b ? a : b; } +f64 MaxF64(f64 a, f64 b) { return a >= b ? a : b; } + +//- Clamp +u32 ClampU32(u32 v, u32 min, u32 max) { return v < min ? min : v > max ? max : v; } +i32 ClampI32(i32 v, i32 min, i32 max) { return v < min ? min : v > max ? max : v; } +f32 ClampF32(f32 v, f32 min, f32 max) { return v < min ? min : v > max ? max : v; } +u64 ClampU64(u64 v, u64 min, u64 max) { return v < min ? min : v > max ? max : v; } +i64 ClampI64(i64 v, i64 min, i64 max) { return v < min ? min : v > max ? max : v; } +f64 ClampF64(f64 v, f64 min, f64 max) { return v < min ? min : v > max ? max : v; } + +//////////////////////////////// +//~ Rounding ops + +//- Round +f32 RoundF32(f32 f) +{ + return IxRoundF32ToF32(f); +} + +f64 RoundF64(f64 f) +{ + return IxRoundF64ToF64(f); +} + +i32 RoundF32ToI32(f32 f) +{ + return IxRoundF32ToI32(f); +} + +i64 RoundF64ToI64(f64 f) +{ + return IxRoundF64ToI64(f); +} + +//- Floor +f32 FloorF32(f32 f) +{ + return IxFloorF32ToF32(f); +} + +f64 FloorF64(f64 f) +{ + return IxFloorF64ToF64(f); +} + +i32 FloorF32ToI32(f32 f) +{ + return IxFloorF32ToI32(f); +} + +i64 FloorF64ToI64(f64 f) +{ + return IxFloorF64ToI64(f); +} + +//- Ceil +f32 CeilF32(f32 f) +{ + return IxCeilF32ToF32(f); +} + +f64 CeilF64(f64 f) +{ + return IxCeilF64ToF64(f); +} + +i32 CeilF32ToI32(f32 f) +{ + return IxCeilF32ToI32(f); +} + +i64 CeilF64ToI64(f64 f) +{ + return IxCeilF64ToI64(f); +} + +//- Trunc +f32 TruncF32(f32 f) +{ + return IxTruncF32ToF32(f); +} + +f64 TruncF64(f64 f) +{ + return IxTruncF64ToF64(f); +} + +//////////////////////////////// +//~ Fmod + +f32 ModF32(f32 x, f32 m) +{ + return x - m * TruncF32(x / m); +} + +f64 ModF64(f64 x, f64 m) +{ + return x - m * TruncF64(x / m); +} + +//////////////////////////////// +//~ Floating point sign + +f32 AbsF32(f32 f) +{ + u32 truncated = *(u32 *)&f & 0x7FFFFFFF; + return *(f32 *)&truncated; +} + +f64 AbsF64(f64 f) +{ + u64 truncated = *(u64 *)&f & 0x7FFFFFFFFFFFFFFFULL; + return *(f64 *)&truncated; +} + +u32 AbsI32(i32 v) +{ + return (u32)(v * ((v >= 0) - (v < 0))); +} + +u64 AbsI64(i64 v) +{ + return (u64)(v * ((v >= 0) - (v < 0))); +} + +i32 SignF32(f32 f) +{ + u32 sign_bit = (*(u32 *)&f >> 31) & 1; + return 1 + -((i32)(sign_bit << 1)); +} + +i64 SignF64(f64 f) +{ + u64 sign_bit = (*(u64 *)&f >> 63) & 1; + return 1 + -((i64)(sign_bit << 1)); +} + +//////////////////////////////// +//~ U64 pow + +/* Taken from https://gist.github.com/orlp/3551590 */ +u64 PowU64(u64 base, u8 exp) +{ + LocalPersist const u8 highest_bit_set[] = { + 0, 1, 2, 2, 3, 3, 3, 3, + 4, 4, 4, 4, 4, 4, 4, 4, + 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, + 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 255, /* Anything past 63 is a guaranteed overflow with base > 1 */ + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + }; + + u64 result = 1; + + switch (highest_bit_set[exp]) + { + case 255: + { + /* 255 = overflow, return 0 */ + if (base == 1) + { + return 1; + } + // if (base == -1) { + // return 1 - 2 * (exp & 1); + // } + return 0; + } break; + case 6: + { + if (exp & 1) result *= base; + exp >>= 1; + base *= base; + } FALLTHROUGH; + case 5: + { + if (exp & 1) result *= base; + exp >>= 1; + base *= base; + } FALLTHROUGH; + case 4: + { + if (exp & 1) result *= base; + exp >>= 1; + base *= base; + } FALLTHROUGH; + case 3: + { + if (exp & 1) result *= base; + exp >>= 1; + base *= base; + } FALLTHROUGH; + case 2: + { + if (exp & 1) result *= base; + exp >>= 1; + base *= base; + } FALLTHROUGH; + case 1: + { + if (exp & 1) result *= base; + } FALLTHROUGH; + default: return result; + } +} + +//////////////////////////////// +//~ Align up + +u64 AlignU64Pow2(u64 x) +{ + u64 result = 0; + if (x > 0) + { + result = x - 1; + result |= result >> 1; + result |= result >> 2; + result |= result >> 4; + result |= result >> 8; + result |= result >> 16; + result |= result >> 32; + ++result; + } + return result; +} + +//////////////////////////////// +//~ Logn + +/* Based on FreeBSD's implementation + * https://github.com/freebsd/freebsd-src/blob/main/lib/msun/src/e_logf.c */ +f32 LnF32(f32 x) +{ + LocalPersist const f32 ln2_hi = 6.9313812256e-01f; + LocalPersist const f32 ln2_lo = 9.0580006145e-06f; + + i32 x_int = *(i32 *)&x; + + i32 k = 0; + if (x_int < 0x00800000) + { + f32 two_p25 = 3.3554432000e+07; + if ((x_int & 0x7fffffff) == 0) + { + /* Return -inf if x is 0 */ + return -two_p25 / 0; + } + else if (x_int < 0) + { + /* Return NaN if x is negative */ + return (x - x) / 0; + } + k -= 25; + x *= two_p25; + x_int = *(i32 *)&x; + } + else if (x_int >= 0x7f800000) + { + return x + x; + } + + k += (x_int >> 23) - 127; + x_int &= 0x007fffff; + i32 i = (x_int + (0x95f64 << 3)) & 0x800000; + i32 x_int_normalized = x_int | (i ^ 0x3f800000); + x = *(f32 *)&x_int_normalized - 1.0f; + k += (i >> 23); + + if ((0x007fffff & (0x8000 + x_int)) < 0xc000) + { + if (x == 0) + { + if (k == 0) + { + return 0; + } + else + { + return (f32)k * ln2_hi + (f32)k * ln2_lo; + } + } + f32 r = x * x * (0.5f - 0.33333333333333333f * x); + if (k == 0) + { + return x - r; + } + else + { + return (f32)k * ln2_hi - ((r - (f32)k * ln2_lo) - x); + } + } + + f32 s = x / (2.0f + x); + f32 z = s * s; + f32 w = z * z; + f32 r = (z * (0.66666662693f + w * 0.28498786688f)) + (w * (0.40000972152f + w * 0.24279078841f)); + if (((x_int - (0x6147a << 3)) | ((0x6b851 << 3) - x_int)) > 0) + { + f32 hfsq = 0.5f * x * x; + if (k == 0) + { + return x - (hfsq - s * (hfsq + r)); + } + else + { + return (f32)k * ln2_hi - ((hfsq - (s * (hfsq + r) + (f32)k * ln2_lo)) - x); + } + } + else + { + if (k == 0) + { + return x - s * (x - r); + } + else + { + return (f32)k * ln2_hi - ((s * (x - r) - (f32)k * ln2_lo) - x); + } + } +} + +//////////////////////////////// +//~ Exp + +/* Based on FreeBSD's implementation + * https://github.com/freebsd/freebsd-src/blob/main/lib/msun/src/e_expf.c */ +f32 ExpF32(f32 x) +{ + LocalPersist const f32 half[2] = { 0.5, -0.5 }; + LocalPersist const f32 o_threshold = 8.8721679688e+01f; + LocalPersist const f32 u_threshold = -1.0397208405e+02f; + LocalPersist const f32 ln2_hi[2] = { 6.9314575195e-01f, -6.9314575195e-01f }; + LocalPersist const f32 ln2_lo[2] = { 1.4286067653e-06f, -1.4286067653e-06f }; + LocalPersist const f32 inv_ln2 = 1.4426950216e+00f; + LocalPersist const f32 huge = 1.0e+30f; + LocalPersist const f32 two_m100 = 7.8886090522e-31f; + + u32 x_uint = *(u32 *)&x; + i32 x_sign_bit = (i32)((x_uint >> 31) & 1); + x_uint &= 0x7fffffff; + + /* Filter out non-finite argument */ + if (x_uint >= 0x42b17218) + { /* if |x|>=88.721... */ + if (x_uint > 0x7f800000) + { + return x + x; /* NaN */ + } + else if (x_uint == 0x7f800000) + { + return (x_sign_bit == 0) ? x : 0; + } + if (x > o_threshold) + { + /* Overflow */ + return F32Infinity; + } + else if (x < u_threshold) + { + /* Underflow */ + return two_m100 * two_m100; + } + } + + /* Argument reduction */ + i32 k = 0; + f32 hi = 0; + f32 lo = 0; + if (x_uint > 0x3eb17218) + { + if (x_uint < 0x3F851592) + { + hi = x - ln2_hi[x_sign_bit]; + lo = ln2_lo[x_sign_bit]; + k = 1 - x_sign_bit - x_sign_bit; + } + else + { + k = (i32)(inv_ln2 * x + half[x_sign_bit]); + hi = x - (f32)k * ln2_hi[0]; + lo = (f32)k * ln2_lo[0]; + } + x = hi - lo; + } + else if (x_uint < 0x39000000) + { + if (huge + x > 1.0f) + { + return 1.0f + x; + } + } + else + { + k = 0; + } + + f32 two_pk; + if (k >= -125) + { + u32 temp = ((u32)(0x7f + k)) << 23; + two_pk = *(f32 *)&temp; + } + else + { + u32 temp = ((u32)(0x7f + (k + 100))) << 23; + two_pk = *(f32 *)&temp; + } + + f32 t = x * x; + f32 c = x - t * (1.6666625440e-1f + t * -2.7667332906e-3f); + if (k == 0) + { + return 1.0f - ((x * c) / (c - 2.0f) - x); + } + else + { + f32 y = 1.0f - ((lo - (x * c) / (2.0f - c)) - hi); + if (k >= -125) + { + if (k == 128) + { + u32 temp = 0x7f800000; + return y * 2.0f * (*(f32 *)&temp); + } + return y * two_pk; + } + else + { + return y * two_pk * two_m100; + } + } +} + +//////////////////////////////// +//~ Pow + +f32 PowF32(f32 a, f32 b) +{ + if (a >= 0) + { + /* a is positive */ + return ExpF32(LnF32(a) * b); + } + else + { + /* a is negative */ + f32 res_sign = RoundF32ToI32(b) % 2 == 0 ? 1 : -1; + return ExpF32(LnF32(-a) * b) * res_sign; + } +} + +//////////////////////////////// +//~ Sqrt + +f32 SqrtF32(f32 x) +{ + return IxSqrtF32(x); +} + +f64 SqrtF64(f64 x) +{ + return IxSqrtF64(x); +} + +f32 RSqrtF32(f32 x) +{ + return IxRsqrtF32(x); +} + +//////////////////////////////// +//~ Trig + +/* Functions based on Cephes implementation (https://www.netlib.org/cephes/): + * - SinApproxF32 + * - CosApproxF32 + * - ReduceToPio4 + * - ArcTanF32 + */ + +//- Reduce +/* Reduce postive x to range [0, Pi/4] (Cody-Waite argument reduction). + * Returns 0 if x > (2^24 - 1); + * Sets octant_out=-1 on error. */ +f32 ReduceToPio4(f32 x, i32 *octant_out) +{ + i32 octant = -1; + + if (x <= ((1 << 24) - 1)) + { + octant = x * (4 / Pi); /* Integer part of x/(Pi/4) */ + f32 y = octant; + if (octant & 1) + { + octant += 1; + y += 1.0; + } + + /* Modulo 360 degrees */ + octant &= 7; + + if (x > 8192) + { + x = x - y * (Pi / 4); + } + else + { + /* Extended precision modular arithmetic */ + x = ((x - y * 0.78515625) - y * 2.4187564849853515625e-4) - y * 3.77489497744594108e-8; + } + } + + *octant_out = octant; + return x; +} + +//- Sin approximation +/* Approximate sin in range [0, Pi/4] */ +f32 SinApproxF32(f32 x) +{ + f32 x_sq = x * x; + return (((-1.9515295891E-4 * x_sq + 8.3321608736E-3) * x_sq - 1.6666654611E-1) * x_sq * x) + x; +} + +//- Cos approximation +f32 CosApproxF32(f32 x) +{ + f32 x_sq = x * x; + return (((2.443315711809948E-005 * x_sq - 1.388731625493765E-003) * x_sq + 4.166664568298827E-002) * x_sq * x_sq) - (x_sq * 0.5) + 1; +} + +//- Sin +f32 SinF32(f32 x) +{ + f32 sign = 1; + + if (IsF32Nan(x)) + { + return 0; + } + else if (x < 0) + { + sign = -1; + x = -x; + } + + i32 octant; + x = ReduceToPio4(x, &octant); + + /* Reflect in x axis */ + if (octant > 3) + { + sign = -sign; + octant -= 4; + } + + switch (octant) + { + case -1: return 0; + case 1: case 2: return CosApproxF32(x) * sign; + default: return SinApproxF32(x) * sign; + } +} + +//- Cos +f32 CosF32(f32 x) +{ + f32 sign = 1; + + if (IsF32Nan(x)) + { + return 0; + } + else if (x < 0) + { + x = -x; + } + + i32 octant; + x = ReduceToPio4(x, &octant); + + /* Reflect in x axis */ + if (octant > 3) + { + sign = -sign; + octant -= 4; + } + + if (octant > 1) + { + sign = -sign; + } + + switch (octant) + { + case -1: return 0; + case 1: case 2: return SinApproxF32(x) * sign; + default: return CosApproxF32(x) * sign; + } +} + +//- ArcTan +f32 ArcTanF32(f32 x) +{ + f32 sign = 1; + if (x < 0) + { + sign = -1; + x = -x; + } + + /* Reduce range */ + f32 y; + if (x > 2.414213562373095) + { /* tan((Pi / 8) * 3) */ + y = Pi / 2; + x = -(1.f / x); + } + else if (x > 0.4142135623730950) + { /* tan(Pi / 8) */ + y = Pi / 4; + x = (x - 1.f) / (x + 1.f); + } + else + { + y = 0; + } + + f32 x_sq = x * x; + y += ((((8.05374449538e-2 * x_sq - 1.38776856032E-1) * x_sq + 1.99777106478E-1) * x_sq - 3.33329491539E-1) * x_sq * x + x); + + return y * sign; +} + +//- ArcTan2 +f32 ArcTan2F32(f32 y, f32 x) +{ + f32 result; + if (x == 0) + { + if (y < 0) + { + result = 3 * Pi / 2; + } + else if (y == 0) + { + result = 0; + } + else + { + result = Pi / 2; + } + } + else if (y == 0) + { + if (x < 0) + { + result = Pi; + } + else + { + result = 0; + } + } + else + { + f32 offset; + if (x < 0) + { + offset = Pi; + } + else if (y < 0) + { + offset = Pi * 2; + } + else + { + offset = 0; + } + result = ArcTanF32(y / x) + offset; + } + return result; +} + +//- ArcSin +f32 ArcSinF32(f32 x) +{ + /* TODO: Dedicated arcsin approximation */ + return ArcTan2F32(x, SqrtF32(1.0f - (x * x))); +} + +//- ArcCos +f32 ArcCosF32(f32 x) +{ + /* TODO: Dedicated arccos approximation */ + return (Pi / 2.0f) - ArcTan2F32(x, SqrtF32(1.0f - (x * x))); +} + +//////////////////////////////// +//~ Angle unwind + +/* Returns angle in range [-Pi, Pi] */ +f32 UnwindAngleF32(f32 a) +{ + f32 d = ModF32(a, Tau); + return ModF32(2.0f * d, Tau) - d; +} + +//////////////////////////////// +//~ Float lerp + +f32 LerpF32(f32 val0, f32 val1, f32 t) +{ + return val0 + ((val1 - val0) * t); +} + +f64 LerpF64(f64 val0, f64 val1, f64 t) +{ + return val0 + ((val1 - val0) * t); +} + +f32 LerpAngleF32(f32 a, f32 b, f32 t) +{ + f32 diff = UnwindAngleF32(b - a); + return a + diff * t; +} + +//////////////////////////////// +//~ Int lerp + +i32 LerpI32(i32 val0, i32 val1, f32 t) +{ + return val0 + RoundF32ToI32(((f32)(val1 - val0) * t)); +} + + +//- Lerp i64 +i64 LerpI64(i64 val0, i64 val1, f64 t) +{ + return val0 + RoundF64ToI64(((f64)(val1 - val0) * t)); +} + +//////////////////////////////// +//~ Vec2 operations + +b32 IsVec2Zero(Vec2 a) +{ + return a.x == 0 && a.y == 0; +} + +b32 EqVec2(Vec2 a, Vec2 b) +{ + return a.x == b.x && a.y == b.y; +} + +//- Mul + +Vec2 MulVec2(Vec2 a, f32 s) +{ + return VEC2(a.x * s, a.y * s); +} + +Vec2 MulVec2Vec2(Vec2 a, Vec2 b) +{ + return VEC2(a.x * b.x, a.y * b.y); +} + +Vec2 NegVec2(Vec2 a) +{ + return VEC2(-a.x, -a.y); +} + +//- Div + +Vec2 DivVec2(Vec2 a, f32 s) +{ + f32 d = 1 / s; + return VEC2(a.x * d, a.y * d); +} + +Vec2 DivVec2Vec2(Vec2 a, Vec2 b) +{ + return VEC2(a.x * (1 / b.x), a.y * (1 / b.y)); +} + +//- Add + +Vec2 AddVec2(Vec2 a, Vec2 b) +{ + return VEC2(a.x + b.x, a.y + b.y); +} + +Vec2 SubVec2(Vec2 a, Vec2 b) +{ + return VEC2(a.x - b.x, a.y - b.y); +} + +//- Len + +f32 Vec2Len(Vec2 a) +{ + return SqrtF32(a.x * a.x + a.y * a.y); +} + +f32 Vec2LenSq(Vec2 a) +{ + return a.x * a.x + a.y * a.y; +} + +Vec2 Vec2WithLen(Vec2 a, f32 len) +{ + f32 l_sq = a.x * a.x + a.y * a.y; + if (l_sq != 0) + { + f32 denom = len / SqrtF32(l_sq); + a.x *= denom; + a.y *= denom; + } + return a; +} + +Vec2 ClampVec2Len(Vec2 a, f32 max) +{ + f32 l_sq = a.x * a.x + a.y * a.y; + if (l_sq > max * max) + { + f32 denom = max / SqrtF32(l_sq); + a.x *= denom; + a.y *= denom; + } + return a; +} + +f32 Vec2Distance(Vec2 a, Vec2 b) +{ + f32 dx = b.x - a.x; + f32 dy = b.y - a.y; + return SqrtF32(dx * dx + dy * dy); +} + +Vec2 NormVec2(Vec2 a) +{ + return Vec2WithLen(a, 1.f); +} + + +//- Dot + +f32 DotVec2(Vec2 a, Vec2 b) +{ + return a.x * b.x + a.y * b.y; +} + +/* Returns signed area between vectors (positive in clockwise direction) + * AKA perpendicular dot product + * AKA 2d cross-product */ +f32 WedgeVec2(Vec2 a, Vec2 b) +{ + return a.x * b.y - a.y * b.x; +} + +/* Clockwise (right) perpendicular vector */ +Vec2 PerpVec2(Vec2 a) +{ + return VEC2(-a.y, a.x); +} + +/* Clockwise (right) perpendicular vector scaled by s */ +Vec2 MulPerpVec2(Vec2 a, f32 s) +{ + return VEC2(s * -a.y, s * a.x); +} + +Vec2 PerpVec2TowardsDir(Vec2 v, Vec2 dir) +{ + f32 wedge = WedgeVec2(v, dir); + return MulPerpVec2(v, (wedge >= 0) - (wedge < 0)); +} + +//- Round / floor / ceil + +Vec2 RoundVec2(Vec2 a) +{ + return VEC2(RoundF32(a.x), RoundF32(a.y)); +} + +Vec2I32 RoundVec2ToVec2I32(Vec2 a) +{ + return VEC2I32(RoundF32ToI32(a.x), RoundF32ToI32(a.y)); +} + +Vec2 FloorVec2(Vec2 a) +{ + return VEC2(FloorF32(a.x), FloorF32(a.y)); +} + +Vec2 CeilVec2(Vec2 a) +{ + return VEC2(CeilF32(a.x), CeilF32(a.y)); +} + +//- Angle + +/* Returns 1 if winding between vectors a & b is clockwise or straight, -1 if counter-clockwise */ +i32 WindingFromVec2(Vec2 a, Vec2 b) +{ + f32 w = WedgeVec2(a, b); + return (w >= 0) - (w < 0); +} + +Vec2 RotateVec2(Vec2 v, f32 a) +{ + f32 c = CosF32(a); + f32 s = SinF32(a); + return VEC2(v.x * c - v.y * s, v.x * s + v.y * c); +} + +Vec2 Vec2FromAngle(f32 a) +{ + return VEC2(CosF32(a), SinF32(a)); +} + +f32 AngleFromVec2(Vec2 v) +{ + return ArcTan2F32(v.y, v.x); +} + +f32 AngleFromVec2Dirs(Vec2 dir1, Vec2 dir2) +{ + return ArcTan2F32(WedgeVec2(dir1, dir2), DotVec2(dir1, dir2)); +} + +f32 AngleFromVec2Points(Vec2 pt1, Vec2 pt2) +{ + return AngleFromVec2(SubVec2(pt2, pt1)); +} + +//- Closest point + +Vec2 ClosestPointFromRay(Vec2 ray_pos, Vec2 ray_dir_norm, Vec2 p) +{ + Vec2 ray_p_dir = SubVec2(p, ray_pos); + f32 dot = DotVec2(ray_dir_norm, ray_p_dir); + Vec2 ray_dir_closest = MulVec2(ray_dir_norm, dot); + return AddVec2(ray_pos, ray_dir_closest); +} + +//- Lerp + +/* Interpolate position vectors */ +Vec2 LerpVec2(Vec2 val0, Vec2 val1, f32 t) +{ + return VEC2(LerpF32(val0.x, val1.x, t), LerpF32(val0.y, val1.y, t)); +} + +/* Interpolate direction vectors (spherical lerp) */ +Vec2 SlerpVec2(Vec2 val0, Vec2 val1, f32 t) +{ + f32 rot = LerpAngleF32(AngleFromVec2(val0), AngleFromVec2(val1), t); + f32 len = LerpF32(Vec2Len(val0), Vec2Len(val1), t); + return MulVec2(Vec2FromAngle(rot), len); +} + +//////////////////////////////// +//~ Vec2I32 Operations + +b32 EqVec2I32(Vec2I32 a, Vec2I32 b) +{ + return a.x == b.x && a.y == b.y; +} + +Vec2I32 NegVec2I32(Vec2I32 a) +{ + return VEC2I32(-a.x, -a.y); +} + +Vec2I32 AddVec2I32(Vec2I32 a, Vec2I32 b) +{ + return VEC2I32(a.x + b.x, a.y + b.y); +} + +Vec2I32 SubVec2I32(Vec2I32 a, Vec2I32 b) +{ + return VEC2I32(a.x - b.x, a.y - b.y); +} + +//////////////////////////////// +//~ Xform operations + +b32 EqXform(Xform xf1, Xform xf2) +{ + return EqVec2(xf1.og, xf2.og) && EqVec2(xf1.bx, xf2.bx) && EqVec2(xf1.by, xf2.by); +} + +//- Initialization +Xform XformFromPos(Vec2 v) +{ + Xform xf; + xf.bx = VEC2(1, 0); + xf.by = VEC2(0, 1); + xf.og = v; + return xf; +} + +Xform XformFromRot(f32 r) +{ + Xform result; + f32 c = CosF32(r); + f32 s = SinF32(r); + result.bx = VEC2(c, s); + result.by = VEC2(-s, c); + result.og = VEC2(0, 0); + return result; +} + +Xform XformFromScale(Vec2 scale) +{ + Xform result; + result.bx = VEC2(scale.x, 0); + result.by = VEC2(0, scale.y); + result.og = VEC2(0, 0); + return result; +} + +Xform XformFromTrs(Trs trs) +{ + Xform xf = XformFromPos(trs.t); + xf = RotateXform(xf, trs.r); + xf = ScaleXform(xf, trs.s); + return xf; +} + +Xform XformFromRect(Rect rect) +{ + Vec2 half_size = MulVec2(rect.size, 0.5); + Xform xf = ZI; + xf.bx = VEC2(rect.size.x, 0); + xf.by = VEC2(0, rect.size.y); + xf.og = AddVec2(rect.pos, half_size); + return xf; +} + +//- Translation +Xform TranslateXform(Xform xf, Vec2 v) +{ + xf.og = VEC2(xf.bx.x * v.x + xf.by.x * v.y + xf.og.x, xf.bx.y * v.x + xf.by.y * v.y + xf.og.y); + return xf; +} + +Xform WorldTranslateXform(Xform xf, Vec2 v) +{ + xf.og = AddVec2(xf.og, v); + return xf; +} + +//- Rotation +Xform RotateXform(Xform xf, f32 r) +{ + return MulXform(xf, XformFromRot(r)); +} + +Xform WorldRotateXform(Xform xf, f32 r) +{ + return MulXform(XformFromRot(r), xf); +} + +Xform WorldRotateXformBasis(Xform xf, f32 r) +{ + f32 diff = r; + f32 c = CosF32(diff); + f32 s = SinF32(diff); + xf.bx = VEC2(xf.bx.x * c - xf.bx.y * s, xf.bx.x * s + xf.bx.y * c); + xf.by = VEC2(xf.by.x * c - xf.by.y * s, xf.by.x * s + xf.by.y * c); + return xf; +} + +Xform XformWIthWorldRotation(Xform xf, f32 r) +{ + return WorldRotateXformBasis(xf, r - RotationFromXform(xf)); +} + +//- Scale +Xform ScaleXform(Xform xf, Vec2 scale) +{ + xf.bx = MulVec2(xf.bx, scale.x); + xf.by = MulVec2(xf.by, scale.y); + return xf; +} + +Xform WorldScaleXform(Xform xf, Vec2 scale) +{ + Xform result; + result.bx = MulVec2Vec2(xf.bx, scale); + result.by = MulVec2Vec2(xf.by, scale); + result.og = MulVec2Vec2(xf.og, scale); + return result; +} + +//- Lerp +Xform LerpXform(Xform a, Xform b, f32 t) +{ + Xform result; + result.bx = SlerpVec2(a.bx, b.bx, t); + result.by = SlerpVec2(a.by, b.by, t); + result.og = LerpVec2(a.og, b.og, t); + return result; +} + +//- Invert +Xform InvertXform(Xform xf) +{ + f32 det = DeterminantFromXform(xf); + f32 inv_det = 1.0f / det; + + f32 old_bx_x = xf.bx.x; + xf.bx.x = xf.by.y; + xf.by.y = old_bx_x; + + xf.bx = MulVec2Vec2(xf.bx, VEC2(inv_det, -inv_det)); + xf.by = MulVec2Vec2(xf.by, VEC2(-inv_det, inv_det)); + + xf.og = MulXformBasisV2(xf, NegVec2(xf.og)); + + return xf; +} + +//- Mul +Xform MulXform(Xform a, Xform b) +{ + Xform result; + result.bx.x = a.bx.x * b.bx.x + a.by.x * b.bx.y; + result.bx.y = a.bx.y * b.bx.x + a.by.y * b.bx.y; + result.by.x = a.bx.x * b.by.x + a.by.x * b.by.y; + result.by.y = a.bx.y * b.by.x + a.by.y * b.by.y; + result.og = MulXformV2(a, b.og); + return result; +} + +Vec2 MulXformBasisV2(Xform xf, Vec2 v) +{ + return VEC2( + xf.bx.x * v.x + xf.by.x * v.y, + xf.bx.y * v.x + xf.by.y * v.y + ); +} + +Vec2 MulXformV2(Xform xf, Vec2 v) +{ + Vec2 result = MulXformBasisV2(xf, v); + result = AddVec2(result, xf.og); + return result; +} + + +Quad MulXformQuad(Xform xf, Quad quad) +{ + Quad result; + result.p0 = MulXformV2(xf, quad.p0); + result.p1 = MulXformV2(xf, quad.p1); + result.p2 = MulXformV2(xf, quad.p2); + result.p3 = MulXformV2(xf, quad.p3); + return result; +} + +Vec2 InvertXformBasisMulV2(Xform xf, Vec2 v) +{ + Xform inv = InvertXform(xf); + Vec2 result = MulXformBasisV2(inv, v); + return result; +} + +/* TODO: Get rid of this? Just force caller to use invert manually since it's expensive. */ +Vec2 InvertXformMulV2(Xform xf, Vec2 v) +{ + Xform inv = InvertXform(xf); + return MulXformV2(inv, v); +} + +//- Helpers +Xform BasisFromXform(Xform xf) +{ + Xform result = ZI; + result.bx = xf.bx; + result.by = xf.by; + return result; +} + +f32 DeterminantFromXform(Xform xf) +{ + return WedgeVec2(xf.bx, xf.by); +} + +Vec2 RightFromXform(Xform xf) +{ + return xf.bx; +} + +Vec2 LeftFromXform(Xform xf) +{ + return NegVec2(xf.bx); +} + +Vec2 UpFromXform(Xform xf) +{ + return NegVec2(xf.by); +} + +Vec2 DownFromXform(Xform xf) +{ + return xf.by; +} + +f32 RotationFromXform(Xform xf) +{ + return AngleFromVec2(xf.bx); +} + +Vec2 ScaleFromXform(Xform xf) +{ + f32 det_sign = SignF32(DeterminantFromXform(xf)); + return VEC2(Vec2Len(xf.bx), det_sign * Vec2Len(xf.by)); +} + +//////////////////////////////// +//~ Quad operations + +Quad QuadFromRect(Rect rect) +{ + Quad result; + result.p0 = VEC2(rect.x, rect.y); /* Top left */ + result.p1 = VEC2(rect.x + rect.width, rect.y); /* Top right */ + result.p2 = VEC2(rect.x + rect.width, rect.y + rect.height); /* Bottom right */ + result.p3 = VEC2(rect.x, rect.y + rect.height); /* Bottom left */ + return result; +} + +Quad QuadFromAabb(Aabb aabb) +{ + Quad result; + result.p0 = VEC2(aabb.p0.x, aabb.p0.y); /* Top left */ + result.p1 = VEC2(aabb.p1.x, aabb.p0.y); /* Top right */ + result.p2 = VEC2(aabb.p1.x, aabb.p1.y); /* Bottom right */ + result.p3 = VEC2(aabb.p0.x, aabb.p1.y); /* Bottom left */ + return result; +} + +Quad QuadFromLine(Vec2 start, Vec2 end, f32 thickness) +{ + f32 width = thickness / 2.f; + + Vec2 dir = NormVec2(SubVec2(end, start)); + Vec2 dir_perp = PerpVec2(dir); + + Vec2 left = MulVec2(dir_perp, -width); + Vec2 right = MulVec2(dir_perp, width); + + Quad result; + result.p0 = AddVec2(start, left); + result.p1 = AddVec2(start, right); + result.p2 = AddVec2(end, right); + result.p3 = AddVec2(end, left); + return result; +} + +Quad QuadFromRay(Vec2 pos, Vec2 rel, f32 thickness) +{ + Vec2 end = AddVec2(pos, rel); + return QuadFromLine(pos, end, thickness); +} + +Quad ScaleQuad(Quad q, f32 s) +{ + q.p0 = MulVec2(q.p0, s); + q.p1 = MulVec2(q.p1, s); + q.p2 = MulVec2(q.p2, s); + q.p3 = MulVec2(q.p3, s); + return q; +} + +Quad RoundQuad(Quad quad) +{ + Quad result; + result.p0 = RoundVec2(quad.p0); + result.p1 = RoundVec2(quad.p1); + result.p2 = RoundVec2(quad.p2); + result.p3 = RoundVec2(quad.p3); + return result; +} + +Quad FloorQuad(Quad quad) +{ + Quad result; + result.p0 = FloorVec2(quad.p0); + result.p1 = FloorVec2(quad.p1); + result.p2 = FloorVec2(quad.p2); + result.p3 = FloorVec2(quad.p3); + return result; +} + +//////////////////////////////// +//~ Spring operations + +/* https://box2d.org/files/ErinCatto_SoftConstraints_GDC2011.pdf */ +SoftSpring MakeSpring(f32 hertz, f32 damping_ratio, f32 dt) +{ + SoftSpring result; + if (hertz == 0.0f) + { + result.bias_rate = 0; + result.mass_scale = 1; + result.impulse_scale = 0; + } + else + { + f32 angular_frequency = Tau * hertz; + f32 a = 2 * damping_ratio + angular_frequency * dt; + f32 b = angular_frequency * a * dt; + f32 c = 1 / (b + 1); + result.bias_rate = angular_frequency / a; + result.mass_scale = b * c; + result.impulse_scale = c; + } + return result; +} + +//////////////////////////////// +//~ Mat4x4 operations + +Mat4x4 Mat4x4FromXform(Xform xf) +{ + return CppCompatInitListType(Mat4x4) + { + .e = { + {xf.bx.x, xf.bx.y, 0, 0}, + {xf.by.x, xf.by.y, 0, 0}, + {0, 0, 1, 0}, + {xf.og.x, xf.og.y, 0, 1}, + } + }; +} + +Mat4x4 Mat4x4FromOrtho(f32 left, f32 right, f32 bottom, f32 top, f32 near_z, f32 far_z) +{ + Mat4x4 m = ZI; + + f32 rl = 1.0f / (right - left); + f32 tb = 1.0f / (top - bottom); + f32 fn = -1.0f / (far_z - near_z); + + m.e[0][0] = 2.0f * rl; + m.e[1][1] = 2.0f * tb; + m.e[2][2] = 2.0f * fn; + m.e[3][0] = -(right + left) * rl; + m.e[3][1] = -(top + bottom) * tb; + m.e[3][2] = (far_z + near_z) * fn; + m.e[3][3] = 1.0f; + + return m; +} + +Mat4x4 MulMat4x4(Mat4x4 m1, Mat4x4 m2) +{ + f32 a00 = m1.e[0][0], a01 = m1.e[0][1], a02 = m1.e[0][2], a03 = m1.e[0][3], + a10 = m1.e[1][0], a11 = m1.e[1][1], a12 = m1.e[1][2], a13 = m1.e[1][3], + a20 = m1.e[2][0], a21 = m1.e[2][1], a22 = m1.e[2][2], a23 = m1.e[2][3], + a30 = m1.e[3][0], a31 = m1.e[3][1], a32 = m1.e[3][2], a33 = m1.e[3][3], + + b00 = m2.e[0][0], b01 = m2.e[0][1], b02 = m2.e[0][2], b03 = m2.e[0][3], + b10 = m2.e[1][0], b11 = m2.e[1][1], b12 = m2.e[1][2], b13 = m2.e[1][3], + b20 = m2.e[2][0], b21 = m2.e[2][1], b22 = m2.e[2][2], b23 = m2.e[2][3], + b30 = m2.e[3][0], b31 = m2.e[3][1], b32 = m2.e[3][2], b33 = m2.e[3][3]; + + Mat4x4 result; + result.e[0][0] = a00 * b00 + a10 * b01 + a20 * b02 + a30 * b03; + result.e[0][1] = a01 * b00 + a11 * b01 + a21 * b02 + a31 * b03; + result.e[0][2] = a02 * b00 + a12 * b01 + a22 * b02 + a32 * b03; + result.e[0][3] = a03 * b00 + a13 * b01 + a23 * b02 + a33 * b03; + result.e[1][0] = a00 * b10 + a10 * b11 + a20 * b12 + a30 * b13; + result.e[1][1] = a01 * b10 + a11 * b11 + a21 * b12 + a31 * b13; + result.e[1][2] = a02 * b10 + a12 * b11 + a22 * b12 + a32 * b13; + result.e[1][3] = a03 * b10 + a13 * b11 + a23 * b12 + a33 * b13; + result.e[2][0] = a00 * b20 + a10 * b21 + a20 * b22 + a30 * b23; + result.e[2][1] = a01 * b20 + a11 * b21 + a21 * b22 + a31 * b23; + result.e[2][2] = a02 * b20 + a12 * b21 + a22 * b22 + a32 * b23; + result.e[2][3] = a03 * b20 + a13 * b21 + a23 * b22 + a33 * b23; + result.e[3][0] = a00 * b30 + a10 * b31 + a20 * b32 + a30 * b33; + result.e[3][1] = a01 * b30 + a11 * b31 + a21 * b32 + a31 * b33; + result.e[3][2] = a02 * b30 + a12 * b31 + a22 * b32 + a32 * b33; + result.e[3][3] = a03 * b30 + a13 * b31 + a23 * b32 + a33 * b33; + + return result; +} + +Mat4x4 ProjectMat4x4View(Xform view, f32 viewport_width, f32 viewport_height) +{ + Mat4x4 projection = Mat4x4FromOrtho(0.0, viewport_width, viewport_height, 0.0, -1.0, 1.0); + Mat4x4 view4x4 = Mat4x4FromXform(view); + return MulMat4x4(projection, view4x4); +} diff --git a/src/meta/meta_math.h b/src/meta/meta_math.h new file mode 100644 index 00000000..cca831f1 --- /dev/null +++ b/src/meta/meta_math.h @@ -0,0 +1,413 @@ +/* Math functions are default 32 bit (f32, i32, etc) unless specified */ + +#define Pi ((f32)3.14159265358979323846) +#define Tau ((f32)6.28318530717958647693) +#define GoldenRatio ((f32)1.61803398874989484820) + +//////////////////////////////// +//~ Floating point vector2 types + +Struct(Vec2) { + f32 x, y; +}; +#define VEC2(x, y) CppCompatInitListType(Vec2) { (x), (y) } + +Struct(Vec2Array) { + Vec2 *points; + u64 count; +}; + +//////////////////////////////// +//~ Floating point vector3 types + +Struct(Vec3) { + f32 x, y, z; +}; +#define VEC3(x, y, z) CppCompatInitListType(Vec3) { (x), (y), (z) } + +Struct(Vec3Array) { + Vec3 *points; + u64 count; +}; + +//////////////////////////////// +//~ Floating point vector4 types + +Struct(Vec4) { + f32 x, y, z, w; +}; +#define VEC4(x, y, z, w) ((Vec4) { (x), (y), (z), (w) }) + +Struct(Vec4Array) { + Vec4 *points; + u64 count; +}; + +//////////////////////////////// +//~ Integer vec2 types + +Struct(Vec2I32) { + i32 x, y; +}; +#define VEC2I32(x, y) CppCompatInitListType(Vec2I32) { (x), (y) } + +//////////////////////////////// +//~ Integer vector3 types + +Struct(Vec3I32) { + i32 x, y, z; +}; +#define VEC3I32(x, y, z) CppCompatInitListType(Vec3I32) { (x), (y), (z) } + +//////////////////////////////// +//~ Integer vector4 types + +Struct(Vec4I32) +{ + i32 x, y, z, w; +}; +#define VEC4I32(x, y, z, w) CppCompatInitListType(Vec4I32) { (x), (y), (z), (w) } + +//////////////////////////////// +//~ Xform types + +Struct(Xform) +{ + Vec2 bx; /* X basis vector (x axis) */ + Vec2 by; /* Y basis vector (y axis)*/ + Vec2 og; /* Translation vector (origin) */ +}; + +/* (T)ranslation, (R)otation, (S)cale */ +Struct(Trs) +{ + Vec2 t; + Vec2 s; + f32 r; +}; + +//////////////////////////////// +//~ Rect types + +Struct(Rect) { + union { + struct { f32 x, y, width, height; }; + struct { Vec2 pos, size; }; + }; +}; + + +/* Values expected to be normalized 0.0 -> 1.0 */ +Struct(ClipRect) +{ + Vec2 p0, p1; +}; + +//////////////////////////////// +//~ Axis aligned bounding box types + +Struct(Aabb) { + Vec2 p0, p1; +}; + +//////////////////////////////// +//~ Quad types + +Struct(Quad) { + union { + struct { Vec2 p0, p1, p2, p3; }; + struct { Vec2 e[4]; }; + }; +}; + +//////////////////////////////// +//~ Spring types + +Struct(SoftSpring) +{ + f32 bias_rate; + f32 mass_scale; + f32 impulse_scale; +}; + +//////////////////////////////// +//~ Mat4x4 types + +Struct(Mat4x4) +{ + union + { + struct { Vec4 bx, by, bz, bw; }; + f32 e[4][4]; + }; +}; + +//////////////////////////////// +//~ Min / max + +//- Min +u8 MinU8(u8 a, u8 b); +i8 MinI8(i8 a, i8 b); +u32 MinU32(u32 a, u32 b); +i32 MinI32(i32 a, i32 b); +f32 MinF32(f32 a, f32 b); +u64 MinU64(u64 a, u64 b); +i64 MinI64(i64 a, i64 b); +f64 MinF64(f64 a, f64 b); + +//- Max +u8 MaxU8(u8 a, u8 b); +i8 MaxI8(i8 a, i8 b); +u32 MaxU32(u32 a, u32 b); +i32 MaxI32(i32 a, i32 b); +f32 MaxF32(f32 a, f32 b); +u64 MaxU64(u64 a, u64 b); +i64 MaxI64(i64 a, i64 b); +f64 MaxF64(f64 a, f64 b); + +//- Clamp +u32 ClampU32(u32 v, u32 min, u32 max); +i32 ClampI32(i32 v, i32 min, i32 max); +f32 ClampF32(f32 v, f32 min, f32 max); +u64 ClampU64(u64 v, u64 min, u64 max); +i64 ClampI64(i64 v, i64 min, i64 max); +f64 ClampF64(f64 v, f64 min, f64 max); + +//////////////////////////////// +//~ Rounding ops + +//- Round +f32 RoundF32(f32 f); +f64 RoundF64(f64 f); +i32 RoundF32ToI32(f32 f); +i64 RoundF64ToI64(f64 f); + +//- Floor +f32 FloorF32(f32 f); +f64 FloorF64(f64 f); +i32 FloorF32ToI32(f32 f); +i64 FloorF64ToI64(f64 f); + +//- Ceil +f32 CeilF32(f32 f); +f64 CeilF64(f64 f); +i32 CeilF32ToI32(f32 f); +i64 CeilF64ToI64(f64 f); + +//- Trunc +f32 TruncF32(f32 f); +f64 TruncF64(f64 f); + +//////////////////////////////// +//~ Fmod + +f32 ModF32(f32 x, f32 m); +f64 ModF64(f64 x, f64 m); + +//////////////////////////////// +//~ Abs + +f32 AbsF32(f32 f); +f64 AbsF64(f64 f); +u32 AbsI32(i32 v); +u64 AbsI64(i64 v); +i32 SignF32(f32 f); +i64 SignF64(f64 f); + +//////////////////////////////// +//~ Exponential ops + +u64 PowU64(u64 base, u8 exp); +u64 AlignU64Pow2(u64 x); +f32 LnF32(f32 x); +f32 ExpF32(f32 x); +f32 PowF32(f32 a, f32 b); +f32 SqrtF32(f32 x); +f64 SqrtF64(f64 x); +f32 RSqrtF32(f32 x); + +//////////////////////////////// +//~ Trig + +f32 ReduceToPio4(f32 x, i32 *octant_out); +f32 SinApproxF32(f32 x); +f32 CosApproxF32(f32 x); +f32 SinF32(f32 x); +f32 CosF32(f32 x); +f32 ArcTanF32(f32 x); +f32 ArcTan2F32(f32 y, f32 x); +f32 ArcSinF32(f32 x); +f32 ArcCosF32(f32 x); + +//////////////////////////////// +//~ Angle unwind + +/* Returns angle in range [-Pi, Pi] */ +f32 UnwindAngleF32(f32 a); + +//////////////////////////////// +//~ Float lerp + +f32 LerpF32(f32 val0, f32 val1, f32 t); +f64 LerpF64(f64 val0, f64 val1, f64 t); +f32 LerpAngleF32(f32 a, f32 b, f32 t); + +//////////////////////////////// +//~ Int lerp + +i32 LerpI32(i32 val0, i32 val1, f32 t); +i64 LerpI64(i64 val0, i64 val1, f64 t); + +//////////////////////////////// +//~ Vec2 operations + +#define Vec2FromVec2I32(v) VEC2((v).x, (v).y) + +b32 IsVec2Zero(Vec2 a); +b32 EqVec2(Vec2 a, Vec2 b); + +//- Mul +Vec2 MulVec2(Vec2 a, f32 s); +Vec2 MulVec2Vec2(Vec2 a, Vec2 b); +Vec2 NegVec2(Vec2 a); + +//- Div +Vec2 DivVec2(Vec2 a, f32 s); +Vec2 DivVec2Vec2(Vec2 a, Vec2 b); + +//- Add +Vec2 AddVec2(Vec2 a, Vec2 b); +Vec2 SubVec2(Vec2 a, Vec2 b); + +//- Len +f32 Vec2Len(Vec2 a); +f32 Vec2LenSq(Vec2 a); +Vec2 Vec2WithLen(Vec2 a, f32 len); +Vec2 ClampVec2Len(Vec2 a, f32 max); +f32 Vec2Distance(Vec2 a, Vec2 b); +Vec2 NormVec2(Vec2 a); + +//- Dot +f32 DotVec2(Vec2 a, Vec2 b); +f32 WedgeVec2(Vec2 a, Vec2 b); +Vec2 PerpVec2(Vec2 a); +Vec2 MulPerpVec2(Vec2 a, f32 s); +Vec2 PerpVec2TowardsDir(Vec2 v, Vec2 dir); + +//- Round / floor / ceil +Vec2 RoundVec2(Vec2 a); +Vec2I32 RoundVec2ToVec2I32(Vec2 a); +Vec2 FloorVec2(Vec2 a); +Vec2 CeilVec2(Vec2 a); + +//- Angle +i32 WindingFromVec2(Vec2 a, Vec2 b); +Vec2 RotateVec2(Vec2 v, f32 a); +Vec2 Vec2FromAngle(f32 a); +f32 AngleFromVec2(Vec2 v); +f32 AngleFromVec2Dirs(Vec2 dir1, Vec2 dir2); +f32 AngleFromVec2Points(Vec2 pt1, Vec2 pt2); + +//- Closest point +Vec2 ClosestPointFromRay(Vec2 ray_pos, Vec2 ray_dir_norm, Vec2 p); + +//- Lerp +Vec2 LerpVec2(Vec2 val0, Vec2 val1, f32 t); +Vec2 SlerpVec2(Vec2 val0, Vec2 val1, f32 t); + +//////////////////////////////// +//~ Vec2I32 Operations + +b32 EqVec2I32(Vec2I32 a, Vec2I32 b); +Vec2I32 NegVec2I32(Vec2I32 a); +Vec2I32 AddVec2I32(Vec2I32 a, Vec2I32 b); +Vec2I32 SubVec2I32(Vec2I32 a, Vec2I32 b); + +//////////////////////////////// +//~ Xform operations + +b32 EqXform(Xform xf1, Xform xf2); + +//- Initialization +#define XformIdentity CppCompatInitListType(Xform) { .bx = VEC2(1, 0), .by = VEC2(0, 1) } +#define XformIdentityNoCast { .bx = VEC2(1, 0), .by = VEC2(0, 1) } +Xform XformFromPos(Vec2 v); +Xform XformFromRot(f32 r); +Xform XformFromScale(Vec2 scale); +Xform XformFromTrs(Trs trs); +Xform XformFromRect(Rect rect); + +//- Translation +Xform TranslateXform(Xform xf, Vec2 v); +Xform WorldTranslateXform(Xform xf, Vec2 v); + +//- Rotation +Xform RotateXform(Xform xf, f32 r); +Xform WorldRotateXform(Xform xf, f32 r); +Xform WorldRotateXformBasis(Xform xf, f32 r); +Xform XformWIthWorldRotation(Xform xf, f32 r); + +//- Scale +Xform ScaleXform(Xform xf, Vec2 scale); +Xform WorldScaleXform(Xform xf, Vec2 scale); + +//- Lerp +Xform LerpXform(Xform a, Xform b, f32 t); + +//- Invert +Xform InvertXform(Xform xf); + +//- Mul +Vec2 MulXformV2(Xform xf, Vec2 v); +Xform MulXform(Xform a, Xform b); +Quad MulXformQuad(Xform xf, Quad quad); +Vec2 MulXformBasisV2(Xform xf, Vec2 v); +Vec2 InvertXformMulV2(Xform xf, Vec2 v); +Vec2 InvertXformBasisMulV2(Xform xf, Vec2 v); + +//- Helpers +Xform BasisFromXform(Xform xf); +f32 DeterminantFromXform(Xform xf); +Vec2 RightFromXform(Xform xf); +Vec2 LeftFromXform(Xform xf); +Vec2 UpFromXform(Xform xf); +Vec2 DownFromXform(Xform xf); +f32 RotationFromXform(Xform xf); +Vec2 ScaleFromXform(Xform xf); + +//- Trs +#define TRS(...) ((Trs) { .t = VEC2(0,0), .s = VEC2(1, 1), .r = 0, __VA_ARGS__ }) + +//////////////////////////////// +//~ Rect operations + +#define RectFromScalar(_x, _y, _width, _height) (Rect) { .x = (_x), .y = (_y), .width = (_width), .height = (_height) } +#define RectFromVec2(_pos, _size) (Rect) { .pos = (_pos), .size = (_size) } +#define AllClipped ((ClipRect) { { 0.0f, 0.0f }, { 1.0f, 1.0f } }) + +//////////////////////////////// +//~ Quad operations + +#define UnitSquareQuad (Quad) { .p0 = VEC2(0, 0), .p1 = VEC2(0, 1), .p2 = VEC2(1, 1), .p3 = VEC2(1, 0) } +#define CenteredUnitSquareQuad (Quad) { .p0 = VEC2(-0.5f, -0.5f), .p1 = VEC2(0.5f, -0.5f), .p2 = VEC2(0.5f, 0.5f), .p3 = VEC2(-0.5f, 0.5f) } + +Quad QuadFromRect(Rect rect); +Quad QuadFromAabb(Aabb aabb); +Quad QuadFromLine(Vec2 start, Vec2 end, f32 thickness); +Quad QuadFromRay(Vec2 pos, Vec2 rel, f32 thickness); +Quad ScaleQuad(Quad q, f32 s); +Quad RoundQuad(Quad quad); +Quad FloorQuad(Quad quad); + +//////////////////////////////// +//~ Spring operations + +SoftSpring MakeSpring(f32 hertz, f32 damping_ratio, f32 dt); + +//////////////////////////////// +//~ Mat4x4 operations + +Mat4x4 Mat4x4FromXform(Xform xf); +Mat4x4 Mat4x4FromOrtho(f32 left, f32 right, f32 bottom, f32 top, f32 near_z, f32 far_z); +Mat4x4 MulMat4x4(Mat4x4 m1, Mat4x4 m2); +Mat4x4 ProjectMat4x4View(Xform view, f32 viewport_width, f32 viewport_height); diff --git a/src/meta/meta_memory.c b/src/meta/meta_memory.c new file mode 100644 index 00000000..5f2cbd98 --- /dev/null +++ b/src/meta/meta_memory.c @@ -0,0 +1,92 @@ +#if PlatformIsWindows + +//////////////////////////////// +//~ Windows headers + +#define WIN32_LEAN_AND_MEAN +#define UNICODE +#include + +//////////////////////////////// +//~ 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); +} + +#else +# error Memory allocation not implemented for this platform +#endif + +//////////////////////////////// +//~ Memory operations + +void *CopyBytes(void *dst, void *src, u64 count) +{ + char *dst_pchar = dst; + char *src_pchar = src; + for (u64 i = 0; i < count; ++i) + { + dst_pchar[i] = src_pchar[i]; + } + return dst; +} + +void *SetBytes(void *dst, u8 c, u64 count) +{ + char *dst_pchar = dst; + for (u64 i = 0; i < count; ++i) + { + dst_pchar[i] = (char)c; + } + return dst; +} + +i32 CmpBytes(void *p1, void *p2, u64 count) +{ + i32 result = 0; + char *p1_pchar = p1; + char *p2_pchar = p2; + for (u64 i = 0; i < count; ++i) + { + result = p1_pchar[i] - p2_pchar[i]; + if (result != 0) + { + break; + } + } + return result; +} diff --git a/src/meta/meta_memory.h b/src/meta/meta_memory.h new file mode 100644 index 00000000..fcb2f726 --- /dev/null +++ b/src/meta/meta_memory.h @@ -0,0 +1,29 @@ +//////////////////////////////// +//~ Memory allocation + +//- Reserve +void *ReserveMemory(u64 size); +void ReleaseMemory(void *address); + +//- Commit +void *CommitMemory(void *address, u64 size); +void DecommitMemory(void *address, u64 size); + +//- Protect +void SetMemoryReadonly(void *address, u64 size); +void SetMemoryReadWrite(void *address, u64 size); + +//////////////////////////////// +//~ Memory operations + +//- Wrappers +#define ZeroStruct(ptr) ZeroBytes((ptr), sizeof(*(ptr))) +#define ZeroArray(a) Assert(IsArray(a)); ZeroBytes((a), sizeof((a))) +#define CopyStruct(ptr_dst, ptr_src) CopyBytes((ptr_dst), (ptr_src), sizeof(*(ptr_dst))) +#define EqStruct(p1, p2) EqBytes((p1), (p2), sizeof(*p1)) +#define ZeroBytes(ptr, count) SetBytes((ptr), 0, (count)) +#define EqBytes(p1, p2, n) (CmpBytes((p1), (p2), (n)) == 0) + +void *CopyBytes(void *dst, void *src, u64 count); +void *SetBytes(void *dst, u8 c, u64 count); +i32 CmpBytes(void *p1, void *p2, u64 count); diff --git a/src/meta/meta_os.h b/src/meta/meta_os.h new file mode 100644 index 00000000..1b0cccb6 --- /dev/null +++ b/src/meta/meta_os.h @@ -0,0 +1,4 @@ +//////////////////////////////// +//~ @hookdeclStartup hooks + +void StartupOs(void); diff --git a/src/meta/meta_string.c b/src/meta/meta_string.c new file mode 100644 index 00000000..1e831453 --- /dev/null +++ b/src/meta/meta_string.c @@ -0,0 +1,897 @@ +//////////////////////////////// +//~ Conversion + +//- Char conversion + +String StringFromChar(Arena *arena, char c) +{ + u8 *dst = PushStructNoZero(arena, u8); + *dst = c; + return (String) + { + .len = 1, + .text = dst + }; +} + +//- Unsigned int conversion + +String StringFromU64(Arena *arena, u64 n, u64 base, u64 zfill) +{ + /* Base too large */ + Assert(base <= (countof(IntChars) - 1)); + + TempArena scratch = BeginScratch(arena); + + /* Build backwards text starting from least significant digit */ + u64 len = 0; + u8 *backwards_text = PushDry(scratch.arena, u8); + do + { + StringFromChar(scratch.arena, IntChars[n % base]); + ++len; + n /= base; + } while (n > 0); + + while (len < zfill) + { + StringFromChar(scratch.arena, '0'); + ++len; + } + + /* Reverse text into final string */ + u8 *final_text = PushStructsNoZero(arena, u8, len); + for (u64 i = 0; i < len; ++i) + { + final_text[i] = backwards_text[len - i - 1]; + } + + EndScratch(scratch); + + return (String) + { + .len = len, + .text = final_text + }; +} + +//- Signed int conversion + +String StringFromI64(Arena *arena, i64 n, u64 base, u64 zfill) +{ + u8 *final_text = PushDry(arena, u8); + u8 len = 0; + if (n < 0) + { + /* Push sign */ + StringFromChar(arena, '-'); + len = 1; + n = -n; + } + /* Push unsigned number */ + String uint_str = StringFromU64(arena, n, base, zfill); + return (String) + { + .len = len + uint_str.len, + .text = final_text + }; +} + +//- Pointer conversion + +String StringFromPtr(Arena *arena, void *ptr) +{ + String prepend = PushString(arena, Lit("0x")); + String uint_str = StringFromU64(arena, (u64)ptr, 16, sizeof(ptr)); + return (String) + { + .len = prepend.len + uint_str.len, + .text = prepend.text + }; +} + +//- Floating point conversion + +String StringFromF64(Arena *arena, f64 f, u32 precision) +{ + TempArena scratch = BeginScratch(arena); + u8 *final_text = PushDry(arena, u8); + u64 final_len = 0; + + if (IsF32Nan(f)) + { + final_len += PushString(arena, Lit("NaN")).len; + } + else if (f == F64Infinity) + { + final_len += PushString(arena, Lit("inf")).len; + } + else if (f == -F64Infinity) + { + final_len += PushString(arena, Lit("-inf")).len; + } + else + { + if (f < 0) + { + StringFromChar(arena, '-'); + f = -f; + ++final_len; + } + + /* Add one half of next precision level to round up */ + f += 0.5 / (f64)PowU64(10, (u8)precision); + + f64 part_whole = TruncF64(f); + f64 part_decimal = f - part_whole; + + /* Print whole part */ + { + /* Build backwards text starting from least significant digit */ + u8 *backwards_text = PushDry(scratch.arena, u8); + u64 backwards_text_len = 0; + do + { + u64 digit = (u64)RoundF64ToI64(ModF64(part_whole, 10.0)); + StringFromChar(scratch.arena, IntChars[digit % 10]); + ++backwards_text_len; + part_whole = TruncF64(part_whole / 10.0); + } while (part_whole > 0); + + /* Reverse text into final string */ + PushStructsNoZero(arena, u8, backwards_text_len); + for (u64 i = backwards_text_len; i-- > 0;) + { + final_text[final_len++] = backwards_text[i]; + } + } + + /* Print decimal part */ + if (precision > 0) + { + StringFromChar(arena, '.'); + for (u64 i = 0; i < precision; ++i) + { + part_decimal *= 10.0; + u64 digit = (u64)part_decimal; + part_decimal -= digit; + StringFromChar(arena, IntChars[digit % 10]); + } + final_len += (u64)precision + 1; + } + } + + EndScratch(scratch); + + return (String) + { + .len = final_len, + .text = final_text + }; +} + +//////////////////////////////// +//~ String operations + +//- Copy + +String PushString(Arena *arena, String src) +{ + String str = { + .len = src.len, + .text = PushStructsNoZero(arena, u8, src.len) + }; + CopyBytes(str.text, src.text, src.len); + return str; +} + +String PushStringToBuff(String dst, String src) +{ + String result = ZI; + result.len = MinU64(dst.len, src.len); + result.text = dst.text; + CopyBytes(result.text, src.text, result.len); + return result; +} + +//- Repeat + +String RepeatString(Arena *arena, String src, u64 count) +{ + u64 final_len = src.len * count; + u8 *final_text = PushStructsNoZero(arena, u8, final_len); + for (u64 i = 0; i < count; ++i) + { + CopyBytes(final_text + (src.len * i), src.text, src.len); + } + return (String) + { + .text = final_text, + .len = final_len + }; +} + +//- Concatenate + +String CatString(Arena *arena, String str1, String str2) +{ + String new_str = ZI; + new_str.len = str1.len + str2.len; + new_str.text = PushStructsNoZero(arena, u8, new_str.len); + CopyBytes(new_str.text, str1.text, str1.len); + CopyBytes(new_str.text + str1.len, str2.text, str2.len); + return new_str; +} + +//- Split + +/* `arena` is where pieces will be allocated. These strings point + * into the existing string and do not allocate any new text. */ +StringArray SplitString(Arena *arena, String str, String delim) +{ + StringArray pieces = ZI; + pieces.strings = PushDry(arena, String); + i64 piece_start = 0; + for (i64 i = 0; i < (i64)str.len - (i64)delim.len; ++i) + { + String cmp = ZI; + cmp.text = &str.text[i]; + cmp.len = MinI64(str.len - i, delim.len); + + b32 is_delimiter = EqString(cmp, delim); + if (is_delimiter) + { + String piece = ZI; + piece.text = &str.text[piece_start]; + piece.len = i - piece_start; + i += delim.len; + piece_start = i; + if (piece.len > 0) + { + *PushStructNoZero(arena, String) = piece; + ++pieces.count; + } + } + } + if (piece_start < (i64)str.len) + { + String piece = ZI; + piece.text = &str.text[piece_start]; + piece.len = str.len - piece_start; + *PushStructNoZero(arena, String) = piece; + ++pieces.count; + } + return pieces; +} + +//- Indent + +/* NOTE: Really slow */ +String IndentString(Arena *arena, String str, u32 indent) +{ + TempArena scratch = BeginScratch(arena); + + u64 final_len = 0; + u8 *final_text = PushDry(arena, u8); + + StringArray split = SplitString(scratch.arena, str, Lit("\n")); + for (u64 i = 0; i < split.count; ++i) + { + String piece = split.strings[i]; + for (u32 j = 0; j < indent; ++j) + { + StringFromChar(arena, ' '); + ++final_len; + } + PushString(arena, piece); + final_len += piece.len; + if (i < split.count - 1) + { + StringFromChar(arena, '\n'); + ++final_len; + } + } + + EndScratch(scratch); + + return (String) + { + .len = final_len, + .text = final_text + }; +} + +//- Lower + +String LowerString(Arena *arena, String str) +{ + String result = ZI; + result.text = PushStructsNoZero(arena, u8, str.len); + result.len = str.len; + + for (u64 i = 0; i < str.len; ++i) + { + u8 c = str.text[i]; + if (65 <= c && c <= 90) + { + c += 32; + } + result.text[i] = c; + } + + return result; +} + +//- Compare + +b32 EqString(String str1, String str2) +{ + b32 eq = 1; + if (str1.len == str2.len) + { + for (u64 i = 0; i < str1.len; ++i) + { + if (str1.text[i] != str2.text[i]) + { + eq = 0; + break; + } + } + } + else + { + eq = 0; + } + return eq; +} + +i32 CmpString(String str1, String str2) +{ + i32 result = 0; + for (u64 i = 0; i < MinU64(str1.len, str2.len); ++i) + { + result = str1.text[i] - str2.text[i]; + if (result != 0) + { + break; + } + } + if (result == 0) + { + if (str1.len > str2.len) + { + result = str1.text[str2.len]; + } + else if (str2.len > str1.len) + { + result = str2.text[str1.len]; + } + } + return result; +} + +//- Match + +b32 StringContains(String str, String substring) +{ + if (substring.len > str.len) + { + return 0; + } + + for (u64 i = 0; i <= str.len - substring.len; ++i) + { + b32 match = 1; + for (u64 j = 0; j < substring.len; ++j) + { + if (str.text[i + j] != substring.text[j]) + { + match = 0; + break; + } + } + if (match) + { + return 1; + } + } + + return 0; +} + +b32 StringStartsWith(String str, String substring) +{ + if (str.len >= substring.len) + { + for (u64 i = 0; i < substring.len; ++i) + { + if (str.text[i] != substring.text[i]) + { + return 0; + } + } + return 1; + } + return 0; +} + +b32 StringEndsWith(String str, String substring) +{ + if (str.len >= substring.len) + { + u64 start = str.len - substring.len; + for (u64 i = 0; i < substring.len; ++i) + { + if (str.text[start + i] != substring.text[i]) + { + return 0; + } + } + return 1; + } + return 0; +} + +//////////////////////////////// +//~ String list operations + +StringNode *PushStringNode(Arena *arena, StringList *l, String s) +{ + StringNode *n = PushStruct(arena, StringNode); + n->s = s; + n->prev = l->last; + if (l->last) + { + l->last->next = n; + } + else + { + l->first = n; + } + l->last = n; + return n; +} + +//////////////////////////////// +//~ Formatting + +/* String formatting only has one format specifier: "%F". All specifier info is + * included in the arguments (instead of w/ the specifier like in printf). + * + * Example: + * StringFormat(arena, Lit("Hello there %F"), FmtString(Lit("George"))) + * + * NOTE: FmtEnd must be passed as the last arg in the va_list (This is + * done automatically by the `StringFormat` macro). + * + * Format arguments: + * FmtChar: Format a single u8 character + * FmtString: Format a `string` struct + * FmtUint: Format a u64 + * FmtSint: Format an i64 + * FmtFloat: Format an f64 with DefaultFmtPrecision + * FmtFloatP: Format an f64 with specified precision + * FmtHex: Format a u64 in hexadecimal notation + * FmtPtr: Format a pointer in hexadecimal notation prefixed by "0x" + * + * FmtEnd (internal): Denote the end of the va_list + * + * TODO: + * %n equivalent? (nothing) + * %e/%E equivalent? (scientific notation of floats) + * %o equivalent? (octal representation) + */ +String StringFormatV(Arena *arena, String fmt, va_list args) +{ + __prof; + + u64 final_len = 0; + u8 *final_text = PushDry(arena, u8); + + u8 *end = fmt.text + fmt.len; + b32 no_more_args = 0; + for (u8 *c = fmt.text; c < end; ++c) + { + u8 *next = ((c + 1) < end) ? (c + 1) : (u8 *)"\0"; + + /* Escape '%%' */ + b32 escape = !no_more_args && *c == '%' && *next == '%'; + if (escape) + { + /* Skip the escape '%' char from parsing */ + ++c; + } + + if (!no_more_args && !escape && *c == '%' && *next == 'F') + { + String parsed_str = ZI; + /* Detect arg type and parse to string */ + FmtArg arg = va_arg(args, FmtArg); + switch (arg.kind) + { + case FmtKind_Char: + { + parsed_str = StringFromChar(arena, arg.value.c); + } break; + + case FmtKind_String: + { + parsed_str = PushString(arena, arg.value.string); + } break; + + case FmtKind_Uint: + { + parsed_str = StringFromU64(arena, arg.value.uint, 10, arg.zfill); + } break; + + case FmtKind_Sint: + { + parsed_str = StringFromI64(arena, arg.value.sint, 10, arg.zfill); + } break; + + case FmtKind_Hex: + { + parsed_str = StringFromU64(arena, arg.value.sint, 16, arg.zfill); + } break; + + case FmtKind_Ptr: + { + parsed_str = StringFromPtr(arena, arg.value.ptr); + } break; + + case FmtKind_Float: + { + parsed_str = StringFromF64(arena, arg.value.f, arg.precision); + } break; + + case FmtKind_End: + { + /* Unexpected end. Not enough FMT args passed to function. */ + Assert(0); + parsed_str = PushString(arena, Lit("")); + no_more_args = 1; + } break; + + default: + { + /* Unknown format type */ + Assert(0); + parsed_str = PushString(arena, Lit("")); + no_more_args = 1; + } break; + } + /* Update final string len / start */ + final_len += parsed_str.len; + /* Skip 'F' from parsing */ + ++c; + } + else + { + /* Parse character normally */ + StringFromChar(arena, *c); + ++final_len; + } + } + +#if RtcIsEnabled + if (!no_more_args) + { + FmtArg last_arg = va_arg(args, FmtArg); + /* End arg not reached. Too many FMT values passed to function. */ + Assert(last_arg.kind == FmtKind_End); + } +#endif + + return (String) + { + .len = final_len, + .text = final_text + }; +} + +String _StringFormat(Arena *arena, String fmt, ...) +{ + va_list args; + va_start(args, fmt); + String new_str = StringFormatV(arena, fmt, args); + va_end(args); + return new_str; +} + +//////////////////////////////// +//~ Unicode + +//- Codepoint iteration + +CodepointIter BeginCodepointIter(String str) +{ + return (CodepointIter) + { + .src = str + }; +} + +/* Returns 0 if done iterating */ +b32 AdvanceCodepointIter(CodepointIter *iter) +{ + if (iter->pos < iter->src.len) + { + String str_remaining = { .len = (iter->src.len - iter->pos), .text = iter->src.text + iter->pos }; + Utf8DecodeResult decoded = DecodeUtf8(str_remaining); + iter->pos += decoded.advance8; + iter->codepoint = decoded.codepoint; + return 1; + } + else + { + return 0; + } +} + +void EndCodepointIter(CodepointIter *iter) +{ + /* Do nothing */ + LAX iter; +} + +//- String decode + +/* utf8 <- utf16 */ +String StringFromString16(Arena *arena, String16 str16) +{ + String result = { + .len = 0, + .text = PushDry(arena, u8) + }; + + u64 pos16 = 0; + while (pos16 < str16.len) + { + String16 str16_remaining = { .len = (str16.len - pos16), .text = str16.text + pos16 }; + Utf16DecodeResult decoded = DecodeUtf16(str16_remaining); + Utf8EncodeResult encoded = EncodeUtf8(decoded.codepoint); + + u8 *dst = PushStructsNoZero(arena, u8, encoded.count8); + CopyBytes(dst, encoded.chars8, encoded.count8); + + pos16 += decoded.advance16; + result.len += encoded.count8; + } + + return result; +} + +/* utf8 <- utf32 */ +String StringFromString32(Arena *arena, String32 str32) +{ + String result = { + .len = 0, + .text = PushDry(arena, u8) + }; + + u64 pos32 = 0; + while (pos32 < str32.len) + { + String32 str32_remaining = { .len = (str32.len - pos32), .text = str32.text + pos32 }; + Utf32DecodeResult decoded = DecodeUtf32(str32_remaining); + Utf8EncodeResult encoded = EncodeUtf8(decoded.codepoint); + + u8 *dst = PushStructsNoZero(arena, u8, encoded.count8); + CopyBytes(dst, &encoded.chars8, encoded.count8); + + pos32 += 1; + result.len += encoded.count8; + } + + return result; +} + +//- String encode + +/* utf16 <- utf8 */ +String16 String16FromString(Arena *arena, String str8) +{ + String16 result = { + .len = 0, + .text = PushDry(arena, u16) + }; + + u64 pos8 = 0; + while (pos8 < str8.len) + { + String str8_remaining = { .len = (str8.len - pos8), .text = str8.text + pos8 }; + Utf8DecodeResult decoded = DecodeUtf8(str8_remaining); + Utf16EncodeResult encoded = EncodeUtf16(decoded.codepoint); + + u16 *dst = PushStructsNoZero(arena, u16, encoded.count16); + CopyBytes(dst, encoded.chars16, (encoded.count16 << 1)); + + pos8 += decoded.advance8; + result.len += encoded.count16; + } + + return result; +} + +/* utf32 <- utf8 */ +String32 String32FromString(Arena *arena, String str8) +{ + String32 result = { + .len = 0, + .text = PushDry(arena, u32) + }; + + u64 pos8 = 0; + while (pos8 < str8.len) + { + String str8_remaining = { .len = (str8.len - pos8), .text = str8.text + pos8 }; + Utf8DecodeResult decoded = DecodeUtf8(str8_remaining); + Utf32EncodeResult encoded = EncodeUtf32(decoded.codepoint); + + u32 *dst = PushStructNoZero(arena, u32); + *dst = encoded.chars32; + + pos8 += decoded.advance8; + result.len += 1; + } + + return result; +} + +//////////////////////////////// +//~ Legacy null-terminated C string operations + +//- Narrow C strings + +u64 CstrLenNoLimit(char *cstr) +{ + char *end = cstr; + if (cstr) + { + while (*end) + { + ++end; + } + } + return end - cstr; +} + +u64 CstrLen(char *cstr, u64 limit) +{ + char *end = cstr; + if (cstr) + { + for (u64 i = 0; i < limit; ++i) + { + if (*end) + { + ++end; + } + else + { + break; + } + } + } + return end - cstr; +} + +char *CstrFromString(Arena *arena, String src) +{ + u8 *text = PushStructsNoZero(arena, u8, src.len + 1); + CopyBytes(text, src.text, src.len); + text[src.len] = 0; + return (char *)text; +} + +char *CstrFromStringToBuff(String dst_buff, String src) +{ + if (dst_buff.len > 0) + { + u64 len = MinU64(src.len, dst_buff.len - 1); + CopyBytes(dst_buff.text, src.text, len); + dst_buff.text[len] = 0; + } + return (char *)dst_buff.text; +} + +String StringFromCstrNoLimit(char *cstr) +{ + u64 len = CstrLenNoLimit(cstr); + return (String) + { + .len = len, + .text = (u8 *)cstr + }; +} + +String StringFromCstr(char *cstr, u64 limit) +{ + u64 len = CstrLen(cstr, limit); + return (String) + { + .text = (u8 *)cstr, + .len = len + }; +} + +//- Wide C strings + +u64 WstrLenNoLimit(wchar_t *wstr) +{ + wchar_t *end = wstr; + if (end) + { + while (*end) + { + ++end; + } + } + return end - wstr; +} + +u64 WstrLen(wchar_t *wstr, u64 limit) +{ + wchar_t *end = wstr; + if (wstr) + { + for (u64 i = 0; i < limit; ++i) + { + if (*end) + { + ++end; + } + else + { + break; + } + } + } + return end - wstr; +} + +wchar_t *WstrFromString(Arena *arena, String src) +{ + String16 str16 = String16FromString(arena, src); + *PushStructNoZero(arena, u16) = 0; + return (wchar_t *)str16.text; +} + +wchar_t *WstrFromString16(Arena *arena, String16 src) +{ + u16 *text = PushStructsNoZero(arena, u16, src.len + 1); + text[src.len] = 0; + return (wchar_t *)text; +} + +String StringFromWstrNoLimit(Arena *arena, wchar_t *wstr) +{ + String16 str16 = String16FromWstrNoLimit(wstr); + return StringFromString16(arena, str16); +} + +String StringFromWstr(Arena *arena, wchar_t *wstr, u64 limit) +{ + String16 str16 = String16FromWstr(wstr, limit); + return StringFromString16(arena, str16); +} + +String16 String16FromWstrNoLimit(wchar_t *wstr) +{ + u64 len = WstrLenNoLimit(wstr); + return (String16) + { + .len = len, + .text = (u16 *)wstr + }; +} + +String16 String16FromWstr(wchar_t *wstr, u64 limit) +{ + u64 len = WstrLen(wstr, limit); + return (String16) + { + .len = len, + .text = (u16 *)wstr + }; +} diff --git a/src/meta/meta_string.h b/src/meta/meta_string.h new file mode 100644 index 00000000..3078eee1 --- /dev/null +++ b/src/meta/meta_string.h @@ -0,0 +1,172 @@ +//////////////////////////////// +//~ String types + +Struct(StringArray) +{ + u64 count; + String *strings; +}; + +Struct(StringNode) +{ + String s; + StringNode *next; + StringNode *prev; +}; + +Struct(StringList) +{ + StringNode *first; + StringNode *last; +}; + +//////////////////////////////// +//~ Formatting types + +#define DefaultFmtPrecision 3 +#define IntChars ("0123456789abcdef") + +typedef i32 FmtKind; enum +{ + FmtKind_None, + + /* Arbitrary magic numbers for argument validation */ + FmtKind_Char = 0x0f5281df, + FmtKind_String = 0x0a5ffa9a, + FmtKind_Uint = 0x0746f19b, + FmtKind_Sint = 0x08603694, + FmtKind_Hex = 0x0a3d0792, + FmtKind_Ptr = 0x0c4519e4, + FmtKind_Float = 0x04814143, + + FmtKind_End = 0x0ecbc5ae +}; + +Struct(FmtArg) +{ + FmtKind kind; + u32 precision; + u32 zfill; + union + { + u8 c; + String string; + u64 uint; + i64 sint; + void *ptr; + f64 f; + } value; +}; + +//////////////////////////////// +//~ Unicode types + +Struct(CodepointIter) +{ + u32 codepoint; + + /* Internal */ + String src; + u64 pos; +}; + +//////////////////////////////// +//~ String utils + +#define STRING(size, data) (CppCompatInitListType(String) { (size), (data) }) +#define Lit(cstr_lit) CppCompatInitListType(String) { (sizeof((cstr_lit)) - 1), (u8 *)(cstr_lit) } +#define LitNoCast(cstr_lit) { .len = (sizeof((cstr_lit)) - 1), .text = (u8 *)(cstr_lit) } +#define StringFromPointers(p0, p1) (CppCompatInitListType(String) { (u8 *)(p1) - (u8 *)(p0), (u8 *)p0 }) +#define StringFromStruct(ptr) (CppCompatInitListType(String) { sizeof(*(ptr)), (u8 *)(ptr) }) +#define StringFromArena(arena) (STRING((arena)->pos, ArenaBase(arena))) + +/* String from static array */ +#define StringFromArray(a) \ + ( \ + Assert(IsArray(a)), \ + ((String) { .len = sizeof(a), .text = (u8 *)(a) }) \ + ) + +//////////////////////////////// +//~ Conversion operations + +String StringFromChar(Arena *arena, char c); +String StringFromU64(Arena *arena, u64 n, u64 base, u64 zfill); +String StringFromI64(Arena *arena, i64 n, u64 base, u64 zfill); +String StringFromPtr(Arena *arena, void *ptr); +String StringFromF64(Arena *arena, f64 f, u32 precision); + +//////////////////////////////// +//~ String operation + +String PushString(Arena *arena, String src); +String PushStringToBuff(String dst, String src); +String RepeatString(Arena *arena, String src, u64 count); +String CatString(Arena *arena, String str1, String str2); +StringArray SplitString(Arena *arena, String str, String delim); +String IndentString(Arena *arena, String str, u32 indent); +String LowerString(Arena *arena, String str); +b32 EqString(String str1, String str2); +i32 CmpString(String str1, String str2); +b32 StringContains(String str, String substring); +b32 StringStartsWith(String str, String substring); +b32 StringEndsWith(String str, String substring); + +//////////////////////////////// +//~ Formatting + +//- Format arg helpers +#define FmtChar(v) (FmtArg) {.kind = FmtKind_Char, .value.c = (v)} +#define FmtString(v) (FmtArg) {.kind = FmtKind_String, .value.string = (v)} +#define FmtUint(v) (FmtArg) {.kind = FmtKind_Uint, .value.uint = (v)} +#define FmtUintZ(v, z) (FmtArg) {.kind = FmtKind_Uint, .value.uint = (v), .zfill = (z)} +#define FmtSint(v) (FmtArg) {.kind = FmtKind_Sint, .value.sint = (v)} +#define FmtHex(v) (FmtArg) {.kind = FmtKind_Hex, .value.uint = (v)} +#define FmtPtr(v) (FmtArg) {.kind = FmtKind_Ptr, .value.ptr = (v)} +#define FmtFloat(v) FmtFloatP(v, DefaultFmtPrecision) +#define FmtFloatP(v, p) (FmtArg) {.kind = FmtKind_Float, .value.f = (v), .precision = (p)} +#define FmtHandle(v) (FmtArg) {.kind = FmtKind_Handle, .value.handle.h64[0] = (v).idx, .value.handle.h64[1] = (v).gen} +#define FmtUid(v) (FmtArg) {.kind = FmtKind_Uid, .value.uid = (v) } +#define FmtEnd (FmtArg) {.kind = FmtKind_End} + +//- Format functions +#define StringFormat(arena, fmt, ...) _StringFormat((arena), (fmt), __VA_ARGS__, FmtEnd) +String _StringFormat(Arena *arena, String fmt, ...); +String StringFormatV(Arena *arena, String fmt, va_list args); + +//////////////////////////////// +//~ Unicode operations + +//- Iter +CodepointIter BeginCodepointIter(String str); +b32 AdvanceCodepointIter(CodepointIter *iter); +void EndCodepointIter(CodepointIter *iter); + +//- Decode string +String StringFromString16(Arena *arena, String16 str16); +String StringFromString32(Arena *arena, String32 str32); + +//- Encode string +String16 String16FromString(Arena *arena, String str8); +String32 String32FromString(Arena *arena, String str8); + +//////////////////////////////// +//~ Legacy null-terminated C string operations + +//- Narrow strings +u64 CstrLenNoLimit(char *cstr); +u64 CstrLen(char *cstr, u64 limit); +char *CstrFromString(Arena *arena, String src); +char *CstrFromStringToBuff(String dest_buff, String src); +String StringFromCstrNoLimit(char *cstr); +String StringFromCstr(char *cstr, u64 limit); + +//- Wide strings +u64 WstrLenNoLimit(wchar_t *wstr); +u64 WstrLen(wchar_t *wstr, u64 limit); +wchar_t *WstrFromString(Arena *arena, String src); +wchar_t *WstrFromString16(Arena *arena, String16 src); +String StringFromWstrNoLimit(Arena *arena, wchar_t *wstr); +String StringFromWstr(Arena *arena, wchar_t *wstr, u64 limit); +String16 String16FromWstrNoLimit(wchar_t *wstr); +String16 String16FromWstr(wchar_t *wstr, u64 limit); diff --git a/src/meta/meta_uni.c b/src/meta/meta_uni.c new file mode 100644 index 00000000..f7a498bf --- /dev/null +++ b/src/meta/meta_uni.c @@ -0,0 +1,245 @@ +//////////////////////////////// +//~ Utf8 + +//- Decode + +Utf8DecodeResult DecodeUtf8(String str) +{ + LocalPersist const u8 lengths[32] = { + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,2,2,2,2,3,3,4,5 + }; + + Utf8DecodeResult result = ZI; + u32 codepoint = U32Max; + u32 advance = 0; + if (str.len > 0) + { + u8 c0 = str.text[0]; + u8 utf8_len = lengths[c0 >> 3]; + + advance = 1; + switch (utf8_len) + { + case 1: + { + codepoint = c0; + } break; + + case 2: + { + if (str.len >= 2) + { + u8 c1 = str.text[1]; + if (lengths[c1 >> 3] == 0) + { + codepoint = (c1 & 0x3F) << 0; + codepoint |= (c0 & 0x1F) << 6; + advance = 2; + } + } + } break; + + case 3: + { + if (str.len >= 3) + { + u8 c1 = str.text[1]; + u8 c2 = str.text[2]; + if (lengths[c1 >> 3] == 0 && + lengths[c2 >> 3] == 0) + { + codepoint = (c2 & 0x3F) << 0; + codepoint |= (c1 & 0x3F) << 6; + codepoint |= (c0 & 0x0F) << 12; + advance = 3; + } + } + } break; + + case 4: + { + if (str.len >= 4) + { + u8 c1 = str.text[1]; + u8 c2 = str.text[2]; + u8 c3 = str.text[3]; + if (lengths[c1 >> 3] == 0 && + lengths[c2 >> 3] == 0 && + lengths[c3 >> 3] == 0) + { + codepoint = (c3 & 0x3F) << 0; + codepoint |= (c2 & 0x3F) << 6; + codepoint |= (c1 & 0x3F) << 12; + codepoint |= (c0 & 0x07) << 16; + advance = 3; + } + } + } break; + + default: break; + } + } + + result.advance8 = advance; + result.codepoint = codepoint; + return result; +} + +//- Encode + +Utf8EncodeResult EncodeUtf8(u32 codepoint) +{ + Utf8EncodeResult result = ZI; + + if (codepoint <= 0x7F) + { + result.count8 = 1; + result.chars8[0] = codepoint; + } + else if (codepoint <= 0x7FF) + { + result.count8 = 2; + result.chars8[1] = 0x80 | ((codepoint >> 0) & 0x3F); + result.chars8[0] = 0xC0 | ((codepoint >> 6) & 0x1F); + } + else if (codepoint <= 0xFFFF) + { + result.count8 = 3; + result.chars8[2] = 0x80 | ((codepoint >> 0) & 0x3F); + result.chars8[1] = 0x80 | ((codepoint >> 6) & 0x3F); + result.chars8[0] = 0xE0 | ((codepoint >> 12) & 0x0F); + } + else if (codepoint <= 0x10FFFF) + { + result.count8 = 4; + result.chars8[3] = 0x80 | ((codepoint >> 0) & 0x3F); + result.chars8[2] = 0x80 | ((codepoint >> 6) & 0x3F); + result.chars8[1] = 0x80 | ((codepoint >> 12) & 0x3F); + result.chars8[0] = 0xF0 | ((codepoint >> 18) & 0x07); + } + else + { + /* Invalid codepoint */ + result.count8 = 1; + result.chars8[0] = '?'; + } + + return result; +} + +//////////////////////////////// +//~ Utf16 + +//- Decode + +Utf16DecodeResult DecodeUtf16(String16 str) +{ + Utf16DecodeResult result = ZI; + u32 codepoint = U32Max; + u32 advance = 0; + + if (str.len >= 1) + { + u16 c0 = str.text[0]; + codepoint = c0; + advance = 1; + if (str.len >= 2) + { + u16 c1 = str.text[1]; + if ((0xD800 <= c0 && c0 < 0xDC00) && (0xDC00 <= c1 && c1 < 0xE000)) + { + codepoint = (c1 & 0x3FF) << 0; + codepoint |= (c0 & 0x3FF) << 10; + advance = 2; + } + } + } + + result.advance16 = advance; + result.codepoint = codepoint; + return result; +} + +//- Encode + +Utf16EncodeResult EncodeUtf16(u32 codepoint) +{ + Utf16EncodeResult result = ZI; + + if (codepoint <= 0xFFFF) + { + result.count16 = 1; + result.chars16[0] = codepoint; + } + else if (codepoint <= 0x10FFFF) + { + result.count16 = 2; + result.chars16[1] = 0xDC00 | ((codepoint >> 0) & 0x3FF); + result.chars16[0] = 0xD800 | ((codepoint >> 10) & 0x3FF); + } + else + { + /* Invalid codepoint */ + result.count16 = 1; + result.chars16[0] = '?'; + } + + return result; +} + +//- Surrogate check + +b32 IsUtf16HighSurrogate(u16 c) +{ + return 0xD800 <= c && c < 0xDC00; +} + +b32 IsUtf16LowSurrogate(u16 c) +{ + return 0xDC00 <= c && c < 0xE000; +} + +//////////////////////////////// +//~ Utf32 + +//- Decode + +Utf32DecodeResult DecodeUtf32(String32 str) +{ + Utf32DecodeResult result = ZI; + u32 codepoint = U32Max; + u32 advance = 0; + + if (str.len >= 1) + { + u32 c = str.text[0]; + advance = 1; + if (c <= 0x10FFFF) + { + codepoint = c; + } + } + + result.advance32 = advance; + result.codepoint = codepoint; + return result; +} + +//- Encode + +Utf32EncodeResult EncodeUtf32(u32 codepoint) +{ + Utf32EncodeResult result = ZI; + + if (codepoint <= 0x10FFFF) + { + result.chars32 = codepoint; + } + else + { + /* Invalid codepoint */ + result.chars32 = '?'; + } + + return result; +} diff --git a/src/meta/meta_uni.h b/src/meta/meta_uni.h new file mode 100644 index 00000000..f542a12e --- /dev/null +++ b/src/meta/meta_uni.h @@ -0,0 +1,64 @@ +//////////////////////////////// +//~ Utf8 types + +Struct(Utf8DecodeResult) +{ + u32 advance8; + u32 codepoint; +}; + +Struct(Utf8EncodeResult) +{ + u32 count8; + u8 chars8[4]; +}; + +//////////////////////////////// +//~ Utf16 types + +Struct(Utf16DecodeResult) +{ + u32 advance16; + u32 codepoint; +}; + +Struct(Utf16EncodeResult) +{ + u32 count16; + u16 chars16[2]; +}; + +//////////////////////////////// +//~ Utf32 types + +Struct(Utf32DecodeResult) +{ + u32 advance32; + u32 codepoint; +}; + +Struct(Utf32EncodeResult) +{ + u32 chars32; +}; + +//////////////////////////////// +//~ Utf8 operations + +Utf8DecodeResult DecodeUtf8(String str); +Utf8EncodeResult EncodeUtf8(u32 codepoint); + +//////////////////////////////// +//~ Utf16 operations + +Utf16DecodeResult DecodeUtf16(String16 str); +Utf16EncodeResult EncodeUtf16(u32 codepoint); + +b32 IsUtf16HighSurrogate(u16 c); +b32 IsUtf16LowSurrogate(u16 c); + +//////////////////////////////// +//~ Utf32 operations + +Utf32DecodeResult DecodeUtf32(String32 str); +Utf32EncodeResult EncodeUtf32(u32 codepoint); diff --git a/src/meta/win32/meta_win32.c b/src/meta/win32/meta_win32.c new file mode 100644 index 00000000..9990094d --- /dev/null +++ b/src/meta/win32/meta_win32.c @@ -0,0 +1,68 @@ +W32_SharedState W32_shared_state = ZI; + +//////////////////////////////// +//~ Windows libs + +#pragma comment(lib, "kernel32") +#pragma comment(lib, "user32") + +//////////////////////////////// +//~ @hookdef OS startup hook + +void StartupOs(void) +{ + W32_SharedState *g = &W32_shared_state; + g->tls_index = TlsAlloc(); + TlsSetValue(g->tls_index, 0); +} + +//////////////////////////////// +//~ @hookdef Core Panic hooks + +/* TODO: Remove stdio & printf */ +#include + +void Panic(String msg) +{ + char msg_cstr[4096]; + CstrFromStringToBuff(StringFromArray(msg_cstr), msg); + ShowMessageBoxCstr("Fatal error", msg_cstr, 1); + printf(msg_cstr); + fflush(stdout); + ExitProcess(1); +} + +//////////////////////////////// +//~ @hookdef Core Debugger hooks + +b32 IsRunningInDebugger(void) +{ + return IsDebuggerPresent(); +} + +//////////////////////////////// +//~ @hookdef Core Thread hooks + +i16 ThreadId(void) +{ + W32_SharedState *g = &W32_shared_state; + return (i16)(i64)TlsGetValue(g->tls_index); +} + +//////////////////////////////// +//~ @hookdef OS Message box hooks + +void ShowMessageBox(String title, String msg, b32 error) +{ + char title_cstr[256]; + char msg_cstr[4096]; + CstrFromStringToBuff(StringFromArray(title_cstr), title); + CstrFromStringToBuff(StringFromArray(msg_cstr), msg); + ShowMessageBoxCstr(title_cstr, msg_cstr, error); +} + +void ShowMessageBoxCstr(char *title_cstr, char *msg_cstr, b32 error) +{ + u32 mb_flags = MB_SETFOREGROUND | (!!error * MB_ICONERROR) | (!error * MB_ICONINFORMATION); + MessageBoxExA(0, msg_cstr, title_cstr, mb_flags, 0); +} diff --git a/src/meta/win32/meta_win32.h b/src/meta/win32/meta_win32.h new file mode 100644 index 00000000..eb6c10d7 --- /dev/null +++ b/src/meta/win32/meta_win32.h @@ -0,0 +1,24 @@ +//////////////////////////////// +//~ Windows headers + +#pragma warning(push, 0) +# define UNICODE +# define WIN32_LEAN_AND_MEAN +# include +#pragma warning(pop) + +//////////////////////////////// +//~ Shared state + +Struct(W32_SharedState) +{ + DWORD tls_index; +}; + +extern W32_SharedState W32_shared_state; + +//////////////////////////////// +//~ @hookdecl Message box + +void ShowMessageBox(String title, String msg, b32 error); +void ShowMessageBoxCstr(char *title_cstr, char *msg_cstr, b32 error);