VEFontCache: Various changes to field types, increased shape cache default capacity

This commit is contained in:
Edward R. Gonzalez 2024-06-29 22:34:24 -04:00
parent a666300f41
commit a9ddd7668f
6 changed files with 88 additions and 84 deletions

View File

@ -20,12 +20,12 @@ PoolList :: struct {
free_list : [dynamic]PoolListIter,
front : PoolListIter,
back : PoolListIter,
size : u32,
capacity : u32,
size : i32,
capacity : i32,
dbg_name : string,
}
pool_list_init :: proc( pool : ^PoolList, capacity : u32, dbg_name : string = "" )
pool_list_init :: proc( pool : ^PoolList, capacity : i32, dbg_name : string = "" )
{
error : AllocatorError
pool.items, error = make( [dynamic]PoolListItem, int(capacity) )
@ -158,13 +158,13 @@ LRU_Link :: struct {
}
LRU_Cache :: struct {
capacity : u32,
num : u32,
capacity : i32,
num : i32,
table : map[u64]LRU_Link,
key_queue : PoolList,
}
LRU_init :: proc( cache : ^LRU_Cache, capacity : u32, dbg_name : string = "" ) {
LRU_init :: proc( cache : ^LRU_Cache, capacity : i32, dbg_name : string = "" ) {
error : AllocatorError
cache.capacity = capacity
cache.table, error = make( map[u64]LRU_Link, uint(capacity) )

View File

@ -12,6 +12,7 @@ See: [docs/Readme.md](docs/Readme.md) for the library's interface
* Font face parser info encapsulated in parser_info struct.
* ve_fontcache_loadfile not ported (ust use core:os or os2, then call load_font)
* Macro defines have been coverted (mostly) to runtime parameters
* Support for hot_reloading
## TODOs

View File

@ -17,12 +17,13 @@ FontID :: distinct i64
Glyph :: distinct i32
Entry :: struct {
parser_info : ParserFontInfo,
shaper_info : ShaperInfo,
id : FontID,
used : b32,
size : f32,
size_scale : f32,
parser_info : ParserFontInfo,
shaper_info : ShaperInfo,
id : FontID,
used : b32,
curve_quality : f32,
size : f32,
size_scale : f32,
}
Entry_Default :: Entry {
@ -43,16 +44,14 @@ Context :: struct {
temp_path : [dynamic]Vertex,
temp_codepoint_seen : map[u64]bool,
temp_codepoint_seen_num : u32,
temp_codepoint_seen_num : int,
snap_width : u32,
snap_height : u32,
snap_width : f32,
snap_height : f32,
colour : Colour,
cursor_pos : Vec2,
// draw_cursor_pos : Vec2,
draw_layer : struct {
vertices_offset : int,
indices_offset : int,
@ -64,8 +63,8 @@ Context :: struct {
glyph_buffer : GlyphDrawBuffer,
shape_cache : ShapedTextCache,
curve_quality : u32,
text_shape_adv : b32,
default_curve_quality : i32,
text_shape_adv : b32,
debug_print : b32,
debug_print_verbose : b32,
@ -113,7 +112,7 @@ InitAtlasParams_Default :: InitAtlasParams {
}
InitGlyphDrawParams :: struct {
over_sample : Vec2,
over_sample : Vec2i,
buffer_batch : u32,
draw_padding : u32,
}
@ -130,8 +129,8 @@ InitShapeCacheParams :: struct {
}
InitShapeCacheParams_Default :: InitShapeCacheParams {
capacity = 2048,
reserve_length = 2048,
capacity = 8 * 1024,
reserve_length = 1 * 1024,
}
// ve_fontcache_init
@ -140,7 +139,7 @@ startup :: proc( ctx : ^Context, parser_kind : ParserKind,
atlas_params := InitAtlasParams_Default,
glyph_draw_params := InitGlyphDrawParams_Default,
shape_cache_params := InitShapeCacheParams_Default,
curve_quality : u32 = 3,
default_curve_quality : u32 = 3,
entires_reserve : u32 = 512,
temp_path_reserve : u32 = 1024,
temp_codepoint_seen_reserve : u32 = 2048,
@ -152,10 +151,10 @@ startup :: proc( ctx : ^Context, parser_kind : ParserKind,
ctx.backing = allocator
context.allocator = ctx.backing
if curve_quality == 0 {
curve_quality = 3
if default_curve_quality == 0 {
default_curve_quality = 3
}
ctx.curve_quality = curve_quality
ctx.default_curve_quality = default_curve_quality
error : AllocatorError
entries, error = make( [dynamic]Entry, len = 0, cap = entires_reserve )
@ -181,8 +180,8 @@ startup :: proc( ctx : ^Context, parser_kind : ParserKind,
using region
next_idx = 0;
width = region_params.width
height = region_params.height
width = i32(region_params.width)
height = i32(region_params.height)
size = {
i32(params.width) / factor.x,
i32(params.height) / factor.y,
@ -194,28 +193,26 @@ startup :: proc( ctx : ^Context, parser_kind : ParserKind,
assert( capacity.x * capacity.y == expected_cap )
error : AllocatorError
// state.cache, error = make( HMapChained(LRU_Link), uint(capacity.x * capacity.y) )
// assert( error == .None, "VEFontCache.init_atlas_region : Failed to allocate state.cache")
LRU_init( & state, u32(capacity.x * capacity.y) )
LRU_init( & state, capacity.x * capacity.y )
}
init_atlas_region( & atlas.region_a, atlas_params, atlas_params.region_a, { 4, 2}, 1024 )
init_atlas_region( & atlas.region_b, atlas_params, atlas_params.region_b, { 4, 2}, 512 )
init_atlas_region( & atlas.region_c, atlas_params, atlas_params.region_c, { 4, 1}, 512 )
init_atlas_region( & atlas.region_d, atlas_params, atlas_params.region_d, { 2, 1}, 256 )
atlas.width = atlas_params.width
atlas.height = atlas_params.height
atlas.glyph_padding = atlas_params.glyph_padding
atlas.width = i32(atlas_params.width)
atlas.height = i32(atlas_params.height)
atlas.glyph_padding = i32(atlas_params.glyph_padding)
atlas.region_a.offset = {0, 0}
atlas.region_b.offset.x = 0
atlas.region_b.offset.y = atlas.region_a.size.y
atlas.region_c.offset.x = atlas.region_a.size.x
atlas.region_c.offset.y = 0
atlas.region_d.offset.x = i32(atlas.width) / 2
atlas.region_d.offset.x = atlas.width / 2
atlas.region_d.offset.y = 0
LRU_init( & shape_cache.state, shape_cache_params.capacity )
LRU_init( & shape_cache.state, i32(shape_cache_params.capacity) )
shape_cache.storage, error = make( [dynamic]ShapedText, shape_cache_params.capacity )
assert(error == .None, "VEFontCache.init : Failed to allocate shape_cache.storage")
@ -242,11 +239,11 @@ startup :: proc( ctx : ^Context, parser_kind : ParserKind,
// Note(From original author): We can actually go over VE_FONTCACHE_GLYPHDRAW_BUFFER_BATCH batches due to smart packing!
{
using glyph_buffer
over_sample = glyph_draw_params.over_sample
batch = glyph_draw_params.buffer_batch
width = atlas.region_d.width * u32(over_sample.x) * batch
height = atlas.region_d.height * u32(over_sample.y)
draw_padding = glyph_draw_params.draw_padding
over_sample = vec2(glyph_draw_params.over_sample)
batch = cast(i32) glyph_draw_params.buffer_batch
width = atlas.region_d.width * i32(over_sample.x) * batch
height = atlas.region_d.height * i32(over_sample.y)
draw_padding = cast(i32) glyph_draw_params.draw_padding
draw_list.calls, error = make( [dynamic]DrawCall, len = 0, cap = glyph_draw_params.buffer_batch * 2 )
assert( error == .None, "VEFontCache.init : Failed to allocate calls for draw_list" )
@ -292,7 +289,7 @@ hot_reload :: proc( ctx : ^Context, allocator : Allocator )
LRU_reload( & atlas.region_d.state, allocator)
LRU_reload( & shape_cache.state, allocator )
for idx : u32 = 0; idx < u32(len(shape_cache.storage)); idx += 1 {
for idx : i32 = 0; idx < i32(len(shape_cache.storage)); idx += 1 {
stroage_entry := & shape_cache.storage[idx]
using stroage_entry
@ -329,7 +326,7 @@ shutdown :: proc( ctx : ^Context )
}
// ve_fontcache_load
load_font :: proc( ctx : ^Context, label : string, data : []byte, size_px : f32 ) -> (font_id : FontID)
load_font :: proc( ctx : ^Context, label : string, data : []byte, size_px : f32, glyph_curve_quality : u32 = 0 ) -> (font_id : FontID)
{
assert( ctx != nil )
assert( len(data) > 0 )
@ -352,19 +349,25 @@ load_font :: proc( ctx : ^Context, label : string, data : []byte, size_px : f32
entry := & entries[ id ]
{
using entry
used = true
parser_info = parser_load_font( & parser_ctx, label, data )
// assert( parser_info != nil, "VEFontCache.load_font: Failed to load font info from parser" )
shaper_info = shaper_load_font( & shaper_ctx, label, data, transmute(rawptr) id )
// assert( shaper_info != nil, "VEFontCache.load_font: Failed to load font from shaper")
size = size_px
size_scale = size_px < 0.0 ? \
parser_scale_for_pixel_height( & parser_info, -size_px ) \
: parser_scale_for_mapping_em_to_pixels( & parser_info, size_px )
used = true
shaper_info = shaper_load_font( & shaper_ctx, label, data, transmute(rawptr) id )
// assert( shaper_info != nil, "VEFontCache.load_font: Failed to load font from shaper")
if glyph_curve_quality == 0 {
curve_quality = f32(ctx.default_curve_quality)
}
else {
curve_quality = f32(glyph_curve_quality)
}
}
entry.id = FontID(id)
ctx.entries[ id ].id = FontID(id)
@ -395,8 +398,8 @@ unload_font :: proc( ctx : ^Context, font : FontID )
// ve_fontcache_configure_snap
configure_snap :: #force_inline proc( ctx : ^Context, snap_width, snap_height : u32 ) {
assert( ctx != nil )
ctx.snap_width = snap_width
ctx.snap_height = snap_height
ctx.snap_width = f32(snap_width)
ctx.snap_height = f32(snap_height)
}
get_cursor_pos :: #force_inline proc "contextless" ( ctx : ^Context ) -> Vec2 { return ctx.cursor_pos }
@ -410,13 +413,11 @@ draw_text :: proc( ctx : ^Context, font : FontID, text_utf8 : string, position,
ctx.cursor_pos = {}
position := position
snap_width := f32(ctx.snap_width)
snap_height := f32(ctx.snap_height)
if ctx.snap_width > 0 do position.x = cast(f32) cast(u32) (position.x * snap_width + 0.5) / snap_width
if ctx.snap_height > 0 do position.y = cast(f32) cast(u32) (position.y * snap_height + 0.5) / snap_height
position := position
if ctx.snap_width > 0 do position.x = cast(f32) cast(u32) (position.x * ctx.snap_width + 0.5) / ctx.snap_width
if ctx.snap_height > 0 do position.y = cast(f32) cast(u32) (position.y * ctx.snap_height + 0.5) / ctx.snap_height
entry := & ctx.entries[ font ]
entry := & ctx.entries[ font ]
ChunkType :: enum u32 { Visible, Formatting }
chunk_kind : ChunkType
@ -429,7 +430,7 @@ draw_text :: proc( ctx : ^Context, font : FontID, text_utf8 : string, position,
text_chunk = transmute(string) text_utf8_bytes[ : ]
if len(text_chunk) > 0 {
shaped := shape_text_cached( ctx, font, text_chunk, entry )
ctx.cursor_pos = draw_text_shape( ctx, font, entry, shaped, position, scale, snap_width, snap_height )
ctx.cursor_pos = draw_text_shape( ctx, font, entry, shaped, position, scale, ctx.snap_width, ctx.snap_height )
}
return true
}

View File

@ -13,21 +13,21 @@ AtlasRegionKind :: enum u8 {
AtlasRegion :: struct {
state : LRU_Cache,
width : u32,
height : u32,
width : i32,
height : i32,
size : Vec2i,
capacity : Vec2i,
offset : Vec2i,
next_idx : u32,
next_idx : i32,
}
Atlas :: struct {
width : u32,
height : u32,
width : i32,
height : i32,
glyph_padding : u32,
glyph_padding : i32,
region_a : AtlasRegion,
region_b : AtlasRegion,
@ -43,8 +43,8 @@ atlas_bbox :: proc( atlas : ^Atlas, region : AtlasRegionKind, local_idx : i32 )
size.x = f32(atlas.region_a.width)
size.y = f32(atlas.region_a.height)
position.x = cast(f32) (( local_idx % atlas.region_a.capacity.x ) * i32(atlas.region_a.width))
position.y = cast(f32) (( local_idx / atlas.region_a.capacity.x ) * i32(atlas.region_a.height))
position.x = cast(f32) (( local_idx % atlas.region_a.capacity.x ) * atlas.region_a.width)
position.y = cast(f32) (( local_idx / atlas.region_a.capacity.x ) * atlas.region_a.height)
position.x += f32(atlas.region_a.offset.x)
position.y += f32(atlas.region_a.offset.y)
@ -53,8 +53,8 @@ atlas_bbox :: proc( atlas : ^Atlas, region : AtlasRegionKind, local_idx : i32 )
size.x = f32(atlas.region_b.width)
size.y = f32(atlas.region_b.height)
position.x = cast(f32) (( local_idx % atlas.region_b.capacity.x ) * i32(atlas.region_b.width))
position.y = cast(f32) (( local_idx / atlas.region_b.capacity.x ) * i32(atlas.region_b.height))
position.x = cast(f32) (( local_idx % atlas.region_b.capacity.x ) * atlas.region_b.width)
position.y = cast(f32) (( local_idx / atlas.region_b.capacity.x ) * atlas.region_b.height)
position.x += f32(atlas.region_b.offset.x)
position.y += f32(atlas.region_b.offset.y)
@ -63,8 +63,8 @@ atlas_bbox :: proc( atlas : ^Atlas, region : AtlasRegionKind, local_idx : i32 )
size.x = f32(atlas.region_c.width)
size.y = f32(atlas.region_c.height)
position.x = cast(f32) (( local_idx % atlas.region_c.capacity.x ) * i32(atlas.region_c.width))
position.y = cast(f32) (( local_idx / atlas.region_c.capacity.x ) * i32(atlas.region_c.height))
position.x = cast(f32) (( local_idx % atlas.region_c.capacity.x ) * atlas.region_c.width)
position.y = cast(f32) (( local_idx / atlas.region_c.capacity.x ) * atlas.region_c.height)
position.x += f32(atlas.region_c.offset.x)
position.y += f32(atlas.region_c.offset.y)
@ -73,8 +73,8 @@ atlas_bbox :: proc( atlas : ^Atlas, region : AtlasRegionKind, local_idx : i32 )
size.x = f32(atlas.region_d.width)
size.y = f32(atlas.region_d.height)
position.x = cast(f32) (( local_idx % atlas.region_d.capacity.x ) * i32(atlas.region_d.width))
position.y = cast(f32) (( local_idx / atlas.region_d.capacity.x ) * i32(atlas.region_d.height))
position.x = cast(f32) (( local_idx % atlas.region_d.capacity.x ) * atlas.region_d.width)
position.y = cast(f32) (( local_idx / atlas.region_d.capacity.x ) * atlas.region_d.height)
position.x += f32(atlas.region_d.offset.x)
position.y += f32(atlas.region_d.offset.y)
@ -101,8 +101,8 @@ decide_codepoint_region :: proc(ctx : ^Context, entry : ^Entry, glyph_index : Gl
glyph_buffer := & ctx.glyph_buffer
glyph_padding := f32( atlas.glyph_padding ) * 2
bounds_width_scaled := u32(bounds_width * entry.size_scale + glyph_padding)
bounds_height_scaled := u32(bounds_height * entry.size_scale + glyph_padding)
bounds_width_scaled := i32(bounds_width * entry.size_scale + glyph_padding)
bounds_height_scaled := i32(bounds_height * entry.size_scale + glyph_padding)
// Use a lookup table for faster region selection
region_lookup := [4]struct { kind: AtlasRegionKind, region: ^AtlasRegion } {

View File

@ -39,10 +39,10 @@ FrameBufferPass :: enum u32 {
GlyphDrawBuffer :: struct {
over_sample : Vec2,
batch : u32,
width : u32,
height : u32,
draw_padding : u32,
batch : i32,
width : i32,
height : i32,
draw_padding : i32,
batch_x : i32,
clear_draw_list : DrawList,
@ -123,9 +123,10 @@ cache_glyph :: proc(ctx : ^Context, font : FontID, glyph_index : Glyph, entry :
p0 := path[ len(path) - 1].pos
p1 := Vec2{ f32(edge.contour_x0), f32(edge.contour_y0) }
p2 := Vec2{ f32(edge.x), f32(edge.y) }
step := 1.0 / f32(quality)
for index := u32(1); index <= quality; index += 1 {
alpha := f32(index) * step
step := 1.0 / entry.curve_quality
for index : f32 = 1; index <= entry.curve_quality; index += 1 {
alpha := index * step
append( path, Vertex { pos = eval_point_on_bezier3(p0, p1, p2, alpha) } )
}
@ -135,9 +136,10 @@ cache_glyph :: proc(ctx : ^Context, font : FontID, glyph_index : Glyph, entry :
p1 := Vec2{ f32(edge.contour_x0), f32(edge.contour_y0) }
p2 := Vec2{ f32(edge.contour_x1), f32(edge.contour_y1) }
p3 := Vec2{ f32(edge.x), f32(edge.y) }
step := 1.0 / f32(quality)
for index := u32(1); index <= quality; index += 1 {
alpha := f32(index) * step
step := 1.0 / entry.curve_quality
for index : f32 = 1; index <= entry.curve_quality; index += 1 {
alpha := index * step
append( path, Vertex { pos = eval_point_on_bezier4(p0, p1, p2, p3, alpha) } )
}
}
@ -306,7 +308,7 @@ check_glyph_in_atlas :: #force_inline proc( ctx : ^Context, font : FontID, entry
if atlas_index == - 1
{
if region.next_idx > u32( region.state.capacity) {
if region.next_idx > region.state.capacity {
// We will evict LRU. We must predict which LRU will get evicted, and if it's something we've seen then we need to take slowpath and flush batch.
next_evict_codepoint := LRU_get_next_evicted( & region.state )
seen, success := ctx.temp_codepoint_seen[next_evict_codepoint]

View File

@ -120,7 +120,7 @@ textspace_x_form :: #force_inline proc "contextless" ( position, scale : ^Vec2,
}
}
Use_SIMD_For_Bezier_Ops :: false
Use_SIMD_For_Bezier_Ops :: true
when ! Use_SIMD_For_Bezier_Ops
{