glyph cache progress

This commit is contained in:
jacob 2025-12-11 19:35:11 -06:00
parent ad359c8495
commit e9e8dcc04d
19 changed files with 516 additions and 363 deletions

View File

@ -94,46 +94,36 @@ void *PushBytesNoZero(Arena *arena, u64 size, u64 align)
{
Assert(align > 0);
void *result = 0;
u8 *base = ArenaFirst(arena, u8);
/* Check to avoid aligning when size = 0 */
if (size > 0)
{
u64 aligned_start_pos = (arena->pos + (align - 1));
aligned_start_pos -= aligned_start_pos % align;
u64 start_pos = AlignU64(arena->pos, align);
u64 end_pos = start_pos + size;
u64 new_pos = aligned_start_pos + size;
if (new_pos > arena->committed)
/* Commit new block(s) */
if (end_pos > arena->committed)
{
u64 blocks_needed = (end_pos - arena->committed + ArenaBlockSize - 1) / ArenaBlockSize;
u64 commit_bytes = blocks_needed * ArenaBlockSize;
u64 new_capacity = arena->committed + commit_bytes;
if (new_capacity > arena->reserved)
{
/* Commit new block(s) */
u64 blocks_needed = (new_pos - arena->committed + ArenaBlockSize - 1) / ArenaBlockSize;
u64 commit_bytes = blocks_needed * ArenaBlockSize;
u64 new_capacity = arena->committed + commit_bytes;
if (new_capacity > arena->reserved)
{
/* Hard fail if we overflow reserved memory for now */
Panic(Lit("Failed to commit new memory block: Overflow of reserved memory"));
}
void *commit_address = base + arena->committed;
if (!CommitMemory(commit_address, commit_bytes))
{
/* Hard fail on memory allocation failure for now */
Panic(Lit("Failed to commit new memory block: System may be out of memory"));
}
arena->committed += commit_bytes;
AddGstat(GSTAT_MEMORY_COMMITTED, commit_bytes);
AsanPoison(commit_address, commit_bytes);
/* Hard fail if we overflow reserved memory for now */
Panic(Lit("Failed to commit new memory block: Overflow of reserved memory"));
}
void *commit_address = base + arena->committed;
if (!CommitMemory(commit_address, commit_bytes))
{
/* Hard fail on memory allocation failure for now */
Panic(Lit("Failed to commit new memory block: System may be out of memory"));
}
arena->committed += commit_bytes;
AddGstat(GSTAT_MEMORY_COMMITTED, commit_bytes);
AsanPoison(commit_address, commit_bytes);
}
result = base + aligned_start_pos;
AsanUnpoison(result, new_pos - aligned_start_pos);
arena->pos = new_pos;
}
else
{
result = base + arena->pos;
}
void *result = base + start_pos;
AsanUnpoison(result, end_pos - start_pos);
arena->pos = end_pos;
return result;
}
@ -147,28 +137,8 @@ void *PushBytes(Arena *arena, u64 size, u64 align)
void *PushAlign(Arena *arena, u64 align)
{
void *result = 0;
if (align > 0)
{
u64 aligned_start_pos = (arena->pos + (align - 1));
aligned_start_pos -= aligned_start_pos % align;
u64 align_bytes = aligned_start_pos - (u64)arena->pos;
if (align_bytes > 0)
{
result = (void *)PushStructsNoZero(arena, u8, align_bytes);
}
else
{
result = (void *)(ArenaFirst(arena, u8) + arena->pos);
}
}
else
{
/* 0 alignment */
Assert(0);
result = (void *)(ArenaFirst(arena, u8) + arena->pos);
}
return result;
u64 push_count = AlignU64(arena->pos, align) - arena->pos;
return PushStructsNoZero(arena, u8, push_count);
}
void PopTo(Arena *arena, u64 pos)
@ -198,17 +168,13 @@ void PopBytes(Arena *arena, u64 size, void *copy_dst)
void *ArenaFirst_(Arena *arena, u64 align)
{
u64 aligned_start_pos = align - 1;
aligned_start_pos -= aligned_start_pos % align;
void *result = ((u8 *)arena + ArenaHeaderSize) + aligned_start_pos;
void *result = (void *)AlignU64((u64)arena + ArenaHeaderSize, align);
return result;
}
void *ArenaNext_(Arena *arena, u64 align)
{
u64 aligned_start_pos = (arena->pos + (align - 1));
aligned_start_pos -= aligned_start_pos % align;
void *result = ((u8 *)arena + ArenaHeaderSize) + aligned_start_pos;
void *result = (void *)AlignU64((u64)arena + ArenaHeaderSize + arena->pos, align);
return result;
}
@ -233,7 +199,7 @@ void EndTempArena(TempArena temp)
TempArena BeginScratch(Arena *potential_conflict)
{
/* This function is currently hard-coded to search through 2 scratch arenas */
/* This function is currently hard-coded for 2 thread-local scratch arenas */
StaticAssert(ScratchArenasPerCtx == 2);
/* Use `BeginScratchNoConflict` if no conflicts are present */

View File

@ -1,7 +1,7 @@
/* TODO: Elminiate meta arena. Just store levels in first 4096 bytes of buddy arena, and then zone header data at the beginning of each allocation. */
////////////////////////////////////////////////////////////
//~ Buddy ctx
//~ Buddy context
BuddyCtx *AcquireBuddyCtx(u64 reserve)
{
@ -31,105 +31,7 @@ void ReleaseBuddyCtx(BuddyCtx *ctx)
}
////////////////////////////////////////////////////////////
//~ Block acquire / release
BuddyBlock *AcquireBuddyBlock(BuddyCtx *ctx, u64 size)
{
if (size > 0x00FFFFFFFFFFFFFFULL)
{
/* TODO: Error */
Assert(0);
}
/* TODO: Minimum block size */
/* TODO: Faster MSB calculation */
u64 desired_block_size = 1;
u64 desired_level_tier = 0;
while (desired_block_size < size && desired_level_tier < 64)
{
desired_block_size <<= 1;
++desired_level_tier;
}
BuddyBlock *block = GetUnusedBuddyBlock(ctx, &ctx->levels[desired_level_tier]);
return block;
}
void ReleaseBuddyBlock(BuddyBlock *block)
{
block->is_used = 0;
BuddyLevel *level = block->level;
BuddyBlock *parent = block->parent;
BuddyBlock *sibling = block->sibling;
if (!sibling->is_used && parent != 0)
{
/* Merge siblings */
BuddyCtx *ctx = level->ctx;
PopBuddyBlock(ctx, level, block);
PopBuddyBlock(ctx, level, sibling);
/* Release parent */
ReleaseBuddyBlock(parent);
}
else
{
if (level->first_unused_block)
{
block->next = level->first_unused_block;
level->first_unused_block->prev = block;
}
level->first_unused_block = block;
}
}
////////////////////////////////////////////////////////////
//~ Block push / pop
//- Push
BuddyBlock *PushBuddyBlock(BuddyCtx *ctx)
{
BuddyBlock *block;
if (ctx->first_free_block)
{
block = ctx->first_free_block;
ctx->first_free_block = block->next;
}
else
{
block = PushStructNoZero(ctx->meta_arena, BuddyBlock);
}
ZeroStruct(block);
return block;
}
//- Pop
void PopBuddyBlock(BuddyCtx *ctx, BuddyLevel *level, BuddyBlock *block)
{
/* Remove from unused list */
{
BuddyBlock *prev = block->prev;
BuddyBlock *next = block->next;
if (prev)
{
prev->next = next;
}
else
{
level->first_unused_block = next;
}
if (next)
{
next->prev = prev;
}
}
block->next = ctx->first_free_block;
ctx->first_free_block = block;
}
////////////////////////////////////////////////////////////
//~ Get unused block
//~ Buddy block
BuddyBlock *GetUnusedBuddyBlock(BuddyCtx *ctx, BuddyLevel *level)
{
@ -212,3 +114,93 @@ BuddyBlock *GetUnusedBuddyBlock(BuddyCtx *ctx, BuddyLevel *level)
return block;
}
BuddyBlock *PushBuddyBlock(BuddyCtx *ctx)
{
BuddyBlock *block;
if (ctx->first_free_block)
{
block = ctx->first_free_block;
ctx->first_free_block = block->next;
}
else
{
block = PushStructNoZero(ctx->meta_arena, BuddyBlock);
}
ZeroStruct(block);
return block;
}
void PopBuddyBlock(BuddyCtx *ctx, BuddyLevel *level, BuddyBlock *block)
{
/* Remove from unused list */
{
BuddyBlock *prev = block->prev;
BuddyBlock *next = block->next;
if (prev)
{
prev->next = next;
}
else
{
level->first_unused_block = next;
}
if (next)
{
next->prev = prev;
}
}
block->next = ctx->first_free_block;
ctx->first_free_block = block;
}
BuddyBlock *AcquireBuddyBlock(BuddyCtx *ctx, u64 size)
{
if (size > 0x00FFFFFFFFFFFFFFULL)
{
/* TODO: Error */
Assert(0);
}
/* TODO: Minimum block size */
/* TODO: Faster MSB calculation */
u64 desired_block_size = 1;
u64 desired_level_tier = 0;
while (desired_block_size < size && desired_level_tier < 64)
{
desired_block_size <<= 1;
++desired_level_tier;
}
BuddyBlock *block = GetUnusedBuddyBlock(ctx, &ctx->levels[desired_level_tier]);
return block;
}
void ReleaseBuddyBlock(BuddyBlock *block)
{
block->is_used = 0;
BuddyLevel *level = block->level;
BuddyBlock *parent = block->parent;
BuddyBlock *sibling = block->sibling;
if (!sibling->is_used && parent != 0)
{
/* Merge siblings */
BuddyCtx *ctx = level->ctx;
PopBuddyBlock(ctx, level, block);
PopBuddyBlock(ctx, level, sibling);
/* Release parent */
ReleaseBuddyBlock(parent);
}
else
{
if (level->first_unused_block)
{
block->next = level->first_unused_block;
level->first_unused_block->prev = block;
}
level->first_unused_block = block;
}
}

View File

@ -44,13 +44,13 @@ void ReleaseBuddyCtx(BuddyCtx *ctx);
////////////////////////////////////////////////////////////
//~ Buddy block
//- Acquire / release
BuddyBlock *AcquireBuddyBlock(BuddyCtx *ctx, u64 size);
void ReleaseBuddyBlock(BuddyBlock *block);
//- Get unused
BuddyBlock *GetUnusedBuddyBlock(BuddyCtx *ctx, BuddyLevel *level);
//- Push / pop
BuddyBlock *PushBuddyBlock(BuddyCtx *ctx);
void PopBuddyBlock(BuddyCtx *ctx, BuddyLevel *level, BuddyBlock *block);
//- Get unused
BuddyBlock *GetUnusedBuddyBlock(BuddyCtx *ctx, BuddyLevel *level);
//- Acquire / release
BuddyBlock *AcquireBuddyBlock(BuddyCtx *ctx, u64 size);
void ReleaseBuddyBlock(BuddyBlock *block);

View File

@ -1148,6 +1148,43 @@ u32 U32FromVec4(Vec4 v)
return result;
}
////////////////////////////////////////////////////////////
//~ Range
Vec2 DimsFromRng2(Rng2 r)
{
Vec2 result = ZI;
result.x = r.p1.x - r.p0.x;
result.y = r.p1.y - r.p0.y;
return result;
}
Rng2 UnionRng2(Rng2 a, Rng2 b)
{
Rng2 result = ZI;
result.p0.x = MinF32(a.p0.x, b.p0.x);
result.p0.y = MinF32(a.p0.y, b.p0.y);
result.p1.x = MaxF32(a.p1.x, b.p1.x);
result.p1.y = MaxF32(a.p1.y, b.p1.y);
return result;
}
Rng2 AddRng2Vec2(Rng2 r, Vec2 v)
{
Rng2 result = ZI;
result.p0 = AddVec2(result.p0, v);
result.p1 = AddVec2(result.p1, v);
return result;
}
Rng2 DivRng2Vec2(Rng2 r, Vec2 v)
{
Rng2 result = ZI;
result.p0 = DivVec2Vec2(result.p0, v);
result.p1 = DivVec2Vec2(result.p1, v);
return result;
}
////////////////////////////////////////////////////////////
//~ Xform

View File

@ -385,6 +385,15 @@ Vec2I32 SubVec2I32(Vec2I32 a, Vec2I32 b);
Vec4 Vec4FromU32(u32 v);
u32 U32FromVec4(Vec4 v);
////////////////////////////////////////////////////////////
//~ Range
Vec2 DimsFromRng2(Rng2 r);
Rng2 UnionRng2(Rng2 a, Rng2 b);
Rng2 AddRng2Vec2(Rng2 r, Vec2 v);
Rng2 DivRng2Vec2(Rng2 a, Vec2 v);
////////////////////////////////////////////////////////////
//~ Xform

View File

@ -1,5 +1,5 @@
////////////////////////////////////////////////////////////
//~ Math types
//~ Base math types
#define Pi ((f32)3.14159265358979323846)
#define Tau ((f32)6.28318530717958647693)
@ -21,6 +21,21 @@ typedef float4 Aabb;
typedef float4 Quad;
typedef float4x4 Mat4x4;
////////////////////////////////////////////////////////////
//~ Range types
Struct(Rng) { f32 min; f32 max; };
Struct(RngI32) { i32 min; i32 max; };
Struct(RngU32) { u32 min; u32 max; };
Struct(Rng2) { Vec2 p0; Vec2 p1; };
Struct(Rng2I32) { Vec2I32 p0; Vec2I32 p1; };
Struct(Rng2U32) { Vec2U32 p0; Vec2U32 p1; };
Struct(Rng3) { Vec3 p0; Vec3 p1; };
Struct(Rng3I32) { Vec3I32 p0; Vec3I32 p1; };
Struct(Rng3U32) { Vec3U32 p0; Vec3U32 p1; };
////////////////////////////////////////////////////////////
//~ Countof

View File

@ -69,7 +69,7 @@
#define FLOOD_DEBUG 0
#define GPU_DEBUG 1
#define GPU_DEBUG 0
#define GPU_DEBUG_VALIDATION 0
#define GPU_SHADER_PRINT 1

View File

@ -1,12 +1,12 @@
/* TODO: Use default font texture handle */
Readonly GC_RunRect GC_NilRunRect = {
0
};
GC_State GC = ZI;
Readonly GC_Run GC_NilRun = {
.count = 1,
.rects = &GC_NilRunRect
};
////////////////////////////////////////////////////////////
//~ Bootstrap
void GC_BootStrap(void)
{
// OnAsync(GC_AsyncTick);
}
////////////////////////////////////////////////////////////
//~ Key helpers
@ -18,22 +18,119 @@ GC_FontKey GC_FontKeyFromResource(ResourceKey resource)
return result;
}
u64 GC_HashFromGlyphDesc(GC_GlyphDesc desc)
{
return RandU64FromSeeds(desc.font.v, ((u64)desc.codepoint << 32) | *(u32 *)&desc.font_size);
}
////////////////////////////////////////////////////////////
//~ Run
GC_Run *GC_RunFromString(Arena *arena, GC_FontKey key, String str)
/* TODO: Thread-local cache */
GC_Run GC_RunFromString(Arena *arena, String str, GC_FontKey font, f32 font_size)
{
/* TODO */
GC_Run *result = PushStruct(arena, GC_Run);
GC_Run result = ZI;
TempArena scratch = BeginScratch(arena);
GC_RunRect *rr = PushStruct(arena, GC_RunRect);
rr->advance = 1;
result->rects = rr;
result->count = 1;
String32 codepoints = String32FromString(scratch.arena, str);
String32 uncached_codepoints = ZI;
uncached_codepoints.text = PushStructsNoZero(scratch.arena, u32, codepoints.len);
u64 ready_glyphs_count = 0;
GC_Glyph **ready_glyphs = PushStructsNoZero(scratch.arena, GC_Glyph *, str.len);
{
if (codepoints.len > 0)
{
Lock lock = LockS(&GC.glyphs_mutex);
for (u64 codepoint_idx = 0; codepoint_idx < codepoints.len; ++codepoint_idx)
{
u32 codepoint = codepoints.text[codepoint_idx];
GC_GlyphDesc desc = ZI;
desc.font = font;
desc.font_size = font_size;
desc.codepoint = codepoint;
u64 hash = GC_HashFromGlyphDesc(desc);
GC_GlyphBin *bin = &GC.glyph_bins[hash % countof(GC.glyph_bins)];
GC_Glyph *glyph = bin->first;
for (; glyph; glyph = glyph->next)
{
if (glyph->hash == hash)
{
break;
}
}
if (glyph == 0)
{
uncached_codepoints.text[uncached_codepoints.len] = codepoint;
uncached_codepoints.len += 1;
}
else
{
ready_glyphs[ready_glyphs_count] = glyph;
ready_glyphs_count += 1;
}
}
Unlock(&lock);
}
}
f32 baseline_pos = 0;
result.rects = PushStructs(arena, GC_RunRect, ready_glyphs_count);
result.rects_count = ready_glyphs_count;
for (u64 glyph_idx = 0; glyph_idx < ready_glyphs_count; ++glyph_idx)
{
GC_Glyph *glyph = ready_glyphs[glyph_idx];
GC_RunRect *rect = &result.rects[glyph_idx];
rect->tex = glyph->tex_ref;
rect->tex_uv = glyph->tex_uv;
rect->tex_rect = glyph->tex_rect;
rect->baseline_pos = baseline_pos;
rect->advance = glyph->advance;
rect->bounds.p0 = glyph->baseline_offset;
rect->bounds.p1 = AddVec2(rect->bounds.p0, DimsFromRng2(glyph->tex_rect));
if (glyph_idx == 0)
{
result.bounds = rect->bounds;
}
else
{
result.bounds = UnionRng2(result.bounds, rect->bounds);
}
baseline_pos += rect->advance;
result.baseline_length = MaxF32(result.baseline_length, baseline_pos);
}
if (ready_glyphs_count > 0)
{
GC_Glyph *glyph = ready_glyphs[0];
result.font_size = glyph->font_size;
result.font_ascent = glyph->font_ascent;
result.font_descent = glyph->font_descent;
result.font_cap = glyph->font_cap;
}
result.ready = uncached_codepoints.len == 0;
EndScratch(scratch);
return result;
}
////////////////////////////////////////////////////////////
//~ Async tick
void GC_AsyncTick(WaveLaneCtx *lane)
{
}

View File

@ -6,50 +6,102 @@ Struct(GC_FontKey)
u64 v;
};
////////////////////////////////////////////////////////////
//~ Glyph types
Struct(GC_GlyphDesc)
{
GC_FontKey font;
f32 font_size;
u32 codepoint;
};
Struct(GC_Glyph)
{
GC_Glyph *next;
u64 hash;
/* Layout info */
Vec2 baseline_offset;
f32 advance;
/* Atlas info */
G_Texture2DRef tex_ref;
Rng2 tex_uv;
Rng2 tex_rect;
/* Font info */
f32 font_size;
f32 font_ascent;
f32 font_descent;
f32 font_cap;
};
Struct(GC_GlyphBin)
{
GC_Glyph *first;
};
////////////////////////////////////////////////////////////
//~ Run types
Struct(GC_RunRect)
{
Vec2 offset; /* Vector from baseline offset to top left of glyph rect */
f32 pos; /* Horizontal distance from start of baseline */
Rng2 bounds; /* Visual bounds in relation to the baseline */
f32 baseline_pos; /* Horizontal distance from start of baseline */
f32 advance;
Vec2 size;
G_Texture2DRef tex;
Rng2 uv;
Rng2 tex_uv;
Rng2 tex_rect;
};
Struct(GC_Run)
{
/* Run data */
Vec2 p0; /* Start of baseline to top-left-most rect */
Vec2 p1; /* Start of baseline to bottom-right-most rect corner */
u32 count;
Rng2 bounds; /* Visual bounds of the run in relation to the baseline */
f32 baseline_length;
u64 rects_count;
GC_RunRect *rects;
/* Font stats */
f32 size;
f32 ascent;
f32 descent;
f32 cap;
/* Font info */
f32 font_size;
f32 font_ascent;
f32 font_descent;
f32 font_cap;
b32 loaded;
b32 ready;
};
extern Readonly GC_RunRect GC_NilRunRect;
extern Readonly GC_Run GC_NilRun;
////////////////////////////////////////////////////////////
//~ State
Struct(GC_State)
{
Mutex glyphs_mutex;
GC_GlyphBin glyph_bins[16384];
} extern GC;
////////////////////////////////////////////////////////////
//~ Bootstrap
void GC_Bootstrap(void);
////////////////////////////////////////////////////////////
//~ Key helpers
GC_FontKey GC_FontKeyFromResource(ResourceKey resource);
u64 GC_HashFromGlyphDesc(GC_GlyphDesc desc);
////////////////////////////////////////////////////////////
//~ Run
GC_Run *GC_RunFromString(Arena *arena, GC_FontKey key, String str);
GC_Run GC_RunFromString(Arena *arena, String str, GC_FontKey font, f32 font_size);
////////////////////////////////////////////////////////////
//~ Async tick
void GC_AsyncTick(WaveLaneCtx *lane);

View File

@ -317,6 +317,7 @@ void G_Bootstrap(void)
}
}
}
}
//////////////////////////////

View File

@ -249,7 +249,7 @@ void V_TickForever(WaveLaneCtx *lane)
{
/* TODO: Don't rely on ui report for draw size since it introduces one frame of delay when resizing */
UI_Report vis_rep = UI_ReportFromKey(vis_box);
draw_size = Vec2I32FromFields(SubVec2(vis_rep.screen_p1, vis_rep.screen_p0));
draw_size = Vec2I32FromVec(DimsFromRng2(vis_rep.screen_rect));
}
draw_size.x = MaxI32(draw_size.x, 1);
draw_size.y = MaxI32(draw_size.y, 1);
@ -624,9 +624,11 @@ void V_TickForever(WaveLaneCtx *lane)
G_DumbMemoryLayoutSync(frame->cl, draw_target, G_Layout_DirectQueue_ShaderRead);
{
Vec2 uv0 = DivVec2Vec2(Vec2FromVec(viewport.p0), Vec2FromVec(window_frame.monitor_size));
Vec2 uv1 = DivVec2Vec2(Vec2FromVec(viewport.p1), Vec2FromVec(window_frame.monitor_size));
UI_SetRawTexture(vis_box, draw_target_ro, uv0, uv1);
Rng2 uv = ZI;
uv.p0 = Vec2FromVec(viewport.p0);
uv.p1 = Vec2FromVec(viewport.p1);
uv = DivRng2Vec2(uv, Vec2FromVec(window_frame.monitor_size));
UI_SetRawTexture(vis_box, draw_target_ro, uv);
}
}

View File

@ -53,7 +53,7 @@ PixelShader(V_DQuadPS, V_DQuadPSOutput, V_DQuadPSInput input)
Vec4 final_color = 0;
V_DQuadPSOutput output;
output.SV_Target0 = final_color;
output.sv_target0 = final_color;
return output;
}
@ -85,6 +85,6 @@ VertexShader(V_DVertVS, V_DVertPSInput)
PixelShader(V_DVertPS, V_DVertPSOutput, V_DVertPSInput input)
{
V_DVertPSOutput output;
output.SV_Target0 = input.color_lin;
output.sv_target0 = input.color_lin;
return output;
}

View File

@ -9,7 +9,7 @@ Struct(V_DQuadPSInput)
Struct(V_DQuadPSOutput)
{
Semantic(Vec4, SV_Target0);
Semantic(Vec4, sv_target0);
};
////////////////////////////////////////////////////////////
@ -24,7 +24,7 @@ Struct(V_DVertPSInput)
Struct(V_DVertPSOutput)
{
Semantic(Vec4, SV_Target0);
Semantic(Vec4, sv_target0);
};
////////////////////////////////////////////////////////////

View File

@ -31,7 +31,7 @@ Struct(PT_BlitPSInput)
Struct(PT_BlitPSOutput)
{
Semantic(Vec4, SV_Target0);
Semantic(Vec4, sv_target0);
};
//////////////////////////////
@ -68,6 +68,6 @@ PixelShader(PT_BlitPS, PT_BlitPSOutput, PT_BlitPSInput input)
result.r = (f32)noise_val / (f32)U16Max;
PT_BlitPSOutput output;
output.SV_Target0 = result;
output.sv_target0 = result;
return output;
}

View File

@ -150,15 +150,15 @@ void UI_PushDefaults(void)
case UI_StyleKind_Font: { desc.style.Font = UI_GetDefaultFont(); } break;
u8 prefetch[127] = ZI;
for (u64 i = 0; i < countof(prefetch); ++i)
for (u64 idx = 0; idx < countof(prefetch); ++idx)
{
prefetch[i] = i;
prefetch[idx] = idx;
}
case UI_StyleKind_FontSize: { desc.style.FontSize = 16.0f; } break;
case UI_StyleKind_Tint: { desc.style.Tint = Color_White; } break;
case UI_StyleKind_Tag: { desc.style.Tag = HashFnv64(Fnv64Basis, Lit("root")); } break;
case UI_StyleKind_DebugColor: { desc.style.DebugColor = Rgba(1, 0, 1, 0.5); } break;
case UI_StyleKind_BackgroundTextureUv1: { desc.style.BackgroundTextureUv1 = VEC2(1, 1); } break;
case UI_StyleKind_FontSize: { desc.style.FontSize = 16.0f; } break;
case UI_StyleKind_Tint: { desc.style.Tint = Color_White; } break;
case UI_StyleKind_Tag: { desc.style.Tag = HashFnv64(Fnv64Basis, Lit("root")); } break;
case UI_StyleKind_DebugColor: { desc.style.DebugColor = Rgba(1, 0, 1, 0.5); } break;
case UI_StyleKind_BackgroundTextureSliceUv: { desc.style.BackgroundTextureSliceUv = RNG2(VEC2(0, 0), VEC2(1, 1)); } break;
};
UI_PushStyle(desc);
}
@ -411,7 +411,7 @@ UI_Key UI_BuildBoxEx(UI_Key key)
}
void UI_SetRawTexture(UI_Key key, G_Texture2DRef tex, Vec2 uv0, Vec2 uv1)
void UI_SetRawTexture(UI_Key key, G_Texture2DRef tex, Rng2 uv)
{
UI_Frame *frame = UI_CurrentFrame();
UI_CmdNode *n = PushStruct(frame->arena, UI_CmdNode);
@ -419,8 +419,7 @@ void UI_SetRawTexture(UI_Key key, G_Texture2DRef tex, Vec2 uv0, Vec2 uv1)
{
n->cmd.set_raw_texture.key = key;
n->cmd.set_raw_texture.tex = tex;
n->cmd.set_raw_texture.uv0 = uv0;
n->cmd.set_raw_texture.uv1 = uv1;
n->cmd.set_raw_texture.slice_uv = uv;
}
++frame->cmds_count;
SllQueuePush(frame->first_cmd_node, frame->last_cmd_node, n);
@ -547,8 +546,8 @@ UI_Frame *UI_BeginFrame(UI_FrameFlag frame_flags, Vec4 swapchain_color)
b32 is_cursor_in_box = 0;
{
/* TODO: More efficient test. This logic is just copied from the renderer's SDF function for now. */
Vec2 p0 = box->p0;
Vec2 p1 = box->p1;
Vec2 p0 = box->rect.p0;
Vec2 p1 = box->rect.p1;
Vec2 point = frame->cursor_pos;
b32 is_corner = 0;
f32 non_corner_edge_dist = MinF32(MinF32(point.x - p0.x, p1.x - point.x), MinF32(point.y - p0.y, p1.y - point.y));
@ -597,7 +596,7 @@ UI_Frame *UI_BeginFrame(UI_FrameFlag frame_flags, Vec4 swapchain_color)
{
++hovered_box->report.m1_downs;
hovered_box->report.m1_held = 1;
hovered_box->report.last_m1_offset = SubVec2(frame->cursor_pos, hovered_box->p0);
hovered_box->report.last_m1_offset = SubVec2(frame->cursor_pos, hovered_box->rect.p0);
active_box = hovered_box;
}
}
@ -642,8 +641,7 @@ UI_Frame *UI_BeginFrame(UI_FrameFlag frame_flags, Vec4 swapchain_color)
report->active = LerpF32(report->active, target_active, active_blend_rate);
report->hovered = LerpF32(report->hovered, target_hovered, hovered_blend_rate);
report->screen_p0 = box->p0;
report->screen_p1 = box->p1;
report->screen_rect = box->rect;
}
frame->hovered_box = hovered_box ? hovered_box->key : UI_NilKey;
@ -789,10 +787,9 @@ void UI_EndFrame(UI_Frame *frame)
}
/* Fetch run */
box->glyph_run = &GC_NilRun;
if (box->desc.text.len > 0)
{
box->glyph_run = GC_RunFromString(frame->arena, box->desc.font, box->desc.text);
box->glyph_run = GC_RunFromString(frame->arena, box->desc.text, box->desc.font, box->desc.font_size);
}
}
@ -809,8 +806,7 @@ void UI_EndFrame(UI_Frame *frame)
if (box)
{
box->raw_texture = cmd.set_raw_texture.tex;
box->raw_texture_uv0 = cmd.set_raw_texture.uv0;
box->raw_texture_uv1 = cmd.set_raw_texture.uv1;
box->raw_texture_slice_uv = cmd.set_raw_texture.slice_uv;
}
} break;
}
@ -908,13 +904,11 @@ void UI_EndFrame(UI_Frame *frame)
f32 text_size = 0;
if (axis == Axis_X)
{
GC_RunRect rr = box->glyph_run->rects[box->glyph_run->count - 1];
f32 baseline_length = rr.pos + rr.advance;
text_size = baseline_length;
text_size = box->glyph_run.baseline_length;
}
else
{
text_size = box->glyph_run->ascent + box->glyph_run->descent;
text_size = box->glyph_run.font_ascent + box->glyph_run.font_descent;
}
box->solved_dims[axis] = text_size + (pref_size.v * 2);
}
@ -1107,16 +1101,16 @@ void UI_EndFrame(UI_Frame *frame)
if (AnyBit(box->desc.flags, UI_BoxFlag_Floating))
{
Vec2 offset = box->desc.floating_pos;
final_pos = AddVec2(parent->p0, offset);
final_pos = AddVec2(parent->rect.p0, offset);
if (!AnyBit(box->desc.flags, UI_BoxFlag_NoFloatingClamp))
{
{
f32 overshoot = MaxF32(0, (final_pos.x + dims_vec.x) - parent->p1.x);
final_pos.x = MaxF32(parent->p0.x, final_pos.x - overshoot);
f32 overshoot = MaxF32(0, (final_pos.x + dims_vec.x) - parent->rect.p1.x);
final_pos.x = MaxF32(parent->rect.p0.x, final_pos.x - overshoot);
}
{
f32 overshoot = MaxF32((final_pos.y + dims_vec.y) - parent->p1.y, 0);
final_pos.y = MaxF32(parent->p0.y, final_pos.y - overshoot);
f32 overshoot = MaxF32((final_pos.y + dims_vec.y) - parent->rect.p1.y, 0);
final_pos.y = MaxF32(parent->rect.p0.y, final_pos.y - overshoot);
}
}
}
@ -1151,22 +1145,22 @@ void UI_EndFrame(UI_Frame *frame)
} break;
}
}
final_pos.x = parent->p0.x + offset[0];
final_pos.y = parent->p0.y + offset[1];
final_pos.x = parent->rect.p0.x + offset[0];
final_pos.y = parent->rect.p0.y + offset[1];
parent->layout_cursor += dims_arr[parent->desc.child_layout_axis];
}
/* Submit position */
Vec2 floored_final_pos = FloorVec2(final_pos);
Vec2 ceiled_dims = CeilVec2(dims_vec);
box->p0 = FloorVec2(floored_final_pos);
box->p1 = AddVec2(floored_final_pos, ceiled_dims);
box->rect.p0 = FloorVec2(floored_final_pos);
box->rect.p1 = AddVec2(floored_final_pos, ceiled_dims);
}
/* Rounding */
{
UI_Round rounding = box->desc.rounding;
Vec2 half_dims = MulVec2(SubVec2(box->p1, box->p0), 0.5);
Vec2 half_dims = MulVec2(SubVec2(box->rect.p1, box->rect.p0), 0.5);
f32 min_half_dims = MinF32(half_dims.x, half_dims.y);
f32 final_rounding_tl = 0;
f32 final_rounding_tr = 0;
@ -1194,10 +1188,10 @@ void UI_EndFrame(UI_Frame *frame)
if (parent && !AllBits(box->desc.flags, UI_BoxFlag_Floating | UI_BoxFlag_NoFloatingClamp))
{
Vec2 vtl = SubVec2(VEC2(parent->p0.x, parent->p0.y), VEC2(box->p0.x, box->p0.y));
Vec2 vtr = SubVec2(VEC2(parent->p1.x, parent->p0.y), VEC2(box->p1.x, box->p0.y));
Vec2 vbr = SubVec2(VEC2(parent->p1.x, parent->p1.y), VEC2(box->p1.x, box->p1.y));
Vec2 vbl = SubVec2(VEC2(parent->p0.x, parent->p1.y), VEC2(box->p0.x, box->p1.y));
Vec2 vtl = SubVec2(VEC2(parent->rect.p0.x, parent->rect.p0.y), VEC2(box->rect.p0.x, box->rect.p0.y));
Vec2 vtr = SubVec2(VEC2(parent->rect.p1.x, parent->rect.p0.y), VEC2(box->rect.p1.x, box->rect.p0.y));
Vec2 vbr = SubVec2(VEC2(parent->rect.p1.x, parent->rect.p1.y), VEC2(box->rect.p1.x, box->rect.p1.y));
Vec2 vbl = SubVec2(VEC2(parent->rect.p0.x, parent->rect.p1.y), VEC2(box->rect.p0.x, box->rect.p1.y));
final_rounding_tl = MaxF32(final_rounding_tl, parent->rounding_tl - Vec2Len(vtl));
final_rounding_tr = MaxF32(final_rounding_tr, parent->rounding_tr - Vec2Len(vtr));
final_rounding_br = MaxF32(final_rounding_br, parent->rounding_br - Vec2Len(vbr));
@ -1226,18 +1220,15 @@ void UI_EndFrame(UI_Frame *frame)
UI_Box *box = boxes_pre[pre_index];
b32 is_visible = 1;
is_visible = is_visible && (box->desc.tint.w != 0);
is_visible = is_visible && (box->p1.x > box->p0.x);
is_visible = is_visible && (box->p1.y > box->p0.y);
is_visible = is_visible && (box->rect.p1.x > box->rect.p0.x);
is_visible = is_visible && (box->rect.p1.y > box->rect.p0.y);
if (is_visible || AnyBit(frame->frame_flags, UI_FrameFlag_Debug))
{
/* Box rect */
{
UI_DRect *rect = PushStruct(frame->rects_arena, UI_DRect);
rect->p0 = box->p0;
rect->p1 = box->p1;
rect->tex_uv0 = VEC2(0, 0);
rect->tex_uv1 = VEC2(1, 1);
rect->bounds = box->rect;
rect->background_lin = LinearFromSrgb(box->desc.background_color);
rect->border_lin = LinearFromSrgb(box->desc.border_color);
rect->debug_lin = LinearFromSrgb(box->desc.debug_color);
@ -1248,54 +1239,54 @@ void UI_EndFrame(UI_Frame *frame)
rect->br_rounding = box->rounding_br;
rect->bl_rounding = box->rounding_bl;
rect->tex = box->raw_texture;
rect->tex_uv0 = box->raw_texture_uv0;
rect->tex_uv1 = box->raw_texture_uv1;
rect->tex_slice_uv = box->raw_texture_slice_uv;
}
/* Text rects */
GC_Run *raw_run = box->glyph_run;
if (AnyBit(box->desc.flags, UI_BoxFlag_DrawText) && raw_run->loaded)
GC_Run raw_run = box->glyph_run;
if (AnyBit(box->desc.flags, UI_BoxFlag_DrawText) && raw_run.ready)
{
f32 max_baseline = box->p1.x - box->p0.x;
b32 should_truncate = raw_run->count > 0 && (raw_run->rects[raw_run->count - 1].pos + raw_run->rects[raw_run->count - 1].advance) > max_baseline;
f32 max_baseline = DimsFromRng2(box->rect).x;
b32 should_truncate = raw_run.baseline_length > max_baseline && !AnyBit(box->desc.flags, UI_BoxFlag_NoTextTruncation);
/* Truncate run */
GC_RunRect *truncated_rects = raw_run->rects;
u32 truncated_rects_count = raw_run->count;
if (should_truncate && !AnyBit(box->desc.flags, UI_BoxFlag_NoTextTruncation))
u64 final_rects_count = 0;
GC_RunRect *final_rects = 0;
if (should_truncate)
{
/* Get elipses run */
GC_Run *elipses_run = GC_RunFromString(scratch.arena, box->desc.font, Lit("..."));
if (elipses_run->count > 0)
{
max_baseline -= elipses_run->rects[elipses_run->count - 1].pos + elipses_run->rects[elipses_run->count - 1].advance;
}
GC_Run elipses_run = GC_RunFromString(scratch.arena, Lit("..."), box->desc.font, box->desc.font_size);
f32 elipses_start_pos = max_baseline - elipses_run.baseline_length;
/* Subtract glyphs */
while (truncated_rects_count > 0)
/* Append non-overflowed rects */
final_rects = PushStructsNoZero(scratch.arena, GC_RunRect, raw_run.rects_count);
for (u64 rect_idx = 0; rect_idx < raw_run.rects_count; ++rect_idx)
{
GC_RunRect rr = raw_run->rects[truncated_rects_count - 1];
if (rr.pos + rr.advance <= max_baseline)
GC_RunRect rr = raw_run.rects[rect_idx];
if ((rr.baseline_pos + rr.advance) <= elipses_start_pos)
{
break;
}
--truncated_rects_count;
}
/* Merge trunc rects */
/* FIXME: Verify this stil works as expected */
{
truncated_rects_count += elipses_run->count;
truncated_rects = PushStructsNoZero(scratch.arena, GC_RunRect, truncated_rects_count + elipses_run->count);
CopyStructs(truncated_rects, raw_run->rects, raw_run->count);
f32 elipses_offset = truncated_rects_count > 0 ? (truncated_rects[truncated_rects_count - 1].pos + truncated_rects[truncated_rects_count - 1].advance) : 0;
for (u32 i = 0; i < elipses_run->count; ++i)
{
GC_RunRect *rr = &truncated_rects[i + truncated_rects_count];
*rr = elipses_run->rects[i];
rr->pos += elipses_offset;
final_rects[final_rects_count] = rr;
final_rects_count += 1;
}
}
/* Append elipses */
for (u64 rect_idx = 0; rect_idx < elipses_run.rects_count; ++rect_idx)
{
GC_RunRect rr = elipses_run.rects[rect_idx];
rr.baseline_pos += elipses_start_pos;
rr.bounds = AddRng2Vec2(rr.bounds, VEC2(elipses_start_pos, 0));
if ((rr.baseline_pos + rr.advance) <= max_baseline)
{
final_rects[final_rects_count] = rr;
final_rects_count += 1;
}
}
}
else
{
final_rects = raw_run.rects;
final_rects_count = raw_run.rects_count;
}
UI_AxisAlignment x_alignment = box->desc.child_alignment[Axis_X];
@ -1306,68 +1297,64 @@ void UI_EndFrame(UI_Frame *frame)
}
/* Compute baseline */
f32 ascent = raw_run->ascent;
f32 descent = raw_run->descent;
f32 cap = raw_run->cap;
f32 baseline_width = truncated_rects_count > 0 ? (truncated_rects[truncated_rects_count - 1].pos + truncated_rects[truncated_rects_count - 1].advance) : 0;
f32 baseline_height = ascent + descent;
f32 box_width = box->p1.x - box->p0.x;
f32 box_height = box->p1.y - box->p0.y;
f32 ascent = raw_run.font_ascent;
f32 font_descent = raw_run.font_descent;
f32 cap = raw_run.font_cap;
f32 baseline_width = raw_run.baseline_length;
f32 baseline_height = ascent + font_descent;
Vec2 box_dims = DimsFromRng2(box->rect);
Vec2 baseline = ZI;
switch (x_alignment)
{
case UI_AxisAlignment_Start:
{
baseline.x = box->p0.x;
baseline.x = box->rect.p0.x;
} break;
case UI_AxisAlignment_End:
{
baseline.x = box->p1.x;
baseline.x = box->rect.p1.x;
baseline.x -= baseline_width;
} break;
case UI_AxisAlignment_Center:
{
baseline.x = box->p0.x;
baseline.x += (box_width - baseline_width) / 2;
baseline.x = box->rect.p0.x;
baseline.x += (box_dims.x - baseline_width) / 2;
} break;
}
switch (y_alignment)
{
case UI_AxisAlignment_Start:
{
baseline.y = box->p0.y;
baseline.y = box->rect.p0.y;
baseline.y += ascent;
} break;
case UI_AxisAlignment_End:
{
baseline.y = box->p1.y;
baseline.y -= descent;
baseline.y = box->rect.p1.y;
baseline.y -= font_descent;
} break;
case UI_AxisAlignment_Center:
{
baseline.y = box->p0.y;
baseline.y += box_height / 2;
baseline.y = box->rect.p0.y;
baseline.y += box_dims.y / 2;
baseline.y += cap / 2;
} break;
}
baseline = CeilVec2(baseline);
/* Push text rects */
for (u64 i = 0; i < truncated_rects_count; ++i)
for (u64 rect_idx = 0; rect_idx < final_rects_count; ++rect_idx)
{
GC_RunRect rr = truncated_rects[i];
Vec2 glyph_size = rr.size;
if (glyph_size.x != 0 || glyph_size.y != 0)
GC_RunRect rr = final_rects[rect_idx];
Vec2 glyph_dims = DimsFromRng2(rr.bounds);
if (glyph_dims.x != 0 || glyph_dims.y != 0)
{
UI_DRect *rect = PushStruct(frame->rects_arena, UI_DRect);
rect->p0 = AddVec2(baseline, VEC2(rr.pos, 0));
rect->p0 = AddVec2(rect->p0, rr.offset);
rect->p1 = AddVec2(rect->p0, glyph_size);
rect->bounds = AddRng2Vec2(rr.bounds, baseline);
rect->debug_lin = LinearFromSrgb(box->desc.debug_color);
rect->tint_lin = LinearFromSrgb(box->desc.tint);
rect->tex = rr.tex;
rect->tex_uv0 = rr.uv.p0;
rect->tex_uv1 = rr.uv.p1;
rect->tex_slice_uv = rr.tex_uv;
}
}
}

View File

@ -90,34 +90,33 @@ Enum(UI_BoxFlag)
////////////////////////////////////////////////////////////
//~ Style types
#define UI_StyleKindsXMacro(X) \
X(Flags, UI_BoxFlag) \
X(Parent, UI_Key) \
X(Tag, u64) \
X(ChildLayoutAxis, Axis) \
X(ChildAlignmentX, UI_AxisAlignment) \
X(ChildAlignmentY, UI_AxisAlignment) \
X(Width, UI_Size) \
X(Height, UI_Size) \
X(BackgroundColor, Vec4) \
X(BorderColor, Vec4) \
X(DebugColor, Vec4) \
X(Tint, Vec4) \
X(Border, f32) \
X(FloatingPos, Vec2) \
X(Rounding, UI_Round) \
X(Font, GC_FontKey) \
X(FontSize, u32) \
X(Text, String) \
X(BackgroundTexture, G_Texture2DRef) \
X(BackgroundTextureUv0, Vec2) \
X(BackgroundTextureUv1, Vec2) \
/* --------------------------------------- */ \
/* ----------- Virtual styles ----------- */ \
/* --------------------------------------- */ \
X(BeginVirtualStyles_, i8) \
X(ChildAlignment, UI_Alignment) \
X(AxisSize, UI_Size) \
#define UI_StyleKindsXMacro(X) \
X(Flags, UI_BoxFlag) \
X(Parent, UI_Key) \
X(Tag, u64) \
X(ChildLayoutAxis, Axis) \
X(ChildAlignmentX, UI_AxisAlignment) \
X(ChildAlignmentY, UI_AxisAlignment) \
X(Width, UI_Size) \
X(Height, UI_Size) \
X(BackgroundColor, Vec4) \
X(BorderColor, Vec4) \
X(DebugColor, Vec4) \
X(Tint, Vec4) \
X(Border, f32) \
X(FloatingPos, Vec2) \
X(Rounding, UI_Round) \
X(Font, GC_FontKey) \
X(FontSize, u32) \
X(Text, String) \
X(BackgroundTexture, G_Texture2DRef) \
X(BackgroundTextureSliceUv, Rng2) \
/* -------------------------------------------- */ \
/* -------------- Virtual styles -------------- */ \
/* -------------------------------------------- */ \
X(BeginVirtualStyles_, i8) \
X(ChildAlignment, UI_Alignment) \
X(AxisSize, UI_Size) \
/* -------------------------------------------------- */
Enum(UI_StyleKind)
@ -187,8 +186,7 @@ Struct(UI_Report)
Vec2 last_m1_offset;
/* Where was this box last rendered in screen coordinates */
Vec2 screen_p0;
Vec2 screen_p1;
Rng2 screen_rect;
};
////////////////////////////////////////////////////////////
@ -233,8 +231,7 @@ Struct(UI_Cmd)
{
UI_Key key;
G_Texture2DRef tex;
Vec2 uv0;
Vec2 uv1;
Rng2 slice_uv;
} set_raw_texture;
};
};
@ -269,22 +266,20 @@ Struct(UI_Box)
//- Cmd data
UI_BoxDesc desc;
G_Texture2DRef raw_texture;
Vec2 raw_texture_uv0;
Vec2 raw_texture_uv1;
Rng2 raw_texture_slice_uv;
//- Pre-layout data
u64 pre_index;
u64 post_index;
//- Layout data
GC_Run *glyph_run;
GC_Run glyph_run;
f32 layout_cursor;
f32 solved_dims[Axis_CountXY];
f32 final_children_size_accum[Axis_CountXY];
//- Layout results
Vec2 p0;
Vec2 p1;
Rng2 rect;
f32 rounding_tl;
f32 rounding_tr;
f32 rounding_br;
@ -443,7 +438,7 @@ UI_Style UI_PopStyle(UI_StyleDesc desc);
UI_Key UI_BuildBoxEx(UI_Key key);
#define UI_BuildBox() UI_BuildBoxEx(UI_TransKey())
void UI_SetRawTexture(UI_Key key, G_Texture2DRef tex, Vec2 uv0, Vec2 uv1);
void UI_SetRawTexture(UI_Key key, G_Texture2DRef tex, Rng2 uv);
////////////////////////////////////////////////////////////
//~ Report

View File

@ -20,16 +20,16 @@ Struct(UI_DParams)
Struct(UI_DRect)
{
Vec2 p0;
Vec2 p1;
Rng2 bounds;
G_Texture2DRef tex;
Rng2 tex_slice_uv;
Vec4 tint_lin;
Vec4 background_lin;
Vec4 border_lin;
Vec4 debug_lin;
f32 border;
Vec2 tex_uv0;
Vec2 tex_uv1;
G_Texture2DRef tex;
f32 tl_rounding;
f32 tr_rounding;

View File

@ -11,8 +11,8 @@ VertexShader(UI_DRectVS, UI_DRectPSInput)
UI_DRect rect = rects[SV_InstanceID];
Vec2 rect_uv = RectUvFromVertexId(SV_VertexID);
Vec2 tex_uv = lerp(rect.tex_uv0, rect.tex_uv1, rect_uv);
Vec2 target_pos = lerp(rect.p0, rect.p1, rect_uv);
Vec2 tex_uv = lerp(rect.tex_slice_uv.p0, rect.tex_slice_uv.p1, rect_uv);
Vec2 target_pos = lerp(rect.bounds.p0, rect.bounds.p1, rect_uv);
UI_DRectPSInput result;
result.sv_position = Vec4(NdcFromPos(target_pos, Vec2(params.target_size).xy), 0, 1);
@ -39,8 +39,8 @@ PixelShader(UI_DRectPS, UI_DRectPSOutput, UI_DRectPSInput input)
Vec2 p = input.sv_position.xy;
Vec2 rect_uv = input.rect_uv;
Vec2 p0 = rect.p0;
Vec2 p1 = rect.p1;
Vec2 p0 = rect.bounds.p0;
Vec2 p1 = rect.bounds.p1;
/* Compute rect sdf (negative means pixel is inside of rect) */
f32 rect_dist = min(min(p.x - p0.x, p1.x - p.x), min(p.y - p0.y, p1.y - p.y));
@ -147,6 +147,6 @@ PixelShader(UI_BlitPS, UI_BlitPSOutput, UI_BlitPSInput input)
Vec4 result = tex.Sample(sampler, uv);
UI_BlitPSOutput output;
output.SV_Target0 = result;
output.sv_target0 = result;
return output;
}

View File

@ -28,7 +28,7 @@ Struct(UI_BlitPSInput)
Struct(UI_BlitPSOutput)
{
Semantic(Vec4, SV_Target0);
Semantic(Vec4, sv_target0);
};
////////////////////////////////////////////////////////////