From 38be79d7a9359b5a956feeef73fc6ce54e65f0d9 Mon Sep 17 00:00:00 2001 From: Ed_ Date: Thu, 6 Jun 2024 00:43:07 -0400 Subject: [PATCH] VEFontCache: runtime bugfixes --- code/font/VEFontCache/LRU.odin | 2 +- code/font/VEFontCache/VEFontCache.odin | 77 +++------------ code/font/VEFontCache/draw.odin | 54 +++++++++++ code/font/VEFontCache/parser.odin | 4 +- code/font/VEFontCache/shaper.odin | 2 +- code/font/fontstash/atlas.odin | 2 +- code/sectr/engine/render.odin | 2 +- ...ontstash.odin => .provider_fontstash.odin} | 0 .../{provider.odin => .raw_provider.odin} | 5 - code/sectr/font/cache.odin | 1 - code/sectr/font/provider_VEFontCache.odin | 97 ++++++++++++++++++- 11 files changed, 170 insertions(+), 76 deletions(-) rename code/sectr/font/{provider_fontstash.odin => .provider_fontstash.odin} (100%) rename code/sectr/font/{provider.odin => .raw_provider.odin} (98%) delete mode 100644 code/sectr/font/cache.odin diff --git a/code/font/VEFontCache/LRU.odin b/code/font/VEFontCache/LRU.odin index 239da88..cd39711 100644 --- a/code/font/VEFontCache/LRU.odin +++ b/code/font/VEFontCache/LRU.odin @@ -118,7 +118,7 @@ LRU_init :: proc( cache : ^LRU_Cache, capacity : u32 ) { error : AllocatorError 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") + assert( error == .None, "VEFontCache.LRU_init : Failed to allocate cache's table") pool_list_init( & cache.key_queue, capacity ) } diff --git a/code/font/VEFontCache/VEFontCache.odin b/code/font/VEFontCache/VEFontCache.odin index d82569a..9b763cd 100644 --- a/code/font/VEFontCache/VEFontCache.odin +++ b/code/font/VEFontCache/VEFontCache.odin @@ -298,14 +298,18 @@ init :: proc( ctx : ^Context, atlas.region_d.offset.x = atlas.width / 2 LRU_init( & shape_cache.state, shape_cache_params.capacity ) + + shape_cache.storage, error = make( Array(ShapedText), u64(shape_cache_params.reserve_length) ) + assert(error == .None, "VEFontCache.init : Failed to allocate shape_cache.storage") + for idx : u32 = 0; idx < shape_cache_params.capacity; idx += 1 { stroage_entry := & shape_cache.storage.data[idx] using stroage_entry glyphs, error = make( Array(Glyph), cast(u64) shape_cache_params.reserve_length ) - assert( error != .None, "VEFontCache.init : Failed to allocate glyphs array for shape cache storage" ) + assert( error == .None, "VEFontCache.init : Failed to allocate glyphs array for shape cache storage" ) positions, error = make( Array(Vec2), cast(u64) shape_cache_params.reserve_length ) - assert( error != .None, "VEFontCache.init : Failed to allocate positions array for shape cache storage" ) + assert( error == .None, "VEFontCache.init : Failed to allocate positions array for shape cache storage" ) } // Note(From original author): We can actually go over VE_FONTCACHE_GLYPHDRAW_BUFFER_BATCH batches due to smart packing! @@ -318,22 +322,22 @@ init :: proc( ctx : ^Context, draw_padding = glyph_draw_params.draw_padding draw_list.calls, error = make( Array(DrawCall), cast(u64) glyph_draw_params.buffer_batch * 2 ) - assert( error != .None, "VEFontCache.init : Failed to allocate calls for draw_list" ) + assert( error == .None, "VEFontCache.init : Failed to allocate calls for draw_list" ) draw_list.indices, error = make( Array(u32), cast(u64) glyph_draw_params.buffer_batch * 2 * 6 ) - assert( error != .None, "VEFontCache.init : Failed to allocate indices array for draw_list" ) + assert( error == .None, "VEFontCache.init : Failed to allocate indices array for draw_list" ) draw_list.vertices, error = make( Array(Vertex), cast(u64) glyph_draw_params.buffer_batch * 2 * 4 ) - assert( error != .None, "VEFontCache.init : Failed to allocate vertices array for draw_list" ) + assert( error == .None, "VEFontCache.init : Failed to allocate vertices array for draw_list" ) clear_draw_list.calls, error = make( Array(DrawCall), cast(u64) glyph_draw_params.buffer_batch * 2 ) - assert( error != .None, "VEFontCache.init : Failed to allocate calls for calls for clear_draw_list" ) + assert( error == .None, "VEFontCache.init : Failed to allocate calls for calls for clear_draw_list" ) clear_draw_list.indices, error = make( Array(u32), cast(u64) glyph_draw_params.buffer_batch * 2 * 4 ) - assert( error != .None, "VEFontCache.init : Failed to allocate calls for indices array for clear_draw_list" ) + assert( error == .None, "VEFontCache.init : Failed to allocate calls for indices array for clear_draw_list" ) clear_draw_list.vertices, error = make( Array(Vertex), cast(u64) glyph_draw_params.buffer_batch * 2 * 4 ) - assert( error != .None, "VEFontCache.init : Failed to allocate vertices array for clear_draw_list" ) + assert( error == .None, "VEFontCache.init : Failed to allocate vertices array for clear_draw_list" ) } parser_init( & parser_ctx ) @@ -360,6 +364,7 @@ load_font :: proc( ctx : ^Context, label : string, data : []byte, size_px : f32 assert( ctx != nil ) assert( len(data) > 0 ) using ctx + context.allocator = backing id : i32 = -1 for index : i32 = 0; index < i32(entries.num); index += 1 { @@ -398,6 +403,7 @@ unload_font :: proc( ctx : ^Context, font : FontID ) { assert( ctx != nil ) assert( font >= 0 && u64(font) < ctx.entries.num ) + context.allocator = ctx.backing using ctx entry := & entries.data[ font ] @@ -642,65 +648,10 @@ cache_glyph_to_atlas :: proc( ctx : ^Context, font : FontID, glyph_index : Glyph append( & atlas.draw_list.calls, call ) } - // Render glyph to glyph_update_FBO cache_glyph( ctx, font, glyph_index, glyph_draw_scale, glyph_draw_translate ) } -directly_draw_massive_glyph :: proc( ctx : ^Context, entry : ^Entry, glyph : Glyph, bounds_0 : Vec2i, bounds_width, bounds_height : u32, over_sample, position, scale : Vec2 ) -{ - flush_glyph_buffer_to_atlas( ctx ) - - // Draw un-antialiased glyph to update FBO. - glyph_draw_scale := over_sample * entry.size_scale - glyph_draw_translate := - Vec2{ f32(bounds_0.x), f32(bounds_0.y)} * glyph_draw_scale + Vec2{ f32(ctx.atlas.glyph_padding), f32(ctx.atlas.glyph_padding) } - screenspace_x_form( & glyph_draw_translate, & glyph_draw_scale, f32(ctx.atlas.buffer_width), f32(ctx.atlas.buffer_height) ) - - cache_glyph( ctx, entry.id, glyph, glyph_draw_scale, glyph_draw_translate ) - - // Figure out the source rect. - glyph_position := Vec2 {} - glyph_width := f32(bounds_width) * entry.size_scale * over_sample.x - glyph_height := f32(bounds_height) * entry.size_scale * over_sample.y - glyph_dst_width := f32(bounds_width) * entry.size_scale - glyph_dst_height := f32(bounds_height) * entry.size_scale - glyph_width += f32(2 * ctx.atlas.glyph_padding) - glyph_height += f32(2 * ctx.atlas.glyph_padding) - - // Figure out the destination rect. - bounds_scaled := Vec2 { - cast(f32) i32(f32(bounds_0.x) * entry.size_scale - 0.5), - cast(f32) i32(f32(bounds_0.y) * entry.size_scale - 0.5), - } - dst := position + scale * bounds_scaled - dst_width := scale.x * glyph_dst_width - dst_height := scale.y * glyph_dst_height - dst.x -= scale.x * f32(ctx.atlas.glyph_padding) - dst.y -= scale.y * f32(ctx.atlas.glyph_padding) - - glyph_size := Vec2 { glyph_width, glyph_height } - textspace_x_form( & glyph_position, & glyph_size, f32(ctx.atlas.buffer_width), f32(ctx.atlas.buffer_height) ) - - // Add the glyph drawcall. - call : DrawCall - { - using call - pass = .Target_Unchanged - colour = ctx.colour - start_index = u32(ctx.draw_list.indices.num) - blit_quad( & ctx.draw_list, dst, dst + { dst_width, dst_height }, glyph_position, glyph_position + glyph_size ) - end_index = 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 ) -} - is_empty :: proc( ctx : ^Context, entry : ^Entry, glyph_index : Glyph ) -> b32 { if glyph_index == 0 do return true diff --git a/code/font/VEFontCache/draw.odin b/code/font/VEFontCache/draw.odin index 31cb53e..99a82dd 100644 --- a/code/font/VEFontCache/draw.odin +++ b/code/font/VEFontCache/draw.odin @@ -89,6 +89,60 @@ 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 ) +{ + flush_glyph_buffer_to_atlas( ctx ) + + // Draw un-antialiased glyph to update FBO. + glyph_draw_scale := over_sample * entry.size_scale + glyph_draw_translate := - Vec2{ f32(bounds_0.x), f32(bounds_0.y)} * glyph_draw_scale + Vec2{ f32(ctx.atlas.glyph_padding), f32(ctx.atlas.glyph_padding) } + screenspace_x_form( & glyph_draw_translate, & glyph_draw_scale, f32(ctx.atlas.buffer_width), f32(ctx.atlas.buffer_height) ) + + cache_glyph( ctx, entry.id, glyph, glyph_draw_scale, glyph_draw_translate ) + + // Figure out the source rect. + glyph_position := Vec2 {} + glyph_width := f32(bounds_width) * entry.size_scale * over_sample.x + glyph_height := f32(bounds_height) * entry.size_scale * over_sample.y + glyph_dst_width := f32(bounds_width) * entry.size_scale + glyph_dst_height := f32(bounds_height) * entry.size_scale + glyph_width += f32(2 * ctx.atlas.glyph_padding) + glyph_height += f32(2 * ctx.atlas.glyph_padding) + + // Figure out the destination rect. + bounds_scaled := Vec2 { + cast(f32) i32(f32(bounds_0.x) * entry.size_scale - 0.5), + cast(f32) i32(f32(bounds_0.y) * entry.size_scale - 0.5), + } + dst := position + scale * bounds_scaled + dst_width := scale.x * glyph_dst_width + dst_height := scale.y * glyph_dst_height + dst.x -= scale.x * f32(ctx.atlas.glyph_padding) + dst.y -= scale.y * f32(ctx.atlas.glyph_padding) + + glyph_size := Vec2 { glyph_width, glyph_height } + textspace_x_form( & glyph_position, & glyph_size, f32(ctx.atlas.buffer_width), f32(ctx.atlas.buffer_height) ) + + // Add the glyph drawcall. + call : DrawCall + { + using call + pass = .Target_Unchanged + colour = ctx.colour + start_index = u32(ctx.draw_list.indices.num) + blit_quad( & ctx.draw_list, dst, dst + { dst_width, dst_height }, glyph_position, glyph_position + glyph_size ) + end_index = 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 ) +} + draw_cached_glyph :: proc( ctx : ^Context, entry : ^Entry, glyph_index : Glyph, position, scale : Vec2 ) -> b32 { // Glyph not in current font diff --git a/code/font/VEFontCache/parser.odin b/code/font/VEFontCache/parser.odin index 69e69d9..661911f 100644 --- a/code/font/VEFontCache/parser.odin +++ b/code/font/VEFontCache/parser.odin @@ -78,7 +78,7 @@ parser_load_font :: proc( ctx : ParserContext, label : string, data : []byte ) - error : AllocatorError font, error = set( ctx.fonts, key, ParserFontInfo {} ) - assert( error != .None, "VEFontCache.parser_load_font: Failed to set a new parser font info" ) + assert( error == .None, "VEFontCache.parser_load_font: Failed to set a new parser font info" ) switch ctx.kind { case .Freetype: @@ -434,7 +434,7 @@ parser_convert_conic_to_cubic_freetype :: proc( vertices : Array(ParserGlyphVert scratch_arena : Arena; arena_init(& scratch_arena, scratch[:]) points, error := make( Array(freetype.Vector), 256, allocator = arena_allocator( &scratch_arena) ) - assert(error != .None) + assert(error == .None) append( & points, p0) append( & points, p1) diff --git a/code/font/VEFontCache/shaper.odin b/code/font/VEFontCache/shaper.odin index 9ec8720..dfa9107 100644 --- a/code/font/VEFontCache/shaper.odin +++ b/code/font/VEFontCache/shaper.odin @@ -44,7 +44,7 @@ shaper_load_font :: proc( ctx : ^ShaperContext, label : string, data : []byte, u error : AllocatorError info, error = set( ctx.infos, key, ShaperInfo {} ) - assert( error != .None, "VEFontCache.parser_load_font: Failed to set a new shaper info" ) + assert( error == .None, "VEFontCache.parser_load_font: Failed to set a new shaper info" ) using info blob = harfbuzz.blob_create( raw_data(data), cast(c.uint) len(data), harfbuzz.Memory_Mode.READONLY, user_data, nil ) diff --git a/code/font/fontstash/atlas.odin b/code/font/fontstash/atlas.odin index fc38893..8c26bd3 100644 --- a/code/font/fontstash/atlas.odin +++ b/code/font/fontstash/atlas.odin @@ -65,7 +65,7 @@ atlas_init :: proc( ctx : ^Context, width, height : i32, num_nodes : u32 = Init_ { error : AllocatorError ctx.atlas, error = make( Array(AtlasNode), u64(num_nodes), dbg_name = "font atlas" ) - ensure(error != AllocatorError.None, "Failed to allocate font atlas") + ensure(error == AllocatorError.None, "Failed to allocate font atlas") ctx.width = width ctx.height = height diff --git a/code/sectr/engine/render.odin b/code/sectr/engine/render.odin index f461b8e..c80322d 100644 --- a/code/sectr/engine/render.odin +++ b/code/sectr/engine/render.odin @@ -68,7 +68,7 @@ render :: proc() } // learnopengl.com/In-Practice/Text-Rendering - if true + when false { profile("learngl_text_render_pass") using font_provider_data diff --git a/code/sectr/font/provider_fontstash.odin b/code/sectr/font/.provider_fontstash.odin similarity index 100% rename from code/sectr/font/provider_fontstash.odin rename to code/sectr/font/.provider_fontstash.odin diff --git a/code/sectr/font/provider.odin b/code/sectr/font/.raw_provider.odin similarity index 98% rename from code/sectr/font/provider.odin rename to code/sectr/font/.raw_provider.odin index a971c36..6f9568a 100644 --- a/code/sectr/font/provider.odin +++ b/code/sectr/font/.raw_provider.odin @@ -21,11 +21,6 @@ Font_TTF_Default_Chars_Padding :: 4 Font_Load_Use_Default_Size :: -1 Font_Load_Gen_ID :: "" -Font_Atlas_Packing_Method :: enum u32 { - Raylib_Basic = 0, // Basic packing algo - Skyeline_Rect = 1, // stb_pack_rect -} - FontID :: struct { key : u64, label : string, diff --git a/code/sectr/font/cache.odin b/code/sectr/font/cache.odin deleted file mode 100644 index 2432dbc..0000000 --- a/code/sectr/font/cache.odin +++ /dev/null @@ -1 +0,0 @@ -package sectr diff --git a/code/sectr/font/provider_VEFontCache.odin b/code/sectr/font/provider_VEFontCache.odin index 64fc184..856d0ff 100644 --- a/code/sectr/font/provider_VEFontCache.odin +++ b/code/sectr/font/provider_VEFontCache.odin @@ -1,3 +1,98 @@ package sectr -import "codebase:font/VEFontCache" +import "core:os" +import ve "codebase:font/VEFontCache" +import sokol_gfx "thirdparty:sokol/gfx" + + +Font_Provider_Use_Freetype :: false +Font_Largest_Px_Size :: 72 +Font_Size_Interval :: 2 + +Font_Default :: FontID { 0, "" } +Font_Default_Point_Size :: 18.0 + +Font_Load_Use_Default_Size :: -1 +Font_Load_Gen_ID :: "" + +FontID :: struct { + key : u64, + label : string, +} + +FontDef :: struct { + path_file : string, + ve_id : ve.FontID, +} + +FontProviderData :: struct +{ + ve_font_cache : ve.Context, + font_cache : HMapChained(FontDef), + + gfx_bindings : sokol_gfx.Bindings, + gfx_pipeline : sokol_gfx.Pipeline, + gfx_v_buffer : sokol_gfx.Buffer, + gfx_uv_buffer : sokol_gfx.Buffer, + gfx_sampler : sokol_gfx.Sampler, +} + +font_provider_startup :: proc() +{ + profile(#procedure) + state := get_state() + + provider_data := state.font_provider_data; using provider_data + + error : AllocatorError + font_cache, error = make( HMapChained(FontDef), hmap_closest_prime(1 * Kilo), persistent_allocator() /*dbg_name = "font_cache"*/ ) + verify( error == AllocatorError.None, "Failed to allocate font_cache" ) + + ve.init( & provider_data.ve_font_cache, allocator = persistent_slab_allocator() ) + log("VEFontCached initialized") + + // TODO(Ed): Setup sokol hookup for VEFontCache +} + +font_provider_shutdown :: proc() +{ + state := get_state() + provider_data := state.font_provider_data; using provider_data + + ve.shutdown( & provider_data.ve_font_cache ) +} + + +font_load :: proc(path_file : string, + default_size : f32 = Font_Load_Use_Default_Size, + desired_id : string = Font_Load_Gen_ID +) -> FontID +{ + profile(#procedure) + + logf("Loading font: %v", path_file) + provider_data := & get_state().font_provider_data; using provider_data + + font_data, read_succeded : = os.read_entire_file( path_file ) + verify( b32(read_succeded), str_fmt("Failed to read font file for: %v", path_file) ) + font_data_size := cast(i32) len(font_data) + + desired_id := desired_id + // Use file name as key + if len(desired_id) == 0 { + // NOTE(Ed): This should never be used except for laziness so I'll be throwing a warning everytime. + log("desired_key not provided, using file name. Give it a proper name!", LogLevel.Warning) + // desired_id = cast(FontID) file_name_from_path(path_file) + desired_id = file_name_from_path(path_file) + } + + key := cast(u64) crc32( transmute([]byte) desired_id ) + def, set_error := hmap_chained_set(font_cache, key, FontDef{}) + verify( set_error == AllocatorError.None, "Failed to add new font entry to cache" ) + + // TODO(Ed): Load even sizes from 8px to upper bound. + def.ve_id = ve.load_font( & provider_data.ve_font_cache, desired_id, font_data, default_size ) + + fid := FontID { key, desired_id } + return fid +}