227 lines
7.2 KiB
C
227 lines
7.2 KiB
C
F_SharedState F_shared_state = ZI;
|
|
|
|
////////////////////////////////
|
|
//~ Startup
|
|
|
|
F_StartupReceipt F_Startup(AC_StartupReceipt *asset_cache_sr, TTF_StartupReceipt *ttf_sr)
|
|
{
|
|
__prof;
|
|
F_SharedState *g = &F_shared_state;
|
|
LAX asset_cache_sr;
|
|
LAX ttf_sr;
|
|
g->params.arena = AllocArena(Gibi(64));
|
|
return (F_StartupReceipt) { 0 };
|
|
}
|
|
|
|
////////////////////////////////
|
|
//~ Load job
|
|
|
|
F_LoadJobSig *F_AllocJobSig(void)
|
|
{
|
|
F_SharedState *g = &F_shared_state;
|
|
F_LoadJobSig *p = 0;
|
|
{
|
|
P_Lock lock = P_LockE(&g->params.mutex);
|
|
if (g->params.head_free)
|
|
{
|
|
p = g->params.head_free;
|
|
g->params.head_free = p->next_free;
|
|
}
|
|
else
|
|
{
|
|
p = PushStruct(g->params.arena, F_LoadJobSig);
|
|
}
|
|
P_Unlock(&lock);
|
|
}
|
|
return p;
|
|
}
|
|
|
|
void F_ReleaseJobSig(F_LoadJobSig *p)
|
|
{
|
|
F_SharedState *g = &F_shared_state;
|
|
P_Lock lock = P_LockE(&g->params.mutex);
|
|
p->next_free = g->params.head_free;
|
|
g->params.head_free = p;
|
|
P_Unlock(&lock);
|
|
}
|
|
|
|
P_JobDef(F_LoadAssetJob, job)
|
|
{
|
|
__prof;
|
|
TempArena scratch = BeginScratchNoConflict();
|
|
|
|
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);
|
|
f32 point_size = params->point_size;
|
|
AC_Asset *asset = params->asset;
|
|
|
|
P_LogInfoF("Loading font \"%F\" (point size %F)", FmtString(path), FmtFloat((f64)point_size));
|
|
i64 start_ns = P_TimeNs();
|
|
|
|
Assert(StringEndsWith(path, Lit(".ttf")));
|
|
Assert(countof(font_codes) < F_LookupTableSize);
|
|
|
|
/* Decode */
|
|
RES_Resource res = RES_OpenResource(path);
|
|
if (!RES_ResourceExists(&res))
|
|
{
|
|
/* FIME: Load baked font instead of panicking */
|
|
P_Panic(StringFormat(scratch.arena,
|
|
Lit("Font \"%F\" not found"),
|
|
FmtString(path)));
|
|
}
|
|
TTF_Result result = TTF_Decode(scratch.arena, RES_GetResourceData(&res), point_size, font_codes, countof(font_codes));
|
|
RES_CloseResource(&res);
|
|
|
|
/* Send texture to GPU */
|
|
GPU_Resource *texture = GPU_AllocTexture(GP_TEXTURE_FORMAT_R8G8B8A8_UNORM, 0, VEC2I32(result.image_width, result.image_height), result.image_pixels);
|
|
|
|
/* Allocate store memory */
|
|
F_Font *font = 0;
|
|
{
|
|
AC_Store store = AC_OpenStore();
|
|
font = PushStruct(store.arena, F_Font);
|
|
font->glyphs = PushStructsNoZero(store.arena, F_Glyph, result.glyphs_count);
|
|
font->lookup = PushStructs(store.arena, u16, F_LookupTableSize);
|
|
AC_CloseStore(&store);
|
|
}
|
|
|
|
/* Set font data */
|
|
font->texture = texture;
|
|
font->image_width = result.image_width;
|
|
font->image_height = result.image_height;
|
|
font->glyphs_count = result.glyphs_count;
|
|
font->point_size = point_size;
|
|
|
|
/* FIXME: Load baked font instead of panicking */
|
|
if (font->glyphs_count <= 0)
|
|
{
|
|
P_Panic(StringFormat(scratch.arena,
|
|
Lit("Parsed 0 glyphs from font \"%F\"!"),
|
|
FmtString(path)));
|
|
}
|
|
|
|
/* CopyStruct glyphs from decode result */
|
|
StaticAssert(sizeof(*font->glyphs) == sizeof(*result.glyphs)); /* Font glyph size must match TTF glyph size for memcpy */
|
|
CopyBytes(font->glyphs, result.glyphs, sizeof(*font->glyphs) * result.glyphs_count);
|
|
|
|
/* Build lookup table */
|
|
for (u64 i = 0; i < countof(font_codes); ++i)
|
|
{
|
|
u32 codepoint = font_codes[i];
|
|
font->lookup[codepoint] = result.cache_indices[i];
|
|
}
|
|
|
|
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)));
|
|
AC_MarkReady(asset, font);
|
|
|
|
EndScratch(scratch);
|
|
}
|
|
|
|
////////////////////////////////
|
|
//~ Load
|
|
|
|
/* Returns the asset from the asset cache */
|
|
AC_Asset *F_LoadAsset(String path, f32 point_size, b32 wait)
|
|
{
|
|
__prof;
|
|
TempArena scratch = BeginScratchNoConflict();
|
|
|
|
/* Concatenate point_size to path for key */
|
|
String key = StringFormat(scratch.arena,
|
|
Lit("%F%F_font"),
|
|
FmtString(path),
|
|
FmtFloatP((f64)point_size, 1));
|
|
u64 hash = AC_HashFromKey(key);
|
|
b32 is_first_touch;
|
|
AC_Asset *asset = AC_TouchCache(key, hash, &is_first_touch);
|
|
|
|
if (is_first_touch)
|
|
{
|
|
/* Assemble task params */
|
|
F_LoadJobSig *params = F_AllocJobSig();
|
|
if (path.len > (sizeof(params->path_cstr) - 1))
|
|
{
|
|
P_Panic(StringFormat(scratch.arena,
|
|
Lit("Font path \"%F\" too long!"),
|
|
FmtString(path)));
|
|
}
|
|
CstrBuffFromStringToBuff(StringFromArray(params->path_cstr), path);
|
|
params->path_len = path.len;
|
|
params->asset = asset;
|
|
params->point_size = point_size;
|
|
|
|
/* PushStruct task */
|
|
AC_MarkLoading(asset);
|
|
P_Run(1, F_LoadAssetJob, params, P_Pool_Background, P_Priority_Low, 0);
|
|
if (wait)
|
|
{
|
|
AC_WaitOnAssetReady(asset);
|
|
}
|
|
}
|
|
|
|
EndScratch(scratch);
|
|
return asset;
|
|
}
|
|
|
|
F_Font *F_LoadFontAsync(String path, f32 point_size)
|
|
{
|
|
__prof;
|
|
AC_Asset *asset = F_LoadAsset(path, point_size, 0);
|
|
F_Font *f = (F_Font *)AC_DataFromStore(asset);
|
|
return f;
|
|
}
|
|
|
|
F_Font *F_LoadFontWait(String path, f32 point_size)
|
|
{
|
|
__prof;
|
|
AC_Asset *asset = F_LoadAsset(path, point_size, 1);
|
|
AC_WaitOnAssetReady(asset);
|
|
F_Font *f = (F_Font *)AC_DataFromStore(asset);
|
|
return f;
|
|
}
|
|
|
|
////////////////////////////////
|
|
//~ Get glyph
|
|
|
|
F_Glyph *F_GetGlyph(F_Font *font, u32 codepoint)
|
|
{
|
|
if (codepoint < F_LookupTableSize)
|
|
{
|
|
u16 index = font->lookup[codepoint];
|
|
if (index < font->glyphs_count)
|
|
{
|
|
return &font->glyphs[index];
|
|
}
|
|
}
|
|
if (codepoint == '?')
|
|
{
|
|
return &font->glyphs[font->lookup[0]];
|
|
}
|
|
else
|
|
{
|
|
return &font->glyphs[font->lookup['?']];
|
|
}
|
|
}
|