Progress on VEFontCache port, only cache_glyph_to_atlas & shape_text_uncached left

This commit is contained in:
Edward R. Gonzalez 2024-06-04 18:44:12 -04:00
parent 26e53bf327
commit 991e7a81c0
8 changed files with 488 additions and 175 deletions

View File

@ -18,8 +18,8 @@ PoolList :: struct {
free_list : Array( PoolListIter ),
front : PoolListIter,
back : PoolListIter,
size : i32,
capacity : i32,
size : u32,
capacity : u32,
}
pool_list_init :: proc( pool : ^PoolList, capacity : u32 )
@ -33,7 +33,7 @@ pool_list_init :: proc( pool : ^PoolList, capacity : u32 )
assert( error == .None, "VEFontCache.pool_list_init : Failed to allocate free_list array")
array_resize( & pool.items, u64(capacity) )
pool.capacity = i32(capacity)
pool.capacity = capacity
for id in 0 ..< capacity do pool.free_list.data[id] = i32(id)
}
@ -61,7 +61,7 @@ pool_list_erase :: proc( pool : ^PoolList, iter : PoolListIter )
{
using pool
if size <= 0 do return
assert( iter >= 0 && iter < capacity )
assert( iter >= 0 && iter < i32(capacity) )
assert( free_list.num == u64(capacity - size) )
iter_node := & items.data[ iter ]
@ -108,14 +108,15 @@ LRU_Link :: struct {
}
LRU_Cache :: struct {
capacity : i32,
capacity : u32,
num : u32,
table : HMapChained(LRU_Link),
key_queue : PoolList,
}
LRU_init :: proc( cache : ^LRU_Cache, capacity : u32 ) {
error : AllocatorError
cache.capacity = i32(capacity)
cache.capacity = capacity
cache.table, error = make( HMapChained(LRU_Link), hmap_closest_prime( uint(capacity)) )
assert( error != .None, "VEFontCache.LRU_init : Failed to allocate cache's table")
@ -167,12 +168,15 @@ LRU_put :: proc( cache : ^LRU_Cache, key : u64, value : i32 ) -> u64 {
if cache.key_queue.size >= cache.capacity {
evict = pool_list_pop_back( & cache.key_queue )
hmap_chained_remove( cache.table, evict )
cache.num -= 1
}
pool_list_push_front( & cache.key_queue, key )
link := LRU_find( cache, key )
link.value = value
link.ptr = cache.key_queue.front
hmap_chained_set( cache.table, key, LRU_Link {
value = value,
ptr = cache.key_queue.front
})
cache.num += 1
return evict
}

View File

@ -7,7 +7,7 @@ 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...)
Changes:
- Support for freetype(WIP), only supports processing true type formatted data however
- Support for freetype(WIP)
- Font Parser & Glyph Shaper are abstracted to their own interface
- Font Face parser info stored separately from entries
- ve_fontcache_loadfile not ported (just use odin's core:os or os2), then call load_font
@ -77,8 +77,9 @@ Context :: struct {
entries : Array(Entry),
temp_path : Array(Vec2),
temp_codepoint_seen : HMapChained(bool),
temp_path : Array(Vec2),
temp_codepoint_seen : HMapChained(bool),
temp_codepoint_seen_num : u32,
snap_width : u32,
snap_height : u32,
@ -96,10 +97,19 @@ Context :: struct {
debug_print_verbose : b32
}
font_key_from_label :: proc( label : string ) -> u64 {
get_cursor_pos :: proc( ctx : ^Context ) -> Vec2 { return ctx.cursor_pos }
set_colour :: proc( ctx : ^Context, colour : Colour ) { ctx.colour = colour }
font_glyph_lru_code :: #force_inline proc( font : FontID, glyph_index : Glyph ) -> (lru_code : u64)
{
lru_code = u64(glyph_index) + ( ( 0x100000000 * u64(font) ) & 0xFFFFFFFF00000000 )
return
}
font_key_from_label :: #force_inline proc( label : string ) -> u64 {
hash : u64
for str_byte in transmute([]byte) label {
hash = ((hash << 5) + hash) + u64(str_byte)
hash = ((hash << 8) + hash) + u64(str_byte)
}
return hash
}
@ -175,7 +185,7 @@ init :: proc( ctx : ^Context,
advance_snap_smallfont_size : u32 = 12,
entires_reserve : u32 = Kilobyte,
temp_path_reserve : u32 = Kilobyte,
temp_codepoint_seen_reserve : u32 = 4 * Kilobyte,
temp_codepoint_seen_reserve : u32 = 512,
)
{
assert( ctx != nil, "Must provide a valid context" )
@ -438,7 +448,7 @@ cache_glyph :: proc( ctx : ^Context, font : FontID, glyph_index : Glyph, scale,
}
// Note(Original Author): Figure out scaling so it fits within our box.
draw : DrawCall
draw := DrawCall_Default
draw.pass = FrameBufferPass.Glyph
draw.start_index = u32(ctx.draw_list.indices.num)
@ -503,162 +513,96 @@ cache_glyph :: proc( ctx : ^Context, font : FontID, glyph_index : Glyph, scale,
return false
}
decide_codepoint_region :: proc( ctx : ^Context, entry : ^Entry, glyph_index : Glyph
) -> (region : AtlasRegionKind, state : ^LRU_Cache, next_idx : ^u32, over_sample : ^Vec2)
{
if parser_is_glyph_empty( entry.parser_info, glyph_index ) {
region = .None
}
screenspace_x_form :: proc( position, scale : ^Vec2, width, height : f32 ) {
scale.x = (scale.x / width ) * 2.0
scale.y = (scale.y / height) * 2.0
position.x = position.x * (2.0 / width) - 1.0
position.y = position.y * (2.0 / width) - 1.0
}
textspace_x_form :: proc( position, scale : ^Vec2, width, height : f32 ) {
position.x /= width
position.y /= height
scale.x /= width
scale.y /= height
}
cache_glyph_to_atlas :: proc( ctx : ^Context, font : FontID, glyph_index : Glyph )
{
assert( ctx != nil )
assert( font >= 0 && font < FontID(ctx.entries.num) )
entry := & ctx.entries.data[ font ]
if glyph_index == 0 do return
if parser_is_glyph_empty( entry.parser_info, glyph_index ) do return
// Get hb_font text metrics. These are unscaled!
bounds_0, bounds_1 := parser_get_glyph_box( entry.parser_info, glyph_index )
bounds_width := bounds_1.x - bounds_0.x
bounds_height := bounds_1.y - bounds_0.y
atlas := & ctx.atlas
region, state, next_idx, over_sample := decide_codepoint_region( ctx, entry, glyph_index )
bounds_width_scaled := cast(u32) (f32(bounds_width) * entry.size_scale + 2.0 * f32(atlas.glyph_padding))
bounds_height_scaled := cast(u32) (f32(bounds_height) * entry.size_scale + 2.0 * f32(atlas.glyph_padding))
// E region is special case and not cached to atlas.
if region == .None || region == .E do return
if bounds_width_scaled <= atlas.region_a.width && bounds_height_scaled <= atlas.region_a.height
}
is_empty :: proc( ctx : ^Context, entry : ^Entry, glyph_index : Glyph ) -> b32
{
if glyph_index == 0 do return true
if parser_is_glyph_empty( entry.parser_info, glyph_index ) do return true
return false
}
reset_batch_codepoint_state :: proc( ctx : ^Context ) {
clear( ctx.temp_codepoint_seen )
ctx.temp_codepoint_seen_num = 0
}
shape_text_cached :: proc( ctx : ^Context, font : FontID, text_utf8 : string ) -> ^ShapedText
{
ELFhash64 :: proc( hash : ^u64, ptr : ^( $Type), count := 1 )
{
// Region A for small glyphs. These are good for things such as punctuation.
region = .A
state = & atlas.region_a.state
next_idx = & atlas.region_a.next_idx
x := u64(0)
bytes = transmute( [^]byte) ptr
for index : i32 = 0; index < i32( size_of(Type)); index += 1 {
(hash^) = ((hash^) << 4 ) + bytes[index]
x = (hash^) & 0xF000000000000000
if x != 0 {
(hash^) ~= (x >> 24)
}
(hash^) &= ~x
}
}
else if bounds_width_scaled <= atlas.region_b.width && bounds_height_scaled <= atlas.region_b.height
hash := cast(u64) 0x9f8e00d51d263c24;
shape_cache := & ctx.shape_cache
state := & ctx.shape_cache.state
shape_cache_idx := LRU_get( state, hash )
if shape_cache_idx == -1
{
// Region B for tall glyphs. These are good for things such as european alphabets.
region = .B
state = & atlas.region_b.state
next_idx = & atlas.region_b.next_idx
}
else if bounds_width_scaled <= atlas.region_c.width && bounds_height_scaled <= atlas.region_c.height
{
// Region C for big glyphs. These are good for things such as asian typography.
region = .C
state = & atlas.region_c.state
next_idx = & atlas.region_c.next_idx
}
else if bounds_width_scaled <= atlas.region_d.width && bounds_height_scaled <= atlas.region_d.height
{
// Region D for huge glyphs. These are good for things such as titles and 4k.
region = .D
state = & atlas.region_d.state
next_idx = & atlas.region_d.next_idx
}
else if bounds_width_scaled <= atlas.buffer_width && bounds_height_scaled <= atlas.buffer_height
{
// Region 'E' for massive glyphs. These are rendered uncached and un-oversampled.
region = .E
state = nil
next_idx = nil
if bounds_width_scaled <= atlas.buffer_width / 2 && bounds_height_scaled <= atlas.buffer_height / 2
{
(over_sample^) = { 2.0, 2.0 }
if shape_cache.next_cache_id < i32(state.capacity) {
shape_cache_idx = shape_cache.next_cache_id
LRU_put( state, hash, shape_cache_idx )
}
else
{
(over_sample^) = { 1.0, 1.0 }
next_evict_idx := LRU_get_next_evicted( state )
assert( next_evict_idx != 0xFFFFFFFFFFFFFFFF )
shape_cache_idx = LRU_peek( state, next_evict_idx )
assert( shape_cache_idx != - 1 )
LRU_put( state, hash, shape_cache_idx )
}
return
}
else {
region = .None
return
}
assert(state != nil)
assert(next_idx != nil)
return
}
flush_glyph_buffer_to_atlas :: proc()
{
}
screenspace_x_form :: proc()
{
}
textspace_x_form :: proc()
{
}
atlas_bbox :: proc()
{
}
cache_glyph_to_atlas :: proc()
{
return & shape_cache.storage.data[ shape_cache_idx ]
}
shape_text_uncached :: proc()
{
}
ELFhash64 :: proc()
{
}
shape_text_cached :: proc()
{
}
directly_draw_massive_glyph :: proc()
{
}
empty :: proc()
{
}
draw_cached_glyph :: proc()
{
}
reset_batch_codepoint_state :: proc()
{
}
can_batch_glyph :: proc()
{
}
draw_text_batch :: proc()
{
}
draw_text :: proc()
{
}
get_cursor_pos :: proc()
{
}
optimize_draw_list :: proc()
{
}
set_colour :: proc()
{
}

View File

@ -1,17 +1,5 @@
package VEFontCache
GlyphDrawBuffer :: struct {
over_sample : Vec2,
buffer_batch : u32,
buffer_width : u32,
buffer_height : u32,
draw_padding : u32,
update_batch_x : i32,
clear_draw_list : DrawList,
draw_list : DrawList,
}
AtlasRegion :: struct {
state : LRU_Cache,
@ -38,3 +26,162 @@ Atlas :: struct {
using glyph_update_batch : GlyphDrawBuffer,
}
atlas_bbox :: proc( atlas : ^Atlas, region : AtlasRegionKind, local_idx : u32 ) -> (position : Vec2, width, height : f32)
{
switch region
{
case .A:
width = f32(atlas.region_a.width)
height = f32(atlas.region_b.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)
case .B:
width = f32(atlas.region_b.width)
height = f32(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)
case .C:
width = f32(atlas.region_c.width)
height = f32(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)
case .D:
width = f32(atlas.region_d.width)
height = f32(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)
case .None: fallthrough
case .E:
assert(false, "What?")
}
return
}
can_batch_glyph :: proc( ctx : ^Context, font : FontID, entry : ^Entry, glyph_index : Glyph ) -> b32
{
assert( ctx != nil )
assert( entry.id == font )
// Decide which atlas to target
assert( glyph_index != -1 )
region, state, next_index, over_sample := decide_codepoint_region( ctx, entry, glyph_index )
// E region can't batch
if region == .E || region == .None do return false
if ctx.temp_codepoint_seen_num > 1024 do return false
// Note(Ed): Why 1024?
// Is this glyph cached?
// lru_code := u64(glyph_index) + ( ( 0x100000000 * u64(font) ) & 0xFFFFFFFF00000000 )
lru_code := font_glyph_lru_code(font, glyph_index)
atlas_index := LRU_get( state, lru_code )
if atlas_index == - 1
{
if (next_index^) >= u32(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( state )
seen := get( ctx.temp_codepoint_seen, next_evict_codepoint )
assert(seen != nil)
if (seen^) {
return false
}
}
cache_glyph_to_atlas( ctx, font, glyph_index )
}
assert( LRU_get( state, lru_code ) != 1 )
set( ctx.temp_codepoint_seen, lru_code, true )
ctx.temp_codepoint_seen_num += 1
return true
}
decide_codepoint_region :: proc( ctx : ^Context, entry : ^Entry, glyph_index : Glyph
) -> (region : AtlasRegionKind, state : ^LRU_Cache, next_idx : ^u32, over_sample : Vec2)
{
if parser_is_glyph_empty( entry.parser_info, glyph_index ) {
region = .None
}
bounds_0, bounds_1 := parser_get_glyph_box( entry.parser_info, glyph_index )
bounds_width := bounds_1.x - bounds_0.x
bounds_height := bounds_1.y - bounds_0.y
atlas := & ctx.atlas
bounds_width_scaled := cast(u32) (f32(bounds_width) * entry.size_scale + 2.0 * f32(atlas.glyph_padding))
bounds_height_scaled := cast(u32) (f32(bounds_height) * entry.size_scale + 2.0 * f32(atlas.glyph_padding))
if bounds_width_scaled <= atlas.region_a.width && bounds_height_scaled <= atlas.region_a.height
{
// Region A for small glyphs. These are good for things such as punctuation.
region = .A
state = & atlas.region_a.state
next_idx = & atlas.region_a.next_idx
}
else if bounds_width_scaled <= atlas.region_b.width && bounds_height_scaled <= atlas.region_b.height
{
// Region B for tall glyphs. These are good for things such as european alphabets.
region = .B
state = & atlas.region_b.state
next_idx = & atlas.region_b.next_idx
}
else if bounds_width_scaled <= atlas.region_c.width && bounds_height_scaled <= atlas.region_c.height
{
// Region C for big glyphs. These are good for things such as asian typography.
region = .C
state = & atlas.region_c.state
next_idx = & atlas.region_c.next_idx
}
else if bounds_width_scaled <= atlas.region_d.width && bounds_height_scaled <= atlas.region_d.height
{
// Region D for huge glyphs. These are good for things such as titles and 4k.
region = .D
state = & atlas.region_d.state
next_idx = & atlas.region_d.next_idx
}
else if bounds_width_scaled <= atlas.buffer_width && bounds_height_scaled <= atlas.buffer_height
{
// Region 'E' for massive glyphs. These are rendered uncached and un-oversampled.
region = .E
state = nil
next_idx = nil
if bounds_width_scaled <= atlas.buffer_width / 2 && bounds_height_scaled <= atlas.buffer_height / 2 {
over_sample = { 2.0, 2.0 }
}
else {
over_sample = { 1.0, 1.0 }
}
return
}
else {
region = .None
return
}
assert(state != nil)
assert(next_idx != nil)
return
}

View File

@ -1,13 +1,5 @@
package VEFontCache
FrameBufferPass :: enum u32 {
None = 0,
Glyph = 1,
Atlas = 2,
Target = 3,
Target_Unchanged = 4,
}
DrawCall :: struct {
pass : FrameBufferPass,
start_index : u32,
@ -32,6 +24,26 @@ DrawList :: struct {
calls : Array(DrawCall),
}
FrameBufferPass :: enum u32 {
None = 0,
Glyph = 1,
Atlas = 2,
Target = 3,
Target_Unchanged = 4,
}
GlyphDrawBuffer :: struct {
over_sample : Vec2,
buffer_batch : u32,
buffer_width : u32,
buffer_height : u32,
draw_padding : u32,
update_batch_x : i32,
clear_draw_list : DrawList,
draw_list : DrawList,
}
blit_quad :: proc( draw_list : ^DrawList, p0, p1 : Vec2, uv0, uv1 : Vec2 )
{
v_offset := cast(u32) draw_list.vertices.num
@ -77,6 +89,87 @@ clear_draw_list :: proc( draw_list : ^DrawList ) {
clear( draw_list.vertices )
}
directly_draw_massive_glyph :: proc( ctx : ^Context, entry : ^Entry, glyph : Glyph, bounds_0 : Vec2i, bounds_width, bounds_height : u32, over_sample, position, scale : Vec2 )
{
}
draw_cached_glyph :: proc( ctx : ^Context, entry : ^Entry, glyph_index : Glyph, position, scale : Vec2 ) -> b32
{
// Glyph not in current font
if glyph_index == 0 do return true
if parser_is_glyph_empty( entry.parser_info, glyph_index ) do return true
bounds_0, bounds_1 := parser_get_glyph_box( entry.parser_info, glyph_index )
bounds_width := bounds_1.x - bounds_0.x
bounds_height := bounds_1.y - bounds_0.y
// Decide which atlas to target
region, state, next_idx, over_sample := decide_codepoint_region( ctx, entry, glyph_index )
// E region is special case and not cached to atlas
if region == .E
{
directly_draw_massive_glyph( ctx, entry, glyph_index, bounds_0, bounds_width, bounds_height, over_sample, position, scale )
return true
}
// Is this codepoint cached?
// lru_code := u64(glyph_index) + ( ( 0x100000000 * u64(entry.id) ) & 0xFFFFFFFF00000000 )
lru_code := font_glyph_lru_code(entry.id, glyph_index)
atlas_index := LRU_get( state, lru_code )
if atlas_index == - 1 {
return false
}
atlas := & ctx.atlas
// Figure out the source bounding box in the atlas texture
position, width, height := atlas_bbox( atlas, region, u32(atlas_index) )
glyph_position := position
glyph_width := f32(bounds_width) * entry.size_scale
glyph_height := f32(bounds_height) * entry.size_scale
glyph_width += 2 * f32(atlas.glyph_padding)
glyph_height += 2 * f32(atlas.glyph_padding)
glyph_scale := Vec2 { glyph_width, glyph_height }
bounds_0_scaled := Vec2{ f32(bounds_0.x), f32(bounds_0.y) } * entry.size_scale - { 0.5, 0.5 }
dst := Vec2 {
position.x + scale.x * bounds_0_scaled.x,
position.y + scale.y * bounds_0_scaled.y,
}
dst_width := scale.x * glyph_width
dst_height := scale.y * glyph_height
dst -= scale * { f32(atlas.glyph_padding), f32(atlas.glyph_padding) }
dst_scale := Vec2 { dst_width, dst_height }
textspace_x_form( & glyph_position, & dst_scale, f32(atlas.buffer_width), f32(atlas.buffer_height) )
// Add the glyph drawcall
call := DrawCall_Default
{
using call
pass = .Target_Unchanged
colour = ctx.colour
start_index = cast(u32) ctx.draw_list.indices.num
blit_quad( & ctx.draw_list, dst, dst + dst_scale, glyph_position, glyph_position + glyph_scale )
end_index = cast(u32) ctx.draw_list.indices.num
}
append( & ctx.draw_list.calls, call )
// Clear glyph_update_FBO
call.pass = .Glyph
call.start_index = 0
call.end_index = 0
call.clear_before_draw = true
append( & ctx.draw_list.calls, call )
return true
}
// Constructs a triangle fan to fill a shape using the provided path
// outside_point represents the center point of the fan.
//
@ -125,12 +218,92 @@ draw_filled_path :: proc( draw_list : ^DrawList, outside_point : Vec2, path : []
}
}
draw_text :: proc( ctx : ^Context, font : FontID, text_utf8 : string, position : Vec2, scale : Vec2 ) -> b32
{
assert( ctx != nil )
assert( font > 0 && font < FontID(ctx.entries.num) )
shaped := shape_text_cached( ctx, font, text_utf8 )
snap_width := f32(ctx.snap_width)
snap_height := f32(ctx.snap_height)
position := position
if ctx.snap_width > 0 do position.x = (position.x * snap_width + 0.5) / snap_width
if ctx.snap_height > 0 do position.y = (position.y * snap_height + 0.5) / snap_height
entry := & ctx.entries.data[ font ]
batch_start_idx : i32 = 0
for index : i32 = 0; index < i32(shaped.glyphs.num); index += 1
{
glyph_index := shaped.glyphs.data[ index ]
if is_empty( ctx, entry, glyph_index ) do continue
if can_batch_glyph( ctx, font, entry, glyph_index ) do continue
draw_text_batch( ctx, entry, shaped, batch_start_idx, index, position, scale )
reset_batch_codepoint_state( ctx )
cache_glyph_to_atlas( ctx, font, glyph_index )
// lru_code := u64(glyph_index) + ( ( 0x100000000 * u64(font) ) & 0xFFFFFFFF00000000 )
lru_code := font_glyph_lru_code(font, glyph_index)
set( ctx.temp_codepoint_seen, lru_code, true )
ctx.temp_codepoint_seen_num += 1
batch_start_idx = index
}
draw_text_batch( ctx, entry, shaped, batch_start_idx, i32(shaped.glyphs.num), position, scale )
reset_batch_codepoint_state( ctx )
ctx.cursor_pos.x = position.x + shaped.end_cursor_pos.x * scale.x
ctx.cursor_pos.y = position.y + shaped.end_cursor_pos.y * scale.y
return true
}
draw_text_batch :: proc( ctx : ^Context, entry : ^Entry, shaped : ^ShapedText, batch_start_idx, batch_end_idx : i32, position, scale : Vec2 )
{
flush_glyph_buffer_to_atlas( ctx )
for index := batch_start_idx; index < batch_end_idx; index += 1
{
glyph_index := shaped.glyphs.data[ index ]
shaped_position := shaped.positions.data[index]
glyph_translate_x := position.x + shaped_position.x * scale.x
glyph_translate_y := position.y + shaped_position.y * scale.y
glyph_cached := draw_cached_glyph( ctx, entry, glyph_index, {glyph_translate_x, glyph_translate_y}, scale)
assert( glyph_cached == true )
}
}
// ve_fontcache_flush_drawlist
flush_draw_list :: proc( ctx : ^Context ) {
assert( ctx != nil )
clear_draw_list( & ctx.draw_list )
}
flush_glyph_buffer_to_atlas :: proc( ctx : ^Context )
{
// Flush drawcalls to draw list
merge_draw_list( & ctx.draw_list, & ctx.atlas.clear_draw_list )
merge_draw_list( & ctx.draw_list, & ctx.atlas.draw_list)
clear_draw_list( & ctx.atlas.draw_list )
clear_draw_list( & ctx.atlas.clear_draw_list )
// Clear glyph_update_FBO
if ctx.atlas.update_batch_x != 0
{
call := DrawCall_Default
call.pass = .Glyph
call.start_index = 0
call.end_index = 0
call.clear_before_draw = true
append( & ctx.draw_list.calls, call )
ctx.atlas.update_batch_x = 0
}
}
// ve_fontcache_drawlist
get_draw_list :: proc( ctx : ^Context ) -> ^DrawList {
assert( ctx != nil )
@ -164,3 +337,35 @@ merge_draw_list :: proc( dst, src : ^DrawList )
assert( error == .None )
}
}
optimize_draw_list :: proc( ctx : ^Context )
{
assert( ctx != nil )
write_index : i32 = 0
for index : i32 = 0; index < i32(ctx.draw_list.calls.num); index += 1
{
assert( write_index <= index )
draw_0 := & ctx.draw_list.calls.data[ write_index ]
draw_1 := & ctx.draw_list.calls.data[ index ]
merge : b32 = true
if draw_0.pass != draw_1.pass do merge = false
if draw_0.end_index != draw_1.start_index do merge = false
if draw_0.region != draw_1.region do merge = false
if draw_1.clear_before_draw do merge = false
if draw_0.colour != draw_1.colour do merge = false
if merge {
draw_0.end_index = draw_1.end_index
draw_1.start_index = draw_1.end_index
}
else {
write_index += 1
draw_2 := & ctx.draw_list.calls.data[ write_index ]
if write_index != index do draw_2 = draw_1
}
}
resize( & ctx.draw_list.calls, u64(write_index + 1) )
}

View File

@ -40,6 +40,7 @@ array_underlying_slice :: grime.array_underlying_slice
HMapChained :: grime.HMapChained
hmap_chained_clear :: grime.hmap_chained_clear
hmap_chained_init :: grime.hmap_chained_init
hmap_chained_get :: grime.hmap_chained_get
hmap_chained_remove :: grime.hmap_chained_remove
@ -76,6 +77,7 @@ append_at :: proc {
clear :: proc {
array_clear,
hmap_chained_clear,
}
delete :: proc {
@ -95,6 +97,10 @@ remove_at :: proc {
array_remove_at,
}
resize :: proc {
array_resize,
}
set :: proc {
hmap_chained_set,
}

View File

@ -5,6 +5,8 @@ Notes:
Freetype will do memory allocations and has an interface the user can implement.
That interface is not exposed from this parser but could be added to parser_init.
STB_Truetype has macros for its allocation unfortuantely
*/
import "core:c"
@ -383,7 +385,12 @@ parser_get_glyph_box :: proc( font : ^ParserFontInfo, glyph_index : Glyph ) -> (
switch font.kind
{
case .Freetype:
freetype.load_glyph( font.freetype_info, c.uint(glyph_index), { .No_Bitmap, .No_Hinting, .No_Scale } )
metrics := font.freetype_info.glyph.metrics
bounds_0 = {u32(metrics.hori_bearing_x), u32(metrics.hori_bearing_y - metrics.height)}
bounds_1 = {u32(metrics.hori_bearing_x + metrics.width), u32(metrics.hori_bearing_y)}
case .STB_TrueType:
x0, y0, x1, y1 : i32

View File

@ -89,7 +89,7 @@ hmap_chained_clear :: proc( using self : HMapChained($Type))
if slot == nil {
continue
}
for probe_slot = slot.next; probe_slot != nil; probe_slot = probe_slot.next {
for probe_slot := slot.next; probe_slot != nil; probe_slot = probe_slot.next {
slot.occupied = false
}
slot.occupied = false

View File

@ -257,7 +257,7 @@ ui_key_from_string :: #force_inline proc "contextless" ( value : string ) -> UI_
when USE_RAD_DEBUGGERS_METHOD {
hash : u64
for str_byte in transmute([]byte) value {
hash = ((hash << 5) + hash) + u64(str_byte)
hash = ((hash << 8) + hash) + u64(str_byte)
}
key = cast(UI_Key) hash
}