WIP - VEFontCahe: More progress on optimizing codepaths with SOA
This commit is contained in:
parent
dd1752f84b
commit
4adbfaa9fd
@ -243,7 +243,7 @@ lru_peek :: #force_inline proc "contextless" ( cache : ^LRU_Cache, key : u64, mu
|
||||
|
||||
lru_put :: #force_inline proc( cache : ^LRU_Cache, key : u64, value : i32 ) -> u64
|
||||
{
|
||||
profile(#procedure)
|
||||
// profile(#procedure)
|
||||
if link, ok := & cache.table[ key ]; ok {
|
||||
pool_list_move_to_front( & cache.key_queue, link.ptr )
|
||||
link.value = value
|
||||
|
@ -34,6 +34,8 @@ Atlas :: struct {
|
||||
region_b : Atlas_Region,
|
||||
region_c : Atlas_Region,
|
||||
region_d : Atlas_Region,
|
||||
|
||||
regions : [4] ^Atlas_Region,
|
||||
}
|
||||
|
||||
atlas_bbox :: #force_inline proc "contextless" ( atlas : ^Atlas, region : Atlas_Region_Kind, local_idx : i32 ) -> (position, size: Vec2)
|
||||
@ -86,32 +88,27 @@ atlas_bbox :: #force_inline proc "contextless" ( atlas : ^Atlas, region : Atlas_
|
||||
return
|
||||
}
|
||||
|
||||
decide_codepoint_region :: #force_inline proc (ctx : ^Context, entry : ^Entry, glyph_index : Glyph ) -> (region_kind : Atlas_Region_Kind, region : ^Atlas_Region, over_sample : Vec2)
|
||||
atlas_region_bbox :: proc( region : Atlas_Region, local_idx : i32 ) -> (position, size: Vec2)
|
||||
{
|
||||
size.x = f32(region.width)
|
||||
size.y = f32(region.height)
|
||||
|
||||
position.x = cast(f32) (( local_idx % region.capacity.x ) * region.width)
|
||||
position.y = cast(f32) (( local_idx / region.capacity.x ) * region.height)
|
||||
|
||||
position.x += f32(region.offset.x)
|
||||
position.y += f32(region.offset.y)
|
||||
return
|
||||
}
|
||||
|
||||
decide_codepoint_region :: #force_inline proc (atlas : Atlas, glyph_buffer : Glyph_Draw_Buffer, size_scale : f32, glyph_index : Glyph, bounds_size : Vec2 ) -> (region_kind : Atlas_Region_Kind, over_sample : Vec2)
|
||||
{
|
||||
profile(#procedure)
|
||||
if parser_is_glyph_empty(&entry.parser_info, glyph_index) {
|
||||
return .None, nil, {}
|
||||
}
|
||||
glyph_padding_dbl := atlas.glyph_padding * 2
|
||||
bounds_size_scaled := bounds_size * size_scale + glyph_padding_dbl
|
||||
|
||||
bounds_0, bounds_1 := parser_get_glyph_box(&entry.parser_info, glyph_index)
|
||||
bounds_size := vec2(bounds_1) - vec2(bounds_0)
|
||||
|
||||
atlas := & ctx.atlas
|
||||
glyph_buffer := & ctx.glyph_buffer
|
||||
glyph_padding_dbl := atlas.glyph_padding * 2
|
||||
|
||||
bounds_size_scaled := bounds_size * entry.size_scale * atlas.glyph_over_scalar + glyph_padding_dbl
|
||||
|
||||
// Use a lookup table for faster region selection
|
||||
region_lookup := [4]struct { kind: Atlas_Region_Kind, region: ^Atlas_Region } {
|
||||
{ .A, & atlas.region_a },
|
||||
{ .B, & atlas.region_b },
|
||||
{ .C, & atlas.region_c },
|
||||
{ .D, & atlas.region_d },
|
||||
}
|
||||
|
||||
for region in region_lookup do if bounds_size_scaled.x <= f32(region.region.width) && bounds_size_scaled.y <= f32(region.region.height) {
|
||||
return region.kind, region.region, glyph_buffer.over_sample
|
||||
for kind in 0 ..< 4 do if bounds_size_scaled.x <= f32( atlas.regions[kind].width) && bounds_size_scaled.y <= f32(atlas.regions[kind].height) {
|
||||
return cast(Atlas_Region_Kind) kind, glyph_buffer.over_sample
|
||||
}
|
||||
|
||||
if bounds_size_scaled.x <= f32(glyph_buffer.width) \
|
||||
@ -121,9 +118,9 @@ decide_codepoint_region :: #force_inline proc (ctx : ^Context, entry : ^Entry, g
|
||||
bounds_size_scaled.y <= f32(glyph_buffer.height / 2) ? \
|
||||
{2.0, 2.0} \
|
||||
: {1.0, 1.0}
|
||||
return .E, nil, over_sample
|
||||
return .E, over_sample
|
||||
}
|
||||
return .None, nil, {}
|
||||
return .None, {}
|
||||
}
|
||||
|
||||
// Grab an atlas LRU cache slot.
|
||||
@ -151,3 +148,40 @@ atlas_reserve_slot :: #force_inline proc ( region : ^Atlas_Region, lru_code : u6
|
||||
assert( lru_get( & region.state, lru_code ) != - 1 )
|
||||
return
|
||||
}
|
||||
|
||||
check_and_reserve_slot_in_atlas :: #force_inline proc( ctx : ^Context, font : Font_ID, entry : ^Entry, glyph_index : Glyph,
|
||||
lru_code : u64,
|
||||
atlas_index : ^i32,
|
||||
region_kind : Atlas_Region_Kind,
|
||||
region : ^Atlas_Region,
|
||||
over_sample : Vec2
|
||||
) -> (found, should_cache : b8 )
|
||||
{
|
||||
profile(#procedure)
|
||||
assert( glyph_index != -1 )
|
||||
|
||||
if ctx.temp_codepoint_seen_num > i32(cap(ctx.temp_codepoint_seen)) do return
|
||||
|
||||
if (atlas_index ^) == - 1
|
||||
{
|
||||
// Check to see if we reached capacity for the atlas
|
||||
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 )
|
||||
success : bool
|
||||
found, success = ctx.temp_codepoint_seen[next_evict_codepoint]
|
||||
assert(success != false)
|
||||
|
||||
if (found) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
should_cache = true
|
||||
(atlas_index ^) = atlas_reserve_slot(region, lru_code)
|
||||
}
|
||||
|
||||
found = true
|
||||
return
|
||||
}
|
@ -331,31 +331,23 @@ cache_glyph :: proc(ctx : ^Context, font : Font_ID, glyph_index : Glyph, entry :
|
||||
* can_batch_glyph : If it determines that the glyph was not detected and we haven't reached capacity in the atlas
|
||||
* draw_text_shape : Glyph
|
||||
*/
|
||||
cache_glyph_to_atlas :: proc( ctx : ^Context,
|
||||
cache_glyph_to_atlas :: proc ( ctx : ^Context,
|
||||
font : Font_ID,
|
||||
glyph_index : Glyph,
|
||||
bounds : GlyphBounds,
|
||||
bounds_size : Vec2,
|
||||
region_pos : Vec2,
|
||||
region_size : Vec2,
|
||||
lru_code : u64,
|
||||
atlas_index : i32,
|
||||
entry : ^Entry,
|
||||
region_kind : Atlas_Region_Kind,
|
||||
region : ^Atlas_Region,
|
||||
over_sample : Vec2 )
|
||||
over_sample : Vec2
|
||||
)
|
||||
{
|
||||
profile(#procedure)
|
||||
|
||||
// Get hb_font text metrics. These are unscaled!
|
||||
bounds_0, bounds_1 := parser_get_glyph_box( & entry.parser_info, glyph_index )
|
||||
vbounds_0 := vec2(bounds_0)
|
||||
vbounds_1 := vec2(bounds_1)
|
||||
bounds_size := vbounds_1 - vbounds_0
|
||||
|
||||
// E region is special case and not cached to atlas.
|
||||
if region_kind == .None || region_kind == .E do return
|
||||
|
||||
atlas_index := atlas_index
|
||||
// TODO(Ed): Try to make sure this is resolve always
|
||||
if atlas_index == -1 do atlas_index = atlas_reserve_slot( region, lru_code )
|
||||
|
||||
atlas := & ctx.atlas
|
||||
glyph_buffer := & ctx.glyph_buffer
|
||||
atlas_size := Vec2 { f32(atlas.width), f32(atlas.height) }
|
||||
@ -372,7 +364,7 @@ cache_glyph_to_atlas :: proc( ctx : ^Context,
|
||||
|
||||
// Draw oversized glyph to glyph render target (FBO)
|
||||
glyph_draw_scale := over_sample * entry.size_scale
|
||||
glyph_draw_translate := -1 * vbounds_0 * glyph_draw_scale + vec2( glyph_padding )
|
||||
glyph_draw_translate := -1 * bounds.p0 * glyph_draw_scale + vec2( glyph_padding )
|
||||
|
||||
// Allocate a glyph glyph render target region (FBO)
|
||||
gwidth_scaled_px := bounds_size.x * glyph_draw_scale.x + over_sample.x * glyph_padding + 1.0
|
||||
@ -380,14 +372,13 @@ cache_glyph_to_atlas :: proc( ctx : ^Context,
|
||||
flush_glyph_buffer_to_atlas( ctx )
|
||||
}
|
||||
|
||||
// Calculate the src and destination regions
|
||||
slot_position, slot_size := atlas_bbox( atlas, region_kind, atlas_index )
|
||||
region_pos := region_pos
|
||||
|
||||
dst_glyph_position := slot_position
|
||||
dst_glyph_position := region_pos
|
||||
dst_glyph_size := ceil(bounds_size * entry.size_scale + glyph_padding)
|
||||
dst_size := (slot_size)
|
||||
dst_size := (region_size)
|
||||
screenspace_x_form( & dst_glyph_position, & dst_glyph_size, atlas_size )
|
||||
screenspace_x_form( & slot_position, & dst_size, atlas_size )
|
||||
screenspace_x_form( & region_pos, & dst_size, atlas_size )
|
||||
|
||||
src_position := Vec2 { f32(glyph_buffer.batch_x), 0 }
|
||||
src_size := (bounds_size * glyph_draw_scale + over_sample * glyph_padding)
|
||||
@ -406,7 +397,7 @@ cache_glyph_to_atlas :: proc( ctx : ^Context,
|
||||
start_index = cast(u32) len(glyph_buffer.clear_draw_list.indices)
|
||||
|
||||
blit_quad( & glyph_buffer.clear_draw_list,
|
||||
slot_position, slot_position + dst_size,
|
||||
region_pos, region_pos + dst_size,
|
||||
{ 1.0, 1.0 }, { 1.0, 1.0 } )
|
||||
|
||||
end_index = cast(u32) len(glyph_buffer.clear_draw_list.indices)
|
||||
@ -420,7 +411,7 @@ cache_glyph_to_atlas :: proc( ctx : ^Context,
|
||||
start_index = cast(u32) len(glyph_buffer.draw_list.indices)
|
||||
|
||||
blit_quad( & glyph_buffer.draw_list,
|
||||
dst_glyph_position, slot_position + dst_glyph_size,
|
||||
dst_glyph_position, region_pos + dst_glyph_size,
|
||||
src_position, src_position + src_size )
|
||||
|
||||
end_index = cast(u32) len(glyph_buffer.draw_list.indices)
|
||||
@ -430,49 +421,7 @@ cache_glyph_to_atlas :: proc( ctx : ^Context,
|
||||
append( & glyph_buffer.draw_list.calls, blit_to_atlas )
|
||||
|
||||
// Render glyph to glyph render target (FBO)
|
||||
cache_glyph( ctx, font, glyph_index, entry, vbounds_0, vbounds_1, glyph_draw_scale, glyph_draw_translate )
|
||||
}
|
||||
|
||||
// If the glyuph is found in the atlas, nothing occurs, otherwise, the glyph call is setup to catch it to the atlas
|
||||
check_glyph_in_atlas :: #force_inline proc( ctx : ^Context, font : Font_ID, entry : ^Entry, glyph_index : Glyph,
|
||||
lru_code : u64,
|
||||
atlas_index : i32,
|
||||
region_kind : Atlas_Region_Kind,
|
||||
region : ^Atlas_Region,
|
||||
over_sample : Vec2
|
||||
) -> (seen, should_cache : b8)
|
||||
{
|
||||
profile(#procedure)
|
||||
assert( glyph_index != -1 )
|
||||
|
||||
// E region can't batch
|
||||
if region_kind == .E || region_kind == .None do return
|
||||
if ctx.temp_codepoint_seen_num > i32(cap(ctx.temp_codepoint_seen)) do return
|
||||
|
||||
if atlas_index == - 1
|
||||
{
|
||||
// Check to see if we reached capacity for the atlas
|
||||
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 )
|
||||
success : bool
|
||||
seen, success = ctx.temp_codepoint_seen[next_evict_codepoint]
|
||||
assert(success != false)
|
||||
|
||||
if (seen) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
should_cache = true
|
||||
cache_glyph_to_atlas( ctx, font, glyph_index, lru_code, atlas_index, entry, region_kind, region, over_sample )
|
||||
}
|
||||
|
||||
assert( lru_get( & region.state, lru_code ) != -1 )
|
||||
mark_batch_codepoint_seen( ctx, lru_code)
|
||||
seen = true
|
||||
return
|
||||
cache_glyph( ctx, font, glyph_index, entry, bounds.p0, bounds.p1, glyph_draw_scale, glyph_draw_translate )
|
||||
}
|
||||
|
||||
// ve_fontcache_clear_Draw_List
|
||||
@ -485,7 +434,7 @@ clear_draw_list :: #force_inline proc ( draw_list : ^Draw_List ) {
|
||||
directly_draw_massive_glyph :: proc( ctx : ^Context,
|
||||
entry : ^Entry,
|
||||
glyph : Glyph,
|
||||
bounds_0, bounds_1 : Vec2,
|
||||
bounds : GlyphBounds,
|
||||
bounds_size : Vec2,
|
||||
over_sample, position, scale : Vec2 )
|
||||
{
|
||||
@ -495,24 +444,22 @@ directly_draw_massive_glyph :: proc( ctx : ^Context,
|
||||
glyph_padding := f32(ctx.atlas.glyph_padding)
|
||||
glyph_buffer_size := Vec2 { f32(ctx.glyph_buffer.width), f32(ctx.glyph_buffer.height) }
|
||||
|
||||
// Draw un-antialiased glyph to update FBO.
|
||||
// Draw un-antialiased glyph to draw_buffer
|
||||
glyph_draw_scale := over_sample * entry.size_scale
|
||||
glyph_draw_translate := -1 * bounds_0 * glyph_draw_scale + vec2_from_scalar(glyph_padding)
|
||||
glyph_draw_translate := -1 * bounds.p0 * glyph_draw_scale + vec2_from_scalar(glyph_padding)
|
||||
screenspace_x_form( & glyph_draw_translate, & glyph_draw_scale, glyph_buffer_size )
|
||||
|
||||
cache_glyph( ctx, entry.id, glyph, entry, bounds_0, bounds_1, glyph_draw_scale, glyph_draw_translate )
|
||||
cache_glyph( ctx, entry.id, glyph, entry, bounds.p0, bounds.p1, glyph_draw_scale, glyph_draw_translate )
|
||||
|
||||
glyph_padding_dbl := glyph_padding * 2
|
||||
bounds_scaled := bounds_size * entry.size_scale
|
||||
bounds_scaled := bounds_size * entry.size_scale
|
||||
|
||||
// Figure out the source rect.
|
||||
glyph_position := Vec2 {}
|
||||
glyph_size := vec2(glyph_padding)
|
||||
glyph_dst_size := glyph_size + bounds_scaled
|
||||
glyph_size += bounds_scaled * over_sample
|
||||
glyph_size := glyph_padding + bounds_scaled * over_sample
|
||||
glyph_dst_size := glyph_padding + bounds_scaled
|
||||
|
||||
// Figure out the destination rect.
|
||||
bounds_0_scaled := (bounds_0 * entry.size_scale)
|
||||
bounds_0_scaled := (bounds.p0 * entry.size_scale)
|
||||
dst := position + scale * bounds_0_scaled - glyph_padding * scale
|
||||
dst_size := glyph_dst_size * scale
|
||||
textspace_x_form( & glyph_position, & glyph_size, glyph_buffer_size )
|
||||
@ -595,8 +542,9 @@ draw_filled_path :: proc( draw_list : ^Draw_List, outside_point : Vec2, path : [
|
||||
}
|
||||
}
|
||||
|
||||
draw_text_batch :: proc(ctx: ^Context, entry: ^Entry, shaped: ^Shaped_Text,
|
||||
batch_start_idx, batch_end_idx : i32,
|
||||
draw_text_batch :: #force_inline proc (ctx: ^Context, entry: ^Entry, shaped: ^Shaped_Text,
|
||||
// batch_start_idx, batch_end_idx : i32,
|
||||
glyph_pack : #soa[]GlyphPackEntry,
|
||||
position, scale : Vec2,
|
||||
snap_width, snap_height : f32 )
|
||||
{
|
||||
@ -607,61 +555,66 @@ draw_text_batch :: proc(ctx: ^Context, entry: ^Entry, shaped: ^Shaped_Text,
|
||||
atlas_size := Vec2{ f32(atlas.width), f32(atlas.height) }
|
||||
glyph_padding := atlas.glyph_padding
|
||||
|
||||
for index := batch_start_idx; index < batch_end_idx; index += 1
|
||||
// for glyph, index in glyph_pack
|
||||
// {
|
||||
// profile("oversized")
|
||||
// if glyph.region_kind != .E do continue
|
||||
// directly_draw_massive_glyph(ctx, entry, glyph.index,
|
||||
// glyph.bounds,
|
||||
// glyph.bounds_size,
|
||||
// glyph.over_sample, glyph.translate, scale )
|
||||
// }
|
||||
|
||||
for glyph, index in glyph_pack
|
||||
{
|
||||
profile("glyph")
|
||||
glyph_index := shaped.glyphs[index]
|
||||
// profile_begin("oversized")
|
||||
// if glyph.region_kind == .E
|
||||
// {
|
||||
// directly_draw_massive_glyph(ctx, entry, glyph.index,
|
||||
// glyph.bounds,
|
||||
// glyph.bounds_size,
|
||||
// glyph.over_sample, glyph.translate, scale )
|
||||
// continue
|
||||
// }
|
||||
// profile_end()
|
||||
|
||||
if glyph_index == 0 || parser_is_glyph_empty( & entry.parser_info, glyph_index) do continue
|
||||
profile("cached")
|
||||
|
||||
region_kind, region, over_sample := decide_codepoint_region( ctx, entry, glyph_index )
|
||||
lru_code := font_glyph_lru_code( entry.id, glyph_index )
|
||||
atlas_index := region_kind != .E ? lru_get( & region.state, lru_code ) : -1
|
||||
bounds_0, bounds_1 := parser_get_glyph_box( & entry.parser_info, glyph_index )
|
||||
vbounds_0 := vec2(bounds_0)
|
||||
vbounds_1 := vec2(bounds_1)
|
||||
bounds_size := vbounds_1 - vbounds_0
|
||||
glyph_scale := glyph.bounds_size * entry.size_scale + glyph_padding
|
||||
bounds_0_scaled := ceil(glyph.bounds.p0 * entry.size_scale - 0.5 )
|
||||
dst_pos := glyph.translate + bounds_0_scaled * scale
|
||||
dst_scale := glyph_scale * scale
|
||||
src_pos := glyph.region_pos
|
||||
|
||||
shaped_position := shaped.positions[index]
|
||||
glyph_translate := position + (shaped_position) * scale
|
||||
textspace_x_form( & src_pos, & glyph_scale, atlas_size )
|
||||
|
||||
if region_kind == .E
|
||||
{
|
||||
directly_draw_massive_glyph(ctx, entry, glyph_index,
|
||||
vbounds_0, vbounds_1,
|
||||
bounds_size,
|
||||
over_sample, glyph_translate, scale )
|
||||
}
|
||||
else if atlas_index != -1
|
||||
{
|
||||
profile("derive manual")
|
||||
call := Draw_Call_Default
|
||||
call.pass = .Target
|
||||
call.colour = ctx.colour
|
||||
call.start_index = u32(len(ctx.draw_list.indices))
|
||||
call := Draw_Call_Default
|
||||
call.pass = .Target
|
||||
call.colour = ctx.colour
|
||||
call.start_index = u32(len(ctx.draw_list.indices))
|
||||
|
||||
// Draw cached glyph
|
||||
slot_position, _ := atlas_bbox( atlas, region_kind, atlas_index )
|
||||
glyph_scale := bounds_size * entry.size_scale + glyph_padding
|
||||
bounds_0_scaled := ceil(vbounds_0 * entry.size_scale - 0.5 )
|
||||
dst := glyph_translate + bounds_0_scaled * scale
|
||||
dst_scale := glyph_scale * scale
|
||||
blit_quad(& ctx.draw_list,
|
||||
dst_pos, dst_pos + dst_scale,
|
||||
src_pos, src_pos + glyph_scale )
|
||||
|
||||
textspace_x_form( & slot_position, & glyph_scale, atlas_size )
|
||||
blit_quad(&ctx.draw_list,
|
||||
dst, dst + dst_scale,
|
||||
slot_position, slot_position + glyph_scale )
|
||||
call.end_index = u32(len(ctx.draw_list.indices))
|
||||
call.end_index = u32(len(ctx.draw_list.indices))
|
||||
|
||||
append(&ctx.draw_list.calls, call)
|
||||
}
|
||||
append(&ctx.draw_list.calls, call)
|
||||
}
|
||||
}
|
||||
|
||||
GlyphBounds :: struct {
|
||||
p0, p1 : Vec2
|
||||
}
|
||||
|
||||
GlyphPackEntry :: struct {
|
||||
lru_code : u64,
|
||||
region : ^Atlas_Region,
|
||||
bounds : GlyphBounds,
|
||||
bounds_size : Vec2,
|
||||
over_sample : Vec2,
|
||||
translate : Vec2,
|
||||
region_pos : Vec2,
|
||||
region_size : Vec2,
|
||||
lru_code : u64,
|
||||
atlas_index : i32,
|
||||
index : Glyph,
|
||||
shape_id : i32,
|
||||
@ -681,68 +634,91 @@ draw_text_shape :: #force_inline proc( ctx : ^Context,
|
||||
{
|
||||
profile(#procedure)
|
||||
|
||||
glyph_pack, pack_alloc_eror := make_soa(#soa[]GlyphPackEntry, len(shaped.glyphs), allocator = context.temp_allocator)
|
||||
glyph_pack, glyph_pack_alloc_error := make_soa( #soa[]GlyphPackEntry, len(shaped.glyphs), allocator = context.temp_allocator )
|
||||
oversized_pack, oversized_pack_alloc_error := make_soa( #soa[dynamic]GlyphPackEntry, length = 0, capacity = len(shaped.glyphs), allocator = context.temp_allocator )
|
||||
|
||||
profile_begin("SOA glyph pack processing")
|
||||
atlas := & ctx.atlas
|
||||
|
||||
profile_begin("SOA setup")
|
||||
|
||||
profile_begin("index & translate")
|
||||
for & glyph, index in glyph_pack
|
||||
{
|
||||
glyph.shape_id = cast(i32) index
|
||||
glyph.index = shaped.glyphs[ index ]
|
||||
}
|
||||
// for & glyph, index in glyph_pack
|
||||
// {
|
||||
// glyph.region_kind,
|
||||
// glyph.region,
|
||||
// glyph.over_sample = decide_codepoint_region( ctx, entry, glyph.index )
|
||||
// }
|
||||
// for & glyph, index in glyph_pack
|
||||
// {
|
||||
// glyph.lru_code = font_glyph_lru_code(entry.id, glyph.index)
|
||||
// }
|
||||
// for & glyph, index in glyph_pack
|
||||
// {
|
||||
// glyph.atlas_index = -1
|
||||
// if glyph.region_kind != .E do glyph.atlas_index = lru_get( & glyph.region.state, glyph.lru_code )
|
||||
// }
|
||||
// for & glyph, index in glyph_pack
|
||||
// {
|
||||
// glyph.in_atlas, glyph.should_cache = check_glyph_in_atlas( ctx, font, entry, glyph.index, glyph.lru_code, glyph.atlas_index, glyph.region_kind, glyph.region, glyph.over_sample )
|
||||
// }
|
||||
// for & glyph, index in glyph_pack
|
||||
// {
|
||||
// if ! glyph.should_cache do continue
|
||||
// cache_glyph_to_atlas(ctx, font, glyph.index, glyph.lru_code, glyph.atlas_index, entry, glyph.region_kind, glyph.region, glyph.over_sample)
|
||||
// }
|
||||
// for & glyph, index in glyph_pack
|
||||
// {
|
||||
// if ! glyph.in_atlas do continue
|
||||
|
||||
// assert( lru_get( & glyph.region.state, glyph.lru_code ) != -1 )
|
||||
// mark_batch_codepoint_seen( ctx, glyph.lru_code)
|
||||
// }
|
||||
profile_end()
|
||||
|
||||
profile_begin("translate")
|
||||
for & glyph, index in glyph_pack
|
||||
{
|
||||
glyph.translate = position + (shaped.positions[index]) * scale
|
||||
}
|
||||
profile_end()
|
||||
|
||||
profile_begin("bounds")
|
||||
for & glyph, index in glyph_pack
|
||||
{
|
||||
glyph.lru_code = font_glyph_lru_code(entry.id, glyph.index)
|
||||
glyph.bounds = parser_get_bounds( & entry.parser_info, glyph.index )
|
||||
glyph.bounds_size = glyph.bounds.p1 - glyph.bounds.p0
|
||||
}
|
||||
profile_end()
|
||||
|
||||
profile_begin("region")
|
||||
for & glyph, index in glyph_pack
|
||||
{
|
||||
glyph.region_kind,
|
||||
glyph.over_sample = decide_codepoint_region( ctx.atlas, ctx.glyph_buffer, entry.size_scale, glyph.index, glyph.bounds_size )
|
||||
}
|
||||
profile_end()
|
||||
|
||||
profile_begin("atlas region slot check & reservation")
|
||||
for & glyph, index in glyph_pack
|
||||
{
|
||||
region := atlas.regions[glyph.region_kind]
|
||||
|
||||
if glyph.region_kind == .E do continue
|
||||
|
||||
glyph.atlas_index = lru_get( & region.state, glyph.lru_code )
|
||||
glyph.in_atlas, glyph.should_cache = check_and_reserve_slot_in_atlas( ctx, font, entry, glyph.index, glyph.lru_code, & glyph.atlas_index, glyph.region_kind, region, glyph.over_sample )
|
||||
glyph.region_pos, glyph.region_size = atlas_region_bbox(region ^, glyph.atlas_index)
|
||||
}
|
||||
profile_end()
|
||||
|
||||
profile_begin("caching to atlas")
|
||||
for glyph, index in glyph_pack
|
||||
{
|
||||
if glyph.region_kind == .E do continue
|
||||
if ! glyph.should_cache do continue
|
||||
cache_glyph_to_atlas(ctx, font, glyph.index, glyph.bounds, glyph.bounds_size, glyph.region_pos, glyph.region_size, glyph.lru_code, glyph.atlas_index, entry, glyph.region_kind, atlas.regions[glyph.region_kind], glyph.over_sample)
|
||||
}
|
||||
profile_end()
|
||||
|
||||
profile_end()
|
||||
|
||||
|
||||
// First batch the other cached glyphs
|
||||
// flush_glyph_buffer_to_atlas(ctx)
|
||||
// draw_text_batch( ctx, entry, shaped, glyph_pack[batch_start_idx : index], position, scale, snap_width, snap_height )
|
||||
// reset_batch_codepoint_state( ctx )
|
||||
|
||||
|
||||
// Prepare uncached glyphs for caching
|
||||
batch_start_idx : i32 = 0
|
||||
for & glyph, index in glyph_pack
|
||||
for glyph, index in glyph_pack
|
||||
{
|
||||
// if is_glyph_empty( ctx, entry, glyph.index ) do continue
|
||||
profile("caching glyph")
|
||||
|
||||
glyph.region_kind, glyph.region, glyph.over_sample = decide_codepoint_region( ctx, entry, glyph.index )
|
||||
if glyph.region_kind == .E {
|
||||
append_soa( & oversized_pack, glyph )
|
||||
continue
|
||||
}
|
||||
|
||||
glyph.lru_code = font_glyph_lru_code(entry.id, glyph.index)
|
||||
|
||||
glyph.atlas_index = -1
|
||||
if glyph.region_kind != .E do glyph.atlas_index = lru_get( & glyph.region.state, glyph.lru_code )
|
||||
|
||||
glyph.in_atlas, glyph.should_cache = check_glyph_in_atlas( ctx, font, entry, glyph.index, glyph.lru_code, glyph.atlas_index, glyph.region_kind, glyph.region, glyph.over_sample )
|
||||
// if glyph.should_cache {
|
||||
// cache_glyph_to_atlas(ctx, font, glyph.index, glyph.lru_code, glyph.atlas_index, entry, glyph.region_kind, glyph.region, glyph.over_sample)
|
||||
// glyph.atlas_index = atlas_reserve_slot(glyph.region, glyph.lru_code)
|
||||
// }
|
||||
if glyph.in_atlas {
|
||||
// assert( lru_get( & glyph.region.state, glyph.lru_code ) != -1 )
|
||||
// mark_batch_codepoint_seen( ctx, glyph.lru_code)
|
||||
profile("glyph in atlas")
|
||||
// assert( lru_get( & atlas.regions[glyph.region_kind].state, glyph.lru_code ) != -1 )
|
||||
mark_batch_codepoint_seen( ctx, glyph.lru_code)
|
||||
continue
|
||||
}
|
||||
|
||||
@ -750,63 +726,32 @@ draw_text_shape :: #force_inline proc( ctx : ^Context,
|
||||
|
||||
// First batch the other cached glyphs
|
||||
// flush_glyph_buffer_to_atlas(ctx)
|
||||
draw_text_batch( ctx, entry, shaped, batch_start_idx, glyph.shape_id, position, scale, snap_width, snap_height )
|
||||
reset_batch_codepoint_state( ctx )
|
||||
// draw_text_batch( ctx, entry, shaped, glyph_pack[batch_start_idx : index], position, scale, snap_width, snap_height )
|
||||
// reset_batch_codepoint_state( ctx )
|
||||
|
||||
cache_glyph_to_atlas( ctx, font, glyph.index, glyph.lru_code, glyph.atlas_index, entry, glyph.region_kind, glyph.region, glyph.over_sample )
|
||||
cache_glyph_to_atlas( ctx, font, glyph.index, glyph.bounds, glyph.bounds_size, glyph.region_pos, glyph.region_size, glyph.lru_code, glyph.atlas_index, entry, glyph.region_kind, atlas.regions[glyph.region_kind], glyph.over_sample )
|
||||
mark_batch_codepoint_seen( ctx, glyph.lru_code)
|
||||
|
||||
batch_start_idx = 1
|
||||
batch_start_idx = glyph.shape_id
|
||||
}
|
||||
|
||||
draw_text_batch( ctx, entry, shaped, batch_start_idx, cast(i32) len(shaped.glyphs), position, scale, snap_width , snap_height )
|
||||
draw_text_batch( ctx, entry, shaped, glyph_pack[batch_start_idx : len(glyph_pack)], position, scale, snap_width , snap_height )
|
||||
reset_batch_codepoint_state( ctx )
|
||||
|
||||
cursor_pos = position + shaped.end_cursor_pos * scale
|
||||
return
|
||||
}
|
||||
profile_begin("draw oversized glyphs")
|
||||
flush_glyph_buffer_to_atlas(ctx)
|
||||
|
||||
// Helper for draw_text_latin_mono
|
||||
draw_text_mono_latin_batch :: #force_inline proc( ctx : ^Context,
|
||||
font : Font_ID,
|
||||
entry : ^Entry,
|
||||
shaped : ^Shaped_Text,
|
||||
position, scale : Vec2,
|
||||
snap_width, snap_height : f32
|
||||
) -> (cursor_pos : Vec2) #no_bounds_check
|
||||
{
|
||||
profile(#procedure)
|
||||
batch_start_idx : i32 = 0
|
||||
for index : i32 = 0; index < cast(i32) len(shaped.glyphs); index += 1
|
||||
for & glyph, index in oversized_pack
|
||||
{
|
||||
glyph_index := shaped.glyphs[ index ]
|
||||
if is_glyph_empty( ctx, entry, glyph_index ) do continue
|
||||
|
||||
region_kind, region, over_sample := decide_codepoint_region( ctx, entry, glyph_index )
|
||||
lru_code := font_glyph_lru_code(entry.id, glyph_index)
|
||||
atlas_index := cast(i32) -1
|
||||
|
||||
if region_kind != .E do atlas_index = lru_get( & region.state, lru_code )
|
||||
|
||||
in_atlas, should_cache := check_glyph_in_atlas( ctx, font, entry, glyph_index, lru_code, atlas_index, region_kind, region, over_sample )
|
||||
if in_atlas do continue
|
||||
if should_cache do cache_glyph_to_atlas(ctx, font, glyph_index, lru_code, atlas_index, entry, region_kind, region, over_sample )
|
||||
|
||||
// We can no longer directly append the shape as it has missing glyphs in the atlas
|
||||
|
||||
// First batch the other cached glyphs
|
||||
// flush_glyph_buffer_to_atlas(ctx)
|
||||
draw_text_batch( ctx, entry, shaped, batch_start_idx, index, position, scale, snap_width, snap_height )
|
||||
reset_batch_codepoint_state( ctx )
|
||||
|
||||
cache_glyph_to_atlas( ctx, font, glyph_index, lru_code, atlas_index, entry, region_kind, region, over_sample )
|
||||
mark_batch_codepoint_seen( ctx, lru_code)
|
||||
batch_start_idx = index
|
||||
directly_draw_massive_glyph(ctx, entry, glyph.index,
|
||||
glyph.bounds,
|
||||
glyph.bounds_size,
|
||||
glyph.over_sample, glyph.translate, scale )
|
||||
}
|
||||
|
||||
draw_text_batch( ctx, entry, shaped, batch_start_idx, cast(i32) len(shaped.glyphs), position, scale, snap_width , snap_height )
|
||||
reset_batch_codepoint_state( ctx )
|
||||
|
||||
profile_end()
|
||||
|
||||
cursor_pos = position + shaped.end_cursor_pos * scale
|
||||
return
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
package vefontcache
|
||||
|
||||
import "base:runtime"
|
||||
import "core:hash"
|
||||
fnv64a :: hash.fnv64a
|
||||
import "core:math"
|
||||
@ -45,6 +46,10 @@ append :: proc {
|
||||
append_elem_string,
|
||||
}
|
||||
|
||||
append_soa :: proc {
|
||||
append_soa_elem
|
||||
}
|
||||
|
||||
ceil :: proc {
|
||||
math.ceil_f16,
|
||||
math.ceil_f16le,
|
||||
@ -91,6 +96,7 @@ make :: proc {
|
||||
}
|
||||
|
||||
make_soa :: proc {
|
||||
make_soa_dynamic_array_len_cap,
|
||||
make_soa_slice,
|
||||
}
|
||||
|
||||
@ -113,6 +119,7 @@ vec2_64 :: proc {
|
||||
|
||||
import "../../grime"
|
||||
|
||||
|
||||
DISABLE_PROFILING :: false
|
||||
|
||||
@(deferred_none = profile_end, disabled = DISABLE_PROFILING)
|
||||
|
@ -57,16 +57,17 @@ font_glyph_lru_code :: #force_inline proc "contextless" ( font : Font_ID, glyph_
|
||||
is_glyph_empty :: #force_inline 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
|
||||
if parser_is_glyph_empty( entry.parser_info, glyph_index ) do return true
|
||||
return false
|
||||
}
|
||||
|
||||
mark_batch_codepoint_seen :: #force_inline proc ( ctx : ^Context, lru_code : u64 ) {
|
||||
mark_batch_codepoint_seen :: #force_inline proc "contextless" ( ctx : ^Context, lru_code : u64 ) {
|
||||
ctx.temp_codepoint_seen[lru_code] = true
|
||||
ctx.temp_codepoint_seen_num += 1
|
||||
}
|
||||
|
||||
reset_batch_codepoint_state :: #force_inline proc( ctx : ^Context ) {
|
||||
profile(#procedure)
|
||||
clear_map( & ctx.temp_codepoint_seen )
|
||||
ctx.temp_codepoint_seen_num = 0
|
||||
}
|
||||
|
@ -111,7 +111,7 @@ parser_unload_font :: proc( font : ^Parser_Font_Info )
|
||||
}
|
||||
}
|
||||
|
||||
parser_find_glyph_index :: #force_inline proc "contextless" ( font : ^Parser_Font_Info, codepoint : rune ) -> (glyph_index : Glyph)
|
||||
parser_find_glyph_index :: #force_inline proc "contextless" ( font : Parser_Font_Info, codepoint : rune ) -> (glyph_index : Glyph)
|
||||
{
|
||||
profile(#procedure)
|
||||
switch font.kind
|
||||
@ -126,7 +126,7 @@ parser_find_glyph_index :: #force_inline proc "contextless" ( font : ^Parser_Fon
|
||||
return
|
||||
|
||||
case .STB_TrueType:
|
||||
glyph_index = transmute(Glyph) stbtt.FindGlyphIndex( & font.stbtt_info, codepoint )
|
||||
glyph_index = transmute(Glyph) stbtt.FindGlyphIndex( font.stbtt_info, codepoint )
|
||||
return
|
||||
}
|
||||
return Glyph(-1)
|
||||
@ -220,12 +220,14 @@ parser_get_font_vertical_metrics :: #force_inline proc "contextless" ( font : ^P
|
||||
return
|
||||
}
|
||||
|
||||
parser_get_glyph_box :: #force_inline proc "contextless" ( font : ^Parser_Font_Info, glyph_index : Glyph ) -> (bounds_0, bounds_1 : Vec2i)
|
||||
parser_get_bounds :: #force_inline proc "contextless" ( font : ^Parser_Font_Info, glyph_index : Glyph ) -> (bounds : GlyphBounds)
|
||||
{
|
||||
profile(#procedure)
|
||||
|
||||
bounds_0, bounds_1 : Vec2i
|
||||
|
||||
switch font.kind
|
||||
{
|
||||
|
||||
case .Freetype:
|
||||
freetype.load_glyph( font.freetype_info, c.uint(glyph_index), { .No_Bitmap, .No_Hinting, .No_Scale } )
|
||||
|
||||
@ -242,6 +244,7 @@ parser_get_glyph_box :: #force_inline proc "contextless" ( font : ^Parser_Font_I
|
||||
bounds_0 = { x0, y0 }
|
||||
bounds_1 = { x1, y1 }
|
||||
}
|
||||
bounds = { vec2(bounds_0), vec2(bounds_1) }
|
||||
return
|
||||
}
|
||||
|
||||
@ -269,7 +272,7 @@ parser_get_glyph_shape :: #force_inline proc ( font : ^Parser_Font_Info, glyph_i
|
||||
return
|
||||
}
|
||||
|
||||
parser_is_glyph_empty :: #force_inline proc "contextless" ( font : ^Parser_Font_Info, glyph_index : Glyph ) -> b32
|
||||
parser_is_glyph_empty :: #force_inline proc "contextless" ( font : Parser_Font_Info, glyph_index : Glyph ) -> b32
|
||||
{
|
||||
switch font.kind
|
||||
{
|
||||
@ -287,7 +290,7 @@ parser_is_glyph_empty :: #force_inline proc "contextless" ( font : ^Parser_Font_
|
||||
return false
|
||||
|
||||
case .STB_TrueType:
|
||||
return stbtt.IsGlyphEmpty( & font.stbtt_info, cast(c.int) glyph_index )
|
||||
return stbtt.IsGlyphEmpty( font.stbtt_info, cast(c.int) glyph_index )
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
@ -76,7 +76,7 @@ shape_text_uncached_advanced :: #force_inline proc( ctx : ^Context, font : Font_
|
||||
line_gap := f32(line_gap_i32)
|
||||
line_height := (ascent - descent + line_gap) * entry.size_scale
|
||||
|
||||
shaper_shape_from_text( & ctx.shaper_ctx, & entry.parser_info, & entry.shaper_info, output, text_utf8, ascent_i32, descent_i32, line_gap_i32, entry.size, entry.size_scale )
|
||||
shaper_shape_from_text( & ctx.shaper_ctx, entry.parser_info, & entry.shaper_info, output, text_utf8, ascent_i32, descent_i32, line_gap_i32, entry.size, entry.size_scale )
|
||||
}
|
||||
|
||||
shape_text_uncached_latin :: proc( ctx : ^Context, font : Font_ID, text_utf8 : string, entry : ^Entry, output : ^Shaped_Text )
|
||||
@ -119,8 +119,8 @@ shape_text_uncached_latin :: proc( ctx : ^Context, font : Font_ID, text_utf8 : s
|
||||
position.x = ceil(position.x)
|
||||
}
|
||||
|
||||
glyph_index := parser_find_glyph_index( & entry.parser_info, codepoint )
|
||||
is_glyph_empty := parser_is_glyph_empty( & entry.parser_info,glyph_index )
|
||||
glyph_index := parser_find_glyph_index( entry.parser_info, codepoint )
|
||||
is_glyph_empty := parser_is_glyph_empty( entry.parser_info,glyph_index )
|
||||
if ! is_glyph_empty
|
||||
{
|
||||
append( & output.glyphs, glyph_index)
|
||||
|
@ -54,7 +54,7 @@ shaper_unload_font :: proc( ctx : ^Shaper_Info )
|
||||
if blob != nil do harfbuzz.blob_destroy( blob )
|
||||
}
|
||||
|
||||
shaper_shape_from_text :: #force_inline proc( ctx : ^Shaper_Context, parser_info : ^Parser_Font_Info, info : ^Shaper_Info, output :^Shaped_Text, text_utf8 : string,
|
||||
shaper_shape_from_text :: #force_inline proc( ctx : ^Shaper_Context, parser_info : Parser_Font_Info, info : ^Shaper_Info, output :^Shaped_Text, text_utf8 : string,
|
||||
ascent, descent, line_gap : i32, size, size_scale : f32 )
|
||||
{
|
||||
profile(#procedure)
|
||||
@ -72,7 +72,7 @@ shaper_shape_from_text :: #force_inline proc( ctx : ^Shaper_Context, parser_info
|
||||
line_height := ((ascent - descent + line_gap) * size_scale)
|
||||
|
||||
position : Vec2
|
||||
shape_run :: #force_inline proc( parser_info : ^Parser_Font_Info, buffer : harfbuzz.Buffer, script : harfbuzz.Script, font : harfbuzz.Font, output : ^Shaped_Text,
|
||||
shape_run :: #force_inline proc( parser_info : Parser_Font_Info, buffer : harfbuzz.Buffer, script : harfbuzz.Script, font : harfbuzz.Font, output : ^Shaped_Text,
|
||||
position : ^Vec2, max_line_width: ^f32, line_count: ^int,
|
||||
ascent, descent, line_gap, size, size_scale: f32,
|
||||
snap_shape_pos : b32, adv_snap_small_font_threshold : f32 )
|
||||
|
@ -225,6 +225,13 @@ startup :: proc( ctx : ^Context, parser_kind : Parser_Kind = .STB_TrueType,
|
||||
atlas.region_d.offset.x = atlas.width / 2
|
||||
atlas.region_d.offset.y = 0
|
||||
|
||||
atlas.regions = {
|
||||
& atlas.region_a,
|
||||
& atlas.region_b,
|
||||
& atlas.region_c,
|
||||
& atlas.region_d,
|
||||
}
|
||||
|
||||
lru_init( & shape_cache.state, i32(shape_cache_params.capacity) )
|
||||
|
||||
shape_cache.storage, error = make( [dynamic]Shaped_Text, shape_cache_params.capacity )
|
||||
@ -471,36 +478,6 @@ draw_text :: #force_inline proc( ctx : ^Context, font : Font_ID, text_utf8 : str
|
||||
return true
|
||||
}
|
||||
|
||||
draw_text_mono_latin :: #force_inline proc( ctx : ^Context, font : Font_ID, text_utf8 : string, position, scale : Vec2 ) -> b32
|
||||
{
|
||||
profile(#procedure)
|
||||
assert( ctx != nil )
|
||||
assert( font >= 0 && int(font) < len(ctx.entries) )
|
||||
|
||||
ctx.cursor_pos = {}
|
||||
|
||||
position := position
|
||||
if ctx.snap_width > 0 do position.x = ceil(position.x * ctx.snap_width ) / ctx.snap_width
|
||||
if ctx.snap_height > 0 do position.y = ceil(position.y * ctx.snap_height) / ctx.snap_height
|
||||
|
||||
entry := & ctx.entries[ font ]
|
||||
|
||||
ChunkType :: enum u32 { Visible, Formatting }
|
||||
chunk_kind : ChunkType
|
||||
chunk_start : int = 0
|
||||
chunk_end : int = 0
|
||||
|
||||
text_utf8_bytes := transmute([]u8) text_utf8
|
||||
text_chunk : string
|
||||
|
||||
text_chunk = transmute(string) text_utf8_bytes[ : ]
|
||||
if len(text_chunk) > 0 {
|
||||
shaped := shape_text_cached( ctx, font, text_chunk, entry, shape_text_uncached_latin )
|
||||
ctx.cursor_pos = draw_text_shape( ctx, font, entry, shaped, position, scale, ctx.snap_width, ctx.snap_height )
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// ve_fontcache_Draw_List
|
||||
get_draw_list :: #force_inline proc( ctx : ^Context, optimize_before_returning := true ) -> ^Draw_List {
|
||||
assert( ctx != nil )
|
||||
|
@ -31,3 +31,4 @@ profile_begin :: #force_inline proc "contextless" ( name : string, loc := #calle
|
||||
profile_end :: #force_inline proc "contextless" () {
|
||||
spall._buffer_end( & Module_Context.ctx, & Module_Context.buffer)
|
||||
}
|
||||
|
||||
|
@ -6,7 +6,7 @@ import ve "codebase:font/VEFontCache"
|
||||
import sokol_gfx "thirdparty:sokol/gfx"
|
||||
|
||||
Font_Provider_Use_Freetype :: false
|
||||
Font_Largest_Px_Size :: 72
|
||||
Font_Largest_Px_Size :: 152
|
||||
Font_Size_Interval :: 2
|
||||
|
||||
Font_Default :: FontID { 0, "" }
|
||||
|
Loading…
Reference in New Issue
Block a user