From e59879f69697641469f4422ede349a39b1c6eb77 Mon Sep 17 00:00:00 2001 From: Ed_ Date: Sat, 29 Jun 2024 22:38:59 -0400 Subject: [PATCH] Changes based on latest from Sectr Prototype --- LRU.odin | 33 +++----- Readme.md | 14 +++- VEFontCache.odin | 106 ++++++++++++------------- atlas.odin | 32 ++++---- draw.odin | 200 +++++++++++++---------------------------------- mappings.odin | 8 +- misc.odin | 28 +++---- parser.odin | 3 +- 8 files changed, 166 insertions(+), 258 deletions(-) diff --git a/LRU.odin b/LRU.odin index ea97a6b..f04da54 100644 --- a/LRU.odin +++ b/LRU.odin @@ -20,12 +20,12 @@ PoolList :: struct { free_list : [dynamic]PoolListIter, front : PoolListIter, back : PoolListIter, - size : u32, - capacity : u32, + size : i32, + capacity : i32, dbg_name : string, } -pool_list_init :: proc( pool : ^PoolList, capacity : u32, dbg_name : string = "" ) +pool_list_init :: proc( pool : ^PoolList, capacity : i32, dbg_name : string = "" ) { error : AllocatorError pool.items, error = make( [dynamic]PoolListItem, int(capacity) ) @@ -53,13 +53,11 @@ pool_list_init :: proc( pool : ^PoolList, capacity : u32, dbg_name : string = "" back = -1 } -pool_list_free :: proc( pool : ^PoolList ) -{ +pool_list_free :: proc( pool : ^PoolList ) { // TODO(Ed): Implement } -pool_list_reload :: proc( pool : ^PoolList, allocator : Allocator ) -{ +pool_list_reload :: proc( pool : ^PoolList, allocator : Allocator ) { reload_array( & pool.items, allocator ) reload_array( & pool.free_list, allocator ) } @@ -160,13 +158,13 @@ LRU_Link :: struct { } LRU_Cache :: struct { - capacity : u32, - num : u32, + capacity : i32, + num : i32, table : map[u64]LRU_Link, key_queue : PoolList, } -LRU_init :: proc( cache : ^LRU_Cache, capacity : u32, dbg_name : string = "" ) { +LRU_init :: proc( cache : ^LRU_Cache, capacity : i32, dbg_name : string = "" ) { error : AllocatorError cache.capacity = capacity cache.table, error = make( map[u64]LRU_Link, uint(capacity) ) @@ -175,23 +173,15 @@ LRU_init :: proc( cache : ^LRU_Cache, capacity : u32, dbg_name : string = "" ) { pool_list_init( & cache.key_queue, capacity, dbg_name = dbg_name ) } -LRU_free :: proc( cache : ^LRU_Cache ) -{ +LRU_free :: proc( cache : ^LRU_Cache ) { // TODO(Ed): Implement } -LRU_reload :: #force_inline proc( cache : ^LRU_Cache, allocator : Allocator ) -{ +LRU_reload :: #force_inline proc( cache : ^LRU_Cache, allocator : Allocator ) { reload_map( & cache.table, allocator ) pool_list_reload( & cache.key_queue, allocator ) } -LRU_hash_key :: #force_inline proc( key : u64 ) -> ( hash : u64 ) { - bytes := transmute( [8]byte ) key - hash = fnv64a( bytes[:] ) - return -} - LRU_find :: #force_inline proc "contextless" ( cache : ^LRU_Cache, key : u64, must_find := false ) -> (LRU_Link, bool) { link, success := cache.table[key] return link, success @@ -205,8 +195,7 @@ LRU_get :: #force_inline proc( cache: ^LRU_Cache, key : u64 ) -> i32 { return -1 } -LRU_get_next_evicted :: #force_inline proc ( cache : ^LRU_Cache ) -> u64 -{ +LRU_get_next_evicted :: #force_inline proc ( cache : ^LRU_Cache ) -> u64 { if cache.key_queue.size >= cache.capacity { evict := pool_list_peek_back( & cache.key_queue ) return evict diff --git a/Readme.md b/Readme.md index f6690a9..d020675 100644 --- a/Readme.md +++ b/Readme.md @@ -6,9 +6,17 @@ Its original purpose was for use in game engines, however its rendeirng quality See: [docs/Readme.md](docs/Readme.md) for the library's interface +## Changes from orignal + +* Font Parser & Glyph shaper are abstracted to their own interface +* Font face parser info encapsulated in parser_info struct. +* ve_fontcache_loadfile not ported (ust use core:os or os2, then call load_font) +* Macro defines have been coverted (mostly) to runtime parameters +* Support for hot_reloading + ## TODOs -### (Making it a more idiomatic library): +### Thirdparty support: * Setup freetype, harfbuzz, depedency management within the library @@ -28,6 +36,7 @@ See: [docs/Readme.md](docs/Readme.md) for the library's interface * Support for harfbuzz * Ability to set a draw transform, viewport and projection * By default the library's position is in unsigned normalized render space + * Could implement a similar design to sokol_gp's interface * Allow curve_quality to be set on a per-font basis ### Optimization: @@ -43,9 +52,10 @@ See: [docs/Readme.md](docs/Readme.md) for the library's interface * glyph_draw_buffer * shape_cache * This would need to converge to the singlar draw_list on a per layer basis (then user reqeusts a draw_list layer there could a yield to wait for the jobs to finish); if the interface expects the user to issue the commands single-threaded unless, we just assume the user is going to feed the gpu the commands & data through separate threads as well (not ideal ux). + * How the contexts are given jobs should be left up to the user (can recommend a screen quadrant based approach in demo examples) Failed Attempts: * Attempted to chunk the text to more granular 'shapes' from `draw_list` before doing the actual call to `draw_text_shape`. This lead to a larger performance cost due to the additional iteration across the text string. -* Attempted to cache the shape draw_list for future calls. Led to larger performance cost due to additional iteration in the `merge_draw_list`. +* Attempted to cache the shape draw_list for future calls. Led to larger performance cost due to additional iteration in the `merge_draw_list`. * The shapes glyphs must still be traversed to identify if the glyph is cached. This arguably could be handled in `shape_text_uncached`, however that would require a significan't amount of refactoring to identify... (and would be more unergonomic when shapers libs are processing the text) diff --git a/VEFontCache.odin b/VEFontCache.odin index d6bff85..528efcf 100644 --- a/VEFontCache.odin +++ b/VEFontCache.odin @@ -1,14 +1,11 @@ /* A port of (https://github.com/hypernewbie/VEFontCache) to Odin. -Status: -This port is heavily tied to the grime package in SectrPrototype. - Changes: - 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 -- Macro defines have been made into runtime parameters +- Macro defines have been made (mostly) into runtime parameters */ package VEFontCache @@ -20,12 +17,13 @@ FontID :: distinct i64 Glyph :: distinct i32 Entry :: struct { - parser_info : ParserFontInfo, - shaper_info : ShaperInfo, - id : FontID, - used : b32, - size : f32, - size_scale : f32, + parser_info : ParserFontInfo, + shaper_info : ShaperInfo, + id : FontID, + used : b32, + curve_quality : f32, + size : f32, + size_scale : f32, } Entry_Default :: Entry { @@ -46,16 +44,14 @@ Context :: struct { temp_path : [dynamic]Vertex, temp_codepoint_seen : map[u64]bool, - temp_codepoint_seen_num : u32, + temp_codepoint_seen_num : int, - snap_width : u32, - snap_height : u32, + snap_width : f32, + snap_height : f32, colour : Colour, cursor_pos : Vec2, - // draw_cursor_pos : Vec2, - draw_layer : struct { vertices_offset : int, indices_offset : int, @@ -67,8 +63,8 @@ Context :: struct { glyph_buffer : GlyphDrawBuffer, shape_cache : ShapedTextCache, - curve_quality : u32, - text_shape_adv : b32, + default_curve_quality : i32, + text_shape_adv : b32, debug_print : b32, debug_print_verbose : b32, @@ -116,7 +112,7 @@ InitAtlasParams_Default :: InitAtlasParams { } InitGlyphDrawParams :: struct { - over_sample : Vec2, + over_sample : Vec2i, buffer_batch : u32, draw_padding : u32, } @@ -133,8 +129,8 @@ InitShapeCacheParams :: struct { } InitShapeCacheParams_Default :: InitShapeCacheParams { - capacity = 2048, - reserve_length = 2048, + capacity = 8 * 1024, + reserve_length = 1 * 1024, } // ve_fontcache_init @@ -143,7 +139,7 @@ startup :: proc( ctx : ^Context, parser_kind : ParserKind, atlas_params := InitAtlasParams_Default, glyph_draw_params := InitGlyphDrawParams_Default, shape_cache_params := InitShapeCacheParams_Default, - curve_quality : u32 = 3, + default_curve_quality : u32 = 3, entires_reserve : u32 = 512, temp_path_reserve : u32 = 1024, temp_codepoint_seen_reserve : u32 = 2048, @@ -155,10 +151,10 @@ startup :: proc( ctx : ^Context, parser_kind : ParserKind, ctx.backing = allocator context.allocator = ctx.backing - if curve_quality == 0 { - curve_quality = 3 + if default_curve_quality == 0 { + default_curve_quality = 3 } - ctx.curve_quality = curve_quality + ctx.default_curve_quality = default_curve_quality error : AllocatorError entries, error = make( [dynamic]Entry, len = 0, cap = entires_reserve ) @@ -184,8 +180,8 @@ startup :: proc( ctx : ^Context, parser_kind : ParserKind, using region next_idx = 0; - width = region_params.width - height = region_params.height + width = i32(region_params.width) + height = i32(region_params.height) size = { i32(params.width) / factor.x, i32(params.height) / factor.y, @@ -197,28 +193,26 @@ startup :: proc( ctx : ^Context, parser_kind : ParserKind, assert( capacity.x * capacity.y == expected_cap ) error : AllocatorError - // state.cache, error = make( HMapChained(LRU_Link), uint(capacity.x * capacity.y) ) - // assert( error == .None, "VEFontCache.init_atlas_region : Failed to allocate state.cache") - LRU_init( & state, u32(capacity.x * capacity.y) ) + LRU_init( & state, capacity.x * capacity.y ) } init_atlas_region( & atlas.region_a, atlas_params, atlas_params.region_a, { 4, 2}, 1024 ) init_atlas_region( & atlas.region_b, atlas_params, atlas_params.region_b, { 4, 2}, 512 ) init_atlas_region( & atlas.region_c, atlas_params, atlas_params.region_c, { 4, 1}, 512 ) init_atlas_region( & atlas.region_d, atlas_params, atlas_params.region_d, { 2, 1}, 256 ) - atlas.width = atlas_params.width - atlas.height = atlas_params.height - atlas.glyph_padding = atlas_params.glyph_padding + atlas.width = i32(atlas_params.width) + atlas.height = i32(atlas_params.height) + atlas.glyph_padding = i32(atlas_params.glyph_padding) atlas.region_a.offset = {0, 0} atlas.region_b.offset.x = 0 atlas.region_b.offset.y = atlas.region_a.size.y atlas.region_c.offset.x = atlas.region_a.size.x atlas.region_c.offset.y = 0 - atlas.region_d.offset.x = i32(atlas.width) / 2 + atlas.region_d.offset.x = atlas.width / 2 atlas.region_d.offset.y = 0 - LRU_init( & shape_cache.state, shape_cache_params.capacity ) + LRU_init( & shape_cache.state, i32(shape_cache_params.capacity) ) shape_cache.storage, error = make( [dynamic]ShapedText, shape_cache_params.capacity ) assert(error == .None, "VEFontCache.init : Failed to allocate shape_cache.storage") @@ -245,11 +239,11 @@ startup :: proc( ctx : ^Context, parser_kind : ParserKind, // Note(From original author): We can actually go over VE_FONTCACHE_GLYPHDRAW_BUFFER_BATCH batches due to smart packing! { using glyph_buffer - over_sample = glyph_draw_params.over_sample - batch = glyph_draw_params.buffer_batch - width = atlas.region_d.width * u32(over_sample.x) * batch - height = atlas.region_d.height * u32(over_sample.y) - draw_padding = glyph_draw_params.draw_padding + over_sample = vec2(glyph_draw_params.over_sample) + batch = cast(i32) glyph_draw_params.buffer_batch + width = atlas.region_d.width * i32(over_sample.x) * batch + height = atlas.region_d.height * i32(over_sample.y) + draw_padding = cast(i32) glyph_draw_params.draw_padding draw_list.calls, error = make( [dynamic]DrawCall, len = 0, cap = glyph_draw_params.buffer_batch * 2 ) assert( error == .None, "VEFontCache.init : Failed to allocate calls for draw_list" ) @@ -295,7 +289,7 @@ hot_reload :: proc( ctx : ^Context, allocator : Allocator ) LRU_reload( & atlas.region_d.state, allocator) LRU_reload( & shape_cache.state, allocator ) - for idx : u32 = 0; idx < u32(len(shape_cache.storage)); idx += 1 { + for idx : i32 = 0; idx < i32(len(shape_cache.storage)); idx += 1 { stroage_entry := & shape_cache.storage[idx] using stroage_entry @@ -332,7 +326,7 @@ shutdown :: proc( ctx : ^Context ) } // ve_fontcache_load -load_font :: proc( ctx : ^Context, label : string, data : []byte, size_px : f32 ) -> (font_id : FontID) +load_font :: proc( ctx : ^Context, label : string, data : []byte, size_px : f32, glyph_curve_quality : u32 = 0 ) -> (font_id : FontID) { assert( ctx != nil ) assert( len(data) > 0 ) @@ -355,19 +349,25 @@ load_font :: proc( ctx : ^Context, label : string, data : []byte, size_px : f32 entry := & entries[ id ] { using entry + used = true parser_info = parser_load_font( & parser_ctx, label, data ) // assert( parser_info != nil, "VEFontCache.load_font: Failed to load font info from parser" ) + shaper_info = shaper_load_font( & shaper_ctx, label, data, transmute(rawptr) id ) + // assert( shaper_info != nil, "VEFontCache.load_font: Failed to load font from shaper") + size = size_px size_scale = size_px < 0.0 ? \ parser_scale_for_pixel_height( & parser_info, -size_px ) \ : parser_scale_for_mapping_em_to_pixels( & parser_info, size_px ) - used = true - - shaper_info = shaper_load_font( & shaper_ctx, label, data, transmute(rawptr) id ) - // assert( shaper_info != nil, "VEFontCache.load_font: Failed to load font from shaper") + if glyph_curve_quality == 0 { + curve_quality = f32(ctx.default_curve_quality) + } + else { + curve_quality = f32(glyph_curve_quality) + } } entry.id = FontID(id) ctx.entries[ id ].id = FontID(id) @@ -398,8 +398,8 @@ unload_font :: proc( ctx : ^Context, font : FontID ) // ve_fontcache_configure_snap configure_snap :: #force_inline proc( ctx : ^Context, snap_width, snap_height : u32 ) { assert( ctx != nil ) - ctx.snap_width = snap_width - ctx.snap_height = snap_height + ctx.snap_width = f32(snap_width) + ctx.snap_height = f32(snap_height) } get_cursor_pos :: #force_inline proc "contextless" ( ctx : ^Context ) -> Vec2 { return ctx.cursor_pos } @@ -413,13 +413,11 @@ draw_text :: proc( ctx : ^Context, font : FontID, text_utf8 : string, position, ctx.cursor_pos = {} - position := position - snap_width := f32(ctx.snap_width) - snap_height := f32(ctx.snap_height) - if ctx.snap_width > 0 do position.x = cast(f32) cast(u32) (position.x * snap_width + 0.5) / snap_width - if ctx.snap_height > 0 do position.y = cast(f32) cast(u32) (position.y * snap_height + 0.5) / snap_height + position := position + if ctx.snap_width > 0 do position.x = cast(f32) cast(u32) (position.x * ctx.snap_width + 0.5) / ctx.snap_width + if ctx.snap_height > 0 do position.y = cast(f32) cast(u32) (position.y * ctx.snap_height + 0.5) / ctx.snap_height - entry := & ctx.entries[ font ] + entry := & ctx.entries[ font ] ChunkType :: enum u32 { Visible, Formatting } chunk_kind : ChunkType @@ -432,7 +430,7 @@ draw_text :: proc( ctx : ^Context, font : FontID, text_utf8 : string, position, text_chunk = transmute(string) text_utf8_bytes[ : ] if len(text_chunk) > 0 { shaped := shape_text_cached( ctx, font, text_chunk, entry ) - ctx.cursor_pos = draw_text_shape( ctx, font, entry, shaped, position, scale, snap_width, snap_height ) + ctx.cursor_pos = draw_text_shape( ctx, font, entry, shaped, position, scale, ctx.snap_width, ctx.snap_height ) } return true } diff --git a/atlas.odin b/atlas.odin index dd942ef..600a826 100644 --- a/atlas.odin +++ b/atlas.odin @@ -13,21 +13,21 @@ AtlasRegionKind :: enum u8 { AtlasRegion :: struct { state : LRU_Cache, - width : u32, - height : u32, + width : i32, + height : i32, size : Vec2i, capacity : Vec2i, offset : Vec2i, - next_idx : u32, + next_idx : i32, } Atlas :: struct { - width : u32, - height : u32, + width : i32, + height : i32, - glyph_padding : u32, + glyph_padding : i32, region_a : AtlasRegion, region_b : AtlasRegion, @@ -43,8 +43,8 @@ atlas_bbox :: proc( atlas : ^Atlas, region : AtlasRegionKind, local_idx : i32 ) size.x = f32(atlas.region_a.width) size.y = f32(atlas.region_a.height) - position.x = cast(f32) (( local_idx % atlas.region_a.capacity.x ) * i32(atlas.region_a.width)) - position.y = cast(f32) (( local_idx / atlas.region_a.capacity.x ) * i32(atlas.region_a.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) @@ -53,8 +53,8 @@ atlas_bbox :: proc( atlas : ^Atlas, region : AtlasRegionKind, local_idx : i32 ) size.x = f32(atlas.region_b.width) size.y = f32(atlas.region_b.height) - position.x = cast(f32) (( local_idx % atlas.region_b.capacity.x ) * i32(atlas.region_b.width)) - position.y = cast(f32) (( local_idx / atlas.region_b.capacity.x ) * i32(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) @@ -63,8 +63,8 @@ atlas_bbox :: proc( atlas : ^Atlas, region : AtlasRegionKind, local_idx : i32 ) size.x = f32(atlas.region_c.width) size.y = f32(atlas.region_c.height) - position.x = cast(f32) (( local_idx % atlas.region_c.capacity.x ) * i32(atlas.region_c.width)) - position.y = cast(f32) (( local_idx / atlas.region_c.capacity.x ) * i32(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) @@ -73,8 +73,8 @@ atlas_bbox :: proc( atlas : ^Atlas, region : AtlasRegionKind, local_idx : i32 ) size.x = f32(atlas.region_d.width) size.y = f32(atlas.region_d.height) - position.x = cast(f32) (( local_idx % atlas.region_d.capacity.x ) * i32(atlas.region_d.width)) - position.y = cast(f32) (( local_idx / atlas.region_d.capacity.x ) * i32(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) @@ -101,8 +101,8 @@ decide_codepoint_region :: proc(ctx : ^Context, entry : ^Entry, glyph_index : Gl glyph_buffer := & ctx.glyph_buffer glyph_padding := f32( atlas.glyph_padding ) * 2 - bounds_width_scaled := u32(bounds_width * entry.size_scale + glyph_padding) - bounds_height_scaled := u32(bounds_height * entry.size_scale + glyph_padding) + bounds_width_scaled := i32(bounds_width * entry.size_scale + glyph_padding) + bounds_height_scaled := i32(bounds_height * entry.size_scale + glyph_padding) // Use a lookup table for faster region selection region_lookup := [4]struct { kind: AtlasRegionKind, region: ^AtlasRegion } { diff --git a/draw.odin b/draw.odin index 1e8ad8c..5679bbf 100644 --- a/draw.odin +++ b/draw.odin @@ -39,10 +39,10 @@ FrameBufferPass :: enum u32 { GlyphDrawBuffer :: struct { over_sample : Vec2, - batch : u32, - width : u32, - height : u32, - draw_padding : u32, + batch : i32, + width : i32, + height : i32, + draw_padding : i32, batch_x : i32, clear_draw_list : DrawList, @@ -106,23 +106,8 @@ cache_glyph :: proc(ctx : ^Context, font : FontID, glyph_index : Glyph, entry : path := &ctx.temp_path clear(path) - append_bezier_curve :: #force_inline proc(path: ^[dynamic]Vertex, p0, p1, p2: Vec2, quality: u32) { - step := 1.0 / f32(quality) - for index := u32(1); index <= quality; index += 1 { - alpha := f32(index) * step - append( path, Vertex { pos = eval_point_on_bezier3(p0, p1, p2, alpha) } ) - } - } - - append_bezier_curve_cubic :: #force_inline proc(path: ^[dynamic]Vertex, p0, p1, p2, p3: Vec2, quality: u32) { - step := 1.0 / f32(quality) - for index := u32(1); index <= quality; index += 1 { - alpha := f32(index) * step - append( path, Vertex { pos = eval_point_on_bezier4(p0, p1, p2, p3, alpha) } ) - } - } - - for edge in shape do #partial switch edge.type { + for edge in shape do #partial switch edge.type + { case .Move: if len(path) > 0 { draw_filled_path(&ctx.draw_list, outside, path[:], scale, translate, ctx.debug_print_verbose) @@ -138,7 +123,12 @@ cache_glyph :: proc(ctx : ^Context, font : FontID, glyph_index : Glyph, entry : p0 := path[ len(path) - 1].pos p1 := Vec2{ f32(edge.contour_x0), f32(edge.contour_y0) } p2 := Vec2{ f32(edge.x), f32(edge.y) } - append_bezier_curve( path, p0, p1, p2, ctx.curve_quality ) + + step := 1.0 / entry.curve_quality + for index : f32 = 1; index <= entry.curve_quality; index += 1 { + alpha := index * step + append( path, Vertex { pos = eval_point_on_bezier3(p0, p1, p2, alpha) } ) + } case .Cubic: assert( len(path) > 0) @@ -146,7 +136,12 @@ cache_glyph :: proc(ctx : ^Context, font : FontID, glyph_index : Glyph, entry : p1 := Vec2{ f32(edge.contour_x0), f32(edge.contour_y0) } p2 := Vec2{ f32(edge.contour_x1), f32(edge.contour_y1) } p3 := Vec2{ f32(edge.x), f32(edge.y) } - append_bezier_curve_cubic( path, p0, p1, p2, p3, ctx.curve_quality ) + + step := 1.0 / entry.curve_quality + for index : f32 = 1; index <= entry.curve_quality; index += 1 { + alpha := index * step + append( path, Vertex { pos = eval_point_on_bezier4(p0, p1, p2, p3, alpha) } ) + } } if len(path) > 0 { @@ -313,7 +308,7 @@ check_glyph_in_atlas :: #force_inline proc( ctx : ^Context, font : FontID, entry if atlas_index == - 1 { - if region.next_idx > u32( 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_codepoint := LRU_get_next_evicted( & region.state ) seen, success := ctx.temp_codepoint_seen[next_evict_codepoint] @@ -405,90 +400,6 @@ directly_draw_massive_glyph :: proc( ctx : ^Context, append( & ctx.draw_list.calls, ..calls[:] ) } -draw_cached_glyph :: proc( ctx : ^Context, shaped : ^ShapedText, - entry : ^Entry, - glyph_index : Glyph, - lru_code : u64, - atlas_index : i32, - bounds_0, bounds_1 : Vec2, - region_kind : AtlasRegionKind, - region : ^AtlasRegion, - over_sample : Vec2, - position, scale : Vec2 -) -> b32 -{ - // profile(#procedure) - bounds_size := Vec2 { - f32(bounds_1.x - bounds_0.x), - f32(bounds_1.y - bounds_0.y), - } - - // E region is special case and not cached to atlas - if region_kind == .E - { - directly_draw_massive_glyph( ctx, entry, glyph_index, bounds_0, bounds_1, bounds_size, over_sample, position, scale ) - return true - } - - // Is this codepoint cached? - if atlas_index == - 1 { - return false - } - - atlas := & ctx.atlas - atlas_size := Vec2 { f32(atlas.width), f32(atlas.height) } - glyph_padding := f32(atlas.glyph_padding) - - // Figure out the source bounding box in the atlas texture - slot_position, _ := atlas_bbox( atlas, region_kind, atlas_index ) - - glyph_scale := bounds_size * entry.size_scale + glyph_padding - - bounds_0_scaled := bounds_0 * entry.size_scale //- { 0.5, 0.5 } - bounds_0_scaled = ceil(bounds_0_scaled) - - dst := position + (bounds_0_scaled - glyph_padding) * scale - dst_scale := glyph_scale * scale - - textspace_x_form( & slot_position, & glyph_scale, atlas_size ) - - // Shape call setup - when false - { - call := DrawCall_Default - { - using call - pass = .Target - colour = ctx.colour - start_index = cast(u32) len(shaped.draw_list.indices) - - blit_quad( & shaped.draw_list, - dst, dst + dst_scale, - slot_position, slot_position + glyph_scale ) - end_index = cast(u32) len(shaped.draw_list.indices) - } - append( & shaped.draw_list.calls, call ) - } - else - { - // Add the glyph drawcall - call := DrawCall_Default - { - using call - pass = .Target - colour = ctx.colour - start_index = cast(u32) len(ctx.draw_list.indices) - - blit_quad( & ctx.draw_list, - dst, dst + dst_scale, - slot_position, slot_position + glyph_scale ) - end_index = cast(u32) len(ctx.draw_list.indices) - } - 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. // @@ -551,49 +462,50 @@ draw_text_batch :: proc(ctx: ^Context, entry: ^Entry, shaped: ^ShapedText, for index := batch_start_idx; index < batch_end_idx; index += 1 { - glyph_index := shaped.glyphs[index] + glyph_index := shaped.glyphs[index] - if glyph_index == 0 || parser_is_glyph_empty( & entry.parser_info, glyph_index) do continue + if glyph_index == 0 || parser_is_glyph_empty( & entry.parser_info, 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 := 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 := Vec2 { vbounds_1.x - vbounds_0.x, vbounds_1.y - vbounds_0.y } + 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 := Vec2 { vbounds_1.x - vbounds_0.x, vbounds_1.y - vbounds_0.y } - shaped_position := shaped.positions[index] - glyph_translate := position + shaped_position * scale + shaped_position := shaped.positions[index] + glyph_translate := position + shaped_position * scale - 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 - { - 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 ) - dst := glyph_translate + (bounds_0_scaled - glyph_padding) * scale - dst_scale := glyph_scale * scale - textspace_x_form( & slot_position, & 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 + { + // Draw cacxhed 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 ) + dst := glyph_translate + (bounds_0_scaled - glyph_padding) * scale + dst_scale := glyph_scale * scale + textspace_x_form( & slot_position, & glyph_scale, atlas_size ) - call := DrawCall_Default - call.pass = .Target - call.colour = ctx.colour - call.start_index = u32(len(ctx.draw_list.indices)) + call := DrawCall_Default + call.pass = .Target + call.colour = ctx.colour + call.start_index = u32(len(ctx.draw_list.indices)) - blit_quad(&ctx.draw_list, - dst, dst + dst_scale, - slot_position, slot_position + glyph_scale ) + blit_quad(&ctx.draw_list, + dst, dst + dst_scale, + slot_position, slot_position + glyph_scale ) - call.end_index = u32(len(ctx.draw_list.indices)) - append(&ctx.draw_list.calls, call) - } + call.end_index = u32(len(ctx.draw_list.indices)) + append(&ctx.draw_list.calls, call) + } } } diff --git a/mappings.odin b/mappings.odin index e575c15..085006f 100644 --- a/mappings.odin +++ b/mappings.odin @@ -23,10 +23,10 @@ import "core:mem" Arena :: mem.Arena arena_allocator :: mem.arena_allocator arena_init :: mem.arena_init -import "codebase:grime" - log :: grime.log - logf :: grime.logf - profile :: grime.profile +// import "codebase:grime" +// log :: grime.log +// logf :: grime.logf +// profile :: grime.profile //#region("Proc overload mappings") diff --git a/misc.odin b/misc.odin index cba3de4..8470fd1 100644 --- a/misc.odin +++ b/misc.odin @@ -19,24 +19,24 @@ vec2i_from_vec2 :: #force_inline proc "contextless" ( v2 : Vec2 ) -> Vec2 @(require_results) ceil_vec2 :: proc "contextless" ( v : Vec2 ) -> Vec2 { return { ceil_f32(v.x), ceil_f32(v.y) } } // This buffer is used below excluisvely to prevent any allocator recusion when verbose logging from allocators. -// This means a single line is limited to 32k buffer (increase naturally if this SOMEHOW becomes a bottleneck...) -// Logger_Allocator_Buffer : [32 * Kilobyte]u8 +// This means a single line is limited to 4k buffer +Logger_Allocator_Buffer : [4 * Kilobyte]u8 -// log :: proc( msg : string, level := core_log.Level.Info, loc := #caller_location ) { -// temp_arena : Arena; arena_init(& temp_arena, Logger_Allocator_Buffer[:]) -// context.allocator = arena_allocator(& temp_arena) -// context.temp_allocator = arena_allocator(& temp_arena) +log :: proc( msg : string, level := core_log.Level.Info, loc := #caller_location ) { + temp_arena : Arena; arena_init(& temp_arena, Logger_Allocator_Buffer[:]) + context.allocator = arena_allocator(& temp_arena) + context.temp_allocator = arena_allocator(& temp_arena) -// core_log.log( level, msg, location = loc ) -// } + core_log.log( level, msg, location = loc ) +} -// logf :: proc( fmt : string, args : ..any, level := core_log.Level.Info, loc := #caller_location ) { -// temp_arena : Arena; arena_init(& temp_arena, Logger_Allocator_Buffer[:]) -// context.allocator = arena_allocator(& temp_arena) -// context.temp_allocator = arena_allocator(& temp_arena) +logf :: proc( fmt : string, args : ..any, level := core_log.Level.Info, loc := #caller_location ) { + temp_arena : Arena; arena_init(& temp_arena, Logger_Allocator_Buffer[:]) + context.allocator = arena_allocator(& temp_arena) + context.temp_allocator = arena_allocator(& temp_arena) -// core_log.logf( level, fmt, ..args, location = loc ) -// } + core_log.logf( level, fmt, ..args, location = loc ) +} reload_array :: proc( self : ^[dynamic]$Type, allocator : Allocator ) { raw := transmute( ^runtime.Raw_Dynamic_Array) self diff --git a/parser.odin b/parser.odin index 52796ec..5d7cb74 100644 --- a/parser.odin +++ b/parser.odin @@ -73,8 +73,7 @@ parser_init :: proc( ctx : ^ParserContext ) // assert( error == .None, "VEFontCache.parser_init: Failed to allocate fonts array" ) } -parser_shutdown :: proc( ctx : ^ParserContext ) -{ +parser_shutdown :: proc( ctx : ^ParserContext ) { // TODO(Ed): Implement }