simplification pass over os core layer; simplification pass over base arena; set up build.sh; stub out new spot for linux os core

This commit is contained in:
Ryan Fleury
2024-07-15 17:23:01 -07:00
parent 6447a2a993
commit 1b74fb0760
58 changed files with 66001 additions and 66756 deletions
+137 -137
View File
@@ -1,137 +1,137 @@
@echo off
setlocal
cd /D "%~dp0"
:: --- Usage Notes (2024/1/10) ------------------------------------------------
::
:: This is a central build script for the RAD Debugger project. It takes a list
:: of simple alphanumeric-only arguments which control (a) what is built, (b)
:: which compiler & linker are used, and (c) extra high-level build options. By
:: default, if no options are passed, then the main "raddbg" graphical debugger
:: is built.
::
:: Below is a non-exhaustive list of possible ways to use the script:
:: `build raddbg`
:: `build raddbg clang`
:: `build raddbg release`
:: `build raddbg asan telemetry`
:: `build rdi_from_pdb`
::
:: For a full list of possible build targets and their build command lines,
:: search for @build_targets in this file.
::
:: Below is a list of all possible non-target command line options:
::
:: - `asan`: enable address sanitizer
:: - `telemetry`: enable RAD telemetry profiling support
:: --- Unpack Arguments -------------------------------------------------------
for %%a in (%*) do set "%%a=1"
if not "%msvc%"=="1" if not "%clang%"=="1" set msvc=1
if not "%release%"=="1" set debug=1
if "%debug%"=="1" set release=0 && echo [debug mode]
if "%release%"=="1" set debug=0 && echo [release mode]
if "%msvc%"=="1" set clang=0 && echo [msvc compile]
if "%clang%"=="1" set msvc=0 && echo [clang compile]
if "%~1"=="" echo [default mode, assuming `raddbg` build] && set raddbg=1
if "%~1"=="release" if "%~2"=="" echo [default mode, assuming `raddbg` build] && set raddbg=1
:: --- Unpack Command Line Build Arguments ------------------------------------
set auto_compile_flags=
if "%telemetry%"=="1" set auto_compile_flags=%auto_compile_flags% -DPROFILE_TELEMETRY=1 && echo [telemetry profiling enabled]
if "%asan%"=="1" set auto_compile_flags=%auto_compile_flags% -fsanitize=address && echo [asan enabled]
:: --- Compile/Link Line Definitions ------------------------------------------
set cl_common= /I..\src\ /I..\local\ /nologo /FC /Z7
set clang_common= -I..\src\ -I..\local\ -gcodeview -fdiagnostics-absolute-paths -Wall -Wno-unknown-warning-option -Wno-missing-braces -Wno-unused-function -Wno-writable-strings -Wno-unused-value -Wno-unused-variable -Wno-unused-local-typedef -Wno-deprecated-register -Wno-deprecated-declarations -Wno-unused-but-set-variable -Wno-single-bit-bitfield-constant-conversion -Wno-compare-distinct-pointer-types -Wno-initializer-overrides -Wno-incompatible-pointer-types-discards-qualifiers -Xclang -flto-visibility-public-std -D_USE_MATH_DEFINES -Dstrdup=_strdup -Dgnu_printf=printf
set cl_debug= call cl /Od /Ob1 /DBUILD_DEBUG=1 %cl_common% %auto_compile_flags%
set cl_release= call cl /O2 /DBUILD_DEBUG=0 %cl_common% %auto_compile_flags%
set clang_debug= call clang -g -O0 -DBUILD_DEBUG=1 %clang_common% %auto_compile_flags%
set clang_release= call clang -g -O2 -DBUILD_DEBUG=0 %clang_common% %auto_compile_flags%
set cl_link= /link /MANIFEST:EMBED /INCREMENTAL:NO /natvis:"%~dp0\src\natvis\base.natvis" logo.res
set clang_link= -fuse-ld=lld -Xlinker /MANIFEST:EMBED -Xlinker /natvis:"%~dp0\src\natvis\base.natvis" logo.res
set cl_out= /out:
set clang_out= -o
:: --- Per-Build Settings -----------------------------------------------------
set link_dll=-DLL
if "%msvc%"=="1" set only_compile=/c
if "%clang%"=="1" set only_compile=-c
if "%msvc%"=="1" set EHsc=/EHsc
if "%clang%"=="1" set EHsc=
if "%msvc%"=="1" set no_aslr=/DYNAMICBASE:NO
if "%clang%"=="1" set no_aslr=-Wl,/DYNAMICBASE:NO
if "%msvc%"=="1" set rc=call rc
if "%clang%"=="1" set rc=call llvm-rc
:: --- Choose Compile/Link Lines ----------------------------------------------
if "%msvc%"=="1" set compile_debug=%cl_debug%
if "%msvc%"=="1" set compile_release=%cl_release%
if "%msvc%"=="1" set compile_link=%cl_link%
if "%msvc%"=="1" set out=%cl_out%
if "%clang%"=="1" set compile_debug=%clang_debug%
if "%clang%"=="1" set compile_release=%clang_release%
if "%clang%"=="1" set compile_link=%clang_link%
if "%clang%"=="1" set out=%clang_out%
if "%debug%"=="1" set compile=%compile_debug%
if "%release%"=="1" set compile=%compile_release%
:: --- Prep Directories -------------------------------------------------------
if not exist build mkdir build
if not exist local mkdir local
:: --- Produce Logo Icon File -------------------------------------------------
pushd build
%rc% /nologo /fo logo.res ..\data\logo.rc || exit /b 1
popd
:: --- Get Current Git Commit Id ----------------------------------------------
for /f %%i in ('call git describe --always --dirty') do set compile=%compile% -DBUILD_GIT_HASH=\"%%i\"
:: --- Build & Run Metaprogram ------------------------------------------------
if "%no_meta%"=="1" echo [skipping metagen]
if not "%no_meta%"=="1" (
pushd build
%compile_debug% ..\src\metagen\metagen_main.c %compile_link% %out%metagen.exe || exit /b 1
metagen.exe || exit /b 1
popd
)
:: --- Build Everything (@build_targets) --------------------------------------
pushd build
if "%raddbg%"=="1" set didbuild=1 && %compile% ..\src\raddbg\raddbg_main.c %compile_link% %out%raddbg.exe || exit /b 1
if "%rdi_from_pdb%"=="1" set didbuild=1 && %compile% ..\src\rdi_from_pdb\rdi_from_pdb_main.c %compile_link% %out%rdi_from_pdb.exe || exit /b 1
if "%rdi_from_dwarf%"=="1" set didbuild=1 && %compile% ..\src\rdi_from_dwarf\rdi_from_dwarf.c %compile_link% %out%rdi_from_dwarf.exe || exit /b 1
if "%rdi_dump%"=="1" set didbuild=1 && %compile% ..\src\rdi_dump\rdi_dump_main.c %compile_link% %out%rdi_dump.exe || exit /b 1
if "%rdi_breakpad_from_pdb%"=="1" set didbuild=1 && %compile% ..\src\rdi_breakpad_from_pdb\rdi_breakpad_from_pdb_main.c %compile_link% %out%rdi_breakpad_from_pdb.exe || exit /b 1
if "%ryan_scratch%"=="1" set didbuild=1 && %compile% ..\src\scratch\ryan_scratch.c %compile_link% %out%ryan_scratch.exe || exit /b 1
if "%cpp_tests%"=="1" set didbuild=1 && %compile% ..\src\scratch\i_hate_c_plus_plus.cpp %compile_link% %out%cpp_tests.exe || exit /b 1
if "%look_at_raddbg%"=="1" set didbuild=1 && %compile% ..\src\scratch\look_at_raddbg.c %compile_link% %out%look_at_raddbg.exe || exit /b 1
if "%mule_main%"=="1" set didbuild=1 && del vc*.pdb mule*.pdb && %compile_release% %only_compile% ..\src\mule\mule_inline.cpp && %compile_release% %only_compile% ..\src\mule\mule_o2.cpp && %compile_debug% %EHsc% ..\src\mule\mule_main.cpp ..\src\mule\mule_c.c mule_inline.obj mule_o2.obj %compile_link% %no_aslr% %out%mule_main.exe || exit /b 1
if "%mule_module%"=="1" set didbuild=1 && %compile% ..\src\mule\mule_module.cpp %compile_link% %link_dll% %out%mule_module.dll || exit /b 1
if "%mule_hotload%"=="1" set didbuild=1 && %compile% ..\src\mule\mule_hotload_main.c %compile_link% %out%mule_hotload.exe & %compile% ..\src\mule\mule_hotload_module_main.c %compile_link% %link_dll% %out%mule_hotload_module.dll || exit /b 1
if "%mule_peb_trample%"=="1" (
set didbuild=1
if exist mule_peb_trample.exe move mule_peb_trample.exe mule_peb_trample_old_%random%.exe
if exist mule_peb_trample_new.pdb move mule_peb_trample_new.pdb mule_peb_trample_old_%random%.pdb
if exist mule_peb_trample_new.rdi move mule_peb_trample_new.rdi mule_peb_trample_old_%random%.rdi
%compile% ..\src\mule\mule_peb_trample.c %compile_link% %out%mule_peb_trample_new.exe || exit /b 1
move mule_peb_trample_new.exe mule_peb_trample.exe
)
popd
:: --- Unset ------------------------------------------------------------------
for %%a in (%*) do set "%%a=0"
set raddbg=
set compile=
set compile_link=
set out=
set msvc=
set debug=
set release=
:: --- Warn On No Builds ------------------------------------------------------
if "%didbuild%"=="" (
echo [WARNING] no valid build target specified; must use build target names as arguments to this script, like `build raddbg` or `build rdi_from_pdb`.
exit /b 1
)
@echo off
setlocal
cd /D "%~dp0"
:: --- Usage Notes (2024/1/10) ------------------------------------------------
::
:: This is a central build script for the RAD Debugger project, for use in
:: Windows development environments. It takes a list of simple alphanumeric-
:: only arguments which control (a) what is built, (b) which compiler & linker
:: are used, and (c) extra high-level build options. By default, if no options
:: are passed, then the main "raddbg" graphical debugger is built.
::
:: Below is a non-exhaustive list of possible ways to use the script:
:: `build raddbg`
:: `build raddbg clang`
:: `build raddbg release`
:: `build raddbg asan telemetry`
:: `build rdi_from_pdb`
::
:: For a full list of possible build targets and their build command lines,
:: search for @build_targets in this file.
::
:: Below is a list of all possible non-target command line options:
::
:: - `asan`: enable address sanitizer
:: - `telemetry`: enable RAD telemetry profiling support
:: --- Unpack Arguments -------------------------------------------------------
for %%a in (%*) do set "%%a=1"
if not "%msvc%"=="1" if not "%clang%"=="1" set msvc=1
if not "%release%"=="1" set debug=1
if "%debug%"=="1" set release=0 && echo [debug mode]
if "%release%"=="1" set debug=0 && echo [release mode]
if "%msvc%"=="1" set clang=0 && echo [msvc compile]
if "%clang%"=="1" set msvc=0 && echo [clang compile]
if "%~1"=="" echo [default mode, assuming `raddbg` build] && set raddbg=1
if "%~1"=="release" if "%~2"=="" echo [default mode, assuming `raddbg` build] && set raddbg=1
:: --- Unpack Command Line Build Arguments ------------------------------------
set auto_compile_flags=
if "%telemetry%"=="1" set auto_compile_flags=%auto_compile_flags% -DPROFILE_TELEMETRY=1 && echo [telemetry profiling enabled]
if "%asan%"=="1" set auto_compile_flags=%auto_compile_flags% -fsanitize=address && echo [asan enabled]
:: --- Compile/Link Line Definitions ------------------------------------------
set cl_common= /I..\src\ /I..\local\ /nologo /FC /Z7
set clang_common= -I..\src\ -I..\local\ -gcodeview -fdiagnostics-absolute-paths -Wall -Wno-unknown-warning-option -Wno-missing-braces -Wno-unused-function -Wno-writable-strings -Wno-unused-value -Wno-unused-variable -Wno-unused-local-typedef -Wno-deprecated-register -Wno-deprecated-declarations -Wno-unused-but-set-variable -Wno-single-bit-bitfield-constant-conversion -Wno-compare-distinct-pointer-types -Wno-initializer-overrides -Wno-incompatible-pointer-types-discards-qualifiers -Xclang -flto-visibility-public-std -D_USE_MATH_DEFINES -Dstrdup=_strdup -Dgnu_printf=printf
set cl_debug= call cl /Od /Ob1 /DBUILD_DEBUG=1 %cl_common% %auto_compile_flags%
set cl_release= call cl /O2 /DBUILD_DEBUG=0 %cl_common% %auto_compile_flags%
set clang_debug= call clang -g -O0 -DBUILD_DEBUG=1 %clang_common% %auto_compile_flags%
set clang_release= call clang -g -O2 -DBUILD_DEBUG=0 %clang_common% %auto_compile_flags%
set cl_link= /link /MANIFEST:EMBED /INCREMENTAL:NO /natvis:"%~dp0\src\natvis\base.natvis" logo.res
set clang_link= -fuse-ld=lld -Xlinker /MANIFEST:EMBED -Xlinker /natvis:"%~dp0\src\natvis\base.natvis" logo.res
set cl_out= /out:
set clang_out= -o
:: --- Per-Build Settings -----------------------------------------------------
set link_dll=-DLL
if "%msvc%"=="1" set only_compile=/c
if "%clang%"=="1" set only_compile=-c
if "%msvc%"=="1" set EHsc=/EHsc
if "%clang%"=="1" set EHsc=
if "%msvc%"=="1" set no_aslr=/DYNAMICBASE:NO
if "%clang%"=="1" set no_aslr=-Wl,/DYNAMICBASE:NO
if "%msvc%"=="1" set rc=call rc
if "%clang%"=="1" set rc=call llvm-rc
:: --- Choose Compile/Link Lines ----------------------------------------------
if "%msvc%"=="1" set compile_debug=%cl_debug%
if "%msvc%"=="1" set compile_release=%cl_release%
if "%msvc%"=="1" set compile_link=%cl_link%
if "%msvc%"=="1" set out=%cl_out%
if "%clang%"=="1" set compile_debug=%clang_debug%
if "%clang%"=="1" set compile_release=%clang_release%
if "%clang%"=="1" set compile_link=%clang_link%
if "%clang%"=="1" set out=%clang_out%
if "%debug%"=="1" set compile=%compile_debug%
if "%release%"=="1" set compile=%compile_release%
:: --- Prep Directories -------------------------------------------------------
if not exist build mkdir build
if not exist local mkdir local
:: --- Produce Logo Icon File -------------------------------------------------
pushd build
%rc% /nologo /fo logo.res ..\data\logo.rc || exit /b 1
popd
:: --- Get Current Git Commit Id ----------------------------------------------
for /f %%i in ('call git describe --always --dirty') do set compile=%compile% -DBUILD_GIT_HASH=\"%%i\"
:: --- Build & Run Metaprogram ------------------------------------------------
if "%no_meta%"=="1" echo [skipping metagen]
if not "%no_meta%"=="1" (
pushd build
%compile_debug% ..\src\metagen\metagen_main.c %compile_link% %out%metagen.exe || exit /b 1
metagen.exe || exit /b 1
popd
)
:: --- Build Everything (@build_targets) --------------------------------------
pushd build
if "%raddbg%"=="1" set didbuild=1 && %compile% ..\src\raddbg\raddbg_main.c %compile_link% %out%raddbg.exe || exit /b 1
if "%rdi_from_pdb%"=="1" set didbuild=1 && %compile% ..\src\rdi_from_pdb\rdi_from_pdb_main.c %compile_link% %out%rdi_from_pdb.exe || exit /b 1
if "%rdi_from_dwarf%"=="1" set didbuild=1 && %compile% ..\src\rdi_from_dwarf\rdi_from_dwarf.c %compile_link% %out%rdi_from_dwarf.exe || exit /b 1
if "%rdi_dump%"=="1" set didbuild=1 && %compile% ..\src\rdi_dump\rdi_dump_main.c %compile_link% %out%rdi_dump.exe || exit /b 1
if "%rdi_breakpad_from_pdb%"=="1" set didbuild=1 && %compile% ..\src\rdi_breakpad_from_pdb\rdi_breakpad_from_pdb_main.c %compile_link% %out%rdi_breakpad_from_pdb.exe || exit /b 1
if "%ryan_scratch%"=="1" set didbuild=1 && %compile% ..\src\scratch\ryan_scratch.c %compile_link% %out%ryan_scratch.exe || exit /b 1
if "%cpp_tests%"=="1" set didbuild=1 && %compile% ..\src\scratch\i_hate_c_plus_plus.cpp %compile_link% %out%cpp_tests.exe || exit /b 1
if "%look_at_raddbg%"=="1" set didbuild=1 && %compile% ..\src\scratch\look_at_raddbg.c %compile_link% %out%look_at_raddbg.exe || exit /b 1
if "%mule_main%"=="1" set didbuild=1 && del vc*.pdb mule*.pdb && %compile_release% %only_compile% ..\src\mule\mule_inline.cpp && %compile_release% %only_compile% ..\src\mule\mule_o2.cpp && %compile_debug% %EHsc% ..\src\mule\mule_main.cpp ..\src\mule\mule_c.c mule_inline.obj mule_o2.obj %compile_link% %no_aslr% %out%mule_main.exe || exit /b 1
if "%mule_module%"=="1" set didbuild=1 && %compile% ..\src\mule\mule_module.cpp %compile_link% %link_dll% %out%mule_module.dll || exit /b 1
if "%mule_hotload%"=="1" set didbuild=1 && %compile% ..\src\mule\mule_hotload_main.c %compile_link% %out%mule_hotload.exe & %compile% ..\src\mule\mule_hotload_module_main.c %compile_link% %link_dll% %out%mule_hotload_module.dll || exit /b 1
if "%mule_peb_trample%"=="1" (
set didbuild=1
if exist mule_peb_trample.exe move mule_peb_trample.exe mule_peb_trample_old_%random%.exe
if exist mule_peb_trample_new.pdb move mule_peb_trample_new.pdb mule_peb_trample_old_%random%.pdb
if exist mule_peb_trample_new.rdi move mule_peb_trample_new.rdi mule_peb_trample_old_%random%.rdi
%compile% ..\src\mule\mule_peb_trample.c %compile_link% %out%mule_peb_trample_new.exe || exit /b 1
move mule_peb_trample_new.exe mule_peb_trample.exe
)
popd
:: --- Unset ------------------------------------------------------------------
for %%a in (%*) do set "%%a=0"
set raddbg=
set compile=
set compile_link=
set out=
set msvc=
set debug=
set release=
:: --- Warn On No Builds ------------------------------------------------------
if "%didbuild%"=="" (
echo [WARNING] no valid build target specified; must use build target names as arguments to this script, like `build raddbg` or `build rdi_from_pdb`.
exit /b 1
)
+57
View File
@@ -0,0 +1,57 @@
#!/bin/bash
# --- Unpack Arguments --------------------------------------------------------
for arg in "$@"; do declare $arg='1'; done
if [ ! "$gcc" = "1" ]; then clang=1; fi
if [ ! "$release" = "1" ]; then debug=1; fi
if [ "$debug" = "1" ]; then release=0 && echo "[debug mode]"; fi
if [ "$release" = "1" ]; then debug=0 && echo "[release mode]"; fi
if [ "$clang" = "1" ]; then gcc=0 && echo "[clang compile]"; fi
if [ "$gcc" = "1" ]; then clang=0 && echo "[gcc compile]"; fi
# --- Unpack Command Line Build Arguments -------------------------------------
auto_compile_flags=''
# --- Compile/Link Line Definitions -------------------------------------------
clang_common='-I../src/ -I../local/ -gcodeview -fdiagnostics-absolute-paths -Wall -Wno-unknown-warning-option -Wno-missing-braces -Wno-unused-function -Wno-writable-strings -Wno-unused-value -Wno-unused-variable -Wno-unused-local-typedef -Wno-deprecated-register -Wno-deprecated-declarations -Wno-unused-but-set-variable -Wno-single-bit-bitfield-constant-conversion -Wno-compare-distinct-pointer-types -Wno-initializer-overrides -Wno-incompatible-pointer-types-discards-qualifiers -Xclang -flto-visibility-public-std -D_USE_MATH_DEFINES -Dstrdup=_strdup -Dgnu_printf=printf'
clang_debug="clang -g -O0 -DBUILD_DEBUG=1 ${clang_common} ${auto_compile_flags}"
clang_release="clang -g -O2 -DBUILD_DEBUG=0 ${clang_common} ${auto_compile_flags}"
clang_link=""
clang_out="-o"
# --- Per-Build Settings ------------------------------------------------------
link_dll="-fPIC"
# --- Choose Compile/Link Lines -----------------------------------------------
if [ "$clang" = "1" ]; then compile_debug="$clang_debug"; fi
if [ "$clang" = "1" ]; then compile_release="$clang_release"; fi
if [ "$clang" = "1" ]; then compile_link="$clang_link"; fi
if [ "$clang" = "1" ]; then out="$clang_out"; fi
if [ "$debug" = "1" ]; then compile="$compile_debug"; fi
if [ "$release" = "1" ]; then compile="$compile_release"; fi
# --- Prep Directories --------------------------------------------------------
if [ ! -d build ]; then mkdir build; fi
if [ ! -d local ]; then mkdir local; fi
# --- Build & Run Metaprogram -------------------------------------------------
if [ "$no_meta" = "1" ]; then echo "[skipping metagen]"; fi
if [ "$no_meta" = "" ]
then
cd build
$compile_debug ../src/metagen/metagen_main.c $compile_link $out metagen || exit 1
metagen || exit 1
cd ..
fi
# --- Build Everything (@build_targets) ---------------------------------------
cd build
if [ "$raddbg" = "1" ]; then didbuild=1 && $compile ../src/raddbg/raddbg_main.c $compile_link $out raddbg || exit 1; fi
cd ..
# --- Warn On No Builds -------------------------------------------------------
#if [ "$didbuild" = "" ]
#then
# echo "[WARNING] no valid build target specified; must use build target names as arguments to this script, like `./build.sh raddbg` or `./build.sh rdi_from_pdb`."
# exit 1
#fi
+195 -302
View File
@@ -1,302 +1,195 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
////////////////////////////////
// Implementation
internal Arena *
arena_alloc__sized(U64 init_res, U64 init_cmt)
{
ProfBeginFunction();
Assert(ARENA_HEADER_SIZE < init_cmt && init_cmt <= init_res);
void *memory = 0;
U64 res = 0;
U64 cmt = 0;
B32 large_pages = os_large_pages_enabled();
if(large_pages)
{
U64 page_size = os_large_page_size();
res = AlignPow2(init_res, page_size);
#if OS_WINDOWS
cmt = res;
#else
cmt = AlignPow2(init_cmt, page_size);
#endif
memory = os_reserve_large(res);
if(!os_commit_large(memory, cmt))
{
memory = 0;
os_release(memory, res);
}
}
else
{
U64 page_size = os_page_size();
res = AlignPow2(init_res, page_size);
cmt = AlignPow2(init_cmt, page_size);
memory = os_reserve(res);
if(!os_commit(memory, cmt))
{
memory = 0;
os_release(memory, res);
}
}
Arena *arena = (Arena*)memory;
if(arena)
{
AsanPoisonMemoryRegion(memory, cmt);
AsanUnpoisonMemoryRegion(memory, ARENA_HEADER_SIZE);
arena->prev = 0;
arena->current = arena;
arena->base_pos = 0;
arena->pos = ARENA_HEADER_SIZE;
arena->cmt = cmt;
arena->res = res;
arena->align = 8;
arena->grow = 1;
arena->large_pages = large_pages;
}
ProfEnd();
return arena;
}
internal Arena *
arena_alloc(void)
{
ProfBeginFunction();
U64 init_res, init_cmt;
if (os_large_pages_enabled()) {
init_res = ARENA_RESERVE_SIZE_LARGE_PAGES;
init_cmt = ARENA_COMMIT_SIZE_LARGE_PAGES;
} else {
init_res = ARENA_RESERVE_SIZE;
init_cmt = ARENA_COMMIT_SIZE;
}
Arena *arena = arena_alloc__sized(init_res, init_cmt);
ProfEnd();
return arena;
}
internal void
arena_release(Arena *arena)
{
for (Arena *node = arena->current, *prev = 0; node != 0; node = prev) {
prev = node->prev;
os_release(node, node->res);
}
}
internal U64
arena_huge_push_threshold(void)
{
U64 reserve_size = os_large_pages_enabled() ? ARENA_RESERVE_SIZE_LARGE_PAGES : ARENA_RESERVE_SIZE;
U64 threshold = (reserve_size - ARENA_HEADER_SIZE) / 2 + 1;
return threshold;
}
internal void *
arena_push__impl(Arena *arena, U64 size)
{
Arena *current = arena->current;
U64 pos_mem = AlignPow2(current->pos, arena->align);
U64 pos_new = pos_mem + size;
if (current->res < pos_new && arena->grow) {
Arena *new_block;
// normal growth path
if (size < arena_huge_push_threshold()) {
new_block = arena_alloc();
}
// huge growth path
else {
U64 new_block_size = size + ARENA_HEADER_SIZE;
new_block = arena_alloc__sized(new_block_size, new_block_size);
}
if (new_block) {
new_block->base_pos = current->base_pos + current->res;
SLLStackPush_N(arena->current, new_block, prev);
current = new_block;
pos_mem = AlignPow2(current->pos, current->align);
pos_new = pos_mem + size;
}
}
if (current->cmt < pos_new) {
U64 cmt_new_aligned, cmt_new_clamped, cmt_new_size;
B32 is_cmt_ok;
if (current->large_pages) {
cmt_new_aligned = AlignPow2(pos_new, ARENA_COMMIT_SIZE_LARGE_PAGES);
cmt_new_clamped = ClampTop(cmt_new_aligned, current->res);
cmt_new_size = cmt_new_clamped - current->cmt;
is_cmt_ok = os_commit_large((U8*)current + current->cmt, cmt_new_size);
} else {
cmt_new_aligned = AlignPow2(pos_new, ARENA_COMMIT_SIZE);
cmt_new_clamped = ClampTop(cmt_new_aligned, current->res);
cmt_new_size = cmt_new_clamped - current->cmt;
is_cmt_ok = os_commit((U8*)current + current->cmt, cmt_new_size);
}
if (is_cmt_ok) {
current->cmt = cmt_new_clamped;
}
}
void *memory = 0;
if (current->cmt >= pos_new) {
memory = (U8*)current + pos_mem;
current->pos = pos_new;
AsanUnpoisonMemoryRegion(memory, size);
}
#if OS_FEATURE_GRAPHICAL
if(Unlikely(memory == 0))
{
os_graphical_message(1, str8_lit("Fatal Allocation Failure"), str8_lit("Unexpected memory allocation failure."));
os_exit_process(1);
}
#endif
return memory;
}
internal U64
arena_pos(Arena *arena)
{
Arena *current = arena->current;
U64 pos = current->base_pos + current->pos;
return pos;
}
internal void
arena_pop_to(Arena *arena, U64 big_pos_unclamped)
{
U64 big_pos = ClampBot(ARENA_HEADER_SIZE, big_pos_unclamped);
// unroll the chain
Arena *current = arena->current;
for (Arena *prev = 0; current->base_pos >= big_pos; current = prev) {
prev = current->prev;
os_release(current, current->res);
}
AssertAlways(current);
arena->current = current;
// compute arena-relative position
U64 new_pos = big_pos - current->base_pos;
AssertAlways(new_pos <= current->pos);
// poison popped memory block
AsanPoisonMemoryRegion((U8*)current + new_pos, (current->pos - new_pos));
// update position
current->pos = new_pos;
}
internal void
arena_absorb(Arena *arena, Arena *sub)
{
// base adjustment
Arena *current = arena->current;
U64 base_adjust = current->base_pos + current->res;
for (Arena *node = sub->current; node != 0; node = node->prev) {
node->base_pos += base_adjust;
}
// attach sub to arena
sub->prev = arena->current;
arena->current = sub->current;
sub->current = sub;
}
////////////////////////////////
// Wrappers
internal void *
arena_push(Arena *arena, U64 size)
{
void *memory = arena_push__impl(arena, size);
return memory;
}
internal void *
arena_push_contiguous(Arena *arena, U64 size)
{
B32 restore = arena->grow;
arena->grow = 0;
void *memory = arena_push(arena, size);
arena->grow = restore;
return memory;
}
internal void
arena_push_align(Arena *arena, U64 align)
{
Assert(IsPow2(align));
U64 amt = AlignPadPow2(arena->pos, align);
void *ptr = arena_push(arena, amt);
MemoryZero(ptr, amt);
}
internal void
arena_put_back(Arena *arena, U64 amt)
{
U64 pos_old = arena_pos(arena);
U64 pos_new = pos_old;
if (amt < pos_old) {
pos_new = pos_old - amt;
}
arena_pop_to(arena, pos_new);
}
internal void
arena_clear(Arena *arena)
{
arena_pop_to(arena, 0);
}
internal Temp
temp_begin(Arena *arena)
{
U64 pos = arena_pos(arena);
Temp temp = {arena, pos};
return temp;
}
internal void
temp_end(Temp temp)
{
arena_pop_to(temp.arena, temp.pos);
}
////////////////////////////////
//~ NOTE(allen): "Mini-Arena" Helper
internal B32
ensure_commit(void **cmtptr, void *pos, U64 cmt_block_size){
B32 result = 0;
U8 *cmt = (U8*)*cmtptr;
if (cmt < (U8*)pos){
U64 cmt_size_raw = (U8*)pos - cmt;
U64 cmt_size = AlignPow2(cmt_size_raw, cmt_block_size);
if (os_commit(cmt, cmt_size)){
*cmtptr = cmt + cmt_size;
result = 1;
}
}
else{
result = 1;
}
return(result);
}
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
////////////////////////////////
//~ rjf: Arena Functions
//- rjf: arena creation/destruction
internal Arena *
arena_alloc_(ArenaParams *params)
{
// rjf: round up reserve/commit sizes
U64 reserve_size = params->reserve_size;
U64 commit_size = params->commit_size;
if(params->flags & ArenaFlag_LargePages)
{
reserve_size = AlignPow2(reserve_size, os_get_system_info()->large_page_size);
commit_size = AlignPow2(commit_size, os_get_system_info()->large_page_size);
}
else
{
reserve_size = AlignPow2(reserve_size, os_get_system_info()->page_size);
commit_size = AlignPow2(commit_size, os_get_system_info()->page_size);
}
// rjf: reserve/commit initial block
void *base = params->optional_backing_buffer;
if(base == 0)
{
if(params->flags & ArenaFlag_LargePages)
{
base = os_reserve_large(reserve_size);
os_commit_large(base, commit_size);
}
else
{
base = os_reserve(reserve_size);
os_commit(base, commit_size);
}
}
// rjf: panic on arena creation failure
#if OS_FEATURE_GRAPHICAL
if(Unlikely(base == 0))
{
os_graphical_message(1, str8_lit("Fatal Allocation Failure"), str8_lit("Unexpected memory allocation failure."));
os_abort(1);
}
#endif
// rjf: extract arena header & fill
Arena *arena = (Arena *)base;
arena->current = arena;
arena->flags = params->flags;
arena->cmt_size = (U32)params->commit_size;
arena->res_size = params->reserve_size;
arena->base_pos = 0;
arena->pos = ARENA_HEADER_SIZE;
arena->cmt = commit_size;
arena->res = reserve_size;
AsanPoisonMemoryRegion(base, commit_size);
AsanUnpoisonMemoryRegion(base, ARENA_HEADER_SIZE);
return arena;
}
internal void
arena_release(Arena *arena)
{
for(Arena *n = arena->current, *prev = 0; n != 0; n = prev)
{
prev = n->prev;
os_release(n, n->res);
}
}
//- rjf: arena push/pop core functions
internal void *
arena_push(Arena *arena, U64 size, U64 align)
{
Arena *current = arena->current;
U64 pos_pre = AlignPow2(current->pos, align);
U64 pos_pst = pos_pre + size;
// rjf: chain, if needed
if(current->res < pos_pst && !(arena->flags & ArenaFlag_NoChain))
{
U64 res_size = current->res_size;
U64 cmt_size = current->cmt_size;
if(size > cmt_size)
{
res_size = size + ARENA_HEADER_SIZE;
cmt_size = size + ARENA_HEADER_SIZE;
}
Arena *new_block = arena_alloc(.reserve_size = res_size,
.commit_size = cmt_size,
.flags = current->flags);
new_block->base_pos = current->base_pos + current->res;
SLLStackPush_N(arena->current, new_block, prev);
current = new_block;
pos_pre = AlignPow2(current->pos, align);
pos_pst = pos_pst + size;
}
// rjf: commit new pages, if needed
if(current->cmt < pos_pst && !(current->flags & ArenaFlag_LargePages))
{
U64 cmt_pst_aligned = AlignPow2(pos_pst, current->cmt_size);
U64 cmt_pst_clamped = ClampTop(cmt_pst_aligned, current->res);
U64 cmt_size = cmt_pst_clamped - current->cmt;
os_commit((U8 *)current + current->cmt, cmt_size);
current->cmt = cmt_pst_clamped;
}
// rjf: push onto current block
void *result = 0;
if(current->cmt >= pos_pst)
{
result = (U8 *)current+pos_pre;
current->pos = pos_pst;
AsanUnpoisonMemoryRegion(result, size);
}
// rjf: panic on failure
#if OS_FEATURE_GRAPHICAL
if(Unlikely(result == 0))
{
os_graphical_message(1, str8_lit("Fatal Allocation Failure"), str8_lit("Unexpected memory allocation failure."));
os_abort(1);
}
#endif
return result;
}
internal U64
arena_pos(Arena *arena)
{
Arena *current = arena->current;
U64 pos = current->base_pos + current->pos;
return pos;
}
internal void
arena_pop_to(Arena *arena, U64 pos)
{
U64 big_pos = ClampBot(ARENA_HEADER_SIZE, pos);
Arena *current = arena->current;
for(Arena *prev = 0; current->base_pos >= big_pos; current = prev)
{
prev = current->prev;
os_release(current, current->res);
}
arena->current = current;
U64 new_pos = big_pos - current->base_pos;
AssertAlways(new_pos <= current->pos);
AsanPoisonMemoryRegion((U8*)current + new_pos, (current->pos - new_pos));
current->pos = new_pos;
}
//- rjf: arena push/pop helpers
internal void
arena_clear(Arena *arena)
{
arena_pop_to(arena, 0);
}
internal void
arena_pop(Arena *arena, U64 amt)
{
U64 pos_old = arena_pos(arena);
U64 pos_new = pos_old;
if(amt < pos_old)
{
pos_new = pos_old - amt;
}
arena_pop_to(arena, pos_new);
}
//- rjf: temporary arena scopes
internal Temp
temp_begin(Arena *arena)
{
U64 pos = arena_pos(arena);
Temp temp = {arena, pos};
return temp;
}
internal void
temp_end(Temp temp)
{
arena_pop_to(temp.arena, temp.pos);
}
+80 -87
View File
@@ -1,87 +1,80 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#ifndef BASE_ARENA_H
#define BASE_ARENA_H
////////////////////////////////
//~ rjf: Constants
#define ARENA_HEADER_SIZE 128
#ifndef ARENA_RESERVE_SIZE
# define ARENA_RESERVE_SIZE MB(64)
#endif
#ifndef ARENA_COMMIT_SIZE
# define ARENA_COMMIT_SIZE KB(64)
#endif
#ifndef ARENA_RESERVE_SIZE_LARGE_PAGES
# define ARENA_RESERVE_SIZE_LARGE_PAGES MB(8)
#endif
#ifndef ARENA_COMMIT_SIZE_LARGE_PAGES
# define ARENA_COMMIT_SIZE_LARGE_PAGES MB(2)
#endif
////////////////////////////////
//~ rjf: Arena Types
typedef struct Arena Arena;
struct Arena
{
struct Arena *prev;
struct Arena *current;
U64 base_pos;
U64 pos;
U64 cmt;
U64 res;
U64 align;
B8 grow;
B8 large_pages;
};
typedef struct Temp Temp;
struct Temp
{
Arena *arena;
U64 pos;
};
////////////////////////////////
// Implementation
internal Arena* arena_alloc__sized(U64 init_res, U64 init_cmt);
internal Arena* arena_alloc(void);
internal void arena_release(Arena *arena);
internal void* arena_push__impl(Arena *arena, U64 size);
internal U64 arena_pos(Arena *arena);
internal void arena_pop_to(Arena *arena, U64 pos);
internal void arena_absorb(Arena *arena, Arena *sub);
////////////////////////////////
// Wrappers
internal void* arena_push(Arena *arena, U64 size);
internal void* arena_push_contiguous(Arena *arena, U64 size);
internal void arena_clear(Arena *arena);
internal void arena_push_align(Arena *arena, U64 align);
internal void arena_put_back(Arena *arena, U64 amt);
internal Temp temp_begin(Arena *arena);
internal void temp_end(Temp temp);
////////////////////////////////
//~ NOTE(allen): "Mini-Arena" Helper
internal B32 ensure_commit(void **cmt, void *pos, U64 cmt_block_size);
////////////////////////////////
//~ NOTE(allen): Main API Macros
#define push_array_no_zero(a,T,c) (T*)arena_push((a), sizeof(T)*(c))
#define push_array(a,T,c) (T*)MemoryZero(push_array_no_zero(a,T,c), sizeof(T)*(c))
#endif // BASE_ARENA_H
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#ifndef BASE_ARENA_H
#define BASE_ARENA_H
////////////////////////////////
//~ rjf: Constants
#define ARENA_HEADER_SIZE 64
////////////////////////////////
//~ rjf: Types
typedef U32 ArenaFlags;
enum
{
ArenaFlag_NoChain = (1<<0),
ArenaFlag_LargePages = (1<<1),
};
typedef struct ArenaParams ArenaParams;
struct ArenaParams
{
ArenaFlags flags;
U64 reserve_size;
U64 commit_size;
void *optional_backing_buffer;
};
typedef struct Arena Arena;
struct Arena
{
Arena *prev; // previous arena in chain
Arena *current; // current arena in chain
ArenaFlags flags;
U32 cmt_size;
U64 res_size;
U64 base_pos;
U64 pos;
U64 cmt;
U64 res;
};
StaticAssert(sizeof(Arena) <= ARENA_HEADER_SIZE, arena_header_size_check);
typedef struct Temp Temp;
struct Temp
{
Arena *arena;
U64 pos;
};
////////////////////////////////
//~ rjf: Arena Functions
//- rjf: arena creation/destruction
internal Arena *arena_alloc_(ArenaParams *params);
#define arena_alloc(...) arena_alloc_(&(ArenaParams){.reserve_size = MB(64), .commit_size = KB(64), __VA_ARGS__})
internal void arena_release(Arena *arena);
//- rjf: arena push/pop/pos core functions
internal void *arena_push(Arena *arena, U64 size, U64 align);
internal U64 arena_pos(Arena *arena);
internal void arena_pop_to(Arena *arena, U64 pos);
//- rjf: arena push/pop helpers
internal void arena_clear(Arena *arena);
internal void arena_pop(Arena *arena, U64 amt);
//- rjf: temporary arena scopes
internal Temp temp_begin(Arena *arena);
internal void temp_end(Temp temp);
//- rjf: push helper macros
#define push_array_no_zero_aligned(a, T, c, align) (T *)arena_push((a), sizeof(T)*(c), (align))
#define push_array_aligned(a, T, c, align) (T *)MemoryZero(push_array_no_zero_aligned(a, T, c, align), sizeof(T)*(c))
#define push_array_no_zero(a, T, c) push_array_no_zero_aligned(a, T, c, Max(8, AlignOf(T)))
#define push_array(a, T, c) push_array_aligned(a, T, c, Max(8, AlignOf(T)))
#endif // BASE_ARENA_H
+801 -787
View File
File diff suppressed because it is too large Load Diff
+92 -95
View File
@@ -1,95 +1,92 @@
internal void
main_thread_base_entry_point(void (*entry_point)(CmdLine *cmdline), char **arguments, U64 arguments_count)
{
#if PROFILE_TELEMETRY
local_persist U8 tm_data[MB(64)];
tmLoadLibrary(TM_RELEASE);
tmSetMaxThreadCount(256);
tmInitialize(sizeof(tm_data), (char *)tm_data);
#endif
TCTX tctx;
tctx_init_and_equip(&tctx);
ThreadNameF("[main thread]");
Temp scratch = scratch_begin(0, 0);
String8List command_line_argument_strings = os_string_list_from_argcv(scratch.arena, (int)arguments_count, arguments);
CmdLine cmdline = cmd_line_from_string_list(scratch.arena, command_line_argument_strings);
B32 capture = cmd_line_has_flag(&cmdline, str8_lit("capture"));
if(capture)
{
ProfBeginCapture(arguments[0]);
}
#if defined(OS_CORE_H) && !defined(OS_INIT_MANUAL)
os_init();
#endif
#if defined(TASK_SYSTEM_H) && !defined(TS_INIT_MANUAL)
ts_init();
#endif
#if defined(HASH_STORE_H) && !defined(HS_INIT_MANUAL)
hs_init();
#endif
#if defined(FILE_STREAM_H) && !defined(FS_INIT_MANUAL)
fs_init();
#endif
#if defined(TEXT_CACHE_H) && !defined(TXT_INIT_MANUAL)
txt_init();
#endif
#if defined(MUTABLE_TEXT_H) && !defined(MTX_INIT_MANUAL)
mtx_init();
#endif
#if defined(DASM_CACHE_H) && !defined(DASM_INIT_MANUAL)
dasm_init();
#endif
#if defined(DI_H) && !defined(DI_INIT_MANUAL)
di_init();
#endif
#if defined(FUZZY_SEARCH_H) && !defined(FZY_INIT_MANUAL)
fzy_init();
#endif
#if defined(DEMON_CORE_H) && !defined(DMN_INIT_MANUAL)
dmn_init();
#endif
#if defined(CTRL_CORE_H) && !defined(CTRL_INIT_MANUAL)
ctrl_init();
#endif
#if defined(OS_GRAPHICAL_H) && !defined(OS_GFX_INIT_MANUAL)
os_graphical_init();
#endif
#if defined(FONT_PROVIDER_H) && !defined(FP_INIT_MANUAL)
fp_init();
#endif
#if defined(RENDER_CORE_H) && !defined(R_INIT_MANUAL)
r_init(&cmdline);
#endif
#if defined(TEXTURE_CACHE_H) && !defined(TEX_INIT_MANUAL)
tex_init();
#endif
#if defined(GEO_CACHE_H) && !defined(GEO_INIT_MANUAL)
geo_init();
#endif
#if defined(FONT_CACHE_H) && !defined(F_INIT_MANUAL)
f_init();
#endif
#if defined(DF_CORE_H) && !defined(DF_INIT_MANUAL)
DF_StateDeltaHistory *hist = df_state_delta_history_alloc();
df_core_init(&cmdline, hist);
#endif
#if defined(DF_GFX_H) && !defined(DF_GFX_INIT_MANUAL)
df_gfx_init(update_and_render, df_state_delta_history());
#endif
entry_point(&cmdline);
if(capture)
{
ProfEndCapture();
}
scratch_end(scratch);
tctx_release();
}
internal void
supplement_thread_base_entry_point(void (*entry_point)(void *params), void *params)
{
TCTX tctx;
tctx_init_and_equip(&tctx);
entry_point(params);
tctx_release();
}
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
internal void
main_thread_base_entry_point(void (*entry_point)(CmdLine *cmdline), char **arguments, U64 arguments_count)
{
#if PROFILE_TELEMETRY
local_persist U8 tm_data[MB(64)];
tmLoadLibrary(TM_RELEASE);
tmSetMaxThreadCount(256);
tmInitialize(sizeof(tm_data), (char *)tm_data);
#endif
ThreadNameF("[main thread]");
Temp scratch = scratch_begin(0, 0);
String8List command_line_argument_strings = os_string_list_from_argcv(scratch.arena, (int)arguments_count, arguments);
CmdLine cmdline = cmd_line_from_string_list(scratch.arena, command_line_argument_strings);
B32 capture = cmd_line_has_flag(&cmdline, str8_lit("capture"));
if(capture)
{
ProfBeginCapture(arguments[0]);
}
#if defined(TASK_SYSTEM_H) && !defined(TS_INIT_MANUAL)
ts_init();
#endif
#if defined(HASH_STORE_H) && !defined(HS_INIT_MANUAL)
hs_init();
#endif
#if defined(FILE_STREAM_H) && !defined(FS_INIT_MANUAL)
fs_init();
#endif
#if defined(TEXT_CACHE_H) && !defined(TXT_INIT_MANUAL)
txt_init();
#endif
#if defined(MUTABLE_TEXT_H) && !defined(MTX_INIT_MANUAL)
mtx_init();
#endif
#if defined(DASM_CACHE_H) && !defined(DASM_INIT_MANUAL)
dasm_init();
#endif
#if defined(DI_H) && !defined(DI_INIT_MANUAL)
di_init();
#endif
#if defined(FUZZY_SEARCH_H) && !defined(FZY_INIT_MANUAL)
fzy_init();
#endif
#if defined(DEMON_CORE_H) && !defined(DMN_INIT_MANUAL)
dmn_init();
#endif
#if defined(CTRL_CORE_H) && !defined(CTRL_INIT_MANUAL)
ctrl_init();
#endif
#if defined(OS_GRAPHICAL_H) && !defined(OS_GFX_INIT_MANUAL)
os_gfx_init();
#endif
#if defined(FONT_PROVIDER_H) && !defined(FP_INIT_MANUAL)
fp_init();
#endif
#if defined(RENDER_CORE_H) && !defined(R_INIT_MANUAL)
r_init(&cmdline);
#endif
#if defined(TEXTURE_CACHE_H) && !defined(TEX_INIT_MANUAL)
tex_init();
#endif
#if defined(GEO_CACHE_H) && !defined(GEO_INIT_MANUAL)
geo_init();
#endif
#if defined(FONT_CACHE_H) && !defined(F_INIT_MANUAL)
f_init();
#endif
#if defined(DF_CORE_H) && !defined(DF_INIT_MANUAL)
DF_StateDeltaHistory *hist = df_state_delta_history_alloc();
df_core_init(&cmdline, hist);
#endif
#if defined(DF_GFX_H) && !defined(DF_GFX_INIT_MANUAL)
df_gfx_init(update_and_render, df_state_delta_history());
#endif
entry_point(&cmdline);
if(capture)
{
ProfEndCapture();
}
scratch_end(scratch);
}
internal void
supplement_thread_base_entry_point(void (*entry_point)(void *params), void *params)
{
TCTX tctx;
tctx_init_and_equip(&tctx);
entry_point(params);
tctx_release();
}
+1975 -1975
View File
File diff suppressed because it is too large Load Diff
+41 -41
View File
@@ -1,41 +1,41 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#ifndef BASE_THREAD_CONTEXT_H
#define BASE_THREAD_CONTEXT_H
////////////////////////////////
// NOTE(allen): Thread Context
typedef struct TCTX TCTX;
struct TCTX
{
Arena *arenas[2];
U8 thread_name[32];
U64 thread_name_size;
char *file_name;
U64 line_number;
};
////////////////////////////////
// NOTE(allen): Thread Context Functions
internal void tctx_init_and_equip(TCTX *tctx);
internal void tctx_release(void);
internal TCTX* tctx_get_equipped(void);
internal Arena* tctx_get_scratch(Arena **conflicts, U64 count);
internal void tctx_set_thread_name(String8 name);
internal String8 tctx_get_thread_name(void);
internal void tctx_write_srcloc(char *file_name, U64 line_number);
internal void tctx_read_srcloc(char **file_name, U64 *line_number);
#define tctx_write_this_srcloc() tctx_write_srcloc(__FILE__, __LINE__)
#define scratch_begin(conflicts, count) temp_begin(tctx_get_scratch((conflicts), (count)))
#define scratch_end(scratch) temp_end(scratch)
#endif //BASE_THREAD_CONTEXT_H
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#ifndef BASE_THREAD_CONTEXT_H
#define BASE_THREAD_CONTEXT_H
////////////////////////////////
// NOTE(allen): Thread Context
typedef struct TCTX TCTX;
struct TCTX
{
Arena *arenas[2];
U8 thread_name[32];
U64 thread_name_size;
char *file_name;
U64 line_number;
};
////////////////////////////////
// NOTE(allen): Thread Context Functions
internal void tctx_init_and_equip(TCTX *tctx);
internal void tctx_release(void);
internal TCTX* tctx_get_equipped(void);
internal Arena* tctx_get_scratch(Arena **conflicts, U64 count);
internal void tctx_set_thread_name(String8 name);
internal String8 tctx_get_thread_name(void);
internal void tctx_write_srcloc(char *file_name, U64 line_number);
internal void tctx_read_srcloc(char **file_name, U64 *line_number);
#define tctx_write_this_srcloc() tctx_write_srcloc(__FILE__, __LINE__)
#define scratch_begin(conflicts, count) temp_begin(tctx_get_scratch((conflicts), (count)))
#define scratch_end(scratch) temp_end(scratch)
#endif // BASE_THREAD_CONTEXT_H
+637 -638
View File
File diff suppressed because it is too large Load Diff
+3017 -3017
View File
File diff suppressed because it is too large Load Diff
+1119 -1119
View File
File diff suppressed because it is too large Load Diff
+5296 -5296
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+883 -883
View File
File diff suppressed because it is too large Load Diff
+242 -242
View File
@@ -1,242 +1,242 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#ifndef DEMON_CORE_H
#define DEMON_CORE_H
////////////////////////////////
//~ rjf: Control-Thread-Only Context
//
// An instance of this struct must ONLY be returned by dmn_ctrl_begin, and only
// used by the thread which called it. All APIs which can ONLY run on the
// control thread, which blocks to control & receive events, will take this
// parameter. All other APIs can be called from any thread.
typedef struct DMN_CtrlCtx DMN_CtrlCtx;
struct DMN_CtrlCtx
{
U64 u64[1];
};
////////////////////////////////
//~ rjf: Handle Types
typedef union DMN_Handle DMN_Handle;
union DMN_Handle
{
U32 u32[2];
U64 u64[1];
};
typedef struct DMN_HandleNode DMN_HandleNode;
struct DMN_HandleNode
{
DMN_HandleNode *next;
DMN_Handle v;
};
typedef struct DMN_HandleList DMN_HandleList;
struct DMN_HandleList
{
DMN_HandleNode *first;
DMN_HandleNode *last;
U64 count;
};
typedef struct DMN_HandleArray DMN_HandleArray;
struct DMN_HandleArray
{
DMN_Handle *handles;
U64 count;
};
////////////////////////////////
//~ rjf: Generated Code
#include "generated/demon.meta.h"
////////////////////////////////
//~ rjf: Event Types
typedef struct DMN_Event DMN_Event;
struct DMN_Event
{
DMN_EventKind kind;
DMN_ErrorKind error_kind;
DMN_MemoryEventKind memory_kind;
DMN_ExceptionKind exception_kind;
DMN_Handle process;
DMN_Handle thread;
DMN_Handle module;
Architecture arch;
U64 address;
U64 size;
String8 string;
U32 code; // code gives pid & tid on CreateProcess and CreateThread (respectfully)
U32 flags;
S32 signo;
S32 sigcode;
U64 instruction_pointer;
U64 stack_pointer;
U64 user_data;
B32 exception_repeated;
};
typedef struct DMN_EventNode DMN_EventNode;
struct DMN_EventNode
{
DMN_EventNode *next;
DMN_Event v;
};
typedef struct DMN_EventList DMN_EventList;
struct DMN_EventList
{
DMN_EventNode *first;
DMN_EventNode *last;
U64 count;
};
////////////////////////////////
//~ rjf: Run Control Types
typedef struct DMN_Trap DMN_Trap;
struct DMN_Trap
{
DMN_Handle process;
U64 vaddr;
U64 id;
};
typedef struct DMN_TrapChunkNode DMN_TrapChunkNode;
struct DMN_TrapChunkNode
{
DMN_TrapChunkNode *next;
DMN_Trap *v;
U64 cap;
U64 count;
};
typedef struct DMN_TrapChunkList DMN_TrapChunkList;
struct DMN_TrapChunkList
{
DMN_TrapChunkNode *first;
DMN_TrapChunkNode *last;
U64 node_count;
U64 trap_count;
};
typedef struct DMN_RunCtrls DMN_RunCtrls;
struct DMN_RunCtrls
{
DMN_Handle single_step_thread;
B8 ignore_previous_exception;
B8 run_entities_are_unfrozen;
B8 run_entities_are_processes;
DMN_Handle *run_entities;
U64 run_entity_count;
DMN_TrapChunkList traps;
};
////////////////////////////////
//~ rjf: System Process Listing Types
typedef struct DMN_ProcessIter DMN_ProcessIter;
struct DMN_ProcessIter
{
U64 v[2];
};
typedef struct DMN_ProcessInfo DMN_ProcessInfo;
struct DMN_ProcessInfo
{
String8 name;
U32 pid;
};
////////////////////////////////
//~ rjf: Basic Type Functions (Helpers, Implemented Once)
//- rjf: handles
internal DMN_Handle dmn_handle_zero(void);
internal B32 dmn_handle_match(DMN_Handle a, DMN_Handle b);
//- rjf: trap chunk lists
internal void dmn_trap_chunk_list_push(Arena *arena, DMN_TrapChunkList *list, U64 cap, DMN_Trap *trap);
internal void dmn_trap_chunk_list_concat_in_place(DMN_TrapChunkList *dst, DMN_TrapChunkList *to_push);
internal void dmn_trap_chunk_list_concat_shallow_copy(Arena *arena, DMN_TrapChunkList *dst, DMN_TrapChunkList *to_push);
//- rjf: handle lists
internal void dmn_handle_list_push(Arena *arena, DMN_HandleList *list, DMN_Handle handle);
internal DMN_HandleArray dmn_handle_array_from_list(Arena *arena, DMN_HandleList *list);
internal DMN_HandleArray dmn_handle_array_copy(Arena *arena, DMN_HandleArray *src);
//- rjf: event list building
internal DMN_Event *dmn_event_list_push(Arena *arena, DMN_EventList *list);
////////////////////////////////
//~ rjf: Thread Reading Helper Functions (Helpers, Implemented Once)
internal U64 dmn_rip_from_thread(DMN_Handle thread);
internal U64 dmn_rsp_from_thread(DMN_Handle thread);
////////////////////////////////
//~ rjf: @dmn_os_hooks Main Layer Initialization (Implemented Per-OS)
internal void dmn_init(void);
////////////////////////////////
//~ rjf: @dmn_os_hooks Blocking Control Thread Operations (Implemented Per-OS)
internal DMN_CtrlCtx *dmn_ctrl_begin(void);
internal void dmn_ctrl_exclusive_access_begin(void);
internal void dmn_ctrl_exclusive_access_end(void);
#define DMN_CtrlExclusiveAccessScope DeferLoop(dmn_ctrl_exclusive_access_begin(), dmn_ctrl_exclusive_access_end())
internal U32 dmn_ctrl_launch(DMN_CtrlCtx *ctx, OS_LaunchOptions *options);
internal B32 dmn_ctrl_attach(DMN_CtrlCtx *ctx, U32 pid);
internal B32 dmn_ctrl_kill(DMN_CtrlCtx *ctx, DMN_Handle process, U32 exit_code);
internal B32 dmn_ctrl_detach(DMN_CtrlCtx *ctx, DMN_Handle process);
internal DMN_EventList dmn_ctrl_run(Arena *arena, DMN_CtrlCtx *ctx, DMN_RunCtrls *ctrls);
////////////////////////////////
//~ rjf: @dmn_os_hooks Halting (Implemented Per-OS)
internal void dmn_halt(U64 code, U64 user_data);
////////////////////////////////
//~ rjf: @dmn_os_hooks Introspection Functions (Implemented Per-OS)
//- rjf: run/memory/register counters
internal U64 dmn_run_gen(void);
internal U64 dmn_mem_gen(void);
internal U64 dmn_reg_gen(void);
//- rjf: non-blocking-control-thread access barriers
internal B32 dmn_access_open(void);
internal void dmn_access_close(void);
#define DMN_AccessScope DeferLoopChecked(dmn_access_open(), dmn_access_close())
//- rjf: processes
internal U64 dmn_process_memory_reserve(DMN_Handle process, U64 vaddr, U64 size);
internal void dmn_process_memory_commit(DMN_Handle process, U64 vaddr, U64 size);
internal void dmn_process_memory_decommit(DMN_Handle process, U64 vaddr, U64 size);
internal void dmn_process_memory_release(DMN_Handle process, U64 vaddr, U64 size);
internal void dmn_process_memory_protect(DMN_Handle process, U64 vaddr, U64 size, OS_AccessFlags flags);
internal U64 dmn_process_read(DMN_Handle process, Rng1U64 range, void *dst);
internal B32 dmn_process_write(DMN_Handle process, Rng1U64 range, void *src);
#define dmn_process_read_struct(process, vaddr, ptr) dmn_process_read((process), r1u64((vaddr), (vaddr)+(sizeof(*ptr))), ptr)
#define dmn_process_write_struct(process, vaddr, ptr) dmn_process_write((process), r1u64((vaddr), (vaddr)+(sizeof(*ptr))), ptr)
//- rjf: threads
internal Architecture dmn_arch_from_thread(DMN_Handle handle);
internal U64 dmn_stack_base_vaddr_from_thread(DMN_Handle handle);
internal U64 dmn_tls_root_vaddr_from_thread(DMN_Handle handle);
internal B32 dmn_thread_read_reg_block(DMN_Handle handle, void *reg_block);
internal B32 dmn_thread_write_reg_block(DMN_Handle handle, void *reg_block);
//- rjf: system process listing
internal void dmn_process_iter_begin(DMN_ProcessIter *iter);
internal B32 dmn_process_iter_next(Arena *arena, DMN_ProcessIter *iter, DMN_ProcessInfo *info_out);
internal void dmn_process_iter_end(DMN_ProcessIter *iter);
#endif // DEMON_CORE_H
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#ifndef DEMON_CORE_H
#define DEMON_CORE_H
////////////////////////////////
//~ rjf: Control-Thread-Only Context
//
// An instance of this struct must ONLY be returned by dmn_ctrl_begin, and only
// used by the thread which called it. All APIs which can ONLY run on the
// control thread, which blocks to control & receive events, will take this
// parameter. All other APIs can be called from any thread.
typedef struct DMN_CtrlCtx DMN_CtrlCtx;
struct DMN_CtrlCtx
{
U64 u64[1];
};
////////////////////////////////
//~ rjf: Handle Types
typedef union DMN_Handle DMN_Handle;
union DMN_Handle
{
U32 u32[2];
U64 u64[1];
};
typedef struct DMN_HandleNode DMN_HandleNode;
struct DMN_HandleNode
{
DMN_HandleNode *next;
DMN_Handle v;
};
typedef struct DMN_HandleList DMN_HandleList;
struct DMN_HandleList
{
DMN_HandleNode *first;
DMN_HandleNode *last;
U64 count;
};
typedef struct DMN_HandleArray DMN_HandleArray;
struct DMN_HandleArray
{
DMN_Handle *handles;
U64 count;
};
////////////////////////////////
//~ rjf: Generated Code
#include "generated/demon.meta.h"
////////////////////////////////
//~ rjf: Event Types
typedef struct DMN_Event DMN_Event;
struct DMN_Event
{
DMN_EventKind kind;
DMN_ErrorKind error_kind;
DMN_MemoryEventKind memory_kind;
DMN_ExceptionKind exception_kind;
DMN_Handle process;
DMN_Handle thread;
DMN_Handle module;
Architecture arch;
U64 address;
U64 size;
String8 string;
U32 code; // code gives pid & tid on CreateProcess and CreateThread (respectfully)
U32 flags;
S32 signo;
S32 sigcode;
U64 instruction_pointer;
U64 stack_pointer;
U64 user_data;
B32 exception_repeated;
};
typedef struct DMN_EventNode DMN_EventNode;
struct DMN_EventNode
{
DMN_EventNode *next;
DMN_Event v;
};
typedef struct DMN_EventList DMN_EventList;
struct DMN_EventList
{
DMN_EventNode *first;
DMN_EventNode *last;
U64 count;
};
////////////////////////////////
//~ rjf: Run Control Types
typedef struct DMN_Trap DMN_Trap;
struct DMN_Trap
{
DMN_Handle process;
U64 vaddr;
U64 id;
};
typedef struct DMN_TrapChunkNode DMN_TrapChunkNode;
struct DMN_TrapChunkNode
{
DMN_TrapChunkNode *next;
DMN_Trap *v;
U64 cap;
U64 count;
};
typedef struct DMN_TrapChunkList DMN_TrapChunkList;
struct DMN_TrapChunkList
{
DMN_TrapChunkNode *first;
DMN_TrapChunkNode *last;
U64 node_count;
U64 trap_count;
};
typedef struct DMN_RunCtrls DMN_RunCtrls;
struct DMN_RunCtrls
{
DMN_Handle single_step_thread;
B8 ignore_previous_exception;
B8 run_entities_are_unfrozen;
B8 run_entities_are_processes;
DMN_Handle *run_entities;
U64 run_entity_count;
DMN_TrapChunkList traps;
};
////////////////////////////////
//~ rjf: System Process Listing Types
typedef struct DMN_ProcessIter DMN_ProcessIter;
struct DMN_ProcessIter
{
U64 v[2];
};
typedef struct DMN_ProcessInfo DMN_ProcessInfo;
struct DMN_ProcessInfo
{
String8 name;
U32 pid;
};
////////////////////////////////
//~ rjf: Basic Type Functions (Helpers, Implemented Once)
//- rjf: handles
internal DMN_Handle dmn_handle_zero(void);
internal B32 dmn_handle_match(DMN_Handle a, DMN_Handle b);
//- rjf: trap chunk lists
internal void dmn_trap_chunk_list_push(Arena *arena, DMN_TrapChunkList *list, U64 cap, DMN_Trap *trap);
internal void dmn_trap_chunk_list_concat_in_place(DMN_TrapChunkList *dst, DMN_TrapChunkList *to_push);
internal void dmn_trap_chunk_list_concat_shallow_copy(Arena *arena, DMN_TrapChunkList *dst, DMN_TrapChunkList *to_push);
//- rjf: handle lists
internal void dmn_handle_list_push(Arena *arena, DMN_HandleList *list, DMN_Handle handle);
internal DMN_HandleArray dmn_handle_array_from_list(Arena *arena, DMN_HandleList *list);
internal DMN_HandleArray dmn_handle_array_copy(Arena *arena, DMN_HandleArray *src);
//- rjf: event list building
internal DMN_Event *dmn_event_list_push(Arena *arena, DMN_EventList *list);
////////////////////////////////
//~ rjf: Thread Reading Helper Functions (Helpers, Implemented Once)
internal U64 dmn_rip_from_thread(DMN_Handle thread);
internal U64 dmn_rsp_from_thread(DMN_Handle thread);
////////////////////////////////
//~ rjf: @dmn_os_hooks Main Layer Initialization (Implemented Per-OS)
internal void dmn_init(void);
////////////////////////////////
//~ rjf: @dmn_os_hooks Blocking Control Thread Operations (Implemented Per-OS)
internal DMN_CtrlCtx *dmn_ctrl_begin(void);
internal void dmn_ctrl_exclusive_access_begin(void);
internal void dmn_ctrl_exclusive_access_end(void);
#define DMN_CtrlExclusiveAccessScope DeferLoop(dmn_ctrl_exclusive_access_begin(), dmn_ctrl_exclusive_access_end())
internal U32 dmn_ctrl_launch(DMN_CtrlCtx *ctx, OS_ProcessLaunchParams *params);
internal B32 dmn_ctrl_attach(DMN_CtrlCtx *ctx, U32 pid);
internal B32 dmn_ctrl_kill(DMN_CtrlCtx *ctx, DMN_Handle process, U32 exit_code);
internal B32 dmn_ctrl_detach(DMN_CtrlCtx *ctx, DMN_Handle process);
internal DMN_EventList dmn_ctrl_run(Arena *arena, DMN_CtrlCtx *ctx, DMN_RunCtrls *ctrls);
////////////////////////////////
//~ rjf: @dmn_os_hooks Halting (Implemented Per-OS)
internal void dmn_halt(U64 code, U64 user_data);
////////////////////////////////
//~ rjf: @dmn_os_hooks Introspection Functions (Implemented Per-OS)
//- rjf: run/memory/register counters
internal U64 dmn_run_gen(void);
internal U64 dmn_mem_gen(void);
internal U64 dmn_reg_gen(void);
//- rjf: non-blocking-control-thread access barriers
internal B32 dmn_access_open(void);
internal void dmn_access_close(void);
#define DMN_AccessScope DeferLoopChecked(dmn_access_open(), dmn_access_close())
//- rjf: processes
internal U64 dmn_process_memory_reserve(DMN_Handle process, U64 vaddr, U64 size);
internal void dmn_process_memory_commit(DMN_Handle process, U64 vaddr, U64 size);
internal void dmn_process_memory_decommit(DMN_Handle process, U64 vaddr, U64 size);
internal void dmn_process_memory_release(DMN_Handle process, U64 vaddr, U64 size);
internal void dmn_process_memory_protect(DMN_Handle process, U64 vaddr, U64 size, OS_AccessFlags flags);
internal U64 dmn_process_read(DMN_Handle process, Rng1U64 range, void *dst);
internal B32 dmn_process_write(DMN_Handle process, Rng1U64 range, void *src);
#define dmn_process_read_struct(process, vaddr, ptr) dmn_process_read((process), r1u64((vaddr), (vaddr)+(sizeof(*ptr))), ptr)
#define dmn_process_write_struct(process, vaddr, ptr) dmn_process_write((process), r1u64((vaddr), (vaddr)+(sizeof(*ptr))), ptr)
//- rjf: threads
internal Architecture dmn_arch_from_thread(DMN_Handle handle);
internal U64 dmn_stack_base_vaddr_from_thread(DMN_Handle handle);
internal U64 dmn_tls_root_vaddr_from_thread(DMN_Handle handle);
internal B32 dmn_thread_read_reg_block(DMN_Handle handle, void *reg_block);
internal B32 dmn_thread_write_reg_block(DMN_Handle handle, void *reg_block);
//- rjf: system process listing
internal void dmn_process_iter_begin(DMN_ProcessIter *iter);
internal B32 dmn_process_iter_next(Arena *arena, DMN_ProcessIter *iter, DMN_ProcessInfo *info_out);
internal void dmn_process_iter_end(DMN_ProcessIter *iter);
#endif // DEMON_CORE_H
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+9312 -9357
View File
File diff suppressed because it is too large Load Diff
+1829 -1834
View File
File diff suppressed because it is too large Load Diff
+9461 -9461
View File
File diff suppressed because it is too large Load Diff
+641 -641
View File
File diff suppressed because it is too large Load Diff
+310 -310
View File
@@ -1,310 +1,310 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
////////////////////////////////
//~ rjf: Top-Level API
internal void
fs_init(void)
{
Arena *arena = arena_alloc();
fs_shared = push_array(arena, FS_Shared, 1);
fs_shared->arena = arena;
fs_shared->change_gen = 1;
fs_shared->slots_count = 1024;
fs_shared->stripes_count = os_logical_core_count();
fs_shared->slots = push_array(arena, FS_Slot, fs_shared->slots_count);
fs_shared->stripes = push_array(arena, FS_Stripe, fs_shared->stripes_count);
for(U64 idx = 0; idx < fs_shared->stripes_count; idx += 1)
{
fs_shared->stripes[idx].arena = arena_alloc();
fs_shared->stripes[idx].cv = os_condition_variable_alloc();
fs_shared->stripes[idx].rw_mutex = os_rw_mutex_alloc();
}
fs_shared->u2s_ring_size = KB(64);
fs_shared->u2s_ring_base = push_array_no_zero(arena, U8, fs_shared->u2s_ring_size);
fs_shared->u2s_ring_cv = os_condition_variable_alloc();
fs_shared->u2s_ring_mutex = os_mutex_alloc();
fs_shared->streamer_count = Clamp(1, os_logical_core_count()-1, 4);
fs_shared->streamers = push_array(arena, OS_Handle, 1);
for(U64 idx = 0; idx < fs_shared->streamer_count; idx += 1)
{
fs_shared->streamers[idx] = os_launch_thread(fs_streamer_thread__entry_point, (void *)idx, 0);
}
fs_shared->detector_thread = os_launch_thread(fs_detector_thread__entry_point, 0, 0);
}
////////////////////////////////
//~ rjf: Change Generation
internal U64
fs_change_gen(void)
{
return ins_atomic_u64_eval(&fs_shared->change_gen);
}
////////////////////////////////
//~ rjf: Cache Interaction
internal U128
fs_hash_from_path(String8 path, U64 endt_us)
{
Temp scratch = scratch_begin(0, 0);
U128 result = {0};
path = path_normalized_from_string(scratch.arena, path);
U128 path_key = hs_hash_from_data(path);
for(U64 rewind_idx = 0; rewind_idx < 2; rewind_idx += 1)
{
result = hs_hash_from_key(path_key, rewind_idx);
if(!u128_match(result, u128_zero()))
{
break;
}
else if(u128_match(result, u128_zero()) && rewind_idx == 0)
{
U64 slot_idx = path_key.u64[0]%fs_shared->slots_count;
U64 stripe_idx = slot_idx%fs_shared->stripes_count;
FS_Slot *slot = &fs_shared->slots[slot_idx];
FS_Stripe *stripe = &fs_shared->stripes[stripe_idx];
OS_MutexScopeR(stripe->rw_mutex) for(;;)
{
FS_Node *node = 0;
for(FS_Node *n = slot->first; n != 0; n = n->next)
{
if(str8_match(path, n->path, 0))
{
node = n;
break;
}
}
if(node == 0) OS_MutexScopeRWPromote(stripe->rw_mutex)
{
node = push_array(stripe->arena, FS_Node, 1);
SLLQueuePush(slot->first, slot->last, node);
node->path = push_str8_copy(stripe->arena, path);
}
if(!ins_atomic_u32_eval_cond_assign(&node->is_working, 1, 0) &&
!fs_u2s_enqueue_path(path, endt_us))
{
ins_atomic_u32_eval_assign(&node->is_working, 0);
}
result = hs_hash_from_key(path_key, 0);
if(u128_match(result, u128_zero()) && os_now_microseconds() <= endt_us)
{
os_condition_variable_wait_rw_r(stripe->cv, stripe->rw_mutex, endt_us);
}
else
{
break;
}
}
}
}
scratch_end(scratch);
return result;
}
internal U128
fs_key_from_path(String8 path)
{
U128 key = hs_hash_from_data(path);
fs_hash_from_path(path, 0);
return key;
}
internal U64
fs_timestamp_from_path(String8 path)
{
Temp scratch = scratch_begin(0, 0);
U64 result = 0;
path = path_normalized_from_string(scratch.arena, path);
U128 path_key = hs_hash_from_data(path);
U64 slot_idx = path_key.u64[0]%fs_shared->slots_count;
U64 stripe_idx = slot_idx%fs_shared->stripes_count;
FS_Slot *slot = &fs_shared->slots[slot_idx];
FS_Stripe *stripe = &fs_shared->stripes[stripe_idx];
OS_MutexScopeR(stripe->rw_mutex)
{
for(FS_Node *n = slot->first; n != 0; n = n->next)
{
if(str8_match(path, n->path, 0))
{
result = n->timestamp;
break;
}
}
}
scratch_end(scratch);
return result;
}
////////////////////////////////
//~ rjf: Streamer Threads
internal B32
fs_u2s_enqueue_path(String8 path, U64 endt_us)
{
B32 result = 0;
path.size = Min(path.size, fs_shared->u2s_ring_size);
OS_MutexScope(fs_shared->u2s_ring_mutex) for(;;)
{
U64 unconsumed_size = fs_shared->u2s_ring_write_pos - fs_shared->u2s_ring_read_pos;
U64 available_size = fs_shared->u2s_ring_size - unconsumed_size;
if(available_size >= sizeof(U64) + path.size)
{
result = 1;
fs_shared->u2s_ring_write_pos += ring_write_struct(fs_shared->u2s_ring_base, fs_shared->u2s_ring_size, fs_shared->u2s_ring_write_pos, &path.size);
fs_shared->u2s_ring_write_pos += ring_write(fs_shared->u2s_ring_base, fs_shared->u2s_ring_size, fs_shared->u2s_ring_write_pos, path.str, path.size);
fs_shared->u2s_ring_write_pos += 7;
fs_shared->u2s_ring_write_pos -= fs_shared->u2s_ring_write_pos%8;
break;
}
os_condition_variable_wait(fs_shared->u2s_ring_cv, fs_shared->u2s_ring_mutex, endt_us);
}
if(result)
{
os_condition_variable_broadcast(fs_shared->u2s_ring_cv);
}
return result;
}
internal String8
fs_u2s_dequeue_path(Arena *arena)
{
String8 path = {0};
OS_MutexScope(fs_shared->u2s_ring_mutex) for(;;)
{
U64 unconsumed_size = fs_shared->u2s_ring_write_pos - fs_shared->u2s_ring_read_pos;
if(unconsumed_size >= sizeof(U64))
{
fs_shared->u2s_ring_read_pos += ring_read_struct(fs_shared->u2s_ring_base, fs_shared->u2s_ring_size, fs_shared->u2s_ring_read_pos, &path.size);
path.str = push_array(arena, U8, path.size);
fs_shared->u2s_ring_read_pos += ring_read(fs_shared->u2s_ring_base, fs_shared->u2s_ring_size, fs_shared->u2s_ring_read_pos, path.str, path.size);
fs_shared->u2s_ring_read_pos += 7;
fs_shared->u2s_ring_read_pos -= fs_shared->u2s_ring_read_pos%8;
break;
}
os_condition_variable_wait(fs_shared->u2s_ring_cv, fs_shared->u2s_ring_mutex, max_U64);
}
os_condition_variable_broadcast(fs_shared->u2s_ring_cv);
return path;
}
internal void
fs_streamer_thread__entry_point(void *p)
{
ThreadNameF("[fs] streamer #%I64u", (U64)p);
for(;;)
{
Temp scratch = scratch_begin(0, 0);
//- rjf: unpack path
String8 path = fs_u2s_dequeue_path(scratch.arena);
U128 key = hs_hash_from_data(path);
U64 slot_idx = key.u64[0]%fs_shared->slots_count;
U64 stripe_idx = slot_idx%fs_shared->stripes_count;
FS_Slot *slot = &fs_shared->slots[slot_idx];
FS_Stripe *stripe = &fs_shared->stripes[stripe_idx];
//- rjf: load
ProfBegin("load \"%.*s\"", str8_varg(path));
FileProperties pre_props = os_properties_from_file_path(path);
OS_Handle file = os_file_open(OS_AccessFlag_Read|OS_AccessFlag_ShareRead|OS_AccessFlag_ShareWrite, path);
U64 data_arena_size = pre_props.size+ARENA_HEADER_SIZE;
data_arena_size += KB(4)-1;
data_arena_size -= data_arena_size%KB(4);
ProfBegin("allocate");
Arena *data_arena = arena_alloc__sized(data_arena_size, data_arena_size);
ProfEnd();
ProfBegin("read");
String8 data = os_string_from_file_range(data_arena, file, r1u64(0, pre_props.size));
ProfEnd();
os_file_close(file);
FileProperties post_props = os_properties_from_file_path(path);
//- rjf: abort if modification timestamps differ - we did not successfully read the file
if(pre_props.modified != post_props.modified)
{
ProfScope("abort")
{
arena_release(data_arena);
MemoryZeroStruct(&data);
data_arena = 0;
}
}
//- rjf: submit
else
{
ProfScope("submit")
{
hs_submit_data(key, &data_arena, data);
}
}
//- rjf: commit info to cache
ProfScope("commit to cache") OS_MutexScopeW(stripe->rw_mutex)
{
FS_Node *node = 0;
for(FS_Node *n = slot->first; n != 0; n = n->next)
{
if(str8_match(n->path, path, 0))
{
node = n;
break;
}
}
if(node != 0)
{
if(node->timestamp != 0)
{
ins_atomic_u64_inc_eval(&fs_shared->change_gen);
}
if(post_props.modified == pre_props.modified)
{
node->timestamp = post_props.modified;
}
ins_atomic_u32_eval_assign(&node->is_working, 0);
}
}
os_condition_variable_broadcast(stripe->cv);
ProfEnd();
scratch_end(scratch);
}
}
////////////////////////////////
//~ rjf: Change Detector Thread
internal void
fs_detector_thread__entry_point(void *p)
{
ThreadNameF("[fs] detector");
for(;;)
{
U64 slots_per_stripe = fs_shared->slots_count/fs_shared->stripes_count;
for(U64 stripe_idx = 0; stripe_idx < fs_shared->stripes_count; stripe_idx += 1)
{
FS_Stripe *stripe = &fs_shared->stripes[stripe_idx];
OS_MutexScopeR(stripe->rw_mutex) for(U64 slot_in_stripe_idx = 0; slot_in_stripe_idx < slots_per_stripe; slot_in_stripe_idx += 1)
{
U64 slot_idx = stripe_idx*slots_per_stripe + slot_in_stripe_idx;
FS_Slot *slot = &fs_shared->slots[slot_idx];
for(FS_Node *n = slot->first; n != 0; n = n->next)
{
FileProperties props = os_properties_from_file_path(n->path);
if(props.modified != n->timestamp)
{
if(!ins_atomic_u32_eval_cond_assign(&n->is_working, 1, 0) &&
!fs_u2s_enqueue_path(n->path, os_now_microseconds()+100000))
{
ins_atomic_u32_eval_assign(&n->is_working, 0);
}
}
}
}
}
os_sleep_milliseconds(100);
}
}
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
////////////////////////////////
//~ rjf: Top-Level API
internal void
fs_init(void)
{
Arena *arena = arena_alloc();
fs_shared = push_array(arena, FS_Shared, 1);
fs_shared->arena = arena;
fs_shared->change_gen = 1;
fs_shared->slots_count = 1024;
fs_shared->stripes_count = os_get_system_info()->logical_processor_count;
fs_shared->slots = push_array(arena, FS_Slot, fs_shared->slots_count);
fs_shared->stripes = push_array(arena, FS_Stripe, fs_shared->stripes_count);
for(U64 idx = 0; idx < fs_shared->stripes_count; idx += 1)
{
fs_shared->stripes[idx].arena = arena_alloc();
fs_shared->stripes[idx].cv = os_condition_variable_alloc();
fs_shared->stripes[idx].rw_mutex = os_rw_mutex_alloc();
}
fs_shared->u2s_ring_size = KB(64);
fs_shared->u2s_ring_base = push_array_no_zero(arena, U8, fs_shared->u2s_ring_size);
fs_shared->u2s_ring_cv = os_condition_variable_alloc();
fs_shared->u2s_ring_mutex = os_mutex_alloc();
fs_shared->streamer_count = Clamp(1, os_get_system_info()->logical_processor_count-1, 4);
fs_shared->streamers = push_array(arena, OS_Handle, 1);
for(U64 idx = 0; idx < fs_shared->streamer_count; idx += 1)
{
fs_shared->streamers[idx] = os_thread_launch(fs_streamer_thread__entry_point, (void *)idx, 0);
}
fs_shared->detector_thread = os_thread_launch(fs_detector_thread__entry_point, 0, 0);
}
////////////////////////////////
//~ rjf: Change Generation
internal U64
fs_change_gen(void)
{
return ins_atomic_u64_eval(&fs_shared->change_gen);
}
////////////////////////////////
//~ rjf: Cache Interaction
internal U128
fs_hash_from_path(String8 path, U64 endt_us)
{
Temp scratch = scratch_begin(0, 0);
U128 result = {0};
path = path_normalized_from_string(scratch.arena, path);
U128 path_key = hs_hash_from_data(path);
for(U64 rewind_idx = 0; rewind_idx < 2; rewind_idx += 1)
{
result = hs_hash_from_key(path_key, rewind_idx);
if(!u128_match(result, u128_zero()))
{
break;
}
else if(u128_match(result, u128_zero()) && rewind_idx == 0)
{
U64 slot_idx = path_key.u64[0]%fs_shared->slots_count;
U64 stripe_idx = slot_idx%fs_shared->stripes_count;
FS_Slot *slot = &fs_shared->slots[slot_idx];
FS_Stripe *stripe = &fs_shared->stripes[stripe_idx];
OS_MutexScopeR(stripe->rw_mutex) for(;;)
{
FS_Node *node = 0;
for(FS_Node *n = slot->first; n != 0; n = n->next)
{
if(str8_match(path, n->path, 0))
{
node = n;
break;
}
}
if(node == 0) OS_MutexScopeRWPromote(stripe->rw_mutex)
{
node = push_array(stripe->arena, FS_Node, 1);
SLLQueuePush(slot->first, slot->last, node);
node->path = push_str8_copy(stripe->arena, path);
}
if(!ins_atomic_u32_eval_cond_assign(&node->is_working, 1, 0) &&
!fs_u2s_enqueue_path(path, endt_us))
{
ins_atomic_u32_eval_assign(&node->is_working, 0);
}
result = hs_hash_from_key(path_key, 0);
if(u128_match(result, u128_zero()) && os_now_microseconds() <= endt_us)
{
os_condition_variable_wait_rw_r(stripe->cv, stripe->rw_mutex, endt_us);
}
else
{
break;
}
}
}
}
scratch_end(scratch);
return result;
}
internal U128
fs_key_from_path(String8 path)
{
U128 key = hs_hash_from_data(path);
fs_hash_from_path(path, 0);
return key;
}
internal U64
fs_timestamp_from_path(String8 path)
{
Temp scratch = scratch_begin(0, 0);
U64 result = 0;
path = path_normalized_from_string(scratch.arena, path);
U128 path_key = hs_hash_from_data(path);
U64 slot_idx = path_key.u64[0]%fs_shared->slots_count;
U64 stripe_idx = slot_idx%fs_shared->stripes_count;
FS_Slot *slot = &fs_shared->slots[slot_idx];
FS_Stripe *stripe = &fs_shared->stripes[stripe_idx];
OS_MutexScopeR(stripe->rw_mutex)
{
for(FS_Node *n = slot->first; n != 0; n = n->next)
{
if(str8_match(path, n->path, 0))
{
result = n->timestamp;
break;
}
}
}
scratch_end(scratch);
return result;
}
////////////////////////////////
//~ rjf: Streamer Threads
internal B32
fs_u2s_enqueue_path(String8 path, U64 endt_us)
{
B32 result = 0;
path.size = Min(path.size, fs_shared->u2s_ring_size);
OS_MutexScope(fs_shared->u2s_ring_mutex) for(;;)
{
U64 unconsumed_size = fs_shared->u2s_ring_write_pos - fs_shared->u2s_ring_read_pos;
U64 available_size = fs_shared->u2s_ring_size - unconsumed_size;
if(available_size >= sizeof(U64) + path.size)
{
result = 1;
fs_shared->u2s_ring_write_pos += ring_write_struct(fs_shared->u2s_ring_base, fs_shared->u2s_ring_size, fs_shared->u2s_ring_write_pos, &path.size);
fs_shared->u2s_ring_write_pos += ring_write(fs_shared->u2s_ring_base, fs_shared->u2s_ring_size, fs_shared->u2s_ring_write_pos, path.str, path.size);
fs_shared->u2s_ring_write_pos += 7;
fs_shared->u2s_ring_write_pos -= fs_shared->u2s_ring_write_pos%8;
break;
}
os_condition_variable_wait(fs_shared->u2s_ring_cv, fs_shared->u2s_ring_mutex, endt_us);
}
if(result)
{
os_condition_variable_broadcast(fs_shared->u2s_ring_cv);
}
return result;
}
internal String8
fs_u2s_dequeue_path(Arena *arena)
{
String8 path = {0};
OS_MutexScope(fs_shared->u2s_ring_mutex) for(;;)
{
U64 unconsumed_size = fs_shared->u2s_ring_write_pos - fs_shared->u2s_ring_read_pos;
if(unconsumed_size >= sizeof(U64))
{
fs_shared->u2s_ring_read_pos += ring_read_struct(fs_shared->u2s_ring_base, fs_shared->u2s_ring_size, fs_shared->u2s_ring_read_pos, &path.size);
path.str = push_array(arena, U8, path.size);
fs_shared->u2s_ring_read_pos += ring_read(fs_shared->u2s_ring_base, fs_shared->u2s_ring_size, fs_shared->u2s_ring_read_pos, path.str, path.size);
fs_shared->u2s_ring_read_pos += 7;
fs_shared->u2s_ring_read_pos -= fs_shared->u2s_ring_read_pos%8;
break;
}
os_condition_variable_wait(fs_shared->u2s_ring_cv, fs_shared->u2s_ring_mutex, max_U64);
}
os_condition_variable_broadcast(fs_shared->u2s_ring_cv);
return path;
}
internal void
fs_streamer_thread__entry_point(void *p)
{
ThreadNameF("[fs] streamer #%I64u", (U64)p);
for(;;)
{
Temp scratch = scratch_begin(0, 0);
//- rjf: unpack path
String8 path = fs_u2s_dequeue_path(scratch.arena);
U128 key = hs_hash_from_data(path);
U64 slot_idx = key.u64[0]%fs_shared->slots_count;
U64 stripe_idx = slot_idx%fs_shared->stripes_count;
FS_Slot *slot = &fs_shared->slots[slot_idx];
FS_Stripe *stripe = &fs_shared->stripes[stripe_idx];
//- rjf: load
ProfBegin("load \"%.*s\"", str8_varg(path));
FileProperties pre_props = os_properties_from_file_path(path);
OS_Handle file = os_file_open(OS_AccessFlag_Read|OS_AccessFlag_ShareRead|OS_AccessFlag_ShareWrite, path);
U64 data_arena_size = pre_props.size+ARENA_HEADER_SIZE;
data_arena_size += KB(4)-1;
data_arena_size -= data_arena_size%KB(4);
ProfBegin("allocate");
Arena *data_arena = arena_alloc(.reserve_size = data_arena_size, .commit_size = data_arena_size);
ProfEnd();
ProfBegin("read");
String8 data = os_string_from_file_range(data_arena, file, r1u64(0, pre_props.size));
ProfEnd();
os_file_close(file);
FileProperties post_props = os_properties_from_file_path(path);
//- rjf: abort if modification timestamps differ - we did not successfully read the file
if(pre_props.modified != post_props.modified)
{
ProfScope("abort")
{
arena_release(data_arena);
MemoryZeroStruct(&data);
data_arena = 0;
}
}
//- rjf: submit
else
{
ProfScope("submit")
{
hs_submit_data(key, &data_arena, data);
}
}
//- rjf: commit info to cache
ProfScope("commit to cache") OS_MutexScopeW(stripe->rw_mutex)
{
FS_Node *node = 0;
for(FS_Node *n = slot->first; n != 0; n = n->next)
{
if(str8_match(n->path, path, 0))
{
node = n;
break;
}
}
if(node != 0)
{
if(node->timestamp != 0)
{
ins_atomic_u64_inc_eval(&fs_shared->change_gen);
}
if(post_props.modified == pre_props.modified)
{
node->timestamp = post_props.modified;
}
ins_atomic_u32_eval_assign(&node->is_working, 0);
}
}
os_condition_variable_broadcast(stripe->cv);
ProfEnd();
scratch_end(scratch);
}
}
////////////////////////////////
//~ rjf: Change Detector Thread
internal void
fs_detector_thread__entry_point(void *p)
{
ThreadNameF("[fs] detector");
for(;;)
{
U64 slots_per_stripe = fs_shared->slots_count/fs_shared->stripes_count;
for(U64 stripe_idx = 0; stripe_idx < fs_shared->stripes_count; stripe_idx += 1)
{
FS_Stripe *stripe = &fs_shared->stripes[stripe_idx];
OS_MutexScopeR(stripe->rw_mutex) for(U64 slot_in_stripe_idx = 0; slot_in_stripe_idx < slots_per_stripe; slot_in_stripe_idx += 1)
{
U64 slot_idx = stripe_idx*slots_per_stripe + slot_in_stripe_idx;
FS_Slot *slot = &fs_shared->slots[slot_idx];
for(FS_Node *n = slot->first; n != 0; n = n->next)
{
FileProperties props = os_properties_from_file_path(n->path);
if(props.modified != n->timestamp)
{
if(!ins_atomic_u32_eval_cond_assign(&n->is_working, 1, 0) &&
!fs_u2s_enqueue_path(n->path, os_now_microseconds()+100000))
{
ins_atomic_u32_eval_assign(&n->is_working, 0);
}
}
}
}
}
os_sleep_milliseconds(100);
}
}
File diff suppressed because it is too large Load Diff
+383 -383
View File
@@ -1,383 +1,383 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
////////////////////////////////
//~ rjf: Main Layer Initialization
internal void
geo_init(void)
{
Arena *arena = arena_alloc();
geo_shared = push_array(arena, GEO_Shared, 1);
geo_shared->arena = arena;
geo_shared->slots_count = 1024;
geo_shared->stripes_count = Min(geo_shared->slots_count, os_logical_core_count());
geo_shared->slots = push_array(arena, GEO_Slot, geo_shared->slots_count);
geo_shared->stripes = push_array(arena, GEO_Stripe, geo_shared->stripes_count);
geo_shared->stripes_free_nodes = push_array(arena, GEO_Node *, geo_shared->stripes_count);
for(U64 idx = 0; idx < geo_shared->stripes_count; idx += 1)
{
geo_shared->stripes[idx].arena = arena_alloc();
geo_shared->stripes[idx].rw_mutex = os_rw_mutex_alloc();
geo_shared->stripes[idx].cv = os_condition_variable_alloc();
}
geo_shared->u2x_ring_size = KB(64);
geo_shared->u2x_ring_base = push_array_no_zero(arena, U8, geo_shared->u2x_ring_size);
geo_shared->u2x_ring_cv = os_condition_variable_alloc();
geo_shared->u2x_ring_mutex = os_mutex_alloc();
geo_shared->xfer_thread_count = Clamp(1, os_logical_core_count()-1, 4);
geo_shared->xfer_threads = push_array(arena, OS_Handle, geo_shared->xfer_thread_count);
for(U64 idx = 0; idx < geo_shared->xfer_thread_count; idx += 1)
{
geo_shared->xfer_threads[idx] = os_launch_thread(geo_xfer_thread__entry_point, (void *)idx, 0);
}
geo_shared->evictor_thread = os_launch_thread(geo_evictor_thread__entry_point, 0, 0);
}
////////////////////////////////
//~ rjf: Thread Context Initialization
internal void
geo_tctx_ensure_inited(void)
{
if(geo_tctx == 0)
{
Arena *arena = arena_alloc();
geo_tctx = push_array(arena, GEO_TCTX, 1);
geo_tctx->arena = arena;
}
}
////////////////////////////////
//~ rjf: User Clock
internal void
geo_user_clock_tick(void)
{
ins_atomic_u64_inc_eval(&geo_shared->user_clock_idx);
}
internal U64
geo_user_clock_idx(void)
{
return ins_atomic_u64_eval(&geo_shared->user_clock_idx);
}
////////////////////////////////
//~ rjf: Scoped Access
internal GEO_Scope *
geo_scope_open(void)
{
geo_tctx_ensure_inited();
GEO_Scope *scope = geo_tctx->free_scope;
if(scope)
{
SLLStackPop(geo_tctx->free_scope);
}
else
{
scope = push_array_no_zero(geo_tctx->arena, GEO_Scope, 1);
}
MemoryZeroStruct(scope);
return scope;
}
internal void
geo_scope_close(GEO_Scope *scope)
{
for(GEO_Touch *touch = scope->top_touch, *next = 0; touch != 0; touch = next)
{
U128 hash = touch->hash;
next = touch->next;
U64 slot_idx = hash.u64[1]%geo_shared->slots_count;
U64 stripe_idx = slot_idx%geo_shared->stripes_count;
GEO_Slot *slot = &geo_shared->slots[slot_idx];
GEO_Stripe *stripe = &geo_shared->stripes[stripe_idx];
OS_MutexScopeR(stripe->rw_mutex)
{
for(GEO_Node *n = slot->first; n != 0; n = n->next)
{
if(u128_match(hash, n->hash))
{
ins_atomic_u64_dec_eval(&n->scope_ref_count);
break;
}
}
}
SLLStackPush(geo_tctx->free_touch, touch);
}
SLLStackPush(geo_tctx->free_scope, scope);
}
internal void
geo_scope_touch_node__stripe_r_guarded(GEO_Scope *scope, GEO_Node *node)
{
GEO_Touch *touch = geo_tctx->free_touch;
ins_atomic_u64_inc_eval(&node->scope_ref_count);
ins_atomic_u64_eval_assign(&node->last_time_touched_us, os_now_microseconds());
ins_atomic_u64_eval_assign(&node->last_user_clock_idx_touched, geo_user_clock_idx());
if(touch != 0)
{
SLLStackPop(geo_tctx->free_touch);
}
else
{
touch = push_array_no_zero(geo_tctx->arena, GEO_Touch, 1);
}
MemoryZeroStruct(touch);
touch->hash = node->hash;
SLLStackPush(scope->top_touch, touch);
}
////////////////////////////////
//~ rjf: Cache Lookups
internal R_Handle
geo_buffer_from_hash(GEO_Scope *scope, U128 hash)
{
R_Handle handle = {0};
if(!u128_match(hash, u128_zero()))
{
U64 slot_idx = hash.u64[1]%geo_shared->slots_count;
U64 stripe_idx = slot_idx%geo_shared->stripes_count;
GEO_Slot *slot = &geo_shared->slots[slot_idx];
GEO_Stripe *stripe = &geo_shared->stripes[stripe_idx];
B32 found = 0;
OS_MutexScopeR(stripe->rw_mutex)
{
for(GEO_Node *n = slot->first; n != 0; n = n->next)
{
if(u128_match(hash, n->hash))
{
handle = n->buffer;
found = !r_handle_match(r_handle_zero(), handle);
geo_scope_touch_node__stripe_r_guarded(scope, n);
break;
}
}
}
B32 node_is_new = 0;
if(!found)
{
OS_MutexScopeW(stripe->rw_mutex)
{
GEO_Node *node = 0;
for(GEO_Node *n = slot->first; n != 0; n = n->next)
{
if(u128_match(hash, n->hash))
{
node = n;
break;
}
}
if(node == 0)
{
node = geo_shared->stripes_free_nodes[stripe_idx];
if(node)
{
SLLStackPop(geo_shared->stripes_free_nodes[stripe_idx]);
}
else
{
node = push_array_no_zero(stripe->arena, GEO_Node, 1);
}
MemoryZeroStruct(node);
DLLPushBack(slot->first, slot->last, node);
node->hash = hash;
node_is_new = 1;
}
}
}
if(node_is_new)
{
geo_u2x_enqueue_req(hash, max_U64);
}
}
return handle;
}
internal R_Handle
geo_buffer_from_key(GEO_Scope *scope, U128 key)
{
R_Handle handle = {0};
for(U64 rewind_idx = 0; rewind_idx < 2; rewind_idx += 1)
{
U128 hash = hs_hash_from_key(key, rewind_idx);
handle = geo_buffer_from_hash(scope, hash);
if(!r_handle_match(handle, r_handle_zero()))
{
break;
}
}
return handle;
}
////////////////////////////////
//~ rjf: Transfer Threads
internal B32
geo_u2x_enqueue_req(U128 hash, U64 endt_us)
{
B32 good = 0;
OS_MutexScope(geo_shared->u2x_ring_mutex) for(;;)
{
U64 unconsumed_size = geo_shared->u2x_ring_write_pos-geo_shared->u2x_ring_read_pos;
U64 available_size = geo_shared->u2x_ring_size-unconsumed_size;
if(available_size >= sizeof(hash))
{
good = 1;
geo_shared->u2x_ring_write_pos += ring_write_struct(geo_shared->u2x_ring_base, geo_shared->u2x_ring_size, geo_shared->u2x_ring_write_pos, &hash);
break;
}
if(os_now_microseconds() >= endt_us)
{
break;
}
os_condition_variable_wait(geo_shared->u2x_ring_cv, geo_shared->u2x_ring_mutex, endt_us);
}
if(good)
{
os_condition_variable_broadcast(geo_shared->u2x_ring_cv);
}
return good;
}
internal void
geo_u2x_dequeue_req(U128 *hash_out)
{
OS_MutexScope(geo_shared->u2x_ring_mutex) for(;;)
{
U64 unconsumed_size = geo_shared->u2x_ring_write_pos-geo_shared->u2x_ring_read_pos;
if(unconsumed_size >= sizeof(*hash_out))
{
geo_shared->u2x_ring_read_pos += ring_read_struct(geo_shared->u2x_ring_base, geo_shared->u2x_ring_size, geo_shared->u2x_ring_read_pos, hash_out);
break;
}
os_condition_variable_wait(geo_shared->u2x_ring_cv, geo_shared->u2x_ring_mutex, max_U64);
}
os_condition_variable_broadcast(geo_shared->u2x_ring_cv);
}
internal void
geo_xfer_thread__entry_point(void *p)
{
for(;;)
{
HS_Scope *scope = hs_scope_open();
//- rjf: decode
U128 hash = {0};
geo_u2x_dequeue_req(&hash);
//- rjf: unpack hash
U64 slot_idx = hash.u64[1]%geo_shared->slots_count;
U64 stripe_idx = slot_idx%geo_shared->stripes_count;
GEO_Slot *slot = &geo_shared->slots[slot_idx];
GEO_Stripe *stripe = &geo_shared->stripes[stripe_idx];
//- rjf: take task
B32 got_task = 0;
OS_MutexScopeR(stripe->rw_mutex)
{
for(GEO_Node *n = slot->first; n != 0; n = n->next)
{
if(u128_match(n->hash, hash))
{
got_task = !ins_atomic_u32_eval_cond_assign(&n->is_working, 1, 0);
break;
}
}
}
//- rjf: hash -> data
String8 data = {0};
if(got_task)
{
data = hs_data_from_hash(scope, hash);
}
//- rjf: data -> buffer
R_Handle buffer = {0};
if(got_task && data.size != 0)
{
buffer = r_buffer_alloc(R_ResourceKind_Static, data.size, data.str);
}
//- rjf: commit results to cache
if(got_task) OS_MutexScopeW(stripe->rw_mutex)
{
for(GEO_Node *n = slot->first; n != 0; n = n->next)
{
if(u128_match(n->hash, hash))
{
n->buffer = buffer;
ins_atomic_u32_eval_assign(&n->is_working, 0);
ins_atomic_u64_inc_eval(&n->load_count);
break;
}
}
}
hs_scope_close(scope);
}
}
////////////////////////////////
//~ rjf: Evictor Threads
internal void
geo_evictor_thread__entry_point(void *p)
{
for(;;)
{
U64 check_time_us = os_now_microseconds();
U64 check_time_user_clocks = geo_user_clock_idx();
U64 evict_threshold_us = 10*1000000;
U64 evict_threshold_user_clocks = 10;
for(U64 slot_idx = 0; slot_idx < geo_shared->slots_count; slot_idx += 1)
{
U64 stripe_idx = slot_idx%geo_shared->stripes_count;
GEO_Slot *slot = &geo_shared->slots[slot_idx];
GEO_Stripe *stripe = &geo_shared->stripes[stripe_idx];
B32 slot_has_work = 0;
OS_MutexScopeR(stripe->rw_mutex)
{
for(GEO_Node *n = slot->first; n != 0; n = n->next)
{
if(n->scope_ref_count == 0 &&
n->last_time_touched_us+evict_threshold_us <= check_time_us &&
n->last_user_clock_idx_touched+evict_threshold_user_clocks <= check_time_user_clocks &&
n->load_count != 0 &&
n->is_working == 0)
{
slot_has_work = 1;
break;
}
}
}
if(slot_has_work) OS_MutexScopeW(stripe->rw_mutex)
{
for(GEO_Node *n = slot->first, *next = 0; n != 0; n = next)
{
next = n->next;
if(n->scope_ref_count == 0 &&
n->last_time_touched_us+evict_threshold_us <= check_time_us &&
n->last_user_clock_idx_touched+evict_threshold_user_clocks <= check_time_user_clocks &&
n->load_count != 0 &&
n->is_working == 0)
{
DLLRemove(slot->first, slot->last, n);
if(!r_handle_match(n->buffer, r_handle_zero()))
{
r_buffer_release(n->buffer);
}
SLLStackPush(geo_shared->stripes_free_nodes[stripe_idx], n);
}
}
}
os_sleep_milliseconds(5);
}
os_sleep_milliseconds(1000);
}
}
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
////////////////////////////////
//~ rjf: Main Layer Initialization
internal void
geo_init(void)
{
Arena *arena = arena_alloc();
geo_shared = push_array(arena, GEO_Shared, 1);
geo_shared->arena = arena;
geo_shared->slots_count = 1024;
geo_shared->stripes_count = Min(geo_shared->slots_count, os_get_system_info()->logical_processor_count);
geo_shared->slots = push_array(arena, GEO_Slot, geo_shared->slots_count);
geo_shared->stripes = push_array(arena, GEO_Stripe, geo_shared->stripes_count);
geo_shared->stripes_free_nodes = push_array(arena, GEO_Node *, geo_shared->stripes_count);
for(U64 idx = 0; idx < geo_shared->stripes_count; idx += 1)
{
geo_shared->stripes[idx].arena = arena_alloc();
geo_shared->stripes[idx].rw_mutex = os_rw_mutex_alloc();
geo_shared->stripes[idx].cv = os_condition_variable_alloc();
}
geo_shared->u2x_ring_size = KB(64);
geo_shared->u2x_ring_base = push_array_no_zero(arena, U8, geo_shared->u2x_ring_size);
geo_shared->u2x_ring_cv = os_condition_variable_alloc();
geo_shared->u2x_ring_mutex = os_mutex_alloc();
geo_shared->xfer_thread_count = Clamp(1, os_get_system_info()->logical_processor_count-1, 4);
geo_shared->xfer_threads = push_array(arena, OS_Handle, geo_shared->xfer_thread_count);
for(U64 idx = 0; idx < geo_shared->xfer_thread_count; idx += 1)
{
geo_shared->xfer_threads[idx] = os_thread_launch(geo_xfer_thread__entry_point, (void *)idx, 0);
}
geo_shared->evictor_thread = os_thread_launch(geo_evictor_thread__entry_point, 0, 0);
}
////////////////////////////////
//~ rjf: Thread Context Initialization
internal void
geo_tctx_ensure_inited(void)
{
if(geo_tctx == 0)
{
Arena *arena = arena_alloc();
geo_tctx = push_array(arena, GEO_TCTX, 1);
geo_tctx->arena = arena;
}
}
////////////////////////////////
//~ rjf: User Clock
internal void
geo_user_clock_tick(void)
{
ins_atomic_u64_inc_eval(&geo_shared->user_clock_idx);
}
internal U64
geo_user_clock_idx(void)
{
return ins_atomic_u64_eval(&geo_shared->user_clock_idx);
}
////////////////////////////////
//~ rjf: Scoped Access
internal GEO_Scope *
geo_scope_open(void)
{
geo_tctx_ensure_inited();
GEO_Scope *scope = geo_tctx->free_scope;
if(scope)
{
SLLStackPop(geo_tctx->free_scope);
}
else
{
scope = push_array_no_zero(geo_tctx->arena, GEO_Scope, 1);
}
MemoryZeroStruct(scope);
return scope;
}
internal void
geo_scope_close(GEO_Scope *scope)
{
for(GEO_Touch *touch = scope->top_touch, *next = 0; touch != 0; touch = next)
{
U128 hash = touch->hash;
next = touch->next;
U64 slot_idx = hash.u64[1]%geo_shared->slots_count;
U64 stripe_idx = slot_idx%geo_shared->stripes_count;
GEO_Slot *slot = &geo_shared->slots[slot_idx];
GEO_Stripe *stripe = &geo_shared->stripes[stripe_idx];
OS_MutexScopeR(stripe->rw_mutex)
{
for(GEO_Node *n = slot->first; n != 0; n = n->next)
{
if(u128_match(hash, n->hash))
{
ins_atomic_u64_dec_eval(&n->scope_ref_count);
break;
}
}
}
SLLStackPush(geo_tctx->free_touch, touch);
}
SLLStackPush(geo_tctx->free_scope, scope);
}
internal void
geo_scope_touch_node__stripe_r_guarded(GEO_Scope *scope, GEO_Node *node)
{
GEO_Touch *touch = geo_tctx->free_touch;
ins_atomic_u64_inc_eval(&node->scope_ref_count);
ins_atomic_u64_eval_assign(&node->last_time_touched_us, os_now_microseconds());
ins_atomic_u64_eval_assign(&node->last_user_clock_idx_touched, geo_user_clock_idx());
if(touch != 0)
{
SLLStackPop(geo_tctx->free_touch);
}
else
{
touch = push_array_no_zero(geo_tctx->arena, GEO_Touch, 1);
}
MemoryZeroStruct(touch);
touch->hash = node->hash;
SLLStackPush(scope->top_touch, touch);
}
////////////////////////////////
//~ rjf: Cache Lookups
internal R_Handle
geo_buffer_from_hash(GEO_Scope *scope, U128 hash)
{
R_Handle handle = {0};
if(!u128_match(hash, u128_zero()))
{
U64 slot_idx = hash.u64[1]%geo_shared->slots_count;
U64 stripe_idx = slot_idx%geo_shared->stripes_count;
GEO_Slot *slot = &geo_shared->slots[slot_idx];
GEO_Stripe *stripe = &geo_shared->stripes[stripe_idx];
B32 found = 0;
OS_MutexScopeR(stripe->rw_mutex)
{
for(GEO_Node *n = slot->first; n != 0; n = n->next)
{
if(u128_match(hash, n->hash))
{
handle = n->buffer;
found = !r_handle_match(r_handle_zero(), handle);
geo_scope_touch_node__stripe_r_guarded(scope, n);
break;
}
}
}
B32 node_is_new = 0;
if(!found)
{
OS_MutexScopeW(stripe->rw_mutex)
{
GEO_Node *node = 0;
for(GEO_Node *n = slot->first; n != 0; n = n->next)
{
if(u128_match(hash, n->hash))
{
node = n;
break;
}
}
if(node == 0)
{
node = geo_shared->stripes_free_nodes[stripe_idx];
if(node)
{
SLLStackPop(geo_shared->stripes_free_nodes[stripe_idx]);
}
else
{
node = push_array_no_zero(stripe->arena, GEO_Node, 1);
}
MemoryZeroStruct(node);
DLLPushBack(slot->first, slot->last, node);
node->hash = hash;
node_is_new = 1;
}
}
}
if(node_is_new)
{
geo_u2x_enqueue_req(hash, max_U64);
}
}
return handle;
}
internal R_Handle
geo_buffer_from_key(GEO_Scope *scope, U128 key)
{
R_Handle handle = {0};
for(U64 rewind_idx = 0; rewind_idx < 2; rewind_idx += 1)
{
U128 hash = hs_hash_from_key(key, rewind_idx);
handle = geo_buffer_from_hash(scope, hash);
if(!r_handle_match(handle, r_handle_zero()))
{
break;
}
}
return handle;
}
////////////////////////////////
//~ rjf: Transfer Threads
internal B32
geo_u2x_enqueue_req(U128 hash, U64 endt_us)
{
B32 good = 0;
OS_MutexScope(geo_shared->u2x_ring_mutex) for(;;)
{
U64 unconsumed_size = geo_shared->u2x_ring_write_pos-geo_shared->u2x_ring_read_pos;
U64 available_size = geo_shared->u2x_ring_size-unconsumed_size;
if(available_size >= sizeof(hash))
{
good = 1;
geo_shared->u2x_ring_write_pos += ring_write_struct(geo_shared->u2x_ring_base, geo_shared->u2x_ring_size, geo_shared->u2x_ring_write_pos, &hash);
break;
}
if(os_now_microseconds() >= endt_us)
{
break;
}
os_condition_variable_wait(geo_shared->u2x_ring_cv, geo_shared->u2x_ring_mutex, endt_us);
}
if(good)
{
os_condition_variable_broadcast(geo_shared->u2x_ring_cv);
}
return good;
}
internal void
geo_u2x_dequeue_req(U128 *hash_out)
{
OS_MutexScope(geo_shared->u2x_ring_mutex) for(;;)
{
U64 unconsumed_size = geo_shared->u2x_ring_write_pos-geo_shared->u2x_ring_read_pos;
if(unconsumed_size >= sizeof(*hash_out))
{
geo_shared->u2x_ring_read_pos += ring_read_struct(geo_shared->u2x_ring_base, geo_shared->u2x_ring_size, geo_shared->u2x_ring_read_pos, hash_out);
break;
}
os_condition_variable_wait(geo_shared->u2x_ring_cv, geo_shared->u2x_ring_mutex, max_U64);
}
os_condition_variable_broadcast(geo_shared->u2x_ring_cv);
}
internal void
geo_xfer_thread__entry_point(void *p)
{
for(;;)
{
HS_Scope *scope = hs_scope_open();
//- rjf: decode
U128 hash = {0};
geo_u2x_dequeue_req(&hash);
//- rjf: unpack hash
U64 slot_idx = hash.u64[1]%geo_shared->slots_count;
U64 stripe_idx = slot_idx%geo_shared->stripes_count;
GEO_Slot *slot = &geo_shared->slots[slot_idx];
GEO_Stripe *stripe = &geo_shared->stripes[stripe_idx];
//- rjf: take task
B32 got_task = 0;
OS_MutexScopeR(stripe->rw_mutex)
{
for(GEO_Node *n = slot->first; n != 0; n = n->next)
{
if(u128_match(n->hash, hash))
{
got_task = !ins_atomic_u32_eval_cond_assign(&n->is_working, 1, 0);
break;
}
}
}
//- rjf: hash -> data
String8 data = {0};
if(got_task)
{
data = hs_data_from_hash(scope, hash);
}
//- rjf: data -> buffer
R_Handle buffer = {0};
if(got_task && data.size != 0)
{
buffer = r_buffer_alloc(R_ResourceKind_Static, data.size, data.str);
}
//- rjf: commit results to cache
if(got_task) OS_MutexScopeW(stripe->rw_mutex)
{
for(GEO_Node *n = slot->first; n != 0; n = n->next)
{
if(u128_match(n->hash, hash))
{
n->buffer = buffer;
ins_atomic_u32_eval_assign(&n->is_working, 0);
ins_atomic_u64_inc_eval(&n->load_count);
break;
}
}
}
hs_scope_close(scope);
}
}
////////////////////////////////
//~ rjf: Evictor Threads
internal void
geo_evictor_thread__entry_point(void *p)
{
for(;;)
{
U64 check_time_us = os_now_microseconds();
U64 check_time_user_clocks = geo_user_clock_idx();
U64 evict_threshold_us = 10*1000000;
U64 evict_threshold_user_clocks = 10;
for(U64 slot_idx = 0; slot_idx < geo_shared->slots_count; slot_idx += 1)
{
U64 stripe_idx = slot_idx%geo_shared->stripes_count;
GEO_Slot *slot = &geo_shared->slots[slot_idx];
GEO_Stripe *stripe = &geo_shared->stripes[stripe_idx];
B32 slot_has_work = 0;
OS_MutexScopeR(stripe->rw_mutex)
{
for(GEO_Node *n = slot->first; n != 0; n = n->next)
{
if(n->scope_ref_count == 0 &&
n->last_time_touched_us+evict_threshold_us <= check_time_us &&
n->last_user_clock_idx_touched+evict_threshold_user_clocks <= check_time_user_clocks &&
n->load_count != 0 &&
n->is_working == 0)
{
slot_has_work = 1;
break;
}
}
}
if(slot_has_work) OS_MutexScopeW(stripe->rw_mutex)
{
for(GEO_Node *n = slot->first, *next = 0; n != 0; n = next)
{
next = n->next;
if(n->scope_ref_count == 0 &&
n->last_time_touched_us+evict_threshold_us <= check_time_us &&
n->last_user_clock_idx_touched+evict_threshold_user_clocks <= check_time_user_clocks &&
n->load_count != 0 &&
n->is_working == 0)
{
DLLRemove(slot->first, slot->last, n);
if(!r_handle_match(n->buffer, r_handle_zero()))
{
r_buffer_release(n->buffer);
}
SLLStackPush(geo_shared->stripes_free_nodes[stripe_idx], n);
}
}
}
os_sleep_milliseconds(5);
}
os_sleep_milliseconds(1000);
}
}
+339 -339
View File
@@ -1,339 +1,339 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
////////////////////////////////
//~ rjf: Basic Helpers
#if !defined(BLAKE2_H)
#define HAVE_SSE2
#include "third_party/blake2/blake2.h"
#include "third_party/blake2/blake2b.c"
#endif
internal U128
hs_hash_from_data(String8 data)
{
U128 u128 = {0};
if(data.size != 0)
{
blake2b((U8 *)&u128.u64[0], sizeof(u128), data.str, data.size, 0, 0);
}
return u128;
}
////////////////////////////////
//~ rjf: Main Layer Initialization
internal void
hs_init(void)
{
Arena *arena = arena_alloc();
hs_shared = push_array(arena, HS_Shared, 1);
hs_shared->arena = arena;
hs_shared->slots_count = 4096;
hs_shared->stripes_count = Min(hs_shared->slots_count, os_logical_core_count());
hs_shared->slots = push_array(arena, HS_Slot, hs_shared->slots_count);
hs_shared->stripes = push_array(arena, HS_Stripe, hs_shared->stripes_count);
hs_shared->stripes_free_nodes = push_array(arena, HS_Node *, hs_shared->stripes_count);
for(U64 idx = 0; idx < hs_shared->stripes_count; idx += 1)
{
HS_Stripe *stripe = &hs_shared->stripes[idx];
stripe->arena = arena_alloc();
stripe->rw_mutex = os_rw_mutex_alloc();
stripe->cv = os_condition_variable_alloc();
}
hs_shared->key_slots_count = 4096;
hs_shared->key_stripes_count = Min(hs_shared->key_slots_count, os_logical_core_count());
hs_shared->key_slots = push_array(arena, HS_KeySlot, hs_shared->key_slots_count);
hs_shared->key_stripes = push_array(arena, HS_Stripe, hs_shared->key_stripes_count);
for(U64 idx = 0; idx < hs_shared->key_stripes_count; idx += 1)
{
HS_Stripe *stripe = &hs_shared->key_stripes[idx];
stripe->arena = arena_alloc();
stripe->rw_mutex = os_rw_mutex_alloc();
stripe->cv = os_condition_variable_alloc();
}
hs_shared->evictor_thread = os_launch_thread(hs_evictor_thread__entry_point, 0, 0);
}
////////////////////////////////
//~ rjf: Thread Context Initialization
internal void
hs_tctx_ensure_inited(void)
{
if(hs_tctx == 0)
{
Arena *arena = arena_alloc();
hs_tctx = push_array(arena, HS_TCTX, 1);
hs_tctx->arena = arena;
}
}
////////////////////////////////
//~ rjf: Cache Submission
internal U128
hs_submit_data(U128 key, Arena **data_arena, String8 data)
{
U64 key_slot_idx = key.u64[1]%hs_shared->key_slots_count;
U64 key_stripe_idx = key_slot_idx%hs_shared->key_stripes_count;
HS_KeySlot *key_slot = &hs_shared->key_slots[key_slot_idx];
HS_Stripe *key_stripe = &hs_shared->key_stripes[key_stripe_idx];
U128 hash = hs_hash_from_data(data);
U64 slot_idx = hash.u64[1]%hs_shared->slots_count;
U64 stripe_idx = slot_idx%hs_shared->stripes_count;
HS_Slot *slot = &hs_shared->slots[slot_idx];
HS_Stripe *stripe = &hs_shared->stripes[stripe_idx];
//- rjf: commit data to cache - if already there, just bump key refcount
ProfScope("commit data to cache - if already there, just bump key refcount") OS_MutexScopeW(stripe->rw_mutex)
{
HS_Node *existing_node = 0;
for(HS_Node *n = slot->first; n != 0; n = n->next)
{
if(u128_match(n->hash, hash))
{
existing_node = n;
break;
}
}
if(existing_node == 0)
{
HS_Node *node = hs_shared->stripes_free_nodes[stripe_idx];
if(node)
{
SLLStackPop(hs_shared->stripes_free_nodes[stripe_idx]);
}
else
{
node = push_array(stripe->arena, HS_Node, 1);
}
node->hash = hash;
node->arena = *data_arena;
node->data = data;
node->scope_ref_count = 0;
node->key_ref_count = 1;
DLLPushBack(slot->first, slot->last, node);
}
else
{
existing_node->key_ref_count += 1;
arena_release(*data_arena);
}
*data_arena = 0;
}
//- rjf: commit this hash to key cache
U128 key_expired_hash = {0};
ProfScope("commit this hash to key cache") OS_MutexScopeW(key_stripe->rw_mutex)
{
HS_KeyNode *key_node = 0;
for(HS_KeyNode *n = key_slot->first; n != 0; n = n->next)
{
if(u128_match(n->key, key))
{
key_node = n;
break;
}
}
if(!key_node)
{
key_node = push_array(key_stripe->arena, HS_KeyNode, 1);
key_node->key = key;
SLLQueuePush(key_slot->first, key_slot->last, key_node);
}
if(key_node)
{
if(key_node->hash_history_gen >= ArrayCount(key_node->hash_history))
{
key_expired_hash = key_node->hash_history[key_node->hash_history_gen%ArrayCount(key_node->hash_history)];
}
key_node->hash_history[key_node->hash_history_gen%ArrayCount(key_node->hash_history)] = hash;
key_node->hash_history_gen += 1;
}
}
//- rjf: if this key's history cache was full, dec key ref count of oldest hash
ProfScope("if this key's history cache was full, dec key ref count of oldest hash")
if(!u128_match(key_expired_hash, u128_zero()))
{
U64 old_hash_slot_idx = key_expired_hash.u64[1]%hs_shared->slots_count;
U64 old_hash_stripe_idx = old_hash_slot_idx%hs_shared->stripes_count;
HS_Slot *old_hash_slot = &hs_shared->slots[old_hash_slot_idx];
HS_Stripe *old_hash_stripe = &hs_shared->stripes[old_hash_stripe_idx];
OS_MutexScopeR(old_hash_stripe->rw_mutex)
{
for(HS_Node *n = old_hash_slot->first; n != 0; n = n->next)
{
if(u128_match(n->hash, key_expired_hash))
{
ins_atomic_u64_dec_eval(&n->key_ref_count);
break;
}
}
}
}
return hash;
}
////////////////////////////////
//~ rjf: Scoped Access
internal HS_Scope *
hs_scope_open(void)
{
hs_tctx_ensure_inited();
HS_Scope *scope = hs_tctx->free_scope;
if(scope)
{
SLLStackPop(hs_tctx->free_scope);
}
else
{
scope = push_array_no_zero(hs_tctx->arena, HS_Scope, 1);
}
MemoryZeroStruct(scope);
return scope;
}
internal void
hs_scope_close(HS_Scope *scope)
{
for(HS_Touch *touch = scope->top_touch, *next = 0; touch != 0; touch = next)
{
U128 hash = touch->hash;
next = touch->next;
U64 slot_idx = hash.u64[1]%hs_shared->slots_count;
U64 stripe_idx = slot_idx%hs_shared->stripes_count;
HS_Slot *slot = &hs_shared->slots[slot_idx];
HS_Stripe *stripe = &hs_shared->stripes[stripe_idx];
OS_MutexScopeR(stripe->rw_mutex)
{
for(HS_Node *n = slot->first; n != 0; n = n->next)
{
if(u128_match(hash, n->hash))
{
ins_atomic_u64_dec_eval(&n->scope_ref_count);
break;
}
}
}
SLLStackPush(hs_tctx->free_touch, touch);
}
SLLStackPush(hs_tctx->free_scope, scope);
}
internal void
hs_scope_touch_node__stripe_r_guarded(HS_Scope *scope, HS_Node *node)
{
HS_Touch *touch = hs_tctx->free_touch;
ins_atomic_u64_inc_eval(&node->scope_ref_count);
if(touch != 0)
{
SLLStackPop(hs_tctx->free_touch);
}
else
{
touch = push_array_no_zero(hs_tctx->arena, HS_Touch, 1);
}
MemoryZeroStruct(touch);
touch->hash = node->hash;
SLLStackPush(scope->top_touch, touch);
}
////////////////////////////////
//~ rjf: Cache Lookup
internal U128
hs_hash_from_key(U128 key, U64 rewind_count)
{
U128 result = {0};
U64 key_slot_idx = key.u64[1]%hs_shared->key_slots_count;
U64 key_stripe_idx = key_slot_idx%hs_shared->key_stripes_count;
HS_KeySlot *key_slot = &hs_shared->key_slots[key_slot_idx];
HS_Stripe *key_stripe = &hs_shared->key_stripes[key_stripe_idx];
OS_MutexScopeR(key_stripe->rw_mutex)
{
for(HS_KeyNode *n = key_slot->first; n != 0; n = n->next)
{
if(u128_match(n->key, key) && n->hash_history_gen > 0 && n->hash_history_gen-1 >= rewind_count)
{
result = n->hash_history[(n->hash_history_gen-1-rewind_count)%ArrayCount(n->hash_history)];
break;
}
}
}
return result;
}
internal String8
hs_data_from_hash(HS_Scope *scope, U128 hash)
{
String8 result = {0};
U64 slot_idx = hash.u64[1]%hs_shared->slots_count;
U64 stripe_idx = slot_idx%hs_shared->stripes_count;
HS_Slot *slot = &hs_shared->slots[slot_idx];
HS_Stripe *stripe = &hs_shared->stripes[stripe_idx];
OS_MutexScopeR(stripe->rw_mutex)
{
for(HS_Node *n = slot->first; n != 0; n = n->next)
{
if(u128_match(n->hash, hash))
{
result = n->data;
hs_scope_touch_node__stripe_r_guarded(scope, n);
break;
}
}
}
return result;
}
////////////////////////////////
//~ rjf: Evictor Thread
internal void
hs_evictor_thread__entry_point(void *p)
{
for(;;)
{
for(U64 slot_idx = 0; slot_idx < hs_shared->slots_count; slot_idx += 1)
{
U64 stripe_idx = slot_idx%hs_shared->stripes_count;
HS_Slot *slot = &hs_shared->slots[slot_idx];
HS_Stripe *stripe = &hs_shared->stripes[stripe_idx];
B32 slot_has_work = 0;
OS_MutexScopeR(stripe->rw_mutex)
{
for(HS_Node *n = slot->first; n != 0; n = n->next)
{
U64 key_ref_count = ins_atomic_u64_eval(&n->key_ref_count);
U64 scope_ref_count = ins_atomic_u64_eval(&n->scope_ref_count);
if(key_ref_count == 0 && scope_ref_count == 0)
{
slot_has_work = 1;
break;
}
}
}
if(slot_has_work) OS_MutexScopeW(stripe->rw_mutex)
{
for(HS_Node *n = slot->first, *next = 0; n != 0; n = next)
{
next = n->next;
U64 key_ref_count = ins_atomic_u64_eval(&n->key_ref_count);
U64 scope_ref_count = ins_atomic_u64_eval(&n->scope_ref_count);
if(key_ref_count == 0 && scope_ref_count == 0)
{
DLLRemove(slot->first, slot->last, n);
SLLStackPush(hs_shared->stripes_free_nodes[stripe_idx], n);
arena_release(n->arena);
}
}
}
}
os_sleep_milliseconds(1000);
}
}
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
////////////////////////////////
//~ rjf: Basic Helpers
#if !defined(BLAKE2_H)
#define HAVE_SSE2
#include "third_party/blake2/blake2.h"
#include "third_party/blake2/blake2b.c"
#endif
internal U128
hs_hash_from_data(String8 data)
{
U128 u128 = {0};
if(data.size != 0)
{
blake2b((U8 *)&u128.u64[0], sizeof(u128), data.str, data.size, 0, 0);
}
return u128;
}
////////////////////////////////
//~ rjf: Main Layer Initialization
internal void
hs_init(void)
{
Arena *arena = arena_alloc();
hs_shared = push_array(arena, HS_Shared, 1);
hs_shared->arena = arena;
hs_shared->slots_count = 4096;
hs_shared->stripes_count = Min(hs_shared->slots_count, os_get_system_info()->logical_processor_count);
hs_shared->slots = push_array(arena, HS_Slot, hs_shared->slots_count);
hs_shared->stripes = push_array(arena, HS_Stripe, hs_shared->stripes_count);
hs_shared->stripes_free_nodes = push_array(arena, HS_Node *, hs_shared->stripes_count);
for(U64 idx = 0; idx < hs_shared->stripes_count; idx += 1)
{
HS_Stripe *stripe = &hs_shared->stripes[idx];
stripe->arena = arena_alloc();
stripe->rw_mutex = os_rw_mutex_alloc();
stripe->cv = os_condition_variable_alloc();
}
hs_shared->key_slots_count = 4096;
hs_shared->key_stripes_count = Min(hs_shared->key_slots_count, os_get_system_info()->logical_processor_count);
hs_shared->key_slots = push_array(arena, HS_KeySlot, hs_shared->key_slots_count);
hs_shared->key_stripes = push_array(arena, HS_Stripe, hs_shared->key_stripes_count);
for(U64 idx = 0; idx < hs_shared->key_stripes_count; idx += 1)
{
HS_Stripe *stripe = &hs_shared->key_stripes[idx];
stripe->arena = arena_alloc();
stripe->rw_mutex = os_rw_mutex_alloc();
stripe->cv = os_condition_variable_alloc();
}
hs_shared->evictor_thread = os_thread_launch(hs_evictor_thread__entry_point, 0, 0);
}
////////////////////////////////
//~ rjf: Thread Context Initialization
internal void
hs_tctx_ensure_inited(void)
{
if(hs_tctx == 0)
{
Arena *arena = arena_alloc();
hs_tctx = push_array(arena, HS_TCTX, 1);
hs_tctx->arena = arena;
}
}
////////////////////////////////
//~ rjf: Cache Submission
internal U128
hs_submit_data(U128 key, Arena **data_arena, String8 data)
{
U64 key_slot_idx = key.u64[1]%hs_shared->key_slots_count;
U64 key_stripe_idx = key_slot_idx%hs_shared->key_stripes_count;
HS_KeySlot *key_slot = &hs_shared->key_slots[key_slot_idx];
HS_Stripe *key_stripe = &hs_shared->key_stripes[key_stripe_idx];
U128 hash = hs_hash_from_data(data);
U64 slot_idx = hash.u64[1]%hs_shared->slots_count;
U64 stripe_idx = slot_idx%hs_shared->stripes_count;
HS_Slot *slot = &hs_shared->slots[slot_idx];
HS_Stripe *stripe = &hs_shared->stripes[stripe_idx];
//- rjf: commit data to cache - if already there, just bump key refcount
ProfScope("commit data to cache - if already there, just bump key refcount") OS_MutexScopeW(stripe->rw_mutex)
{
HS_Node *existing_node = 0;
for(HS_Node *n = slot->first; n != 0; n = n->next)
{
if(u128_match(n->hash, hash))
{
existing_node = n;
break;
}
}
if(existing_node == 0)
{
HS_Node *node = hs_shared->stripes_free_nodes[stripe_idx];
if(node)
{
SLLStackPop(hs_shared->stripes_free_nodes[stripe_idx]);
}
else
{
node = push_array(stripe->arena, HS_Node, 1);
}
node->hash = hash;
node->arena = *data_arena;
node->data = data;
node->scope_ref_count = 0;
node->key_ref_count = 1;
DLLPushBack(slot->first, slot->last, node);
}
else
{
existing_node->key_ref_count += 1;
arena_release(*data_arena);
}
*data_arena = 0;
}
//- rjf: commit this hash to key cache
U128 key_expired_hash = {0};
ProfScope("commit this hash to key cache") OS_MutexScopeW(key_stripe->rw_mutex)
{
HS_KeyNode *key_node = 0;
for(HS_KeyNode *n = key_slot->first; n != 0; n = n->next)
{
if(u128_match(n->key, key))
{
key_node = n;
break;
}
}
if(!key_node)
{
key_node = push_array(key_stripe->arena, HS_KeyNode, 1);
key_node->key = key;
SLLQueuePush(key_slot->first, key_slot->last, key_node);
}
if(key_node)
{
if(key_node->hash_history_gen >= ArrayCount(key_node->hash_history))
{
key_expired_hash = key_node->hash_history[key_node->hash_history_gen%ArrayCount(key_node->hash_history)];
}
key_node->hash_history[key_node->hash_history_gen%ArrayCount(key_node->hash_history)] = hash;
key_node->hash_history_gen += 1;
}
}
//- rjf: if this key's history cache was full, dec key ref count of oldest hash
ProfScope("if this key's history cache was full, dec key ref count of oldest hash")
if(!u128_match(key_expired_hash, u128_zero()))
{
U64 old_hash_slot_idx = key_expired_hash.u64[1]%hs_shared->slots_count;
U64 old_hash_stripe_idx = old_hash_slot_idx%hs_shared->stripes_count;
HS_Slot *old_hash_slot = &hs_shared->slots[old_hash_slot_idx];
HS_Stripe *old_hash_stripe = &hs_shared->stripes[old_hash_stripe_idx];
OS_MutexScopeR(old_hash_stripe->rw_mutex)
{
for(HS_Node *n = old_hash_slot->first; n != 0; n = n->next)
{
if(u128_match(n->hash, key_expired_hash))
{
ins_atomic_u64_dec_eval(&n->key_ref_count);
break;
}
}
}
}
return hash;
}
////////////////////////////////
//~ rjf: Scoped Access
internal HS_Scope *
hs_scope_open(void)
{
hs_tctx_ensure_inited();
HS_Scope *scope = hs_tctx->free_scope;
if(scope)
{
SLLStackPop(hs_tctx->free_scope);
}
else
{
scope = push_array_no_zero(hs_tctx->arena, HS_Scope, 1);
}
MemoryZeroStruct(scope);
return scope;
}
internal void
hs_scope_close(HS_Scope *scope)
{
for(HS_Touch *touch = scope->top_touch, *next = 0; touch != 0; touch = next)
{
U128 hash = touch->hash;
next = touch->next;
U64 slot_idx = hash.u64[1]%hs_shared->slots_count;
U64 stripe_idx = slot_idx%hs_shared->stripes_count;
HS_Slot *slot = &hs_shared->slots[slot_idx];
HS_Stripe *stripe = &hs_shared->stripes[stripe_idx];
OS_MutexScopeR(stripe->rw_mutex)
{
for(HS_Node *n = slot->first; n != 0; n = n->next)
{
if(u128_match(hash, n->hash))
{
ins_atomic_u64_dec_eval(&n->scope_ref_count);
break;
}
}
}
SLLStackPush(hs_tctx->free_touch, touch);
}
SLLStackPush(hs_tctx->free_scope, scope);
}
internal void
hs_scope_touch_node__stripe_r_guarded(HS_Scope *scope, HS_Node *node)
{
HS_Touch *touch = hs_tctx->free_touch;
ins_atomic_u64_inc_eval(&node->scope_ref_count);
if(touch != 0)
{
SLLStackPop(hs_tctx->free_touch);
}
else
{
touch = push_array_no_zero(hs_tctx->arena, HS_Touch, 1);
}
MemoryZeroStruct(touch);
touch->hash = node->hash;
SLLStackPush(scope->top_touch, touch);
}
////////////////////////////////
//~ rjf: Cache Lookup
internal U128
hs_hash_from_key(U128 key, U64 rewind_count)
{
U128 result = {0};
U64 key_slot_idx = key.u64[1]%hs_shared->key_slots_count;
U64 key_stripe_idx = key_slot_idx%hs_shared->key_stripes_count;
HS_KeySlot *key_slot = &hs_shared->key_slots[key_slot_idx];
HS_Stripe *key_stripe = &hs_shared->key_stripes[key_stripe_idx];
OS_MutexScopeR(key_stripe->rw_mutex)
{
for(HS_KeyNode *n = key_slot->first; n != 0; n = n->next)
{
if(u128_match(n->key, key) && n->hash_history_gen > 0 && n->hash_history_gen-1 >= rewind_count)
{
result = n->hash_history[(n->hash_history_gen-1-rewind_count)%ArrayCount(n->hash_history)];
break;
}
}
}
return result;
}
internal String8
hs_data_from_hash(HS_Scope *scope, U128 hash)
{
String8 result = {0};
U64 slot_idx = hash.u64[1]%hs_shared->slots_count;
U64 stripe_idx = slot_idx%hs_shared->stripes_count;
HS_Slot *slot = &hs_shared->slots[slot_idx];
HS_Stripe *stripe = &hs_shared->stripes[stripe_idx];
OS_MutexScopeR(stripe->rw_mutex)
{
for(HS_Node *n = slot->first; n != 0; n = n->next)
{
if(u128_match(n->hash, hash))
{
result = n->data;
hs_scope_touch_node__stripe_r_guarded(scope, n);
break;
}
}
}
return result;
}
////////////////////////////////
//~ rjf: Evictor Thread
internal void
hs_evictor_thread__entry_point(void *p)
{
for(;;)
{
for(U64 slot_idx = 0; slot_idx < hs_shared->slots_count; slot_idx += 1)
{
U64 stripe_idx = slot_idx%hs_shared->stripes_count;
HS_Slot *slot = &hs_shared->slots[slot_idx];
HS_Stripe *stripe = &hs_shared->stripes[stripe_idx];
B32 slot_has_work = 0;
OS_MutexScopeR(stripe->rw_mutex)
{
for(HS_Node *n = slot->first; n != 0; n = n->next)
{
U64 key_ref_count = ins_atomic_u64_eval(&n->key_ref_count);
U64 scope_ref_count = ins_atomic_u64_eval(&n->scope_ref_count);
if(key_ref_count == 0 && scope_ref_count == 0)
{
slot_has_work = 1;
break;
}
}
}
if(slot_has_work) OS_MutexScopeW(stripe->rw_mutex)
{
for(HS_Node *n = slot->first, *next = 0; n != 0; n = next)
{
next = n->next;
U64 key_ref_count = ins_atomic_u64_eval(&n->key_ref_count);
U64 scope_ref_count = ins_atomic_u64_eval(&n->scope_ref_count);
if(key_ref_count == 0 && scope_ref_count == 0)
{
DLLRemove(slot->first, slot->last, n);
SLLStackPush(hs_shared->stripes_free_nodes[stripe_idx], n);
arena_release(n->arena);
}
}
}
}
os_sleep_milliseconds(1000);
}
}
+1532 -1519
View File
File diff suppressed because it is too large Load Diff
+39 -39
View File
@@ -1,39 +1,39 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#ifndef OS_INC_H
#define OS_INC_H
#if !defined(OS_FEATURE_SOCKET)
# define OS_FEATURE_SOCKET 0
#endif
#if !defined(OS_FEATURE_GRAPHICAL)
# define OS_FEATURE_GRAPHICAL 0
#endif
#include "core/metagen_os_core.h"
#if OS_FEATURE_SOCKET
#include "socket/metagen_os_socket.h"
#endif
#if OS_FEATURE_GRAPHICAL
#include "gfx/metagen_os_gfx.h"
#endif
#if OS_WINDOWS
# include "core/win32/metagen_os_core_win32.h"
# if OS_FEATURE_SOCKET
# include "socket/win32/metagen_os_socket_win32.h"
# endif
# if OS_FEATURE_GRAPHICAL
# include "gfx/win32/metagen_os_gfx_win32.h"
# endif
#elif OS_LINUX
# include "core/linux/metagen_os_core_linux.h"
#else
# error no OS layer setup
#endif
#endif //OS_SWITCH_H
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#ifndef OS_INC_H
#define OS_INC_H
#if !defined(OS_FEATURE_SOCKET)
# define OS_FEATURE_SOCKET 0
#endif
#if !defined(OS_FEATURE_GRAPHICAL)
# define OS_FEATURE_GRAPHICAL 0
#endif
#include "core/metagen_os_core.h"
#if OS_FEATURE_SOCKET
#include "socket/metagen_os_socket.h"
#endif
#if OS_FEATURE_GRAPHICAL
#include "gfx/metagen_os_gfx.h"
#endif
#if OS_WINDOWS
# include "core/win32/metagen_os_core_win32.h"
# if OS_FEATURE_SOCKET
# include "socket/win32/metagen_os_socket_win32.h"
# endif
# if OS_FEATURE_GRAPHICAL
# include "gfx/win32/metagen_os_gfx_win32.h"
# endif
#elif OS_LINUX
# include "core/linux/metagen_os_core_linux.h"
#else
# error no OS layer setup
#endif
#endif // OS_INC_H
+142 -142
View File
@@ -1,142 +1,142 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
////////////////////////////////
//~ rjf: Main Layer Initialization
internal void
mtx_init(void)
{
Arena *arena = arena_alloc();
mtx_shared = push_array(arena, MTX_Shared, 1);
mtx_shared->arena = arena;
mtx_shared->slots_count = 256;
mtx_shared->stripes_count = Min(mtx_shared->slots_count, os_logical_core_count());
mtx_shared->slots = push_array(arena, MTX_Slot, mtx_shared->slots_count);
mtx_shared->stripes = push_array(arena, MTX_Stripe, mtx_shared->stripes_count);
for(U64 idx = 0; idx < mtx_shared->stripes_count; idx += 1)
{
mtx_shared->stripes[idx].arena = arena_alloc();
mtx_shared->stripes[idx].rw_mutex = os_rw_mutex_alloc();
}
mtx_shared->mut_threads_count = Min(os_logical_core_count(), 4);
mtx_shared->mut_threads = push_array(arena, MTX_MutThread, mtx_shared->mut_threads_count);
for(U64 idx = 0; idx < mtx_shared->mut_threads_count; idx += 1)
{
mtx_shared->mut_threads[idx].ring_size = KB(64);
mtx_shared->mut_threads[idx].ring_base = push_array_no_zero(arena, U8, mtx_shared->mut_threads[idx].ring_size);
mtx_shared->mut_threads[idx].cv = os_condition_variable_alloc();
mtx_shared->mut_threads[idx].mutex = os_mutex_alloc();
mtx_shared->mut_threads[idx].thread = os_launch_thread(mtx_mut_thread__entry_point, &mtx_shared->mut_threads[idx], 0);
}
}
////////////////////////////////
//~ rjf: Buffer Operations
internal void
mtx_push_op(U128 buffer_key, MTX_Op op)
{
MTX_MutThread *thread = &mtx_shared->mut_threads[buffer_key.u64[1]%mtx_shared->mut_threads_count];
mtx_enqueue_op(thread, buffer_key, op);
}
////////////////////////////////
//~ rjf: Mutation Threads
internal void
mtx_enqueue_op(MTX_MutThread *thread, U128 buffer_key, MTX_Op op)
{
// TODO(rjf): if op.replace is too big, need to split into multiple edits
OS_MutexScope(thread->mutex) for(;;)
{
U64 unconsumed_size = thread->ring_write_pos - thread->ring_read_pos;
U64 available_size = thread->ring_size - unconsumed_size;
if(available_size >= sizeof(buffer_key) + sizeof(op.range) + sizeof(op.replace.size) + op.replace.size)
{
thread->ring_write_pos += ring_write_struct(thread->ring_base, thread->ring_size, thread->ring_write_pos, &buffer_key);
thread->ring_write_pos += ring_write_struct(thread->ring_base, thread->ring_size, thread->ring_write_pos, &op.range);
thread->ring_write_pos += ring_write_struct(thread->ring_base, thread->ring_size, thread->ring_write_pos, &op.replace.size);
thread->ring_write_pos += ring_write(thread->ring_base, thread->ring_size, thread->ring_write_pos, op.replace.str, op.replace.size);
thread->ring_write_pos += 7;
thread->ring_write_pos -= thread->ring_write_pos%8;
break;
}
os_condition_variable_wait(thread->cv, thread->mutex, max_U64);
}
os_condition_variable_broadcast(thread->cv);
}
internal void
mtx_dequeue_op(Arena *arena, MTX_MutThread *thread, U128 *buffer_key_out, MTX_Op *op_out)
{
OS_MutexScope(thread->mutex) for(;;)
{
U64 unconsumed_size = thread->ring_write_pos - thread->ring_read_pos;
if(unconsumed_size >= sizeof(*buffer_key_out) + sizeof(op_out->range) + sizeof(op_out->replace.size))
{
thread->ring_read_pos += ring_read_struct(thread->ring_base, thread->ring_size, thread->ring_read_pos, buffer_key_out);
thread->ring_read_pos += ring_read_struct(thread->ring_base, thread->ring_size, thread->ring_read_pos, &op_out->range);
thread->ring_read_pos += ring_read_struct(thread->ring_base, thread->ring_size, thread->ring_read_pos, &op_out->replace.size);
op_out->replace.str = push_array_no_zero(arena, U8, op_out->replace.size);
thread->ring_read_pos += ring_read(thread->ring_base, thread->ring_size, thread->ring_read_pos, op_out->replace.str, op_out->replace.size);
thread->ring_read_pos += 7;
thread->ring_read_pos -= thread->ring_read_pos%8;
break;
}
os_condition_variable_wait(thread->cv, thread->mutex, max_U64);
}
os_condition_variable_broadcast(thread->cv);
}
internal void
mtx_mut_thread__entry_point(void *p)
{
MTX_MutThread *mut_thread = (MTX_MutThread *)p;
ThreadNameF("[mtx] mut thread #%I64u", (U64)(mut_thread - mtx_shared->mut_threads));
for(;;)
{
Temp scratch = scratch_begin(0, 0);
HS_Scope *hs_scope = hs_scope_open();
//- rjf: get next op
U128 buffer_key = {0};
MTX_Op op = {0};
mtx_dequeue_op(scratch.arena, mut_thread, &buffer_key, &op);
//- rjf: get buffer's current data
U128 hash = hs_hash_from_key(buffer_key, 0);
String8 data = hs_data_from_hash(hs_scope, hash);
//- rjf: clamp op by data
op.range.min = Min(op.range.min, data.size);
op.range.max = Min(op.range.max, data.size);
//- rjf: construct new buffer
if(op.range.max != op.range.min || op.replace.size != 0)
{
U64 new_data_size = data.size + op.replace.size - dim_1u64(op.range);
Arena *arena = arena_alloc__sized(new_data_size + ARENA_HEADER_SIZE, new_data_size + ARENA_HEADER_SIZE);
U8 *new_data_base = push_array_no_zero(arena, U8, new_data_size);
String8 pre_replace_data = str8_substr(data, r1u64(0, op.range.min));
String8 post_replace_data = str8_substr(data, r1u64(op.range.max, data.size));
if(pre_replace_data.size != 0)
{
MemoryCopy(new_data_base+0, pre_replace_data.str, pre_replace_data.size);
}
if(op.replace.size != 0)
{
MemoryCopy(new_data_base+pre_replace_data.size, op.replace.str, op.replace.size);
}
if(post_replace_data.size != 0)
{
MemoryCopy(new_data_base+pre_replace_data.size+op.replace.size, post_replace_data.str, post_replace_data.size);
}
String8 new_data = str8(new_data_base, new_data_size);
hs_submit_data(buffer_key, &arena, new_data);
}
hs_scope_close(hs_scope);
scratch_end(scratch);
}
}
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
////////////////////////////////
//~ rjf: Main Layer Initialization
internal void
mtx_init(void)
{
Arena *arena = arena_alloc();
mtx_shared = push_array(arena, MTX_Shared, 1);
mtx_shared->arena = arena;
mtx_shared->slots_count = 256;
mtx_shared->stripes_count = Min(mtx_shared->slots_count, os_get_system_info()->logical_processor_count);
mtx_shared->slots = push_array(arena, MTX_Slot, mtx_shared->slots_count);
mtx_shared->stripes = push_array(arena, MTX_Stripe, mtx_shared->stripes_count);
for(U64 idx = 0; idx < mtx_shared->stripes_count; idx += 1)
{
mtx_shared->stripes[idx].arena = arena_alloc();
mtx_shared->stripes[idx].rw_mutex = os_rw_mutex_alloc();
}
mtx_shared->mut_threads_count = Min(os_get_system_info()->logical_processor_count, 4);
mtx_shared->mut_threads = push_array(arena, MTX_MutThread, mtx_shared->mut_threads_count);
for(U64 idx = 0; idx < mtx_shared->mut_threads_count; idx += 1)
{
mtx_shared->mut_threads[idx].ring_size = KB(64);
mtx_shared->mut_threads[idx].ring_base = push_array_no_zero(arena, U8, mtx_shared->mut_threads[idx].ring_size);
mtx_shared->mut_threads[idx].cv = os_condition_variable_alloc();
mtx_shared->mut_threads[idx].mutex = os_mutex_alloc();
mtx_shared->mut_threads[idx].thread = os_thread_launch(mtx_mut_thread__entry_point, &mtx_shared->mut_threads[idx], 0);
}
}
////////////////////////////////
//~ rjf: Buffer Operations
internal void
mtx_push_op(U128 buffer_key, MTX_Op op)
{
MTX_MutThread *thread = &mtx_shared->mut_threads[buffer_key.u64[1]%mtx_shared->mut_threads_count];
mtx_enqueue_op(thread, buffer_key, op);
}
////////////////////////////////
//~ rjf: Mutation Threads
internal void
mtx_enqueue_op(MTX_MutThread *thread, U128 buffer_key, MTX_Op op)
{
// TODO(rjf): if op.replace is too big, need to split into multiple edits
OS_MutexScope(thread->mutex) for(;;)
{
U64 unconsumed_size = thread->ring_write_pos - thread->ring_read_pos;
U64 available_size = thread->ring_size - unconsumed_size;
if(available_size >= sizeof(buffer_key) + sizeof(op.range) + sizeof(op.replace.size) + op.replace.size)
{
thread->ring_write_pos += ring_write_struct(thread->ring_base, thread->ring_size, thread->ring_write_pos, &buffer_key);
thread->ring_write_pos += ring_write_struct(thread->ring_base, thread->ring_size, thread->ring_write_pos, &op.range);
thread->ring_write_pos += ring_write_struct(thread->ring_base, thread->ring_size, thread->ring_write_pos, &op.replace.size);
thread->ring_write_pos += ring_write(thread->ring_base, thread->ring_size, thread->ring_write_pos, op.replace.str, op.replace.size);
thread->ring_write_pos += 7;
thread->ring_write_pos -= thread->ring_write_pos%8;
break;
}
os_condition_variable_wait(thread->cv, thread->mutex, max_U64);
}
os_condition_variable_broadcast(thread->cv);
}
internal void
mtx_dequeue_op(Arena *arena, MTX_MutThread *thread, U128 *buffer_key_out, MTX_Op *op_out)
{
OS_MutexScope(thread->mutex) for(;;)
{
U64 unconsumed_size = thread->ring_write_pos - thread->ring_read_pos;
if(unconsumed_size >= sizeof(*buffer_key_out) + sizeof(op_out->range) + sizeof(op_out->replace.size))
{
thread->ring_read_pos += ring_read_struct(thread->ring_base, thread->ring_size, thread->ring_read_pos, buffer_key_out);
thread->ring_read_pos += ring_read_struct(thread->ring_base, thread->ring_size, thread->ring_read_pos, &op_out->range);
thread->ring_read_pos += ring_read_struct(thread->ring_base, thread->ring_size, thread->ring_read_pos, &op_out->replace.size);
op_out->replace.str = push_array_no_zero(arena, U8, op_out->replace.size);
thread->ring_read_pos += ring_read(thread->ring_base, thread->ring_size, thread->ring_read_pos, op_out->replace.str, op_out->replace.size);
thread->ring_read_pos += 7;
thread->ring_read_pos -= thread->ring_read_pos%8;
break;
}
os_condition_variable_wait(thread->cv, thread->mutex, max_U64);
}
os_condition_variable_broadcast(thread->cv);
}
internal void
mtx_mut_thread__entry_point(void *p)
{
MTX_MutThread *mut_thread = (MTX_MutThread *)p;
ThreadNameF("[mtx] mut thread #%I64u", (U64)(mut_thread - mtx_shared->mut_threads));
for(;;)
{
Temp scratch = scratch_begin(0, 0);
HS_Scope *hs_scope = hs_scope_open();
//- rjf: get next op
U128 buffer_key = {0};
MTX_Op op = {0};
mtx_dequeue_op(scratch.arena, mut_thread, &buffer_key, &op);
//- rjf: get buffer's current data
U128 hash = hs_hash_from_key(buffer_key, 0);
String8 data = hs_data_from_hash(hs_scope, hash);
//- rjf: clamp op by data
op.range.min = Min(op.range.min, data.size);
op.range.max = Min(op.range.max, data.size);
//- rjf: construct new buffer
if(op.range.max != op.range.min || op.replace.size != 0)
{
U64 new_data_size = data.size + op.replace.size - dim_1u64(op.range);
Arena *arena = arena_alloc(.commit_size = new_data_size + ARENA_HEADER_SIZE, .reserve_size = new_data_size + ARENA_HEADER_SIZE);
U8 *new_data_base = push_array_no_zero(arena, U8, new_data_size);
String8 pre_replace_data = str8_substr(data, r1u64(0, op.range.min));
String8 post_replace_data = str8_substr(data, r1u64(op.range.max, data.size));
if(pre_replace_data.size != 0)
{
MemoryCopy(new_data_base+0, pre_replace_data.str, pre_replace_data.size);
}
if(op.replace.size != 0)
{
MemoryCopy(new_data_base+pre_replace_data.size, op.replace.str, op.replace.size);
}
if(post_replace_data.size != 0)
{
MemoryCopy(new_data_base+pre_replace_data.size+op.replace.size, post_replace_data.str, post_replace_data.size);
}
String8 new_data = str8(new_data_base, new_data_size);
hs_submit_data(buffer_key, &arena, new_data);
}
hs_scope_close(hs_scope);
scratch_end(scratch);
}
}
File diff suppressed because it is too large Load Diff
+7 -88
View File
@@ -1,88 +1,7 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#ifndef LINUX_H
#define LINUX_H
////////////////////////////////
//~ NOTE(allen): Get all these linux includes
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <linux/limits.h>
#include <time.h>
#include <dirent.h>
#include <pthread.h>
#include <sys/syscall.h>
#include <signal.h>
#include <errno.h>
#include <dlfcn.h>
#include <sys/sysinfo.h>
////////////////////////////////
//~ NOTE(allen): File Iterator
struct LNX_FileIter{
int fd;
DIR *dir;
};
StaticAssert(sizeof(Member(OS_FileIter, memory)) >= sizeof(LNX_FileIter), file_iter_memory_size);
////////////////////////////////
//~ NOTE(allen): Threading Entities
enum LNX_EntityKind{
LNX_EntityKind_Null,
LNX_EntityKind_Thread,
LNX_EntityKind_Mutex,
LNX_EntityKind_ConditionVariable,
};
struct LNX_Entity{
LNX_Entity *next;
LNX_EntityKind kind;
volatile U32 reference_mask;
union{
struct{
OS_ThreadFunctionType *func;
void *ptr;
pthread_t handle;
} thread;
pthread_mutex_t mutex;
pthread_cond_t cond;
};
};
////////////////////////////////
//~ NOTE(allen): Safe Call Chain
struct LNX_SafeCallChain{
LNX_SafeCallChain *next;
OS_ThreadFunctionType *fail_handler;
void *ptr;
};
////////////////////////////////
//~ NOTE(allen): Helpers
internal B32 lnx_write_list_to_file_descriptor(int fd, String8List list);
internal void lnx_date_time_from_tm(DateTime *out, struct tm *in, U32 msec);
internal void lnx_tm_from_date_time(struct tm *out, DateTime *in);
internal void lnx_dense_time_from_timespec(DenseTime *out, struct timespec *in);
internal void lnx_file_properties_from_stat(FileProperties *out, struct stat *in);
internal String8 lnx_string_from_signal(int signum);
internal String8 lnx_string_from_errno(int error_number);
internal LNX_Entity* lnx_alloc_entity(LNX_EntityKind kind);
internal void lnx_free_entity(LNX_Entity *entity);
internal void* lnx_thread_base(void *ptr);
internal void lnx_safe_call_sig_handler(int);
#endif //LINUX_H
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#ifndef OS_CORE_LINUX_H
#define OS_CORE_LINUX_H
#endif // OS_CORE_LINUX_H
File diff suppressed because it is too large Load Diff
+88
View File
@@ -0,0 +1,88 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#ifndef LINUX_H
#define LINUX_H
////////////////////////////////
//~ NOTE(allen): Get all these linux includes
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <linux/limits.h>
#include <time.h>
#include <dirent.h>
#include <pthread.h>
#include <sys/syscall.h>
#include <signal.h>
#include <errno.h>
#include <dlfcn.h>
#include <sys/sysinfo.h>
////////////////////////////////
//~ NOTE(allen): File Iterator
struct LNX_FileIter{
int fd;
DIR *dir;
};
StaticAssert(sizeof(Member(OS_FileIter, memory)) >= sizeof(LNX_FileIter), file_iter_memory_size);
////////////////////////////////
//~ NOTE(allen): Threading Entities
enum LNX_EntityKind{
LNX_EntityKind_Null,
LNX_EntityKind_Thread,
LNX_EntityKind_Mutex,
LNX_EntityKind_ConditionVariable,
};
struct LNX_Entity{
LNX_Entity *next;
LNX_EntityKind kind;
volatile U32 reference_mask;
union{
struct{
OS_ThreadFunctionType *func;
void *ptr;
pthread_t handle;
} thread;
pthread_mutex_t mutex;
pthread_cond_t cond;
};
};
////////////////////////////////
//~ NOTE(allen): Safe Call Chain
struct LNX_SafeCallChain{
LNX_SafeCallChain *next;
OS_ThreadFunctionType *fail_handler;
void *ptr;
};
////////////////////////////////
//~ NOTE(allen): Helpers
internal B32 lnx_write_list_to_file_descriptor(int fd, String8List list);
internal void lnx_date_time_from_tm(DateTime *out, struct tm *in, U32 msec);
internal void lnx_tm_from_date_time(struct tm *out, DateTime *in);
internal void lnx_dense_time_from_timespec(DenseTime *out, struct timespec *in);
internal void lnx_file_properties_from_stat(FileProperties *out, struct stat *in);
internal String8 lnx_string_from_signal(int signum);
internal String8 lnx_string_from_errno(int error_number);
internal LNX_Entity* lnx_alloc_entity(LNX_EntityKind kind);
internal void lnx_free_entity(LNX_Entity *entity);
internal void* lnx_thread_base(void *ptr);
internal void lnx_safe_call_sig_handler(int);
#endif //LINUX_H
+173 -243
View File
@@ -1,243 +1,173 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
////////////////////////////////
//~ rjf: Handle Type Functions (Helpers, Implemented Once)
internal OS_Handle
os_handle_zero(void)
{
OS_Handle handle = {0};
return handle;
}
internal B32
os_handle_match(OS_Handle a, OS_Handle b)
{
return a.u64[0] == b.u64[0];
}
internal void
os_handle_list_push(Arena *arena, OS_HandleList *handles, OS_Handle handle)
{
OS_HandleNode *n = push_array(arena, OS_HandleNode, 1);
n->v = handle;
SLLQueuePush(handles->first, handles->last, n);
handles->count += 1;
}
internal OS_HandleArray
os_handle_array_from_list(Arena *arena, OS_HandleList *list)
{
OS_HandleArray result = {0};
result.count = list->count;
result.v = push_array_no_zero(arena, OS_Handle, result.count);
U64 idx = 0;
for(OS_HandleNode *n = list->first; n != 0; n = n->next, idx += 1)
{
result.v[idx] = n->v;
}
return result;
}
////////////////////////////////
//~ rjf: System Path Helper (Helper, Implemented Once)
internal String8
os_string_from_system_path(Arena *arena, OS_SystemPath path)
{
String8List strs = {0};
os_string_list_from_system_path(arena, path, &strs);
String8 result = str8_list_first(&strs);
return result;
}
////////////////////////////////
//~ rjf: Command Line Argc/Argv Helper (Helper, Implemented Once)
internal String8List
os_string_list_from_argcv(Arena *arena, int argc, char **argv)
{
String8List result = {0};
for(int i = 0; i < argc; i += 1)
{
String8 str = str8_cstring(argv[i]);
str8_list_push(arena, &result, str);
}
return result;
}
////////////////////////////////
//~ rjf: Filesystem Helpers (Helpers, Implemented Once)
internal String8
os_data_from_file_path(Arena *arena, String8 path)
{
OS_Handle file = os_file_open(OS_AccessFlag_Read|OS_AccessFlag_ShareRead, path);
FileProperties props = os_properties_from_file(file);
String8 data = os_string_from_file_range(arena, file, r1u64(0, props.size));
os_file_close(file);
return data;
}
internal B32
os_write_data_to_file_path(String8 path, String8 data)
{
B32 good = 0;
OS_Handle file = os_file_open(OS_AccessFlag_Write, path);
if(!os_handle_match(file, os_handle_zero()))
{
good = 1;
os_file_write(file, r1u64(0, data.size), data.str);
os_file_close(file);
}
return good;
}
internal B32
os_write_data_list_to_file_path(String8 path, String8List list)
{
B32 good = 0;
OS_Handle file = os_file_open(OS_AccessFlag_Write, path);
if(!os_handle_match(file, os_handle_zero()))
{
good = 1;
U64 off = 0;
for(String8Node *n = list.first; n != 0; n = n->next)
{
os_file_write(file, r1u64(off, off+n->string.size), n->string.str);
off += n->string.size;
}
os_file_close(file);
}
return good;
}
internal B32
os_append_data_to_file_path(String8 path, String8 data)
{
B32 good = 0;
if(data.size != 0)
{
OS_Handle file = os_file_open(OS_AccessFlag_Write|OS_AccessFlag_Append, path);
if(!os_handle_match(file, os_handle_zero()))
{
good = 1;
U64 pos = os_properties_from_file(file).size;
os_file_write(file, r1u64(pos, pos+data.size), data.str);
os_file_close(file);
}
}
return good;
}
internal OS_FileID
os_id_from_file_path(String8 path)
{
OS_Handle file = os_file_open(OS_AccessFlag_Read|OS_AccessFlag_ShareRead, path);
OS_FileID id = os_id_from_file(file);
os_file_close(file);
return id;
}
internal S64
os_file_id_compare(OS_FileID a, OS_FileID b)
{
S64 cmp = MemoryCompare((void*)&a.v[0], (void*)&b.v[0], sizeof(a.v));
return cmp;
}
internal String8
os_string_from_file_range(Arena *arena, OS_Handle file, Rng1U64 range)
{
U64 pre_pos = arena_pos(arena);
String8 result;
result.size = dim_1u64(range);
result.str = push_array_no_zero(arena, U8, result.size);
U64 actual_read_size = os_file_read(file, range, result.str);
if(actual_read_size < result.size)
{
arena_pop_to(arena, pre_pos + actual_read_size);
result.size = actual_read_size;
}
return result;
}
////////////////////////////////
//~ rjf: Synchronization Primitive Helpers (Helpers, Implemented Once)
internal void
os_mutex_take(OS_Handle mutex){
os_mutex_take_(mutex);
}
internal void
os_mutex_drop(OS_Handle mutex){
os_mutex_drop_(mutex);
}
internal void
os_rw_mutex_take_r(OS_Handle rw_mutex){
os_rw_mutex_take_r_(rw_mutex);
}
internal void
os_rw_mutex_drop_r(OS_Handle rw_mutex){
os_rw_mutex_drop_r_(rw_mutex);
}
internal void
os_rw_mutex_take_w(OS_Handle rw_mutex){
os_rw_mutex_take_w_(rw_mutex);
}
internal void
os_rw_mutex_drop_w(OS_Handle rw_mutex){
os_rw_mutex_drop_w_(rw_mutex);
}
internal B32
os_condition_variable_wait(OS_Handle cv, OS_Handle mutex, U64 endt_us){
B32 result = os_condition_variable_wait_(cv, mutex, endt_us);
return(result);
}
internal B32
os_condition_variable_wait_rw_r(OS_Handle cv, OS_Handle mutex_rw, U64 endt_us){
B32 result = os_condition_variable_wait_rw_r_(cv, mutex_rw, endt_us);
return(result);
}
internal B32
os_condition_variable_wait_rw_w(OS_Handle cv, OS_Handle mutex_rw, U64 endt_us){
B32 result = os_condition_variable_wait_rw_w_(cv, mutex_rw, endt_us);
return(result);
}
internal void
os_condition_variable_signal(OS_Handle cv){
os_condition_variable_signal_(cv);
}
internal void
os_condition_variable_broadcast(OS_Handle cv){
os_condition_variable_broadcast_(cv);
}
internal String8
os_string_from_guid(Arena *arena, OS_Guid guid)
{
String8 result = push_str8f(arena, "%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X",
guid.data1,
guid.data2,
guid.data3,
guid.data4[0],
guid.data4[1],
guid.data4[2],
guid.data4[3],
guid.data4[4],
guid.data4[5],
guid.data4[6],
guid.data4[7]);
return result;
}
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
////////////////////////////////
//~ rjf: Handle Type Functions (Helpers, Implemented Once)
internal OS_Handle
os_handle_zero(void)
{
OS_Handle handle = {0};
return handle;
}
internal B32
os_handle_match(OS_Handle a, OS_Handle b)
{
return a.u64[0] == b.u64[0];
}
internal void
os_handle_list_push(Arena *arena, OS_HandleList *handles, OS_Handle handle)
{
OS_HandleNode *n = push_array(arena, OS_HandleNode, 1);
n->v = handle;
SLLQueuePush(handles->first, handles->last, n);
handles->count += 1;
}
internal OS_HandleArray
os_handle_array_from_list(Arena *arena, OS_HandleList *list)
{
OS_HandleArray result = {0};
result.count = list->count;
result.v = push_array_no_zero(arena, OS_Handle, result.count);
U64 idx = 0;
for(OS_HandleNode *n = list->first; n != 0; n = n->next, idx += 1)
{
result.v[idx] = n->v;
}
return result;
}
////////////////////////////////
//~ rjf: Command Line Argc/Argv Helper (Helper, Implemented Once)
internal String8List
os_string_list_from_argcv(Arena *arena, int argc, char **argv)
{
String8List result = {0};
for(int i = 0; i < argc; i += 1)
{
String8 str = str8_cstring(argv[i]);
str8_list_push(arena, &result, str);
}
return result;
}
////////////////////////////////
//~ rjf: Filesystem Helpers (Helpers, Implemented Once)
internal String8
os_data_from_file_path(Arena *arena, String8 path)
{
OS_Handle file = os_file_open(OS_AccessFlag_Read|OS_AccessFlag_ShareRead, path);
FileProperties props = os_properties_from_file(file);
String8 data = os_string_from_file_range(arena, file, r1u64(0, props.size));
os_file_close(file);
return data;
}
internal B32
os_write_data_to_file_path(String8 path, String8 data)
{
B32 good = 0;
OS_Handle file = os_file_open(OS_AccessFlag_Write, path);
if(!os_handle_match(file, os_handle_zero()))
{
good = 1;
os_file_write(file, r1u64(0, data.size), data.str);
os_file_close(file);
}
return good;
}
internal B32
os_write_data_list_to_file_path(String8 path, String8List list)
{
B32 good = 0;
OS_Handle file = os_file_open(OS_AccessFlag_Write, path);
if(!os_handle_match(file, os_handle_zero()))
{
good = 1;
U64 off = 0;
for(String8Node *n = list.first; n != 0; n = n->next)
{
os_file_write(file, r1u64(off, off+n->string.size), n->string.str);
off += n->string.size;
}
os_file_close(file);
}
return good;
}
internal B32
os_append_data_to_file_path(String8 path, String8 data)
{
B32 good = 0;
if(data.size != 0)
{
OS_Handle file = os_file_open(OS_AccessFlag_Write|OS_AccessFlag_Append, path);
if(!os_handle_match(file, os_handle_zero()))
{
good = 1;
U64 pos = os_properties_from_file(file).size;
os_file_write(file, r1u64(pos, pos+data.size), data.str);
os_file_close(file);
}
}
return good;
}
internal OS_FileID
os_id_from_file_path(String8 path)
{
OS_Handle file = os_file_open(OS_AccessFlag_Read|OS_AccessFlag_ShareRead, path);
OS_FileID id = os_id_from_file(file);
os_file_close(file);
return id;
}
internal S64
os_file_id_compare(OS_FileID a, OS_FileID b)
{
S64 cmp = MemoryCompare((void*)&a.v[0], (void*)&b.v[0], sizeof(a.v));
return cmp;
}
internal String8
os_string_from_file_range(Arena *arena, OS_Handle file, Rng1U64 range)
{
U64 pre_pos = arena_pos(arena);
String8 result;
result.size = dim_1u64(range);
result.str = push_array_no_zero(arena, U8, result.size);
U64 actual_read_size = os_file_read(file, range, result.str);
if(actual_read_size < result.size)
{
arena_pop_to(arena, pre_pos + actual_read_size);
result.size = actual_read_size;
}
return result;
}
////////////////////////////////
//~ rjf: GUID Helpers (Helpers, Implemented Once)
internal String8
os_string_from_guid(Arena *arena, OS_Guid guid)
{
String8 result = push_str8f(arena, "%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X",
guid.data1,
guid.data2,
guid.data3,
guid.data4[0],
guid.data4[1],
guid.data4[2],
guid.data4[3],
guid.data4[4],
guid.data4[5],
guid.data4[6],
guid.data4[7]);
return result;
}
+339 -380
View File
@@ -1,380 +1,339 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#ifndef OS_CORE_H
#define OS_CORE_H
////////////////////////////////
//~ rjf: Access Flags
typedef U32 OS_AccessFlags;
enum
{
OS_AccessFlag_Read = (1<<0),
OS_AccessFlag_Write = (1<<1),
OS_AccessFlag_Execute = (1<<2),
OS_AccessFlag_Append = (1<<3),
OS_AccessFlag_ShareRead = (1<<4),
OS_AccessFlag_ShareWrite = (1<<5),
};
////////////////////////////////
//~ allen: Files
typedef U32 OS_FileIterFlags;
enum
{
OS_FileIterFlag_SkipFolders = (1 << 0),
OS_FileIterFlag_SkipFiles = (1 << 1),
OS_FileIterFlag_SkipHiddenFiles = (1 << 2),
OS_FileIterFlag_Done = (1 << 31),
};
typedef struct OS_FileIter OS_FileIter;
struct OS_FileIter
{
OS_FileIterFlags flags;
U8 memory[800];
};
typedef struct OS_FileInfo OS_FileInfo;
struct OS_FileInfo
{
String8 name;
FileProperties props;
};
// nick: on-disk file identifier
typedef struct OS_FileID OS_FileID;
struct OS_FileID
{
U64 v[3];
};
////////////////////////////////
//~ rjf: System Paths
typedef enum OS_SystemPath
{
OS_SystemPath_Binary,
OS_SystemPath_Initial,
OS_SystemPath_Current,
OS_SystemPath_UserProgramData,
OS_SystemPath_ModuleLoad,
}
OS_SystemPath;
typedef enum OS_PathFromUserKind
{
OS_PathFromUserKind_Save,
OS_PathFromUserKind_Load,
}
OS_PathFromUserKind;
typedef struct OS_PathFromUser OS_PathFromUser;
struct OS_PathFromUser
{
OS_PathFromUserKind kind;
String8 path;
U64 filter_count;
String8 *filter_extensions;
String8 *filter_names;
};
////////////////////////////////
//~ allen: Launch Input
typedef struct OS_LaunchOptions OS_LaunchOptions;
struct OS_LaunchOptions
{
String8List cmd_line;
String8 path;
String8List env;
B32 inherit_env;
B32 consoleless;
};
////////////////////////////////
//~ rjf: Handle Type
typedef struct OS_Handle OS_Handle;
struct OS_Handle
{
U64 u64[1];
};
typedef struct OS_HandleNode OS_HandleNode;
struct OS_HandleNode
{
OS_HandleNode *next;
OS_Handle v;
};
typedef struct OS_HandleList OS_HandleList;
struct OS_HandleList
{
OS_HandleNode *first;
OS_HandleNode *last;
U64 count;
};
typedef struct OS_HandleArray OS_HandleArray;
struct OS_HandleArray
{
OS_Handle *v;
U64 count;
};
////////////////////////////////
// Time
#define OS_UNIX_TIME_MAX max_U32
typedef U32 OS_UnixTime;
////////////////////////////////
// Global Unique ID
typedef struct OS_Guid
{
U32 data1;
U16 data2;
U16 data3;
U8 data4[8];
} OS_Guid;
StaticAssert(sizeof(OS_Guid) == 16, os_guid_check);
////////////////////////////////
//~ rjf: Thread Types
typedef void OS_ThreadFunctionType(void *ptr);
////////////////////////////////
//~ rjf: Handle Type Functions (Helpers, Implemented Once)
internal OS_Handle os_handle_zero(void);
internal B32 os_handle_match(OS_Handle a, OS_Handle b);
internal void os_handle_list_push(Arena *arena, OS_HandleList *handles, OS_Handle handle);
internal OS_HandleArray os_handle_array_from_list(Arena *arena, OS_HandleList *list);
////////////////////////////////
//~ rjf: System Path Helper (Helper, Implemented Once)
internal String8 os_string_from_system_path(Arena *arena, OS_SystemPath path);
////////////////////////////////
//~ rjf: Command Line Argc/Argv Helper (Helper, Implemented Once)
internal String8List os_string_list_from_argcv(Arena *arena, int argc, char **argv);
////////////////////////////////
//~ rjf: Filesystem Helpers (Helpers, Implemented Once)
internal String8 os_data_from_file_path(Arena *arena, String8 path);
internal B32 os_write_data_to_file_path(String8 path, String8 data);
internal B32 os_write_data_list_to_file_path(String8 path, String8List list);
internal B32 os_append_data_to_file_path(String8 path, String8 data);
internal OS_FileID os_id_from_file_path(String8 path);
internal S64 os_file_id_compare(OS_FileID a, OS_FileID b);
internal String8 os_string_from_file_range(Arena *arena, OS_Handle file, Rng1U64 range);
////////////////////////////////
//~ rjf: Synchronization Primitive Helpers (Helpers, Implemented Once)
internal void os_mutex_take(OS_Handle mutex);
internal void os_mutex_drop(OS_Handle mutex);
internal void os_rw_mutex_take_r(OS_Handle rw_mutex);
internal void os_rw_mutex_drop_r(OS_Handle rw_mutex);
internal void os_rw_mutex_take_w(OS_Handle rw_mutex);
internal void os_rw_mutex_drop_w(OS_Handle rw_mutex);
// returns false on timeout, true on signal, (max_wait_ms = max_U64) -> no timeout
internal B32 os_condition_variable_wait(OS_Handle cv, OS_Handle mutex, U64 endt_us);
internal B32 os_condition_variable_wait_rw_r(OS_Handle cv, OS_Handle rw_mutex, U64 endt_us);
internal B32 os_condition_variable_wait_rw_w(OS_Handle cv, OS_Handle rw_mutex, U64 endt_us);
internal void os_condition_variable_signal(OS_Handle cv);
internal void os_condition_variable_broadcast(OS_Handle cv);
#define OS_MutexScope(mutex) DeferLoop(os_mutex_take(mutex), os_mutex_drop(mutex))
#define OS_MutexScopeR(mutex) DeferLoop(os_rw_mutex_take_r(mutex), os_rw_mutex_drop_r(mutex))
#define OS_MutexScopeW(mutex) DeferLoop(os_rw_mutex_take_w(mutex), os_rw_mutex_drop_w(mutex))
#define OS_MutexScopeRWPromote(mutex) DeferLoop((os_rw_mutex_drop_r(mutex), os_rw_mutex_take_w(mutex)), (os_rw_mutex_drop_w(mutex), os_rw_mutex_take_r(mutex)))
////////////////////////////////
//~ rjf: @os_hooks Main Initialization API (Implemented Per-OS)
internal void os_init(void);
////////////////////////////////
//~ rjf: @os_hooks Memory Allocation (Implemented Per-OS)
internal void* os_reserve(U64 size);
internal B32 os_commit(void *ptr, U64 size);
internal void* os_reserve_large(U64 size);
internal B32 os_commit_large(void *ptr, U64 size);
internal void os_decommit(void *ptr, U64 size);
internal void os_release(void *ptr, U64 size);
internal B32 os_set_large_pages(B32 flag);
internal B32 os_large_pages_enabled(void);
internal U64 os_large_page_size(void);
internal void* os_alloc_ring_buffer(U64 size, U64 *actual_size_out);
internal void os_free_ring_buffer(void *ring_buffer, U64 actual_size);
////////////////////////////////
//~ rjf: @os_hooks System Info (Implemented Per-OS)
internal String8 os_machine_name(void);
internal U64 os_page_size(void);
internal U64 os_allocation_granularity(void);
internal U64 os_logical_core_count(void);
////////////////////////////////
//~ rjf: @os_hooks Process & Thread Info (Implemented Per-OS)
internal S32 os_get_pid(void);
internal S32 os_get_tid(void);
internal String8List os_get_environment(void);
internal U64 os_string_list_from_system_path(Arena *arena, OS_SystemPath path, String8List *out);
////////////////////////////////
//~ rjf: @os_hooks Thread Names
internal void os_set_thread_name(String8 string);
////////////////////////////////
//~ rjf: @os_hooks Process Control (Implemented Per-OS)
internal void os_exit_process(S32 exit_code);
////////////////////////////////
//~ rjf: @os_hooks File System (Implemented Per-OS)
//- rjf: files
internal OS_Handle os_file_open(OS_AccessFlags flags, String8 path);
internal void os_file_close(OS_Handle file);
internal U64 os_file_read(OS_Handle file, Rng1U64 rng, void *out_data);
internal void os_file_write(OS_Handle file, Rng1U64 rng, void *data);
internal B32 os_file_set_times(OS_Handle file, DateTime time);
internal FileProperties os_properties_from_file(OS_Handle file);
internal OS_FileID os_id_from_file(OS_Handle file);
internal B32 os_delete_file_at_path(String8 path);
internal B32 os_copy_file_path(String8 dst, String8 src);
internal String8 os_full_path_from_path(Arena *arena, String8 path);
internal B32 os_file_path_exists(String8 path);
internal FileProperties os_properties_from_file_path(String8 path);
//- rjf: file maps
internal OS_Handle os_file_map_open(OS_AccessFlags flags, OS_Handle file);
internal void os_file_map_close(OS_Handle map);
internal void * os_file_map_view_open(OS_Handle map, OS_AccessFlags flags, Rng1U64 range);
internal void os_file_map_view_close(OS_Handle map, void *ptr);
//- rjf: directory iteration
internal OS_FileIter *os_file_iter_begin(Arena *arena, String8 path, OS_FileIterFlags flags);
internal B32 os_file_iter_next(Arena *arena, OS_FileIter *iter, OS_FileInfo *info_out);
internal void os_file_iter_end(OS_FileIter *iter);
//- rjf: directory creation
internal B32 os_make_directory(String8 path);
////////////////////////////////
//~ rjf: @os_hooks Shared Memory (Implemented Per-OS)
internal OS_Handle os_shared_memory_alloc(U64 size, String8 name);
internal OS_Handle os_shared_memory_open(String8 name);
internal void os_shared_memory_close(OS_Handle handle);
internal void * os_shared_memory_view_open(OS_Handle handle, Rng1U64 range);
internal void os_shared_memory_view_close(OS_Handle handle, void *ptr);
////////////////////////////////
//~ rjf: @os_hooks Time (Implemented Per-OS)
internal OS_UnixTime os_now_unix(void);
internal DateTime os_now_universal_time(void);
internal DateTime os_universal_time_from_local_time(DateTime *local_time);
internal DateTime os_local_time_from_universal_time(DateTime *universal_time);
internal U64 os_now_microseconds(void);
internal void os_sleep_milliseconds(U32 msec);
////////////////////////////////
//~ rjf: @os_hooks Child Processes (Implemented Per-OS)
internal B32 os_launch_process(OS_LaunchOptions *options, OS_Handle *handle_out);
internal B32 os_process_wait(OS_Handle handle, U64 endt_us);
internal void os_process_release_handle(OS_Handle handle);
////////////////////////////////
//~ rjf: @os_hooks Threads (Implemented Per-OS)
internal OS_Handle os_launch_thread(OS_ThreadFunctionType *func, void *ptr, void *params);
internal B32 os_thread_wait(OS_Handle handle, U64 endt_us);
internal void os_release_thread_handle(OS_Handle thread);
////////////////////////////////
//~ rjf: @os_hooks Synchronization Primitives (Implemented Per-OS)
// NOTE(allen): Mutexes are recursive - support counted acquire/release nesting
// on a single thread
//- rjf: recursive mutexes
internal OS_Handle os_mutex_alloc(void);
internal void os_mutex_release(OS_Handle mutex);
internal void os_mutex_take_(OS_Handle mutex);
internal void os_mutex_drop_(OS_Handle mutex);
//- rjf: reader/writer mutexes
internal OS_Handle os_rw_mutex_alloc(void);
internal void os_rw_mutex_release(OS_Handle rw_mutex);
internal void os_rw_mutex_take_r_(OS_Handle mutex);
internal void os_rw_mutex_drop_r_(OS_Handle mutex);
internal void os_rw_mutex_take_w_(OS_Handle mutex);
internal void os_rw_mutex_drop_w_(OS_Handle mutex);
//- rjf: condition variables
internal OS_Handle os_condition_variable_alloc(void);
internal void os_condition_variable_release(OS_Handle cv);
// returns false on timeout, true on signal, (max_wait_ms = max_U64) -> no timeout
internal B32 os_condition_variable_wait_(OS_Handle cv, OS_Handle mutex, U64 endt_us);
internal B32 os_condition_variable_wait_rw_r_(OS_Handle cv, OS_Handle mutex_rw, U64 endt_us);
internal B32 os_condition_variable_wait_rw_w_(OS_Handle cv, OS_Handle mutex_rw, U64 endt_us);
internal void os_condition_variable_signal_(OS_Handle cv);
internal void os_condition_variable_broadcast_(OS_Handle cv);
//- rjf: cross-process semaphores
internal OS_Handle os_semaphore_alloc(U32 initial_count, U32 max_count, String8 name);
internal void os_semaphore_release(OS_Handle semaphore);
internal OS_Handle os_semaphore_open(String8 name);
internal void os_semaphore_close(OS_Handle semaphore);
internal B32 os_semaphore_take(OS_Handle semaphore, U64 endt_us);
internal void os_semaphore_drop(OS_Handle semaphore);
////////////////////////////////
//~ rjf: @os_hooks Dynamically-Loaded Libraries (Implemented Per-OS)
internal OS_Handle os_library_open(String8 path);
internal VoidProc *os_library_load_proc(OS_Handle lib, String8 name);
internal void os_library_close(OS_Handle lib);
////////////////////////////////
//~ rjf: @os_hooks Safe Calls (Implemented Per-OS)
internal void os_safe_call(OS_ThreadFunctionType *func, OS_ThreadFunctionType *fail_handler, void *ptr);
////////////////////////////////
//~ rjf: @os_hooks GUIDs (Implemented Per-OS)
internal OS_Guid os_make_guid(void);
internal String8 os_string_from_guid(Arena *arena, OS_Guid guid);
////////////////////////////////
//~ rjf: @os_hooks Entry Points (Implemented Per-OS)
// NOTE(rjf): The implementation of `os_core` will define low-level entry
// points if BUILD_ENTRY_DEFINING_UNIT is defined to 1. These will call
// into the standard codebase program entry points, named "entry_point".
#if BUILD_ENTRY_DEFINING_UNIT
internal void entry_point(CmdLine *cmdline);
#endif
#endif // OS_CORE_H
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#ifndef OS_CORE_H
#define OS_CORE_H
////////////////////////////////
//~ rjf: System Info
typedef struct OS_SystemInfo OS_SystemInfo;
struct OS_SystemInfo
{
U32 logical_processor_count;
U64 page_size;
U64 large_page_size;
U64 allocation_granularity;
U64 microsecond_resolution;
String8 machine_name;
};
////////////////////////////////
//~ rjf: Process Info
typedef struct OS_ProcessInfo OS_ProcessInfo;
struct OS_ProcessInfo
{
U32 pid;
String8 binary_path;
String8 initial_path;
String8 user_program_data_path;
String8List module_load_paths;
String8List environment;
};
////////////////////////////////
//~ rjf: Access Flags
typedef U32 OS_AccessFlags;
enum
{
OS_AccessFlag_Read = (1<<0),
OS_AccessFlag_Write = (1<<1),
OS_AccessFlag_Execute = (1<<2),
OS_AccessFlag_Append = (1<<3),
OS_AccessFlag_ShareRead = (1<<4),
OS_AccessFlag_ShareWrite = (1<<5),
};
////////////////////////////////
//~ rjf: Files
typedef U32 OS_FileIterFlags;
enum
{
OS_FileIterFlag_SkipFolders = (1 << 0),
OS_FileIterFlag_SkipFiles = (1 << 1),
OS_FileIterFlag_SkipHiddenFiles = (1 << 2),
OS_FileIterFlag_Done = (1 << 31),
};
typedef struct OS_FileIter OS_FileIter;
struct OS_FileIter
{
OS_FileIterFlags flags;
U8 memory[800];
};
typedef struct OS_FileInfo OS_FileInfo;
struct OS_FileInfo
{
String8 name;
FileProperties props;
};
// nick: on-disk file identifier
typedef struct OS_FileID OS_FileID;
struct OS_FileID
{
U64 v[3];
};
////////////////////////////////
//~ rjf: Process Launch Parameters
typedef struct OS_ProcessLaunchParams OS_ProcessLaunchParams;
struct OS_ProcessLaunchParams
{
String8List cmd_line;
String8 path;
String8List env;
B32 inherit_env;
B32 consoleless;
};
////////////////////////////////
//~ rjf: Handle Type
typedef struct OS_Handle OS_Handle;
struct OS_Handle
{
U64 u64[1];
};
typedef struct OS_HandleNode OS_HandleNode;
struct OS_HandleNode
{
OS_HandleNode *next;
OS_Handle v;
};
typedef struct OS_HandleList OS_HandleList;
struct OS_HandleList
{
OS_HandleNode *first;
OS_HandleNode *last;
U64 count;
};
typedef struct OS_HandleArray OS_HandleArray;
struct OS_HandleArray
{
OS_Handle *v;
U64 count;
};
////////////////////////////////
//~ rjf: Globally Unique IDs
typedef struct OS_Guid OS_Guid;
struct OS_Guid
{
U32 data1;
U16 data2;
U16 data3;
U8 data4[8];
};
StaticAssert(sizeof(OS_Guid) == 16, os_guid_check);
////////////////////////////////
//~ rjf: Thread Types
typedef void OS_ThreadFunctionType(void *ptr);
////////////////////////////////
//~ rjf: Handle Type Functions (Helpers, Implemented Once)
internal OS_Handle os_handle_zero(void);
internal B32 os_handle_match(OS_Handle a, OS_Handle b);
internal void os_handle_list_push(Arena *arena, OS_HandleList *handles, OS_Handle handle);
internal OS_HandleArray os_handle_array_from_list(Arena *arena, OS_HandleList *list);
////////////////////////////////
//~ rjf: Command Line Argc/Argv Helper (Helper, Implemented Once)
internal String8List os_string_list_from_argcv(Arena *arena, int argc, char **argv);
////////////////////////////////
//~ rjf: Filesystem Helpers (Helpers, Implemented Once)
internal String8 os_data_from_file_path(Arena *arena, String8 path);
internal B32 os_write_data_to_file_path(String8 path, String8 data);
internal B32 os_write_data_list_to_file_path(String8 path, String8List list);
internal B32 os_append_data_to_file_path(String8 path, String8 data);
internal OS_FileID os_id_from_file_path(String8 path);
internal S64 os_file_id_compare(OS_FileID a, OS_FileID b);
internal String8 os_string_from_file_range(Arena *arena, OS_Handle file, Rng1U64 range);
////////////////////////////////
//~ rjf: GUID Helpers (Helpers, Implemented Once)
internal String8 os_string_from_guid(Arena *arena, OS_Guid guid);
////////////////////////////////
//~ rjf: @os_hooks System/Process Info (Implemented Per-OS)
internal OS_SystemInfo *os_get_system_info(void);
internal OS_ProcessInfo *os_get_process_info(void);
internal String8 os_get_current_path(Arena *arena);
////////////////////////////////
//~ rjf: @os_hooks Memory Allocation (Implemented Per-OS)
//- rjf: basic
internal void *os_reserve(U64 size);
internal B32 os_commit(void *ptr, U64 size);
internal void os_decommit(void *ptr, U64 size);
internal void os_release(void *ptr, U64 size);
//- rjf: large pages
internal B32 os_set_large_pages_enabled(B32 flag);
internal B32 os_large_pages_enabled(void);
internal void *os_reserve_large(U64 size);
internal B32 os_commit_large(void *ptr, U64 size);
////////////////////////////////
//~ rjf: @os_hooks Thread Info (Implemented Per-OS)
internal U32 os_tid(void);
internal void os_set_thread_name(String8 string);
////////////////////////////////
//~ rjf: @os_hooks Aborting (Implemented Per-OS)
internal void os_abort(S32 exit_code);
////////////////////////////////
//~ rjf: @os_hooks File System (Implemented Per-OS)
//- rjf: files
internal OS_Handle os_file_open(OS_AccessFlags flags, String8 path);
internal void os_file_close(OS_Handle file);
internal U64 os_file_read(OS_Handle file, Rng1U64 rng, void *out_data);
internal void os_file_write(OS_Handle file, Rng1U64 rng, void *data);
internal B32 os_file_set_times(OS_Handle file, DateTime time);
internal FileProperties os_properties_from_file(OS_Handle file);
internal OS_FileID os_id_from_file(OS_Handle file);
internal B32 os_delete_file_at_path(String8 path);
internal B32 os_copy_file_path(String8 dst, String8 src);
internal String8 os_full_path_from_path(Arena *arena, String8 path);
internal B32 os_file_path_exists(String8 path);
internal FileProperties os_properties_from_file_path(String8 path);
//- rjf: file maps
internal OS_Handle os_file_map_open(OS_AccessFlags flags, OS_Handle file);
internal void os_file_map_close(OS_Handle map);
internal void * os_file_map_view_open(OS_Handle map, OS_AccessFlags flags, Rng1U64 range);
internal void os_file_map_view_close(OS_Handle map, void *ptr);
//- rjf: directory iteration
internal OS_FileIter *os_file_iter_begin(Arena *arena, String8 path, OS_FileIterFlags flags);
internal B32 os_file_iter_next(Arena *arena, OS_FileIter *iter, OS_FileInfo *info_out);
internal void os_file_iter_end(OS_FileIter *iter);
//- rjf: directory creation
internal B32 os_make_directory(String8 path);
////////////////////////////////
//~ rjf: @os_hooks Shared Memory (Implemented Per-OS)
internal OS_Handle os_shared_memory_alloc(U64 size, String8 name);
internal OS_Handle os_shared_memory_open(String8 name);
internal void os_shared_memory_close(OS_Handle handle);
internal void * os_shared_memory_view_open(OS_Handle handle, Rng1U64 range);
internal void os_shared_memory_view_close(OS_Handle handle, void *ptr);
////////////////////////////////
//~ rjf: @os_hooks Time (Implemented Per-OS)
internal U64 os_now_microseconds(void);
internal U32 os_now_unix(void);
internal DateTime os_now_universal_time(void);
internal DateTime os_universal_time_from_local(DateTime *local_time);
internal DateTime os_local_time_from_universal(DateTime *universal_time);
internal void os_sleep_milliseconds(U32 msec);
////////////////////////////////
//~ rjf: @os_hooks Child Processes (Implemented Per-OS)
internal OS_Handle os_process_launch(OS_ProcessLaunchParams *params);
internal B32 os_process_join(OS_Handle handle, U64 endt_us);
internal void os_process_detach(OS_Handle handle);
////////////////////////////////
//~ rjf: @os_hooks Threads (Implemented Per-OS)
internal OS_Handle os_thread_launch(OS_ThreadFunctionType *func, void *ptr, void *params);
internal B32 os_thread_join(OS_Handle handle, U64 endt_us);
internal void os_thread_detach(OS_Handle handle);
////////////////////////////////
//~ rjf: @os_hooks Synchronization Primitives (Implemented Per-OS)
//- rjf: recursive mutexes
internal OS_Handle os_mutex_alloc(void);
internal void os_mutex_release(OS_Handle mutex);
internal void os_mutex_take(OS_Handle mutex);
internal void os_mutex_drop(OS_Handle mutex);
//- rjf: reader/writer mutexes
internal OS_Handle os_rw_mutex_alloc(void);
internal void os_rw_mutex_release(OS_Handle rw_mutex);
internal void os_rw_mutex_take_r(OS_Handle mutex);
internal void os_rw_mutex_drop_r(OS_Handle mutex);
internal void os_rw_mutex_take_w(OS_Handle mutex);
internal void os_rw_mutex_drop_w(OS_Handle mutex);
//- rjf: condition variables
internal OS_Handle os_condition_variable_alloc(void);
internal void os_condition_variable_release(OS_Handle cv);
// returns false on timeout, true on signal, (max_wait_ms = max_U64) -> no timeout
internal B32 os_condition_variable_wait(OS_Handle cv, OS_Handle mutex, U64 endt_us);
internal B32 os_condition_variable_wait_rw_r(OS_Handle cv, OS_Handle mutex_rw, U64 endt_us);
internal B32 os_condition_variable_wait_rw_w(OS_Handle cv, OS_Handle mutex_rw, U64 endt_us);
internal void os_condition_variable_signal(OS_Handle cv);
internal void os_condition_variable_broadcast(OS_Handle cv);
//- rjf: cross-process semaphores
internal OS_Handle os_semaphore_alloc(U32 initial_count, U32 max_count, String8 name);
internal void os_semaphore_release(OS_Handle semaphore);
internal OS_Handle os_semaphore_open(String8 name);
internal void os_semaphore_close(OS_Handle semaphore);
internal B32 os_semaphore_take(OS_Handle semaphore, U64 endt_us);
internal void os_semaphore_drop(OS_Handle semaphore);
//- rjf: scope macros
#define OS_MutexScope(mutex) DeferLoop(os_mutex_take(mutex), os_mutex_drop(mutex))
#define OS_MutexScopeR(mutex) DeferLoop(os_rw_mutex_take_r(mutex), os_rw_mutex_drop_r(mutex))
#define OS_MutexScopeW(mutex) DeferLoop(os_rw_mutex_take_w(mutex), os_rw_mutex_drop_w(mutex))
#define OS_MutexScopeRWPromote(mutex) DeferLoop((os_rw_mutex_drop_r(mutex), os_rw_mutex_take_w(mutex)), (os_rw_mutex_drop_w(mutex), os_rw_mutex_take_r(mutex)))
////////////////////////////////
//~ rjf: @os_hooks Dynamically-Loaded Libraries (Implemented Per-OS)
internal OS_Handle os_library_open(String8 path);
internal void os_library_close(OS_Handle lib);
internal VoidProc *os_library_load_proc(OS_Handle lib, String8 name);
////////////////////////////////
//~ rjf: @os_hooks Safe Calls (Implemented Per-OS)
internal void os_safe_call(OS_ThreadFunctionType *func, OS_ThreadFunctionType *fail_handler, void *ptr);
////////////////////////////////
//~ rjf: @os_hooks GUIDs (Implemented Per-OS)
internal OS_Guid os_make_guid(void);
////////////////////////////////
//~ rjf: @os_hooks Entry Points (Implemented Per-OS)
// NOTE(rjf): The implementation of `os_core` will define low-level entry
// points if BUILD_ENTRY_DEFINING_UNIT is defined to 1. These will call
// into the standard codebase program entry points, named "entry_point".
#if BUILD_ENTRY_DEFINING_UNIT
internal void entry_point(CmdLine *cmdline);
#endif
#endif // OS_CORE_H
File diff suppressed because it is too large Load Diff
+124 -84
View File
@@ -1,84 +1,124 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#ifndef WIN32_H
#define WIN32_H
////////////////////////////////
//~ NOTE(allen): Negotiate the windows header include order
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <windowsx.h>
#include <timeapi.h>
#include <tlhelp32.h>
#include <Shlobj.h>
#include <processthreadsapi.h>
////////////////////////////////
//~ NOTE(allen): File Iterator
typedef struct W32_FileIter W32_FileIter;
struct W32_FileIter
{
HANDLE handle;
WIN32_FIND_DATAW find_data;
B32 is_volume_iter;
String8Array drive_strings;
U64 drive_strings_iter_idx;
};
StaticAssert(sizeof(Member(OS_FileIter, memory)) >= sizeof(W32_FileIter), file_iter_memory_size);
////////////////////////////////
//~ NOTE(allen): Threading Entities
typedef enum W32_EntityKind
{
W32_EntityKind_Null,
W32_EntityKind_Thread,
W32_EntityKind_Mutex,
W32_EntityKind_RWMutex,
W32_EntityKind_ConditionVariable,
}
W32_EntityKind;
typedef struct W32_Entity W32_Entity;
struct W32_Entity
{
W32_Entity *next;
W32_EntityKind kind;
volatile U32 reference_mask;
union{
struct{
OS_ThreadFunctionType *func;
void *ptr;
HANDLE handle;
DWORD tid;
} thread;
CRITICAL_SECTION mutex;
SRWLOCK rw_mutex;
CONDITION_VARIABLE cv;
};
};
////////////////////////////////
//~ rjf: Helpers
//- rjf: files
internal FilePropertyFlags w32_file_property_flags_from_dwFileAttributes(DWORD dwFileAttributes);
internal void w32_file_properties_from_attributes(FileProperties *properties, WIN32_FILE_ATTRIBUTE_DATA *attributes);
//- rjf: time
internal void w32_date_time_from_system_time(DateTime *out, SYSTEMTIME *in);
internal void w32_system_time_from_date_time(SYSTEMTIME *out, DateTime *in);
internal void w32_dense_time_from_file_time(DenseTime *out, FILETIME *in);
internal U32 w32_sleep_ms_from_endt_us(U64 endt_us);
//- rjf: entities
internal W32_Entity* w32_alloc_entity(W32_EntityKind kind);
internal void w32_free_entity(W32_Entity *entity);
//- rjf: threads
internal DWORD w32_thread_base(void *ptr);
#endif //WIN32_H
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#ifndef OS_CORE_WIN32_H
#define OS_CORE_WIN32_H
////////////////////////////////
//~ rjf: Includes / Libraries
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <windowsx.h>
#include <timeapi.h>
#include <tlhelp32.h>
#include <Shlobj.h>
#include <processthreadsapi.h>
#pragma comment(lib, "user32")
#pragma comment(lib, "winmm")
#pragma comment(lib, "shell32")
#pragma comment(lib, "advapi32")
#pragma comment(lib, "rpcrt4")
#pragma comment(lib, "shlwapi")
#pragma comment(lib, "comctl32")
#pragma comment(linker,"\"/manifestdependency:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"") // this is required for loading correct comctl32 dll file
////////////////////////////////
//~ rjf: File Iterator Types
typedef struct OS_W32_FileIter OS_W32_FileIter;
struct OS_W32_FileIter
{
HANDLE handle;
WIN32_FIND_DATAW find_data;
B32 is_volume_iter;
String8Array drive_strings;
U64 drive_strings_iter_idx;
};
StaticAssert(sizeof(Member(OS_FileIter, memory)) >= sizeof(OS_W32_FileIter), file_iter_memory_size);
////////////////////////////////
//~ rjf: Entity Types
typedef enum OS_W32_EntityKind
{
OS_W32_EntityKind_Null,
OS_W32_EntityKind_Thread,
OS_W32_EntityKind_Mutex,
OS_W32_EntityKind_RWMutex,
OS_W32_EntityKind_ConditionVariable,
}
OS_W32_EntityKind;
typedef struct OS_W32_Entity OS_W32_Entity;
struct OS_W32_Entity
{
OS_W32_Entity *next;
OS_W32_EntityKind kind;
union
{
struct
{
OS_ThreadFunctionType *func;
void *ptr;
HANDLE handle;
DWORD tid;
} thread;
CRITICAL_SECTION mutex;
SRWLOCK rw_mutex;
CONDITION_VARIABLE cv;
};
};
////////////////////////////////
//~ rjf: State
typedef struct OS_W32_State OS_W32_State;
struct OS_W32_State
{
Arena *arena;
// rjf: info
OS_SystemInfo system_info;
OS_ProcessInfo process_info;
// rjf: large pages
B32 large_pages_enabled;
// rjf: entity storage
CRITICAL_SECTION entity_mutex;
Arena *entity_arena;
OS_W32_Entity *entity_free;
};
////////////////////////////////
//~ rjf: Globals
global OS_W32_State os_w32_state = {0};
////////////////////////////////
//~ rjf: File Info Conversion Helpers
internal FilePropertyFlags os_w32_file_property_flags_from_dwFileAttributes(DWORD dwFileAttributes);
internal void os_w32_file_properties_from_attribute_data(FileProperties *properties, WIN32_FILE_ATTRIBUTE_DATA *attributes);
////////////////////////////////
//~ rjf: Time Conversion Helpers
internal void os_w32_date_time_from_system_time(DateTime *out, SYSTEMTIME *in);
internal void os_w32_system_time_from_date_time(SYSTEMTIME *out, DateTime *in);
internal void os_w32_dense_time_from_file_time(DenseTime *out, FILETIME *in);
internal U32 os_w32_sleep_ms_from_endt_us(U64 endt_us);
////////////////////////////////
//~ rjf: Entity Functions
internal OS_W32_Entity *os_w32_entity_alloc(OS_W32_EntityKind kind);
internal void os_w32_entity_release(OS_W32_Entity *entity);
////////////////////////////////
//~ rjf: Thread Entry Point
internal DWORD os_w32_thread_entry_point(void *ptr);
#endif // OS_CORE_WIN32_H
+191 -182
View File
@@ -1,182 +1,191 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#ifndef OS_GRAPHICAL_H
#define OS_GRAPHICAL_H
////////////////////////////////
//~ rjf: Window Types
typedef U32 OS_WindowFlags;
enum
{
OS_WindowFlag_CustomBorder = (1<<0),
};
typedef void OS_WindowRepaintFunctionType(OS_Handle window, void *user_data);
////////////////////////////////
//~ rjf: Cursor Types
typedef enum OS_Cursor
{
OS_Cursor_Pointer,
OS_Cursor_IBar,
OS_Cursor_LeftRight,
OS_Cursor_UpDown,
OS_Cursor_DownRight,
OS_Cursor_UpRight,
OS_Cursor_UpDownLeftRight,
OS_Cursor_HandPoint,
OS_Cursor_Disabled,
OS_Cursor_COUNT,
}
OS_Cursor;
////////////////////////////////
//~ rjf: Generated Code
#include "os/gfx/generated/os_gfx.meta.h"
////////////////////////////////
//~ rjf: Event Types
typedef enum OS_EventKind
{
OS_EventKind_Null,
OS_EventKind_Press,
OS_EventKind_Release,
OS_EventKind_MouseMove,
OS_EventKind_Text,
OS_EventKind_Scroll,
OS_EventKind_WindowLoseFocus,
OS_EventKind_WindowClose,
OS_EventKind_FileDrop,
OS_EventKind_Wakeup,
OS_EventKind_COUNT
}
OS_EventKind;
typedef U32 OS_EventFlags;
enum
{
OS_EventFlag_Ctrl = (1<<0),
OS_EventFlag_Shift = (1<<1),
OS_EventFlag_Alt = (1<<2),
};
typedef struct OS_Event OS_Event;
struct OS_Event
{
OS_Event *next;
OS_Event *prev;
U64 timestamp_us;
OS_Handle window;
OS_EventKind kind;
OS_EventFlags flags;
OS_Key key;
B32 is_repeat;
B32 right_sided;
U32 character;
U32 repeat_count;
Vec2F32 pos;
Vec2F32 delta;
String8List strings;
};
typedef struct OS_EventList OS_EventList;
struct OS_EventList
{
U64 count;
OS_Event *first;
OS_Event *last;
};
////////////////////////////////
//~ rjf: Event Functions (Helpers, Implemented Once)
internal String8List os_string_list_from_event_flags(Arena *arena, OS_EventFlags flags);
internal U32 os_codepoint_from_event_flags_and_key(OS_EventFlags flags, OS_Key key);
internal void os_eat_event(OS_EventList *events, OS_Event *event);
internal B32 os_key_press(OS_EventList *events, OS_Handle window, OS_EventFlags flags, OS_Key key);
internal B32 os_key_release(OS_EventList *events, OS_Handle window, OS_EventFlags flags, OS_Key key);
internal B32 os_text(OS_EventList *events, OS_Handle window, U32 character);
internal OS_EventList os_event_list_copy(Arena *arena, OS_EventList *src);
internal void os_event_list_concat_in_place(OS_EventList *dst, OS_EventList *to_push);
////////////////////////////////
//~ rjf: @os_hooks Main Initialization API (Implemented Per-OS)
internal void os_graphical_init(void);
////////////////////////////////
//~ rjf: @os_hooks Clipboards (Implemented Per-OS)
internal void os_set_clipboard_text(String8 string);
internal String8 os_get_clipboard_text(Arena *arena);
////////////////////////////////
//~ rjf: @os_hooks Windows (Implemented Per-OS)
internal OS_Handle os_window_open(Vec2F32 resolution, OS_WindowFlags flags, String8 title);
internal void os_window_close(OS_Handle window);
internal void os_window_first_paint(OS_Handle window);
internal void os_window_equip_repaint(OS_Handle window, OS_WindowRepaintFunctionType *repaint, void *user_data);
internal void os_window_focus(OS_Handle window);
internal B32 os_window_is_focused(OS_Handle window);
internal B32 os_window_is_fullscreen(OS_Handle window);
internal void os_window_set_fullscreen(OS_Handle window, B32 fullscreen);
internal B32 os_window_is_maximized(OS_Handle window);
internal void os_window_set_maximized(OS_Handle window, B32 maximized);
internal void os_window_minimize(OS_Handle window);
internal void os_window_bring_to_front(OS_Handle window);
internal void os_window_set_monitor(OS_Handle window, OS_Handle monitor);
internal void os_window_clear_custom_border_data(OS_Handle handle);
internal void os_window_push_custom_title_bar(OS_Handle handle, F32 thickness);
internal void os_window_push_custom_edges(OS_Handle handle, F32 thickness);
internal void os_window_push_custom_title_bar_client_area(OS_Handle handle, Rng2F32 rect);
internal Rng2F32 os_rect_from_window(OS_Handle window);
internal Rng2F32 os_client_rect_from_window(OS_Handle window);
internal F32 os_dpi_from_window(OS_Handle window);
////////////////////////////////
//~ rjf: @os_hooks Monitors (Implemented Per-OS)
internal OS_HandleArray os_push_monitors_array(Arena *arena);
internal OS_Handle os_primary_monitor(void);
internal OS_Handle os_monitor_from_window(OS_Handle window);
internal String8 os_name_from_monitor(Arena *arena, OS_Handle monitor);
internal Vec2F32 os_dim_from_monitor(OS_Handle monitor);
////////////////////////////////
//~ rjf: @os_hooks Events (Implemented Per-OS)
internal void os_send_wakeup_event(void);
internal OS_EventList os_get_events(Arena *arena, B32 wait);
internal OS_EventFlags os_get_event_flags(void);
internal B32 os_key_is_down(OS_Key key);
internal Vec2F32 os_mouse_from_window(OS_Handle window);
////////////////////////////////
//~ rjf: @os_hooks Cursors (Implemented Per-OS)
internal void os_set_cursor(OS_Cursor cursor);
////////////////////////////////
//~ rjf: @os_hooks System Properties (Implemented Per-OS)
internal F32 os_double_click_time(void);
internal F32 os_caret_blink_time(void);
internal F32 os_default_refresh_rate(void);
////////////////////////////////
//~ rjf: @os_hooks Native User-Facing Graphical Messages (Implemented Per-OS)
internal void os_graphical_message(B32 error, String8 title, String8 message);
////////////////////////////////
//~ rjf: @os_hooks Shell Operations
internal void os_show_in_filesystem_ui(String8 path);
#endif // OS_GRAPHICAL_H
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#ifndef OS_GRAPHICAL_H
#define OS_GRAPHICAL_H
////////////////////////////////
//~ rjf: Graphics System Info
typedef struct OS_GfxInfo OS_GfxInfo;
struct OS_GfxInfo
{
F32 double_click_time;
F32 caret_blink_time;
F32 default_refresh_rate;
};
////////////////////////////////
//~ rjf: Window Types
typedef U32 OS_WindowFlags;
enum
{
OS_WindowFlag_CustomBorder = (1<<0),
};
typedef void OS_WindowRepaintFunctionType(OS_Handle window, void *user_data);
////////////////////////////////
//~ rjf: Cursor Types
typedef enum OS_Cursor
{
OS_Cursor_Pointer,
OS_Cursor_IBar,
OS_Cursor_LeftRight,
OS_Cursor_UpDown,
OS_Cursor_DownRight,
OS_Cursor_UpRight,
OS_Cursor_UpDownLeftRight,
OS_Cursor_HandPoint,
OS_Cursor_Disabled,
OS_Cursor_COUNT,
}
OS_Cursor;
////////////////////////////////
//~ rjf: Generated Code
#include "os/gfx/generated/os_gfx.meta.h"
////////////////////////////////
//~ rjf: Event Types
typedef enum OS_EventKind
{
OS_EventKind_Null,
OS_EventKind_Press,
OS_EventKind_Release,
OS_EventKind_MouseMove,
OS_EventKind_Text,
OS_EventKind_Scroll,
OS_EventKind_WindowLoseFocus,
OS_EventKind_WindowClose,
OS_EventKind_FileDrop,
OS_EventKind_Wakeup,
OS_EventKind_COUNT
}
OS_EventKind;
typedef U32 OS_EventFlags;
enum
{
OS_EventFlag_Ctrl = (1<<0),
OS_EventFlag_Shift = (1<<1),
OS_EventFlag_Alt = (1<<2),
};
typedef struct OS_Event OS_Event;
struct OS_Event
{
OS_Event *next;
OS_Event *prev;
U64 timestamp_us;
OS_Handle window;
OS_EventKind kind;
OS_EventFlags flags;
OS_Key key;
B32 is_repeat;
B32 right_sided;
U32 character;
U32 repeat_count;
Vec2F32 pos;
Vec2F32 delta;
String8List strings;
};
typedef struct OS_EventList OS_EventList;
struct OS_EventList
{
U64 count;
OS_Event *first;
OS_Event *last;
};
////////////////////////////////
//~ rjf: Event Functions (Helpers, Implemented Once)
internal String8List os_string_list_from_event_flags(Arena *arena, OS_EventFlags flags);
internal U32 os_codepoint_from_event_flags_and_key(OS_EventFlags flags, OS_Key key);
internal void os_eat_event(OS_EventList *events, OS_Event *event);
internal B32 os_key_press(OS_EventList *events, OS_Handle window, OS_EventFlags flags, OS_Key key);
internal B32 os_key_release(OS_EventList *events, OS_Handle window, OS_EventFlags flags, OS_Key key);
internal B32 os_text(OS_EventList *events, OS_Handle window, U32 character);
internal OS_EventList os_event_list_copy(Arena *arena, OS_EventList *src);
internal void os_event_list_concat_in_place(OS_EventList *dst, OS_EventList *to_push);
////////////////////////////////
//~ rjf: @os_hooks Main Initialization API (Implemented Per-OS)
internal void os_gfx_init(void);
////////////////////////////////
//~ rjf: @os_hooks Graphics System Info (Implemented Per-OS)
internal OS_GfxInfo *os_get_gfx_info(void);
////////////////////////////////
//~ rjf: @os_hooks Clipboards (Implemented Per-OS)
internal void os_set_clipboard_text(String8 string);
internal String8 os_get_clipboard_text(Arena *arena);
////////////////////////////////
//~ rjf: @os_hooks Windows (Implemented Per-OS)
internal OS_Handle os_window_open(Vec2F32 resolution, OS_WindowFlags flags, String8 title);
internal void os_window_close(OS_Handle window);
internal void os_window_first_paint(OS_Handle window);
internal void os_window_equip_repaint(OS_Handle window, OS_WindowRepaintFunctionType *repaint, void *user_data);
internal void os_window_focus(OS_Handle window);
internal B32 os_window_is_focused(OS_Handle window);
internal B32 os_window_is_fullscreen(OS_Handle window);
internal void os_window_set_fullscreen(OS_Handle window, B32 fullscreen);
internal B32 os_window_is_maximized(OS_Handle window);
internal void os_window_set_maximized(OS_Handle window, B32 maximized);
internal void os_window_minimize(OS_Handle window);
internal void os_window_bring_to_front(OS_Handle window);
internal void os_window_set_monitor(OS_Handle window, OS_Handle monitor);
internal void os_window_clear_custom_border_data(OS_Handle handle);
internal void os_window_push_custom_title_bar(OS_Handle handle, F32 thickness);
internal void os_window_push_custom_edges(OS_Handle handle, F32 thickness);
internal void os_window_push_custom_title_bar_client_area(OS_Handle handle, Rng2F32 rect);
internal Rng2F32 os_rect_from_window(OS_Handle window);
internal Rng2F32 os_client_rect_from_window(OS_Handle window);
internal F32 os_dpi_from_window(OS_Handle window);
////////////////////////////////
//~ rjf: @os_hooks Monitors (Implemented Per-OS)
internal OS_HandleArray os_push_monitors_array(Arena *arena);
internal OS_Handle os_primary_monitor(void);
internal OS_Handle os_monitor_from_window(OS_Handle window);
internal String8 os_name_from_monitor(Arena *arena, OS_Handle monitor);
internal Vec2F32 os_dim_from_monitor(OS_Handle monitor);
////////////////////////////////
//~ rjf: @os_hooks Events (Implemented Per-OS)
internal void os_send_wakeup_event(void);
internal OS_EventList os_get_events(Arena *arena, B32 wait);
internal OS_EventFlags os_get_event_flags(void);
internal B32 os_key_is_down(OS_Key key);
internal Vec2F32 os_mouse_from_window(OS_Handle window);
////////////////////////////////
//~ rjf: @os_hooks Cursors (Implemented Per-OS)
internal void os_set_cursor(OS_Cursor cursor);
////////////////////////////////
//~ rjf: @os_hooks Native User-Facing Graphical Messages (Implemented Per-OS)
internal void os_graphical_message(B32 error, String8 title, String8 message);
////////////////////////////////
//~ rjf: @os_hooks Shell Operations
internal void os_show_in_filesystem_ui(String8 path);
#endif // OS_GRAPHICAL_H
+229 -256
View File
@@ -1,256 +1,229 @@
////////////////////////////////
//~ rjf: @os_hooks Main Initialization API (Implemented Per-OS)
internal void
os_graphical_init(void)
{
}
////////////////////////////////
//~ rjf: @os_hooks Clipboards (Implemented Per-OS)
internal void
os_set_clipboard_text(String8 string)
{
}
internal String8
os_get_clipboard_text(Arena *arena)
{
return str8_zero();
}
////////////////////////////////
//~ rjf: @os_hooks Windows (Implemented Per-OS)
internal OS_Handle
os_window_open(Vec2F32 resolution, OS_WindowFlags flags, String8 title)
{
OS_Handle handle = {1};
return handle;
}
internal void
os_window_close(OS_Handle window)
{
}
internal void
os_window_first_paint(OS_Handle window)
{
}
internal void
os_window_equip_repaint(OS_Handle window, OS_WindowRepaintFunctionType *repaint, void *user_data)
{
}
internal void
os_window_focus(OS_Handle window)
{
}
internal B32
os_window_is_focused(OS_Handle window)
{
return 0;
}
internal B32
os_window_is_fullscreen(OS_Handle window)
{
return 0;
}
internal void
os_window_set_fullscreen(OS_Handle window, B32 fullscreen)
{
}
internal B32
os_window_is_maximized(OS_Handle window)
{
return 0;
}
internal void
os_window_set_maximized(OS_Handle window, B32 maximized)
{
}
internal void
os_window_minimize(OS_Handle window)
{
}
internal void
os_window_bring_to_front(OS_Handle window)
{
}
internal void
os_window_set_monitor(OS_Handle window, OS_Handle monitor)
{
}
internal void
os_window_clear_custom_border_data(OS_Handle handle)
{
}
internal void
os_window_push_custom_title_bar(OS_Handle handle, F32 thickness)
{
}
internal void
os_window_push_custom_edges(OS_Handle handle, F32 thickness)
{
}
internal void
os_window_push_custom_title_bar_client_area(OS_Handle handle, Rng2F32 rect)
{
}
internal Rng2F32
os_rect_from_window(OS_Handle window)
{
Rng2F32 rect = r2f32(v2f32(0, 0), v2f32(500, 500));
return rect;
}
internal Rng2F32
os_client_rect_from_window(OS_Handle window)
{
Rng2F32 rect = r2f32(v2f32(0, 0), v2f32(500, 500));
return rect;
}
internal F32
os_dpi_from_window(OS_Handle window)
{
return 96.f;
}
////////////////////////////////
//~ rjf: @os_hooks Monitors (Implemented Per-OS)
internal OS_HandleArray
os_push_monitors_array(Arena *arena)
{
OS_HandleArray arr = {0};
return arr;
}
internal OS_Handle
os_primary_monitor(void)
{
OS_Handle handle = {1};
return handle;
}
internal OS_Handle
os_monitor_from_window(OS_Handle window)
{
OS_Handle handle = {1};
return handle;
}
internal String8
os_name_from_monitor(Arena *arena, OS_Handle monitor)
{
return str8_zero();
}
internal Vec2F32
os_dim_from_monitor(OS_Handle monitor)
{
Vec2F32 v = v2f32(1000, 1000);
return v;
}
////////////////////////////////
//~ rjf: @os_hooks Events (Implemented Per-OS)
internal void
os_send_wakeup_event(void)
{
}
internal OS_EventList
os_get_events(Arena *arena, B32 wait)
{
OS_EventList evts = {0};
return evts;
}
internal OS_EventFlags
os_get_event_flags(void)
{
OS_EventFlags f = 0;
return f;
}
internal B32
os_key_is_down(OS_Key key)
{
return 0;
}
internal Vec2F32
os_mouse_from_window(OS_Handle window)
{
return v2f32(0, 0);
}
////////////////////////////////
//~ rjf: @os_hooks Cursors (Implemented Per-OS)
internal void
os_set_cursor(OS_Cursor cursor)
{
}
////////////////////////////////
//~ rjf: @os_hooks System Properties (Implemented Per-OS)
internal F32
os_double_click_time(void)
{
return 1.f;
}
internal F32
os_caret_blink_time(void)
{
return 1.f;
}
internal F32
os_default_refresh_rate(void)
{
return 60.f;
}
internal B32
os_granular_sleep_enabled(void)
{
return 1;
}
////////////////////////////////
//~ rjf: @os_hooks Native User-Facing Graphical Messages (Implemented Per-OS)
internal void
os_graphical_message(B32 error, String8 title, String8 message)
{
}
////////////////////////////////
//~ rjf: @os_hooks Shell Operations
internal void
os_show_in_filesystem_ui(String8 path)
{
}
////////////////////////////////
//~ rjf: @os_hooks Main Initialization API (Implemented Per-OS)
internal void
os_gfx_init(void)
{
}
////////////////////////////////
//~ rjf: @os_hooks Clipboards (Implemented Per-OS)
internal void
os_set_clipboard_text(String8 string)
{
}
internal String8
os_get_clipboard_text(Arena *arena)
{
return str8_zero();
}
////////////////////////////////
//~ rjf: @os_hooks Windows (Implemented Per-OS)
internal OS_Handle
os_window_open(Vec2F32 resolution, OS_WindowFlags flags, String8 title)
{
OS_Handle handle = {1};
return handle;
}
internal void
os_window_close(OS_Handle window)
{
}
internal void
os_window_first_paint(OS_Handle window)
{
}
internal void
os_window_equip_repaint(OS_Handle window, OS_WindowRepaintFunctionType *repaint, void *user_data)
{
}
internal void
os_window_focus(OS_Handle window)
{
}
internal B32
os_window_is_focused(OS_Handle window)
{
return 0;
}
internal B32
os_window_is_fullscreen(OS_Handle window)
{
return 0;
}
internal void
os_window_set_fullscreen(OS_Handle window, B32 fullscreen)
{
}
internal B32
os_window_is_maximized(OS_Handle window)
{
return 0;
}
internal void
os_window_set_maximized(OS_Handle window, B32 maximized)
{
}
internal void
os_window_minimize(OS_Handle window)
{
}
internal void
os_window_bring_to_front(OS_Handle window)
{
}
internal void
os_window_set_monitor(OS_Handle window, OS_Handle monitor)
{
}
internal void
os_window_clear_custom_border_data(OS_Handle handle)
{
}
internal void
os_window_push_custom_title_bar(OS_Handle handle, F32 thickness)
{
}
internal void
os_window_push_custom_edges(OS_Handle handle, F32 thickness)
{
}
internal void
os_window_push_custom_title_bar_client_area(OS_Handle handle, Rng2F32 rect)
{
}
internal Rng2F32
os_rect_from_window(OS_Handle window)
{
Rng2F32 rect = r2f32(v2f32(0, 0), v2f32(500, 500));
return rect;
}
internal Rng2F32
os_client_rect_from_window(OS_Handle window)
{
Rng2F32 rect = r2f32(v2f32(0, 0), v2f32(500, 500));
return rect;
}
internal F32
os_dpi_from_window(OS_Handle window)
{
return 96.f;
}
////////////////////////////////
//~ rjf: @os_hooks Monitors (Implemented Per-OS)
internal OS_HandleArray
os_push_monitors_array(Arena *arena)
{
OS_HandleArray arr = {0};
return arr;
}
internal OS_Handle
os_primary_monitor(void)
{
OS_Handle handle = {1};
return handle;
}
internal OS_Handle
os_monitor_from_window(OS_Handle window)
{
OS_Handle handle = {1};
return handle;
}
internal String8
os_name_from_monitor(Arena *arena, OS_Handle monitor)
{
return str8_zero();
}
internal Vec2F32
os_dim_from_monitor(OS_Handle monitor)
{
Vec2F32 v = v2f32(1000, 1000);
return v;
}
////////////////////////////////
//~ rjf: @os_hooks Events (Implemented Per-OS)
internal void
os_send_wakeup_event(void)
{
}
internal OS_EventList
os_get_events(Arena *arena, B32 wait)
{
OS_EventList evts = {0};
return evts;
}
internal OS_EventFlags
os_get_event_flags(void)
{
OS_EventFlags f = 0;
return f;
}
internal B32
os_key_is_down(OS_Key key)
{
return 0;
}
internal Vec2F32
os_mouse_from_window(OS_Handle window)
{
return v2f32(0, 0);
}
////////////////////////////////
//~ rjf: @os_hooks Cursors (Implemented Per-OS)
internal void
os_set_cursor(OS_Cursor cursor)
{
}
////////////////////////////////
//~ rjf: @os_hooks Native User-Facing Graphical Messages (Implemented Per-OS)
internal void
os_graphical_message(B32 error, String8 title, String8 message)
{
}
////////////////////////////////
//~ rjf: @os_hooks Shell Operations
internal void
os_show_in_filesystem_ui(String8 path)
{
}
File diff suppressed because it is too large Load Diff
+115 -82
View File
@@ -1,82 +1,115 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#ifndef WIN32_GRAPHICAL_H
#define WIN32_GRAPHICAL_H
#pragma comment(lib, "user32")
#pragma comment(lib, "gdi32")
#ifndef WM_NCUAHDRAWCAPTION
#define WM_NCUAHDRAWCAPTION (0x00AE)
#endif
#ifndef WM_NCUAHDRAWFRAME
#define WM_NCUAHDRAWFRAME (0x00AF)
#endif
////////////////////////////////
//~ rjf: Windows
typedef struct W32_TitleBarClientArea W32_TitleBarClientArea;
struct W32_TitleBarClientArea
{
W32_TitleBarClientArea *next;
Rng2F32 rect;
};
typedef struct W32_Window W32_Window;
struct W32_Window
{
W32_Window *next;
W32_Window *prev;
HWND hwnd;
WINDOWPLACEMENT last_window_placement;
OS_WindowRepaintFunctionType *repaint;
void *repaint_user_data;
F32 dpi;
B32 first_paint_done;
B32 maximized;
B32 custom_border;
F32 custom_border_title_thickness;
F32 custom_border_edge_thickness;
B32 custom_border_composition_enabled;
Arena *paint_arena;
W32_TitleBarClientArea *first_title_bar_client_area;
W32_TitleBarClientArea *last_title_bar_client_area;
};
////////////////////////////////
//~ rjf: Monitor Gathering Bundle
typedef struct W32_MonitorGatherBundle W32_MonitorGatherBundle;
struct W32_MonitorGatherBundle
{
Arena *arena;
OS_HandleList *list;
};
////////////////////////////////
//~ rjf: Basic Helpers
internal Rng2F32 w32_base_rect_from_win32_rect(RECT rect);
////////////////////////////////
//~ rjf: Windows
internal OS_Handle os_window_from_w32_window(W32_Window *window);
internal W32_Window * w32_window_from_os_window(OS_Handle window);
internal W32_Window * w32_window_from_hwnd(HWND hwnd);
internal HWND w32_hwnd_from_window(W32_Window *window);
internal W32_Window * w32_allocate_window(void);
internal void w32_free_window(W32_Window *window);
internal OS_Event * w32_push_event(OS_EventKind kind, W32_Window *window);
internal OS_Key w32_os_key_from_vkey(WPARAM vkey);
internal WPARAM w32_vkey_from_os_key(OS_Key key);
internal LRESULT w32_wnd_proc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
////////////////////////////////
//~ rjf: Monitors
internal BOOL w32_monitor_gather_enum_proc(HMONITOR monitor, HDC hdc, LPRECT rect, LPARAM bundle_ptr);
#endif // WIN32_GRAPHICAL_H
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#ifndef OS_GFX_WIN32_H
#define OS_GFX_WIN32_H
////////////////////////////////
//~ rjf: Includes / Libraries
#include <uxtheme.h>
#include <dwmapi.h>
#include <shellscalingapi.h>
#pragma comment(lib, "gdi32")
#pragma comment(lib, "dwmapi")
#pragma comment(lib, "UxTheme")
#pragma comment(lib, "ole32")
#pragma comment(lib, "user32")
#ifndef WM_NCUAHDRAWCAPTION
#define WM_NCUAHDRAWCAPTION (0x00AE)
#endif
#ifndef WM_NCUAHDRAWFRAME
#define WM_NCUAHDRAWFRAME (0x00AF)
#endif
////////////////////////////////
//~ rjf: Windows
typedef struct OS_W32_TitleBarClientArea OS_W32_TitleBarClientArea;
struct OS_W32_TitleBarClientArea
{
OS_W32_TitleBarClientArea *next;
Rng2F32 rect;
};
typedef struct OS_W32_Window OS_W32_Window;
struct OS_W32_Window
{
OS_W32_Window *next;
OS_W32_Window *prev;
HWND hwnd;
WINDOWPLACEMENT last_window_placement;
OS_WindowRepaintFunctionType *repaint;
void *repaint_user_data;
F32 dpi;
B32 first_paint_done;
B32 maximized;
B32 custom_border;
F32 custom_border_title_thickness;
F32 custom_border_edge_thickness;
B32 custom_border_composition_enabled;
Arena *paint_arena;
OS_W32_TitleBarClientArea *first_title_bar_client_area;
OS_W32_TitleBarClientArea *last_title_bar_client_area;
};
////////////////////////////////
//~ rjf: Monitor Gathering Bundle
typedef struct OS_W32_MonitorGatherBundle OS_W32_MonitorGatherBundle;
struct OS_W32_MonitorGatherBundle
{
Arena *arena;
OS_HandleList *list;
};
////////////////////////////////
//~ rjf: Global State
typedef struct OS_W32_GfxState OS_W32_GfxState;
struct OS_W32_GfxState
{
Arena *arena;
U32 gfx_thread_tid;
HINSTANCE hInstance;
HCURSOR hCursor;
OS_GfxInfo gfx_info;
OS_W32_Window *first_window;
OS_W32_Window *last_window;
OS_W32_Window *free_window;
OS_Key key_from_vkey_table[256];
};
////////////////////////////////
//~ rjf: Globals
global OS_W32_GfxState *os_w32_gfx_state = 0;
global OS_EventList os_w32_event_list = {0};
global Arena *os_w32_event_arena = 0;
B32 os_w32_resizing = 0;
////////////////////////////////
//~ rjf: Basic Helpers
internal Rng2F32 os_w32_rng2f32_from_rect(RECT rect);
////////////////////////////////
//~ rjf: Windows
internal OS_Handle os_w32_handle_from_window(OS_W32_Window *window);
internal OS_W32_Window * os_w32_window_from_handle(OS_Handle window);
internal OS_W32_Window * os_w32_window_from_hwnd(HWND hwnd);
internal HWND os_w32_hwnd_from_window(OS_W32_Window *window);
internal OS_W32_Window * os_w32_window_alloc(void);
internal void os_w32_window_release(OS_W32_Window *window);
internal OS_Event * os_w32_push_event(OS_EventKind kind, OS_W32_Window *window);
internal OS_Key os_w32_os_key_from_vkey(WPARAM vkey);
internal WPARAM os_w32_vkey_from_os_key(OS_Key key);
internal LRESULT os_w32_wnd_proc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
////////////////////////////////
//~ rjf: Monitors
internal BOOL os_w32_monitor_gather_enum_proc(HMONITOR monitor, HDC hdc, LPRECT rect, LPARAM bundle_ptr);
#endif // OS_GFX_WIN32_H
+27 -33
View File
@@ -1,33 +1,27 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
////////////////////////////////
// NOTE(allen): Include OS features for extra features and target OS
#include "core/os_core.c"
#if OS_FEATURE_SOCKET
#include "socket/os_socket.c"
#endif
#if OS_FEATURE_GRAPHICAL
#include "gfx/os_gfx.c"
#endif
#if OS_WINDOWS
# include "core/win32/os_core_win32.c"
# if OS_FEATURE_SOCKET
# include "socket/win32/os_socket_win32.c"
# endif
# if OS_FEATURE_GRAPHICAL && !OS_GFX_STUB
# include "gfx/win32/os_gfx_win32.c"
# endif
#elif OS_LINUX
# include "core/linux/os_core_linux.c"
#else
# error no OS layer setup
#endif
#if OS_GFX_STUB
#include "gfx/stub/os_gfx_stub.c"
#endif
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#include "os/core/os_core.c"
#if OS_FEATURE_GRAPHICAL
# include "os/gfx/os_gfx.c"
#endif
#if OS_WINDOWS
# include "os/core/win32/os_core_win32.c"
#elif OS_LINUX
# include "os/core/linux/os_core_linux.c"
#else
# error OS core layer not implemented for this operating system.
#endif
#if OS_FEATURE_GRAPHICAL
# if OS_GFX_STUB
# include "os/gfx/stub/os_gfx_stub.c"
# elif OS_WINDOWS
# include "os/gfx/win32/os_gfx_win32.c"
# elif OS_LINUX
# include "os/gfx/linux/os_gfx_linux.c"
# else
# error OS graphical layer not implemented for this operating system.
# endif
#endif
+40 -47
View File
@@ -1,47 +1,40 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#ifndef OS_INC_H
#define OS_INC_H
#if !defined(OS_FEATURE_SOCKET)
# define OS_FEATURE_SOCKET 0
#endif
#if !defined(OS_FEATURE_GRAPHICAL)
# define OS_FEATURE_GRAPHICAL 0
#endif
#if !defined(OS_GFX_STUB)
# define OS_GFX_STUB 0
#endif
#include "core/os_core.h"
#if OS_FEATURE_SOCKET
#include "socket/os_socket.h"
#endif
#if OS_FEATURE_GRAPHICAL
#include "gfx/os_gfx.h"
#endif
#if OS_WINDOWS
# include "core/win32/os_core_win32.h"
# if OS_FEATURE_SOCKET
# include "socket/win32/os_socket_win32.h"
# endif
# if OS_FEATURE_GRAPHICAL && !OS_GFX_STUB
# include "gfx/win32/os_gfx_win32.h"
# endif
#elif OS_LINUX
# include "core/linux/os_core_linux.h"
#else
# error no OS layer setup
#endif
#if OS_GFX_STUB
#include "gfx/stub/os_gfx_stub.h"
#endif
#endif //OS_SWITCH_H
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#ifndef OS_INC_H
#define OS_INC_H
#if !defined(OS_FEATURE_GRAPHICAL)
# define OS_FEATURE_GRAPHICAL 0
#endif
#if !defined(OS_GFX_STUB)
# define OS_GFX_STUB 0
#endif
#include "os/core/os_core.h"
#if OS_FEATURE_GRAPHICAL
# include "os/gfx/os_gfx.h"
#endif
#if OS_WINDOWS
# include "os/core/win32/os_core_win32.h"
#elif OS_LINUX
# include "os/core/linux/os_core_linux.h"
#else
# error OS core layer not implemented for this operating system.
#endif
#if OS_FEATURE_GRAPHICAL
# if OS_GFX_STUB
# include "os/gfx/stub/os_gfx_stub.h"
# elif OS_WINDOWS
# include "os/gfx/win32/os_gfx_win32.h"
# elif OS_LINUX
# include "os/gfx/linux/os_gfx_linux.h"
# else
# error OS graphical layer not implemented for this operating system.
# endif
#endif
#endif // OS_INC_H
-16
View File
@@ -1,16 +0,0 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
////////////////////////////////
// NOTE(allen): Helper
internal B32
os_socket_write(OS_Socket *socket, String8 data){
String8Node node = {0};
String8List list = {0};
str8_list_push(&list, &node, data);
B32 result = os_socket_write(socket, list);
return(result);
}
#endif
-49
View File
@@ -1,49 +0,0 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#ifndef OS_SOCKET_H
#define OS_SOCKET_H
enum OS_SocketStatus{
OS_SocketStatus_Uninitialized,
OS_SocketStatus_Connected,
OS_SocketStatus_GracefullyClosed,
OS_SocketStatus_Error,
};
typedef U16 OS_SocketError;
enum{
OS_SocketError_None,
OS_SocketError_SocketSystemNotInitialized,
OS_SocketError_BadPortArgument,
OS_SocketError_BadIPArgument,
OS_SocketError_WSAError,
};
struct OS_Socket{
U8 memory[32];
};
////////////////////////////////
//~ NOTE(allen): Implemented Per Operating System
internal void os_socket_init(void);
internal void os_socket_listen(OS_Socket *socket, String8 port);
internal void os_socket_connect(OS_Socket *socket, String8 ip, String8 port);
internal void os_socket_close(OS_Socket *socket);
internal String8 os_socket_read(Arena *arena, OS_Socket *socket);
internal B32 os_socket_write(OS_Socket *socket, String8List list);
internal B32 os_socket_status(OS_Socket *socket, OS_SocketStatus status);
internal String8 os_socket_error_string(Arena *arena, OS_Socket *socket);
internal void os_socket_assert_on_error(OS_Socket *socket, B32 assert_on_error);
////////////////////////////////
//~ NOTE(allen): Helpers - Portable Implementation
internal B32 os_socket_write(OS_Socket *socket, String8 data);
#endif //OS_SOCKET_H
-353
View File
@@ -1,353 +0,0 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
////////////////////////////////
//~ rjf: Helpers
internal void
w32_socket_set_error(W32_Socket *socket, OS_SocketError error){
socket->error = error;
// NOTE(allen): This flag was set earlier so that the socket would assert
// when an error occurs. The "bug" or issue is whatever caused this error
// not the fact the flag is set. Unless the flag wasn't supposed to be set!
Assert(!(socket->flags & W32_SocketFlag_AssertOnError));
}
internal void
w32_socket_set_error_wsa(W32_Socket *socket, int wsa_error){
switch (wsa_error){
default:
{
socket->wsa_error = wsa_error;
w32_socket_set_error(socket, OS_SocketError_WSAError);
}break;
case WSANOTINITIALISED:
{
w32_socket_set_error(socket, OS_SocketError_SocketSystemNotInitialized);
}break;
}
}
internal B32
w32_socket_read_looped(W32_Socket *w32_socket, void *buffer, U32 size){
U32 p = 0;
CHAR *ptr = (CHAR*)buffer;
for (;p < size;){
DWORD amt = (DWORD)(size - p);
WSABUF wsabuf = {amt, ptr};
// NOTE(allen): The flags pointer is _NOT_ optional but we can ignore it.
// We have to zero it because it's an in/out pointer.
DWORD ignore = 0;
if (WSARecv(w32_socket->socket, &wsabuf, 1, &amt, &ignore, 0, 0) != 0){
w32_socket_set_error_wsa(w32_socket, WSAGetLastError());
break;
}
if (amt == 0){
w32_socket->flags |= W32_SocketFlag_Closed;
break;
}
p += amt;
ptr += amt;
}
B32 result = (p == size);
return(result);
}
////////////////////////////////
//~ rjf: Per-OS Hook Implementations
internal void
os_socket_init(void){
WSADATA wsaData;
WORD vreq = MAKEWORD(2,2);
WSAStartup(vreq, &wsaData);
}
internal void
os_socket_listen(OS_Socket *s, String8 port){
W32_Socket *w32_socket = (W32_Socket*)s->memory;
// NOTE(allen): check port string
char port_buffer[6];
if (port.size == 0 || port.size >= sizeof(port_buffer)){
w32_socket_set_error(w32_socket, OS_SocketError_BadPortArgument);
return;
}
MemoryCopy(port_buffer, port.str, port.size);
port_buffer[port.size] = 0;
// NOTE(allen): listen socket addrinfo
addrinfo listen_hint = {0};
listen_hint.ai_flags = AI_PASSIVE|AI_NUMERICSERV;
listen_hint.ai_family = AF_UNSPEC;
listen_hint.ai_socktype = SOCK_STREAM;
listen_hint.ai_protocol = AF_UNSPEC;
addrinfo *addr = {0};
INT error = getaddrinfo(0, port_buffer, &listen_hint, &addr);
if (error != 0){
w32_socket_set_error_wsa(w32_socket, WSAGetLastError());
return;
}
// NOTE(allen): init listen socket
SOCKET socket_listener = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
W32_SocketCloser listener_closer(&socket_listener);
// NOTE(allen): reuseraddr
{
union { B32 b; char c[1]; } enable;
enable.b = true;
if (setsockopt(socket_listener, SOL_SOCKET, SO_REUSEADDR, enable.c, sizeof(enable)) < 0){
w32_socket_set_error_wsa(w32_socket, WSAGetLastError());
return;
}
}
// NOTE(allen): bind
if (bind(socket_listener, addr->ai_addr, (int)addr->ai_addrlen) < 0){
w32_socket_set_error_wsa(w32_socket, WSAGetLastError());
return;
}
// NOTE(allen): listen
if (listen(socket_listener, 1) < 0){
w32_socket_set_error_wsa(w32_socket, WSAGetLastError());
return;
}
// NOTE(allen): accept
SOCKET client_socket = accept(socket_listener, 0, 0);
W32_SocketCloser client_closer(&client_socket);
// NOTE(allen): TCP_NODELAY
{
union { B32 b; char c[1]; } enable;
enable.b = true;
if (setsockopt(client_socket, IPPROTO_TCP, TCP_NODELAY, enable.c, sizeof(enable)) < 0){
w32_socket_set_error_wsa(w32_socket, WSAGetLastError());
return;
}
}
// NOTE(allen): success
w32_socket->flags |= W32_SocketFlag_Connected;
w32_socket->socket = client_socket;
w32_socket->error = OS_SocketError_None;
client_closer.do_not_close();
}
internal void
os_socket_connect(OS_Socket *s, String8 ip, String8 port){
W32_Socket *w32_socket = (W32_Socket*)s->memory;
// NOTE(allen): check port string
char port_buffer[6];
if (port.size == 0 || port.size >= sizeof(port_buffer)){
w32_socket_set_error(w32_socket, OS_SocketError_BadPortArgument);
return;
}
MemoryCopy(port_buffer, port.str, port.size);
port_buffer[port.size] = 0;
// NOTE(allen): check ip string
if (ip.size == 0){
ip = str8_lit("localhost");
}
char ip_buffer[KB(1)];
if (ip.size >= sizeof(ip_buffer)){
w32_socket_set_error(w32_socket, OS_SocketError_BadIPArgument);
return;
}
MemoryCopy(ip_buffer, ip.str, ip.size);
ip_buffer[ip.size] = 0;
// NOTE(allen): socket addrinfo
addrinfo hint = {0};
hint.ai_flags = AI_PASSIVE|AI_NUMERICSERV;
hint.ai_family = AF_UNSPEC;
hint.ai_socktype = SOCK_STREAM;
hint.ai_protocol = AF_UNSPEC;
addrinfo *addr = {0};
INT error = getaddrinfo(ip_buffer, port_buffer, &hint, &addr);
if (error != 0){
w32_socket_set_error_wsa(w32_socket, WSAGetLastError());
return;
}
// NOTE(allen): init socket
SOCKET socket_server = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
W32_SocketCloser closer(&socket_server);
// NOTE(allen): TCP_NODELAY
{
union { B32 b; char c[1]; } enable;
enable.b = true;
if (setsockopt(socket_server, IPPROTO_TCP, TCP_NODELAY, enable.c, sizeof(enable)) < 0){
w32_socket_set_error_wsa(w32_socket, WSAGetLastError());
return;
}
}
// NOTE(allen): connect
if (connect(socket_server, addr->ai_addr, (int)addr->ai_addrlen) < 0){
w32_socket_set_error_wsa(w32_socket, WSAGetLastError());
return;
}
// NOTE(allen): success
w32_socket->flags |= W32_SocketFlag_Connected;
w32_socket->socket = socket_server;
w32_socket->error = OS_SocketError_None;
closer.do_not_close();
}
internal void
os_socket_close(OS_Socket *socket){
W32_Socket *w32_socket = (W32_Socket*)socket->memory;
closesocket(w32_socket->socket);
MemoryZeroStruct(w32_socket);
}
internal String8
os_socket_read(Arena *arena, OS_Socket *socket){
W32_Socket *w32_socket = (W32_Socket*)socket->memory;
String8 result = {0};
U32 size = 0;
if (w32_socket_read_looped(w32_socket, &size, sizeof(size))){
Temp restore = temp_begin(arena);
result.str = push_array_no_zero(arena, U8, size);
if (w32_socket_read_looped(w32_socket, result.str, size)){
result.size = size;
}
else{
temp_end(restore);
result.str = 0;
}
}
return(result);
}
internal B32
os_socket_write(OS_Socket *socket, String8List list){
U32 size = (U32)list.total_size;
String8Node node = {0};
str8_list_push_front(&list, &node, str8_struct(&size));
W32_Socket *w32_socket = (W32_Socket*)socket->memory;
WSABUF wsabuf[64];
Assert(list.node_count <= ArrayCount(wsabuf));
U64 wsabuf_count = 0;
for (String8Node *node = list.first;
node != 0;
node = node->next){
wsabuf[wsabuf_count].len = (U32)node->string.size;
wsabuf[wsabuf_count].buf = (CHAR*)node->string.str;
wsabuf_count += 1;
}
B32 result = false;
DWORD amt = 0;
if (WSASend(w32_socket->socket, wsabuf, wsabuf_count, &amt, 0, 0, 0) != 0){
w32_socket_set_error_wsa(w32_socket, WSAGetLastError());
}
else if (amt == 0){
w32_socket->flags |= W32_SocketFlag_Connected;
}
else{
result = true;
}
return(result);
}
internal B32
os_socket_status(OS_Socket *socket, OS_SocketStatus status){
W32_Socket *w32_socket = (W32_Socket*)socket;
B32 result = false;
switch (status){
case OS_SocketStatus_Uninitialized:
{
result = (((w32_socket->flags & (W32_SocketFlag_Connected|W32_SocketFlag_Closed)) == 0) &&
(w32_socket->error == 0));
}break;
case OS_SocketStatus_Connected:
{
result = (((w32_socket->flags & (W32_SocketFlag_Connected|W32_SocketFlag_Closed)) == W32_SocketFlag_Connected) &&
(w32_socket->error == 0));
}break;
case OS_SocketStatus_GracefullyClosed:
{
result = (((w32_socket->flags & W32_SocketFlag_Closed) == W32_SocketFlag_Closed) && (w32_socket->error == 0));
}break;
case OS_SocketStatus_Error:
{
result = (w32_socket->error != 0);
}break;
}
return(result);
}
internal String8
os_socket_error_string(Arena *arena, OS_Socket *socket){
String8 result = str8_lit("no error");
W32_Socket *w32_socket = (W32_Socket*)socket;
switch (w32_socket->error){
default:
{
result = str8_lit("Bad error code");
}break;
case OS_SocketError_None:break;
case OS_SocketError_SocketSystemNotInitialized:
{
result = str8_lit("Missing call to os_socket_init");
}break;
case OS_SocketError_BadPortArgument:
{
result = str8_lit("Invalid port argument to socket API");
}break;
case OS_SocketError_BadIPArgument:
{
result = str8_lit("Invalid ip argument to socket API");
}break;
case OS_SocketError_WSAError:
{
DWORD flags = FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_IGNORE_INSERTS|FORMAT_MESSAGE_FROM_SYSTEM;
CHAR *message = 0;
DWORD size = FormatMessageA(flags, 0, w32_socket->wsa_error, 0, (CHAR*)&message, 0, 0);
if (size == 0){
result = str8_lit("Unknown WSA error");
}
else{
String8 string = str8_skip_chop_whitespace(str8((U8*)message, size));
result = push_str8_copy(arena, string);
LocalFree(message);
}
}break;
}
return(result);
}
internal void
os_socket_assert_on_error(OS_Socket *socket, B32 assert_on_error){
W32_Socket *w32_socket = (W32_Socket*)socket;
if (assert_on_error){
w32_socket->flags |= W32_SocketFlag_AssertOnError;
}
else{
w32_socket->flags &= ~W32_SocketFlag_AssertOnError;
}
}
-54
View File
@@ -1,54 +0,0 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#ifndef WIN32_SOCKET_H
#define WIN32_SOCKET_H
////////////////////////////////
//~ rjf: Types
typedef U16 W32_SocketFlags;
enum{
W32_SocketFlag_Connected = (1 << 0),
W32_SocketFlag_Closed = (1 << 1),
W32_SocketFlag_AssertOnError = (1 << 2),
};
struct W32_Socket{
W32_SocketFlags flags;
OS_SocketError error;
int wsa_error;
SOCKET socket;
};
struct W32_SocketCloser{
B32 need_to_close;
SOCKET *socket;
W32_SocketCloser(SOCKET *s){
this->need_to_close = true;
this->socket = s;
}
~W32_SocketCloser(){
this->close_now();
}
void close_now(){
if (this->need_to_close){
closesocket(*this->socket);
this->need_to_close = false;
}
}
void do_not_close(){
this->need_to_close = false;
}
};
StaticAssert(sizeof(Member(OS_Socket, memory)) >= sizeof(W32_Socket), socket_memory_size);
////////////////////////////////
//~ rjf: Helpers
internal void w32_socket_set_error(W32_Socket *socket, OS_SocketError error);
internal void w32_socket_set_error_wsa(W32_Socket *socket, int wsa_error);
internal B32 w32_socket_read_looped(W32_Socket *w32_socket, void *buffer, U32 size);
#endif // WIN32_SOCKET_H
+165 -167
View File
@@ -1,167 +1,165 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
////////////////////////////////
//~ allen: Path Helper Functions
internal StringMatchFlags
path_match_flags_from_os(OperatingSystem os)
{
StringMatchFlags flags = StringMatchFlag_SlashInsensitive;
switch(os)
{
default:{}break;
case OperatingSystem_Windows:
{
flags |= StringMatchFlag_CaseInsensitive;
}break;
case OperatingSystem_Linux:
case OperatingSystem_Mac:
{
// NOTE(rjf): no-op
}break;
}
return flags;
}
internal String8
path_relative_dst_from_absolute_dst_src(Arena *arena, String8 dst, String8 src)
{
Temp scratch = scratch_begin(&arena, 1);
// rjf: gather path parts
String8 dst_name = str8_skip_last_slash(dst);
String8 src_folder = str8_chop_last_slash(src);
String8 dst_folder = str8_chop_last_slash(dst);
String8List src_folders = str8_split_path(scratch.arena, src_folder);
String8List dst_folders = str8_split_path(scratch.arena, dst_folder);
// rjf: count # of backtracks to get from src -> dest
U64 num_backtracks = src_folders.node_count;
for(String8Node *src_n = src_folders.first, *bp_n = dst_folders.first;
src_n != 0 && bp_n != 0;
src_n = src_n->next, bp_n = bp_n->next)
{
if(str8_match(src_n->string, bp_n->string, path_match_flags_from_os(operating_system_from_context())))
{
num_backtracks -= 1;
}
else
{
break;
}
}
// rjf: only build relative string if # of backtracks is not the entire `src`.
// if getting to `dst` from `src` requires erasing the entire `src`, then the
// only possible way to get to `dst` from `src` is via absolute path.
String8 dst_path = {0};
if(num_backtracks >= src_folders.node_count)
{
dst_path = path_normalized_from_string(arena, dst);
}
else
{
// rjf: build backtrack parts
String8List dst_path_strs = {0};
for(U64 idx = 0; idx < num_backtracks; idx += 1)
{
str8_list_push(scratch.arena, &dst_path_strs, str8_lit(".."));
}
// rjf: build parts of dst which are unique from src
{
B32 unique_from_src = 0;
for(String8Node *src_n = src_folders.first, *bp_n = dst_folders.first;
bp_n != 0;
bp_n = bp_n->next)
{
if(!unique_from_src && (src_n == 0 || !str8_match(src_n->string, bp_n->string, path_match_flags_from_os(operating_system_from_context()))))
{
unique_from_src = 1;
}
if(unique_from_src)
{
str8_list_push(scratch.arena, &dst_path_strs, bp_n->string);
}
if(src_n != 0)
{
src_n = src_n->next;
}
}
}
// rjf: build file name
str8_list_push(scratch.arena, &dst_path_strs, dst_name);
// rjf: join
StringJoin join = {0};
{
join.sep = str8_lit("/");
}
dst_path = str8_list_join(arena, &dst_path_strs, &join);
}
scratch_end(scratch);
return dst_path;
}
internal String8
path_absolute_dst_from_relative_dst_src(Arena *arena, String8 dst, String8 src)
{
String8 result = dst;
PathStyle dst_style = path_style_from_str8(dst);
if(dst_style == PathStyle_Relative)
{
Temp scratch = scratch_begin(&arena, 1);
String8 dst_from_src_absolute = push_str8f(scratch.arena, "%S/%S", src, dst);
String8 dst_from_src_absolute_normalized = path_normalized_from_string(arena, dst_from_src_absolute);
result = dst_from_src_absolute_normalized;
scratch_end(scratch);
}
return result;
}
internal String8List
path_normalized_list_from_string(Arena *arena, String8 path_string, PathStyle *style_out){
// analyze path
PathStyle path_style = path_style_from_str8(path_string);
String8List path = str8_split_path(arena, path_string);
// prepend current path to convert relative -> absolute
PathStyle path_style_full = path_style;
if (path.node_count != 0 && path_style == PathStyle_Relative){
String8List current_path_strs = {0};
os_string_list_from_system_path(arena, OS_SystemPath_Current, &current_path_strs);
String8 current_path_string = str8_list_first(&current_path_strs);
PathStyle current_path_style = path_style_from_str8(current_path_string);
Assert(current_path_style != PathStyle_Relative);
String8List current_path = str8_split_path(arena, current_path_string);
str8_list_concat_in_place(&current_path, &path);
path = current_path;
path_style_full = current_path_style;
}
// resolve dots
str8_path_list_resolve_dots_in_place(&path, path_style_full);
// return
if (style_out != 0){
*style_out = path_style_full;
}
return(path);
}
internal String8
path_normalized_from_string(Arena *arena, String8 path_string){
Temp scratch = scratch_begin(&arena, 1);
PathStyle style = PathStyle_Relative;
String8List path = path_normalized_list_from_string(scratch.arena, path_string, &style);
String8 result = str8_path_list_join_by_style(arena, &path, style);
scratch_end(scratch);
return(result);
}
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
////////////////////////////////
//~ allen: Path Helper Functions
internal StringMatchFlags
path_match_flags_from_os(OperatingSystem os)
{
StringMatchFlags flags = StringMatchFlag_SlashInsensitive;
switch(os)
{
default:{}break;
case OperatingSystem_Windows:
{
flags |= StringMatchFlag_CaseInsensitive;
}break;
case OperatingSystem_Linux:
case OperatingSystem_Mac:
{
// NOTE(rjf): no-op
}break;
}
return flags;
}
internal String8
path_relative_dst_from_absolute_dst_src(Arena *arena, String8 dst, String8 src)
{
Temp scratch = scratch_begin(&arena, 1);
// rjf: gather path parts
String8 dst_name = str8_skip_last_slash(dst);
String8 src_folder = str8_chop_last_slash(src);
String8 dst_folder = str8_chop_last_slash(dst);
String8List src_folders = str8_split_path(scratch.arena, src_folder);
String8List dst_folders = str8_split_path(scratch.arena, dst_folder);
// rjf: count # of backtracks to get from src -> dest
U64 num_backtracks = src_folders.node_count;
for(String8Node *src_n = src_folders.first, *bp_n = dst_folders.first;
src_n != 0 && bp_n != 0;
src_n = src_n->next, bp_n = bp_n->next)
{
if(str8_match(src_n->string, bp_n->string, path_match_flags_from_os(operating_system_from_context())))
{
num_backtracks -= 1;
}
else
{
break;
}
}
// rjf: only build relative string if # of backtracks is not the entire `src`.
// if getting to `dst` from `src` requires erasing the entire `src`, then the
// only possible way to get to `dst` from `src` is via absolute path.
String8 dst_path = {0};
if(num_backtracks >= src_folders.node_count)
{
dst_path = path_normalized_from_string(arena, dst);
}
else
{
// rjf: build backtrack parts
String8List dst_path_strs = {0};
for(U64 idx = 0; idx < num_backtracks; idx += 1)
{
str8_list_push(scratch.arena, &dst_path_strs, str8_lit(".."));
}
// rjf: build parts of dst which are unique from src
{
B32 unique_from_src = 0;
for(String8Node *src_n = src_folders.first, *bp_n = dst_folders.first;
bp_n != 0;
bp_n = bp_n->next)
{
if(!unique_from_src && (src_n == 0 || !str8_match(src_n->string, bp_n->string, path_match_flags_from_os(operating_system_from_context()))))
{
unique_from_src = 1;
}
if(unique_from_src)
{
str8_list_push(scratch.arena, &dst_path_strs, bp_n->string);
}
if(src_n != 0)
{
src_n = src_n->next;
}
}
}
// rjf: build file name
str8_list_push(scratch.arena, &dst_path_strs, dst_name);
// rjf: join
StringJoin join = {0};
{
join.sep = str8_lit("/");
}
dst_path = str8_list_join(arena, &dst_path_strs, &join);
}
scratch_end(scratch);
return dst_path;
}
internal String8
path_absolute_dst_from_relative_dst_src(Arena *arena, String8 dst, String8 src)
{
String8 result = dst;
PathStyle dst_style = path_style_from_str8(dst);
if(dst_style == PathStyle_Relative)
{
Temp scratch = scratch_begin(&arena, 1);
String8 dst_from_src_absolute = push_str8f(scratch.arena, "%S/%S", src, dst);
String8 dst_from_src_absolute_normalized = path_normalized_from_string(arena, dst_from_src_absolute);
result = dst_from_src_absolute_normalized;
scratch_end(scratch);
}
return result;
}
internal String8List
path_normalized_list_from_string(Arena *arena, String8 path_string, PathStyle *style_out){
// analyze path
PathStyle path_style = path_style_from_str8(path_string);
String8List path = str8_split_path(arena, path_string);
// prepend current path to convert relative -> absolute
PathStyle path_style_full = path_style;
if (path.node_count != 0 && path_style == PathStyle_Relative){
String8 current_path_string = os_get_current_path(arena);
PathStyle current_path_style = path_style_from_str8(current_path_string);
Assert(current_path_style != PathStyle_Relative);
String8List current_path = str8_split_path(arena, current_path_string);
str8_list_concat_in_place(&current_path, &path);
path = current_path;
path_style_full = current_path_style;
}
// resolve dots
str8_path_list_resolve_dots_in_place(&path, path_style_full);
// return
if (style_out != 0){
*style_out = path_style_full;
}
return(path);
}
internal String8
path_normalized_from_string(Arena *arena, String8 path_string){
Temp scratch = scratch_begin(&arena, 1);
PathStyle style = PathStyle_Relative;
String8List path = path_normalized_list_from_string(scratch.arena, path_string, &style);
String8 result = str8_path_list_join_by_style(arena, &path, style);
scratch_end(scratch);
return(result);
}
+1003 -1004
View File
File diff suppressed because it is too large Load Diff
+397 -397
View File
@@ -1,397 +1,397 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
////////////////////////////////
//~ rjf: Frontend Entry Points
internal void
update_and_render(OS_Handle repaint_window_handle, void *user_data)
{
ProfTick(0);
ProfBeginFunction();
Temp scratch = scratch_begin(0, 0);
//////////////////////////////
//- rjf: begin logging
//
if(main_thread_log == 0)
{
main_thread_log = log_alloc();
String8 user_program_data_path = os_string_from_system_path(scratch.arena, OS_SystemPath_UserProgramData);
String8 user_data_folder = push_str8f(scratch.arena, "%S/raddbg/logs", user_program_data_path);
main_thread_log_path = push_str8f(df_state->arena, "%S/ui_thread.raddbg_log", user_data_folder);
os_make_directory(user_data_folder);
os_write_data_to_file_path(main_thread_log_path, str8_zero());
}
log_select(main_thread_log);
log_scope_begin();
//////////////////////////////
//- rjf: tick cache layers
//
txt_user_clock_tick();
dasm_user_clock_tick();
geo_user_clock_tick();
tex_user_clock_tick();
//////////////////////////////
//- rjf: pick target hz
//
// TODO(rjf): maximize target, given all windows and their monitors
F32 target_hz = os_default_refresh_rate();
if(frame_time_us_history_idx > 32)
{
// rjf: calculate average frame time out of the last N
U64 num_frames_in_history = Min(ArrayCount(frame_time_us_history), frame_time_us_history_idx);
U64 frame_time_history_sum_us = 0;
for(U64 idx = 0; idx < num_frames_in_history; idx += 1)
{
frame_time_history_sum_us += frame_time_us_history[idx];
}
U64 frame_time_history_avg_us = frame_time_history_sum_us/num_frames_in_history;
// rjf: pick among a number of sensible targets to snap to, given how well
// we've been performing
F32 possible_alternate_hz_targets[] = {target_hz, 60.f, 120.f, 144.f, 240.f};
F32 best_target_hz = target_hz;
S64 best_target_hz_frame_time_us_diff = max_S64;
for(U64 idx = 0; idx < ArrayCount(possible_alternate_hz_targets); idx += 1)
{
F32 candidate = possible_alternate_hz_targets[idx];
if(candidate <= target_hz)
{
U64 candidate_frame_time_us = 1000000/(U64)candidate;
S64 frame_time_us_diff = (S64)frame_time_history_avg_us - (S64)candidate_frame_time_us;
if(abs_s64(frame_time_us_diff) < best_target_hz_frame_time_us_diff)
{
best_target_hz = candidate;
best_target_hz_frame_time_us_diff = frame_time_us_diff;
}
}
}
target_hz = best_target_hz;
}
//////////////////////////////
//- rjf: target Hz -> delta time
//
F32 dt = 1.f/target_hz;
//////////////////////////////
//- rjf: get events from the OS
//
OS_EventList events = {0};
if(os_handle_match(repaint_window_handle, os_handle_zero()))
{
events = os_get_events(scratch.arena, df_gfx_state->num_frames_requested == 0);
}
//////////////////////////////
//- rjf: begin measuring actual per-frame work
//
U64 begin_time_us = os_now_microseconds();
//////////////////////////////
//- rjf: bind change
//
if(!df_gfx_state->confirm_active && df_gfx_state->bind_change_active)
{
if(os_key_press(&events, os_handle_zero(), 0, OS_Key_Esc))
{
df_gfx_request_frame();
df_gfx_state->bind_change_active = 0;
}
if(os_key_press(&events, os_handle_zero(), 0, OS_Key_Delete))
{
df_gfx_request_frame();
df_unbind_spec(df_gfx_state->bind_change_cmd_spec, df_gfx_state->bind_change_binding);
df_gfx_state->bind_change_active = 0;
DF_CmdParams p = df_cmd_params_from_gfx();
df_push_cmd__root(&p, df_cmd_spec_from_core_cmd_kind(df_g_cfg_src_write_cmd_kind_table[DF_CfgSrc_User]));
}
for(OS_Event *event = events.first, *next = 0; event != 0; event = next)
{
if(event->kind == OS_EventKind_Press &&
event->key != OS_Key_Esc &&
event->key != OS_Key_Return &&
event->key != OS_Key_Backspace &&
event->key != OS_Key_Delete &&
event->key != OS_Key_LeftMouseButton &&
event->key != OS_Key_RightMouseButton &&
event->key != OS_Key_MiddleMouseButton &&
event->key != OS_Key_Ctrl &&
event->key != OS_Key_Alt &&
event->key != OS_Key_Shift)
{
df_gfx_state->bind_change_active = 0;
DF_Binding binding = zero_struct;
{
binding.key = event->key;
binding.flags = event->flags;
}
df_unbind_spec(df_gfx_state->bind_change_cmd_spec, df_gfx_state->bind_change_binding);
df_bind_spec(df_gfx_state->bind_change_cmd_spec, binding);
U32 codepoint = os_codepoint_from_event_flags_and_key(event->flags, event->key);
os_text(&events, os_handle_zero(), codepoint);
os_eat_event(&events, event);
DF_CmdParams p = df_cmd_params_from_gfx();
df_push_cmd__root(&p, df_cmd_spec_from_core_cmd_kind(df_g_cfg_src_write_cmd_kind_table[DF_CfgSrc_User]));
df_gfx_request_frame();
break;
}
}
}
//////////////////////////////
//- rjf: consume events
//
B32 queue_drag_drop = 0;
{
for(OS_Event *event = events.first, *next = 0;
event != 0;
event = next)
{
next = event->next;
DF_Window *window = df_window_from_os_handle(event->window);
DF_CmdParams params = window ? df_cmd_params_from_window(window) : df_cmd_params_from_gfx();
B32 take = 0;
B32 skip = 0;
//- rjf: try drag-drop
if(df_drag_is_active() && event->kind == OS_EventKind_Release && event->key == OS_Key_LeftMouseButton)
{
skip = 1;
queue_drag_drop = 1;
}
//- rjf: try window close
if(!take && event->kind == OS_EventKind_WindowClose && window != 0)
{
take = 1;
DF_CmdParams params = df_cmd_params_from_window(window);
df_push_cmd__root(&params, df_cmd_spec_from_core_cmd_kind(DF_CoreCmdKind_CloseWindow));
}
//- rjf: try menu bar operations
{
if(!take && event->kind == OS_EventKind_Press && event->key == OS_Key_Alt && event->flags == 0 && event->is_repeat == 0)
{
take = 1;
df_gfx_request_frame();
window->menu_bar_focused_on_press = window->menu_bar_focused;
window->menu_bar_key_held = 1;
window->menu_bar_focus_press_started = 1;
}
if(!take && event->kind == OS_EventKind_Release && event->key == OS_Key_Alt && event->flags == 0 && event->is_repeat == 0)
{
take = 1;
df_gfx_request_frame();
window->menu_bar_key_held = 0;
}
if(window->menu_bar_focused && event->kind == OS_EventKind_Press && event->key == OS_Key_Alt && event->flags == 0 && event->is_repeat == 0)
{
take = 1;
df_gfx_request_frame();
window->menu_bar_focused = 0;
}
else if(window->menu_bar_focus_press_started && !window->menu_bar_focused && event->kind == OS_EventKind_Release && event->flags == 0 && event->key == OS_Key_Alt && event->is_repeat == 0)
{
take = 1;
df_gfx_request_frame();
window->menu_bar_focused = !window->menu_bar_focused_on_press;
window->menu_bar_focus_press_started = 0;
}
else if(event->kind == OS_EventKind_Press && event->key == OS_Key_Esc && window->menu_bar_focused && !ui_any_ctx_menu_is_open())
{
take = 1;
df_gfx_request_frame();
window->menu_bar_focused = 0;
}
}
//- rjf: try hotkey presses
if(!take && event->kind == OS_EventKind_Press)
{
DF_Binding binding = {event->key, event->flags};
DF_CmdSpecList spec_candidates = df_cmd_spec_list_from_binding(scratch.arena, binding);
if(spec_candidates.first != 0 && !df_cmd_spec_is_nil(spec_candidates.first->spec))
{
DF_CmdSpec *run_spec = df_cmd_spec_from_core_cmd_kind(DF_CoreCmdKind_RunCommand);
DF_CmdSpec *spec = spec_candidates.first->spec;
if(run_spec != spec)
{
params.cmd_spec = spec;
df_cmd_params_mark_slot(&params, DF_CmdParamSlot_CmdSpec);
}
U32 hit_char = os_codepoint_from_event_flags_and_key(event->flags, event->key);
take = 1;
df_push_cmd__root(&params, run_spec);
if(event->flags & OS_EventFlag_Alt)
{
window->menu_bar_focus_press_started = 0;
}
}
else if(OS_Key_F1 <= event->key && event->key <= OS_Key_F19)
{
window->menu_bar_focus_press_started = 0;
}
df_gfx_request_frame();
}
//- rjf: try text events
if(!take && event->kind == OS_EventKind_Text)
{
String32 insertion32 = str32(&event->character, 1);
String8 insertion8 = str8_from_32(scratch.arena, insertion32);
DF_CmdSpec *spec = df_cmd_spec_from_core_cmd_kind(DF_CoreCmdKind_InsertText);
params.string = insertion8;
df_cmd_params_mark_slot(&params, DF_CmdParamSlot_String);
df_push_cmd__root(&params, spec);
df_gfx_request_frame();
take = 1;
if(event->flags & OS_EventFlag_Alt)
{
window->menu_bar_focus_press_started = 0;
}
}
//- rjf: do fall-through
if(!take)
{
take = 1;
params.os_event = event;
df_push_cmd__root(&params, df_cmd_spec_from_core_cmd_kind(DF_CoreCmdKind_OSEvent));
}
//- rjf: take
if(take && !skip)
{
os_eat_event(&events, event);
}
}
}
//////////////////////////////
//- rjf: gather root-level commands
//
DF_CmdList cmds = df_core_gather_root_cmds(scratch.arena);
//////////////////////////////
//- rjf: begin frame
//
df_core_begin_frame(scratch.arena, &cmds, dt);
df_gfx_begin_frame(scratch.arena, &cmds);
//////////////////////////////
//- rjf: queue drop for drag/drop
//
if(queue_drag_drop)
{
df_queue_drag_drop();
}
//////////////////////////////
//- rjf: auto-focus moused-over windows while dragging
//
if(df_drag_is_active())
{
B32 over_focused_window = 0;
{
for(DF_Window *window = df_gfx_state->first_window; window != 0; window = window->next)
{
Vec2F32 mouse = os_mouse_from_window(window->os);
Rng2F32 rect = os_client_rect_from_window(window->os);
if(os_window_is_focused(window->os) && contains_2f32(rect, mouse))
{
over_focused_window = 1;
break;
}
}
}
if(!over_focused_window)
{
for(DF_Window *window = df_gfx_state->first_window; window != 0; window = window->next)
{
Vec2F32 mouse = os_mouse_from_window(window->os);
Rng2F32 rect = os_client_rect_from_window(window->os);
if(!os_window_is_focused(window->os) && contains_2f32(rect, mouse))
{
os_window_focus(window->os);
break;
}
}
}
}
//////////////////////////////
//- rjf: update & render
//
{
d_begin_frame();
for(DF_Window *w = df_gfx_state->first_window; w != 0; w = w->next)
{
B32 window_is_focused = os_window_is_focused(w->os);
if(window_is_focused)
{
last_focused_window = df_handle_from_window(w);
}
df_push_interact_regs();
df_window_update_and_render(scratch.arena, w, &cmds);
DF_InteractRegs *window_regs = df_pop_interact_regs();
if(df_window_from_handle(last_focused_window) == w)
{
MemoryCopyStruct(df_interact_regs(), window_regs);
}
}
}
//////////////////////////////
//- rjf: end frontend frame, send signals, etc.
//
df_gfx_end_frame();
df_core_end_frame();
//////////////////////////////
//- rjf: submit rendering to all windows
//
{
r_begin_frame();
for(DF_Window *w = df_gfx_state->first_window; w != 0; w = w->next)
{
r_window_begin_frame(w->os, w->r);
d_submit_bucket(w->os, w->r, w->draw_bucket);
r_window_end_frame(w->os, w->r);
}
r_end_frame();
}
//////////////////////////////
//- rjf: determine frame time, record into history
//
U64 end_time_us = os_now_microseconds();
U64 frame_time_us = end_time_us-begin_time_us;
frame_time_us_history[frame_time_us_history_idx%ArrayCount(frame_time_us_history)] = frame_time_us;
frame_time_us_history_idx += 1;
//////////////////////////////
//- rjf: end logging
//
{
LogScopeResult log = log_scope_end(scratch.arena);
os_append_data_to_file_path(main_thread_log_path, log.strings[LogMsgKind_Info]);
if(log.strings[LogMsgKind_UserError].size != 0)
{
DF_CmdParams p = df_cmd_params_from_gfx();
p.string = log.strings[LogMsgKind_UserError];
df_push_cmd__root(&p, df_cmd_spec_from_core_cmd_kind(DF_CoreCmdKind_Error));
}
}
scratch_end(scratch);
ProfEnd();
}
internal CTRL_WAKEUP_FUNCTION_DEF(wakeup_hook_ctrl)
{
os_send_wakeup_event();
}
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
////////////////////////////////
//~ rjf: Frontend Entry Points
internal void
update_and_render(OS_Handle repaint_window_handle, void *user_data)
{
ProfTick(0);
ProfBeginFunction();
Temp scratch = scratch_begin(0, 0);
//////////////////////////////
//- rjf: begin logging
//
if(main_thread_log == 0)
{
main_thread_log = log_alloc();
String8 user_program_data_path = os_get_process_info()->user_program_data_path;
String8 user_data_folder = push_str8f(scratch.arena, "%S/raddbg/logs", user_program_data_path);
main_thread_log_path = push_str8f(df_state->arena, "%S/ui_thread.raddbg_log", user_data_folder);
os_make_directory(user_data_folder);
os_write_data_to_file_path(main_thread_log_path, str8_zero());
}
log_select(main_thread_log);
log_scope_begin();
//////////////////////////////
//- rjf: tick cache layers
//
txt_user_clock_tick();
dasm_user_clock_tick();
geo_user_clock_tick();
tex_user_clock_tick();
//////////////////////////////
//- rjf: pick target hz
//
// TODO(rjf): maximize target, given all windows and their monitors
F32 target_hz = os_get_gfx_info()->default_refresh_rate;
if(frame_time_us_history_idx > 32)
{
// rjf: calculate average frame time out of the last N
U64 num_frames_in_history = Min(ArrayCount(frame_time_us_history), frame_time_us_history_idx);
U64 frame_time_history_sum_us = 0;
for(U64 idx = 0; idx < num_frames_in_history; idx += 1)
{
frame_time_history_sum_us += frame_time_us_history[idx];
}
U64 frame_time_history_avg_us = frame_time_history_sum_us/num_frames_in_history;
// rjf: pick among a number of sensible targets to snap to, given how well
// we've been performing
F32 possible_alternate_hz_targets[] = {target_hz, 60.f, 120.f, 144.f, 240.f};
F32 best_target_hz = target_hz;
S64 best_target_hz_frame_time_us_diff = max_S64;
for(U64 idx = 0; idx < ArrayCount(possible_alternate_hz_targets); idx += 1)
{
F32 candidate = possible_alternate_hz_targets[idx];
if(candidate <= target_hz)
{
U64 candidate_frame_time_us = 1000000/(U64)candidate;
S64 frame_time_us_diff = (S64)frame_time_history_avg_us - (S64)candidate_frame_time_us;
if(abs_s64(frame_time_us_diff) < best_target_hz_frame_time_us_diff)
{
best_target_hz = candidate;
best_target_hz_frame_time_us_diff = frame_time_us_diff;
}
}
}
target_hz = best_target_hz;
}
//////////////////////////////
//- rjf: target Hz -> delta time
//
F32 dt = 1.f/target_hz;
//////////////////////////////
//- rjf: get events from the OS
//
OS_EventList events = {0};
if(os_handle_match(repaint_window_handle, os_handle_zero()))
{
events = os_get_events(scratch.arena, df_gfx_state->num_frames_requested == 0);
}
//////////////////////////////
//- rjf: begin measuring actual per-frame work
//
U64 begin_time_us = os_now_microseconds();
//////////////////////////////
//- rjf: bind change
//
if(!df_gfx_state->confirm_active && df_gfx_state->bind_change_active)
{
if(os_key_press(&events, os_handle_zero(), 0, OS_Key_Esc))
{
df_gfx_request_frame();
df_gfx_state->bind_change_active = 0;
}
if(os_key_press(&events, os_handle_zero(), 0, OS_Key_Delete))
{
df_gfx_request_frame();
df_unbind_spec(df_gfx_state->bind_change_cmd_spec, df_gfx_state->bind_change_binding);
df_gfx_state->bind_change_active = 0;
DF_CmdParams p = df_cmd_params_from_gfx();
df_push_cmd__root(&p, df_cmd_spec_from_core_cmd_kind(df_g_cfg_src_write_cmd_kind_table[DF_CfgSrc_User]));
}
for(OS_Event *event = events.first, *next = 0; event != 0; event = next)
{
if(event->kind == OS_EventKind_Press &&
event->key != OS_Key_Esc &&
event->key != OS_Key_Return &&
event->key != OS_Key_Backspace &&
event->key != OS_Key_Delete &&
event->key != OS_Key_LeftMouseButton &&
event->key != OS_Key_RightMouseButton &&
event->key != OS_Key_MiddleMouseButton &&
event->key != OS_Key_Ctrl &&
event->key != OS_Key_Alt &&
event->key != OS_Key_Shift)
{
df_gfx_state->bind_change_active = 0;
DF_Binding binding = zero_struct;
{
binding.key = event->key;
binding.flags = event->flags;
}
df_unbind_spec(df_gfx_state->bind_change_cmd_spec, df_gfx_state->bind_change_binding);
df_bind_spec(df_gfx_state->bind_change_cmd_spec, binding);
U32 codepoint = os_codepoint_from_event_flags_and_key(event->flags, event->key);
os_text(&events, os_handle_zero(), codepoint);
os_eat_event(&events, event);
DF_CmdParams p = df_cmd_params_from_gfx();
df_push_cmd__root(&p, df_cmd_spec_from_core_cmd_kind(df_g_cfg_src_write_cmd_kind_table[DF_CfgSrc_User]));
df_gfx_request_frame();
break;
}
}
}
//////////////////////////////
//- rjf: consume events
//
B32 queue_drag_drop = 0;
{
for(OS_Event *event = events.first, *next = 0;
event != 0;
event = next)
{
next = event->next;
DF_Window *window = df_window_from_os_handle(event->window);
DF_CmdParams params = window ? df_cmd_params_from_window(window) : df_cmd_params_from_gfx();
B32 take = 0;
B32 skip = 0;
//- rjf: try drag-drop
if(df_drag_is_active() && event->kind == OS_EventKind_Release && event->key == OS_Key_LeftMouseButton)
{
skip = 1;
queue_drag_drop = 1;
}
//- rjf: try window close
if(!take && event->kind == OS_EventKind_WindowClose && window != 0)
{
take = 1;
DF_CmdParams params = df_cmd_params_from_window(window);
df_push_cmd__root(&params, df_cmd_spec_from_core_cmd_kind(DF_CoreCmdKind_CloseWindow));
}
//- rjf: try menu bar operations
{
if(!take && event->kind == OS_EventKind_Press && event->key == OS_Key_Alt && event->flags == 0 && event->is_repeat == 0)
{
take = 1;
df_gfx_request_frame();
window->menu_bar_focused_on_press = window->menu_bar_focused;
window->menu_bar_key_held = 1;
window->menu_bar_focus_press_started = 1;
}
if(!take && event->kind == OS_EventKind_Release && event->key == OS_Key_Alt && event->flags == 0 && event->is_repeat == 0)
{
take = 1;
df_gfx_request_frame();
window->menu_bar_key_held = 0;
}
if(window->menu_bar_focused && event->kind == OS_EventKind_Press && event->key == OS_Key_Alt && event->flags == 0 && event->is_repeat == 0)
{
take = 1;
df_gfx_request_frame();
window->menu_bar_focused = 0;
}
else if(window->menu_bar_focus_press_started && !window->menu_bar_focused && event->kind == OS_EventKind_Release && event->flags == 0 && event->key == OS_Key_Alt && event->is_repeat == 0)
{
take = 1;
df_gfx_request_frame();
window->menu_bar_focused = !window->menu_bar_focused_on_press;
window->menu_bar_focus_press_started = 0;
}
else if(event->kind == OS_EventKind_Press && event->key == OS_Key_Esc && window->menu_bar_focused && !ui_any_ctx_menu_is_open())
{
take = 1;
df_gfx_request_frame();
window->menu_bar_focused = 0;
}
}
//- rjf: try hotkey presses
if(!take && event->kind == OS_EventKind_Press)
{
DF_Binding binding = {event->key, event->flags};
DF_CmdSpecList spec_candidates = df_cmd_spec_list_from_binding(scratch.arena, binding);
if(spec_candidates.first != 0 && !df_cmd_spec_is_nil(spec_candidates.first->spec))
{
DF_CmdSpec *run_spec = df_cmd_spec_from_core_cmd_kind(DF_CoreCmdKind_RunCommand);
DF_CmdSpec *spec = spec_candidates.first->spec;
if(run_spec != spec)
{
params.cmd_spec = spec;
df_cmd_params_mark_slot(&params, DF_CmdParamSlot_CmdSpec);
}
U32 hit_char = os_codepoint_from_event_flags_and_key(event->flags, event->key);
take = 1;
df_push_cmd__root(&params, run_spec);
if(event->flags & OS_EventFlag_Alt)
{
window->menu_bar_focus_press_started = 0;
}
}
else if(OS_Key_F1 <= event->key && event->key <= OS_Key_F19)
{
window->menu_bar_focus_press_started = 0;
}
df_gfx_request_frame();
}
//- rjf: try text events
if(!take && event->kind == OS_EventKind_Text)
{
String32 insertion32 = str32(&event->character, 1);
String8 insertion8 = str8_from_32(scratch.arena, insertion32);
DF_CmdSpec *spec = df_cmd_spec_from_core_cmd_kind(DF_CoreCmdKind_InsertText);
params.string = insertion8;
df_cmd_params_mark_slot(&params, DF_CmdParamSlot_String);
df_push_cmd__root(&params, spec);
df_gfx_request_frame();
take = 1;
if(event->flags & OS_EventFlag_Alt)
{
window->menu_bar_focus_press_started = 0;
}
}
//- rjf: do fall-through
if(!take)
{
take = 1;
params.os_event = event;
df_push_cmd__root(&params, df_cmd_spec_from_core_cmd_kind(DF_CoreCmdKind_OSEvent));
}
//- rjf: take
if(take && !skip)
{
os_eat_event(&events, event);
}
}
}
//////////////////////////////
//- rjf: gather root-level commands
//
DF_CmdList cmds = df_core_gather_root_cmds(scratch.arena);
//////////////////////////////
//- rjf: begin frame
//
df_core_begin_frame(scratch.arena, &cmds, dt);
df_gfx_begin_frame(scratch.arena, &cmds);
//////////////////////////////
//- rjf: queue drop for drag/drop
//
if(queue_drag_drop)
{
df_queue_drag_drop();
}
//////////////////////////////
//- rjf: auto-focus moused-over windows while dragging
//
if(df_drag_is_active())
{
B32 over_focused_window = 0;
{
for(DF_Window *window = df_gfx_state->first_window; window != 0; window = window->next)
{
Vec2F32 mouse = os_mouse_from_window(window->os);
Rng2F32 rect = os_client_rect_from_window(window->os);
if(os_window_is_focused(window->os) && contains_2f32(rect, mouse))
{
over_focused_window = 1;
break;
}
}
}
if(!over_focused_window)
{
for(DF_Window *window = df_gfx_state->first_window; window != 0; window = window->next)
{
Vec2F32 mouse = os_mouse_from_window(window->os);
Rng2F32 rect = os_client_rect_from_window(window->os);
if(!os_window_is_focused(window->os) && contains_2f32(rect, mouse))
{
os_window_focus(window->os);
break;
}
}
}
}
//////////////////////////////
//- rjf: update & render
//
{
d_begin_frame();
for(DF_Window *w = df_gfx_state->first_window; w != 0; w = w->next)
{
B32 window_is_focused = os_window_is_focused(w->os);
if(window_is_focused)
{
last_focused_window = df_handle_from_window(w);
}
df_push_interact_regs();
df_window_update_and_render(scratch.arena, w, &cmds);
DF_InteractRegs *window_regs = df_pop_interact_regs();
if(df_window_from_handle(last_focused_window) == w)
{
MemoryCopyStruct(df_interact_regs(), window_regs);
}
}
}
//////////////////////////////
//- rjf: end frontend frame, send signals, etc.
//
df_gfx_end_frame();
df_core_end_frame();
//////////////////////////////
//- rjf: submit rendering to all windows
//
{
r_begin_frame();
for(DF_Window *w = df_gfx_state->first_window; w != 0; w = w->next)
{
r_window_begin_frame(w->os, w->r);
d_submit_bucket(w->os, w->r, w->draw_bucket);
r_window_end_frame(w->os, w->r);
}
r_end_frame();
}
//////////////////////////////
//- rjf: determine frame time, record into history
//
U64 end_time_us = os_now_microseconds();
U64 frame_time_us = end_time_us-begin_time_us;
frame_time_us_history[frame_time_us_history_idx%ArrayCount(frame_time_us_history)] = frame_time_us;
frame_time_us_history_idx += 1;
//////////////////////////////
//- rjf: end logging
//
{
LogScopeResult log = log_scope_end(scratch.arena);
os_append_data_to_file_path(main_thread_log_path, log.strings[LogMsgKind_Info]);
if(log.strings[LogMsgKind_UserError].size != 0)
{
DF_CmdParams p = df_cmd_params_from_gfx();
p.string = log.strings[LogMsgKind_UserError];
df_push_cmd__root(&p, df_cmd_spec_from_core_cmd_kind(DF_CoreCmdKind_Error));
}
}
scratch_end(scratch);
ProfEnd();
}
internal CTRL_WAKEUP_FUNCTION_DEF(wakeup_hook_ctrl)
{
os_send_wakeup_event();
}
+574 -574
View File
File diff suppressed because it is too large Load Diff
@@ -1,316 +1,316 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
////////////////////////////////
//~ rjf: Build Options
#define BUILD_VERSION_MAJOR 0
#define BUILD_VERSION_MINOR 9
#define BUILD_VERSION_PATCH 11
#define BUILD_RELEASE_PHASE_STRING_LITERAL "ALPHA"
#define BUILD_TITLE "rdi_breakpad_from_pdb"
#define BUILD_CONSOLE_INTERFACE 1
////////////////////////////////
//~ rjf: Includes
//- rjf: [lib]
#include "lib_rdi_format/rdi_format.h"
#include "lib_rdi_format/rdi_format.c"
#include "lib_rdi_format/rdi_format_parse.h"
#include "lib_rdi_format/rdi_format_parse.c"
#include "third_party/rad_lzb_simple/rad_lzb_simple.h"
#include "third_party/rad_lzb_simple/rad_lzb_simple.c"
//- rjf: [h]
#include "base/base_inc.h"
#include "os/os_inc.h"
#include "task_system/task_system.h"
#include "rdi_make/rdi_make_local.h"
#include "coff/coff.h"
#include "codeview/codeview.h"
#include "codeview/codeview_stringize.h"
#include "msf/msf.h"
#include "pdb/pdb.h"
#include "pdb/pdb_stringize.h"
#include "rdi_from_pdb/rdi_from_pdb.h"
//- rjf: [c]
#include "base/base_inc.c"
#include "os/os_inc.c"
#include "task_system/task_system.c"
#include "rdi_make/rdi_make_local.c"
#include "coff/coff.c"
#include "codeview/codeview.c"
#include "codeview/codeview_stringize.c"
#include "msf/msf.c"
#include "pdb/pdb.c"
#include "pdb/pdb_stringize.c"
#include "rdi_from_pdb/rdi_from_pdb.c"
////////////////////////////////
//~ rjf: Baking Tasks
//- rjf: unit vmap baking
typedef struct P2B_BakeUnitVMapIn P2B_BakeUnitVMapIn;
struct P2B_BakeUnitVMapIn
{
RDIM_UnitChunkList *units;
};
internal TS_TASK_FUNCTION_DEF(p2b_bake_unit_vmap_task__entry_point)
{
P2B_BakeUnitVMapIn *in = (P2B_BakeUnitVMapIn *)p;
RDIM_UnitVMapBakeResult *out = push_array(arena, RDIM_UnitVMapBakeResult, 1);
*out = rdim_bake_unit_vmap(arena, in->units);
return out;
}
//- rjf: line table baking
typedef struct P2B_BakeLineTablesIn P2B_BakeLineTablesIn;
struct P2B_BakeLineTablesIn
{
RDIM_LineTableChunkList *line_tables;
};
internal TS_TASK_FUNCTION_DEF(p2b_bake_line_table_task__entry_point)
{
P2B_BakeLineTablesIn *in = (P2B_BakeLineTablesIn *)p;
RDIM_LineTableBakeResult *out = push_array(arena, RDIM_LineTableBakeResult, 1);
*out = rdim_bake_line_tables(arena, in->line_tables);
return out;
}
//- rjf: per-procedure chunk dumping
typedef struct P2B_DumpProcChunkIn P2B_DumpProcChunkIn;
struct P2B_DumpProcChunkIn
{
RDI_VMapEntry *unit_vmap;
U32 unit_vmap_count;
U32 *unit_line_table_idxs;
U64 unit_count;
RDIM_LineTableBakeResult *line_tables_bake;
RDIM_SymbolChunkNode *chunk;
};
internal TS_TASK_FUNCTION_DEF(p2b_dump_proc_chunk_task__entry_point)
{
P2B_DumpProcChunkIn *in = (P2B_DumpProcChunkIn *)p;
String8List *out = push_array(arena, String8List, 1);
RDI_LineTable *line_tables = in->line_tables_bake->line_tables;
RDI_U64 line_tables_count = in->line_tables_bake->line_tables_count;
RDI_U64 *line_table_voffs = in->line_tables_bake->line_table_voffs;
RDI_U64 line_table_voffs_count = in->line_tables_bake->line_table_voffs_count;
RDI_Line *line_table_lines = in->line_tables_bake->line_table_lines;
RDI_U64 line_table_lines_count = in->line_tables_bake->line_table_lines_count;
for(U64 idx = 0; idx < in->chunk->count; idx += 1)
{
// NOTE(rjf): breakpad does not support multiple voff ranges per procedure.
RDIM_Symbol *proc = &in->chunk->v[idx];
RDIM_Scope *root_scope = proc->root_scope;
if(root_scope != 0 && root_scope->voff_ranges.first != 0)
{
// rjf: dump function record
RDIM_Rng1U64 voff_range = root_scope->voff_ranges.first->v;
str8_list_pushf(arena, out, "FUNC %I64x %I64x %I64x %S\n", voff_range.min, voff_range.max-voff_range.min, 0ull, proc->name);
// rjf: dump function lines
U64 unit_idx = rdi_vmap_idx_from_voff(in->unit_vmap, in->unit_vmap_count, voff_range.min);
if(0 < unit_idx && unit_idx <= in->unit_count)
{
U32 line_table_idx = in->unit_line_table_idxs[unit_idx];
if(0 < line_table_idx && line_table_idx <= line_tables_count)
{
// rjf: unpack unit line info
RDI_LineTable *line_table = &line_tables[line_table_idx];
RDI_ParsedLineTable line_info =
{
line_table_voffs + line_table->voffs_base_idx,
line_table_lines + line_table->lines_base_idx,
0,
line_table->lines_count,
0
};
for(U64 voff = voff_range.min, last_voff = 0;
voff < voff_range.max && voff > last_voff;)
{
RDI_U64 line_info_idx = rdi_line_info_idx_from_voff(&line_info, voff);
if(line_info_idx < line_info.count)
{
RDI_Line *line = &line_info.lines[line_info_idx];
U64 line_voff_min = line_info.voffs[line_info_idx];
U64 line_voff_opl = line_info.voffs[line_info_idx+1];
if(line->file_idx != 0)
{
str8_list_pushf(arena, out, "%I64x %I64x %I64u %I64u\n",
line_voff_min,
line_voff_opl-line_voff_min,
(U64)line->line_num,
(U64)line->file_idx);
}
last_voff = voff;
voff = line_voff_opl;
}
else
{
break;
}
}
}
}
}
}
return out;
}
////////////////////////////////
//~ rjf: Entry Point
internal void
entry_point(CmdLine *cmdline)
{
//- rjf: initialize state, unpack command line
Arena *arena = arena_alloc();
B32 do_help = (cmd_line_has_flag(cmdline, str8_lit("help")) ||
cmd_line_has_flag(cmdline, str8_lit("h")) ||
cmd_line_has_flag(cmdline, str8_lit("?")));
P2R_User2Convert *user2convert = p2r_user2convert_from_cmdln(arena, cmdline);
user2convert->flags &= ~(P2R_ConvertFlag_Types|P2R_ConvertFlag_UDTs);
//- rjf: display help
if(do_help || user2convert->errors.node_count != 0)
{
fprintf(stderr, "--- rdi_breakpad_from_pdb -----------------------------------------------------\n\n");
fprintf(stderr, "This utility converts debug information from PDBs into the textual Breakpad\n");
fprintf(stderr, "symbol information format, used for various external utilities, using the RAD\n");
fprintf(stderr, "Debug Info conversion systems. The following arguments are accepted:\n\n");
fprintf(stderr, "--exe:<path> [optional] Specifies the path of the executable file for which the\n");
fprintf(stderr, " debug info was generated.\n");
fprintf(stderr, "--pdb:<path> Specifies the path of the PDB debug info file to\n");
fprintf(stderr, " convert.\n");
fprintf(stderr, "--out:<path> Specifies the path at which the output Breakpad debug\n");
fprintf(stderr, " info will be written.\n\n");
if(!do_help)
{
for(String8Node *n = user2convert->errors.first; n != 0; n = n->next)
{
fprintf(stderr, "error(input): %.*s\n", str8_varg(n->string));
}
}
os_exit_process(0);
}
//- rjf: convert
P2R_Convert2Bake *convert2bake = 0;
ProfScope("convert")
{
convert2bake = p2r_convert(arena, user2convert);
}
//- rjf: dump breakpad text
String8List dump = {0};
ProfScope("dump breakpad text")
{
RDIM_BakeParams *params = &convert2bake->bake_params;
//- rjf: kick off unit vmap baking
P2B_BakeUnitVMapIn bake_unit_vmap_in = {&params->units};
TS_Ticket bake_unit_vmap_ticket = ts_kickoff(p2b_bake_unit_vmap_task__entry_point, 0, &bake_unit_vmap_in);
//- rjf: kick off line-table baking
P2B_BakeLineTablesIn bake_line_tables_in = {&params->line_tables};
TS_Ticket bake_line_tables_ticket = ts_kickoff(p2b_bake_line_table_task__entry_point, 0, &bake_line_tables_in);
//- rjf: build unit -> line table idx array
U64 unit_count = params->units.total_count;
U32 *unit_line_table_idxs = push_array(arena, U32, unit_count+1);
{
U64 dst_idx = 1;
for(RDIM_UnitChunkNode *n = params->units.first; n != 0; n = n->next)
{
for(U64 n_idx = 0; n_idx < n->count; n_idx += 1, dst_idx += 1)
{
unit_line_table_idxs[dst_idx] = rdim_idx_from_line_table(n->v[n_idx].line_table);
}
}
}
//- rjf: dump MODULE record
str8_list_pushf(arena, &dump, "MODULE windows x86_64 %I64x %S\n", params->top_level_info.exe_hash, params->top_level_info.exe_name);
//- rjf: dump FILE records
ProfScope("dump FILE records")
{
for(RDIM_SrcFileChunkNode *n = params->src_files.first; n != 0; n = n->next)
{
for(U64 idx = 0; idx < n->count; idx += 1)
{
U64 file_idx = rdim_idx_from_src_file(&n->v[idx]);
String8 src_path = n->v[idx].normal_full_path;
str8_list_pushf(arena, &dump, "FILE %I64u %S\n", file_idx, src_path);
}
}
}
//- rjf: join unit vmap
ProfBegin("join unit vmap");
RDIM_UnitVMapBakeResult *bake_unit_vmap_out = ts_join_struct(bake_unit_vmap_ticket, max_U64, RDIM_UnitVMapBakeResult);
RDI_VMapEntry *unit_vmap = bake_unit_vmap_out->vmap.vmap;
U32 unit_vmap_count = bake_unit_vmap_out->vmap.count;
ProfEnd();
//- rjf: join line tables
ProfBegin("join line table");
RDIM_LineTableBakeResult *bake_line_tables_out = ts_join_struct(bake_line_tables_ticket, max_U64, RDIM_LineTableBakeResult);
ProfEnd();
//- rjf: kick off FUNC & line record dump tasks
P2B_DumpProcChunkIn *dump_proc_chunk_in = push_array(arena, P2B_DumpProcChunkIn, params->procedures.chunk_count);
TS_Ticket *dump_proc_chunk_tickets = push_array(arena, TS_Ticket, params->procedures.chunk_count);
ProfScope("kick off FUNC & line record dump tasks")
{
U64 task_idx = 0;
for(RDIM_SymbolChunkNode *n = params->procedures.first; n != 0; n = n->next, task_idx += 1)
{
dump_proc_chunk_in[task_idx].unit_vmap = unit_vmap;
dump_proc_chunk_in[task_idx].unit_vmap_count = unit_vmap_count;
dump_proc_chunk_in[task_idx].unit_line_table_idxs = unit_line_table_idxs;
dump_proc_chunk_in[task_idx].unit_count = unit_count;
dump_proc_chunk_in[task_idx].line_tables_bake = bake_line_tables_out;
dump_proc_chunk_in[task_idx].chunk = n;
dump_proc_chunk_tickets[task_idx] = ts_kickoff(p2b_dump_proc_chunk_task__entry_point, 0, &dump_proc_chunk_in[task_idx]);
}
}
//- rjf: join FUNC & line record dump tasks
ProfScope("join FUNC & line record dump tasks")
{
for(U64 idx = 0; idx < params->procedures.chunk_count; idx += 1)
{
String8List *out = ts_join_struct(dump_proc_chunk_tickets[idx], max_U64, String8List);
str8_list_concat_in_place(&dump, out);
}
}
}
//- rjf: bake
String8 baked = {0};
ProfScope("bake")
{
baked = str8_list_join(arena, &dump, 0);
}
//- rjf: write
ProfScope("write")
{
OS_Handle output_file = os_file_open(OS_AccessFlag_Read|OS_AccessFlag_Write, user2convert->output_name);
os_file_write(output_file, r1u64(0, baked.size), baked.str);
os_file_close(output_file);
}
}
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
////////////////////////////////
//~ rjf: Build Options
#define BUILD_VERSION_MAJOR 0
#define BUILD_VERSION_MINOR 9
#define BUILD_VERSION_PATCH 11
#define BUILD_RELEASE_PHASE_STRING_LITERAL "ALPHA"
#define BUILD_TITLE "rdi_breakpad_from_pdb"
#define BUILD_CONSOLE_INTERFACE 1
////////////////////////////////
//~ rjf: Includes
//- rjf: [lib]
#include "lib_rdi_format/rdi_format.h"
#include "lib_rdi_format/rdi_format.c"
#include "lib_rdi_format/rdi_format_parse.h"
#include "lib_rdi_format/rdi_format_parse.c"
#include "third_party/rad_lzb_simple/rad_lzb_simple.h"
#include "third_party/rad_lzb_simple/rad_lzb_simple.c"
//- rjf: [h]
#include "base/base_inc.h"
#include "os/os_inc.h"
#include "task_system/task_system.h"
#include "rdi_make/rdi_make_local.h"
#include "coff/coff.h"
#include "codeview/codeview.h"
#include "codeview/codeview_stringize.h"
#include "msf/msf.h"
#include "pdb/pdb.h"
#include "pdb/pdb_stringize.h"
#include "rdi_from_pdb/rdi_from_pdb.h"
//- rjf: [c]
#include "base/base_inc.c"
#include "os/os_inc.c"
#include "task_system/task_system.c"
#include "rdi_make/rdi_make_local.c"
#include "coff/coff.c"
#include "codeview/codeview.c"
#include "codeview/codeview_stringize.c"
#include "msf/msf.c"
#include "pdb/pdb.c"
#include "pdb/pdb_stringize.c"
#include "rdi_from_pdb/rdi_from_pdb.c"
////////////////////////////////
//~ rjf: Baking Tasks
//- rjf: unit vmap baking
typedef struct P2B_BakeUnitVMapIn P2B_BakeUnitVMapIn;
struct P2B_BakeUnitVMapIn
{
RDIM_UnitChunkList *units;
};
internal TS_TASK_FUNCTION_DEF(p2b_bake_unit_vmap_task__entry_point)
{
P2B_BakeUnitVMapIn *in = (P2B_BakeUnitVMapIn *)p;
RDIM_UnitVMapBakeResult *out = push_array(arena, RDIM_UnitVMapBakeResult, 1);
*out = rdim_bake_unit_vmap(arena, in->units);
return out;
}
//- rjf: line table baking
typedef struct P2B_BakeLineTablesIn P2B_BakeLineTablesIn;
struct P2B_BakeLineTablesIn
{
RDIM_LineTableChunkList *line_tables;
};
internal TS_TASK_FUNCTION_DEF(p2b_bake_line_table_task__entry_point)
{
P2B_BakeLineTablesIn *in = (P2B_BakeLineTablesIn *)p;
RDIM_LineTableBakeResult *out = push_array(arena, RDIM_LineTableBakeResult, 1);
*out = rdim_bake_line_tables(arena, in->line_tables);
return out;
}
//- rjf: per-procedure chunk dumping
typedef struct P2B_DumpProcChunkIn P2B_DumpProcChunkIn;
struct P2B_DumpProcChunkIn
{
RDI_VMapEntry *unit_vmap;
U32 unit_vmap_count;
U32 *unit_line_table_idxs;
U64 unit_count;
RDIM_LineTableBakeResult *line_tables_bake;
RDIM_SymbolChunkNode *chunk;
};
internal TS_TASK_FUNCTION_DEF(p2b_dump_proc_chunk_task__entry_point)
{
P2B_DumpProcChunkIn *in = (P2B_DumpProcChunkIn *)p;
String8List *out = push_array(arena, String8List, 1);
RDI_LineTable *line_tables = in->line_tables_bake->line_tables;
RDI_U64 line_tables_count = in->line_tables_bake->line_tables_count;
RDI_U64 *line_table_voffs = in->line_tables_bake->line_table_voffs;
RDI_U64 line_table_voffs_count = in->line_tables_bake->line_table_voffs_count;
RDI_Line *line_table_lines = in->line_tables_bake->line_table_lines;
RDI_U64 line_table_lines_count = in->line_tables_bake->line_table_lines_count;
for(U64 idx = 0; idx < in->chunk->count; idx += 1)
{
// NOTE(rjf): breakpad does not support multiple voff ranges per procedure.
RDIM_Symbol *proc = &in->chunk->v[idx];
RDIM_Scope *root_scope = proc->root_scope;
if(root_scope != 0 && root_scope->voff_ranges.first != 0)
{
// rjf: dump function record
RDIM_Rng1U64 voff_range = root_scope->voff_ranges.first->v;
str8_list_pushf(arena, out, "FUNC %I64x %I64x %I64x %S\n", voff_range.min, voff_range.max-voff_range.min, 0ull, proc->name);
// rjf: dump function lines
U64 unit_idx = rdi_vmap_idx_from_voff(in->unit_vmap, in->unit_vmap_count, voff_range.min);
if(0 < unit_idx && unit_idx <= in->unit_count)
{
U32 line_table_idx = in->unit_line_table_idxs[unit_idx];
if(0 < line_table_idx && line_table_idx <= line_tables_count)
{
// rjf: unpack unit line info
RDI_LineTable *line_table = &line_tables[line_table_idx];
RDI_ParsedLineTable line_info =
{
line_table_voffs + line_table->voffs_base_idx,
line_table_lines + line_table->lines_base_idx,
0,
line_table->lines_count,
0
};
for(U64 voff = voff_range.min, last_voff = 0;
voff < voff_range.max && voff > last_voff;)
{
RDI_U64 line_info_idx = rdi_line_info_idx_from_voff(&line_info, voff);
if(line_info_idx < line_info.count)
{
RDI_Line *line = &line_info.lines[line_info_idx];
U64 line_voff_min = line_info.voffs[line_info_idx];
U64 line_voff_opl = line_info.voffs[line_info_idx+1];
if(line->file_idx != 0)
{
str8_list_pushf(arena, out, "%I64x %I64x %I64u %I64u\n",
line_voff_min,
line_voff_opl-line_voff_min,
(U64)line->line_num,
(U64)line->file_idx);
}
last_voff = voff;
voff = line_voff_opl;
}
else
{
break;
}
}
}
}
}
}
return out;
}
////////////////////////////////
//~ rjf: Entry Point
internal void
entry_point(CmdLine *cmdline)
{
//- rjf: initialize state, unpack command line
Arena *arena = arena_alloc();
B32 do_help = (cmd_line_has_flag(cmdline, str8_lit("help")) ||
cmd_line_has_flag(cmdline, str8_lit("h")) ||
cmd_line_has_flag(cmdline, str8_lit("?")));
P2R_User2Convert *user2convert = p2r_user2convert_from_cmdln(arena, cmdline);
user2convert->flags &= ~(P2R_ConvertFlag_Types|P2R_ConvertFlag_UDTs);
//- rjf: display help
if(do_help || user2convert->errors.node_count != 0)
{
fprintf(stderr, "--- rdi_breakpad_from_pdb -----------------------------------------------------\n\n");
fprintf(stderr, "This utility converts debug information from PDBs into the textual Breakpad\n");
fprintf(stderr, "symbol information format, used for various external utilities, using the RAD\n");
fprintf(stderr, "Debug Info conversion systems. The following arguments are accepted:\n\n");
fprintf(stderr, "--exe:<path> [optional] Specifies the path of the executable file for which the\n");
fprintf(stderr, " debug info was generated.\n");
fprintf(stderr, "--pdb:<path> Specifies the path of the PDB debug info file to\n");
fprintf(stderr, " convert.\n");
fprintf(stderr, "--out:<path> Specifies the path at which the output Breakpad debug\n");
fprintf(stderr, " info will be written.\n\n");
if(!do_help)
{
for(String8Node *n = user2convert->errors.first; n != 0; n = n->next)
{
fprintf(stderr, "error(input): %.*s\n", str8_varg(n->string));
}
}
os_abort(0);
}
//- rjf: convert
P2R_Convert2Bake *convert2bake = 0;
ProfScope("convert")
{
convert2bake = p2r_convert(arena, user2convert);
}
//- rjf: dump breakpad text
String8List dump = {0};
ProfScope("dump breakpad text")
{
RDIM_BakeParams *params = &convert2bake->bake_params;
//- rjf: kick off unit vmap baking
P2B_BakeUnitVMapIn bake_unit_vmap_in = {&params->units};
TS_Ticket bake_unit_vmap_ticket = ts_kickoff(p2b_bake_unit_vmap_task__entry_point, 0, &bake_unit_vmap_in);
//- rjf: kick off line-table baking
P2B_BakeLineTablesIn bake_line_tables_in = {&params->line_tables};
TS_Ticket bake_line_tables_ticket = ts_kickoff(p2b_bake_line_table_task__entry_point, 0, &bake_line_tables_in);
//- rjf: build unit -> line table idx array
U64 unit_count = params->units.total_count;
U32 *unit_line_table_idxs = push_array(arena, U32, unit_count+1);
{
U64 dst_idx = 1;
for(RDIM_UnitChunkNode *n = params->units.first; n != 0; n = n->next)
{
for(U64 n_idx = 0; n_idx < n->count; n_idx += 1, dst_idx += 1)
{
unit_line_table_idxs[dst_idx] = rdim_idx_from_line_table(n->v[n_idx].line_table);
}
}
}
//- rjf: dump MODULE record
str8_list_pushf(arena, &dump, "MODULE windows x86_64 %I64x %S\n", params->top_level_info.exe_hash, params->top_level_info.exe_name);
//- rjf: dump FILE records
ProfScope("dump FILE records")
{
for(RDIM_SrcFileChunkNode *n = params->src_files.first; n != 0; n = n->next)
{
for(U64 idx = 0; idx < n->count; idx += 1)
{
U64 file_idx = rdim_idx_from_src_file(&n->v[idx]);
String8 src_path = n->v[idx].normal_full_path;
str8_list_pushf(arena, &dump, "FILE %I64u %S\n", file_idx, src_path);
}
}
}
//- rjf: join unit vmap
ProfBegin("join unit vmap");
RDIM_UnitVMapBakeResult *bake_unit_vmap_out = ts_join_struct(bake_unit_vmap_ticket, max_U64, RDIM_UnitVMapBakeResult);
RDI_VMapEntry *unit_vmap = bake_unit_vmap_out->vmap.vmap;
U32 unit_vmap_count = bake_unit_vmap_out->vmap.count;
ProfEnd();
//- rjf: join line tables
ProfBegin("join line table");
RDIM_LineTableBakeResult *bake_line_tables_out = ts_join_struct(bake_line_tables_ticket, max_U64, RDIM_LineTableBakeResult);
ProfEnd();
//- rjf: kick off FUNC & line record dump tasks
P2B_DumpProcChunkIn *dump_proc_chunk_in = push_array(arena, P2B_DumpProcChunkIn, params->procedures.chunk_count);
TS_Ticket *dump_proc_chunk_tickets = push_array(arena, TS_Ticket, params->procedures.chunk_count);
ProfScope("kick off FUNC & line record dump tasks")
{
U64 task_idx = 0;
for(RDIM_SymbolChunkNode *n = params->procedures.first; n != 0; n = n->next, task_idx += 1)
{
dump_proc_chunk_in[task_idx].unit_vmap = unit_vmap;
dump_proc_chunk_in[task_idx].unit_vmap_count = unit_vmap_count;
dump_proc_chunk_in[task_idx].unit_line_table_idxs = unit_line_table_idxs;
dump_proc_chunk_in[task_idx].unit_count = unit_count;
dump_proc_chunk_in[task_idx].line_tables_bake = bake_line_tables_out;
dump_proc_chunk_in[task_idx].chunk = n;
dump_proc_chunk_tickets[task_idx] = ts_kickoff(p2b_dump_proc_chunk_task__entry_point, 0, &dump_proc_chunk_in[task_idx]);
}
}
//- rjf: join FUNC & line record dump tasks
ProfScope("join FUNC & line record dump tasks")
{
for(U64 idx = 0; idx < params->procedures.chunk_count; idx += 1)
{
String8List *out = ts_join_struct(dump_proc_chunk_tickets[idx], max_U64, String8List);
str8_list_concat_in_place(&dump, out);
}
}
}
//- rjf: bake
String8 baked = {0};
ProfScope("bake")
{
baked = str8_list_join(arena, &dump, 0);
}
//- rjf: write
ProfScope("write")
{
OS_Handle output_file = os_file_open(OS_AccessFlag_Read|OS_AccessFlag_Write, user2convert->output_name);
os_file_write(output_file, r1u64(0, baked.size), baked.str);
os_file_close(output_file);
}
}
File diff suppressed because it is too large Load Diff
+132 -132
View File
@@ -1,132 +1,132 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
////////////////////////////////
//~ rjf: Build Options
#define BUILD_VERSION_MAJOR 0
#define BUILD_VERSION_MINOR 9
#define BUILD_VERSION_PATCH 11
#define BUILD_RELEASE_PHASE_STRING_LITERAL "ALPHA"
#define BUILD_TITLE "rdi_from_pdb"
#define BUILD_CONSOLE_INTERFACE 1
////////////////////////////////
//~ rjf: Includes
//- rjf: [lib]
#include "lib_rdi_format/rdi_format.h"
#include "lib_rdi_format/rdi_format.c"
#include "third_party/rad_lzb_simple/rad_lzb_simple.h"
#include "third_party/rad_lzb_simple/rad_lzb_simple.c"
//- rjf: [h]
#include "base/base_inc.h"
#include "os/os_inc.h"
#include "task_system/task_system.h"
#include "rdi_make/rdi_make_local.h"
#include "coff/coff.h"
#include "codeview/codeview.h"
#include "codeview/codeview_stringize.h"
#include "msf/msf.h"
#include "pdb/pdb.h"
#include "pdb/pdb_stringize.h"
#include "rdi_from_pdb.h"
//- rjf: [c]
#include "base/base_inc.c"
#include "os/os_inc.c"
#include "task_system/task_system.c"
#include "rdi_make/rdi_make_local.c"
#include "coff/coff.c"
#include "codeview/codeview.c"
#include "codeview/codeview_stringize.c"
#include "msf/msf.c"
#include "pdb/pdb.c"
#include "pdb/pdb_stringize.c"
#include "rdi_from_pdb.c"
////////////////////////////////
//~ rjf: Entry Point
internal void
entry_point(CmdLine *cmdline)
{
//- rjf: initialize state, unpack command line
Arena *arena = arena_alloc();
B32 do_help = (cmd_line_has_flag(cmdline, str8_lit("help")) ||
cmd_line_has_flag(cmdline, str8_lit("h")) ||
cmd_line_has_flag(cmdline, str8_lit("?")));
P2R_User2Convert *user2convert = p2r_user2convert_from_cmdln(arena, cmdline);
//- rjf: display help
if(do_help || user2convert->errors.node_count != 0)
{
fprintf(stderr, "--- rdi_from_pdb --------------------------------------------------------------\n\n");
fprintf(stderr, "This utility converts debug information from PDBs into the RAD Debug Info\n");
fprintf(stderr, "format. The following arguments are accepted:\n\n");
fprintf(stderr, "--exe:<path> [optional] Specifies the path of the executable file for which the\n");
fprintf(stderr, " debug info was generated.\n");
fprintf(stderr, "--pdb:<path> Specifies the path of the PDB debug info file to\n");
fprintf(stderr, " convert.\n");
fprintf(stderr, "--out:<path> Specifies the path at which the output Breakpad debug\n");
fprintf(stderr, " info will be written.\n\n");
if(!do_help)
{
for(String8Node *n = user2convert->errors.first; n != 0; n = n->next)
{
fprintf(stderr, "error(input): %.*s\n", str8_varg(n->string));
}
}
os_exit_process(0);
}
//- rjf: convert
P2R_Convert2Bake *convert2bake = 0;
ProfScope("convert")
{
convert2bake = p2r_convert(arena, user2convert);
}
//- rjf: bake
P2R_Bake2Serialize *bake2srlz = 0;
ProfScope("bake")
{
bake2srlz = p2r_bake(arena, convert2bake);
}
//- rjf: serialize
P2R_Serialize2File *srlz2file = 0;
ProfScope("serialize")
{
srlz2file = push_array(arena, P2R_Serialize2File, 1);
srlz2file->bundle = rdim_serialized_section_bundle_from_bake_results(&bake2srlz->bake_results);
}
//- rjf: compress
P2R_Serialize2File *srlz2file_compressed = srlz2file;
if(cmd_line_has_flag(cmdline, str8_lit("compress"))) ProfScope("compress")
{
srlz2file_compressed = push_array(arena, P2R_Serialize2File, 1);
srlz2file_compressed = p2r_compress(arena, srlz2file);
}
//- rjf: serialize
String8List blobs = rdim_file_blobs_from_section_bundle(arena, &srlz2file_compressed->bundle);
//- rjf: write
ProfScope("write")
{
OS_Handle output_file = os_file_open(OS_AccessFlag_Read|OS_AccessFlag_Write, user2convert->output_name);
U64 off = 0;
for(String8Node *n = blobs.first; n != 0; n = n->next)
{
os_file_write(output_file, r1u64(off, off+n->string.size), n->string.str);
off += n->string.size;
}
os_file_close(output_file);
}
}
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
////////////////////////////////
//~ rjf: Build Options
#define BUILD_VERSION_MAJOR 0
#define BUILD_VERSION_MINOR 9
#define BUILD_VERSION_PATCH 11
#define BUILD_RELEASE_PHASE_STRING_LITERAL "ALPHA"
#define BUILD_TITLE "rdi_from_pdb"
#define BUILD_CONSOLE_INTERFACE 1
////////////////////////////////
//~ rjf: Includes
//- rjf: [lib]
#include "lib_rdi_format/rdi_format.h"
#include "lib_rdi_format/rdi_format.c"
#include "third_party/rad_lzb_simple/rad_lzb_simple.h"
#include "third_party/rad_lzb_simple/rad_lzb_simple.c"
//- rjf: [h]
#include "base/base_inc.h"
#include "os/os_inc.h"
#include "task_system/task_system.h"
#include "rdi_make/rdi_make_local.h"
#include "coff/coff.h"
#include "codeview/codeview.h"
#include "codeview/codeview_stringize.h"
#include "msf/msf.h"
#include "pdb/pdb.h"
#include "pdb/pdb_stringize.h"
#include "rdi_from_pdb.h"
//- rjf: [c]
#include "base/base_inc.c"
#include "os/os_inc.c"
#include "task_system/task_system.c"
#include "rdi_make/rdi_make_local.c"
#include "coff/coff.c"
#include "codeview/codeview.c"
#include "codeview/codeview_stringize.c"
#include "msf/msf.c"
#include "pdb/pdb.c"
#include "pdb/pdb_stringize.c"
#include "rdi_from_pdb.c"
////////////////////////////////
//~ rjf: Entry Point
internal void
entry_point(CmdLine *cmdline)
{
//- rjf: initialize state, unpack command line
Arena *arena = arena_alloc();
B32 do_help = (cmd_line_has_flag(cmdline, str8_lit("help")) ||
cmd_line_has_flag(cmdline, str8_lit("h")) ||
cmd_line_has_flag(cmdline, str8_lit("?")));
P2R_User2Convert *user2convert = p2r_user2convert_from_cmdln(arena, cmdline);
//- rjf: display help
if(do_help || user2convert->errors.node_count != 0)
{
fprintf(stderr, "--- rdi_from_pdb --------------------------------------------------------------\n\n");
fprintf(stderr, "This utility converts debug information from PDBs into the RAD Debug Info\n");
fprintf(stderr, "format. The following arguments are accepted:\n\n");
fprintf(stderr, "--exe:<path> [optional] Specifies the path of the executable file for which the\n");
fprintf(stderr, " debug info was generated.\n");
fprintf(stderr, "--pdb:<path> Specifies the path of the PDB debug info file to\n");
fprintf(stderr, " convert.\n");
fprintf(stderr, "--out:<path> Specifies the path at which the output Breakpad debug\n");
fprintf(stderr, " info will be written.\n\n");
if(!do_help)
{
for(String8Node *n = user2convert->errors.first; n != 0; n = n->next)
{
fprintf(stderr, "error(input): %.*s\n", str8_varg(n->string));
}
}
os_abort(0);
}
//- rjf: convert
P2R_Convert2Bake *convert2bake = 0;
ProfScope("convert")
{
convert2bake = p2r_convert(arena, user2convert);
}
//- rjf: bake
P2R_Bake2Serialize *bake2srlz = 0;
ProfScope("bake")
{
bake2srlz = p2r_bake(arena, convert2bake);
}
//- rjf: serialize
P2R_Serialize2File *srlz2file = 0;
ProfScope("serialize")
{
srlz2file = push_array(arena, P2R_Serialize2File, 1);
srlz2file->bundle = rdim_serialized_section_bundle_from_bake_results(&bake2srlz->bake_results);
}
//- rjf: compress
P2R_Serialize2File *srlz2file_compressed = srlz2file;
if(cmd_line_has_flag(cmdline, str8_lit("compress"))) ProfScope("compress")
{
srlz2file_compressed = push_array(arena, P2R_Serialize2File, 1);
srlz2file_compressed = p2r_compress(arena, srlz2file);
}
//- rjf: serialize
String8List blobs = rdim_file_blobs_from_section_bundle(arena, &srlz2file_compressed->bundle);
//- rjf: write
ProfScope("write")
{
OS_Handle output_file = os_file_open(OS_AccessFlag_Read|OS_AccessFlag_Write, user2convert->output_name);
U64 off = 0;
for(String8Node *n = blobs.first; n != 0; n = n->next)
{
os_file_write(output_file, r1u64(off, off+n->string.size), n->string.str);
off += n->string.size;
}
os_file_close(output_file);
}
}
File diff suppressed because it is too large Load Diff
+227 -227
View File
@@ -1,227 +1,227 @@
////////////////////////////////
//~ rjf: Basic Type Functions
internal TS_Ticket
ts_ticket_zero(void)
{
TS_Ticket ticket = {0};
return ticket;
}
internal void
ts_ticket_list_push(Arena *arena, TS_TicketList *list, TS_Ticket ticket)
{
TS_TicketNode *n = push_array(arena, TS_TicketNode, 1);
n->v = ticket;
SLLQueuePush(list->first, list->last, n);
list->count += 1;
}
////////////////////////////////
//~ rjf: Top-Level Layer Initialization
internal void
ts_init(void)
{
Arena *arena = arena_alloc();
ts_shared = push_array(arena, TS_Shared, 1);
ts_shared->arena = arena;
ts_shared->artifact_slots_count = 1024;
ts_shared->artifact_stripes_count = Min(ts_shared->artifact_slots_count, os_logical_core_count());
ts_shared->artifact_slots = push_array(arena, TS_TaskArtifactSlot, ts_shared->artifact_slots_count);
ts_shared->artifact_stripes = push_array(arena, TS_TaskArtifactStripe, ts_shared->artifact_stripes_count);
for(U64 idx = 0; idx < ts_shared->artifact_stripes_count; idx += 1)
{
ts_shared->artifact_stripes[idx].arena = arena_alloc();
ts_shared->artifact_stripes[idx].cv = os_condition_variable_alloc();
ts_shared->artifact_stripes[idx].rw_mutex = os_rw_mutex_alloc();
}
ts_shared->u2t_ring_size = MB(1);
ts_shared->u2t_ring_base = push_array_no_zero(arena, U8, ts_shared->u2t_ring_size);
ts_shared->u2t_ring_mutex = os_mutex_alloc();
ts_shared->u2t_ring_cv = os_condition_variable_alloc();
ts_shared->task_threads_count = os_logical_core_count()-1;
ts_shared->task_threads = push_array(arena, TS_TaskThread, ts_shared->task_threads_count);
for(U64 idx = 0; idx < ts_shared->task_threads_count; idx += 1)
{
ts_shared->task_threads[idx].arena = arena_alloc();
ts_shared->task_threads[idx].thread = os_launch_thread(ts_task_thread__entry_point, (void *)idx, 0);
}
}
////////////////////////////////
//~ rjf: Top-Level Accessors
internal U64
ts_thread_count(void)
{
return ts_shared->task_threads_count;
}
////////////////////////////////
//~ rjf: High-Level Task Kickoff / Joining
internal TS_Ticket
ts_kickoff(TS_TaskFunctionType *entry_point, Arena **optional_arena_ptr, void *p)
{
ProfBeginFunction();
// rjf: obtain number & slot/stripe for next artifact
U64 artifact_num = ins_atomic_u64_inc_eval(&ts_shared->artifact_num_gen);
U64 slot_idx = artifact_num%ts_shared->artifact_slots_count;
U64 stripe_idx = slot_idx%ts_shared->artifact_stripes_count;
TS_TaskArtifactSlot *slot = &ts_shared->artifact_slots[slot_idx];
TS_TaskArtifactStripe *stripe = &ts_shared->artifact_stripes[stripe_idx];
// rjf: allocate artifact
TS_TaskArtifact *artifact = 0;
ProfScope("allocate artifact")
{
OS_MutexScopeW(stripe->rw_mutex)
{
artifact = stripe->free_artifact;
if(artifact != 0)
{
SLLStackPop(stripe->free_artifact);
}
else
{
artifact = push_array_no_zero(stripe->arena, TS_TaskArtifact, 1);
}
artifact->num = artifact_num;
artifact->task_is_done = 0;
artifact->result = 0;
}
}
// rjf: form ticket out of artifact info
TS_Ticket ticket = {artifact_num, (U64)artifact};
// rjf: push task info to task ring buffer
ProfScope("push task info to task ring buffer")
{
OS_MutexScope(ts_shared->u2t_ring_mutex) for(;;)
{
U64 unconsumed_size = ts_shared->u2t_ring_write_pos - ts_shared->u2t_ring_read_pos;
U64 available_size = ts_shared->u2t_ring_size-unconsumed_size;
if(available_size >= sizeof(entry_point) + sizeof(p) + sizeof(ticket))
{
Arena *task_arena = 0;
if(optional_arena_ptr != 0)
{
task_arena = *optional_arena_ptr;
}
ts_shared->u2t_ring_write_pos += ring_write_struct(ts_shared->u2t_ring_base, ts_shared->u2t_ring_size, ts_shared->u2t_ring_write_pos, &entry_point);
ts_shared->u2t_ring_write_pos += ring_write_struct(ts_shared->u2t_ring_base, ts_shared->u2t_ring_size, ts_shared->u2t_ring_write_pos, &task_arena);
ts_shared->u2t_ring_write_pos += ring_write_struct(ts_shared->u2t_ring_base, ts_shared->u2t_ring_size, ts_shared->u2t_ring_write_pos, &p);
ts_shared->u2t_ring_write_pos += ring_write_struct(ts_shared->u2t_ring_base, ts_shared->u2t_ring_size, ts_shared->u2t_ring_write_pos, &ticket);
if(optional_arena_ptr != 0)
{
*optional_arena_ptr = 0;
}
break;
}
os_condition_variable_wait(ts_shared->u2t_ring_cv, ts_shared->u2t_ring_mutex, max_U64);
}
os_condition_variable_signal(ts_shared->u2t_ring_cv);
}
ProfEnd();
return ticket;
}
internal void *
ts_join(TS_Ticket ticket, U64 endt_us)
{
void *result = 0;
U64 artifact_num = ticket.u64[0];
U64 slot_idx = artifact_num%ts_shared->artifact_slots_count;
U64 stripe_idx = slot_idx%ts_shared->artifact_stripes_count;
TS_TaskArtifactSlot *slot = &ts_shared->artifact_slots[slot_idx];
TS_TaskArtifactStripe *stripe = &ts_shared->artifact_stripes[stripe_idx];
TS_TaskArtifact *artifact = (TS_TaskArtifact *)ticket.u64[1];
if(artifact != 0)
{
OS_MutexScopeR(stripe->rw_mutex) for(;;)
{
B64 task_is_done = artifact->task_is_done;
if(task_is_done)
{
OS_MutexScopeRWPromote(stripe->rw_mutex)
{
result = artifact->result;
SLLStackPush(stripe->free_artifact, artifact);
}
break;
}
if(os_now_microseconds() >= endt_us)
{
break;
}
os_condition_variable_wait_rw_r(stripe->cv, stripe->rw_mutex, endt_us);
}
}
return result;
}
////////////////////////////////
//~ rjf: Task Threads
internal void
ts_u2t_dequeue_task(TS_TaskFunctionType **entry_point_out, Arena **arena_out, void **p_out, TS_Ticket *ticket_out)
{
OS_MutexScope(ts_shared->u2t_ring_mutex) for(;;)
{
U64 unconsumed_size = ts_shared->u2t_ring_write_pos - ts_shared->u2t_ring_read_pos;
if(unconsumed_size >= sizeof(*entry_point_out) + sizeof(*p_out) + sizeof(*ticket_out))
{
ts_shared->u2t_ring_read_pos += ring_read_struct(ts_shared->u2t_ring_base, ts_shared->u2t_ring_size, ts_shared->u2t_ring_read_pos, entry_point_out);
ts_shared->u2t_ring_read_pos += ring_read_struct(ts_shared->u2t_ring_base, ts_shared->u2t_ring_size, ts_shared->u2t_ring_read_pos, arena_out);
ts_shared->u2t_ring_read_pos += ring_read_struct(ts_shared->u2t_ring_base, ts_shared->u2t_ring_size, ts_shared->u2t_ring_read_pos, p_out);
ts_shared->u2t_ring_read_pos += ring_read_struct(ts_shared->u2t_ring_base, ts_shared->u2t_ring_size, ts_shared->u2t_ring_read_pos, ticket_out);
break;
}
os_condition_variable_wait(ts_shared->u2t_ring_cv, ts_shared->u2t_ring_mutex, max_U64);
}
os_condition_variable_broadcast(ts_shared->u2t_ring_cv);
}
internal void
ts_task_thread__entry_point(void *p)
{
U64 thread_idx = (U64)p;
ThreadNameF("[ts] task thread #%I64u", thread_idx);
TS_TaskThread *thread = &ts_shared->task_threads[thread_idx];
for(;;)
{
//- rjf: grab next task
TS_TaskFunctionType *task_function = 0;
Arena *task_arena = 0;
void *task_params = 0;
TS_Ticket task_ticket = {0};
ts_u2t_dequeue_task(&task_function, &task_arena, &task_params, &task_ticket);
//- rjf: use task thread's arena if none specified
if(task_arena == 0)
{
task_arena = thread->arena;
}
//- rjf: run task
void *task_result = task_function(task_arena, thread_idx, task_params);
//- rjf: store into artifact
U64 artifact_num = task_ticket.u64[0];
U64 slot_idx = artifact_num%ts_shared->artifact_slots_count;
U64 stripe_idx = slot_idx%ts_shared->artifact_stripes_count;
TS_TaskArtifactSlot *slot = &ts_shared->artifact_slots[slot_idx];
TS_TaskArtifactStripe *stripe = &ts_shared->artifact_stripes[stripe_idx];
TS_TaskArtifact *artifact = (TS_TaskArtifact *)task_ticket.u64[1];
OS_MutexScopeW(stripe->rw_mutex)
{
artifact->task_is_done = 1;
artifact->result = task_result;
}
os_condition_variable_broadcast(stripe->cv);
}
}
////////////////////////////////
//~ rjf: Basic Type Functions
internal TS_Ticket
ts_ticket_zero(void)
{
TS_Ticket ticket = {0};
return ticket;
}
internal void
ts_ticket_list_push(Arena *arena, TS_TicketList *list, TS_Ticket ticket)
{
TS_TicketNode *n = push_array(arena, TS_TicketNode, 1);
n->v = ticket;
SLLQueuePush(list->first, list->last, n);
list->count += 1;
}
////////////////////////////////
//~ rjf: Top-Level Layer Initialization
internal void
ts_init(void)
{
Arena *arena = arena_alloc();
ts_shared = push_array(arena, TS_Shared, 1);
ts_shared->arena = arena;
ts_shared->artifact_slots_count = 1024;
ts_shared->artifact_stripes_count = Min(ts_shared->artifact_slots_count, os_get_system_info()->logical_processor_count);
ts_shared->artifact_slots = push_array(arena, TS_TaskArtifactSlot, ts_shared->artifact_slots_count);
ts_shared->artifact_stripes = push_array(arena, TS_TaskArtifactStripe, ts_shared->artifact_stripes_count);
for(U64 idx = 0; idx < ts_shared->artifact_stripes_count; idx += 1)
{
ts_shared->artifact_stripes[idx].arena = arena_alloc();
ts_shared->artifact_stripes[idx].cv = os_condition_variable_alloc();
ts_shared->artifact_stripes[idx].rw_mutex = os_rw_mutex_alloc();
}
ts_shared->u2t_ring_size = MB(1);
ts_shared->u2t_ring_base = push_array_no_zero(arena, U8, ts_shared->u2t_ring_size);
ts_shared->u2t_ring_mutex = os_mutex_alloc();
ts_shared->u2t_ring_cv = os_condition_variable_alloc();
ts_shared->task_threads_count = os_get_system_info()->logical_processor_count-1;
ts_shared->task_threads = push_array(arena, TS_TaskThread, ts_shared->task_threads_count);
for(U64 idx = 0; idx < ts_shared->task_threads_count; idx += 1)
{
ts_shared->task_threads[idx].arena = arena_alloc();
ts_shared->task_threads[idx].thread = os_thread_launch(ts_task_thread__entry_point, (void *)idx, 0);
}
}
////////////////////////////////
//~ rjf: Top-Level Accessors
internal U64
ts_thread_count(void)
{
return ts_shared->task_threads_count;
}
////////////////////////////////
//~ rjf: High-Level Task Kickoff / Joining
internal TS_Ticket
ts_kickoff(TS_TaskFunctionType *entry_point, Arena **optional_arena_ptr, void *p)
{
ProfBeginFunction();
// rjf: obtain number & slot/stripe for next artifact
U64 artifact_num = ins_atomic_u64_inc_eval(&ts_shared->artifact_num_gen);
U64 slot_idx = artifact_num%ts_shared->artifact_slots_count;
U64 stripe_idx = slot_idx%ts_shared->artifact_stripes_count;
TS_TaskArtifactSlot *slot = &ts_shared->artifact_slots[slot_idx];
TS_TaskArtifactStripe *stripe = &ts_shared->artifact_stripes[stripe_idx];
// rjf: allocate artifact
TS_TaskArtifact *artifact = 0;
ProfScope("allocate artifact")
{
OS_MutexScopeW(stripe->rw_mutex)
{
artifact = stripe->free_artifact;
if(artifact != 0)
{
SLLStackPop(stripe->free_artifact);
}
else
{
artifact = push_array_no_zero(stripe->arena, TS_TaskArtifact, 1);
}
artifact->num = artifact_num;
artifact->task_is_done = 0;
artifact->result = 0;
}
}
// rjf: form ticket out of artifact info
TS_Ticket ticket = {artifact_num, (U64)artifact};
// rjf: push task info to task ring buffer
ProfScope("push task info to task ring buffer")
{
OS_MutexScope(ts_shared->u2t_ring_mutex) for(;;)
{
U64 unconsumed_size = ts_shared->u2t_ring_write_pos - ts_shared->u2t_ring_read_pos;
U64 available_size = ts_shared->u2t_ring_size-unconsumed_size;
if(available_size >= sizeof(entry_point) + sizeof(p) + sizeof(ticket))
{
Arena *task_arena = 0;
if(optional_arena_ptr != 0)
{
task_arena = *optional_arena_ptr;
}
ts_shared->u2t_ring_write_pos += ring_write_struct(ts_shared->u2t_ring_base, ts_shared->u2t_ring_size, ts_shared->u2t_ring_write_pos, &entry_point);
ts_shared->u2t_ring_write_pos += ring_write_struct(ts_shared->u2t_ring_base, ts_shared->u2t_ring_size, ts_shared->u2t_ring_write_pos, &task_arena);
ts_shared->u2t_ring_write_pos += ring_write_struct(ts_shared->u2t_ring_base, ts_shared->u2t_ring_size, ts_shared->u2t_ring_write_pos, &p);
ts_shared->u2t_ring_write_pos += ring_write_struct(ts_shared->u2t_ring_base, ts_shared->u2t_ring_size, ts_shared->u2t_ring_write_pos, &ticket);
if(optional_arena_ptr != 0)
{
*optional_arena_ptr = 0;
}
break;
}
os_condition_variable_wait(ts_shared->u2t_ring_cv, ts_shared->u2t_ring_mutex, max_U64);
}
os_condition_variable_signal(ts_shared->u2t_ring_cv);
}
ProfEnd();
return ticket;
}
internal void *
ts_join(TS_Ticket ticket, U64 endt_us)
{
void *result = 0;
U64 artifact_num = ticket.u64[0];
U64 slot_idx = artifact_num%ts_shared->artifact_slots_count;
U64 stripe_idx = slot_idx%ts_shared->artifact_stripes_count;
TS_TaskArtifactSlot *slot = &ts_shared->artifact_slots[slot_idx];
TS_TaskArtifactStripe *stripe = &ts_shared->artifact_stripes[stripe_idx];
TS_TaskArtifact *artifact = (TS_TaskArtifact *)ticket.u64[1];
if(artifact != 0)
{
OS_MutexScopeR(stripe->rw_mutex) for(;;)
{
B64 task_is_done = artifact->task_is_done;
if(task_is_done)
{
OS_MutexScopeRWPromote(stripe->rw_mutex)
{
result = artifact->result;
SLLStackPush(stripe->free_artifact, artifact);
}
break;
}
if(os_now_microseconds() >= endt_us)
{
break;
}
os_condition_variable_wait_rw_r(stripe->cv, stripe->rw_mutex, endt_us);
}
}
return result;
}
////////////////////////////////
//~ rjf: Task Threads
internal void
ts_u2t_dequeue_task(TS_TaskFunctionType **entry_point_out, Arena **arena_out, void **p_out, TS_Ticket *ticket_out)
{
OS_MutexScope(ts_shared->u2t_ring_mutex) for(;;)
{
U64 unconsumed_size = ts_shared->u2t_ring_write_pos - ts_shared->u2t_ring_read_pos;
if(unconsumed_size >= sizeof(*entry_point_out) + sizeof(*p_out) + sizeof(*ticket_out))
{
ts_shared->u2t_ring_read_pos += ring_read_struct(ts_shared->u2t_ring_base, ts_shared->u2t_ring_size, ts_shared->u2t_ring_read_pos, entry_point_out);
ts_shared->u2t_ring_read_pos += ring_read_struct(ts_shared->u2t_ring_base, ts_shared->u2t_ring_size, ts_shared->u2t_ring_read_pos, arena_out);
ts_shared->u2t_ring_read_pos += ring_read_struct(ts_shared->u2t_ring_base, ts_shared->u2t_ring_size, ts_shared->u2t_ring_read_pos, p_out);
ts_shared->u2t_ring_read_pos += ring_read_struct(ts_shared->u2t_ring_base, ts_shared->u2t_ring_size, ts_shared->u2t_ring_read_pos, ticket_out);
break;
}
os_condition_variable_wait(ts_shared->u2t_ring_cv, ts_shared->u2t_ring_mutex, max_U64);
}
os_condition_variable_broadcast(ts_shared->u2t_ring_cv);
}
internal void
ts_task_thread__entry_point(void *p)
{
U64 thread_idx = (U64)p;
ThreadNameF("[ts] task thread #%I64u", thread_idx);
TS_TaskThread *thread = &ts_shared->task_threads[thread_idx];
for(;;)
{
//- rjf: grab next task
TS_TaskFunctionType *task_function = 0;
Arena *task_arena = 0;
void *task_params = 0;
TS_Ticket task_ticket = {0};
ts_u2t_dequeue_task(&task_function, &task_arena, &task_params, &task_ticket);
//- rjf: use task thread's arena if none specified
if(task_arena == 0)
{
task_arena = thread->arena;
}
//- rjf: run task
void *task_result = task_function(task_arena, thread_idx, task_params);
//- rjf: store into artifact
U64 artifact_num = task_ticket.u64[0];
U64 slot_idx = artifact_num%ts_shared->artifact_slots_count;
U64 stripe_idx = slot_idx%ts_shared->artifact_stripes_count;
TS_TaskArtifactSlot *slot = &ts_shared->artifact_slots[slot_idx];
TS_TaskArtifactStripe *stripe = &ts_shared->artifact_stripes[stripe_idx];
TS_TaskArtifact *artifact = (TS_TaskArtifact *)task_ticket.u64[1];
OS_MutexScopeW(stripe->rw_mutex)
{
artifact->task_is_done = 1;
artifact->result = task_result;
}
os_condition_variable_broadcast(stripe->cv);
}
}
+2386 -2386
View File
File diff suppressed because it is too large Load Diff
+405 -405
View File
@@ -1,405 +1,405 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
////////////////////////////////
//~ rjf: Basic Helpers
internal TEX_Topology
tex_topology_make(Vec2S32 dim, R_Tex2DFormat fmt)
{
TEX_Topology top = {0};
top.dim.x = (S16)Clamp(0, dim.x, max_S32);
top.dim.y = (S16)Clamp(0, dim.y, max_S32);
top.fmt = fmt;
return top;
}
////////////////////////////////
//~ rjf: Main Layer Initialization
internal void
tex_init(void)
{
Arena *arena = arena_alloc();
tex_shared = push_array(arena, TEX_Shared, 1);
tex_shared->arena = arena;
tex_shared->slots_count = 1024;
tex_shared->stripes_count = Min(tex_shared->slots_count, os_logical_core_count());
tex_shared->slots = push_array(arena, TEX_Slot, tex_shared->slots_count);
tex_shared->stripes = push_array(arena, TEX_Stripe, tex_shared->stripes_count);
tex_shared->stripes_free_nodes = push_array(arena, TEX_Node *, tex_shared->stripes_count);
for(U64 idx = 0; idx < tex_shared->stripes_count; idx += 1)
{
tex_shared->stripes[idx].arena = arena_alloc();
tex_shared->stripes[idx].rw_mutex = os_rw_mutex_alloc();
tex_shared->stripes[idx].cv = os_condition_variable_alloc();
}
tex_shared->u2x_ring_size = KB(64);
tex_shared->u2x_ring_base = push_array_no_zero(arena, U8, tex_shared->u2x_ring_size);
tex_shared->u2x_ring_cv = os_condition_variable_alloc();
tex_shared->u2x_ring_mutex = os_mutex_alloc();
tex_shared->xfer_thread_count = Clamp(1, os_logical_core_count()-1, 4);
tex_shared->xfer_threads = push_array(arena, OS_Handle, tex_shared->xfer_thread_count);
for(U64 idx = 0; idx < tex_shared->xfer_thread_count; idx += 1)
{
tex_shared->xfer_threads[idx] = os_launch_thread(tex_xfer_thread__entry_point, (void *)idx, 0);
}
tex_shared->evictor_thread = os_launch_thread(tex_evictor_thread__entry_point, 0, 0);
}
////////////////////////////////
//~ rjf: Thread Context Initialization
internal void
tex_tctx_ensure_inited(void)
{
if(tex_tctx == 0)
{
Arena *arena = arena_alloc();
tex_tctx = push_array(arena, TEX_TCTX, 1);
tex_tctx->arena = arena;
}
}
////////////////////////////////
//~ rjf: User Clock
internal void
tex_user_clock_tick(void)
{
ins_atomic_u64_inc_eval(&tex_shared->user_clock_idx);
}
internal U64
tex_user_clock_idx(void)
{
return ins_atomic_u64_eval(&tex_shared->user_clock_idx);
}
////////////////////////////////
//~ rjf: Scoped Access
internal TEX_Scope *
tex_scope_open(void)
{
tex_tctx_ensure_inited();
TEX_Scope *scope = tex_tctx->free_scope;
if(scope)
{
SLLStackPop(tex_tctx->free_scope);
}
else
{
scope = push_array_no_zero(tex_tctx->arena, TEX_Scope, 1);
}
MemoryZeroStruct(scope);
return scope;
}
internal void
tex_scope_close(TEX_Scope *scope)
{
for(TEX_Touch *touch = scope->top_touch, *next = 0; touch != 0; touch = next)
{
U128 hash = touch->hash;
next = touch->next;
U64 slot_idx = hash.u64[1]%tex_shared->slots_count;
U64 stripe_idx = slot_idx%tex_shared->stripes_count;
TEX_Slot *slot = &tex_shared->slots[slot_idx];
TEX_Stripe *stripe = &tex_shared->stripes[stripe_idx];
OS_MutexScopeR(stripe->rw_mutex)
{
for(TEX_Node *n = slot->first; n != 0; n = n->next)
{
if(u128_match(hash, n->hash) && MemoryMatchStruct(&touch->topology, &n->topology))
{
ins_atomic_u64_dec_eval(&n->scope_ref_count);
break;
}
}
}
SLLStackPush(tex_tctx->free_touch, touch);
}
SLLStackPush(tex_tctx->free_scope, scope);
}
internal void
tex_scope_touch_node__stripe_r_guarded(TEX_Scope *scope, TEX_Node *node)
{
TEX_Touch *touch = tex_tctx->free_touch;
ins_atomic_u64_inc_eval(&node->scope_ref_count);
ins_atomic_u64_eval_assign(&node->last_time_touched_us, os_now_microseconds());
ins_atomic_u64_eval_assign(&node->last_user_clock_idx_touched, tex_user_clock_idx());
if(touch != 0)
{
SLLStackPop(tex_tctx->free_touch);
}
else
{
touch = push_array_no_zero(tex_tctx->arena, TEX_Touch, 1);
}
MemoryZeroStruct(touch);
touch->hash = node->hash;
touch->topology = node->topology;
SLLStackPush(scope->top_touch, touch);
}
////////////////////////////////
//~ rjf: Cache Lookups
internal R_Handle
tex_texture_from_hash_topology(TEX_Scope *scope, U128 hash, TEX_Topology topology)
{
R_Handle handle = {0};
{
U64 slot_idx = hash.u64[1]%tex_shared->slots_count;
U64 stripe_idx = slot_idx%tex_shared->stripes_count;
TEX_Slot *slot = &tex_shared->slots[slot_idx];
TEX_Stripe *stripe = &tex_shared->stripes[stripe_idx];
B32 found = 0;
B32 stale = 0;
OS_MutexScopeR(stripe->rw_mutex)
{
for(TEX_Node *n = slot->first; n != 0; n = n->next)
{
if(u128_match(hash, n->hash) && MemoryMatchStruct(&topology, &n->topology))
{
handle = n->texture;
found = !r_handle_match(r_handle_zero(), handle);
tex_scope_touch_node__stripe_r_guarded(scope, n);
break;
}
}
}
B32 node_is_new = 0;
if(!found)
{
OS_MutexScopeW(stripe->rw_mutex)
{
TEX_Node *node = 0;
for(TEX_Node *n = slot->first; n != 0; n = n->next)
{
if(u128_match(hash, n->hash) && MemoryMatchStruct(&topology, &n->topology))
{
node = n;
break;
}
}
if(node == 0)
{
node = tex_shared->stripes_free_nodes[stripe_idx];
if(node)
{
SLLStackPop(tex_shared->stripes_free_nodes[stripe_idx]);
}
else
{
node = push_array_no_zero(stripe->arena, TEX_Node, 1);
}
MemoryZeroStruct(node);
DLLPushBack(slot->first, slot->last, node);
node->hash = hash;
MemoryCopyStruct(&node->topology, &topology);
node_is_new = 1;
}
}
}
if(node_is_new)
{
tex_u2x_enqueue_req(hash, topology, max_U64);
}
}
return handle;
}
internal R_Handle
tex_texture_from_key_topology(TEX_Scope *scope, U128 key, TEX_Topology topology, U128 *hash_out)
{
R_Handle handle = {0};
for(U64 rewind_idx = 0; rewind_idx < 2; rewind_idx += 1)
{
U128 hash = hs_hash_from_key(key, rewind_idx);
handle = tex_texture_from_hash_topology(scope, hash, topology);
if(!r_handle_match(handle, r_handle_zero()))
{
if(hash_out)
{
*hash_out = hash;
}
break;
}
}
return handle;
}
////////////////////////////////
//~ rjf: Transfer Threads
internal B32
tex_u2x_enqueue_req(U128 hash, TEX_Topology top, U64 endt_us)
{
B32 good = 0;
OS_MutexScope(tex_shared->u2x_ring_mutex) for(;;)
{
U64 unconsumed_size = tex_shared->u2x_ring_write_pos-tex_shared->u2x_ring_read_pos;
U64 available_size = tex_shared->u2x_ring_size-unconsumed_size;
if(available_size >= sizeof(hash)+sizeof(top))
{
good = 1;
tex_shared->u2x_ring_write_pos += ring_write_struct(tex_shared->u2x_ring_base, tex_shared->u2x_ring_size, tex_shared->u2x_ring_write_pos, &hash);
tex_shared->u2x_ring_write_pos += ring_write_struct(tex_shared->u2x_ring_base, tex_shared->u2x_ring_size, tex_shared->u2x_ring_write_pos, &top);
break;
}
if(os_now_microseconds() >= endt_us)
{
break;
}
os_condition_variable_wait(tex_shared->u2x_ring_cv, tex_shared->u2x_ring_mutex, endt_us);
}
if(good)
{
os_condition_variable_broadcast(tex_shared->u2x_ring_cv);
}
return good;
}
internal void
tex_u2x_dequeue_req(U128 *hash_out, TEX_Topology *top_out)
{
OS_MutexScope(tex_shared->u2x_ring_mutex) for(;;)
{
U64 unconsumed_size = tex_shared->u2x_ring_write_pos-tex_shared->u2x_ring_read_pos;
if(unconsumed_size >= sizeof(*hash_out)+sizeof(*top_out))
{
tex_shared->u2x_ring_read_pos += ring_read_struct(tex_shared->u2x_ring_base, tex_shared->u2x_ring_size, tex_shared->u2x_ring_read_pos, hash_out);
tex_shared->u2x_ring_read_pos += ring_read_struct(tex_shared->u2x_ring_base, tex_shared->u2x_ring_size, tex_shared->u2x_ring_read_pos, top_out);
break;
}
os_condition_variable_wait(tex_shared->u2x_ring_cv, tex_shared->u2x_ring_mutex, max_U64);
}
os_condition_variable_broadcast(tex_shared->u2x_ring_cv);
}
internal void
tex_xfer_thread__entry_point(void *p)
{
for(;;)
{
HS_Scope *scope = hs_scope_open();
//- rjf: decode
U128 hash = {0};
TEX_Topology top = {0};
tex_u2x_dequeue_req(&hash, &top);
//- rjf: unpack hash
U64 slot_idx = hash.u64[1]%tex_shared->slots_count;
U64 stripe_idx = slot_idx%tex_shared->stripes_count;
TEX_Slot *slot = &tex_shared->slots[slot_idx];
TEX_Stripe *stripe = &tex_shared->stripes[stripe_idx];
//- rjf: take task
B32 got_task = 0;
OS_MutexScopeR(stripe->rw_mutex)
{
for(TEX_Node *n = slot->first; n != 0; n = n->next)
{
if(u128_match(n->hash, hash) && MemoryMatchStruct(&top, &n->topology))
{
got_task = !ins_atomic_u32_eval_cond_assign(&n->is_working, 1, 0);
break;
}
}
}
//- rjf: hash -> data
String8 data = {0};
if(got_task)
{
data = hs_data_from_hash(scope, hash);
}
//- rjf: data * topology -> texture
R_Handle texture = {0};
if(got_task && top.dim.x > 0 && top.dim.y > 0 && data.size >= (U64)top.dim.x*(U64)top.dim.y*(U64)r_tex2d_format_bytes_per_pixel_table[top.fmt])
{
texture = r_tex2d_alloc(R_ResourceKind_Static, v2s32(top.dim.x, top.dim.y), top.fmt, data.str);
}
//- rjf: commit results to cache
if(got_task) OS_MutexScopeW(stripe->rw_mutex)
{
for(TEX_Node *n = slot->first; n != 0; n = n->next)
{
if(u128_match(n->hash, hash) && MemoryMatchStruct(&top, &n->topology))
{
n->texture = texture;
ins_atomic_u32_eval_assign(&n->is_working, 0);
ins_atomic_u64_inc_eval(&n->load_count);
break;
}
}
}
hs_scope_close(scope);
}
}
////////////////////////////////
//~ rjf: Evictor Threads
internal void
tex_evictor_thread__entry_point(void *p)
{
for(;;)
{
U64 check_time_us = os_now_microseconds();
U64 check_time_user_clocks = tex_user_clock_idx();
U64 evict_threshold_us = 10*1000000;
U64 evict_threshold_user_clocks = 10;
for(U64 slot_idx = 0; slot_idx < tex_shared->slots_count; slot_idx += 1)
{
U64 stripe_idx = slot_idx%tex_shared->stripes_count;
TEX_Slot *slot = &tex_shared->slots[slot_idx];
TEX_Stripe *stripe = &tex_shared->stripes[stripe_idx];
B32 slot_has_work = 0;
OS_MutexScopeR(stripe->rw_mutex)
{
for(TEX_Node *n = slot->first; n != 0; n = n->next)
{
if(n->scope_ref_count == 0 &&
n->last_time_touched_us+evict_threshold_us <= check_time_us &&
n->last_user_clock_idx_touched+evict_threshold_user_clocks <= check_time_user_clocks &&
n->load_count != 0 &&
n->is_working == 0)
{
slot_has_work = 1;
break;
}
}
}
if(slot_has_work) OS_MutexScopeW(stripe->rw_mutex)
{
for(TEX_Node *n = slot->first, *next = 0; n != 0; n = next)
{
next = n->next;
if(n->scope_ref_count == 0 &&
n->last_time_touched_us+evict_threshold_us <= check_time_us &&
n->last_user_clock_idx_touched+evict_threshold_user_clocks <= check_time_user_clocks &&
n->load_count != 0 &&
n->is_working == 0)
{
DLLRemove(slot->first, slot->last, n);
if(!r_handle_match(n->texture, r_handle_zero()))
{
r_tex2d_release(n->texture);
}
SLLStackPush(tex_shared->stripes_free_nodes[stripe_idx], n);
}
}
}
os_sleep_milliseconds(5);
}
os_sleep_milliseconds(1000);
}
}
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
////////////////////////////////
//~ rjf: Basic Helpers
internal TEX_Topology
tex_topology_make(Vec2S32 dim, R_Tex2DFormat fmt)
{
TEX_Topology top = {0};
top.dim.x = (S16)Clamp(0, dim.x, max_S32);
top.dim.y = (S16)Clamp(0, dim.y, max_S32);
top.fmt = fmt;
return top;
}
////////////////////////////////
//~ rjf: Main Layer Initialization
internal void
tex_init(void)
{
Arena *arena = arena_alloc();
tex_shared = push_array(arena, TEX_Shared, 1);
tex_shared->arena = arena;
tex_shared->slots_count = 1024;
tex_shared->stripes_count = Min(tex_shared->slots_count, os_get_system_info()->logical_processor_count);
tex_shared->slots = push_array(arena, TEX_Slot, tex_shared->slots_count);
tex_shared->stripes = push_array(arena, TEX_Stripe, tex_shared->stripes_count);
tex_shared->stripes_free_nodes = push_array(arena, TEX_Node *, tex_shared->stripes_count);
for(U64 idx = 0; idx < tex_shared->stripes_count; idx += 1)
{
tex_shared->stripes[idx].arena = arena_alloc();
tex_shared->stripes[idx].rw_mutex = os_rw_mutex_alloc();
tex_shared->stripes[idx].cv = os_condition_variable_alloc();
}
tex_shared->u2x_ring_size = KB(64);
tex_shared->u2x_ring_base = push_array_no_zero(arena, U8, tex_shared->u2x_ring_size);
tex_shared->u2x_ring_cv = os_condition_variable_alloc();
tex_shared->u2x_ring_mutex = os_mutex_alloc();
tex_shared->xfer_thread_count = Clamp(1, os_get_system_info()->logical_processor_count-1, 4);
tex_shared->xfer_threads = push_array(arena, OS_Handle, tex_shared->xfer_thread_count);
for(U64 idx = 0; idx < tex_shared->xfer_thread_count; idx += 1)
{
tex_shared->xfer_threads[idx] = os_thread_launch(tex_xfer_thread__entry_point, (void *)idx, 0);
}
tex_shared->evictor_thread = os_thread_launch(tex_evictor_thread__entry_point, 0, 0);
}
////////////////////////////////
//~ rjf: Thread Context Initialization
internal void
tex_tctx_ensure_inited(void)
{
if(tex_tctx == 0)
{
Arena *arena = arena_alloc();
tex_tctx = push_array(arena, TEX_TCTX, 1);
tex_tctx->arena = arena;
}
}
////////////////////////////////
//~ rjf: User Clock
internal void
tex_user_clock_tick(void)
{
ins_atomic_u64_inc_eval(&tex_shared->user_clock_idx);
}
internal U64
tex_user_clock_idx(void)
{
return ins_atomic_u64_eval(&tex_shared->user_clock_idx);
}
////////////////////////////////
//~ rjf: Scoped Access
internal TEX_Scope *
tex_scope_open(void)
{
tex_tctx_ensure_inited();
TEX_Scope *scope = tex_tctx->free_scope;
if(scope)
{
SLLStackPop(tex_tctx->free_scope);
}
else
{
scope = push_array_no_zero(tex_tctx->arena, TEX_Scope, 1);
}
MemoryZeroStruct(scope);
return scope;
}
internal void
tex_scope_close(TEX_Scope *scope)
{
for(TEX_Touch *touch = scope->top_touch, *next = 0; touch != 0; touch = next)
{
U128 hash = touch->hash;
next = touch->next;
U64 slot_idx = hash.u64[1]%tex_shared->slots_count;
U64 stripe_idx = slot_idx%tex_shared->stripes_count;
TEX_Slot *slot = &tex_shared->slots[slot_idx];
TEX_Stripe *stripe = &tex_shared->stripes[stripe_idx];
OS_MutexScopeR(stripe->rw_mutex)
{
for(TEX_Node *n = slot->first; n != 0; n = n->next)
{
if(u128_match(hash, n->hash) && MemoryMatchStruct(&touch->topology, &n->topology))
{
ins_atomic_u64_dec_eval(&n->scope_ref_count);
break;
}
}
}
SLLStackPush(tex_tctx->free_touch, touch);
}
SLLStackPush(tex_tctx->free_scope, scope);
}
internal void
tex_scope_touch_node__stripe_r_guarded(TEX_Scope *scope, TEX_Node *node)
{
TEX_Touch *touch = tex_tctx->free_touch;
ins_atomic_u64_inc_eval(&node->scope_ref_count);
ins_atomic_u64_eval_assign(&node->last_time_touched_us, os_now_microseconds());
ins_atomic_u64_eval_assign(&node->last_user_clock_idx_touched, tex_user_clock_idx());
if(touch != 0)
{
SLLStackPop(tex_tctx->free_touch);
}
else
{
touch = push_array_no_zero(tex_tctx->arena, TEX_Touch, 1);
}
MemoryZeroStruct(touch);
touch->hash = node->hash;
touch->topology = node->topology;
SLLStackPush(scope->top_touch, touch);
}
////////////////////////////////
//~ rjf: Cache Lookups
internal R_Handle
tex_texture_from_hash_topology(TEX_Scope *scope, U128 hash, TEX_Topology topology)
{
R_Handle handle = {0};
{
U64 slot_idx = hash.u64[1]%tex_shared->slots_count;
U64 stripe_idx = slot_idx%tex_shared->stripes_count;
TEX_Slot *slot = &tex_shared->slots[slot_idx];
TEX_Stripe *stripe = &tex_shared->stripes[stripe_idx];
B32 found = 0;
B32 stale = 0;
OS_MutexScopeR(stripe->rw_mutex)
{
for(TEX_Node *n = slot->first; n != 0; n = n->next)
{
if(u128_match(hash, n->hash) && MemoryMatchStruct(&topology, &n->topology))
{
handle = n->texture;
found = !r_handle_match(r_handle_zero(), handle);
tex_scope_touch_node__stripe_r_guarded(scope, n);
break;
}
}
}
B32 node_is_new = 0;
if(!found)
{
OS_MutexScopeW(stripe->rw_mutex)
{
TEX_Node *node = 0;
for(TEX_Node *n = slot->first; n != 0; n = n->next)
{
if(u128_match(hash, n->hash) && MemoryMatchStruct(&topology, &n->topology))
{
node = n;
break;
}
}
if(node == 0)
{
node = tex_shared->stripes_free_nodes[stripe_idx];
if(node)
{
SLLStackPop(tex_shared->stripes_free_nodes[stripe_idx]);
}
else
{
node = push_array_no_zero(stripe->arena, TEX_Node, 1);
}
MemoryZeroStruct(node);
DLLPushBack(slot->first, slot->last, node);
node->hash = hash;
MemoryCopyStruct(&node->topology, &topology);
node_is_new = 1;
}
}
}
if(node_is_new)
{
tex_u2x_enqueue_req(hash, topology, max_U64);
}
}
return handle;
}
internal R_Handle
tex_texture_from_key_topology(TEX_Scope *scope, U128 key, TEX_Topology topology, U128 *hash_out)
{
R_Handle handle = {0};
for(U64 rewind_idx = 0; rewind_idx < 2; rewind_idx += 1)
{
U128 hash = hs_hash_from_key(key, rewind_idx);
handle = tex_texture_from_hash_topology(scope, hash, topology);
if(!r_handle_match(handle, r_handle_zero()))
{
if(hash_out)
{
*hash_out = hash;
}
break;
}
}
return handle;
}
////////////////////////////////
//~ rjf: Transfer Threads
internal B32
tex_u2x_enqueue_req(U128 hash, TEX_Topology top, U64 endt_us)
{
B32 good = 0;
OS_MutexScope(tex_shared->u2x_ring_mutex) for(;;)
{
U64 unconsumed_size = tex_shared->u2x_ring_write_pos-tex_shared->u2x_ring_read_pos;
U64 available_size = tex_shared->u2x_ring_size-unconsumed_size;
if(available_size >= sizeof(hash)+sizeof(top))
{
good = 1;
tex_shared->u2x_ring_write_pos += ring_write_struct(tex_shared->u2x_ring_base, tex_shared->u2x_ring_size, tex_shared->u2x_ring_write_pos, &hash);
tex_shared->u2x_ring_write_pos += ring_write_struct(tex_shared->u2x_ring_base, tex_shared->u2x_ring_size, tex_shared->u2x_ring_write_pos, &top);
break;
}
if(os_now_microseconds() >= endt_us)
{
break;
}
os_condition_variable_wait(tex_shared->u2x_ring_cv, tex_shared->u2x_ring_mutex, endt_us);
}
if(good)
{
os_condition_variable_broadcast(tex_shared->u2x_ring_cv);
}
return good;
}
internal void
tex_u2x_dequeue_req(U128 *hash_out, TEX_Topology *top_out)
{
OS_MutexScope(tex_shared->u2x_ring_mutex) for(;;)
{
U64 unconsumed_size = tex_shared->u2x_ring_write_pos-tex_shared->u2x_ring_read_pos;
if(unconsumed_size >= sizeof(*hash_out)+sizeof(*top_out))
{
tex_shared->u2x_ring_read_pos += ring_read_struct(tex_shared->u2x_ring_base, tex_shared->u2x_ring_size, tex_shared->u2x_ring_read_pos, hash_out);
tex_shared->u2x_ring_read_pos += ring_read_struct(tex_shared->u2x_ring_base, tex_shared->u2x_ring_size, tex_shared->u2x_ring_read_pos, top_out);
break;
}
os_condition_variable_wait(tex_shared->u2x_ring_cv, tex_shared->u2x_ring_mutex, max_U64);
}
os_condition_variable_broadcast(tex_shared->u2x_ring_cv);
}
internal void
tex_xfer_thread__entry_point(void *p)
{
for(;;)
{
HS_Scope *scope = hs_scope_open();
//- rjf: decode
U128 hash = {0};
TEX_Topology top = {0};
tex_u2x_dequeue_req(&hash, &top);
//- rjf: unpack hash
U64 slot_idx = hash.u64[1]%tex_shared->slots_count;
U64 stripe_idx = slot_idx%tex_shared->stripes_count;
TEX_Slot *slot = &tex_shared->slots[slot_idx];
TEX_Stripe *stripe = &tex_shared->stripes[stripe_idx];
//- rjf: take task
B32 got_task = 0;
OS_MutexScopeR(stripe->rw_mutex)
{
for(TEX_Node *n = slot->first; n != 0; n = n->next)
{
if(u128_match(n->hash, hash) && MemoryMatchStruct(&top, &n->topology))
{
got_task = !ins_atomic_u32_eval_cond_assign(&n->is_working, 1, 0);
break;
}
}
}
//- rjf: hash -> data
String8 data = {0};
if(got_task)
{
data = hs_data_from_hash(scope, hash);
}
//- rjf: data * topology -> texture
R_Handle texture = {0};
if(got_task && top.dim.x > 0 && top.dim.y > 0 && data.size >= (U64)top.dim.x*(U64)top.dim.y*(U64)r_tex2d_format_bytes_per_pixel_table[top.fmt])
{
texture = r_tex2d_alloc(R_ResourceKind_Static, v2s32(top.dim.x, top.dim.y), top.fmt, data.str);
}
//- rjf: commit results to cache
if(got_task) OS_MutexScopeW(stripe->rw_mutex)
{
for(TEX_Node *n = slot->first; n != 0; n = n->next)
{
if(u128_match(n->hash, hash) && MemoryMatchStruct(&top, &n->topology))
{
n->texture = texture;
ins_atomic_u32_eval_assign(&n->is_working, 0);
ins_atomic_u64_inc_eval(&n->load_count);
break;
}
}
}
hs_scope_close(scope);
}
}
////////////////////////////////
//~ rjf: Evictor Threads
internal void
tex_evictor_thread__entry_point(void *p)
{
for(;;)
{
U64 check_time_us = os_now_microseconds();
U64 check_time_user_clocks = tex_user_clock_idx();
U64 evict_threshold_us = 10*1000000;
U64 evict_threshold_user_clocks = 10;
for(U64 slot_idx = 0; slot_idx < tex_shared->slots_count; slot_idx += 1)
{
U64 stripe_idx = slot_idx%tex_shared->stripes_count;
TEX_Slot *slot = &tex_shared->slots[slot_idx];
TEX_Stripe *stripe = &tex_shared->stripes[stripe_idx];
B32 slot_has_work = 0;
OS_MutexScopeR(stripe->rw_mutex)
{
for(TEX_Node *n = slot->first; n != 0; n = n->next)
{
if(n->scope_ref_count == 0 &&
n->last_time_touched_us+evict_threshold_us <= check_time_us &&
n->last_user_clock_idx_touched+evict_threshold_user_clocks <= check_time_user_clocks &&
n->load_count != 0 &&
n->is_working == 0)
{
slot_has_work = 1;
break;
}
}
}
if(slot_has_work) OS_MutexScopeW(stripe->rw_mutex)
{
for(TEX_Node *n = slot->first, *next = 0; n != 0; n = next)
{
next = n->next;
if(n->scope_ref_count == 0 &&
n->last_time_touched_us+evict_threshold_us <= check_time_us &&
n->last_user_clock_idx_touched+evict_threshold_user_clocks <= check_time_user_clocks &&
n->load_count != 0 &&
n->is_working == 0)
{
DLLRemove(slot->first, slot->last, n);
if(!r_handle_match(n->texture, r_handle_zero()))
{
r_tex2d_release(n->texture);
}
SLLStackPush(tex_shared->stripes_free_nodes[stripe_idx], n);
}
}
}
os_sleep_milliseconds(5);
}
os_sleep_milliseconds(1000);
}
}
+2995 -2995
View File
File diff suppressed because it is too large Load Diff