Misc: VFontCache perf and features, exposing config on prototype side...

This commit is contained in:
Edward R. Gonzalez 2025-01-09 13:23:35 -05:00
parent ce84652417
commit b4abde1094
19 changed files with 5023 additions and 227 deletions

View File

@ -82,7 +82,7 @@ atlas_region_bbox :: #force_inline proc( region : Atlas_Region, local_idx : i32
@(optimization_mode="favor_size")
atlas_decide_region :: #force_inline proc "contextless" (atlas : Atlas, glyph_buffer_size : Vec2, bounds_size_scaled : Vec2 ) -> (region_kind : Atlas_Region_Kind)
{
profile(#procedure)
// profile(#procedure)
glyph_padding_dbl := atlas.glyph_padding * 2
padded_bounds := bounds_size_scaled + glyph_padding_dbl
@ -99,43 +99,6 @@ atlas_decide_region :: #force_inline proc "contextless" (atlas : Atlas, glyph_bu
return .None
}
@(optimization_mode="favor_size")
atlas_decide_region_branchless :: #force_inline proc "contextless" (
atlas: Atlas,
glyph_buffer_size : Vec2,
bounds_size_scaled : Vec2,
) -> Atlas_Region_Kind
{
profile(#procedure)
glyph_padding_dbl := atlas.glyph_padding * 2
padded_bounds := bounds_size_scaled + glyph_padding_dbl
slot_size_region_a := vec2(atlas.region_a.slot_size)
slot_size_region_b := vec2(atlas.region_b.slot_size)
slot_size_region_c := vec2(atlas.region_c.slot_size)
slot_size_region_d := vec2(atlas.region_d.slot_size)
within_a := padded_bounds.x <= slot_size_region_a.x && padded_bounds.y <= slot_size_region_a.y
within_b := padded_bounds.x <= slot_size_region_b.x && padded_bounds.y <= slot_size_region_b.y
within_c := padded_bounds.x <= slot_size_region_c.x && padded_bounds.y <= slot_size_region_c.y
within_d := padded_bounds.x <= slot_size_region_d.x && padded_bounds.y <= slot_size_region_d.y
within_buffer := padded_bounds.x <= glyph_buffer_size.x && padded_bounds.y <= glyph_buffer_size.y
within_none := cast(i32) ! (within_a || within_b || within_c || within_d )
score_a := i32(within_a) * -5
score_b := i32(within_b) * -4
score_c := i32(within_c) * -3
score_d := i32(within_d) * -2
score_buffer := i32(within_buffer) * -1
which_ab := min(score_a, score_b)
which_cd := min(score_c, score_d)
which_region := min(which_ab, which_cd)
resolved := min(which_region, score_buffer) + 6
return Atlas_Region_Kind(resolved)
}
// Grab an atlas LRU cache slot.
@(optimization_mode="favor_size")
atlas_reserve_slot :: #force_inline proc ( region : ^Atlas_Region, lru_code : Atlas_Key ) -> (atlas_index : i32)

View File

@ -35,21 +35,17 @@ Glyph_Draw_Quad :: struct {
Glyph_Pack_Entry :: struct #packed {
position : Vec2,
// index : Glyph,
// lru_code : Atlas_Key,
atlas_index : i32,
in_atlas : b8,
should_cache : b8,
// region_kind : Atlas_Region_Kind,
region_pos : Vec2,
region_size : Vec2,
// bounds : Range2,
// bounds_scaled : Range2,
// bounds_size : Vec2,
// bounds_size_scaled : Vec2,
over_sample : Vec2,
// scale : Vec2,
// bounds_scaled : Range2,
// bounds_size : Vec2,
// bounds_size_scaled : Vec2,
// scale : Vec2,
over_sample : Vec2, // Only used for oversized glyphs
shape : Parser_Glyph_Shape,
draw_transform : Transform,
@ -283,9 +279,7 @@ generate_shapes_draw_list :: #force_inline proc ( ctx : ^Context, font : Font_ID
This procedure has no awareness of layers. That should be handled by a higher-order codepath.
For this level of codepaths what matters is maximizing memory locality for:
* Dealing with shaping (essentially minimizing having to ever deal with it in a hot path if possible)
* Metric resolution of glyphs within a shape
* Note: It may be better to have the shape to track/store the glyph bounds and lru codes
* The shaper has access to the atlas's dependencies and to parser's metrics (including the unscaled bounds).
* Dealing with atlas regioning (the expensive region resolution & parser calls are done on the shape pass)
Pipleine order:
* Resolve atlas lru codes and track shape indexes
@ -339,7 +333,7 @@ generate_shape_draw_list :: proc( draw_list : ^Draw_List, shape : Shaped_Text,
oversized := & glyph_buffer.oversized
to_cache := & glyph_buffer.to_cache
cached := & glyph_buffer.cached
resize_soa_non_zero(glyph_pack, len(shape.glyph_id))
resize_soa_non_zero(glyph_pack, len(shape.glyph))
append_sub_pack :: #force_inline proc ( pack : ^[dynamic]i32, entry : i32 )
{
@ -355,7 +349,7 @@ generate_shape_draw_list :: proc( draw_list : ^Draw_List, shape : Shaped_Text,
}
profile_end()
profile_begin("batching")
profile_begin("batching & segregating glyphs")
clear(oversized)
clear(to_cache)
clear(cached)
@ -372,17 +366,20 @@ generate_shape_draw_list :: proc( draw_list : ^Draw_List, shape : Shaped_Text,
assert(false, "FAILED TO ASSGIN REGION")
continue
}
if region_kind == .E
when ENABLE_OVERSIZED_GLYPHS
{
glyph.over_sample = \
bounds_size_scaled.x <= glyph_buffer_size.x / 2 &&
bounds_size_scaled.y <= glyph_buffer_size.y / 2 ? \
{2.0, 2.0} \
: {1.0, 1.0}
append_sub_pack(oversized, cast(i32) index)
continue
if region_kind == .E
{
glyph.over_sample = \
bounds_size_scaled.x <= glyph_buffer_size.x / 2 &&
bounds_size_scaled.y <= glyph_buffer_size.y / 2 ? \
{2.0, 2.0} \
: {1.0, 1.0}
append_sub_pack(oversized, cast(i32) index)
continue
}
}
glyph.over_sample = glyph_buffer.over_sample
region := atlas.regions[region_kind]
glyph.atlas_index = lru_get( & region.state, atlas_key )
@ -390,13 +387,14 @@ generate_shape_draw_list :: proc( draw_list : ^Draw_List, shape : Shaped_Text,
// Glyphs are prepared in batches based on the capacity of the batch cache.
Prepare_For_Batch:
{
pack := cached
// Determine if we hit the limit for this batch.
if glyph_buffer.batch_cache.num >= glyph_buffer.batch_cache.cap do break Prepare_For_Batch
if glyph.atlas_index == - 1
{
// Check to see if we reached capacity for the atlas
if region.next_idx > 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_glyph := lru_get_next_evicted( region.state )
@ -406,19 +404,15 @@ generate_shape_draw_list :: proc( draw_list : ^Draw_List, shape : Shaped_Text,
break Prepare_For_Batch
}
}
profile("append to_cache")
// profile_begin("glyph needs caching")
glyph.atlas_index = atlas_reserve_slot(region, atlas_key)
glyph.region_pos, glyph.region_size = atlas_region_bbox(region ^, glyph.atlas_index)
append_sub_pack(to_cache, cast(i32) index)
mark_glyph_seen(& glyph_buffer.batch_cache, atlas_key)
continue
pack = to_cache
profile_end()
}
profile("append cached")
// profile("append cached")
glyph.region_pos, glyph.region_size = atlas_region_bbox(region ^, glyph.atlas_index)
append_sub_pack(cached, cast(i32) index)
mark_glyph_seen(& glyph_buffer.batch_cache, atlas_key)
append_sub_pack(pack, cast(i32) index)
continue
}
@ -498,7 +492,7 @@ batch_generate_glyphs_draw_list :: proc ( draw_list : ^Draw_List,
profile(#procedure)
colour := colour
profile_begin("glyph buffer transform & draw quads compute")
profile_begin("glyph transform & draw quads compute")
for id, index in cached
{
// Quad to for drawing atlas slot to target
@ -519,7 +513,7 @@ batch_generate_glyphs_draw_list :: proc ( draw_list : ^Draw_List,
glyph := & glyph_pack[id]
bounds := shape.bounds[id]
bounds_scaled := mul(bounds, font_scale)
glyph_scale := size(bounds_scaled) + glyph_buffer.draw_padding
glyph_scale := ceil(size(bounds_scaled) + glyph_buffer.draw_padding)
f32_allocated_x := cast(f32) glyph_buffer.allocated_x
@ -596,7 +590,7 @@ batch_generate_glyphs_draw_list :: proc ( draw_list : ^Draw_List,
}
profile_end()
profile_begin("generate oversized glyphs draw_list")
profile_begin("gen oversized glyphs draw_list")
when ENABLE_OVERSIZED_GLYPHS do if len(oversized) > 0
{
// colour.r = max(colour.a, enable_debug_vis_type)
@ -604,7 +598,7 @@ batch_generate_glyphs_draw_list :: proc ( draw_list : ^Draw_List,
// colour.b = colour.b * f32(cast(i32) ! b32(cast(i32) enable_debug_vis_type))
for pack_id, index in oversized {
error : Allocator_Error
glyph_pack[pack_id].shape, error = parser_get_glyph_shape(entry.parser_info, shape.glyph_id[pack_id])
glyph_pack[pack_id].shape, error = parser_get_glyph_shape(entry.parser_info, shape.glyph[pack_id])
assert(error == .None)
}
for id, index in oversized
@ -647,7 +641,8 @@ batch_generate_glyphs_draw_list :: proc ( draw_list : ^Draw_List,
}
profile_end()
generate_cached_draw_list :: #force_inline proc (draw_list : ^Draw_List, glyph_pack : #soa[]Glyph_Pack_Entry, sub_pack : []i32, colour : RGBAN )
@(optimization_mode = "favor_size")
generate_blit_from_atlas_draw_list :: #force_inline proc (draw_list : ^Draw_List, glyph_pack : #soa[]Glyph_Pack_Entry, sub_pack : []i32, colour : RGBAN )
{
profile(#procedure)
call := Draw_Call_Default
@ -655,7 +650,7 @@ batch_generate_glyphs_draw_list :: proc ( draw_list : ^Draw_List,
call.colour = colour
for id, index in sub_pack
{
profile("glyph")
// profile("glyph")
call.start_index = u32(len(draw_list.indices))
quad := glyph_pack[id].draw_quad
@ -673,13 +668,13 @@ batch_generate_glyphs_draw_list :: proc ( draw_list : ^Draw_List,
{
for pack_id, index in to_cache {
error : Allocator_Error
glyph_pack[pack_id].shape, error = parser_get_glyph_shape(entry.parser_info, shape.glyph_id[pack_id])
glyph_pack[pack_id].shape, error = parser_get_glyph_shape(entry.parser_info, shape.glyph[pack_id])
assert(error == .None)
}
for id, index in to_cache
{
profile("glyph")
// profile("glyph")
glyph := & glyph_pack[id]
bounds := shape.bounds[id]
bounds_scaled := mul(bounds, font_scale)
@ -707,11 +702,13 @@ batch_generate_glyphs_draw_list :: proc ( draw_list : ^Draw_List,
dst_glyph_pos := glyph.region_pos
dst_glyph_size := bounds_size_scaled + atlas.glyph_padding
dst_glyph_size.x = ceil(dst_glyph_size.x)
dst_glyph_size.y = max(dst_glyph_size.y, ceil(dst_glyph_size.y) * glyph_buffer.snap_glyph_height) // Note(Ed): Seems to improve hinting
to_glyph_buffer_space( & dst_glyph_pos, & dst_glyph_size, atlas_size )
src_position := Vec2 { glyph.buffer_x, 0 }
src_size := (bounds_size_scaled + atlas.glyph_padding) * glyph_buffer.over_sample
src_size.x = ceil(src_size.x)
src_size.y = max(src_size.y, ceil(src_size.y) * glyph_buffer.snap_glyph_height) // Note(Ed): Seems to improve hinting
to_target_space( & src_position, & src_size, glyph_buffer_size )
@ -740,20 +737,20 @@ batch_generate_glyphs_draw_list :: proc ( draw_list : ^Draw_List,
flush_glyph_buffer_draw_list(draw_list, & glyph_buffer.draw_list, & glyph_buffer.clear_draw_list, & glyph_buffer.allocated_x)
for id, index in to_cache do parser_free_shape(entry.parser_info, glyph_pack[id].shape)
profile_begin("generate_cached_draw_list: cached")
profile_begin("gen_cached_draw_list: to_cache")
colour.r = max(colour.r, 1.0 * enable_debug_vis_type)
colour.g = max(colour.g, 1.0 * enable_debug_vis_type)
colour.b = max(colour.b, 1.0 * enable_debug_vis_type)
generate_cached_draw_list( draw_list, glyph_pack[:], to_cache, colour )
generate_blit_from_atlas_draw_list( draw_list, glyph_pack[:], to_cache, colour )
profile_end()
}
profile_end()
profile_begin("generate_cached_draw_list: to_cache")
profile_begin("gen_cached_draw_list: cached")
colour.r = max(colour.r, 0.80 * enable_debug_vis_type)
colour.g = max(colour.g, 0.25 * enable_debug_vis_type)
colour.b = max(colour.b, 0.25 * enable_debug_vis_type)
generate_cached_draw_list( draw_list, glyph_pack[:], cached, colour )
generate_blit_from_atlas_draw_list( draw_list, glyph_pack[:], cached, colour )
profile_end()
}

View File

@ -32,23 +32,6 @@ reload_map :: #force_inline proc( self : ^map [$KeyType] $EntryType, allocator :
to_bytes :: #force_inline proc "contextless" ( typed_data : ^$Type ) -> []byte { return slice_ptr( transmute(^byte) typed_data, size_of(Type) ) }
// Provides the nearest prime number value for the given capacity
// closest_prime :: proc( capacity : uint ) -> uint
// {
// prime_table : []uint = {
// 53, 97, 193, 389, 769, 1543, 3079, 6151, 12289, 24593,
// 49157, 98317, 196613, 393241, 786433, 1572869, 3145739,
// 6291469, 12582917, 25165843, 50331653, 100663319,
// 201326611, 402653189, 805306457, 1610612741, 3221225473, 6442450941
// };
// for slot in prime_table {
// if slot >= capacity {
// return slot
// }
// }
// return prime_table[len(prime_table) - 1]
// }
@(optimization_mode="favor_size")
djb8_hash :: #force_inline proc "contextless" ( hash : ^$Type, bytes : []byte ) { for value in bytes do (hash^) = (( (hash^) << 8) + (hash^) ) + Type(value) }

View File

@ -118,7 +118,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)
{
profile(#procedure)
// profile(#procedure)
// switch font.kind
// {
// case .Freetype:
@ -227,8 +227,7 @@ parser_get_font_vertical_metrics :: #force_inline proc "contextless" ( font : Pa
parser_get_bounds :: #force_inline proc "contextless" ( font : Parser_Font_Info, glyph_index : Glyph ) -> (bounds : Range2)
{
profile(#procedure)
// profile(#procedure)
bounds_0, bounds_1 : Vec2i
// switch font.kind
@ -302,7 +301,7 @@ parser_is_glyph_empty :: #force_inline proc "contextless" ( font : Parser_Font_I
parser_scale :: #force_inline proc "contextless" ( font : Parser_Font_Info, size : f32 ) -> f32
{
profile(#procedure)
// profile(#procedure)
// size_scale := size < 0.0 ? parser_scale_for_pixel_height( font, -size ) : parser_scale_for_mapping_em_to_pixels( font, size )
size_scale := size > 0.0 ? parser_scale_for_pixel_height( font, size ) : parser_scale_for_mapping_em_to_pixels( font, -size )
return size_scale

View File

@ -6,7 +6,7 @@ Note(Ed): The only reason I didn't directly use harfbuzz is because hamza exists
import "core:c"
import "thirdparty:harfbuzz"
Shape_Key :: u32
Shape_Key :: u64
/* A text whose codepoints have had their relevant glyphs and
associated data resolved for processing in a draw list generation stage.
@ -26,14 +26,16 @@ Shape_Key :: u32
your not going to be satisfied with keeping that in the iteration).
*/
Shaped_Text :: struct #packed {
glyph_id : [dynamic]Glyph,
glyph : [dynamic]Glyph,
position : [dynamic]Vec2,
atlas_lru_code : [dynamic]Atlas_Key,
region_kind : [dynamic]Atlas_Region_Kind,
bounds : [dynamic]Range2,
bounds : [dynamic]Range2,
// TODO(Ed): Profile if its worth not doing compute for these per frame.
// bounds_scaled : [dynamic]Range2,
// bounds_size : [dynamic]Vec2,
// bounds_size_Scaled : [dynamic]Vec2,
atlas_bbox : [dynamic]Transform,
end_cursor_pos : Vec2,
size : Vec2,
}
@ -165,7 +167,7 @@ shaper_shape_harfbuzz :: proc( ctx : ^Shaper_Context, text_utf8 : string, entry
{
hb_glyph := glyph_infos[ index ]
hb_gposition := glyph_positions[ index ]
glyph_id := cast(Glyph) hb_glyph.codepoint
glyph := cast(Glyph) hb_glyph.codepoint
if hb_glyph.cluster > 0
{
@ -196,9 +198,9 @@ shaper_shape_harfbuzz :: proc( ctx : ^Shaper_Context, text_utf8 : string, entry
(position^) += advance
(max_line_width^) = max(max_line_width^, position.x)
is_empty := parser_is_glyph_empty(entry.parser_info, glyph_id)
is_empty := parser_is_glyph_empty(entry.parser_info, glyph)
if ! is_empty {
append( & output.glyph_id, glyph_id )
append( & output.glyph, glyph )
append( & output.position, glyph_pos)
}
}
@ -277,47 +279,30 @@ shaper_shape_text_uncached_advanced :: #force_inline proc( ctx : ^Shaper_Context
profile(#procedure)
assert( ctx != nil )
clear( & output.glyph_id )
clear( & output.glyph )
clear( & output.position )
shaper_shape_harfbuzz( ctx, text_utf8, entry, font_px_size, font_scale, output )
// Resolve each glyphs: bounds, atlas lru, and the atlas region as we have everything we need now.
resize( & output.atlas_lru_code, len(output.glyph_id) )
resize( & output.region_kind, len(output.glyph_id) )
resize( & output.bounds, len(output.glyph_id) )
// resize( & output.bounds_scaled, len(output.glyph_id) )
profile_begin("bounds")
for id, index in output.glyph_id
{
bounds := & output.bounds[index]
// bounds_scaled := & output.bounds_scaled[index]
// bounds_size := & output.bounds_size[index]
// bounds_size_scaled := & output.bounds_size_scaled[index]
// scale := & output.scale[index]
(bounds ^) = parser_get_bounds( entry.parser_info, id )
// (bounds_scaled ^) = { bounds.p0 * font_scale, bounds.p1 * font_scale }
// bounds_size = bounds.p1 - bounds.p0
// bounds_size_scaled = bounds_size * font_scale
// scale = glyph.bounds_size_scaled + atlas.glyph_padding
}
profile_end()
resize( & output.atlas_lru_code, len(output.glyph) )
resize( & output.region_kind, len(output.glyph) )
resize( & output.bounds, len(output.glyph) )
profile_begin("atlas_lru_code")
for id, index in output.glyph_id
for id, index in output.glyph
{
output.atlas_lru_code[index] = atlas_glyph_lru_code(entry.id, font_px_size, id)
}
profile_end()
profile_begin("region")
for id, index in output.glyph_id
profile_begin("bounds & region")
for id, index in output.glyph
{
bounds := & output.bounds[index]
bounds_size_scaled := (bounds.p1 - bounds.p0) * font_scale
bounds := & output.bounds[index]
(bounds ^) = parser_get_bounds( entry.parser_info, id )
bounds_size_scaled := (bounds.p1 - bounds.p0) * font_scale
output.region_kind[index] = atlas_decide_region( atlas, glyph_buffer_size, bounds_size_scaled )
}
profile_end()
@ -337,7 +322,7 @@ shaper_shape_text_latin :: proc( ctx : ^Shaper_Context,
profile(#procedure)
assert( ctx != nil )
clear( & output.glyph_id )
clear( & output.glyph )
clear( & output.position )
line_height := (entry.ascent - entry.descent + entry.line_gap) * font_scale
@ -371,7 +356,7 @@ shaper_shape_text_latin :: proc( ctx : ^Shaper_Context,
is_glyph_empty := parser_is_glyph_empty( entry.parser_info, glyph_index )
if ! is_glyph_empty
{
append( & output.glyph_id, glyph_index)
append( & output.glyph, glyph_index)
append( & output.position, Vec2 {
ceil(position.x),
ceil(position.y)

View File

@ -11,7 +11,7 @@ import "base:runtime"
DISABLE_PROFILING :: false
ENABLE_OVERSIZED_GLYPHS :: true
Font_ID :: distinct i32
Font_ID :: distinct i16
Glyph :: distinct i32
Load_Font_Error :: enum(i32) {
@ -138,9 +138,9 @@ Init_Glyph_Draw_Params_Default :: Init_Glyph_Draw_Params {
snap_glyph_height = true,
over_sample = 4,
draw_padding = Init_Atlas_Params_Default.glyph_padding,
shape_gen_scratch_reserve = 10 * 1024,
buffer_glyph_limit = 4,
batch_glyph_limit = 512,
shape_gen_scratch_reserve = 4 * 1024,
buffer_glyph_limit = 16,
batch_glyph_limit = 2 * 1024,
}
Init_Shaper_Params :: struct {
@ -166,7 +166,7 @@ Init_Shape_Cache_Params :: struct {
Init_Shape_Cache_Params_Default :: Init_Shape_Cache_Params {
capacity = 10 * 1024,
reserve = 1024,
reserve = 512,
}
//#region("lifetime")
@ -178,8 +178,8 @@ startup :: proc( ctx : ^Context, parser_kind : Parser_Kind = .STB_TrueType,
glyph_draw_params := Init_Glyph_Draw_Params_Default,
shape_cache_params := Init_Shape_Cache_Params_Default,
shaper_params := Init_Shaper_Params_Default,
alpha_sharpen : f32 = 0.2,
px_scalar : f32 = 1.0,
alpha_sharpen : f32 = 0.15,
px_scalar : f32 = 2.0,
// Curve quality to use for a font when unspecified,
// Affects step size for bezier curve passes in generate_glyph_pass_draw_list
@ -226,7 +226,7 @@ startup :: proc( ctx : ^Context, parser_kind : Parser_Kind = .STB_TrueType,
slot_region_c := Vec2i { 64, 64 } * i32(atlas.size_multiplier)
slot_region_b := Vec2i { 32, 64 } * i32(atlas.size_multiplier)
slot_region_d := Vec2i { 128, 128 } * i32(atlas.size_multiplier)
init_atlas_region :: proc( region : ^Atlas_Region, atlas_size, slot_size : Vec2i, factor : Vec2i )
{
region.next_idx = 0;
@ -274,7 +274,7 @@ startup :: proc( ctx : ^Context, parser_kind : Parser_Kind = .STB_TrueType,
{
stroage_entry := & shape_cache.storage[idx]
stroage_entry.glyph_id, error = make( [dynamic]Glyph, len = 0, cap = shape_cache_params.reserve )
stroage_entry.glyph, error = make( [dynamic]Glyph, len = 0, cap = shape_cache_params.reserve )
assert( error == .None, "VEFontCache.init : Failed to allocate glyphs array for shape cache storage" )
stroage_entry.position, error = make( [dynamic]Vec2, len = 0, cap = shape_cache_params.reserve )
@ -380,7 +380,7 @@ hot_reload :: proc( ctx : ^Context, allocator : Allocator )
lru_reload( & shape_cache.state, allocator )
for idx : i32 = 0; idx < i32(len(shape_cache.storage)); idx += 1 {
storage_entry := & shape_cache.storage[idx]
reload_array( & storage_entry.glyph_id, allocator)
reload_array( & storage_entry.glyph, allocator)
reload_array( & storage_entry.position, allocator)
reload_array( & storage_entry.atlas_lru_code, allocator)
reload_array( & storage_entry.region_kind, allocator)
@ -432,7 +432,7 @@ shutdown :: proc( ctx : ^Context )
for idx : i32 = 0; idx < i32(len(shape_cache.storage)); idx += 1 {
storage_entry := & shape_cache.storage[idx]
delete( storage_entry.glyph_id )
delete( storage_entry.glyph )
delete( storage_entry.position )
delete( storage_entry.atlas_lru_code)
delete( storage_entry.region_kind)
@ -471,7 +471,7 @@ clear_shape_cache :: proc (ctx : ^Context)
stroage_entry := & ctx.shape_cache.storage[idx]
stroage_entry.end_cursor_pos = {}
stroage_entry.size = {}
clear(& stroage_entry.glyph_id)
clear(& stroage_entry.glyph)
clear(& stroage_entry.position)
}
ctx.shape_cache.next_cache_id = 0
@ -486,18 +486,18 @@ load_font :: proc( ctx : ^Context, label : string, data : []byte, glyph_curve_qu
entries := & ctx.entries
id : i32 = -1
id : Font_ID = -1
for index : i32 = 0; index < i32(len(entries)); index += 1 {
if entries[index].used do continue
id = index
id = Font_ID(index)
break
}
if id == -1 {
append_elem( entries, Entry {})
id = cast(i32) len(entries) - 1
id = cast(Font_ID) len(entries) - 1
}
assert( id >= 0 && id < i32(len(entries)) )
assert( id >= 0 && id < Font_ID(len(entries)) )
entry := & entries[ id ]
{
@ -655,11 +655,16 @@ set_colour :: #force_inline proc( ctx : ^Context, colour : RGBA
set_draw_type_visualization :: #force_inline proc( ctx : ^Context, should_enable : b32 ) { assert(ctx != nil); ctx.enable_draw_type_visualization = cast(f32) i32(should_enable); }
set_px_scalar :: #force_inline proc( ctx : ^Context, scalar : f32 ) { assert(ctx != nil); ctx.px_scalar = scalar }
set_snap_glyph_pos :: #force_inline proc( ctx : ^Context, should_snap : b32 ) {
set_snap_glyph_shape_position :: #force_inline proc( ctx : ^Context, should_snap : b32 ) {
assert(ctx != nil)
ctx.shaper_ctx.snap_glyph_position = should_snap
}
set_snap_glyph_render_height :: #force_inline proc( ctx : ^Context, should_snap : b32 ) {
assert(ctx != nil)
ctx.glyph_buffer.snap_glyph_height = cast(f32) i32(should_snap)
}
// Non-scoping context. The most fundamental interface-level draw shape procedure.
// (everything else is either batching/pipelining or quality of life warppers)
// Note: Prefer over draw_text_normalized_space if possible to resolve shape once persistently or at least once per frame or non-dirty state.
@ -728,7 +733,6 @@ draw_text_normalized_space :: #force_inline proc( ctx : ^Context,
entry := ctx.entries[ font ]
adjusted_position := get_snapped_position( ctx^, position )
colour := ctx.colour
colour.a = 1.0 + ctx.alpha_sharpen
@ -757,7 +761,6 @@ draw_text_normalized_space :: #force_inline proc( ctx : ^Context,
)
}
draw_shape :: #force_inline proc( ctx : ^Context, position, scale : Vec2, shape : Shaped_Text ) {
// peek_position
}

View File

@ -15,7 +15,7 @@ set_profiler_module_context :: #force_inline proc "contextless" ( ctx : ^SpallPr
Module_Context = ctx
}
DISABLE_PROFILING :: true
DISABLE_PROFILING :: false
@(deferred_none = profile_end, disabled = DISABLE_PROFILING)
profile :: #force_inline proc "contextless" ( name : string, loc := #caller_location ) {

View File

@ -70,7 +70,7 @@ str_cache_init :: proc( table_allocator, slabs_allocator : Allocator ) -> (cache
cache.slab, alloc_error = slab_init( & policy, allocator = slabs_allocator, dbg_name = dbg_name )
verify(alloc_error == .None, "Failed to initialize the string cache" )
cache.table, alloc_error = make( HMapChained(StrCached), 1 * Kilo, table_allocator, dbg_name = dbg_name )
cache.table, alloc_error = make( HMapChained(StrCached), 4 * Kilo, table_allocator, dbg_name = dbg_name )
return
}

View File

@ -275,7 +275,7 @@ sync_sectr_api :: proc( sectr_api : ^sectr.ModuleAPI, memory : ^ClientMemory, lo
fmt_backing : [16 * Kilobyte] u8
persistent_backing : [2 * Megabyte] byte
persistent_backing : [32 * Megabyte] byte
transient_backing : [32 * Megabyte] byte
main :: proc()
@ -292,7 +292,7 @@ main :: proc()
// Setup profiling
profiler : SpallProfiler
{
buffer_backing := make([]u8, spall.BUFFER_DEFAULT_SIZE)
buffer_backing := make([]u8, spall.BUFFER_DEFAULT_SIZE * 20)
profiler.ctx = spall.context_create("sectr.spall")
profiler.buffer = spall.buffer_create(buffer_backing)
}

View File

@ -10,8 +10,11 @@ UI_SettingsMenu :: struct
zoom_smooth_sensitivity_input : UI_TextInputBox,
zoom_digital_sensitivity_input : UI_TextInputBox,
zoom_scroll_delta_scale_input : UI_TextInputBox,
font_size_canvas_scalar_input : UI_TextInputBox,
font_size_screen_scalar_input : UI_TextInputBox,
text_snap_glyph_shape_posiiton : UI_TextInputBox,
text_snap_glyph_render_height : UI_TextInputBox,
text_size_canvas_scalar_input : UI_TextInputBox,
text_size_screen_scalar_input : UI_TextInputBox,
text_alpha_sharpen : UI_TextInputBox,
cfg_drop_down : UI_DropDown,
zoom_mode_drop_down : UI_DropDown,
@ -496,9 +499,77 @@ ui_settings_menu_builder :: proc( captures : rawptr = nil ) -> ( should_raise :
}
}
Text_Snap_Glyph_Shape_Position:
{
ui_settings_entry_inputbox( & text_snap_glyph_shape_posiiton, false, "settings_menu.text_snap_glyph_shape_posiiton", str_intern("Text: Snap Glyph Shape Position"),
UI_TextInput_Policy {
digits_only = true,
disallow_leading_zeros = false,
disallow_decimal = false,
digit_min = 0,
digit_max = 1,
max_length = 1,
}
)
using text_snap_glyph_shape_posiiton
if was_active
{
value, success := parse_f32(to_string(array_to_slice(input_str)))
if success {
value = clamp(value, 0, 1)
value_b32 := cast(b32) i32(value)
if config.text_snap_glyph_shape_position != value_b32 {
font_provider_flush_caches()
font_provider_set_snap_glyph_shape_position( value_b32 )
config.text_snap_glyph_shape_position = value_b32
}
}
}
else
{
clear( input_str )
append( & input_str, to_runes( str_fmt("%v", i32(config.text_snap_glyph_shape_position) ) ))
}
}
Text_Snap_Glyph_Render_Height:
{
ui_settings_entry_inputbox( & text_snap_glyph_render_height, false, "settings_menu.text_snap_glyph_render_height", str_intern("Text: Snap Glyph Render Height"),
UI_TextInput_Policy {
digits_only = true,
disallow_leading_zeros = false,
disallow_decimal = false,
digit_min = 0,
digit_max = 1,
max_length = 1,
}
)
using text_snap_glyph_render_height
if was_active
{
value, success := parse_f32(to_string(array_to_slice(input_str)))
if success {
value = clamp(value, 0, 1)
value_b32 := cast(b32) i32(value)
if config.text_snap_glyph_render_height != value_b32 {
font_provider_flush_caches()
font_provider_set_snap_glyph_render_height( value_b32 )
config.text_snap_glyph_render_height = value_b32
}
}
}
else
{
clear( input_str )
append( & input_str, to_runes( str_fmt("%v", i32(config.text_snap_glyph_render_height) ) ))
}
}
Text_Size_Screen_Scalar:
{
ui_settings_entry_inputbox( & font_size_screen_scalar_input, false, "settings_menu.font_size_screen_scalar", str_intern("Font: Size Screen Scalar"),
ui_settings_entry_inputbox( & text_size_screen_scalar_input, false, "settings_menu.text_size_screen_scalar", str_intern("Text: Size Screen Scalar"),
UI_TextInput_Policy {
digits_only = true,
disallow_leading_zeros = false,
@ -508,7 +579,7 @@ ui_settings_menu_builder :: proc( captures : rawptr = nil ) -> ( should_raise :
max_length = 5,
}
)
using font_size_screen_scalar_input
using text_size_screen_scalar_input
if was_active
{
@ -525,38 +596,9 @@ ui_settings_menu_builder :: proc( captures : rawptr = nil ) -> ( should_raise :
}
}
Text_Snap_Glyph_Positions:
{
ui_settings_entry_inputbox( & font_size_canvas_scalar_input, false, "settings_menu.font_size_canvas_scalar", str_intern("Font: Size Canvas Scalar"),
UI_TextInput_Policy {
digits_only = true,
disallow_leading_zeros = false,
disallow_decimal = false,
digit_min = 0,
digit_max = 1,
max_length = 1,
}
)
using font_size_canvas_scalar_input
if was_active
{
value, success := parse_f32(to_string(array_to_slice(input_str)))
if success {
value = clamp(value, 0, 1)
config.text_snap_glyph_positions = cast(b32) i32(value)
}
}
else
{
clear( input_str )
append( & input_str, to_runes(str_fmt("%v", config.text_snap_glyph_positions)))
}
}
Text_Size_Canvas_Scalar:
{
ui_settings_entry_inputbox( & font_size_canvas_scalar_input, false, "settings_menu.font_size_canvas_scalar", str_intern("Font: Size Canvas Scalar"),
ui_settings_entry_inputbox( & text_size_canvas_scalar_input, false, "settings_menu.text_size_canvas_scalar", str_intern("Text: Size Canvas Scalar"),
UI_TextInput_Policy {
digits_only = true,
disallow_leading_zeros = false,
@ -566,7 +608,7 @@ ui_settings_menu_builder :: proc( captures : rawptr = nil ) -> ( should_raise :
max_length = 5,
}
)
using font_size_canvas_scalar_input
using text_size_canvas_scalar_input
if was_active
{
@ -585,24 +627,25 @@ ui_settings_menu_builder :: proc( captures : rawptr = nil ) -> ( should_raise :
Text_Alpha_Sharpen:
{
ui_settings_entry_inputbox( & font_size_canvas_scalar_input, false, "settings_menu.text_alpha_sharpen", str_intern("Text: Alpha Sharpen"),
ui_settings_entry_inputbox( & text_alpha_sharpen, false, "settings_menu.text_alpha_sharpen", str_intern("Text: Alpha Sharpen"),
UI_TextInput_Policy {
digits_only = true,
disallow_leading_zeros = false,
disallow_decimal = false,
digit_min = 0.001,
digit_max = 10,
digit_max = 999,
max_length = 4,
}
)
using font_size_canvas_scalar_input
using text_alpha_sharpen
if was_active
{
value, success := parse_f32(to_string(array_to_slice(input_str)))
if success {
value = clamp(value, 0.001, 10.0)
value = clamp(value, 0, 10.0)
config.text_alpha_sharpen = value
font_provider_set_alpha_sharpen(value)
}
}
else

View File

@ -161,10 +161,11 @@ AppConfig :: struct {
color_theme : AppColorTheme,
text_snap_glyph_positions : b32,
text_size_screen_scalar : f32,
text_size_canvas_scalar : f32,
text_alpha_sharpen : f32,
text_snap_glyph_shape_position : b32,
text_snap_glyph_render_height : b32,
text_size_screen_scalar : f32,
text_size_canvas_scalar : f32,
text_alpha_sharpen : f32,
}
AppWindow :: struct {

View File

@ -136,7 +136,7 @@ startup :: proc( prof : ^SpallProfiler, persistent_mem, frame_mem, transient_mem
resolution_height = 600
refresh_rate = 0
cam_min_zoom = 0.025
cam_min_zoom = 0.034
cam_max_zoom = 5.0
cam_zoom_mode = .Digital
cam_zoom_smooth_snappiness = 4.0
@ -152,10 +152,11 @@ startup :: proc( prof : ^SpallProfiler, persistent_mem, frame_mem, transient_mem
color_theme = App_Thm_Dusk
text_snap_glyph_positions = true
text_size_screen_scalar = 2.0
text_size_canvas_scalar = 2.0
text_alpha_sharpen = 0.25
text_snap_glyph_shape_position = false
text_snap_glyph_render_height = false
text_size_screen_scalar = 1.89
text_size_canvas_scalar = 1.89
text_alpha_sharpen = 0.1
}
Desired_OS_Scheduler_MS :: 1
@ -356,8 +357,9 @@ startup :: proc( prof : ^SpallProfiler, persistent_mem, frame_mem, transient_mem
// debug.path_lorem = str_fmt("C:/projects/SectrPrototype/examples/Lorem Ipsum (197).txt", allocator = persistent_slab_allocator())
// debug.path_lorem = str_fmt("C:/projects/SectrPrototype/examples/Lorem Ipsum (1022).txt", allocator = persistent_slab_allocator())
debug.path_lorem = str_fmt("C:/projects/SectrPrototype/examples/sokol_gp.h", allocator = persistent_slab_allocator())
// debug.path_lorem = str_fmt("C:/projects/SectrPrototype/examples/ve_fontcache.h", allocator = persistent_slab_allocator())
debug.path_lorem = str_fmt("C:/projects/SectrPrototype/examples/sokol_gp.h", allocator = persistent_slab_allocator())
// debug.path_lorem = str_fmt("C:/projects/SectrPrototype/examples/sokol_gl.h", allocator = persistent_slab_allocator())
alloc_error : AllocatorError; success : bool
debug.lorem_content, success = os.read_entire_file( debug.path_lorem, persistent_slab_allocator() )
@ -530,9 +532,6 @@ tick_work_frame :: #force_inline proc( host_delta_time_ms : f64 ) -> b32
debug.draw_ui_padding_bounds = false
debug.draw_ui_content_bounds = false
font_provider_set_alpha_sharpen(0.25)
font_provider_set_snap_glyph_pos(true)
// config.engine_refresh_hz = 165
// config.color_theme = App_Thm_Light

View File

@ -123,7 +123,7 @@ render_mode_screenspace :: proc( screen_extent : Extents2, screen_ui : ^UI_State
screen_size := screen_extent * 2
screen_ratio := screen_size.x * ( 1.0 / screen_size.y )
font_provider_set_px_scalar( app_config().text_size_canvas_scalar )
font_provider_set_px_scalar( app_config().text_size_screen_scalar )
ve.configure_snap( ve_ctx, u32(screen_size.x), u32(screen_size.y) )
render_screen_ui( screen_extent, screen_ui, ve_ctx, ve_render )

View File

@ -302,6 +302,7 @@ update :: proc( delta_time : f64 ) -> b32
// TODO(Ed): We need input buffer so that we can consume input actions based on the UI with priority
font_provider_set_px_scalar( app_config().text_size_screen_scalar )
ui_screen_tick( & get_state().screen_ui )
//region WorkspaceImgui Tick

View File

@ -61,6 +61,13 @@ font_provider_reload :: proc( ctx : ^FontProviderContext )
ve.clear_shape_cache(& ctx.ve_ctx)
}
font_provider_flush_caches :: proc()
{
ve_ctx := & get_state().font_provider_ctx.ve_ctx
ve.clear_atlas_region_caches(ve_ctx)
ve.clear_shape_cache(ve_ctx)
}
font_provider_shutdown :: proc( ctx : ^FontProviderContext )
{
ve.shutdown( & ctx.ve_ctx )
@ -120,8 +127,12 @@ font_provider_set_px_scalar :: #force_inline proc( scalar : f32 ) {
ve.set_px_scalar( & get_state().font_provider_ctx.ve_ctx, scalar )
}
font_provider_set_snap_glyph_pos :: #force_inline proc( should_snap : b32 ) {
ve.set_snap_glyph_pos( & get_state().font_provider_ctx.ve_ctx, should_snap )
font_provider_set_snap_glyph_shape_position :: #force_inline proc( should_snap : b32 ) {
ve.set_snap_glyph_shape_position( & get_state().font_provider_ctx.ve_ctx, should_snap )
}
font_provider_set_snap_glyph_render_height :: #force_inline proc( should_snap : b32 ) {
ve.set_snap_glyph_render_height( & get_state().font_provider_ctx.ve_ctx, should_snap )
}
Font_Use_Default_Size :: f32(0.0)

View File

@ -89,9 +89,9 @@ PWS_ParseError :: struct {
}
PWS_ParseError_Max :: 32
PWS_TokenArray_ReserveSize :: 128
PWS_NodeArray_ReserveSize :: 32 * Kilobyte
PWS_LineArray_ReserveSize :: 32 * Kilobyte
PWS_TokenArray_ReserveSize :: 4 * Kilobyte
PWS_NodeArray_ReserveSize :: 64 * Kilobyte
PWS_LineArray_ReserveSize :: 64 * Kilobyte
// TODO(Ed) : The ast arrays should be handled by a slab allocator dedicated to PWS_ASTs
// This can grow in undeterministic ways, persistent will get very polluted otherwise.

View File

@ -114,8 +114,8 @@ UI_Layout_Stack_Size :: 512
UI_Style_Stack_Size :: 512
UI_Parent_Stack_Size :: 512
// UI_Built_Boxes_Array_Size :: 8
UI_Built_Boxes_Array_Size :: 56 * Kilobyte
UI_BoxCache_TableSize :: 8 * Kilobyte
UI_Built_Boxes_Array_Size :: 128 * Kilobyte
UI_BoxCache_TableSize :: 32 * Kilobyte
// TODO(Ed): Rename to UI_Context
UI_State :: struct {
@ -381,7 +381,7 @@ ui_hash_part_from_key_string :: proc ( content : string ) -> string {
ui_key_from_string :: #force_inline proc "contextless" ( value : string ) -> UI_Key
{
// profile(#procedure)
USE_RAD_DEBUGGERS_METHOD :: true
USE_RAD_DEBUGGERS_METHOD :: false
key : UI_Key

4811
examples/sokol_gl.h Normal file

File diff suppressed because it is too large Load Diff

View File

@ -205,11 +205,11 @@ push-location $path_root
# $build_args += $flag_micro_architecture_native
$build_args += $flag_use_separate_modules
$build_args += $flag_thread_count + $CoreCount_Physical
# $build_args += $flag_optimize_none
$build_args += $flag_optimize_none
# $build_args += $flag_optimize_minimal
# $build_args += $flag_optimize_speed
$build_args += $falg_optimize_aggressive
# $build_args += $flag_debug
# $build_args += $falg_optimize_aggressive
$build_args += $flag_debug
$build_args += $flag_pdb_name + $pdb
$build_args += $flag_subsystem + 'windows'
# $build_args += $flag_show_system_calls