From c4c3bba20caa6e1f64ccdafef55ff693d2b05b89 Mon Sep 17 00:00:00 2001 From: Ed_ Date: Fri, 3 Jan 2025 13:06:43 -0500 Subject: [PATCH] VEFontchange: codepath changes and bugfixing --- code/font/vefontcache/atlas.odin | 18 +- code/font/vefontcache/draw.odin | 404 ++++++++++++++----------- code/font/vefontcache/mappings.odin | 38 ++- code/font/vefontcache/misc.odin | 6 +- code/font/vefontcache/parser.odin | 308 +++++++++---------- code/font/vefontcache/shaped_text.odin | 5 +- code/font/vefontcache/vefontcache.odin | 20 +- 7 files changed, 421 insertions(+), 378 deletions(-) diff --git a/code/font/vefontcache/atlas.odin b/code/font/vefontcache/atlas.odin index d8c3067..22aeddc 100644 --- a/code/font/vefontcache/atlas.odin +++ b/code/font/vefontcache/atlas.odin @@ -13,29 +13,29 @@ Atlas_Region_Kind :: enum u8 { Atlas_Region :: struct { state : LRU_Cache, - width : i32, - height : i32, - size : Vec2i, capacity : Vec2i, offset : Vec2i, + width : i32, + height : i32, + next_idx : i32, } Atlas :: struct { - width : i32, - height : i32, - - glyph_padding : f32, // Padding to add to bounds__scaled for choosing which atlas region. - glyph_over_scalar : f32, // Scalar to apply to bounds__scaled for choosing which atlas region. - region_a : Atlas_Region, region_b : Atlas_Region, region_c : Atlas_Region, region_d : Atlas_Region, regions : [5] ^Atlas_Region, + + glyph_padding : f32, // Padding to add to bounds__scaled for choosing which atlas region. + glyph_over_scalar : f32, // Scalar to apply to bounds__scaled for choosing which atlas region. + + width : i32, + height : i32, } atlas_region_bbox :: proc( region : Atlas_Region, local_idx : i32 ) -> (position, size: Vec2) diff --git a/code/font/vefontcache/draw.odin b/code/font/vefontcache/draw.odin index 843ee7a..36f5230 100644 --- a/code/font/vefontcache/draw.odin +++ b/code/font/vefontcache/draw.odin @@ -28,7 +28,7 @@ Glyph_Draw_Quad :: struct { src_scale : Vec2, } -Glyph_Pack_Entry :: struct { +Glyph_Pack_Entry :: struct #packed { position : Vec2, index : Glyph, @@ -53,6 +53,7 @@ Glyph_Pack_Entry :: struct { draw_quad : Glyph_Draw_Quad, draw_atlas_quad : Glyph_Draw_Quad, + draw_quad_clear : Glyph_Draw_Quad, // shape_id : i32, } @@ -177,26 +178,21 @@ construct_filled_path :: #force_inline proc( draw_list : ^Draw_List, outside_poi } } -generate_glyph_pass_draw_list :: #force_inline proc(ctx : ^Context, - // glyph_id : Glyph, - // parser_info : Parser_Font_Info, - glyph_shape : Parser_Glyph_Shape, - curve_quality : f32, - bounds : Range2, +generate_glyph_pass_draw_list :: proc(draw_list : ^Draw_List, path : ^[dynamic]Vertex, + glyph_shape : Parser_Glyph_Shape, + curve_quality : f32, + bounds : Range2, scale, translate : Vec2 -) -> b32 +) { profile(#procedure) - // glyph_shape, error := parser_get_glyph_shape( parser_info, glyph_id ) - outside := Vec2{bounds.p0.x - 21, bounds.p0.y - 33} draw := Draw_Call_Default draw.pass = Frame_Buffer_Pass.Glyph - draw.start_index = u32(len(ctx.draw_list.indices)) + draw.start_index = u32(len(draw_list.indices)) - path := &ctx.temp_path clear(path) step := 1.0 / curve_quality @@ -204,7 +200,7 @@ generate_glyph_pass_draw_list :: #force_inline proc(ctx : ^Context, { case .Move: if len(path) > 0 { - construct_filled_path(&ctx.draw_list, outside, path[:], scale, translate) + construct_filled_path( draw_list, outside, path[:], scale, translate) clear(path) } fallthrough @@ -235,63 +231,50 @@ generate_glyph_pass_draw_list :: #force_inline proc(ctx : ^Context, append( path, Vertex { pos = eval_point_on_bezier4(p0, p1, p2, p3, alpha) } ) } - case: + // case: // assert(false, "WTF") } if len(path) > 0 { - construct_filled_path(&ctx.draw_list, outside, path[:], scale, translate) + construct_filled_path(draw_list, outside, path[:], scale, translate) } - draw.end_index = u32(len(ctx.draw_list.indices)) + draw.end_index = u32(len(draw_list.indices)) if draw.end_index > draw.start_index { - append( & ctx.draw_list.calls, draw) + append( & draw_list.calls, draw) } - - // parser_free_shape(parser_info, glyph_shape) - return true } -cache_glyph_to_atlas :: #force_no_inline proc ( ctx : ^Context, +cache_glyph_to_atlas :: #force_no_inline proc ( #no_alias draw_list, - glyph_buf_draw_list, glyph_buf_clear_list : ^Draw_List, - glyph_buf_Batch_x : ^i32, + glyph_buf_draw_list, + glyph_buf_clear_list : ^Draw_List, + glyph_buf_Batch_x : ^i32, + + temp_path : ^[dynamic]Vertex, + glyph_shape : Parser_Glyph_Shape, + bounds : Range2, + bounds_size_scaled : Vec2, + atlas_size : Vec2, - glyph_padding : f32, glyph_buffer_size : Vec2, + over_sample : Vec2, + glyph_padding : f32, + buf_transform : Transform, - atlas_size : Vec2, - - buf_transform : Transform, - - glyph_shape : Parser_Glyph_Shape, - - bounds : Range2, // -> generate_glyph_pass_draw_list - bounds_size : Vec2, - - - region_pos : Vec2, - region_size : Vec2, - lru_code : u64, - atlas_index : i32, - entry : Entry, - // region_kind : Atlas_Region_Kind, - // region : ^Atlas_Region, - over_sample : Vec2, - - - // glyph_id : Glyph, - // parser_info : Parser_Font_Info, + region_pos : Vec2, + region_size : Vec2, + curve_quality : f32, ) { profile(#procedure) - batch_x := cast(f32) glyph_buf_Batch_x ^ - - glyph_buffer_pad := over_sample.x * glyph_padding + batch_x := cast(f32) glyph_buf_Batch_x ^ + buffer_padding_scaled := glyph_padding * over_sample + buffer_bounds_scale := bounds_size_scaled * over_sample // Allocate a glyph glyph render target region (FBO) - buffer_x_allocation := bounds_size.x * buf_transform.scale.x + glyph_buffer_pad + 2.0 + buffer_x_allocation := buffer_bounds_scale.x + buffer_padding_scaled.x + 2.0 // If we exceed the region availbe to draw on the buffer, flush the calls to reset the buffer if i32(batch_x + buffer_x_allocation) >= i32(glyph_buffer_size.x) { @@ -299,16 +282,15 @@ cache_glyph_to_atlas :: #force_no_inline proc ( ctx : ^Context, batch_x = cast(f32) glyph_buf_Batch_x ^ } - region_pos := region_pos - + region_pos := region_pos dst_glyph_position := region_pos - dst_glyph_size := ceil(bounds_size * entry.size_scale) + glyph_padding - dst_size := (region_size) + dst_glyph_size := bounds_size_scaled + glyph_padding + dst_size := region_size to_screen_space( & dst_glyph_position, & dst_glyph_size, atlas_size ) to_screen_space( & region_pos, & dst_size, atlas_size ) src_position := Vec2 { batch_x, 0 } - src_size := (bounds_size * buf_transform.scale + over_sample * glyph_padding) + src_size := (buffer_bounds_scale + buffer_padding_scaled) to_text_space( & src_position, & src_size, glyph_buffer_size ) clear_target_region : Draw_Call @@ -342,25 +324,18 @@ cache_glyph_to_atlas :: #force_no_inline proc ( ctx : ^Context, append( & glyph_buf_clear_list.calls, clear_target_region ) append( & glyph_buf_draw_list.calls, blit_to_atlas ) - - - screen_space_translate := buf_transform.pos - screen_space_scale := buf_transform.scale - - screen_space_translate.x = (buf_transform.pos.x + batch_x) - glyph_buf_Batch_x^ += i32(buffer_x_allocation) - - to_screen_space( & screen_space_translate, & screen_space_scale, glyph_buffer_size ) + // The glyph buffer space transform for generate_glyph_pass_draw_list + glyph_transform := buf_transform + glyph_transform.pos.x += batch_x + (glyph_buf_Batch_x ^) += i32(buffer_x_allocation) + to_screen_space( & glyph_transform.pos, & glyph_transform.scale, glyph_buffer_size ) // Render glyph to glyph render target (FBO) - generate_glyph_pass_draw_list( ctx, - // glyph_id, - // parser_info, - glyph_shape, - entry.curve_quality, bounds, screen_space_scale, screen_space_translate ) + generate_glyph_pass_draw_list( draw_list, temp_path, glyph_shape, curve_quality, bounds, glyph_transform.scale, glyph_transform.pos ) } -generate_oversized_draw_list :: #force_no_inline proc( ctx : ^Context, +generate_oversized_draw_list :: #force_no_inline proc( draw_list : ^Draw_List, temp_path : ^[dynamic]Vertex, + colour : Colour, curve_quality : f32, glyph_shape : Parser_Glyph_Shape, bounds : Range2, @@ -370,27 +345,19 @@ generate_oversized_draw_list :: #force_no_inline proc( ctx : ^Context, { profile(#procedure) - generate_glyph_pass_draw_list( ctx, - glyph_shape, - curve_quality, - bounds, - glyph_pass_transform.scale, - glyph_pass_transform.pos - ) - calls : [2]Draw_Call draw_to_target := & calls[0] { using draw_to_target pass = .Target_Uncached - colour = ctx.colour - start_index = u32(len(ctx.draw_list.indices)) + colour = colour + start_index = u32(len(draw_list.indices)) - blit_quad( & ctx.draw_list, + blit_quad( draw_list, target_quad.dst_pos, target_quad.dst_pos + target_quad.dst_scale, target_quad.src_pos, target_quad.src_pos + target_quad.src_scale ) - end_index = u32(len(ctx.draw_list.indices)) + end_index = u32(len(draw_list.indices)) } clear_glyph_update := & calls[1] { @@ -400,7 +367,15 @@ generate_oversized_draw_list :: #force_no_inline proc( ctx : ^Context, clear_glyph_update.end_index = 0 clear_glyph_update.clear_before_draw = true } - append( & ctx.draw_list.calls, ..calls[:] ) + append( & draw_list.calls, ..calls[:] ) + + generate_glyph_pass_draw_list( draw_list, temp_path, + glyph_shape, + curve_quality, + bounds, + glyph_pass_transform.scale, + glyph_pass_transform.pos + ) } generate_shape_draw_list :: #force_no_inline proc( ctx : ^Context, @@ -481,48 +456,57 @@ generate_shape_draw_list :: #force_no_inline proc( ctx : ^Context, for & glyph, index in glyph_pack { glyph.region_kind = atlas_decide_region( atlas ^, glyph_buffer_size, glyph.bounds_size_scaled ) - glyph.over_sample = glyph_buffer.over_sample + // glyph.over_sample = glyph_buffer.over_sample } profile_end() profile_begin("atlas slot resolution & to_cache/cached segregation") for & glyph, index in glyph_pack { + if glyph.region_kind == .None { + assert(false, "FAILED TO ASSGIN REGION") + continue + } if glyph.region_kind == .E { glyph.over_sample = \ - glyph.bounds_size_scaled.x <= glyph_buffer_size.x / 2 && - glyph.bounds_size_scaled.y <= glyph_buffer_size.y / 2 ? \ - {2.0, 2.0} \ - : {1.0, 1.0} + glyph.bounds_size_scaled.x <= glyph_buffer_size.x / 2 && + glyph.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[glyph.region_kind] glyph.atlas_index = lru_get( & region.state, glyph.lru_code ) - if ctx.temp_codepoint_seen_num <= i32(cap(ctx.temp_codepoint_seen)) + to_cache_check: { - if glyph.atlas_index == - 1 + if ctx.temp_codepoint_seen_num <= i32(cap(ctx.temp_codepoint_seen)) { - // Check to see if we reached capacity for the atlas - if region.next_idx > region.state.capacity + if glyph.atlas_index == - 1 { - // 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 ) - found, success := ctx.temp_codepoint_seen[next_evict_codepoint] - assert(success != false) - if (found) { - continue + // Check to see if we reached capacity for the atlas + if region.next_idx > region.state.capacity + { + // We will evict LRU. We must predict which LRU will get evicted, and if it's something we've seen then we need to take slowpath and flush batch. + next_evict_codepoint := lru_get_next_evicted( region.state ) + found, success := ctx.temp_codepoint_seen[next_evict_codepoint] + assert(success != false) + if (found) { + break to_cache_check + } } - } - profile("append to_cache") - glyph.atlas_index = atlas_reserve_slot(region, glyph.lru_code) - glyph.region_pos, glyph.region_size = atlas_region_bbox(region ^, glyph.atlas_index) - append_sub_pack(to_cache, cast(i32) index) - continue + profile("append to_cache") + glyph.atlas_index = atlas_reserve_slot(region, glyph.lru_code) + glyph.region_pos, glyph.region_size = atlas_region_bbox(region ^, glyph.atlas_index) + append_sub_pack(to_cache, cast(i32) index) + mark_batch_codepoint_seen(ctx, glyph.lru_code) + continue + } } } @@ -533,18 +517,8 @@ generate_shape_draw_list :: #force_no_inline proc( ctx : ^Context, } profile_end() - profile_begin("font parser shape generation") - for id, index in sub_slice(to_cache) { - error : Allocator_Error - glyph_pack[id].shape, error = parser_get_glyph_shape(entry.parser_info, glyph_pack[id].index) - assert(error == .None) - } - for id, index in sub_slice(oversized) { - error : Allocator_Error - glyph_pack[id].shape, error = parser_get_glyph_shape(entry.parser_info, glyph_pack[id].index) - assert(error == .None) - } - profile_end() + // ctx.colour = {} + ctx.colour.a *= 1.0 + ctx.alpha_scalar profile_begin("transform & quad compute") for id, index in sub_slice(cached) @@ -561,6 +535,8 @@ generate_shape_draw_list :: #force_no_inline proc( ctx : ^Context, for id, index in sub_slice(to_cache) { glyph := & glyph_pack[id] + + // Quad to for drawing atlas slot to target quad := & glyph.draw_quad quad.dst_pos = glyph.position + glyph.bounds_scaled.p0 * target_scale quad.dst_scale = glyph.scale * target_scale @@ -568,71 +544,41 @@ generate_shape_draw_list :: #force_no_inline proc( ctx : ^Context, quad.src_pos = glyph.region_pos to_text_space( & quad.src_pos, & quad.src_scale, atlas_size ) + // The glyph buffer space transform for generate_glyph_pass_draw_list transform := & glyph.draw_transform transform.scale = entry.size_scale * glyph_buffer.over_sample - transform.pos = -1 * glyph_pack[id].bounds.p0 * transform.scale + atlas.glyph_padding + transform.pos = -1 * glyph.bounds.p0 * transform.scale + atlas.glyph_padding + // Unlike with oversized, this cannot be finished here as its final value is dependent on glyph_buffer.batch_x allocation } for id, index in sub_slice(oversized) { - // The glyph buffer space transform for generate_glyph_pass_draw_list glyph := & glyph_pack[id] - transform := glyph.draw_transform - transform.scale = entry.size_scale * glyph_buffer.over_sample - transform.pos = -1 * glyph_pack[id].bounds.p0 * transform.scale + atlas.glyph_padding - to_screen_space( & transform.pos, & transform.scale, glyph_buffer_size ) - - glyph_padding := glyph_buffer.draw_padding - - // Quad to draw during target pass - quad := & glyph.draw_quad - quad.dst_pos = glyph.position + glyph.bounds_scaled.p0 * target_scale - glyph_padding * target_scale - quad.dst_scale = glyph.scale + glyph_padding * target_scale - quad.src_scale = glyph.bounds_size_scaled * glyph.over_sample - quad.src_pos = glyph.position - to_text_space( & quad.src_pos, & quad.src_scale, atlas_size ) - to_text_space( & quad.src_pos, & quad.src_scale, atlas_size ) - } - profile_end() - - profile_begin("to_cache: caching to atlas") - - for id, index in sub_slice(to_cache) - { - glyph := glyph_pack[id] - cache_glyph_to_atlas( ctx, - draw_list, - & glyph_buffer.draw_list, - & glyph_buffer.clear_draw_list, - & glyph_buffer.batch_x, - - atlas.glyph_padding, - glyph_buffer_size, - atlas_size, - - glyph.draw_transform, - - glyph.shape, - glyph.bounds, - glyph.bounds_size, - glyph.region_pos, - glyph.region_size, - glyph.lru_code, - glyph.atlas_index, - entry, - glyph.over_sample, - - // glyph.index, - // entry.parser_info, - ) - mark_batch_codepoint_seen(ctx, glyph.lru_code) - } - - reset_batch_codepoint_state( ctx ) - flush_glyph_buffer_draw_list(draw_list, & glyph_buffer.draw_list, & glyph_buffer.clear_draw_list, & glyph_buffer.batch_x) - - profile_end() - + // The glyph buffer space transform for generate_glyph_pass_draw_list + transform := glyph.draw_transform + transform.scale = entry.size_scale * glyph.over_sample + transform.pos = -1 * glyph.bounds.p0 * transform.scale + vec2(atlas.glyph_padding) + to_screen_space( & transform.pos, & transform.scale, glyph_buffer_size ) + // Oversized will use a cleared glyph_buffer every time. + + glyph_padding := vec2(glyph_buffer.draw_padding) + + target_scale := target_scale + // target_scale = Vec2{ 1, 1} + + // Quad to draw during target pass, every + quad := & glyph.draw_quad + quad.dst_pos = glyph.position + glyph.bounds_scaled.p0 * target_scale - glyph_padding * target_scale + quad.dst_scale = (glyph.bounds_size_scaled + glyph_padding) * target_scale + quad.src_pos = {} + quad.src_scale = glyph.bounds_size_scaled * glyph.over_sample + glyph_padding + to_text_space( & quad.src_pos, & quad.src_scale, glyph_buffer_size ) + + dummy := 1 + dummy += 1 + } + profile_end() + generate_cached_draw_list :: #force_inline proc (draw_list : ^Draw_List, glyph_pack : #soa[]Glyph_Pack_Entry, sub_pack : []i32, colour : Colour ) { profile(#procedure) @@ -653,17 +599,115 @@ generate_shape_draw_list :: #force_no_inline proc( ctx : ^Context, append(& draw_list.calls, call) } } - generate_cached_draw_list( draw_list, glyph_pack[:], sub_slice(to_cache), ctx.colour ) - generate_cached_draw_list( draw_list, glyph_pack[:], sub_slice(cached), ctx.colour ) - reset_batch_codepoint_state( ctx ) + profile_begin("to_cache: caching to atlas") + + // profile_begin("font parser shape generation") + for id, index in sub_slice(to_cache) { + error : Allocator_Error + glyph_pack[id].shape, error = parser_get_glyph_shape(entry.parser_info, glyph_pack[id].index) + assert(error == .None) + } + // profile_end() + + for id, index in sub_slice(to_cache) + { + profile("glyph") + ctx.colour.r = 0.80 + ctx.colour.g = 0.25 + ctx.colour.b = 0.25 + + glyph := glyph_pack[id] + cache_glyph_to_atlas( + draw_list, + & glyph_buffer.draw_list, + & glyph_buffer.clear_draw_list, + & glyph_buffer.batch_x, + + & ctx.temp_path, + + glyph.shape, + glyph.bounds, + glyph.bounds_size_scaled, + atlas_size, + + glyph_buffer_size, + glyph.over_sample, + atlas.glyph_padding, + glyph.draw_transform, + + glyph.region_pos, + glyph.region_size, + entry.curve_quality, + ) + + // call := Draw_Call_Default + // call.pass = .Target + // call.colour = ctx.colour + + // profile("glyph") + // call.start_index = u32(len(draw_list.indices)) + + // quad := glyph_pack[id].draw_quad + // blit_quad(draw_list, + // quad.dst_pos, quad.dst_pos + quad.dst_scale, + // quad.src_pos, quad.src_pos + quad.src_scale + // ) + // call.end_index = u32(len(draw_list.indices)) + // append(& draw_list.calls, call) + } flush_glyph_buffer_draw_list(draw_list, & glyph_buffer.draw_list, & glyph_buffer.clear_draw_list, & glyph_buffer.batch_x) + for id, index in sub_slice(to_cache) do parser_free_shape(entry.parser_info, glyph_pack[id].shape) + profile_end() + + generate_cached_draw_list( draw_list, glyph_pack[:], sub_slice(to_cache), ctx.colour ) + + profile_begin("generate_cached_draw_list: to_cache") + ctx.colour.r = 1.0 + ctx.colour.g = 1.0 + ctx.colour.b = 1.0 + // flush_glyph_buffer_draw_list(draw_list, & glyph_buffer.draw_list, & glyph_buffer.clear_draw_list, & glyph_buffer.batch_x) + // generate_cached_draw_list( draw_list, glyph_pack[:], sub_slice(cached), ctx.colour ) + for id, index in sub_slice(cached) + { + call := Draw_Call_Default + call.pass = .Target + call.colour = ctx.colour + + profile("glyph") + call.start_index = u32(len(draw_list.indices)) + + quad := glyph_pack[id].draw_quad + blit_quad(draw_list, + quad.dst_pos, quad.dst_pos + quad.dst_scale, + quad.src_pos, quad.src_pos + quad.src_scale + ) + call.end_index = u32(len(draw_list.indices)) + append(& draw_list.calls, call) + } + reset_batch_codepoint_state( ctx ) + profile_end() + profile_begin("generate oversized glyphs draw_list") + for id, index in sub_slice(oversized) { + error : Allocator_Error + glyph_pack[id].shape, error = parser_get_glyph_shape(entry.parser_info, glyph_pack[id].index) + assert(error == .None) + } + for id, index in sub_slice(oversized) { + ctx.colour.r = 0.0 + ctx.colour.g = 0.0 + ctx.colour.b = 1.0 + flush_glyph_buffer_draw_list(draw_list, & glyph_buffer.draw_list, & glyph_buffer.clear_draw_list, & glyph_buffer.batch_x) + glyph := glyph_pack[id] - generate_oversized_draw_list(ctx, + generate_oversized_draw_list( + draw_list, + & ctx.temp_path, + ctx.colour, entry.curve_quality, glyph.shape, glyph.bounds, @@ -673,12 +717,10 @@ generate_shape_draw_list :: #force_no_inline proc( ctx : ^Context, } profile_end() - reset_batch_codepoint_state( ctx ) - flush_glyph_buffer_draw_list(draw_list, & glyph_buffer.draw_list, & glyph_buffer.clear_draw_list, & glyph_buffer.batch_x) + // flush_glyph_buffer_draw_list(draw_list, & glyph_buffer.draw_list, & glyph_buffer.clear_draw_list, & glyph_buffer.batch_x) profile_begin("font parser shape cleanup") for id, index in sub_slice(oversized) do parser_free_shape(entry.parser_info, glyph_pack[id].shape) - for id, index in sub_slice(to_cache) do parser_free_shape(entry.parser_info, glyph_pack[id].shape) profile_end() cursor_pos = position + shaped.end_cursor_pos * target_scale diff --git a/code/font/vefontcache/mappings.odin b/code/font/vefontcache/mappings.odin index d22f5b0..c46b9d1 100644 --- a/code/font/vefontcache/mappings.odin +++ b/code/font/vefontcache/mappings.odin @@ -36,8 +36,6 @@ import "core:mem" arena_init :: mem.arena_init import "core:slice" - - //#region("Proc overload mappings") append :: proc { @@ -51,15 +49,15 @@ append_soa :: proc { } 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, + ceil_f16, + ceil_f16le, + ceil_f16be, + ceil_f32, + ceil_f32le, + ceil_f32be, + ceil_f64, + ceil_f64le, + ceil_f64be, ceil_vec2, } @@ -70,15 +68,15 @@ clear :: proc { } floor :: proc { - math.floor_f16, - math.floor_f16le, - math.floor_f16be, - math.floor_f32, - math.floor_f32le, - math.floor_f32be, - math.floor_f64, - math.floor_f64le, - math.floor_f64be, + floor_f16, + floor_f16le, + floor_f16be, + floor_f32, + floor_f32le, + floor_f32be, + floor_f64, + floor_f64le, + floor_f64be, floor_vec2, } diff --git a/code/font/vefontcache/misc.odin b/code/font/vefontcache/misc.odin index 8171d0f..6fd805e 100644 --- a/code/font/vefontcache/misc.odin +++ b/code/font/vefontcache/misc.odin @@ -89,7 +89,7 @@ to_screen_space :: #force_inline proc "contextless" ( #no_alias position, scale pos_64 = pos_64 * quotient * 2.0 - 1.0 scale_64 = scale_64 * quotient * 2.0 - (position^) = { f32(pos_64.x), f32(pos_64.y) } + (position^) = { f32(pos_64.x), f32(pos_64.y) } (scale^) = { f32(scale_64.x), f32(scale_64.y) } } else @@ -98,7 +98,7 @@ to_screen_space :: #force_inline proc "contextless" ( #no_alias position, scale scale_32 := scale^ quotient : Vec2 = 1.0 / size - pos = pos * quotient * 2.0 - 1.0 + pos = pos * quotient * 2.0 - 1.0 scale_32 = scale_32 * quotient * 2.0 (position^) = pos @@ -117,7 +117,7 @@ to_text_space :: #force_inline proc "contextless" ( #no_alias position, scale : pos_64 *= quotient scale_64 *= quotient - (position^) = { f32(pos_64.x), f32(pos_64.y) } + (position^) = { f32(pos_64.x), f32(pos_64.y) } (scale^) = { f32(scale_64.x), f32(scale_64.y) } } else diff --git a/code/font/vefontcache/parser.odin b/code/font/vefontcache/parser.odin index 6961131..95614e1 100644 --- a/code/font/vefontcache/parser.odin +++ b/code/font/vefontcache/parser.odin @@ -14,7 +14,7 @@ import "core:c" import "core:math" import "core:slice" import stbtt "vendor:stb/truetype" -import freetype "thirdparty:freetype" +// import freetype "thirdparty:freetype" Parser_Kind :: enum u32 { STB_TrueType, @@ -26,7 +26,7 @@ Parser_Font_Info :: struct { kind : Parser_Kind, using _ : struct #raw_union { stbtt_info : stbtt.fontinfo, - freetype_info : freetype.Face + // freetype_info : freetype.Face }, data : []byte, } @@ -52,20 +52,20 @@ Parser_Glyph_Shape :: [dynamic]Parser_Glyph_Vertex Parser_Context :: struct { kind : Parser_Kind, - ft_library : freetype.Library, + // ft_library : freetype.Library, } parser_init :: proc( ctx : ^Parser_Context, kind : Parser_Kind ) { - switch kind - { - case .Freetype: - result := freetype.init_free_type( & ctx.ft_library ) - assert( result == freetype.Error.Ok, "VEFontCache.parser_init: Failed to initialize freetype" ) + // switch kind + // { + // case .Freetype: + // result := freetype.init_free_type( & ctx.ft_library ) + // assert( result == freetype.Error.Ok, "VEFontCache.parser_init: Failed to initialize freetype" ) - case .STB_TrueType: + // case .STB_TrueType: // Do nothing intentional - } + // } ctx.kind = kind } @@ -76,22 +76,22 @@ parser_shutdown :: proc( ctx : ^Parser_Context ) { parser_load_font :: proc( ctx : ^Parser_Context, label : string, data : []byte ) -> (font : Parser_Font_Info) { - switch ctx.kind - { - case .Freetype: - when ODIN_OS == .Windows { - error := freetype.new_memory_face( ctx.ft_library, raw_data(data), cast(i32) len(data), 0, & font.freetype_info ) - if error != .Ok do return - } - else when ODIN_OS == .Linux { - error := freetype.new_memory_face( ctx.ft_library, raw_data(data), cast(i64) len(data), 0, & font.freetype_info ) - if error != .Ok do return - } + // switch ctx.kind + // { + // case .Freetype: + // when ODIN_OS == .Windows { + // error := freetype.new_memory_face( ctx.ft_library, raw_data(data), cast(i32) len(data), 0, & font.freetype_info ) + // if error != .Ok do return + // } + // else when ODIN_OS == .Linux { + // error := freetype.new_memory_face( ctx.ft_library, raw_data(data), cast(i64) len(data), 0, & font.freetype_info ) + // if error != .Ok do return + // } - case .STB_TrueType: + // case .STB_TrueType: success := stbtt.InitFont( & font.stbtt_info, raw_data(data), 0 ) if ! success do return - } + // } font.label = label font.data = data @@ -101,122 +101,122 @@ parser_load_font :: proc( ctx : ^Parser_Context, label : string, data : []byte ) parser_unload_font :: proc( font : ^Parser_Font_Info ) { - switch font.kind { - case .Freetype: - error := freetype.done_face( font.freetype_info ) - assert( error == .Ok, "VEFontCache.parser_unload_font: Failed to unload freetype face" ) + // switch font.kind { + // case .Freetype: + // error := freetype.done_face( font.freetype_info ) + // assert( error == .Ok, "VEFontCache.parser_unload_font: Failed to unload freetype face" ) - case .STB_TrueType: + // case .STB_TrueType: // Do Nothing - } + // } } parser_find_glyph_index :: #force_inline proc "contextless" ( font : Parser_Font_Info, codepoint : rune ) -> (glyph_index : Glyph) { profile(#procedure) - switch font.kind - { - case .Freetype: - when ODIN_OS == .Windows { - glyph_index = transmute(Glyph) freetype.get_char_index( font.freetype_info, transmute(u32) codepoint ) - } - else when ODIN_OS == .Linux { - glyph_index = transmute(Glyph) freetype.get_char_index( font.freetype_info, cast(u64) codepoint ) - } - return + // switch font.kind + // { + // case .Freetype: + // when ODIN_OS == .Windows { + // glyph_index = transmute(Glyph) freetype.get_char_index( font.freetype_info, transmute(u32) codepoint ) + // } + // else when ODIN_OS == .Linux { + // glyph_index = transmute(Glyph) freetype.get_char_index( font.freetype_info, cast(u64) codepoint ) + // } + // return - case .STB_TrueType: + // case .STB_TrueType: glyph_index = transmute(Glyph) stbtt.FindGlyphIndex( font.stbtt_info, codepoint ) return - } - return Glyph(-1) + // } + // return Glyph(-1) } parser_free_shape :: #force_inline proc( font : Parser_Font_Info, shape : Parser_Glyph_Shape ) { - switch font.kind - { - case .Freetype: - delete(shape) + // switch font.kind + // { + // case .Freetype: + // delete(shape) - case .STB_TrueType: + // case .STB_TrueType: stbtt.FreeShape( font.stbtt_info, transmute( [^]stbtt.vertex) raw_data(shape) ) - } + // } } parser_get_codepoint_horizontal_metrics :: #force_inline proc "contextless" ( font : Parser_Font_Info, codepoint : rune ) -> ( advance, to_left_side_glyph : i32 ) { - switch font.kind - { - case .Freetype: - glyph_index : Glyph - when ODIN_OS == .Windows { - glyph_index = transmute(Glyph) freetype.get_char_index( font.freetype_info, transmute(u32) codepoint ) - } - else when ODIN_OS == .Linux { - glyph_index = transmute(Glyph) freetype.get_char_index( font.freetype_info, cast(u64) codepoint ) - } + // switch font.kind + // { + // case .Freetype: + // glyph_index : Glyph + // when ODIN_OS == .Windows { + // glyph_index = transmute(Glyph) freetype.get_char_index( font.freetype_info, transmute(u32) codepoint ) + // } + // else when ODIN_OS == .Linux { + // glyph_index = transmute(Glyph) freetype.get_char_index( font.freetype_info, cast(u64) codepoint ) + // } - if glyph_index != 0 - { - freetype.load_glyph( font.freetype_info, c.uint(codepoint), { .No_Bitmap, .No_Hinting, .No_Scale } ) - advance = i32(font.freetype_info.glyph.advance.x) >> 6 - to_left_side_glyph = i32(font.freetype_info.glyph.metrics.hori_bearing_x) >> 6 - } - else - { - advance = 0 - to_left_side_glyph = 0 - } + // if glyph_index != 0 + // { + // freetype.load_glyph( font.freetype_info, c.uint(codepoint), { .No_Bitmap, .No_Hinting, .No_Scale } ) + // advance = i32(font.freetype_info.glyph.advance.x) >> 6 + // to_left_side_glyph = i32(font.freetype_info.glyph.metrics.hori_bearing_x) >> 6 + // } + // else + // { + // advance = 0 + // to_left_side_glyph = 0 + // } - case .STB_TrueType: + // case .STB_TrueType: stbtt.GetCodepointHMetrics( font.stbtt_info, codepoint, & advance, & to_left_side_glyph ) - } + // } return } parser_get_codepoint_kern_advance :: #force_inline proc "contextless" ( font : Parser_Font_Info, prev_codepoint, codepoint : rune ) -> i32 { - switch font.kind - { - case .Freetype: - prev_glyph_index : Glyph - glyph_index : Glyph - when ODIN_OS == .Windows { - prev_glyph_index = transmute(Glyph) freetype.get_char_index( font.freetype_info, transmute(u32) prev_codepoint ) - glyph_index = transmute(Glyph) freetype.get_char_index( font.freetype_info, transmute(u32) codepoint ) - } - else when ODIN_OS == .Linux { - prev_glyph_index = transmute(Glyph) freetype.get_char_index( font.freetype_info, cast(u64) prev_codepoint ) - glyph_index = transmute(Glyph) freetype.get_char_index( font.freetype_info, cast(u64) codepoint ) - } + // switch font.kind + // { + // case .Freetype: + // prev_glyph_index : Glyph + // glyph_index : Glyph + // when ODIN_OS == .Windows { + // prev_glyph_index = transmute(Glyph) freetype.get_char_index( font.freetype_info, transmute(u32) prev_codepoint ) + // glyph_index = transmute(Glyph) freetype.get_char_index( font.freetype_info, transmute(u32) codepoint ) + // } + // else when ODIN_OS == .Linux { + // prev_glyph_index = transmute(Glyph) freetype.get_char_index( font.freetype_info, cast(u64) prev_codepoint ) + // glyph_index = transmute(Glyph) freetype.get_char_index( font.freetype_info, cast(u64) codepoint ) + // } - if prev_glyph_index != 0 && glyph_index != 0 - { - kerning : freetype.Vector - font.freetype_info.driver.clazz.get_kerning( font.freetype_info, transmute(u32) prev_codepoint, transmute(u32) codepoint, & kerning ) - } + // if prev_glyph_index != 0 && glyph_index != 0 + // { + // kerning : freetype.Vector + // font.freetype_info.driver.clazz.get_kerning( font.freetype_info, transmute(u32) prev_codepoint, transmute(u32) codepoint, & kerning ) + // } - case .STB_TrueType: + // case .STB_TrueType: kern := stbtt.GetCodepointKernAdvance( font.stbtt_info, prev_codepoint, codepoint ) return kern - } - return -1 + // } + // return -1 } parser_get_font_vertical_metrics :: #force_inline proc "contextless" ( font : Parser_Font_Info ) -> (ascent, descent, line_gap : i32 ) { - switch font.kind - { - case .Freetype: - info := font.freetype_info - ascent = i32(info.ascender) - descent = i32(info.descender) - line_gap = i32(info.height) - (ascent - descent) + // switch font.kind + // { + // case .Freetype: + // info := font.freetype_info + // ascent = i32(info.ascender) + // descent = i32(info.descender) + // line_gap = i32(info.height) - (ascent - descent) - case .STB_TrueType: + // case .STB_TrueType: stbtt.GetFontVMetrics( font.stbtt_info, & ascent, & descent, & line_gap ) - } + // } return } @@ -250,13 +250,13 @@ parser_get_bounds :: #force_inline proc "contextless" ( font : Parser_Font_Info, parser_get_glyph_shape :: #force_inline proc ( font : Parser_Font_Info, glyph_index : Glyph ) -> (shape : Parser_Glyph_Shape, error : Allocator_Error) { - switch font.kind - { - case .Freetype: - // TODO(Ed): Don't do this, going a completely different route for handling shapes. - // This abstraction fails to be time-saving or performant. + // switch font.kind + // { + // case .Freetype: + // // TODO(Ed): Don't do this, going a completely different route for handling shapes. + // // This abstraction fails to be time-saving or performant. - case .STB_TrueType: + // case .STB_TrueType: stb_shape : [^]stbtt.vertex nverts := stbtt.GetGlyphShape( font.stbtt_info, cast(i32) glyph_index, & stb_shape ) @@ -266,33 +266,33 @@ parser_get_glyph_shape :: #force_inline proc ( font : Parser_Font_Info, glyph_in shape_raw.cap = int(nverts) shape_raw.allocator = runtime.nil_allocator() error = Allocator_Error.None - return - } + // return + // } return } parser_is_glyph_empty :: #force_inline proc "contextless" ( font : Parser_Font_Info, glyph_index : Glyph ) -> b32 { - switch font.kind - { - case .Freetype: - error := freetype.load_glyph( font.freetype_info, cast(u32) glyph_index, { .No_Bitmap, .No_Hinting, .No_Scale } ) - if error == .Ok - { - if font.freetype_info.glyph.format == .Outline { - return font.freetype_info.glyph.outline.n_points == 0 - } - else if font.freetype_info.glyph.format == .Bitmap { - return font.freetype_info.glyph.bitmap.width == 0 && font.freetype_info.glyph.bitmap.rows == 0; - } - } - return false + // switch font.kind + // { + // case .Freetype: + // error := freetype.load_glyph( font.freetype_info, cast(u32) glyph_index, { .No_Bitmap, .No_Hinting, .No_Scale } ) + // if error == .Ok + // { + // if font.freetype_info.glyph.format == .Outline { + // return font.freetype_info.glyph.outline.n_points == 0 + // } + // else if font.freetype_info.glyph.format == .Bitmap { + // return font.freetype_info.glyph.bitmap.width == 0 && font.freetype_info.glyph.bitmap.rows == 0; + // } + // } + // return false - case .STB_TrueType: + // case .STB_TrueType: return stbtt.IsGlyphEmpty( font.stbtt_info, cast(c.int) glyph_index ) - } - return false + // } + // return false } parser_scale :: #force_inline proc "contextless" ( font : Parser_Font_Info, size : f32 ) -> f32 @@ -306,43 +306,43 @@ parser_scale :: #force_inline proc "contextless" ( font : Parser_Font_Info, size parser_scale_for_pixel_height :: #force_inline proc "contextless" ( font : Parser_Font_Info, size : f32 ) -> f32 { - switch font.kind { - case .Freetype: - freetype.set_pixel_sizes( font.freetype_info, 0, cast(u32) size ) - size_scale := size / cast(f32)font.freetype_info.units_per_em - return size_scale + // switch font.kind { + // case .Freetype: + // freetype.set_pixel_sizes( font.freetype_info, 0, cast(u32) size ) + // size_scale := size / cast(f32)font.freetype_info.units_per_em + // return size_scale - case.STB_TrueType: + // case.STB_TrueType: return stbtt.ScaleForPixelHeight( font.stbtt_info, size ) - } - return 0 + // } + // return 0 } parser_scale_for_mapping_em_to_pixels :: #force_inline proc "contextless" ( font : Parser_Font_Info, size : f32 ) -> f32 { - switch font.kind { - case .Freetype: - Inches_To_CM :: cast(f32) 2.54 - Points_Per_CM :: cast(f32) 28.3465 - CM_Per_Point :: cast(f32) 1.0 / DPT_DPCM - CM_Per_Pixel :: cast(f32) 1.0 / DPT_PPCM - DPT_DPCM :: cast(f32) 72.0 * Inches_To_CM // 182.88 points/dots per cm - DPT_PPCM :: cast(f32) 96.0 * Inches_To_CM // 243.84 pixels per cm - DPT_DPI :: cast(f32) 72.0 + // switch font.kind { + // case .Freetype: + // Inches_To_CM :: cast(f32) 2.54 + // Points_Per_CM :: cast(f32) 28.3465 + // CM_Per_Point :: cast(f32) 1.0 / DPT_DPCM + // CM_Per_Pixel :: cast(f32) 1.0 / DPT_PPCM + // DPT_DPCM :: cast(f32) 72.0 * Inches_To_CM // 182.88 points/dots per cm + // DPT_PPCM :: cast(f32) 96.0 * Inches_To_CM // 243.84 pixels per cm + // DPT_DPI :: cast(f32) 72.0 - // TODO(Ed): Don't assume the dots or pixels per inch. - system_dpi :: DPT_DPI + // // TODO(Ed): Don't assume the dots or pixels per inch. + // system_dpi :: DPT_DPI - FT_Font_Size_Point_Unit :: 1.0 / 64.0 - FT_Point_10 :: 64.0 + // FT_Font_Size_Point_Unit :: 1.0 / 64.0 + // FT_Point_10 :: 64.0 - points_per_em := (size / system_dpi ) * DPT_DPI - freetype.set_char_size( font.freetype_info, 0, cast(freetype.F26Dot6) f32(points_per_em * FT_Point_10), cast(u32) DPT_DPI, cast(u32) DPT_DPI ) - size_scale := f32(f64(size) / cast(f64) font.freetype_info.units_per_em) - return size_scale + // points_per_em := (size / system_dpi ) * DPT_DPI + // freetype.set_char_size( font.freetype_info, 0, cast(freetype.F26Dot6) f32(points_per_em * FT_Point_10), cast(u32) DPT_DPI, cast(u32) DPT_DPI ) + // size_scale := f32(f64(size) / cast(f64) font.freetype_info.units_per_em) + // return size_scale - case .STB_TrueType: + // case .STB_TrueType: return stbtt.ScaleForMappingEmToPixels( font.stbtt_info, size ) - } - return 0 + // } + // return 0 } diff --git a/code/font/vefontcache/shaped_text.odin b/code/font/vefontcache/shaped_text.odin index 123cd1a..b439f26 100644 --- a/code/font/vefontcache/shaped_text.odin +++ b/code/font/vefontcache/shaped_text.odin @@ -1,13 +1,12 @@ package vefontcache Shaped_Text :: struct { - font : Font_ID, - entry : ^Entry, - glyphs : [dynamic]Glyph, positions : [dynamic]Vec2, end_cursor_pos : Vec2, size : Vec2, + entry : ^Entry, + font : Font_ID, } Shaped_Text_Cache :: struct { diff --git a/code/font/vefontcache/vefontcache.odin b/code/font/vefontcache/vefontcache.odin index 227d4e7..a33c408 100644 --- a/code/font/vefontcache/vefontcache.odin +++ b/code/font/vefontcache/vefontcache.odin @@ -46,8 +46,9 @@ Context :: struct { snap_width : f32, snap_height : f32, - colour : Colour, - cursor_pos : Vec2, + alpha_scalar : f32, // Will apply a multiplier to the colour's alpha which provides some sharpening of the edges. + colour : Colour, + cursor_pos : Vec2, draw_layer : struct { vertices_offset : int, @@ -116,7 +117,7 @@ Init_Glyph_Draw_Params :: struct { Init_Glyph_Draw_Params_Default :: Init_Glyph_Draw_Params { over_sample = Vec2 { 4, 4 }, - buffer_batch = 8, + buffer_batch = 4, draw_padding = Init_Atlas_Params_Default.glyph_padding, } @@ -151,10 +152,11 @@ 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, - default_curve_quality : u32 = 6, + alpha_sharpen := 0.2, + default_curve_quality : u32 = 3, entires_reserve : u32 = 256, - temp_path_reserve : u32 = 1024, - temp_codepoint_seen_reserve : u32 = 1024, + temp_path_reserve : u32 = 10 * 1024, + temp_codepoint_seen_reserve : u32 = 10 * 1024, ) { assert( ctx != nil, "Must provide a valid context" ) @@ -163,12 +165,14 @@ startup :: proc( ctx : ^Context, parser_kind : Parser_Kind = .STB_TrueType, ctx.backing = allocator context.allocator = ctx.backing + ctx.colour = { 1, 1, 1, 1 } + use_advanced_shaper = shaper_params.use_advanced_text_shaper shaper_ctx.adv_snap_small_font_threshold = f32(shaper_params.adv_snap_small_font_threshold) shaper_ctx.snap_glyph_position = shaper_params.snap_glyph_position if default_curve_quality == 0 { - default_curve_quality = 6 + default_curve_quality = 3 } ctx.default_curve_quality = default_curve_quality @@ -267,7 +271,7 @@ startup :: proc( ctx : ^Context, parser_kind : Parser_Kind = .STB_TrueType, over_sample = 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) + height = atlas.region_d.height * i32(over_sample.y) //* (batch / 2) draw_padding = cast(f32) glyph_draw_params.draw_padding draw_list.calls, error = make( [dynamic]Draw_Call, len = 0, cap = glyph_draw_params.buffer_batch * 2 )