diff --git a/code/font/VEFontCache/LRU.odin b/code/font/VEFontCache/LRU.odin index 6b00752..35ff834 100644 --- a/code/font/VEFontCache/LRU.odin +++ b/code/font/VEFontCache/LRU.odin @@ -175,7 +175,6 @@ LRU_reload :: #force_inline proc( cache : ^LRU_Cache, allocator : Allocator ) LRU_hash_key :: #force_inline proc( key : u64 ) -> ( hash : u64 ) { bytes := transmute( [8]byte ) key hash = fnv64a( bytes[:] ) - // hash = cast(u64) crc64( bytes[:] ) return } diff --git a/code/font/VEFontCache/VEFontCache.odin b/code/font/VEFontCache/VEFontCache.odin index 3f22c67..c4c5076 100644 --- a/code/font/VEFontCache/VEFontCache.odin +++ b/code/font/VEFontCache/VEFontCache.odin @@ -13,7 +13,6 @@ Changes: package VEFontCache import "base:runtime" -import "core:mem" Advance_Snap_Smallfont_Size :: 12 @@ -22,8 +21,9 @@ Vec2 :: [2]f32 Vec2i :: [2]i32 Vec2_64 :: [2]f64 -vec2_from_scalar :: #force_inline proc( scalar : f32 ) -> Vec2 { return { scalar, scalar } } -vec2_64_from_vec2 :: #force_inline proc( v2 : Vec2 ) -> Vec2_64 { return { f64(v2.x), f64(v2.y) }} +vec2_from_scalar :: #force_inline proc( scalar : f32 ) -> Vec2 { return { scalar, scalar } } +vec2_64_from_vec2 :: #force_inline proc( v2 : Vec2 ) -> Vec2_64 { return { f64(v2.x), f64(v2.y) }} +vec2_from_vec2i :: #force_inline proc( v2i : Vec2i ) -> Vec2 { return { f32(v2i.x), f32(v2i.y) }} FontID :: distinct i64 Glyph :: distinct i32 @@ -71,9 +71,10 @@ Context :: struct { calls_offset : int, }, - draw_list : DrawList, - atlas : Atlas, - shape_cache : ShapedTextCache, + draw_list : DrawList, + atlas : Atlas, + glyph_buffer : GlyphDrawBuffer, + shape_cache : ShapedTextCache, curve_quality : u32, text_shape_adv : b32, @@ -103,7 +104,7 @@ InitAtlasParams :: struct { InitAtlasParams_Default :: InitAtlasParams { width = 4096, height = 2048, - glyph_padding = 2, + glyph_padding = 1, region_a = { width = 32, @@ -151,7 +152,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 = 6, + curve_quality : u32 = 3, entires_reserve : u32 = 512, temp_path_reserve : u32 = 512, temp_codepoint_seen_reserve : u32 = 512, @@ -164,7 +165,7 @@ startup :: proc( ctx : ^Context, parser_kind : ParserKind, context.allocator = ctx.backing if curve_quality == 0 { - curve_quality = 6 + curve_quality = 3 } ctx.curve_quality = curve_quality @@ -242,11 +243,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 atlas + using glyph_buffer over_sample = glyph_draw_params.over_sample - buffer_batch = glyph_draw_params.buffer_batch - buffer_width = region_d.width * u32(over_sample.x) * buffer_batch - buffer_height = region_d.height * u32(over_sample.y) + 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 draw_list.calls, error = make( [dynamic]DrawCall, cast(u64) glyph_draw_params.buffer_batch * 2 ) @@ -301,13 +302,13 @@ hot_reload :: proc( ctx : ^Context, allocator : Allocator ) reload_array( & positions, allocator ) } - reload_array( & atlas.draw_list.calls, allocator ) - reload_array( & atlas.draw_list.indices, allocator ) - reload_array( & atlas.draw_list.vertices, allocator ) + reload_array( & glyph_buffer.draw_list.calls, allocator ) + reload_array( & glyph_buffer.draw_list.indices, allocator ) + reload_array( & glyph_buffer.draw_list.vertices, allocator ) - reload_array( & atlas.clear_draw_list.calls, allocator ) - reload_array( & atlas.clear_draw_list.indices, allocator ) - reload_array( & atlas.clear_draw_list.vertices, allocator ) + reload_array( & glyph_buffer.clear_draw_list.calls, allocator ) + reload_array( & glyph_buffer.clear_draw_list.indices, allocator ) + reload_array( & glyph_buffer.clear_draw_list.vertices, allocator ) reload_array( & shape_cache.storage, allocator ) LRU_reload( & shape_cache.state, allocator ) @@ -358,7 +359,7 @@ load_font :: proc( ctx : ^Context, label : string, data : []byte, size_px : f32 // assert( parser_info != nil, "VEFontCache.load_font: Failed to load font info from parser" ) size = size_px - size_scale = size_px < 0.0 ? \ + 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 ) // size_scale = 1.0 @@ -404,10 +405,6 @@ configure_snap :: #force_inline proc( ctx : ^Context, snap_width, snap_height : get_cursor_pos :: #force_inline proc "contextless" ( ctx : ^Context ) -> Vec2 { return ctx.cursor_pos } set_colour :: #force_inline proc "contextless" ( ctx : ^Context, colour : Colour ) { ctx.colour = colour } -// TODO(Ed): Change this to be whitespace aware so that we can optimize the caching of shpaes properly. -// Right now the entire text provided to this call is considered a "shape" this is really bad as basically it invalidates caching for large chunks of text -// Instead we should be aware of whitespace tokens and the chunks between them (the whitespace lexer could be abused for this). -// From there we should maek a 'draw text shape' that breaks up the batch text draws for each of the shapes. draw_text :: proc( ctx : ^Context, font : FontID, text_utf8 : string, position : Vec2, scale : Vec2 ) -> b32 { // profile(#procedure) @@ -424,8 +421,6 @@ draw_text :: proc( ctx : ^Context, font : FontID, text_utf8 : string, position : entry := & ctx.entries[ font ] - last_shaped : ^ShapedText - ChunkType :: enum u32 { Visible, Formatting } chunk_kind : ChunkType chunk_start : int = 0 @@ -434,91 +429,91 @@ draw_text :: proc( ctx : ^Context, font : FontID, text_utf8 : string, position : text_utf8_bytes := transmute([]u8) text_utf8 text_chunk : string - when true { - 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 ) - last_shaped = shaped - } - } - else { - last_byte_offset : int = 0 - byte_offset : int = 0 - for codepoint, offset in text_utf8 + Text_As_Shape :: true + when Text_As_Shape { - Rune_Space :: ' ' - Rune_Tab :: '\t' - Rune_Carriage_Return :: '\r' - Rune_Line_Feed :: '\n' - // Rune_Tab_Vertical :: '\v' - - byte_offset = offset - - switch codepoint + 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 ) + } + } + else + { + last_byte_offset : int = 0 + byte_offset : int = 0 + for codepoint, offset in text_utf8 { - case Rune_Space: fallthrough - case Rune_Tab: fallthrough - case Rune_Line_Feed: fallthrough - case Rune_Carriage_Return: - if chunk_kind == .Formatting { - chunk_end = byte_offset - last_byte_offset = byte_offset - } - else - { - text_chunk = transmute(string) text_utf8_bytes[ chunk_start : byte_offset] - 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 ) - last_shaped = shaped + Rune_Space :: ' ' + Rune_Tab :: '\t' + Rune_Carriage_Return :: '\r' + Rune_Line_Feed :: '\n' + // Rune_Tab_Vertical :: '\v' + + byte_offset = offset + + switch codepoint + { + case Rune_Space: fallthrough + case Rune_Tab: fallthrough + case Rune_Line_Feed: fallthrough + case Rune_Carriage_Return: + if chunk_kind == .Formatting + { + chunk_end = byte_offset + last_byte_offset = byte_offset } + else + { + text_chunk = transmute(string) text_utf8_bytes[ chunk_start : byte_offset] + 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 ) + } - chunk_start = byte_offset - chunk_end = chunk_start - chunk_kind = .Formatting + chunk_start = byte_offset + chunk_end = chunk_start + chunk_kind = .Formatting - last_byte_offset = byte_offset - continue - } - } - - // Visible Chunk - if chunk_kind == .Visible { - chunk_end = byte_offset - last_byte_offset = byte_offset - } - else - { - text_chunk = transmute(string) text_utf8_bytes[ chunk_start : byte_offset ] - 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 ) - last_shaped = shaped + last_byte_offset = byte_offset + continue + } } - chunk_start = byte_offset - chunk_end = chunk_start - chunk_kind = .Visible + // Visible Chunk + if chunk_kind == .Visible { + chunk_end = byte_offset + last_byte_offset = byte_offset + } + else + { + text_chunk = transmute(string) text_utf8_bytes[ chunk_start : byte_offset ] + 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 ) + } - last_byte_offset = byte_offset + chunk_start = byte_offset + chunk_end = chunk_start + chunk_kind = .Visible + + last_byte_offset = byte_offset + } } + + text_chunk = transmute(string) text_utf8_bytes[ chunk_start : ] + 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 ) + } + + chunk_start = byte_offset + chunk_end = chunk_start + chunk_kind = .Visible + + last_byte_offset = byte_offset } - text_chunk = transmute(string) text_utf8_bytes[ chunk_start : byte_offset ] - 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 ) - last_shaped = shaped - } - - chunk_start = byte_offset - chunk_end = chunk_start - chunk_kind = .Visible - - last_byte_offset = byte_offset - } - return true } @@ -526,7 +521,6 @@ draw_text :: proc( ctx : ^Context, font : FontID, text_utf8 : string, position : get_draw_list :: proc( ctx : ^Context, optimize_before_returning := true ) -> ^DrawList { assert( ctx != nil ) if optimize_before_returning do optimize_draw_list( & ctx.draw_list, 0 ) - return & ctx.draw_list } diff --git a/code/font/VEFontCache/atlas.odin b/code/font/VEFontCache/atlas.odin index 89abfa6..554f4cf 100644 --- a/code/font/VEFontCache/atlas.odin +++ b/code/font/VEFontCache/atlas.odin @@ -33,8 +33,6 @@ Atlas :: struct { region_b : AtlasRegion, region_c : AtlasRegion, region_d : AtlasRegion, - - using glyph_update_batch : GlyphDrawBuffer, } atlas_bbox :: proc( atlas : ^Atlas, region : AtlasRegionKind, local_idx : i32 ) -> (position : Vec2, width, height : f32) @@ -136,7 +134,8 @@ decide_codepoint_region :: #force_inline proc( ctx : ^Context, entry : ^Entry, g bounds_width := bounds_1.x - bounds_0.x bounds_height := bounds_1.y - bounds_0.y - atlas := & ctx.atlas + atlas := & ctx.atlas + glyph_buffer := & ctx.glyph_buffer bounds_width_scaled := cast(u32) (f32(bounds_width) * entry.size_scale + 2.0 * f32(atlas.glyph_padding)) bounds_height_scaled := cast(u32) (f32(bounds_height) * entry.size_scale + 2.0 * f32(atlas.glyph_padding)) @@ -165,12 +164,12 @@ decide_codepoint_region :: #force_inline proc( ctx : ^Context, entry : ^Entry, g region_kind = .D region = & atlas.region_d } - else if bounds_width_scaled <= atlas.buffer_width && bounds_height_scaled <= atlas.buffer_height + else if bounds_width_scaled <= glyph_buffer.width && bounds_height_scaled <= glyph_buffer.height { // Region 'E' for massive glyphs. These are rendered uncached and un-oversampled. region_kind = .E region = nil - if bounds_width_scaled <= atlas.buffer_width / 2 && bounds_height_scaled <= atlas.buffer_height / 2 { + if bounds_width_scaled <= glyph_buffer.width / 2 && bounds_height_scaled <= glyph_buffer.height / 2 { over_sample = { 2.0, 2.0 } } else { @@ -183,7 +182,7 @@ decide_codepoint_region :: #force_inline proc( ctx : ^Context, entry : ^Entry, g return } - over_sample = ctx.atlas.over_sample + over_sample = glyph_buffer.over_sample assert(region != nil) return } diff --git a/code/font/VEFontCache/draw.odin b/code/font/VEFontCache/draw.odin index 6305a48..8391bd7 100644 --- a/code/font/VEFontCache/draw.odin +++ b/code/font/VEFontCache/draw.odin @@ -1,7 +1,5 @@ package VEFontCache -import "core:math" - Vertex :: struct { pos : Vec2, u, v : f32, @@ -41,12 +39,12 @@ FrameBufferPass :: enum u32 { GlyphDrawBuffer :: struct { over_sample : Vec2, - buffer_batch : u32, - buffer_width : u32, - buffer_height : u32, + batch : u32, + width : u32, + height : u32, draw_padding : u32, - update_batch_x : i32, + batch_x : i32, clear_draw_list : DrawList, draw_list : DrawList, } @@ -94,7 +92,7 @@ blit_quad :: proc( draw_list : ^DrawList, p0 : Vec2 = {0, 0}, p1 : Vec2 = {1, 1} return } -cache_glyph :: proc( ctx : ^Context, font : FontID, glyph_index : Glyph, entry : ^Entry, bounds_0, bounds_1 : Vec2i, scale, translate : Vec2 ) -> b32 +cache_glyph :: proc( ctx : ^Context, font : FontID, glyph_index : Glyph, entry : ^Entry, bounds_0, bounds_1 : Vec2, scale, translate : Vec2 ) -> b32 { // profile(#procedure) if glyph_index == Glyph(0) { @@ -141,8 +139,8 @@ cache_glyph :: proc( ctx : ^Context, font : FontID, glyph_index : Glyph, entry : Note that this outside point is scaled alongside the glyph in ve_fontcache_draw_filled_path, so we don't need to handle that here. */ outside := Vec2 { - f32(bounds_0.x) - 21, - f32(bounds_0.y) - 33, + bounds_0.x - 21, + bounds_0.y - 33, } // Note(Original Author): Figure out scaling so it fits within our box. @@ -165,7 +163,7 @@ cache_glyph :: proc( ctx : ^Context, font : FontID, glyph_index : Glyph, entry : fallthrough case .Line: - append_elem( & path, Vec2{ f32(edge.x), f32(edge.y) }) + append( & path, Vec2{ f32(edge.x), f32(edge.y) }) case .Curve: assert( len(path) > 0 ) @@ -176,7 +174,7 @@ cache_glyph :: proc( ctx : ^Context, font : FontID, glyph_index : Glyph, entry : step := 1.0 / f32(ctx.curve_quality) alpha := step for index := i32(0); index < i32(ctx.curve_quality); index += 1 { - append_elem( & path, eval_point_on_bezier3( p0, p1, p2, alpha )) + append( & path, eval_point_on_bezier3( p0, p1, p2, alpha )) alpha += step } @@ -190,7 +188,7 @@ cache_glyph :: proc( ctx : ^Context, font : FontID, glyph_index : Glyph, entry : step := 1.0 / f32(ctx.curve_quality) alpha := step for index := i32(0); index < i32(ctx.curve_quality); index += 1 { - append_elem( & path, eval_point_on_bezier4( p0, p1, p2, p3, alpha )) + append( & path, eval_point_on_bezier4( p0, p1, p2, p3, alpha )) alpha += step } @@ -214,9 +212,17 @@ cache_glyph :: proc( ctx : ^Context, font : FontID, glyph_index : Glyph, entry : /* Called by: * 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 + * draw_text_shape : Glyph */ -cache_glyph_to_atlas :: proc( ctx : ^Context, font : FontID, glyph_index : Glyph, lru_code : u64, atlas_index : i32, entry : ^Entry, region_kind : AtlasRegionKind, region : ^AtlasRegion, over_sample : Vec2 ) +cache_glyph_to_atlas :: proc( ctx : ^Context, + font : FontID, + glyph_index : Glyph, + lru_code : u64, + atlas_index : i32, + entry : ^Entry, + region_kind : AtlasRegionKind, + region : ^AtlasRegion, + over_sample : Vec2 ) { // profile(#procedure) @@ -257,26 +263,28 @@ cache_glyph_to_atlas :: proc( ctx : ^Context, font : FontID, glyph_index : Glyph atlas := & ctx.atlas atlas_width := f32(atlas.width) atlas_height := f32(atlas.height) - glyph_buffer_width := f32(atlas.buffer_width) - glyph_buffer_height := f32(atlas.buffer_height) + glyph_buffer := & ctx.glyph_buffer + glyph_buffer_width := f32(glyph_buffer.width) + glyph_buffer_height := f32(glyph_buffer.height) glyph_padding := cast(f32) atlas.glyph_padding if ctx.debug_print { @static debug_total_cached : i32 = 0 - logf("glyph %v%v( %v ) caching to atlas region %v at idx %d. %d total glyphs cached.\n", i32(glyph_index), rune(glyph_index), cast(rune) region_kind, atlas_index, debug_total_cached) + logf("glyph %v%v( %v ) caching to atlas region %v at idx %d. %d total glyphs cached.\n", + i32(glyph_index), rune(glyph_index), cast(rune) region_kind, atlas_index, debug_total_cached) debug_total_cached += 1 } // Draw oversized glyph to update FBO glyph_draw_scale := over_sample * entry.size_scale - glyph_draw_translate := -1 * Vec2 { f32(bounds_0.x), f32(bounds_0.y) } * glyph_draw_scale + vec2( glyph_padding ) + glyph_draw_translate := -1 * vec2(bounds_0) * glyph_draw_scale + vec2( glyph_padding ) glyph_draw_translate.x = cast(f32) (i32(glyph_draw_translate.x + 0.9999999)) glyph_draw_translate.y = cast(f32) (i32(glyph_draw_translate.y + 0.9999999)) // Allocate a glyph_update_FBO region gwidth_scaled_px := i32( bounds_width * glyph_draw_scale.x + 1.0 ) + i32(over_sample.x * glyph_padding) - if i32(atlas.update_batch_x + gwidth_scaled_px) >= i32(atlas.buffer_width) { + if i32(glyph_buffer.batch_x + gwidth_scaled_px) >= i32(glyph_buffer.width) { flush_glyph_buffer_to_atlas( ctx ) } @@ -293,7 +301,7 @@ cache_glyph_to_atlas :: proc( ctx : ^Context, font : FontID, glyph_index : Glyph screenspace_x_form( & dst_glyph_position, & dst_glyph_size, atlas_width, atlas_height ) screenspace_x_form( & dst_position, & dst_size, atlas_width, atlas_height ) - src_position := Vec2 { f32(atlas.update_batch_x), 0 } + src_position := Vec2 { f32(glyph_buffer.batch_x), 0 } src_size := Vec2 { bounds_width * glyph_draw_scale.x, bounds_height * glyph_draw_scale.y, @@ -302,8 +310,8 @@ cache_glyph_to_atlas :: proc( ctx : ^Context, font : FontID, glyph_index : Glyph textspace_x_form( & src_position, & src_size, glyph_buffer_width, glyph_buffer_height ) // Advance glyph_update_batch_x and calculate final glyph drawing transform - glyph_draw_translate.x += f32(atlas.update_batch_x) - atlas.update_batch_x += gwidth_scaled_px + glyph_draw_translate.x += f32(glyph_buffer.batch_x) + glyph_buffer.batch_x += gwidth_scaled_px screenspace_x_form( & glyph_draw_translate, & glyph_draw_scale, glyph_buffer_width, glyph_buffer_height ) call : DrawCall @@ -312,21 +320,21 @@ cache_glyph_to_atlas :: proc( ctx : ^Context, font : FontID, glyph_index : Glyph using call pass = .Atlas region = .Ignore - start_index = cast(u32) len(atlas.clear_draw_list.indices) - blit_quad( & atlas.clear_draw_list, dst_position, dst_position + dst_size, { 1.0, 1.0 }, { 1.0, 1.0 } ) - end_index = cast(u32) len(atlas.clear_draw_list.indices) - append( & atlas.clear_draw_list.calls, call ) + start_index = cast(u32) len(glyph_buffer.clear_draw_list.indices) + blit_quad( & glyph_buffer.clear_draw_list, dst_position, dst_position + dst_size, { 1.0, 1.0 }, { 1.0, 1.0 } ) + end_index = cast(u32) len(glyph_buffer.clear_draw_list.indices) + append( & glyph_buffer.clear_draw_list.calls, call ) // Queue up a blit from glyph_update_FBO to the atlas region = .None - start_index = cast(u32) len(atlas.draw_list.indices) - blit_quad( & atlas.draw_list, dst_glyph_position, dst_position + dst_glyph_size, src_position, src_position + src_size ) - end_index = cast(u32) len(atlas.draw_list.indices) - append( & atlas.draw_list.calls, call ) + start_index = cast(u32) len(glyph_buffer.draw_list.indices) + blit_quad( & glyph_buffer.draw_list, dst_glyph_position, dst_position + dst_glyph_size, src_position, src_position + src_size ) + end_index = cast(u32) len(glyph_buffer.draw_list.indices) + append( & glyph_buffer.draw_list.calls, call ) } // Render glyph to glyph_update_FBO - cache_glyph( ctx, font, glyph_index, entry, bounds_0, bounds_1, glyph_draw_scale, glyph_draw_translate ) + cache_glyph( ctx, font, glyph_index, entry, vec2(bounds_0), vec2(bounds_1), glyph_draw_scale, glyph_draw_translate ) } // ve_fontcache_clear_drawlist @@ -336,18 +344,23 @@ clear_draw_list :: #force_inline proc ( draw_list : ^DrawList ) { clear( & draw_list.vertices ) } -directly_draw_massive_glyph :: proc( ctx : ^Context, entry : ^Entry, glyph : Glyph, bounds_0, bounds_1 : Vec2i, bounds_width, bounds_height : f32, over_sample, position, scale : Vec2 ) +directly_draw_massive_glyph :: proc( ctx : ^Context, + entry : ^Entry, + glyph : Glyph, + bounds_0, bounds_1 : Vec2, + bounds_width, bounds_height : f32, + over_sample, position, scale : Vec2 ) { // profile(#procedure) flush_glyph_buffer_to_atlas( ctx ) glyph_padding := f32(ctx.atlas.glyph_padding) - glyph_buffer_width := f32(ctx.atlas.buffer_width) - glyph_buffer_height := f32(ctx.atlas.buffer_height) + glyph_buffer_width := f32(ctx.glyph_buffer.width) + glyph_buffer_height := f32(ctx.glyph_buffer.height) // Draw un-antialiased glyph to update FBO. glyph_draw_scale := over_sample * entry.size_scale - glyph_draw_translate := -1 * Vec2{ f32(bounds_0.x), f32(bounds_0.y) } * glyph_draw_scale + vec2_from_scalar(glyph_padding) + glyph_draw_translate := -1 * bounds_0 * glyph_draw_scale + vec2_from_scalar(glyph_padding) screenspace_x_form( & glyph_draw_translate, & glyph_draw_scale, glyph_buffer_width, glyph_buffer_height ) cache_glyph( ctx, entry.id, glyph, entry, bounds_0, bounds_1, glyph_draw_scale, glyph_draw_translate ) @@ -368,14 +381,14 @@ directly_draw_massive_glyph :: proc( ctx : ^Context, entry : ^Entry, glyph : Gly // Figure out the destination rect. bounds_0_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), + cast(f32) i32(bounds_0.x * entry.size_scale - 0.5), + cast(f32) i32(bounds_0.y * entry.size_scale - 0.5), } dst := position + scale * bounds_0_scaled dst_width := scale.x * glyph_dst_width dst_height := scale.y * glyph_dst_height - dst.x -= scale.x * f32(ctx.atlas.draw_padding) - dst.y -= scale.y * f32(ctx.atlas.draw_padding) + dst.x -= scale.x * glyph_padding + dst.y -= scale.y * glyph_padding dst_size := Vec2{ dst_width, dst_height } glyph_size := Vec2 { glyph_width, glyph_height } @@ -401,11 +414,16 @@ directly_draw_massive_glyph :: proc( ctx : ^Context, entry : ^Entry, glyph : Gly append( & ctx.draw_list.calls, call ) } -draw_cached_glyph :: proc( ctx : ^Context, entry : ^Entry, glyph_index : Glyph, - lru_code : u64, atlas_index : i32, - bounds_0, bounds_1 : Vec2i, - region_kind : AtlasRegionKind, region : ^AtlasRegion, over_sample : Vec2, - position, scale : Vec2 +draw_cached_glyph :: proc( ctx : ^Context, + 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) @@ -440,10 +458,8 @@ draw_cached_glyph :: proc( ctx : ^Context, entry : ^Entry, glyph_index : Glyph, glyph_scale := Vec2 { glyph_width, glyph_height } bounds_0_scaled := Vec2{ f32(bounds_0.x), f32(bounds_0.y) } * entry.size_scale //- { 0.5, 0.5 } - bounds_0_scaled = { - math.ceil(bounds_0_scaled.x), - math.ceil(bounds_0_scaled.y), - } + bounds_0_scaled = ceil(bounds_0_scaled) + dst := position + bounds_0_scaled * scale dst -= scale * glyph_padding dst_width := scale.x * glyph_width @@ -516,12 +532,14 @@ draw_filled_path :: proc( draw_list : ^DrawList, outside_point : Vec2, path : [] } } -draw_text_batch :: proc( ctx : ^Context, entry : ^Entry, shaped : ^ShapedText, batch_start_idx, batch_end_idx : i32, position, scale : Vec2, snap_width, snap_height : f32 ) +draw_text_batch :: proc( ctx : ^Context, entry : ^Entry, shaped : ^ShapedText, + batch_start_idx, batch_end_idx : i32, + position, scale : Vec2, + snap_width, snap_height : f32 ) { - // flush_glyph_buffer_to_atlas( ctx ) + flush_glyph_buffer_to_atlas( ctx ) for index := batch_start_idx; index < batch_end_idx; index += 1 { - // profile(#procedure) glyph_index := shaped.glyphs[ index ] if glyph_index == 0 do continue @@ -537,10 +555,10 @@ draw_text_batch :: proc( ctx : ^Context, entry : ^Entry, shaped : ^ShapedText, b shaped_position := shaped.positions[index] glyph_translate := position + shaped_position * scale - glyph_cached := draw_cached_glyph( ctx, - entry, glyph_index, - lru_code, atlas_index, - bounds_0, bounds_1, + glyph_cached := draw_cached_glyph( ctx, + entry, glyph_index, + lru_code, atlas_index, + vec2(bounds_0), vec2(bounds_1), region_kind, region, over_sample, glyph_translate, scale) assert( glyph_cached == true ) @@ -548,7 +566,13 @@ draw_text_batch :: proc( ctx : ^Context, entry : ^Entry, shaped : ^ShapedText, b } // Helper for draw_text, all raw text content should be confirmed to be either formatting or visible shapes before getting cached. -draw_text_shape :: proc( ctx : ^Context, font : FontID, entry : ^Entry, shaped : ^ShapedText, position, scale : Vec2, snap_width, snap_height : f32 ) -> (cursor_pos : Vec2) +draw_text_shape :: proc( ctx : ^Context, + font : FontID, + entry : ^Entry, + shaped : ^ShapedText, + position, scale : Vec2, + snap_width, snap_height : f32 +) -> (cursor_pos : Vec2) { // position := position //+ ctx.cursor_pos * scale // profile(#procedure) @@ -568,7 +592,7 @@ draw_text_shape :: proc( ctx : ^Context, font : FontID, entry : ^Entry, shaped : // Glyph has not been catched, needs to be directly drawn. // First batch the other cached glyphs - flush_glyph_buffer_to_atlas(ctx) + // 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 ) @@ -577,7 +601,7 @@ draw_text_shape :: proc( ctx : ^Context, font : FontID, entry : ^Entry, shaped : batch_start_idx = index } - flush_glyph_buffer_to_atlas(ctx) + // flush_glyph_buffer_to_atlas(ctx) draw_text_batch( ctx, entry, shaped, batch_start_idx, i32(len(shaped.glyphs)), position, scale, snap_width , snap_height ) reset_batch_codepoint_state( ctx ) cursor_pos = shaped.end_cursor_pos @@ -588,22 +612,21 @@ flush_glyph_buffer_to_atlas :: proc( ctx : ^Context ) { // profile(#procedure) // Flush drawcalls to draw list - merge_draw_list( & ctx.draw_list, & ctx.atlas.clear_draw_list ) - merge_draw_list( & ctx.draw_list, & ctx.atlas.draw_list) - clear_draw_list( & ctx.atlas.draw_list ) - clear_draw_list( & ctx.atlas.clear_draw_list ) + merge_draw_list( & ctx.draw_list, & ctx.glyph_buffer.clear_draw_list ) + merge_draw_list( & ctx.draw_list, & ctx.glyph_buffer.draw_list) + clear_draw_list( & ctx.glyph_buffer.draw_list ) + clear_draw_list( & ctx.glyph_buffer.clear_draw_list ) // Clear glyph_update_FBO - if ctx.atlas.update_batch_x != 0 + if ctx.glyph_buffer.batch_x != 0 { call := DrawCall_Default - call.pass = .Glyph - call.start_index = 0 - call.end_index = 0 + call.pass = .Glyph + call.start_index = 0 + call.end_index = 0 call.clear_before_draw = true - append( & ctx.draw_list.calls, call ) - ctx.atlas.update_batch_x = 0 + ctx.glyph_buffer.batch_x = 0 } } @@ -614,23 +637,19 @@ merge_draw_list :: proc( dst, src : ^DrawList ) error : AllocatorError v_offset := cast(u32) len( dst.vertices ) - // for index : u32 = 0; index < cast(u32) src.vertices.num; index += 1 { - // error = append( & dst.vertices, src.vertices.data[index] ) - // assert( error == .None ) - // } num_appended : int - num_appended, error = append_elems( & dst.vertices, ..src.vertices[:] ) + num_appended, error = append( & dst.vertices, ..src.vertices[:] ) assert( error == .None ) i_offset := cast(u32) len(dst.indices) - for index : u32 = 0; index < cast(u32) len(src.indices); index += 1 { + for index : int = 0; index < len(src.indices); index += 1 { ignored : int ignored, error = append( & dst.indices, src.indices[index] + v_offset ) assert( error == .None ) } - for index : u32 = 0; index < cast(u32) len(src.calls); index += 1 { - src_call := src.calls[ index ] + for index : int = 0; index < len(src.calls); index += 1 { + src_call := src.calls[ index ] src_call.start_index += i_offset src_call.end_index += i_offset append( & dst.calls, src_call ) diff --git a/code/font/VEFontCache/mappings.odin b/code/font/VEFontCache/mappings.odin index 3369a43..9d1afff 100644 --- a/code/font/VEFontCache/mappings.odin +++ b/code/font/VEFontCache/mappings.odin @@ -1,36 +1,36 @@ package VEFontCache import "core:hash" - crc64 :: hash.crc64_xz - crc32 :: hash.crc32 fnv64a :: hash.fnv64a +import "core:math" + ceil_f16 :: math.ceil_f16 + ceil_f16le :: math.ceil_f16le + ceil_f16be :: math.ceil_f16be + ceil_f32 :: math.ceil_f32 + ceil_f32le :: math.ceil_f32le + ceil_f32be :: math.ceil_f32be + ceil_f64 :: math.ceil_f64 + ceil_f64le :: math.ceil_f64le + ceil_f64be :: math.ceil_f64be +import "core:math/linalg" import "core:mem" + Kilobyte :: mem.Kilobyte + slice_ptr :: mem.slice_ptr -Kilobyte :: mem.Kilobyte - -slice_ptr :: mem.slice_ptr - -Arena :: mem.Arena - -arena_allocator :: mem.arena_allocator -arena_init :: mem.arena_init - -Allocator :: mem.Allocator -AllocatorError :: mem.Allocator_Error + Allocator :: mem.Allocator + AllocatorError :: mem.Allocator_Error + Arena :: mem.Arena + arena_allocator :: mem.arena_allocator + arena_init :: mem.arena_init import "codebase:grime" - -hmap_closest_prime :: grime.hmap_closest_prime - -// logging -log :: grime.log -logf :: grime.logf - -profile :: grime.profile - -reload_array :: grime.reload_array -reload_map :: grime.reload_map + hmap_closest_prime :: grime.hmap_closest_prime + log :: grime.log + logf :: grime.logf + profile :: grime.profile + reload_array :: grime.reload_array + reload_map :: grime.reload_map //#region("Proc overload mappings") @@ -40,6 +40,20 @@ append :: proc { append_elem_string, } +ceil :: proc { + math.ceil_f16, + math.ceil_f16le, + math.ceil_f16be, + math.ceil_f32, + math.ceil_f32le, + math.ceil_f32be, + math.ceil_f64, + math.ceil_f64le, + math.ceil_f64be, + + grime.ceil_vec2, +} + clear :: proc { clear_dynamic_array, } @@ -51,13 +65,17 @@ make :: proc { make_map, } - resize :: proc { resize_dynamic_array, } vec2 :: proc { vec2_from_scalar, + vec2_from_vec2i, +} + +vec2_64 :: proc { + vec2_64_from_vec2, } //#endregion("Proc overload mappings") diff --git a/code/font/VEFontCache/shaper.odin b/code/font/VEFontCache/shaper.odin index e4232be..d06941d 100644 --- a/code/font/VEFontCache/shaper.odin +++ b/code/font/VEFontCache/shaper.odin @@ -4,7 +4,6 @@ Note(Ed): The only reason I didn't directly use harfbuzz is because hamza exists */ import "core:c" -import "core:math" import "thirdparty:harfbuzz" ShaperKind :: enum { @@ -112,9 +111,9 @@ shaper_shape_from_text :: proc( ctx : ^ShaperContext, info : ^ShaperInfo, output (vertical_position^) = cast(f32) i32(vertical_position^ + 0.5) continue } - if math.abs( size ) <= Advance_Snap_Smallfont_Size + if abs( size ) <= Advance_Snap_Smallfont_Size { - (position^) = math.ceil( position^ ) + (position^) = ceil( position^ ) } append( & output.glyphs, glyph_id )