Starting the process of porting VEFontCache

This commit is contained in:
2024-06-02 17:29:44 -04:00
parent 33ddd420b7
commit f99157aae5
11 changed files with 491 additions and 18 deletions

View File

@ -0,0 +1,69 @@
package VEFontCache
/*
The choice was made to keep the LUR cache implementation as close to the original as possible.
*/
PoolListIter :: u32
PoolListValue :: u64
PoolListItem :: struct {
prev : PoolListIter,
next : PoolListIter,
value : PoolListValue,
}
PoolList :: struct {
items : Array( PoolListItem ),
free_list : Array( PoolListIter ),
front : PoolListIter,
back : PoolListIter,
size : i32,
capacity : i32,
}
pool_list_init :: proc( pool : ^PoolList, capacity : u32 )
{
error : AllocatorError
pool.items, error = make( Array( PoolListItem ), u64(capacity) )
assert( error == .None, "VEFontCache.pool_list_init : Failed to allocate items array")
pool.free_list, error = make( Array( PoolListIter ), u64(capacity) )
assert( error == .None, "VEFontCache.pool_list_init : Failed to allocate free_list array")
pool.capacity = i32(capacity)
for id in 0 ..< capacity do pool.free_list.data[id] = id
}
pool_list_push_front :: proc( pool : ^PoolList, value : PoolListValue )
{
using pool
if size >= capacity do return
assert( free_list.num > 0 )
assert( free_list.num == u64(capacity - size) )
id := array_back( free_list )
}
LRU_Link :: struct {
value : i32,
ptr : PoolListIter,
}
LRU_Cache :: struct {
capacity : i32,
table : HMapChained(LRU_Link),
key_queue : PoolList,
}
LRU_init :: proc( cache : ^LRU_Cache, capacity : u32 )
{
error : AllocatorError
cache.capacity = i32(capacity)
cache.table, error = make( HMapChained(LRU_Link), uint(capacity) )
assert( error != .None, "VEFontCache.LRU_init : Failed to allocate cache's table")
}

View File

@ -1,3 +1,204 @@
/*
A port of (https://github.com/hypernewbie/VEFontCache) to Odin.
Status:
This port is heavily tied to the grime package in SectrPrototype.
TODO(Ed): Make an idiomatic port of this for Odin (or just dupe the data structures...)
*/
package VEFontCache
Font_ID :: i64
Glyph :: i32
Colour :: [4]f32
Vec2 :: [2]f32
Vec2i :: [2]u32
AtlasRegionKind :: enum {
A = 0,
B = 1,
C = 2,
D = 3
}
Vertex :: struct {
pos : Vec2,
u, v : f32,
}
// GlyphDrawBuffer :: struct {
// over_sample : Vec2,
// batch : i32,
// width : i32,
// height : i32,
// padding : i32,
// }
ShapedText :: struct {
Glyphs : Array(Glyph),
Positions : Array(Vec2),
end_cursor_pos : Vec2,
}
ShapedTextCache :: struct {
storage : Array(ShapedText),
state : LRU_Cache,
next_cache_id : i32,
}
Entry :: struct {
parser_info : ParserInfo,
shaper_info : ShaperInfo,
id : Font_ID,
used : b32,
size : f32,
size_scale : f32,
}
Entry_Default :: Entry {
id = 0,
used = false,
size = 24.0,
size_scale = 1.0,
}
Context :: struct {
backing : Allocator,
parser_kind : ParserKind,
parser_ctx : ParserContext,
shaper_ctx : ShaperContext,
entries : Array(Entry),
temp_path : Array(Vec2),
temp_codepoint_seen : HMapChained(bool),
snap_width : u32,
snap_height : u32,
colour : Colour,
cursor_pos : Vec2,
draw_list : DrawList,
atlas : Atlas,
shape_cache : ShapedTextCache,
text_shape_adv : b32,
}
Module_Ctx :: Context
InitAtlasRegionParams :: struct {
width : u32,
height : u32,
offset : Vec2i,
}
InitAtlasParams :: struct {
width : u32,
height : u32,
glyph_padding : u32,
region_a : InitAtlasRegionParams,
region_b : InitAtlasRegionParams,
region_c : InitAtlasRegionParams,
region_d : InitAtlasRegionParams,
}
InitAtlasParams_Default :: InitAtlasParams {
width = 4 * Kilobyte,
height = 2 * Kilobyte,
glyph_padding = 1,
}
InitGlyphDrawParams :: struct {
over_sample : Vec2i,
buffer_batch : u32,
padding : u32,
}
InitGlyphDrawParams_Default :: InitGlyphDrawParams {
over_sample = { 4, 4 },
buffer_batch = 4,
padding = InitAtlasParams_Default.glyph_padding,
}
InitShapeCacheParams :: struct {
capacity : u32,
reserve_length : u32,
}
InitShapeCacheParams_Default :: InitShapeCacheParams {
capacity = 256,
reserve_length = 64,
}
init :: proc( ctx : ^Context,
allocator := context.allocator,
atlas_params := InitAtlasParams_Default,
glyph_draw_params := InitGlyphDrawParams_Default,
shape_cache_params := InitShapeCacheParams_Default,
advance_snap_smallfont_size : u32 = 12,
entires_reserve : u32 = Kilobyte,
temp_path_reserve : u32 = Kilobyte,
temp_codepoint_seen_reserve : u32 = 4 * Kilobyte,
)
{
assert( ctx != nil, "Must provide a valid context" )
using ctx
ctx.backing = allocator
context.allocator = ctx.backing
error : AllocatorError
entries, error = make( Array(Entry), u64(entires_reserve) )
assert(error == .None, "VEFontCache.init : Failed to allocate entries")
temp_path, error = make( Array(Vec2), u64(temp_path_reserve) )
assert(error == .None, "VEFontCache.init : Failed to allocate temp_path")
temp_codepoint_seen, error = make( HMapChained(bool), uint(temp_codepoint_seen_reserve) )
assert(error == .None, "VEFontCache.init : Failed to allocate temp_path")
draw_list.vertices, error = make( Array(Vertex), 4 * Kilobyte )
assert(error == .None, "VEFontCache.init : Failed to allocate draw_list.vertices")
draw_list.indices, error = make( Array(u32), 8 * Kilobyte )
assert(error == .None, "VEFontCache.init : Failed to allocate draw_list.indices")
draw_list.calls, error = make( Array(DrawCall), 512 )
assert(error == .None, "VEFontCache.init : Failed to allocate draw_list.calls")
init_atlas_region :: proc( region : ^AtlasRegion, params : InitAtlasParams, region_params : InitAtlasRegionParams ) {
using region
next_idx = 0;
width = region_params.width
height = region_params.height
size = {
params.width / 4,
params.height / 2,
}
capacity = {
size.x / width,
size.y / height,
}
offset = region_params.offset
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, capacity.x * capacity.y )
}
init_atlas_region( & atlas.region_a, atlas_params, atlas_params.region_a )
init_atlas_region( & atlas.region_b, atlas_params, atlas_params.region_b )
init_atlas_region( & atlas.region_c, atlas_params, atlas_params.region_c )
init_atlas_region( & atlas.region_d, atlas_params, atlas_params.region_d )
}

View File

@ -0,0 +1,34 @@
package VEFontCache
GlyphUpdateBatch :: struct {
update_batch_x : i32,
clear_draw_list : DrawList,
draw_list : DrawList,
}
AtlasRegion :: struct {
state : LRU_Cache,
width : u32,
height : u32,
size : Vec2i,
capacity : Vec2i,
offset : Vec2i,
next_idx : u32,
}
Atlas :: struct {
width : u32,
height : u32,
glyph_pad : u16,
region_a : AtlasRegion,
region_b : AtlasRegion,
region_c : AtlasRegion,
region_d : AtlasRegion,
using glyph_update_batch : GlyphUpdateBatch,
}

View File

@ -0,0 +1,32 @@
package VEFontCache
FrameBufferPass :: enum {
Glyph = 1,
Atlas = 2,
Target = 3,
Target_Unchanged = 4,
}
DrawCall :: struct {
pass : u32,
start_index : u32,
end_index : u32,
clear_before_draw : b32,
region : AtlasRegionKind,
colour : [4]f32,
}
DrawCall_Default :: DrawCall {
pass = 0,
start_index = 0,
end_index = 0,
clear_before_draw = false,
region = .A,
colour = { 1.0, 1.0, 1.0, 1.0 }
}
DrawList :: struct {
vertices : Array(Vertex),
indices : Array(u32),
calls : Array(DrawCall),
}

View File

@ -0,0 +1,84 @@
package VEFontCache
import "core:mem"
Kilobyte :: mem.Kilobyte
Allocator :: mem.Allocator
AllocatorError :: mem.Allocator_Error
import "codebase:grime"
// asserts
ensure :: grime.ensure
verify :: grime.verify
// container
Array :: grime.Array
array_init :: grime.array_init
array_append :: grime.array_append
array_append_at :: grime.array_append_at
array_back :: grime.array_back
array_clear :: grime.array_clear
array_free :: grime.array_free
array_remove_at :: grime.array_remove_at
array_to_slice :: grime.array_to_slice
array_to_slice_cpacity :: grime.array_to_slice_capacity
array_underlying_slice :: grime.array_underlying_slice
HMapChained :: grime.HMapChained
hmap_chained_init :: grime.hmap_chained_init
// Pool :: grime.Pool
StackFixed :: grime.StackFixed
stack_clear :: grime.stack_clear
stack_push :: grime.stack_push
stack_pop :: grime.stack_pop
stack_peek_ref :: grime.stack_peek_ref
stack_peek :: grime.stack_peek
stack_push_contextless :: grime.stack_push_contextless
//#region("Proc overload mappings")
append :: proc {
grime.array_append_array,
grime.array_append_slice,
grime.array_append_value,
}
append_at :: proc {
grime.array_append_at_slice,
grime.array_append_at_value,
}
clear :: proc {
array_clear,
}
delete :: proc {
array_free,
}
make :: proc {
array_init,
hmap_chained_init,
}
remove_at :: proc {
array_remove_at,
}
to_slice :: proc {
array_to_slice,
}
underlying_slice :: proc {
array_underlying_slice,
}
//#endregion("Proc overload mappings")

View File

@ -0,0 +1,19 @@
package VEFontCache
import stbtt "vendor:stb/truetype"
import freetype "thirdparty:freetype"
ParserKind :: enum u32 {
stb_true_type,
freetype,
}
ParserInfo :: struct #raw_union {
stbtt_info : stbtt.fontinfo,
freetype_info : freetype.Face
}
ParserContext :: struct {
ft_library : freetype.Library
}

View File

@ -0,0 +1,14 @@
package VEFontCache
import "thirdparty:harfbuzz"
ShaperContext :: struct {
hb_buffer : harfbuzz.Buffer,
}
ShaperInfo :: struct {
blob : harfbuzz.Blob,
face : harfbuzz.Face,
font : harfbuzz.Font,
}

View File

@ -0,0 +1,3 @@
package sectr
import "codebase:font/VEFontCache"