res refactor progress

This commit is contained in:
jacob 2025-08-25 20:49:14 -05:00
parent db8a9deba9
commit db9d3677d5
32 changed files with 116 additions and 1101 deletions

View File

@ -5,8 +5,8 @@ if not exist build mkdir build
pushd build pushd build
set program_build_cmd=meta.exe %* set program_build_cmd=meta.exe %*
set meta_build_cmd=cl.exe ../src/meta/meta.c /Od /Z7 /nologo /link /DEBUG:FULL /INCREMENTAL:NO set meta_build_cmd=cl.exe ../src/meta/meta.c /Od /Z7 /nologo /diagnostics:column /link /DEBUG:FULL /INCREMENTAL:NO
set meta_rebuild_code=995692758 set meta_rebuild_code=1317212284
::- Meta build ::- Meta build
:meta_build :meta_build

View File

@ -414,7 +414,7 @@ void __asan_unpoison_memory_region(void const volatile *add, size_t);
# define alignof(type) __alignof(type) # define alignof(type) __alignof(type)
#endif #endif
//- sizeof_field //- field sizeof
#define sizeof_field(type, field) sizeof(((type *)0)->field) #define sizeof_field(type, field) sizeof(((type *)0)->field)
//- countof //- countof
@ -635,13 +635,13 @@ Struct(StringList)
//~ Fiber id //~ Fiber id
#if PlatformIsWindows #if PlatformIsWindows
# define FiberId *(i16 *)(void *)(volatile u64)__readgsqword(32) # define FiberId (*(i16 *)(void *)(volatile u64)__readgsqword(32))
#else #else
# error FiberId not implemented # error FiberId not implemented
#endif #endif
#define MaxFibers 1024 #define MaxFibers 1024
StaticAssert(MaxFibers < I16Max); /* Fiber id type should fit max threads */ StaticAssert(MaxFibers < I16Max); /* FiberId type should fit MaxFibers */
//////////////////////////////// ////////////////////////////////
//~ Exit callback types //~ Exit callback types

View File

@ -18,7 +18,6 @@
# include "base_math.h" # include "base_math.h"
# include "base_rand.h" # include "base_rand.h"
# include "base_util.h" # include "base_util.h"
# include "base_incbin.h"
# include "base_entry.h" # include "base_entry.h"
#elif LanguageIsGpu #elif LanguageIsGpu
# include "base_math_gpu.h" # include "base_math_gpu.h"
@ -36,7 +35,6 @@
# include "base_buddy.c" # include "base_buddy.c"
# include "base_math.c" # include "base_math.c"
# include "base_rand.c" # include "base_rand.c"
# include "base_incbin.c"
#endif #endif
//- Win32 impl //- Win32 impl

View File

@ -1,78 +0,0 @@
#if PlatformIsWindows
////////////////////////////////
//~ Incbin
/* Find first resource with `type` and return the data in `udata`. */
BOOL IncbinEnumerateResourceNamesFunc(HMODULE module, LPCWSTR type, LPWSTR wstr_entry_name, LONG_PTR udata)
{
TempArena scratch = BeginScratchNoConflict();
IncbinRcSearchParams *params = (IncbinRcSearchParams *)udata;
String entry_name_lower = LowerString(scratch.arena, StringFromWstrNoLimit(scratch.arena, (LPWSTR)wstr_entry_name));
params->found = 0;
params->data = STRING(0, 0);
if (EqString(entry_name_lower, params->name_lower))
{
HRSRC hres = FindResourceW(module, wstr_entry_name, type);
if (hres)
{
HGLOBAL hg = LoadResource(module, hres);
if (hg)
{
params->found = 1;
params->data.len = SizeofResource(module, hres);
params->data.text = LockResource(hg);
}
}
}
EndScratch(scratch);
return !params->found;
}
String StringFromIncbinRcResource(IncbinRcResource *inc)
{
IncbinStatus state = Atomic32Fetch(&inc->state);
if (state != IncbinStatus_Searched)
{
TempArena scratch = BeginScratchNoConflict();
if (state == IncbinStatus_Unsearched)
{
IncbinStatus v = Atomic32FetchTestSet(&inc->state, state, IncbinStatus_Searching);
if (v == state)
{
/* Search RC file for the resource name */
String name_lower = LowerString(scratch.arena, inc->rc_name);
IncbinRcSearchParams params = { .name_lower = name_lower };
EnumResourceNamesW(0, RT_RCDATA, &IncbinEnumerateResourceNamesFunc, (LONG_PTR)&params);
if (!params.found)
{
/* FIXME: enable this */
//Panic(StringFormat(scratch.arena,
// Lit("INCBIN include not found in RC file: \"%F\""),
// FmtString(inc->rc_name)));
(*(volatile int *)0) = 0;
}
inc->data = params.data;
state = IncbinStatus_Searched;
Atomic32FetchSet(&inc->state, state);
}
else
{
state = v;
}
}
/* Spin while another thread searches */
while (state != IncbinStatus_Searched)
{
_mm_pause();
state = Atomic32Fetch(&inc->state);
}
EndScratch(scratch);
}
return inc->data;
}
#endif /* PlatformIsWindows */

View File

@ -1,78 +0,0 @@
#if PlatformIsWindows
////////////////////////////////
//~ Windows incbin types
Struct(IncbinRcSearchParams)
{
/* In */
String name_lower;
/* Out */
b32 found;
String data;
};
Enum(IncbinStatus)
{
IncbinStatus_Unsearched,
IncbinStatus_Searching,
IncbinStatus_Searched
};
Struct(IncbinRcResource)
{
Atomic32 state;
String rc_name;
String data;
};
////////////////////////////////
//~ Msvc incbin operations
BOOL IncbinEnumerateResourceNamesFunc(HMODULE module, LPCWSTR type, LPWSTR wstr_entry_name, LONG_PTR udata);
String StringFromIncbinRcResource(IncbinRcResource *inc);
/* NOTE: Msvc doesn't have an Inline assembler that can include binary data.
* So instead these macros will trigger a lookup into the embedded RC file for
* entries matched by name (this requires the build system to generate and link
* RC file).
*/
#define IncbinInclude(var, _rc_name) static IncbinRcResource _incbin_ ## var = { .rc_name = LitNoCast((_rc_name)) }
#define IncbinGet(var) StringFromIncbinRcResource(&_incbin_ ## var)
String StringFromIncbinRcResource(struct IncbinRcResource *inc);
#else /* PlatformIsWindows */
////////////////////////////////
//~ Clang incbin operations
#if PlatformIsWindows
# define IncbinSection ".rdata, \"dr\""
#elif PlatformIsMac
# define IncbinSection "__TEXT,__const"
#else
# define IncbinSection ".rodata"
#endif
/* Includes raw binary data into the executable. */
/* https://gist.github.com/mmozeiko/ed9655cf50341553d282 */
#define IncbinInclude(var, filename) \
__asm__(".section " IncbinSection "\n" \
".global _incbin_" Stringize(var) "_start\n" \
".balign 16\n" \
"_incbin_" Stringize(var) "_start:\n" \
".incbin \"" filename "\"\n" \
\
".global _incbin_" Stringize(var) "_end\n" \
".balign 1\n" \
"_incbin_" Stringize(var) "_end:\n" \
); \
extern __attribute((aligned(16))) const char _incbin_ ## var ## _start[]; \
extern const char _incbin_ ## var ## _end[]
/* Retrieve a string from included data using the variable supplied to IncbinInclude */
#define IncbinGet(var) StringFromPointers(_incbin_ ## var ## _start, _incbin_ ## var ## _end)
#endif /* CompilerIsClang */

View File

@ -494,7 +494,7 @@ f32 PowF32(f32 a, f32 b)
else else
{ {
/* a is negative */ /* a is negative */
f32 res_sign = RoundF32ToI32(b) % 2 == 0 ? 1 : -1; i32 res_sign = RoundF32ToI32(b) % 2 == 0 ? 1 : -1;
return ExpF32(LnF32(-a) * b) * res_sign; return ExpF32(LnF32(-a) * b) * res_sign;
} }
} }
@ -537,8 +537,8 @@ f32 ReduceToPio4(f32 x, i32 *octant_out)
if (x <= ((1 << 24) - 1)) if (x <= ((1 << 24) - 1))
{ {
octant = x * (4 / Pi); /* Integer part of x/(Pi/4) */ octant = (i32)(x * (4 / Pi)); /* Integer part of x/(Pi/4) */
f32 y = octant; f32 y = (f32)octant;
if (octant & 1) if (octant & 1)
{ {
octant += 1; octant += 1;

View File

@ -55,6 +55,7 @@ AlignedStruct(W32_WaitList, 64)
i32 num_waiters; i32 num_waiters;
W32_WaitList *next_in_bin; W32_WaitList *next_in_bin;
W32_WaitList *prev_in_bin; W32_WaitList *prev_in_bin;
u8 pad[32];
}; };
StaticAssert(alignof(W32_WaitList) == 64); /* Avoid false sharing */ StaticAssert(alignof(W32_WaitList) == 64); /* Avoid false sharing */
@ -123,6 +124,8 @@ AlignedStruct(W32_Fiber, 64)
/* ---------------------------------------------------- */ /* ---------------------------------------------------- */
GenericJobDesc *job_desc; /* 08 bytes */ GenericJobDesc *job_desc; /* 08 bytes */
/* ---------------------------------------------------- */ /* ---------------------------------------------------- */
u8 _pad0[8]; /* 08 bytes (padding) */
/* ---------------------------------------------------- */
/* -------------------- Cache line -------------------- */ /* -------------------- Cache line -------------------- */
/* ---------------------------------------------------- */ /* ---------------------------------------------------- */
i32 job_id; /* 04 bytes */ i32 job_id; /* 04 bytes */
@ -131,7 +134,7 @@ AlignedStruct(W32_Fiber, 64)
/* ---------------------------------------------------- */ /* ---------------------------------------------------- */
W32_YieldParam *yield_param; /* 08 bytes */ W32_YieldParam *yield_param; /* 08 bytes */
/* ---------------------------------------------------- */ /* ---------------------------------------------------- */
u8 _pad0[48]; /* 48 bytes (padding) */ u8 _pad1[48]; /* 48 bytes (padding) */
}; };
StaticAssert(sizeof(W32_Fiber) == 128); /* Padding validation (increase if necessary) */ StaticAssert(sizeof(W32_Fiber) == 128); /* Padding validation (increase if necessary) */

View File

@ -17,12 +17,6 @@
# endif # endif
#endif #endif
/* If we are not compiling in developer mode, assume resources are embedded as
* a tar archive in the executable. Otherwise, assume resources are files on
* disk. */
#define RESOURCES_EMBEDDED (!DeveloperIsEnabled)
#define RESOURCE_RELOADING (DeveloperIsEnabled && !RESOURCES_EMBEDDED)
#define DEFAULT_CAMERA_WIDTH (16) #define DEFAULT_CAMERA_WIDTH (16)
#define DEFAULT_CAMERA_HEIGHT ((f64)DEFAULT_CAMERA_WIDTH / (16.0 / 9.0)) #define DEFAULT_CAMERA_HEIGHT ((f64)DEFAULT_CAMERA_WIDTH / (16.0 / 9.0))

View File

@ -25,27 +25,27 @@ JobDef(F_LoadJob, sig, _)
0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF 0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF
}; };
String path = sig->path; R_Tag resource = sig->resource;
String name = R_NameFromTag(resource);
f32 point_size = sig->point_size; f32 point_size = sig->point_size;
AC_Asset *asset = sig->asset; AC_Asset *asset = sig->asset;
P_LogInfoF("Loading font \"%F\" (point size %F)", FmtString(path), FmtFloat((f64)point_size)); P_LogInfoF("Loading font \"%F\" (point size %F)", FmtString(name), FmtFloat((f64)point_size));
i64 start_ns = TimeNs(); i64 start_ns = TimeNs();
Assert(StringEndsWith(path, Lit(".ttf"))); Assert(StringEndsWith(name, Lit(".ttf")));
Assert(countof(font_codes) < F_LookupTableSize); Assert(countof(font_codes) < F_LookupTableSize);
/* Decode */ /* Decode */
RES_Resource res = RES_OpenResource(path); String resource_data = R_DataFromTag(resource);
if (!RES_ResourceExists(&res)) if (resource_data.len == 0)
{ {
/* FIME: Load baked font instead of panicking */ /* FIME: Load baked font instead of panicking */
Panic(StringFormat(scratch.arena, Panic(StringFormat(scratch.arena,
Lit("Font \"%F\" not found"), Lit("Font \"%F\" not found"),
FmtString(path))); FmtString(name)));
} }
TTF_Result result = TTF_Decode(scratch.arena, RES_GetResourceData(&res), point_size, font_codes, countof(font_codes)); TTF_Result result = TTF_Decode(scratch.arena, resource_data, point_size, font_codes, countof(font_codes));
RES_CloseResource(&res);
/* Send texture to GPU */ /* Send texture to GPU */
GPU_Resource *texture = 0; GPU_Resource *texture = 0;
@ -80,7 +80,7 @@ JobDef(F_LoadJob, sig, _)
{ {
Panic(StringFormat(scratch.arena, Panic(StringFormat(scratch.arena,
Lit("Parsed 0 glyphs from font \"%F\"!"), Lit("Parsed 0 glyphs from font \"%F\"!"),
FmtString(path))); FmtString(name)));
} }
/* Copy glyphs from decode result */ /* Copy glyphs from decode result */
@ -94,7 +94,7 @@ JobDef(F_LoadJob, sig, _)
font->lookup[codepoint] = result.cache_indices[i]; font->lookup[codepoint] = result.cache_indices[i];
} }
P_LogSuccessF("Loaded font \"%F\" (point size %F) in %F seconds", FmtString(path), FmtFloat((f64)point_size), FmtFloat(SecondsFromNs(TimeNs() - start_ns))); P_LogSuccessF("Loaded font \"%F\" (point size %F) in %F seconds", FmtString(name), FmtFloat((f64)point_size), FmtFloat(SecondsFromNs(TimeNs() - start_ns)));
AC_MarkReady(asset, font); AC_MarkReady(asset, font);
EndScratch(scratch); EndScratch(scratch);
@ -104,15 +104,16 @@ JobDef(F_LoadJob, sig, _)
//~ Load //~ Load
/* Returns the asset from the asset cache */ /* Returns the asset from the asset cache */
AC_Asset *F_LoadAsset(String path, f32 point_size, b32 wait) AC_Asset *F_LoadAsset(R_Tag resource, f32 point_size, b32 wait)
{ {
__prof; __prof;
TempArena scratch = BeginScratchNoConflict(); TempArena scratch = BeginScratchNoConflict();
String name = R_NameFromTag(resource);
/* Concatenate point_size to path for key */ /* Concatenate point_size to name for key */
String key = StringFormat(scratch.arena, String key = StringFormat(scratch.arena,
Lit("%F%F_font"), Lit("%F%F_font"),
FmtString(path), FmtString(name),
FmtFloatP((f64)point_size, 1)); FmtFloatP((f64)point_size, 1));
u64 hash = AC_HashFromKey(key); u64 hash = AC_HashFromKey(key);
b32 is_first_touch; b32 is_first_touch;
@ -123,7 +124,7 @@ AC_Asset *F_LoadAsset(String path, f32 point_size, b32 wait)
AC_MarkLoading(asset); AC_MarkLoading(asset);
F_LoadJob_Desc *desc = PushJobDesc(F_LoadJob, .pool = JobPool_Background, .priority = JobPriority_Low); F_LoadJob_Desc *desc = PushJobDesc(F_LoadJob, .pool = JobPool_Background, .priority = JobPriority_Low);
desc->sig->asset = asset; desc->sig->asset = asset;
desc->sig->path = PushString(desc->arena, path); desc->sig->resource = resource;
desc->sig->point_size = point_size; desc->sig->point_size = point_size;
RunJobEx((GenericJobDesc *)desc); RunJobEx((GenericJobDesc *)desc);
if (wait) if (wait)
@ -136,18 +137,18 @@ AC_Asset *F_LoadAsset(String path, f32 point_size, b32 wait)
return asset; return asset;
} }
F_Font *F_LoadFontAsync(String path, f32 point_size) F_Font *F_LoadFontAsync(R_Tag resource, f32 point_size)
{ {
__prof; __prof;
AC_Asset *asset = F_LoadAsset(path, point_size, 0); AC_Asset *asset = F_LoadAsset(resource, point_size, 0);
F_Font *f = (F_Font *)AC_DataFromStore(asset); F_Font *f = (F_Font *)AC_DataFromStore(asset);
return f; return f;
} }
F_Font *F_LoadFontWait(String path, f32 point_size) F_Font *F_LoadFontWait(R_Tag resource, f32 point_size)
{ {
__prof; __prof;
AC_Asset *asset = F_LoadAsset(path, point_size, 1); AC_Asset *asset = F_LoadAsset(resource, point_size, 1);
AC_YieldOnAssetReady(asset); AC_YieldOnAssetReady(asset);
F_Font *f = (F_Font *)AC_DataFromStore(asset); F_Font *f = (F_Font *)AC_DataFromStore(asset);
return f; return f;

View File

@ -27,14 +27,14 @@ Struct(F_Font)
//////////////////////////////// ////////////////////////////////
//~ Font load job //~ Font load job
JobDecl(F_LoadJob, { AC_Asset *asset; f32 point_size; String path; }); JobDecl(F_LoadJob, { AC_Asset *asset; f32 point_size; R_Tag resource; });
//////////////////////////////// ////////////////////////////////
//~ Font load operations //~ Font load operations
AC_Asset *F_LoadAsset(String path, f32 point_size, b32 wait); AC_Asset *F_LoadAsset(R_Tag resource, f32 point_size, b32 wait);
F_Font *F_LoadFontAsync(String path, f32 point_size); F_Font *F_LoadFontAsync(R_Tag resource, f32 point_size);
F_Font *F_LoadFontWait(String path, f32 point_size); F_Font *F_LoadFontWait(R_Tag resource, f32 point_size);
//////////////////////////////// ////////////////////////////////
//~ Font data operations //~ Font data operations

View File

@ -3,8 +3,8 @@
//- Dependencies //- Dependencies
@Dep ttf @Dep ttf
@Dep gpu @Dep gpu
@Dep resource
@Dep asset_cache @Dep asset_cache
@Dep res
//- Api //- Api
@IncludeC font.h @IncludeC font.h

View File

@ -1,19 +0,0 @@
/* This is the file that actually includes binary data meant to be embedded in
* the executable. Embedded files should be added as dependencies to this source
* file via the build system to ensure this translation unit is recompiled upon
* changes to an embedded file. */
#if RESOURCES_EMBEDDED
IncbinInclude(res_tar, IncbinDir "res.tar");
String INC_GetResTar(void)
{
return IncbinGet(res_tar);
}
#endif
IncbinInclude(dxc_tar, IncbinDir "dxc.tar");
String INC_GetDxcTar(void)
{
return IncbinGet(dxc_tar);
}

View File

@ -1,5 +0,0 @@
#if RESOURCES_EMBEDDED
String INC_GetResTar(void);
#endif
String INC_GetDxcTar(void);

View File

@ -1,7 +0,0 @@
@Layer inc
//- Api
@IncludeC inc.h
//- Impl
@IncludeC inc.c

View File

@ -1,9 +1,9 @@
/* TODO: Move decls to meta.h */ /* TODO: Move decls to meta.h */
#define MetaRebuildCode 0x3b5910d6 #define MetaRebuildCode 1317212284
//////////////////////////////// ////////////////////////////////
//~ Metaprogram default compiler definitions //~ Default base layer compiler definitions
#ifndef IsConsoleApp #ifndef IsConsoleApp
# define IsConsoleApp 1 # define IsConsoleApp 1
@ -739,13 +739,12 @@ Error *PushError(Arena *arena, ErrorList *list, String file, i64 pos, String s)
} }
//////////////////////////////// ////////////////////////////////
//~ Entry point //~ Build
void StartupMeta(void) void StartupMeta(void)
{ {
Arena *arena = AcquireArena(Gibi(64)); Arena *arena = AcquireArena(Gibi(64));
i32 ret = 0;
ErrorList errors = ZI; ErrorList errors = ZI;
//- Unpack args //- Unpack args
@ -864,7 +863,7 @@ void StartupMeta(void)
} }
/* Write to file */ /* Write to file */
String c_out = StringFromList(arena, c_out_lines, Lit("\n")); String c_out = StringFromList(arena, c_out_lines, Lit("\n"));
F_ClearWrite(Lit("gen.c"), c_out); F_ClearWrite(Lit("pp_gen.c"), c_out);
} }
//- Echo meta errors //- Echo meta errors
@ -910,11 +909,22 @@ void StartupMeta(void)
//- Msvc //- Msvc
{ {
PushStringToList(arena, &msvc_compiler_flags, Lit("-Zi")); PushStringToList(arena, &msvc_compiler_flags, Lit("-Z7"));
PushStringToList(arena, &msvc_compiler_flags, Lit("-DEBUG")); PushStringToList(arena, &msvc_compiler_flags, Lit("-DEBUG:FULL"));
PushStringToList(arena, &msvc_compiler_flags, Lit("-Fo:gen.obj")); PushStringToList(arena, &msvc_compiler_flags, Lit("-Fo:pp_pp_gen.obj"));
PushStringToList(arena, &msvc_compiler_flags, Lit("-Fe:pp.exe")); PushStringToList(arena, &msvc_compiler_flags, Lit("-Fe:pp.exe"));
PushStringToList(arena, &msvc_compiler_flags, Lit("-nologo")); PushStringToList(arena, &msvc_compiler_flags, Lit("-nologo"));
PushStringToList(arena, &msvc_compiler_flags, Lit("-diagnostics:column"));
/* Enable warnings */
PushStringToList(arena, &msvc_compiler_flags, Lit("-W4"));
PushStringToList(arena, &msvc_compiler_flags, Lit("-we4013")); /* function undefined; assuming extern returning int */
/* Disable warnings */
PushStringToList(arena, &msvc_compiler_flags, Lit("-wd4244")); /* function': conversion from 'int' to 'f32', possible loss of data */
PushStringToList(arena, &msvc_compiler_flags, Lit("-wd4201")); /* nonstandard extension used: nameless struct/union */
PushStringToList(arena, &msvc_compiler_flags, Lit("-wd4324")); /* structure was padded due to alignment specifier */
PushStringToList(arena, &msvc_compiler_flags, Lit("-wd4100")); /* unreferenced parameter */
PushStringToList(arena, &msvc_compiler_flags, Lit("-wd4189")); /* local variable is initialized but not referenced */
PushStringToList(arena, &msvc_compiler_flags, Lit("-wd4200")); /* nonstandard extension used: zero-sized array in struct/union */
} }
//- Clang //- Clang
@ -924,13 +934,14 @@ void StartupMeta(void)
PushStringToList(arena, &clang_compiler_flags, Lit("-g -gcodeview")); PushStringToList(arena, &clang_compiler_flags, Lit("-g -gcodeview"));
PushStringToList(arena, &clang_compiler_flags, Lit("-O0")); PushStringToList(arena, &clang_compiler_flags, Lit("-O0"));
PushStringToList(arena, &clang_compiler_flags, Lit("-msse4.2")); PushStringToList(arena, &clang_compiler_flags, Lit("-msse4.2"));
/* Warnings */ /* Enable warnings */
PushStringToList(arena, &clang_compiler_flags, Lit("-Wall")); PushStringToList(arena, &clang_compiler_flags, Lit("-Wall"));
PushStringToList(arena, &clang_compiler_flags, Lit("-Wframe-larger-than=65536")); PushStringToList(arena, &clang_compiler_flags, Lit("-Wframe-larger-than=65536"));
PushStringToList(arena, &clang_compiler_flags, Lit("-Wmissing-prototypes")); PushStringToList(arena, &clang_compiler_flags, Lit("-Wmissing-prototypes"));
PushStringToList(arena, &clang_compiler_flags, Lit("-Wunused-variable")); PushStringToList(arena, &clang_compiler_flags, Lit("-Wunused-variable"));
PushStringToList(arena, &clang_compiler_flags, Lit("-Wunused-but-set-variable")); PushStringToList(arena, &clang_compiler_flags, Lit("-Wunused-but-set-variable"));
PushStringToList(arena, &clang_compiler_flags, Lit("-Wunused-parameter")); PushStringToList(arena, &clang_compiler_flags, Lit("-Wunused-parameter"));
/* Disable warnings */
PushStringToList(arena, &clang_compiler_flags, Lit("-Wno-initializer-overrides")); PushStringToList(arena, &clang_compiler_flags, Lit("-Wno-initializer-overrides"));
PushStringToList(arena, &clang_compiler_flags, Lit("-Wno-microsoft-enum-forward-reference")); PushStringToList(arena, &clang_compiler_flags, Lit("-Wno-microsoft-enum-forward-reference"));
} }
@ -946,7 +957,7 @@ void StartupMeta(void)
FmtString(StringFromList(arena, shared_compiler_flags, Lit(" "))), FmtString(StringFromList(arena, shared_compiler_flags, Lit(" "))),
FmtString(StringFromList(arena, msvc_compiler_flags, Lit(" "))) FmtString(StringFromList(arena, msvc_compiler_flags, Lit(" ")))
); );
msvc_cmd_str = StringF(arena, "\"cl\" gen.c %F", FmtString(flags_str)); msvc_cmd_str = StringF(arena, "\"cl\" pp_gen.c %F", FmtString(flags_str));
} }
/* Clang */ /* Clang */
{ {
@ -955,12 +966,12 @@ void StartupMeta(void)
FmtString(StringFromList(arena, shared_compiler_flags, Lit(" "))), FmtString(StringFromList(arena, shared_compiler_flags, Lit(" "))),
FmtString(StringFromList(arena, clang_compiler_flags, Lit(" "))) FmtString(StringFromList(arena, clang_compiler_flags, Lit(" ")))
); );
clang_cmd_str = StringF(arena, "\"clang\" gen.c %F", FmtString(flags_str)); clang_cmd_str = StringF(arena, "\"clang\" pp_gen.c %F", FmtString(flags_str));
} }
//- Compile C //- Compile C
if (ret == 0) i32 ret = errors.count > 0;
{ if (ret == 0) {
String cmd_str = msvc_cmd_str; String cmd_str = msvc_cmd_str;
// String cmd_str = clang_cmd_str; // String cmd_str = clang_cmd_str;
OS_CommandResult result = OS_RunCommand(arena, cmd_str); OS_CommandResult result = OS_RunCommand(arena, cmd_str);
@ -973,9 +984,10 @@ void StartupMeta(void)
{ {
Echo(output); Echo(output);
} }
ret = result.code;
} }
ExitNow(ret != 0 ? ret : errors.count > 0); ExitNow(ret);
} }
//////////////////////////////// ////////////////////////////////

View File

@ -43,34 +43,6 @@ Struct(P_FileMap)
b32 valid; b32 valid;
}; };
////////////////////////////////
//~ Watch info types
Enum(P_WatchInfoKind)
{
P_WatchInfoKind_Unknown,
P_WatchInfoKind_Added,
P_WatchInfoKind_Removed,
P_WatchInfoKind_Modified,
P_WatchInfoKind_RenamedOld,
P_WatchInfoKind_RenamedNew
};
Struct(P_WatchInfo)
{
P_WatchInfoKind kind;
String name;
P_WatchInfo *next;
P_WatchInfo *prev;
};
Struct(P_WatchInfoList)
{
P_WatchInfo *first;
P_WatchInfo *last;
u64 count;
};
//////////////////////////////// ////////////////////////////////
//~ Window event types //~ Window event types
@ -344,15 +316,6 @@ P_FileMap P_OpenFileMap(P_File file);
void P_CloseFileMap(P_FileMap map); void P_CloseFileMap(P_FileMap map);
String P_GetFileMapData(P_FileMap map); String P_GetFileMapData(P_FileMap map);
////////////////////////////////
//~ @hookdecl Watch operations
// A watch object allows the caller to watch for changes in a directory
P_Watch *P_AcquireWatch(String path);
void P_ReleaseWatch(P_Watch *dw);
P_WatchInfoList P_ReadWatchWait(Arena *arena, P_Watch *dw);
void P_WakeWatch(P_Watch *dw);
//////////////////////////////// ////////////////////////////////
//~ @hookdecl Window operations //~ @hookdecl Window operations

View File

@ -1234,195 +1234,6 @@ String P_GetFileMapData(P_FileMap map)
return map.mapped_memory; return map.mapped_memory;
} }
////////////////////////////////
//~ @hookdef Watch hooks
P_Watch *P_AcquireWatch(String dir_path)
{
TempArena scratch = BeginScratchNoConflict();
struct P_W32_SharedCtx *g = &P_W32_shared_ctx;
P_W32_Watch *w32_watch = 0;
{
Lock lock = LockE(&g->watches_mutex);
{
if (g->watches_first_free)
{
w32_watch = g->watches_first_free;
g->watches_first_free = w32_watch->next_free;
}
else
{
w32_watch = PushStructNoZero(g->watches_arena, P_W32_Watch);
}
}
Unlock(&lock);
}
ZeroStruct(w32_watch);
wchar_t *dir_path_wstr = WstrFromString(scratch.arena, dir_path);
w32_watch->dir_handle = CreateFileW(
dir_path_wstr,
FILE_LIST_DIRECTORY,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
0,
OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED,
0
);
w32_watch->wake_handle = CreateEventW(0, 0, 0, 0);
EndScratch(scratch);
return (P_Watch *)w32_watch;
}
void P_ReleaseWatch(P_Watch *dw)
{
P_W32_Watch *w32_watch = (P_W32_Watch *)dw;
struct P_W32_SharedCtx *g = &P_W32_shared_ctx;
CloseHandle(w32_watch->dir_handle);
CloseHandle(w32_watch->wake_handle);
Lock lock = LockE(&g->watches_mutex);
{
w32_watch->next_free = g->watches_first_free;
g->watches_first_free = w32_watch;
}
Unlock(&lock);
}
P_WatchInfoList P_ReadWatchWait(Arena *arena, P_Watch *dw)
{
__prof;
P_W32_Watch *w32_watch = (P_W32_Watch *)dw;
P_WatchInfoList list = ZI;
DWORD filter = FILE_NOTIFY_CHANGE_FILE_NAME |
FILE_NOTIFY_CHANGE_DIR_NAME |
FILE_NOTIFY_CHANGE_LAST_WRITE |
FILE_NOTIFY_CHANGE_CREATION;
b32 done = 0;
while (!done)
{
OVERLAPPED ov = ZI;
ov.hEvent = CreateEventW(0, 0, 0, 0);
Assert(ov.hEvent);
BOOL success = ReadDirectoryChangesW(w32_watch->dir_handle,
w32_watch->results_buff,
countof(w32_watch->results_buff),
1,
filter,
0,
&ov,
0);
LAX success;
Assert(success);
HANDLE handles[] = {
ov.hEvent,
w32_watch->wake_handle
};
DWORD wait_result = WaitForMultipleObjects(2, handles, 0, INFINITE);
if (wait_result == WAIT_OBJECT_0)
{
i64 offset = 0;
while (!done)
{
FILE_NOTIFY_INFORMATION *result = (FILE_NOTIFY_INFORMATION *)(w32_watch->results_buff + offset);
P_WatchInfo *info = PushStruct(arena, P_WatchInfo);
if (list.last)
{
list.last->next = info;
info->prev = list.last;
}
else
{
list.first = info;
}
list.last = info;
++list.count;
String16 name16 = ZI;
name16.text = result->FileName;
name16.len = result->FileNameLength / sizeof(wchar_t);
info->name = StringFromString16(arena, name16);
for (u64 i = 0; i < info->name.len; ++i)
{
if (info->name.text[i] == '\\')
{
info->name.text[i] = '/';
}
}
switch (result->Action)
{
case FILE_ACTION_ADDED:
{
info->kind = P_WatchInfoKind_Added;
} break;
case FILE_ACTION_REMOVED:
{
info->kind = P_WatchInfoKind_Removed;
} break;
case FILE_ACTION_MODIFIED:
{
info->kind = P_WatchInfoKind_Modified;
} break;
case FILE_ACTION_RENAMED_OLD_NAME:
{
info->kind = P_WatchInfoKind_RenamedOld;
} break;
case FILE_ACTION_RENAMED_NEW_NAME:
{
info->kind = P_WatchInfoKind_RenamedNew;
} break;
default:
{
info->kind = P_WatchInfoKind_Unknown;
} break;
}
if (result->NextEntryOffset == 0)
{
done = 1;
}
else
{
offset += result->NextEntryOffset;
}
}
}
else if (wait_result == WAIT_OBJECT_0 + 1)
{
ResetEvent(w32_watch->wake_handle);
done = 1;
}
else
{
Assert(0);
}
}
return list;
}
void P_WakeWatch(P_Watch *dw)
{
P_W32_Watch *w32_watch = (P_W32_Watch *)dw;
SetEvent(w32_watch->wake_handle);
}
//////////////////////////////// ////////////////////////////////
//~ @hookdef Window hooks //~ @hookdef Window hooks

0
src/res/res.c Normal file
View File

13
src/res/res.h Normal file
View File

@ -0,0 +1,13 @@
////////////////////////////////
//~ Tag types
Struct(R_Tag)
{
u64 hash;
};
////////////////////////////////
//~ Tag helpers
String R_DataFromTag(R_Tag tag);
String R_NameFromTag(R_Tag tag);

7
src/res/res.lay Normal file
View File

@ -0,0 +1,7 @@
@Layer res
//- Api
@IncludeC res.h
//- Impl
@IncludeC res.c

View File

@ -1,97 +0,0 @@
RES_SharedState RES_shared_state = ZI;
////////////////////////////////
//~ Startup
void RES_Startup(void)
{
__prof;
#if RESOURCES_EMBEDDED
String embedded_data = INC_GetResTar();
if (embedded_data.len <= 0)
{
Panic(Lit("No embedded resources found"));
}
g->archive = TAR_ArchiveFromString(GetPermArena(), embedded_data, Lit(""));
#else
/* Ensure we have the right working directory */
if (!P_IsDir(Lit("res")))
{
Panic(Lit("Resource directory \"res\" not found. Make sure the executable is being launched from the correct working directory."));
}
#endif
}
////////////////////////////////
//~ Open / close
RES_Resource RES_OpenResource(String name)
{
__prof;
#if RESOURCES_EMBEDDED
RES_SharedState *g = &RES_shared_state;
RES_Resource result = ZI;
TAR_Entry *entry = TAR_EntryFromName(&g->archive, name);
result._data = entry->data;
result._name = entry->file_name;
result._exists = entry->valid;
return result;
#else
RES_Resource result = ZI;
if (name.len < countof(result._name_text))
{
u8 path_text[RES_ResourceNameLenMax + (sizeof("result/") - 1)];
String path = ZI;
{
path_text[0] = 'r';
path_text[1] = 'e';
path_text[2] = 's';
path_text[3] = '/';
u64 path_text_len = 4;
CopyBytes(path_text + path_text_len, name.text, name.len);
path_text_len += name.len;
path = STRING(path_text_len, path_text);
}
P_File file = P_OpenFileReadWait(path);
P_FileMap file_map = ZI;
String data = ZI;
if (file.valid)
{
file_map = P_OpenFileMap(file);
if (file_map.valid)
{
data = P_GetFileMapData(file_map);
}
else
{
P_CloseFileMap(file_map);
}
}
else
{
P_CloseFIle(file);
}
result._exists = file.valid && file_map.valid;
result._data = data;
result._file = file;
result._file_map = file_map;
result._name_len = name.len;
CopyBytes(result._name_text, name.text, name.len);
}
else
{
Assert(0);
}
return result;
#endif
}
#if !RESOURCES_EMBEDDED
void RES_CloseResource(RES_Resource *res_ptr)
{
P_CloseFileMap(res_ptr->_file_map);
P_CloseFIle(res_ptr->_file);
}
#endif

View File

@ -1,61 +0,0 @@
////////////////////////////////
//~ Resource types
#define RES_ResourceNameLenMax 256
Struct(RES_Resource)
{
String _data;
b32 _exists;
#if RESOURCES_EMBEDDED
String _name;
#else
P_File _file;
P_FileMap _file_map;
u8 _name_text[RES_ResourceNameLenMax];
u8 _name_len;
#endif
};
////////////////////////////////
//~ Shared state
Struct(RES_SharedState)
{
#if RESOURCES_EMBEDDED
TAR_Archive archive;
#else
i32 _;
#endif
};
extern RES_SharedState RES_shared_state;
////////////////////////////////
//~ Startup
void RES_Startup(void);
////////////////////////////////
//~ Open / close
RES_Resource RES_OpenResource(String name);
#if RESOURCES_EMBEDDED
# define RES_CloseResource(res_ptr) LAX res_ptr
#else
void RES_CloseResource(RES_Resource *res_ptr);
#endif
////////////////////////////////
//~ Resource data operations
#define RES_GetResourceData(res_ptr) (res_ptr)->_data
#define RES_ResourceExists(res_ptr) (res_ptr)->_exists
#if RESOURCES_EMBEDDED
# define RES_GetResourceName(res_ptr) (res_ptr)->_name
#else
# define RES_GetResourceName(res_ptr) STRING((res_ptr)->_name_len, (res_ptr)->_name_text)
#endif

View File

@ -1,15 +0,0 @@
@Layer resource
//- Dependencies
@Dep platform
@Dep tar
@Dep inc
//- Api
@IncludeC resource.h
//- Impl
@IncludeC resource.c
//- Startup
@Startup RES_Startup

View File

@ -5,39 +5,35 @@ JobDef(SND_LoadJob, sig, UNUSED id)
{ {
__prof; __prof;
TempArena scratch = BeginScratchNoConflict(); TempArena scratch = BeginScratchNoConflict();
String path = sig->path; R_Tag resource = sig->resource;
String name = R_NameFromTag(resource);
AC_Asset *asset = sig->asset; AC_Asset *asset = sig->asset;
SND_SoundFlag flags = sig->flags; SND_SoundFlag flags = sig->flags;
P_LogInfoF("Loading sound \"%F\"", FmtString(path)); P_LogInfoF("Loading sound \"%F\"", FmtString(name));
i64 start_ns = TimeNs(); i64 start_ns = TimeNs();
String error_msg = Lit("Unknown error"); String error_msg = Lit("Unknown error");
Assert(StringEndsWith(path, Lit(".mp3")));
/* Decode */ /* Decode */
MP3_Result decoded = ZI; MP3_Result decoded = ZI;
String resource_data = R_DataFromTag(resource);
if (resource_data.len > 0)
{ {
RES_Resource sound_rs = RES_OpenResource(path); u64 decode_flags = 0;
if (RES_ResourceExists(&sound_rs)) if (flags & SND_SoundFlag_Stereo)
{ {
u64 decode_flags = 0; decode_flags |= MP3_DecodeFlag_Stereo;
if (flags & SND_SoundFlag_Stereo)
{
decode_flags |= MP3_DecodeFlag_Stereo;
}
decoded = MP3_Decode(scratch.arena, RES_GetResourceData(&sound_rs), SND_SampleRate, decode_flags);
if (!decoded.success)
{
error_msg = Lit("Failed to decode sound file");
}
} }
else decoded = MP3_Decode(scratch.arena, resource_data, SND_SampleRate, decode_flags);
if (!decoded.success)
{ {
error_msg = Lit("Resource not found"); error_msg = Lit("Failed to decode sound file");
} }
RES_CloseResource(&sound_rs); }
else
{
error_msg = Lit("Missing resource data");
} }
if (decoded.success) if (decoded.success)
@ -60,12 +56,12 @@ JobDef(SND_LoadJob, sig, UNUSED id)
sound->samples = samples; sound->samples = samples;
CopyBytes(sound->samples, decoded.samples, decoded.samples_count * sizeof(*decoded.samples)); CopyBytes(sound->samples, decoded.samples, decoded.samples_count * sizeof(*decoded.samples));
P_LogSuccessF("Loaded sound \"%F\" in %F seconds", FmtString(path), FmtFloat(SecondsFromNs(TimeNs() - start_ns))); P_LogSuccessF("Loaded sound \"%F\" in %F seconds", FmtString(name), FmtFloat(SecondsFromNs(TimeNs() - start_ns)));
AC_MarkReady(asset, sound); AC_MarkReady(asset, sound);
} }
else else
{ {
P_LogErrorF("Error loading sound \"%F\": %F", FmtString(path), FmtString(error_msg)); P_LogErrorF("Error loading sound \"%F\": %F", FmtString(name), FmtString(error_msg));
/* Store */ /* Store */
SND_Sound *sound = 0; SND_Sound *sound = 0;
@ -84,15 +80,16 @@ JobDef(SND_LoadJob, sig, UNUSED id)
//////////////////////////////// ////////////////////////////////
//~ Load sound //~ Load sound
AC_Asset *SND_LoadAsset(String path, SND_SoundFlag flags, b32 wait) AC_Asset *SND_LoadAsset(R_Tag resource, SND_SoundFlag flags, b32 wait)
{ {
__prof; __prof;
TempArena scratch = BeginScratchNoConflict(); TempArena scratch = BeginScratchNoConflict();
/* Generate and append sound flags to path key */ /* Generate and append sound flags to name key */
String name = R_NameFromTag(resource);
String key = StringFormat(scratch.arena, String key = StringFormat(scratch.arena,
Lit("%F%F_sound"), Lit("%F%F_sound"),
FmtString(path), FmtString(name),
FmtUint((u64)flags)); FmtUint((u64)flags));
u64 hash = AC_HashFromKey(key); u64 hash = AC_HashFromKey(key);
b32 is_first_touch; b32 is_first_touch;
@ -102,7 +99,7 @@ AC_Asset *SND_LoadAsset(String path, SND_SoundFlag flags, b32 wait)
{ {
AC_MarkLoading(asset); AC_MarkLoading(asset);
SND_LoadJob_Desc *desc = PushJobDesc(SND_LoadJob, .pool = JobPool_Background, .priority = JobPriority_Low, .counter = &asset->counter); SND_LoadJob_Desc *desc = PushJobDesc(SND_LoadJob, .pool = JobPool_Background, .priority = JobPriority_Low, .counter = &asset->counter);
desc->sig->path = PushString(desc->arena, path); desc->sig->resource = resource;
desc->sig->asset = asset; desc->sig->asset = asset;
desc->sig->flags = flags; desc->sig->flags = flags;
RunJobEx((GenericJobDesc *)desc); RunJobEx((GenericJobDesc *)desc);
@ -116,18 +113,18 @@ AC_Asset *SND_LoadAsset(String path, SND_SoundFlag flags, b32 wait)
return asset; return asset;
} }
SND_Sound *SND_LoadSoundAsync(String path, SND_SoundFlag flags) SND_Sound *SND_LoadSoundAsync(R_Tag resource, SND_SoundFlag flags)
{ {
__prof; __prof;
AC_Asset *asset = SND_LoadAsset(path, flags, 0); AC_Asset *asset = SND_LoadAsset(resource, flags, 0);
SND_Sound *sound = (SND_Sound *)AC_DataFromStore(asset); SND_Sound *sound = (SND_Sound *)AC_DataFromStore(asset);
return sound; return sound;
} }
SND_Sound *SND_LoadSoundWait(String path, SND_SoundFlag flags) SND_Sound *SND_LoadSoundWait(R_Tag resource, SND_SoundFlag flags)
{ {
__prof; __prof;
AC_Asset *asset = SND_LoadAsset(path, flags, 1); AC_Asset *asset = SND_LoadAsset(resource, flags, 1);
AC_YieldOnAssetReady(asset); AC_YieldOnAssetReady(asset);
SND_Sound *sound = (SND_Sound *)AC_DataFromStore(asset); SND_Sound *sound = (SND_Sound *)AC_DataFromStore(asset);
return sound; return sound;

View File

@ -19,7 +19,7 @@ Struct(SND_Sound)
//////////////////////////////// ////////////////////////////////
//~ Sound load operations //~ Sound load operations
JobDecl(SND_LoadJob, { SND_SoundFlag flags; AC_Asset *asset; String path; }); JobDecl(SND_LoadJob, { SND_SoundFlag flags; AC_Asset *asset; R_Tag resource; });
AC_Asset *SND_LoadAsset(String path, SND_SoundFlag flags, b32 wait); AC_Asset *SND_LoadAsset(R_Tag resource, SND_SoundFlag flags, b32 wait);
SND_Sound *SND_LoadSoundAsync(String path, SND_SoundFlag flags); SND_Sound *SND_LoadSoundAsync(R_Tag resource, SND_SoundFlag flags);
SND_Sound *SND_LoadSoundWait(String path, SND_SoundFlag flags); SND_Sound *SND_LoadSoundWait(R_Tag resource, SND_SoundFlag flags);

View File

@ -3,8 +3,8 @@
//- Dependencies //- Dependencies
@Dep platform @Dep platform
@Dep mp3 @Dep mp3
@Dep resource
@Dep asset_cache @Dep asset_cache
@Dep res
//- Api //- Api
@IncludeC sound.h @IncludeC sound.h

View File

@ -48,9 +48,6 @@ void S_Startup(void)
RunJob(1, S_EvictorJob, JobPool_Background, JobPriority_Low, &g->shutdown_counter, 0); RunJob(1, S_EvictorJob, JobPool_Background, JobPriority_Low, &g->shutdown_counter, 0);
OnExit(&S_Shutdown); OnExit(&S_Shutdown);
#if RESOURCE_RELOADING
W_RegisterCallback(&S_WatchSpriteCallback);
#endif
} }
//////////////////////////////// ////////////////////////////////
@ -528,22 +525,6 @@ void S_LoadCacheEntryTexture(S_CacheEntryRef ref, S_Tag tag)
Atomic32FetchSet(&e->state, S_CacheEntryState_Loaded); Atomic32FetchSet(&e->state, S_CacheEntryState_Loaded);
#if RESOURCE_RELOADING
S_CacheEntryBin *bin = &g->cache.bins[e->hash.v % S_CacheBinsCount];
Lock bin_lock = LockE(&bin->mutex);
{
for (S_CacheEntry *old_entry = bin->first; old_entry; old_entry = old_entry->next_in_bin)
{
if (old_entry != e && old_entry->hash.v == e->hash.v)
{
Atomic32FetchSet(&old_entry->out_of_date, 1);
}
}
e->load_time_ns = TimeNs();
}
Unlock(&bin_lock);
#endif
EndScratch(scratch); EndScratch(scratch);
} }
@ -611,22 +592,6 @@ void S_LoadCacheEntrySheet(S_CacheEntryRef ref, S_Tag tag)
Atomic32FetchSet(&e->state, S_CacheEntryState_Loaded); Atomic32FetchSet(&e->state, S_CacheEntryState_Loaded);
#if RESOURCE_RELOADING
S_CacheEntryBin *bin = &g->cache.bins[e->hash.v % S_CacheBinsCount];
Lock bin_lock = LockE(&bin->mutex);
{
for (S_CacheEntry *old_entry = bin->first; old_entry; old_entry = old_entry->next_in_bin)
{
if (old_entry != e && old_entry->hash.v == e->hash.v)
{
Atomic32FetchSet(&old_entry->out_of_date, 1);
}
}
e->load_time_ns = TimeNs();
}
Unlock(&bin_lock);
#endif
EndScratch(scratch); EndScratch(scratch);
} }
@ -777,29 +742,6 @@ S_ScopeCacheEntryRef *S_EntryFromHashLocked(S_Scope *scope, S_Hash hash, Lock *b
S_CacheEntryBin *bin = &g->cache.bins[hash.v % S_CacheBinsCount]; S_CacheEntryBin *bin = &g->cache.bins[hash.v % S_CacheBinsCount];
AssertLockedES(bin_lock, &bin->mutex); /* Lock required for iterating bin */ AssertLockedES(bin_lock, &bin->mutex); /* Lock required for iterating bin */
#if RESOURCE_RELOADING
/* If resource reloading is enabled, then we want to find the
* newest entry rather than the first one that exists since
* there may be more than one matching entry in the cache */
S_CacheEntry *match = 0;
S_CacheEntryState match_state = S_CacheEntryState_None;
for (S_CacheEntry *entry = bin->first; entry; entry = entry->next_in_bin)
{
if (entry->hash.v == hash.v)
{
S_CacheEntryState entry_state = Atomic32Fetch(&entry->state);
if (!match || entry_state > match_state || (entry_state == S_CacheEntryState_Loaded && match_state == S_CacheEntryState_Loaded && entry->load_time_ns > match->load_time_ns))
{
match = entry;
match_state = entry_state;
}
}
}
if (match)
{
scope_ref = S_EnsureRefFromEntryLocked(scope, match, bin_lock);
}
#else
for (S_CacheEntry *entry = bin->first; entry; entry = entry->next_in_bin) for (S_CacheEntry *entry = bin->first; entry; entry = entry->next_in_bin)
{ {
if (entry->hash.v == hash.v) if (entry->hash.v == hash.v)
@ -808,7 +750,6 @@ S_ScopeCacheEntryRef *S_EntryFromHashLocked(S_Scope *scope, S_Hash hash, Lock *b
break; break;
} }
} }
#endif
return scope_ref; return scope_ref;
} }
@ -1095,52 +1036,6 @@ S_SliceArray S_SlicesFromNameIndex(S_Sheet *sheet, String name, u32 frame_index)
return result; return result;
} }
////////////////////////////////
//~ Resource reloading
#if RESOURCE_RELOADING
void S_ReloadSpriteFromTag(S_Scope *scope, S_Tag tag, S_CacheEntryKind kind)
{
S_SharedState *g = &S_shared_state;
S_Hash hash = S_CacheEntryFromTagHash(tag.hash, kind);
S_CacheEntryBin *bin = &g->cache.bins[hash.v % S_CacheBinsCount];
S_ScopeCacheEntryRef *existing_ref = 0;
Lock bin_lock = LockS(&bin->mutex);
{
existing_ref = S_EntryFromHashLocked(scope, hash, &bin_lock);
}
Unlock(&bin_lock);
if (existing_ref)
{
P_LogInfoF("Sprite resource file \"%F\" has changed for sprite [%F].", FmtString(tag.path), FmtHex(hash.v));
S_ScopeCacheEntryRef *scope_ref = S_EntryFromTag(scope, tag, kind, 1);
S_PushLoadJob(scope_ref->ref, tag);
}
}
W_CallbackFuncDef(S_WatchSpriteCallback, name)
{
S_Scope *scope = S_BeginScope();
if (StringStartsWith(name, Lit("res/")))
{
name.len -= Lit("res/").len;
name.text += Lit("res/").len;
}
S_Tag tag = S_TagFromPath(name);
for (S_CacheEntryKind kind = 0; kind < S_CacheEntryKind_Count; ++kind)
{
S_ReloadSpriteFromTag(scope, tag, kind);
}
S_EndScope(scope);
}
#endif
//////////////////////////////// ////////////////////////////////
//~ Evictor //~ Evictor
@ -1161,7 +1056,6 @@ MergesortCompareFuncDef(S_EvictorSortCmp, arg_a, arg_b, _)
* An attempt to evict a cache node will occur when: * An attempt to evict a cache node will occur when:
* - Its refcount = 0 and * - Its refcount = 0 and
* - The cache is over its memory budget and the node's last reference is longer ago than the grace period * - The cache is over its memory budget and the node's last reference is longer ago than the grace period
* - Resource reloading is enabled and the node is out of date due to a change to its original resource file
*/ */
JobDef(S_EvictorJob, UNUSED sig, UNUSED job_id) JobDef(S_EvictorJob, UNUSED sig, UNUSED job_id)
{ {
@ -1179,7 +1073,7 @@ JobDef(S_EvictorJob, UNUSED sig, UNUSED job_id)
/* Scan for evictable nodes */ /* Scan for evictable nodes */
b32 cache_over_budget_threshold = Atomic64Fetch(&g->cache.memory_usage.v) > (i64)S_CacheMemoryBudgetThreshold; b32 cache_over_budget_threshold = Atomic64Fetch(&g->cache.memory_usage.v) > (i64)S_CacheMemoryBudgetThreshold;
if (cache_over_budget_threshold || RESOURCE_RELOADING) if (cache_over_budget_threshold)
{ {
__profn("Evictor scan"); __profn("Evictor scan");
for (u64 i = 0; i < S_CacheBinsCount; ++i) for (u64 i = 0; i < S_CacheBinsCount; ++i)
@ -1195,22 +1089,13 @@ JobDef(S_EvictorJob, UNUSED sig, UNUSED job_id)
if (refcount.count <= 0) if (refcount.count <= 0)
{ {
/* Add node to evict list */ /* Add node to evict list */
#if RESOURCE_RELOADING
b32 is_out_of_date = Atomic32Fetch(&n->out_of_date);
#else
b32 is_out_of_date = 0;
#endif
b32 is_old = cache_over_budget_threshold && ((cur_cycle - refcount.last_ref_cycle) > S_EvictorGracePeriodCycles); b32 is_old = cache_over_budget_threshold && ((cur_cycle - refcount.last_ref_cycle) > S_EvictorGracePeriodCycles);
if (is_old || is_out_of_date) if (is_old)
{ {
S_EvictorNode *en = PushStruct(scratch.arena, S_EvictorNode); S_EvictorNode *en = PushStruct(scratch.arena, S_EvictorNode);
en->cache_entry = n; en->cache_entry = n;
en->cache_bin = bin; en->cache_bin = bin;
en->last_ref_cycle = refcount.last_ref_cycle; en->last_ref_cycle = refcount.last_ref_cycle;
if (is_out_of_date)
{
en->last_ref_cycle = -1;
}
++evict_array_count; ++evict_array_count;
} }
} }

View File

@ -142,10 +142,6 @@ Struct(S_CacheEntry)
/* Free list */ /* Free list */
S_CacheEntry *next_free; S_CacheEntry *next_free;
#if RESOURCE_RELOADING
Atomic32 out_of_date; /* Has the resource changed since this entry was loaded */
#endif
}; };
Struct(S_CacheEntryBin) Struct(S_CacheEntryBin)
@ -335,14 +331,6 @@ S_Span S_SpanFromName(S_Sheet *sheet, String name);
S_Slice S_SliceFromNameIndex(S_Sheet *sheet, String name, u32 frame_index); S_Slice S_SliceFromNameIndex(S_Sheet *sheet, String name, u32 frame_index);
S_SliceArray S_SlicesFromNameIndex(S_Sheet *sheet, String name, u32 frame_index); S_SliceArray S_SlicesFromNameIndex(S_Sheet *sheet, String name, u32 frame_index);
////////////////////////////////
//~ Resource reload
#if RESOURCE_RELOADING
void S_ReloadSpriteFromTag(S_Scope *scope, S_Tag tag, S_CacheEntryKind kind);
W_CallbackFuncDef(S_WatchSpriteCallback, name);
#endif
//////////////////////////////// ////////////////////////////////
//~ Evictor job //~ Evictor job

View File

@ -4,8 +4,6 @@
@Dep platform @Dep platform
@Dep gpu @Dep gpu
@Dep ase @Dep ase
@Dep resource
@Dep watch
//- Api //- Api
@IncludeC sprite.h @IncludeC sprite.h

View File

@ -1,221 +0,0 @@
W_SharedState W_shared_state = ZI;
////////////////////////////////
//~ Startup
void W_Startup(void)
{
W_SharedState *g = &W_shared_state;
g->watch = P_AcquireWatch(Lit("./"));
g->watch_events_arena = AcquireArena(Gibi(64));
RunJob(1, W_MonitorJob, JobPool_Floating, JobPriority_Low, &g->watch_jobs_counter, 0);
RunJob(1, W_DispatcherJob, JobPool_Floating, JobPriority_Low, &g->watch_jobs_counter, 0);
OnExit(&W_Shutdown);
}
ExitFuncDef(W_Shutdown)
{
__prof;
W_SharedState *g = &W_shared_state;
Atomic32FetchSet(&g->W_Shutdown, 1);
{
Lock lock = LockE(&g->watch_dispatcher_mutex);
SignalCv(&g->watch_dispatcher_cv, I32Max);
P_WakeWatch(g->watch);
Unlock(&lock);
}
YieldOnCounter(&g->watch_jobs_counter);
}
////////////////////////////////
//~ Callback
void W_RegisterCallback(W_CallbackFunc *callback)
{
W_SharedState *g = &W_shared_state;
Lock lock = LockE(&g->watch_callbacks_mutex);
{
if (g->num_watch_callbacks < countof(g->watch_callbacks))
{
g->watch_callbacks[g->num_watch_callbacks++] = callback;
}
else
{
Panic(Lit("Max resource watch callbacks reached"));
}
}
Unlock(&lock);
}
JobDef(W_RunCallbacksJob , sig, id)
{
__prof;
String name = sig->name;
W_CallbackFunc *callback = sig->callbacks[id];
callback(name);
}
////////////////////////////////
//~ Monitor job
/* NOTE: We separate the responsibilities of monitoring directory changes
* & dispatching watch callbacks into two separate jobs so that we can delay
* the dispatch, allowing for deduplication of file modification notifications. */
JobDef(W_MonitorJob, UNUSED sig, UNUSED job_id)
{
TempArena scratch = BeginScratchNoConflict();
W_SharedState *g = &W_shared_state;
String ignored[] = {
Lit(".vs"),
Lit(".git")
};
while (!Atomic32Fetch(&g->W_Shutdown))
{
TempArena temp = BeginTempArena(scratch.arena);
P_WatchInfoList info_list = P_ReadWatchWait(temp.arena, g->watch);
if (info_list.first && !Atomic32Fetch(&g->W_Shutdown))
{
Lock lock = LockE(&g->watch_dispatcher_mutex);
{
for (P_WatchInfo *info = info_list.first; info; info = info->next)
{
String name_src = info->name;
b32 ignore = 0;
for (u32 i = 0; i < countof(ignored); ++i)
{
if (StringStartsWith(name_src, ignored[i]))
{
ignore = 1;
break;
}
}
if (!ignore)
{
W_Event *e = PushStruct(g->watch_events_arena, W_Event);
e->name = PushString(g->watch_events_arena, name_src);
if (g->last_watch_event)
{
g->last_watch_event->next = e;
}
else
{
g->first_watch_event = e;
}
g->last_watch_event = e;
}
}
}
SignalCv(&g->watch_dispatcher_cv, I32Max);
Unlock(&lock);
}
EndTempArena(temp);
}
EndScratch(scratch);
}
////////////////////////////////
//~ Dispatcher job
JobDef(W_DispatcherJob, UNUSED sig, UNUSED job_id)
{
W_SharedState *g = &W_shared_state;
b32 shutdown = 0;
while (!shutdown)
{
{
TempArena scratch = BeginScratchNoConflict();
W_Event *first_watch_event = 0;
W_Event *last_watch_event = 0;
/* Delay so that duplicate events pile up */
{
__profn("Delay");
FutexYield(0, 0, 0, NsFromSeconds(W_DispatcherDelaySeconds));
}
/* Pull watch events from queue */
{
Lock lock = LockE(&g->watch_dispatcher_mutex);
for (W_Event *src_event = g->first_watch_event; src_event; src_event = src_event->next)
{
W_Event *e = PushStruct(scratch.arena, W_Event);
e->name = PushString(scratch.arena, src_event->name);
if (last_watch_event)
{
last_watch_event->next = e;
}
else
{
first_watch_event = e;
}
last_watch_event = e;
}
g->first_watch_event = 0;
g->last_watch_event = 0;
ResetArena(g->watch_events_arena);
Unlock(&lock);
}
/* Build callbacks array */
u64 num_callbacks = 0;
W_CallbackFunc **callbacks = 0;
Lock callbacks_lock = LockS(&g->watch_callbacks_mutex);
{
num_callbacks = g->num_watch_callbacks;
callbacks = PushStructsNoZero(scratch.arena, W_CallbackFunc *, num_callbacks);
for (u64 i = 0; i < num_callbacks; ++i)
{
callbacks[i] = g->watch_callbacks[i];
}
}
Unlock(&callbacks_lock);
/* Run callbacks */
{
Dict *dedup_dict = InitDict(scratch.arena, W_DispatcherDedupBins);
for (W_Event *e = first_watch_event; e; e = e->next)
{
__profn("Dispatch");
/* Do not run callbacks for the same file more than once */
b32 skip = 0;
u64 hash = HashFnv64(Fnv64Basis, e->name);
if (DictValueFromHash(dedup_dict, hash) == 1)
{
skip = 1;
}
else
{
SetDictValue(scratch.arena, dedup_dict, hash, 1);
}
if (!skip)
{
Counter counter = ZI;
RunJob(num_callbacks, W_RunCallbacksJob, JobPool_Background, JobPriority_Low, &counter, .name = e->name, .callbacks = callbacks);
YieldOnCounter(&counter);
}
}
}
EndScratch(scratch);
}
/* Yield for event */
Lock lock = LockS(&g->watch_dispatcher_mutex);
{
shutdown = Atomic32Fetch(&g->W_Shutdown);
while (!shutdown && !g->first_watch_event)
{
YieldOnCv(&g->watch_dispatcher_cv, &lock);
shutdown = Atomic32Fetch(&g->W_Shutdown);
}
}
Unlock(&lock);
}
}

View File

@ -1,61 +0,0 @@
////////////////////////////////
//~ Callback types
#define W_CallbackFuncDef(func_name, arg_name) void func_name(String arg_name)
typedef W_CallbackFuncDef(W_CallbackFunc, name);
////////////////////////////////
//~ Event types
Struct(W_Event)
{
String name;
W_Event *next;
};
////////////////////////////////
//~ Shared state
#define W_DispatcherDelaySeconds 0.050
#define W_DispatcherDedupBins 128
Struct(W_SharedState)
{
P_Watch *watch;
Atomic32 W_Shutdown;
Counter watch_jobs_counter;
Mutex watch_dispatcher_mutex;
Arena *watch_events_arena;
W_Event *first_watch_event;
W_Event *last_watch_event;
Cv watch_dispatcher_cv;
Mutex watch_callbacks_mutex;
W_CallbackFunc *watch_callbacks[64];
u64 num_watch_callbacks;
};
extern W_SharedState W_shared_state;
////////////////////////////////
//~ Startup
void W_Startup(void);
ExitFuncDef(W_Shutdown);
////////////////////////////////
//~ Watch operations
void W_RegisterCallback(W_CallbackFunc *callback);
////////////////////////////////
//~ Callback job
JobDecl(W_RunCallbacksJob, { String name; W_CallbackFunc **callbacks; });
////////////////////////////////
//~ Long running jobs
JobDecl(W_MonitorJob, { i32 _; });
JobDecl(W_DispatcherJob, { i32 _; });

View File

@ -1,13 +0,0 @@
@Layer watch
//- Dependencies
@Dep platform
//- Api
@IncludeC watch.h
//- Impl
@IncludeC watch.c
//- Startup
@Startup W_Startup