font layer refactor

This commit is contained in:
jacob 2025-07-30 18:05:16 -05:00
parent 8ba63a7821
commit ca07cde1a9
5 changed files with 158 additions and 101 deletions

View File

@ -241,7 +241,7 @@ void P_AppStartup(String args_str)
N_StartupReceipt host_sr = host_startup(); N_StartupReceipt host_sr = host_startup();
AC_StartupReceipt asset_cache_sr = AC_Startup(); AC_StartupReceipt asset_cache_sr = AC_Startup();
TTF_StartupReceipt ttf_sr = ttf_startup(); TTF_StartupReceipt ttf_sr = ttf_startup();
F_StartupReceipt font_sr = font_startup(&asset_cache_sr, &ttf_sr); F_StartupReceipt font_sr = F_Startup(&asset_cache_sr, &ttf_sr);
S_StartupReceipt sprite_sr = sprite_startup(); S_StartupReceipt sprite_sr = sprite_startup();
M_StartupReceipt mixer_sr = mixer_startup(); M_StartupReceipt mixer_sr = mixer_startup();
SND_StartupReceipt sound_sr = sound_startup(&asset_cache_sr); SND_StartupReceipt sound_sr = sound_startup(&asset_cache_sr);

View File

@ -347,7 +347,7 @@ Rect draw_text(G_RenderSig *sig, D_TextParams params)
{ {
D_TextGlyph *tg = PushStruct(scratch.arena, D_TextGlyph); D_TextGlyph *tg = PushStruct(scratch.arena, D_TextGlyph);
++num_line_glyphs; ++num_line_glyphs;
F_Glyph *glyph = font_get_glyph(params.font, codepoint); F_Glyph *glyph = F_GetGlyph(params.font, codepoint);
tg->off_x = glyph->off_x * params.scale; tg->off_x = glyph->off_x * params.scale;
tg->off_y = glyph->off_y * params.scale; tg->off_y = glyph->off_y * params.scale;
tg->width = glyph->width * params.scale; tg->width = glyph->width * params.scale;

View File

@ -1,83 +1,75 @@
#define LOOKUP_TABLE_SIZE (256) F_SharedState F_shared_state = ZI;
Global u32 g_font_codes[] = {
0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2A,0x2B,0x2C,0x2D,0x2E,0x2F,0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3A,0x3B,0x3C,0x3D,0x3E,0x3F,0x40,0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4A,0x4B,0x4C,0x4D,0x4E,0x4F,0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5A,0x5B,0x5C,0x5D,0x5E,0x5F,0x60,0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6A,0x6B,0x6C,0x6D,0x6E,0x6F,0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7A,0x7B,0x7C,0x7D,0x7E,0x7F,0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F,0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF,0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF,0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF
};
struct font_task_params { ////////////////////////////////
struct font_task_params *next_free; //~ Startup
AC_Asset *asset; F_StartupReceipt F_Startup(AC_StartupReceipt *asset_cache_sr, TTF_StartupReceipt *ttf_sr)
f32 point_size;
u64 path_len;
char path_cstr[1024];
};
struct font_task_params_store {
struct font_task_params *head_free;
Arena *arena;
P_Mutex mutex;
};
/* ========================== *
* Global state
* ========================== */
Global struct {
struct font_task_params_store params;
} G = ZI, DebugAlias(G, G_font);
/* ========================== *
* Startup
* ========================== */
F_StartupReceipt font_startup(AC_StartupReceipt *asset_cache_sr,
TTF_StartupReceipt *ttf_sr)
{ {
__prof; __prof;
F_SharedState *g = &F_shared_state;
(UNUSED)asset_cache_sr; (UNUSED)asset_cache_sr;
(UNUSED)ttf_sr; (UNUSED)ttf_sr;
G.params.arena = AllocArena(Gibi(64)); g->params.arena = AllocArena(Gibi(64));
return (F_StartupReceipt) { 0 }; return (F_StartupReceipt) { 0 };
} }
/* ========================== * ////////////////////////////////
* Load task param store //~ Load job
* ========================== */
internal struct font_task_params *font_task_params_alloc(void) F_LoadJobSig *F_AllocJobSig(void)
{ {
struct font_task_params *p = 0; F_SharedState *g = &F_shared_state;
F_LoadJobSig *p = 0;
{ {
P_Lock lock = P_LockE(&G.params.mutex); P_Lock lock = P_LockE(&g->params.mutex);
if (G.params.head_free) { if (g->params.head_free)
p = G.params.head_free; {
G.params.head_free = p->next_free; p = g->params.head_free;
} else { g->params.head_free = p->next_free;
p = PushStruct(G.params.arena, struct font_task_params); }
else
{
p = PushStruct(g->params.arena, F_LoadJobSig);
} }
P_Unlock(&lock); P_Unlock(&lock);
} }
return p; return p;
} }
internal void font_task_params_release(struct font_task_params *p) void F_ReleaseJobSig(F_LoadJobSig *p)
{ {
P_Lock lock = P_LockE(&G.params.mutex); F_SharedState *g = &F_shared_state;
p->next_free = G.params.head_free; P_Lock lock = P_LockE(&g->params.mutex);
G.params.head_free = p; p->next_free = g->params.head_free;
g->params.head_free = p;
P_Unlock(&lock); P_Unlock(&lock);
} }
/* ========================== * P_JobDef(F_LoadAssetJob, job)
* Load
* ========================== */
internal P_JobDef(font_load_asset_job, job)
{ {
__prof; __prof;
TempArena scratch = BeginScratchNoConflict(); TempArena scratch = BeginScratchNoConflict();
struct font_task_params *params = job.sig; Readonly LocalPersist u32 font_codes[] = {
0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2A,0x2B,0x2C,0x2D,
0x2E,0x2F,0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3A,0x3B,
0x3C,0x3D,0x3E,0x3F,0x40,0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x49,
0x4A,0x4B,0x4C,0x4D,0x4E,0x4F,0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57,
0x58,0x59,0x5A,0x5B,0x5C,0x5D,0x5E,0x5F,0x60,0x61,0x62,0x63,0x64,0x65,
0x66,0x67,0x68,0x69,0x6A,0x6B,0x6C,0x6D,0x6E,0x6F,0x70,0x71,0x72,0x73,
0x74,0x75,0x76,0x77,0x78,0x79,0x7A,0x7B,0x7C,0x7D,0x7E,0x7F,0x80,0x81,
0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,
0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,
0x9E,0x9F,0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,
0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,
0xBA,0xBB,0xBC,0xBD,0xBE,0xBF,0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,
0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,
0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF,0xE0,0xE1,0xE2,0xE3,
0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,
0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF
};
F_LoadJobSig *params = job.sig;
String path = STRING(params->path_len, (u8 *)params->path_cstr); String path = STRING(params->path_len, (u8 *)params->path_cstr);
f32 point_size = params->point_size; f32 point_size = params->point_size;
AC_Asset *asset = params->asset; AC_Asset *asset = params->asset;
@ -86,17 +78,18 @@ internal P_JobDef(font_load_asset_job, job)
i64 start_ns = P_TimeNs(); i64 start_ns = P_TimeNs();
Assert(StringEndsWith(path, Lit(".ttf"))); Assert(StringEndsWith(path, Lit(".ttf")));
Assert(countof(g_font_codes) < LOOKUP_TABLE_SIZE); Assert(countof(font_codes) < F_LookupTableSize);
/* Decode */ /* Decode */
R_Resource res = resource_open(path); R_Resource res = resource_open(path);
if (!resource_exists(&res)) { if (!resource_exists(&res))
{
/* FIME: Load baked font instead of panicking */ /* FIME: Load baked font instead of panicking */
P_Panic(StringFormat(scratch.arena, P_Panic(StringFormat(scratch.arena,
Lit("Font \"%F\" not found"), Lit("Font \"%F\" not found"),
FmtString(path))); FmtString(path)));
} }
TTF_Result result = ttf_decode(scratch.arena, resource_get_data(&res), point_size, g_font_codes, countof(g_font_codes)); TTF_Result result = ttf_decode(scratch.arena, resource_get_data(&res), point_size, font_codes, countof(font_codes));
resource_close(&res); resource_close(&res);
/* Send texture to GPU */ /* Send texture to GPU */
@ -108,7 +101,7 @@ internal P_JobDef(font_load_asset_job, job)
AC_Store store = AC_OpenStore(); AC_Store store = AC_OpenStore();
font = PushStruct(store.arena, F_Font); font = PushStruct(store.arena, F_Font);
font->glyphs = PushStructsNoZero(store.arena, F_Glyph, result.glyphs_count); font->glyphs = PushStructsNoZero(store.arena, F_Glyph, result.glyphs_count);
font->lookup = PushStructs(store.arena, u16, LOOKUP_TABLE_SIZE); font->lookup = PushStructs(store.arena, u16, F_LookupTableSize);
AC_CloseStore(&store); AC_CloseStore(&store);
} }
@ -120,7 +113,8 @@ internal P_JobDef(font_load_asset_job, job)
font->point_size = point_size; font->point_size = point_size;
/* FIXME: Load baked font instead of panicking */ /* FIXME: Load baked font instead of panicking */
if (font->glyphs_count <= 0) { if (font->glyphs_count <= 0)
{
P_Panic(StringFormat(scratch.arena, P_Panic(StringFormat(scratch.arena,
Lit("Parsed 0 glyphs from font \"%F\"!"), Lit("Parsed 0 glyphs from font \"%F\"!"),
FmtString(path))); FmtString(path)));
@ -131,12 +125,13 @@ internal P_JobDef(font_load_asset_job, job)
CopyBytes(font->glyphs, result.glyphs, sizeof(*font->glyphs) * result.glyphs_count); CopyBytes(font->glyphs, result.glyphs, sizeof(*font->glyphs) * result.glyphs_count);
/* Build lookup table */ /* Build lookup table */
for (u64 i = 0; i < countof(g_font_codes); ++i) { for (u64 i = 0; i < countof(font_codes); ++i)
u32 codepoint = g_font_codes[i]; {
u32 codepoint = font_codes[i];
font->lookup[codepoint] = result.cache_indices[i]; font->lookup[codepoint] = result.cache_indices[i];
} }
font_task_params_release(params); F_ReleaseJobSig(params);
P_LogSuccessF("Loaded font \"%F\" (point size %F) in %F seconds", FmtString(path), FmtFloat((f64)point_size), FmtFloat(SecondsFromNs(P_TimeNs() - start_ns))); P_LogSuccessF("Loaded font \"%F\" (point size %F) in %F seconds", FmtString(path), FmtFloat((f64)point_size), FmtFloat(SecondsFromNs(P_TimeNs() - start_ns)));
AC_MarkReady(asset, font); AC_MarkReady(asset, font);
@ -144,8 +139,11 @@ internal P_JobDef(font_load_asset_job, job)
EndScratch(scratch); EndScratch(scratch);
} }
////////////////////////////////
//~ Load
/* Returns the asset from the asset cache */ /* Returns the asset from the asset cache */
AC_Asset *font_load_asset(String path, f32 point_size, b32 wait) AC_Asset *F_LoadAsset(String path, f32 point_size, b32 wait)
{ {
__prof; __prof;
TempArena scratch = BeginScratchNoConflict(); TempArena scratch = BeginScratchNoConflict();
@ -159,10 +157,12 @@ AC_Asset *font_load_asset(String path, f32 point_size, b32 wait)
b32 is_first_touch; b32 is_first_touch;
AC_Asset *asset = AC_TouchCache(key, hash, &is_first_touch); AC_Asset *asset = AC_TouchCache(key, hash, &is_first_touch);
if (is_first_touch) { if (is_first_touch)
{
/* Assemble task params */ /* Assemble task params */
struct font_task_params *params = font_task_params_alloc(); F_LoadJobSig *params = F_AllocJobSig();
if (path.len > (sizeof(params->path_cstr) - 1)) { if (path.len > (sizeof(params->path_cstr) - 1))
{
P_Panic(StringFormat(scratch.arena, P_Panic(StringFormat(scratch.arena,
Lit("Font path \"%F\" too long!"), Lit("Font path \"%F\" too long!"),
FmtString(path))); FmtString(path)));
@ -174,8 +174,9 @@ AC_Asset *font_load_asset(String path, f32 point_size, b32 wait)
/* PushStruct task */ /* PushStruct task */
AC_MarkLoading(asset); AC_MarkLoading(asset);
P_Run(1, font_load_asset_job, params, P_Pool_Background, P_Priority_Low, 0); P_Run(1, F_LoadAssetJob, params, P_Pool_Background, P_Priority_Low, 0);
if (wait) { if (wait)
{
AC_WaitOnAssetReady(asset); AC_WaitOnAssetReady(asset);
} }
} }
@ -184,38 +185,42 @@ AC_Asset *font_load_asset(String path, f32 point_size, b32 wait)
return asset; return asset;
} }
F_Font *font_load_async(String path, f32 point_size) F_Font *F_LoadFontAsync(String path, f32 point_size)
{ {
__prof; __prof;
AC_Asset *asset = font_load_asset(path, point_size, 0); AC_Asset *asset = F_LoadAsset(path, 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 *font_load(String path, f32 point_size) F_Font *F_LoadFontWait(String path, f32 point_size)
{ {
__prof; __prof;
AC_Asset *asset = font_load_asset(path, point_size, 1); AC_Asset *asset = F_LoadAsset(path, point_size, 1);
AC_WaitOnAssetReady(asset); AC_WaitOnAssetReady(asset);
F_Font *f = (F_Font *)AC_DataFromStore(asset); F_Font *f = (F_Font *)AC_DataFromStore(asset);
return f; return f;
} }
/* ========================== * ////////////////////////////////
* Other //~ Get glyph
* ========================== */
F_Glyph *font_get_glyph(F_Font *font, u32 codepoint) F_Glyph *F_GetGlyph(F_Font *font, u32 codepoint)
{
if (codepoint < F_LookupTableSize)
{ {
if (codepoint < LOOKUP_TABLE_SIZE) {
u16 index = font->lookup[codepoint]; u16 index = font->lookup[codepoint];
if (index < font->glyphs_count) { if (index < font->glyphs_count)
{
return &font->glyphs[index]; return &font->glyphs[index];
} }
} }
if (codepoint == '?') { if (codepoint == '?')
{
return &font->glyphs[font->lookup[0]]; return &font->glyphs[font->lookup[0]];
} else { }
else
{
return &font->glyphs[font->lookup['?']]; return &font->glyphs[font->lookup['?']];
} }
} }

View File

@ -1,3 +1,6 @@
////////////////////////////////
//~ Font types
Struct(F_Glyph) { Struct(F_Glyph) {
f32 off_x; f32 off_x;
f32 off_y; f32 off_y;
@ -17,11 +20,60 @@ Struct(F_Font) {
u16 *lookup; u16 *lookup;
}; };
////////////////////////////////
//~ Font job types
Struct(F_LoadJobSig)
{
F_LoadJobSig *next_free;
AC_Asset *asset;
f32 point_size;
u64 path_len;
char path_cstr[1024];
};
Struct(F_LoadJobSigStore)
{
F_LoadJobSig *head_free;
Arena *arena;
P_Mutex mutex;
};
/* ========================== *
* Global state
* ========================== */
#define F_LookupTableSize (256)
Struct(F_SharedState)
{
F_LoadJobSigStore params;
};
extern F_SharedState F_shared_state;
////////////////////////////////
//~ Startup
Struct(F_StartupReceipt) { i32 _; }; Struct(F_StartupReceipt) { i32 _; };
F_StartupReceipt font_startup(AC_StartupReceipt *asset_cache_sr, TTF_StartupReceipt *ttf_sr); F_StartupReceipt F_Startup(AC_StartupReceipt *asset_cache_sr, TTF_StartupReceipt *ttf_sr);
AC_Asset *font_load_asset(String path, f32 point_size, b32 wait); ////////////////////////////////
F_Font *font_load_async(String path, f32 point_size); //~ Font load job
F_Font *font_load(String path, f32 point_size);
F_Glyph *font_get_glyph(F_Font *font, u32 codepoint); F_LoadJobSig *F_AllocJobSig(void);
void F_ReleaseJobSig(F_LoadJobSig *p);
P_JobDef(F_LoadAssetJob, job);
////////////////////////////////
//~ Font load operations
AC_Asset *F_LoadAsset(String path, f32 point_size, b32 wait);
F_Font *F_LoadFontAsync(String path, f32 point_size);
F_Font *F_LoadFontWait(String path, f32 point_size);
////////////////////////////////
//~ Font data operations
F_Glyph *F_GetGlyph(F_Font *font, u32 codepoint);

View File

@ -447,7 +447,7 @@ internal void draw_debug_console(i32 level, b32 minimized)
G.console_logs_height = 0; G.console_logs_height = 0;
i64 now_ns = P_TimeNs(); i64 now_ns = P_TimeNs();
F_Font *font = font_load_async(Lit("font/fixedsys.ttf"), 12.0f); F_Font *font = F_LoadFontAsync(Lit("font/fixedsys.ttf"), 12.0f);
if (font) { if (font) {
P_Lock lock = P_LockE(&G.console_logs_mutex); P_Lock lock = P_LockE(&G.console_logs_mutex);
{ {
@ -1436,7 +1436,7 @@ internal void user_update(P_Window *window)
#if 0 #if 0
/* Draw contact info */ /* Draw contact info */
{ {
F_Font *disp_font = font_load_async(Lit("font/fixedsys.ttf"), 12.0f); F_Font *disp_font = F_LoadFontAsync(Lit("font/fixedsys.ttf"), 12.0f);
if (disp_font) { if (disp_font) {
f32 offset_px = 10; f32 offset_px = 10;
@ -1553,7 +1553,7 @@ internal void user_update(P_Window *window)
#if 0 #if 0
/* Test info */ /* Test info */
{ {
F_Font *disp_font = font_load_async(Lit("font/fixedsys.ttf"), 12.0f); F_Font *disp_font = F_LoadFontAsync(Lit("font/fixedsys.ttf"), 12.0f);
if (disp_font) { if (disp_font) {
f32 offset_px = 10; f32 offset_px = 10;
String fmt = Lit( String fmt = Lit(
@ -1902,7 +1902,7 @@ internal void user_update(P_Window *window)
Ent *ent = hovered_ent; Ent *ent = hovered_ent;
Vec2 pos = AddVec2(G.ui_cursor, VEC2(15, 15)); Vec2 pos = AddVec2(G.ui_cursor, VEC2(15, 15));
F_Font *font = font_load_async(Lit("font/fixedsys.ttf"), 12.0f); F_Font *font = F_LoadFontAsync(Lit("font/fixedsys.ttf"), 12.0f);
if (font) { if (font) {
TempArena temp = BeginTempArena(scratch.arena); TempArena temp = BeginTempArena(scratch.arena);
@ -1927,7 +1927,7 @@ internal void user_update(P_Window *window)
if (G.debug_draw) { if (G.debug_draw) {
__profn("Draw debug info"); __profn("Draw debug info");
F_Font *font = font_load_async(Lit("font/fixedsys.ttf"), 12.0f); F_Font *font = F_LoadFontAsync(Lit("font/fixedsys.ttf"), 12.0f);
if (font) { if (font) {
TempArena temp = BeginTempArena(scratch.arena); TempArena temp = BeginTempArena(scratch.arena);
String text = ZI; String text = ZI;