WIP - VEFontCahe: More progress on optimizing codepaths with SOA
This commit is contained in:
		@@ -243,7 +243,7 @@ lru_peek :: #force_inline proc "contextless" ( cache : ^LRU_Cache, key : u64, mu
 | 
			
		||||
 | 
			
		||||
lru_put :: #force_inline proc( cache : ^LRU_Cache, key : u64, value : i32 ) -> u64
 | 
			
		||||
{
 | 
			
		||||
	profile(#procedure)
 | 
			
		||||
	// profile(#procedure)
 | 
			
		||||
	if link, ok := & cache.table[ key ]; ok {
 | 
			
		||||
		pool_list_move_to_front( & cache.key_queue, link.ptr )
 | 
			
		||||
		link.value = value
 | 
			
		||||
 
 | 
			
		||||
@@ -34,6 +34,8 @@ Atlas :: struct {
 | 
			
		||||
	region_b : Atlas_Region,
 | 
			
		||||
	region_c : Atlas_Region,
 | 
			
		||||
	region_d : Atlas_Region,
 | 
			
		||||
 | 
			
		||||
	regions : [4] ^Atlas_Region,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
atlas_bbox :: #force_inline proc "contextless" ( atlas : ^Atlas, region : Atlas_Region_Kind, local_idx : i32 ) -> (position, size: Vec2)
 | 
			
		||||
@@ -86,32 +88,27 @@ atlas_bbox :: #force_inline proc "contextless" ( atlas : ^Atlas, region : Atlas_
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
decide_codepoint_region :: #force_inline proc (ctx : ^Context, entry : ^Entry, glyph_index : Glyph ) -> (region_kind : Atlas_Region_Kind, region : ^Atlas_Region, over_sample : Vec2)
 | 
			
		||||
atlas_region_bbox :: proc( region : Atlas_Region, local_idx : i32 ) -> (position, size: Vec2)
 | 
			
		||||
{
 | 
			
		||||
	size.x = f32(region.width)
 | 
			
		||||
	size.y = f32(region.height)
 | 
			
		||||
 | 
			
		||||
	position.x = cast(f32) (( local_idx % region.capacity.x ) * region.width)
 | 
			
		||||
	position.y = cast(f32) (( local_idx / region.capacity.x ) * region.height)
 | 
			
		||||
 | 
			
		||||
	position.x += f32(region.offset.x)
 | 
			
		||||
	position.y += f32(region.offset.y)
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
decide_codepoint_region :: #force_inline proc (atlas : Atlas, glyph_buffer : Glyph_Draw_Buffer,  size_scale : f32, glyph_index : Glyph, bounds_size : Vec2 ) -> (region_kind : Atlas_Region_Kind,  over_sample : Vec2)
 | 
			
		||||
{
 | 
			
		||||
	profile(#procedure)
 | 
			
		||||
	if parser_is_glyph_empty(&entry.parser_info, glyph_index) {
 | 
			
		||||
		return .None, nil, {}
 | 
			
		||||
	}
 | 
			
		||||
	glyph_padding_dbl  := atlas.glyph_padding * 2
 | 
			
		||||
	bounds_size_scaled := bounds_size * size_scale + glyph_padding_dbl
 | 
			
		||||
 | 
			
		||||
	bounds_0, bounds_1 := parser_get_glyph_box(&entry.parser_info, glyph_index)
 | 
			
		||||
	bounds_size        := vec2(bounds_1) - vec2(bounds_0)
 | 
			
		||||
 | 
			
		||||
	atlas             := & ctx.atlas
 | 
			
		||||
	glyph_buffer      := & ctx.glyph_buffer
 | 
			
		||||
	glyph_padding_dbl := atlas.glyph_padding * 2
 | 
			
		||||
 | 
			
		||||
	bounds_size_scaled := bounds_size * entry.size_scale * atlas.glyph_over_scalar + glyph_padding_dbl
 | 
			
		||||
 | 
			
		||||
	// Use a lookup table for faster region selection
 | 
			
		||||
	region_lookup := [4]struct { kind: Atlas_Region_Kind, region: ^Atlas_Region } {
 | 
			
		||||
		{ .A, & atlas.region_a },
 | 
			
		||||
		{ .B, & atlas.region_b },
 | 
			
		||||
		{ .C, & atlas.region_c },
 | 
			
		||||
		{ .D, & atlas.region_d },
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for region in region_lookup do if bounds_size_scaled.x <= f32(region.region.width) && bounds_size_scaled.y <= f32(region.region.height) {
 | 
			
		||||
		return region.kind, region.region, glyph_buffer.over_sample
 | 
			
		||||
	for kind in 0 ..< 4 do if bounds_size_scaled.x <= f32( atlas.regions[kind].width) && bounds_size_scaled.y <= f32(atlas.regions[kind].height) {
 | 
			
		||||
		return cast(Atlas_Region_Kind) kind, glyph_buffer.over_sample
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if bounds_size_scaled.x <= f32(glyph_buffer.width) \
 | 
			
		||||
@@ -121,9 +118,9 @@ decide_codepoint_region :: #force_inline proc (ctx : ^Context, entry : ^Entry, g
 | 
			
		||||
			bounds_size_scaled.y <= f32(glyph_buffer.height / 2) ? \
 | 
			
		||||
			  {2.0, 2.0} \
 | 
			
		||||
			: {1.0, 1.0}
 | 
			
		||||
		return .E, nil, over_sample
 | 
			
		||||
		return .E, over_sample
 | 
			
		||||
	}
 | 
			
		||||
	return .None, nil, {}
 | 
			
		||||
	return .None, {}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Grab an atlas LRU cache slot.
 | 
			
		||||
@@ -151,3 +148,40 @@ atlas_reserve_slot :: #force_inline proc ( region : ^Atlas_Region, lru_code : u6
 | 
			
		||||
	assert( lru_get( & region.state, lru_code ) != - 1 )
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
check_and_reserve_slot_in_atlas :: #force_inline proc( ctx : ^Context, font : Font_ID, entry : ^Entry, glyph_index : Glyph,
 | 
			
		||||
	lru_code    : u64,
 | 
			
		||||
	atlas_index : ^i32,
 | 
			
		||||
	region_kind : Atlas_Region_Kind,
 | 
			
		||||
	region      : ^Atlas_Region,
 | 
			
		||||
	over_sample : Vec2
 | 
			
		||||
) -> (found, should_cache : b8 )
 | 
			
		||||
{
 | 
			
		||||
	profile(#procedure)
 | 
			
		||||
	assert( glyph_index != -1 )
 | 
			
		||||
 | 
			
		||||
	if ctx.temp_codepoint_seen_num > i32(cap(ctx.temp_codepoint_seen)) do return
 | 
			
		||||
 | 
			
		||||
	if (atlas_index ^) == - 1
 | 
			
		||||
	{
 | 
			
		||||
		// 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 )
 | 
			
		||||
			success : bool
 | 
			
		||||
			found, success   = ctx.temp_codepoint_seen[next_evict_codepoint]
 | 
			
		||||
			assert(success != false)
 | 
			
		||||
 | 
			
		||||
			if (found) {
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		should_cache = true
 | 
			
		||||
		(atlas_index ^) = atlas_reserve_slot(region, lru_code)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	found = true
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
@@ -331,31 +331,23 @@ cache_glyph :: proc(ctx : ^Context, font : Font_ID, glyph_index : Glyph, entry :
 | 
			
		||||
	* 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
 | 
			
		||||
*/
 | 
			
		||||
cache_glyph_to_atlas :: proc( ctx : ^Context,
 | 
			
		||||
cache_glyph_to_atlas :: proc ( ctx : ^Context,
 | 
			
		||||
	font        : Font_ID,
 | 
			
		||||
	glyph_index : Glyph,
 | 
			
		||||
	bounds      : GlyphBounds,
 | 
			
		||||
	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 )
 | 
			
		||||
	over_sample : Vec2 
 | 
			
		||||
)
 | 
			
		||||
{
 | 
			
		||||
	profile(#procedure)
 | 
			
		||||
 | 
			
		||||
	// Get hb_font text metrics. These are unscaled!
 | 
			
		||||
	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 := vbounds_1 - vbounds_0
 | 
			
		||||
 | 
			
		||||
	// E region is special case and not cached to atlas.
 | 
			
		||||
	if region_kind == .None || region_kind == .E do return
 | 
			
		||||
 | 
			
		||||
	atlas_index := atlas_index
 | 
			
		||||
	// TODO(Ed): Try to make sure this is resolve always
 | 
			
		||||
	if atlas_index == -1 do atlas_index = atlas_reserve_slot( region, lru_code )
 | 
			
		||||
 | 
			
		||||
	atlas             := & ctx.atlas
 | 
			
		||||
	glyph_buffer      := & ctx.glyph_buffer
 | 
			
		||||
	atlas_size        := Vec2 { f32(atlas.width), f32(atlas.height) }
 | 
			
		||||
@@ -372,7 +364,7 @@ cache_glyph_to_atlas :: proc( ctx : ^Context,
 | 
			
		||||
 | 
			
		||||
	// Draw oversized glyph to glyph render target (FBO)
 | 
			
		||||
	glyph_draw_scale       := over_sample * entry.size_scale
 | 
			
		||||
	glyph_draw_translate   := -1 * vbounds_0 * glyph_draw_scale + vec2( glyph_padding )
 | 
			
		||||
	glyph_draw_translate   := -1 * bounds.p0 * glyph_draw_scale + vec2( glyph_padding )
 | 
			
		||||
 | 
			
		||||
	// Allocate a glyph glyph render target region (FBO)
 | 
			
		||||
	gwidth_scaled_px := bounds_size.x * glyph_draw_scale.x + over_sample.x * glyph_padding + 1.0
 | 
			
		||||
@@ -380,14 +372,13 @@ cache_glyph_to_atlas :: proc( ctx : ^Context,
 | 
			
		||||
		flush_glyph_buffer_to_atlas( ctx )
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Calculate the src and destination regions
 | 
			
		||||
	slot_position, slot_size := atlas_bbox( atlas, region_kind, atlas_index )
 | 
			
		||||
	region_pos := region_pos
 | 
			
		||||
 | 
			
		||||
	dst_glyph_position := slot_position
 | 
			
		||||
	dst_glyph_position := region_pos
 | 
			
		||||
	dst_glyph_size     := ceil(bounds_size * entry.size_scale + glyph_padding)
 | 
			
		||||
	dst_size           := (slot_size)
 | 
			
		||||
	dst_size           := (region_size)
 | 
			
		||||
	screenspace_x_form( & dst_glyph_position, & dst_glyph_size, atlas_size )
 | 
			
		||||
	screenspace_x_form( & slot_position,      & dst_size,       atlas_size )
 | 
			
		||||
	screenspace_x_form( & region_pos,         & dst_size,       atlas_size )
 | 
			
		||||
 | 
			
		||||
	src_position := Vec2 { f32(glyph_buffer.batch_x), 0 }
 | 
			
		||||
	src_size     := (bounds_size * glyph_draw_scale + over_sample * glyph_padding)
 | 
			
		||||
@@ -406,7 +397,7 @@ cache_glyph_to_atlas :: proc( ctx : ^Context,
 | 
			
		||||
		start_index = cast(u32) len(glyph_buffer.clear_draw_list.indices)
 | 
			
		||||
 | 
			
		||||
		blit_quad( & glyph_buffer.clear_draw_list,
 | 
			
		||||
			slot_position, slot_position + dst_size,
 | 
			
		||||
			region_pos, region_pos + dst_size,
 | 
			
		||||
			{ 1.0, 1.0 },  { 1.0, 1.0 } )
 | 
			
		||||
 | 
			
		||||
		end_index = cast(u32) len(glyph_buffer.clear_draw_list.indices)
 | 
			
		||||
@@ -420,7 +411,7 @@ cache_glyph_to_atlas :: proc( ctx : ^Context,
 | 
			
		||||
		start_index = cast(u32) len(glyph_buffer.draw_list.indices)
 | 
			
		||||
 | 
			
		||||
		blit_quad( & glyph_buffer.draw_list,
 | 
			
		||||
			dst_glyph_position, slot_position + dst_glyph_size,
 | 
			
		||||
			dst_glyph_position, region_pos + dst_glyph_size,
 | 
			
		||||
			src_position,       src_position  + src_size )
 | 
			
		||||
 | 
			
		||||
		end_index = cast(u32) len(glyph_buffer.draw_list.indices)
 | 
			
		||||
@@ -430,49 +421,7 @@ cache_glyph_to_atlas :: proc( ctx : ^Context,
 | 
			
		||||
	append( & glyph_buffer.draw_list.calls, blit_to_atlas )
 | 
			
		||||
 | 
			
		||||
	// Render glyph to glyph render target (FBO)
 | 
			
		||||
	cache_glyph( ctx, font, glyph_index, entry, vbounds_0, vbounds_1, glyph_draw_scale, glyph_draw_translate )
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// If the glyuph is found in the atlas, nothing occurs, otherwise, the glyph call is setup to catch it to the atlas
 | 
			
		||||
check_glyph_in_atlas :: #force_inline proc( ctx : ^Context, font : Font_ID, entry : ^Entry, glyph_index : Glyph,
 | 
			
		||||
	lru_code    : u64,
 | 
			
		||||
	atlas_index : i32,
 | 
			
		||||
	region_kind : Atlas_Region_Kind,
 | 
			
		||||
	region      : ^Atlas_Region,
 | 
			
		||||
	over_sample : Vec2
 | 
			
		||||
) -> (seen, should_cache : b8)
 | 
			
		||||
{
 | 
			
		||||
	profile(#procedure)
 | 
			
		||||
	assert( glyph_index != -1 )
 | 
			
		||||
 | 
			
		||||
	// E region can't batch
 | 
			
		||||
	if region_kind == .E || region_kind == .None                       do return
 | 
			
		||||
	if ctx.temp_codepoint_seen_num > i32(cap(ctx.temp_codepoint_seen)) do return
 | 
			
		||||
 | 
			
		||||
	if atlas_index == - 1
 | 
			
		||||
	{
 | 
			
		||||
		// 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 )
 | 
			
		||||
			success : bool
 | 
			
		||||
			seen, success   = ctx.temp_codepoint_seen[next_evict_codepoint]
 | 
			
		||||
			assert(success != false)
 | 
			
		||||
 | 
			
		||||
			if (seen) {
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		should_cache = true
 | 
			
		||||
		cache_glyph_to_atlas( ctx, font, glyph_index, lru_code, atlas_index, entry, region_kind, region, over_sample )
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	assert( lru_get( & region.state, lru_code ) != -1 )
 | 
			
		||||
	mark_batch_codepoint_seen( ctx, lru_code)
 | 
			
		||||
	seen = true
 | 
			
		||||
	return
 | 
			
		||||
	cache_glyph( ctx, font, glyph_index, entry, bounds.p0, bounds.p1, glyph_draw_scale, glyph_draw_translate )
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ve_fontcache_clear_Draw_List
 | 
			
		||||
@@ -485,7 +434,7 @@ clear_draw_list :: #force_inline proc ( draw_list : ^Draw_List ) {
 | 
			
		||||
directly_draw_massive_glyph :: proc( ctx : ^Context,
 | 
			
		||||
	entry : ^Entry,
 | 
			
		||||
	glyph : Glyph,
 | 
			
		||||
	bounds_0,    bounds_1        : Vec2,
 | 
			
		||||
	bounds                       : GlyphBounds,
 | 
			
		||||
	bounds_size                  : Vec2,
 | 
			
		||||
	over_sample, position, scale : Vec2 )
 | 
			
		||||
{
 | 
			
		||||
@@ -495,24 +444,22 @@ directly_draw_massive_glyph :: proc( ctx : ^Context,
 | 
			
		||||
	glyph_padding     := f32(ctx.atlas.glyph_padding)
 | 
			
		||||
	glyph_buffer_size := Vec2 { f32(ctx.glyph_buffer.width), f32(ctx.glyph_buffer.height) }
 | 
			
		||||
 | 
			
		||||
	// Draw un-antialiased glyph to update FBO.
 | 
			
		||||
	// Draw un-antialiased glyph to draw_buffer
 | 
			
		||||
	glyph_draw_scale     := over_sample * entry.size_scale
 | 
			
		||||
	glyph_draw_translate := -1 * bounds_0 * glyph_draw_scale + vec2_from_scalar(glyph_padding)
 | 
			
		||||
	glyph_draw_translate := -1 * bounds.p0 * glyph_draw_scale + vec2_from_scalar(glyph_padding)
 | 
			
		||||
	screenspace_x_form( & glyph_draw_translate, & glyph_draw_scale, glyph_buffer_size )
 | 
			
		||||
 | 
			
		||||
	cache_glyph( ctx, entry.id, glyph, entry, bounds_0, bounds_1, glyph_draw_scale, glyph_draw_translate )
 | 
			
		||||
	cache_glyph( ctx, entry.id, glyph, entry, bounds.p0, bounds.p1, glyph_draw_scale, glyph_draw_translate )
 | 
			
		||||
 | 
			
		||||
	glyph_padding_dbl := glyph_padding * 2
 | 
			
		||||
	bounds_scaled     := bounds_size * entry.size_scale
 | 
			
		||||
	bounds_scaled := bounds_size * entry.size_scale
 | 
			
		||||
 | 
			
		||||
	// Figure out the source rect.
 | 
			
		||||
	glyph_position := Vec2 {}
 | 
			
		||||
	glyph_size     := vec2(glyph_padding)
 | 
			
		||||
	glyph_dst_size := glyph_size    + bounds_scaled
 | 
			
		||||
	glyph_size     += bounds_scaled * over_sample
 | 
			
		||||
	glyph_size     := glyph_padding + bounds_scaled * over_sample
 | 
			
		||||
	glyph_dst_size := glyph_padding + bounds_scaled
 | 
			
		||||
 | 
			
		||||
	// Figure out the destination rect.
 | 
			
		||||
	bounds_0_scaled := (bounds_0 * entry.size_scale)
 | 
			
		||||
	bounds_0_scaled := (bounds.p0 * entry.size_scale)
 | 
			
		||||
	dst             := position + scale * bounds_0_scaled - glyph_padding * scale
 | 
			
		||||
	dst_size        := glyph_dst_size * scale
 | 
			
		||||
	textspace_x_form( & glyph_position, & glyph_size, glyph_buffer_size )
 | 
			
		||||
@@ -595,8 +542,9 @@ draw_filled_path :: proc( draw_list : ^Draw_List, outside_point : Vec2, path : [
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
draw_text_batch :: proc(ctx: ^Context, entry: ^Entry, shaped: ^Shaped_Text,
 | 
			
		||||
	batch_start_idx, batch_end_idx : i32,
 | 
			
		||||
draw_text_batch :: #force_inline proc (ctx: ^Context, entry: ^Entry, shaped: ^Shaped_Text,
 | 
			
		||||
	// batch_start_idx, batch_end_idx : i32,
 | 
			
		||||
	glyph_pack : #soa[]GlyphPackEntry,
 | 
			
		||||
	position, scale                : Vec2,
 | 
			
		||||
	snap_width, snap_height        : f32 )
 | 
			
		||||
{
 | 
			
		||||
@@ -607,61 +555,66 @@ draw_text_batch :: proc(ctx: ^Context, entry: ^Entry, shaped: ^Shaped_Text,
 | 
			
		||||
	atlas_size    := Vec2{ f32(atlas.width), f32(atlas.height) }
 | 
			
		||||
	glyph_padding := atlas.glyph_padding
 | 
			
		||||
 | 
			
		||||
	for index := batch_start_idx; index < batch_end_idx; index += 1
 | 
			
		||||
	// for glyph, index in glyph_pack
 | 
			
		||||
	// {
 | 
			
		||||
	// 	profile("oversized")
 | 
			
		||||
	// 	if glyph.region_kind != .E do continue
 | 
			
		||||
	// 	directly_draw_massive_glyph(ctx, entry, glyph.index,
 | 
			
		||||
	// 		glyph.bounds,
 | 
			
		||||
	// 		glyph.bounds_size,
 | 
			
		||||
	// 		glyph.over_sample, glyph.translate, scale )
 | 
			
		||||
	// }
 | 
			
		||||
 | 
			
		||||
	for glyph, index in glyph_pack
 | 
			
		||||
	{
 | 
			
		||||
		profile("glyph")
 | 
			
		||||
		glyph_index := shaped.glyphs[index]
 | 
			
		||||
		// profile_begin("oversized")
 | 
			
		||||
		// if glyph.region_kind == .E
 | 
			
		||||
		// {
 | 
			
		||||
		// 	directly_draw_massive_glyph(ctx, entry, glyph.index,
 | 
			
		||||
		// 		glyph.bounds,
 | 
			
		||||
		// 		glyph.bounds_size,
 | 
			
		||||
		// 		glyph.over_sample, glyph.translate, scale )
 | 
			
		||||
		// 	continue
 | 
			
		||||
		// }
 | 
			
		||||
		// profile_end()
 | 
			
		||||
 | 
			
		||||
		if glyph_index == 0 || parser_is_glyph_empty( & entry.parser_info, glyph_index) do continue
 | 
			
		||||
		profile("cached")
 | 
			
		||||
 | 
			
		||||
		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 := vbounds_1 - vbounds_0
 | 
			
		||||
		glyph_scale      := glyph.bounds_size * entry.size_scale + glyph_padding
 | 
			
		||||
		bounds_0_scaled  := ceil(glyph.bounds.p0 * entry.size_scale - 0.5 )
 | 
			
		||||
		dst_pos          := glyph.translate + bounds_0_scaled * scale
 | 
			
		||||
		dst_scale        := glyph_scale * scale
 | 
			
		||||
		src_pos          := glyph.region_pos
 | 
			
		||||
 | 
			
		||||
		shaped_position := shaped.positions[index]
 | 
			
		||||
		glyph_translate := position + (shaped_position) * scale
 | 
			
		||||
		textspace_x_form( & src_pos, & 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
 | 
			
		||||
		{
 | 
			
		||||
			profile("derive manual")
 | 
			
		||||
			call             := Draw_Call_Default
 | 
			
		||||
			call.pass         = .Target
 | 
			
		||||
			call.colour       = ctx.colour
 | 
			
		||||
			call.start_index  = u32(len(ctx.draw_list.indices))
 | 
			
		||||
		call             := Draw_Call_Default
 | 
			
		||||
		call.pass         = .Target
 | 
			
		||||
		call.colour       = ctx.colour
 | 
			
		||||
		call.start_index  = u32(len(ctx.draw_list.indices))
 | 
			
		||||
 | 
			
		||||
			// Draw cached 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 - 0.5 )
 | 
			
		||||
			dst              := glyph_translate + bounds_0_scaled * scale
 | 
			
		||||
			dst_scale        := glyph_scale * scale
 | 
			
		||||
		blit_quad(& ctx.draw_list,
 | 
			
		||||
			dst_pos, dst_pos + dst_scale,
 | 
			
		||||
			src_pos, src_pos + glyph_scale )
 | 
			
		||||
 | 
			
		||||
			textspace_x_form( & slot_position, & glyph_scale, atlas_size )
 | 
			
		||||
			blit_quad(&ctx.draw_list,
 | 
			
		||||
				dst,           dst           + dst_scale,
 | 
			
		||||
				slot_position, slot_position + glyph_scale )
 | 
			
		||||
			call.end_index = u32(len(ctx.draw_list.indices))
 | 
			
		||||
		call.end_index = u32(len(ctx.draw_list.indices))
 | 
			
		||||
 | 
			
		||||
			append(&ctx.draw_list.calls, call)
 | 
			
		||||
		}
 | 
			
		||||
		append(&ctx.draw_list.calls, call)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GlyphBounds :: struct {
 | 
			
		||||
	p0, p1 : Vec2
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GlyphPackEntry :: struct {
 | 
			
		||||
	lru_code     : u64,
 | 
			
		||||
	region       : ^Atlas_Region,
 | 
			
		||||
	bounds       : GlyphBounds,
 | 
			
		||||
	bounds_size  : Vec2,
 | 
			
		||||
	over_sample  : Vec2,
 | 
			
		||||
	translate    : Vec2,
 | 
			
		||||
	region_pos   : Vec2,
 | 
			
		||||
	region_size  : Vec2,
 | 
			
		||||
	lru_code     : u64,
 | 
			
		||||
	atlas_index  : i32,
 | 
			
		||||
	index        : Glyph,
 | 
			
		||||
	shape_id     : i32,
 | 
			
		||||
@@ -681,68 +634,91 @@ draw_text_shape :: #force_inline proc( ctx : ^Context,
 | 
			
		||||
{
 | 
			
		||||
	profile(#procedure)
 | 
			
		||||
 | 
			
		||||
	glyph_pack, pack_alloc_eror := make_soa(#soa[]GlyphPackEntry, len(shaped.glyphs), allocator = context.temp_allocator)
 | 
			
		||||
	glyph_pack, glyph_pack_alloc_error         := make_soa( #soa[]GlyphPackEntry, len(shaped.glyphs), allocator = context.temp_allocator )
 | 
			
		||||
	oversized_pack, oversized_pack_alloc_error := make_soa( #soa[dynamic]GlyphPackEntry, length = 0, capacity = len(shaped.glyphs), allocator = context.temp_allocator )
 | 
			
		||||
 | 
			
		||||
	profile_begin("SOA glyph pack processing")
 | 
			
		||||
	atlas := & ctx.atlas
 | 
			
		||||
 | 
			
		||||
	profile_begin("SOA setup")
 | 
			
		||||
 | 
			
		||||
	profile_begin("index & translate")
 | 
			
		||||
	for & glyph, index in glyph_pack
 | 
			
		||||
	{
 | 
			
		||||
		glyph.shape_id = cast(i32) index
 | 
			
		||||
		glyph.index    = shaped.glyphs[ index ]
 | 
			
		||||
	}
 | 
			
		||||
	// for & glyph, index in glyph_pack
 | 
			
		||||
	// {
 | 
			
		||||
	// 	glyph.region_kind, 
 | 
			
		||||
	// 	glyph.region, 
 | 
			
		||||
	// 	glyph.over_sample = decide_codepoint_region( ctx, entry, glyph.index )
 | 
			
		||||
	// }
 | 
			
		||||
	// for & glyph, index in glyph_pack
 | 
			
		||||
	// {
 | 
			
		||||
	// 	glyph.lru_code = font_glyph_lru_code(entry.id, glyph.index)
 | 
			
		||||
	// }
 | 
			
		||||
	// for & glyph, index in glyph_pack
 | 
			
		||||
	// {
 | 
			
		||||
	// 	glyph.atlas_index = -1
 | 
			
		||||
	// 	if glyph.region_kind != .E do glyph.atlas_index = lru_get( & glyph.region.state, glyph.lru_code )
 | 
			
		||||
	// }
 | 
			
		||||
	// for & glyph, index in glyph_pack
 | 
			
		||||
	// {
 | 
			
		||||
	// 	glyph.in_atlas, glyph.should_cache = check_glyph_in_atlas( ctx, font, entry, glyph.index, glyph.lru_code, glyph.atlas_index, glyph.region_kind, glyph.region, glyph.over_sample )
 | 
			
		||||
	// }
 | 
			
		||||
	// for & glyph, index in glyph_pack
 | 
			
		||||
	// {
 | 
			
		||||
	// 	if ! glyph.should_cache do continue 
 | 
			
		||||
	// 		cache_glyph_to_atlas(ctx, font, glyph.index, glyph.lru_code, glyph.atlas_index, entry, glyph.region_kind, glyph.region, glyph.over_sample)
 | 
			
		||||
	// }
 | 
			
		||||
	// for & glyph, index in glyph_pack
 | 
			
		||||
	// {
 | 
			
		||||
	// 	if ! glyph.in_atlas do continue
 | 
			
		||||
 | 
			
		||||
	// 	assert( lru_get( & glyph.region.state, glyph.lru_code ) != -1 )
 | 
			
		||||
	// 	mark_batch_codepoint_seen( ctx, glyph.lru_code)
 | 
			
		||||
	// }
 | 
			
		||||
	profile_end()
 | 
			
		||||
 | 
			
		||||
	profile_begin("translate")
 | 
			
		||||
	for & glyph, index in glyph_pack
 | 
			
		||||
	{
 | 
			
		||||
		glyph.translate = position + (shaped.positions[index]) * scale
 | 
			
		||||
	}
 | 
			
		||||
	profile_end()
 | 
			
		||||
 | 
			
		||||
	profile_begin("bounds")
 | 
			
		||||
	for & glyph, index in glyph_pack
 | 
			
		||||
	{
 | 
			
		||||
		glyph.lru_code    = font_glyph_lru_code(entry.id, glyph.index)
 | 
			
		||||
		glyph.bounds      = parser_get_bounds( & entry.parser_info, glyph.index )
 | 
			
		||||
		glyph.bounds_size = glyph.bounds.p1 - glyph.bounds.p0
 | 
			
		||||
	}
 | 
			
		||||
	profile_end()
 | 
			
		||||
 | 
			
		||||
	profile_begin("region")
 | 
			
		||||
	for & glyph, index in glyph_pack
 | 
			
		||||
	{
 | 
			
		||||
		glyph.region_kind, 
 | 
			
		||||
		glyph.over_sample = decide_codepoint_region( ctx.atlas, ctx.glyph_buffer, entry.size_scale, glyph.index, glyph.bounds_size )
 | 
			
		||||
	}
 | 
			
		||||
	profile_end()
 | 
			
		||||
 | 
			
		||||
	profile_begin("atlas region slot check & reservation")
 | 
			
		||||
	for & glyph, index in glyph_pack
 | 
			
		||||
	{
 | 
			
		||||
		region := atlas.regions[glyph.region_kind]
 | 
			
		||||
 | 
			
		||||
		if glyph.region_kind == .E do continue
 | 
			
		||||
 | 
			
		||||
		glyph.atlas_index =  lru_get( & region.state, glyph.lru_code )
 | 
			
		||||
		glyph.in_atlas, glyph.should_cache  = check_and_reserve_slot_in_atlas( ctx, font, entry, glyph.index, glyph.lru_code, & glyph.atlas_index, glyph.region_kind, region, glyph.over_sample )
 | 
			
		||||
		glyph.region_pos, glyph.region_size = atlas_region_bbox(region ^, glyph.atlas_index)
 | 
			
		||||
	}
 | 
			
		||||
	profile_end()
 | 
			
		||||
 | 
			
		||||
	profile_begin("caching to atlas")
 | 
			
		||||
	for glyph, index in glyph_pack
 | 
			
		||||
	{
 | 
			
		||||
		if glyph.region_kind == .E do continue
 | 
			
		||||
		if ! glyph.should_cache    do continue 
 | 
			
		||||
		cache_glyph_to_atlas(ctx, font, glyph.index, glyph.bounds, glyph.bounds_size, glyph.region_pos, glyph.region_size, glyph.lru_code, glyph.atlas_index, entry, glyph.region_kind, atlas.regions[glyph.region_kind], glyph.over_sample)
 | 
			
		||||
	}
 | 
			
		||||
	profile_end()
 | 
			
		||||
 | 
			
		||||
	profile_end()
 | 
			
		||||
 | 
			
		||||
	
 | 
			
		||||
	// First batch the other cached glyphs
 | 
			
		||||
	// flush_glyph_buffer_to_atlas(ctx)
 | 
			
		||||
	// draw_text_batch( ctx, entry, shaped, glyph_pack[batch_start_idx : index], position, scale, snap_width, snap_height )
 | 
			
		||||
	// reset_batch_codepoint_state( ctx )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	// Prepare uncached glyphs for caching
 | 
			
		||||
	batch_start_idx : i32 = 0
 | 
			
		||||
	for & glyph, index in glyph_pack
 | 
			
		||||
	for glyph, index in glyph_pack
 | 
			
		||||
	{
 | 
			
		||||
		// if is_glyph_empty( ctx, entry, glyph.index ) do continue
 | 
			
		||||
		profile("caching glyph")
 | 
			
		||||
 | 
			
		||||
		glyph.region_kind, glyph.region, glyph.over_sample = decide_codepoint_region( ctx, entry, glyph.index )
 | 
			
		||||
		if glyph.region_kind == .E {
 | 
			
		||||
			append_soa( & oversized_pack, glyph )
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		glyph.lru_code = font_glyph_lru_code(entry.id, glyph.index)
 | 
			
		||||
 | 
			
		||||
		glyph.atlas_index = -1
 | 
			
		||||
		if glyph.region_kind != .E do glyph.atlas_index = lru_get( & glyph.region.state, glyph.lru_code )
 | 
			
		||||
 | 
			
		||||
		glyph.in_atlas, glyph.should_cache = check_glyph_in_atlas( ctx, font, entry, glyph.index, glyph.lru_code, glyph.atlas_index, glyph.region_kind, glyph.region, glyph.over_sample )
 | 
			
		||||
		// if glyph.should_cache {
 | 
			
		||||
			// cache_glyph_to_atlas(ctx, font, glyph.index, glyph.lru_code, glyph.atlas_index, entry, glyph.region_kind, glyph.region, glyph.over_sample)
 | 
			
		||||
			// glyph.atlas_index = atlas_reserve_slot(glyph.region, glyph.lru_code)
 | 
			
		||||
		// }
 | 
			
		||||
		if glyph.in_atlas {
 | 
			
		||||
			// assert( lru_get( & glyph.region.state, glyph.lru_code ) != -1 )
 | 
			
		||||
			// mark_batch_codepoint_seen( ctx, glyph.lru_code)
 | 
			
		||||
			profile("glyph in atlas")
 | 
			
		||||
			// assert( lru_get( & atlas.regions[glyph.region_kind].state, glyph.lru_code ) != -1 )
 | 
			
		||||
			mark_batch_codepoint_seen( ctx, glyph.lru_code)
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
@@ -750,63 +726,32 @@ draw_text_shape :: #force_inline proc( ctx : ^Context,
 | 
			
		||||
 | 
			
		||||
		// First batch the other cached glyphs
 | 
			
		||||
		// flush_glyph_buffer_to_atlas(ctx)
 | 
			
		||||
		draw_text_batch( ctx, entry, shaped, batch_start_idx, glyph.shape_id, position, scale, snap_width, snap_height )
 | 
			
		||||
		reset_batch_codepoint_state( ctx )
 | 
			
		||||
		// draw_text_batch( ctx, entry, shaped, glyph_pack[batch_start_idx : index], position, scale, snap_width, snap_height )
 | 
			
		||||
		// reset_batch_codepoint_state( ctx )
 | 
			
		||||
 | 
			
		||||
		cache_glyph_to_atlas( ctx, font, glyph.index, glyph.lru_code, glyph.atlas_index, entry, glyph.region_kind, glyph.region, glyph.over_sample )
 | 
			
		||||
		cache_glyph_to_atlas( ctx, font, glyph.index, glyph.bounds, glyph.bounds_size, glyph.region_pos, glyph.region_size, glyph.lru_code, glyph.atlas_index, entry, glyph.region_kind, atlas.regions[glyph.region_kind], glyph.over_sample )
 | 
			
		||||
		mark_batch_codepoint_seen( ctx, glyph.lru_code)
 | 
			
		||||
	
 | 
			
		||||
		batch_start_idx = 1
 | 
			
		||||
		batch_start_idx = glyph.shape_id
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	draw_text_batch( ctx, entry, shaped, batch_start_idx, cast(i32) len(shaped.glyphs), position, scale, snap_width , snap_height )
 | 
			
		||||
	draw_text_batch( ctx, entry, shaped, glyph_pack[batch_start_idx : len(glyph_pack)], position, scale, snap_width , snap_height )
 | 
			
		||||
	reset_batch_codepoint_state( ctx )
 | 
			
		||||
 | 
			
		||||
	cursor_pos = position + shaped.end_cursor_pos * scale
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
	profile_begin("draw oversized glyphs")
 | 
			
		||||
	flush_glyph_buffer_to_atlas(ctx)
 | 
			
		||||
 | 
			
		||||
// Helper for draw_text_latin_mono
 | 
			
		||||
draw_text_mono_latin_batch :: #force_inline proc( ctx : ^Context,
 | 
			
		||||
	font                    : Font_ID,
 | 
			
		||||
	entry                   : ^Entry,
 | 
			
		||||
	shaped                  : ^Shaped_Text,
 | 
			
		||||
	position,   scale       : Vec2,
 | 
			
		||||
	snap_width, snap_height : f32
 | 
			
		||||
) -> (cursor_pos : Vec2) #no_bounds_check
 | 
			
		||||
{
 | 
			
		||||
	profile(#procedure)
 | 
			
		||||
	batch_start_idx : i32 = 0
 | 
			
		||||
	for index : i32 = 0; index < cast(i32) len(shaped.glyphs); index += 1
 | 
			
		||||
	for & glyph, index in oversized_pack
 | 
			
		||||
	{
 | 
			
		||||
		glyph_index := shaped.glyphs[ index ]
 | 
			
		||||
		if is_glyph_empty( ctx, entry, 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                      := cast(i32) -1
 | 
			
		||||
 | 
			
		||||
		if region_kind != .E do atlas_index = lru_get( & region.state, lru_code )
 | 
			
		||||
 | 
			
		||||
		in_atlas, should_cache := check_glyph_in_atlas( ctx, font, entry, glyph_index, lru_code, atlas_index, region_kind, region, over_sample )
 | 
			
		||||
		if in_atlas            do continue
 | 
			
		||||
		if should_cache        do cache_glyph_to_atlas(ctx, font, glyph_index, lru_code, atlas_index, entry, region_kind, region, over_sample )
 | 
			
		||||
 | 
			
		||||
		// We can no longer directly append the shape as it has missing glyphs in the atlas
 | 
			
		||||
 | 
			
		||||
		// First batch the other cached glyphs
 | 
			
		||||
		// 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 )
 | 
			
		||||
 | 
			
		||||
		cache_glyph_to_atlas( ctx, font, glyph_index, lru_code, atlas_index, entry, region_kind, region, over_sample )
 | 
			
		||||
		mark_batch_codepoint_seen( ctx, lru_code)
 | 
			
		||||
		batch_start_idx = index
 | 
			
		||||
		directly_draw_massive_glyph(ctx, entry, glyph.index,
 | 
			
		||||
			glyph.bounds,
 | 
			
		||||
			glyph.bounds_size,
 | 
			
		||||
			glyph.over_sample, glyph.translate, scale )
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	draw_text_batch( ctx, entry, shaped, batch_start_idx, cast(i32) len(shaped.glyphs), position, scale, snap_width , snap_height )
 | 
			
		||||
	reset_batch_codepoint_state( ctx )
 | 
			
		||||
 | 
			
		||||
	profile_end()
 | 
			
		||||
	
 | 
			
		||||
	cursor_pos = position + shaped.end_cursor_pos * scale
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,6 @@
 | 
			
		||||
package vefontcache
 | 
			
		||||
 | 
			
		||||
import "base:runtime"
 | 
			
		||||
import "core:hash"
 | 
			
		||||
	fnv64a :: hash.fnv64a
 | 
			
		||||
import "core:math"
 | 
			
		||||
@@ -45,6 +46,10 @@ append :: proc {
 | 
			
		||||
	append_elem_string,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
append_soa :: proc {
 | 
			
		||||
	append_soa_elem
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ceil :: proc {
 | 
			
		||||
	math.ceil_f16,
 | 
			
		||||
	math.ceil_f16le,
 | 
			
		||||
@@ -91,6 +96,7 @@ make :: proc {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
make_soa :: proc {
 | 
			
		||||
	make_soa_dynamic_array_len_cap,
 | 
			
		||||
	make_soa_slice,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -113,6 +119,7 @@ vec2_64 :: proc {
 | 
			
		||||
 | 
			
		||||
import "../../grime"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
DISABLE_PROFILING :: false
 | 
			
		||||
 | 
			
		||||
@(deferred_none = profile_end, disabled = DISABLE_PROFILING)
 | 
			
		||||
 
 | 
			
		||||
@@ -57,16 +57,17 @@ font_glyph_lru_code :: #force_inline proc "contextless" ( font : Font_ID, glyph_
 | 
			
		||||
is_glyph_empty :: #force_inline proc ( ctx : ^Context, entry : ^Entry, glyph_index : Glyph ) -> b32
 | 
			
		||||
{
 | 
			
		||||
	if glyph_index == 0 do return true
 | 
			
		||||
	if parser_is_glyph_empty( & entry.parser_info, glyph_index ) do return true
 | 
			
		||||
	if parser_is_glyph_empty( entry.parser_info, glyph_index ) do return true
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
mark_batch_codepoint_seen :: #force_inline proc ( ctx : ^Context, lru_code : u64 ) {
 | 
			
		||||
mark_batch_codepoint_seen :: #force_inline proc "contextless" ( ctx : ^Context, lru_code : u64 ) {
 | 
			
		||||
	ctx.temp_codepoint_seen[lru_code] = true
 | 
			
		||||
	ctx.temp_codepoint_seen_num += 1
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
reset_batch_codepoint_state :: #force_inline proc( ctx : ^Context ) {
 | 
			
		||||
	profile(#procedure)
 | 
			
		||||
	clear_map( & ctx.temp_codepoint_seen )
 | 
			
		||||
	ctx.temp_codepoint_seen_num = 0
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -111,7 +111,7 @@ parser_unload_font :: proc( font : ^Parser_Font_Info )
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
parser_find_glyph_index :: #force_inline proc "contextless" ( font : ^Parser_Font_Info, codepoint : rune ) -> (glyph_index : Glyph)
 | 
			
		||||
parser_find_glyph_index :: #force_inline proc "contextless" ( font : Parser_Font_Info, codepoint : rune ) -> (glyph_index : Glyph)
 | 
			
		||||
{
 | 
			
		||||
	profile(#procedure)
 | 
			
		||||
	switch font.kind
 | 
			
		||||
@@ -126,7 +126,7 @@ parser_find_glyph_index :: #force_inline proc "contextless" ( font : ^Parser_Fon
 | 
			
		||||
			return
 | 
			
		||||
 | 
			
		||||
		case .STB_TrueType:
 | 
			
		||||
			glyph_index = transmute(Glyph) stbtt.FindGlyphIndex( & font.stbtt_info, codepoint )
 | 
			
		||||
			glyph_index = transmute(Glyph) stbtt.FindGlyphIndex( font.stbtt_info, codepoint )
 | 
			
		||||
			return
 | 
			
		||||
	}
 | 
			
		||||
	return Glyph(-1)
 | 
			
		||||
@@ -220,12 +220,14 @@ parser_get_font_vertical_metrics :: #force_inline proc "contextless" ( font : ^P
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
parser_get_glyph_box :: #force_inline proc "contextless" ( font : ^Parser_Font_Info, glyph_index : Glyph ) -> (bounds_0, bounds_1 : Vec2i)
 | 
			
		||||
parser_get_bounds :: #force_inline proc "contextless" ( font : ^Parser_Font_Info, glyph_index : Glyph ) -> (bounds : GlyphBounds)
 | 
			
		||||
{
 | 
			
		||||
	profile(#procedure)
 | 
			
		||||
 | 
			
		||||
	bounds_0, bounds_1 : Vec2i
 | 
			
		||||
 | 
			
		||||
	switch font.kind
 | 
			
		||||
	{
 | 
			
		||||
		
 | 
			
		||||
		case .Freetype:
 | 
			
		||||
			freetype.load_glyph( font.freetype_info, c.uint(glyph_index), { .No_Bitmap, .No_Hinting, .No_Scale } )
 | 
			
		||||
 | 
			
		||||
@@ -242,6 +244,7 @@ parser_get_glyph_box :: #force_inline proc "contextless" ( font : ^Parser_Font_I
 | 
			
		||||
			bounds_0 = { x0, y0 }
 | 
			
		||||
			bounds_1 = { x1, y1 }
 | 
			
		||||
	}
 | 
			
		||||
	bounds = { vec2(bounds_0), vec2(bounds_1) }
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -269,7 +272,7 @@ parser_get_glyph_shape :: #force_inline proc ( font : ^Parser_Font_Info, glyph_i
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
parser_is_glyph_empty :: #force_inline proc "contextless" ( font : ^Parser_Font_Info, glyph_index : Glyph ) -> b32
 | 
			
		||||
parser_is_glyph_empty :: #force_inline proc "contextless" ( font : Parser_Font_Info, glyph_index : Glyph ) -> b32
 | 
			
		||||
{
 | 
			
		||||
	switch font.kind
 | 
			
		||||
	{
 | 
			
		||||
@@ -287,7 +290,7 @@ parser_is_glyph_empty :: #force_inline proc "contextless" ( font : ^Parser_Font_
 | 
			
		||||
			return false
 | 
			
		||||
 | 
			
		||||
		case .STB_TrueType:
 | 
			
		||||
			return stbtt.IsGlyphEmpty( & font.stbtt_info, cast(c.int) glyph_index )
 | 
			
		||||
			return stbtt.IsGlyphEmpty( font.stbtt_info, cast(c.int) glyph_index )
 | 
			
		||||
	}
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -76,7 +76,7 @@ shape_text_uncached_advanced :: #force_inline proc( ctx : ^Context, font : Font_
 | 
			
		||||
	line_gap    := f32(line_gap_i32)
 | 
			
		||||
	line_height := (ascent - descent + line_gap) * entry.size_scale
 | 
			
		||||
 | 
			
		||||
	shaper_shape_from_text( & ctx.shaper_ctx, & entry.parser_info, & entry.shaper_info, output, text_utf8, ascent_i32, descent_i32, line_gap_i32, entry.size, entry.size_scale )
 | 
			
		||||
	shaper_shape_from_text( & ctx.shaper_ctx, entry.parser_info, & entry.shaper_info, output, text_utf8, ascent_i32, descent_i32, line_gap_i32, entry.size, entry.size_scale )
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
shape_text_uncached_latin :: proc( ctx : ^Context, font : Font_ID, text_utf8 : string, entry : ^Entry, output : ^Shaped_Text )
 | 
			
		||||
@@ -119,8 +119,8 @@ shape_text_uncached_latin :: proc( ctx : ^Context, font : Font_ID, text_utf8 : s
 | 
			
		||||
			position.x = ceil(position.x)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		glyph_index := parser_find_glyph_index( & entry.parser_info, codepoint )
 | 
			
		||||
		is_glyph_empty    := parser_is_glyph_empty( & entry.parser_info,glyph_index )
 | 
			
		||||
		glyph_index := parser_find_glyph_index( entry.parser_info, codepoint )
 | 
			
		||||
		is_glyph_empty    := parser_is_glyph_empty( entry.parser_info,glyph_index )
 | 
			
		||||
		if ! is_glyph_empty
 | 
			
		||||
		{
 | 
			
		||||
			append( & output.glyphs, glyph_index)
 | 
			
		||||
 
 | 
			
		||||
@@ -54,7 +54,7 @@ shaper_unload_font :: proc( ctx : ^Shaper_Info )
 | 
			
		||||
	if blob != nil do harfbuzz.blob_destroy( blob )
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
shaper_shape_from_text :: #force_inline proc( ctx : ^Shaper_Context, parser_info : ^Parser_Font_Info, info : ^Shaper_Info, output :^Shaped_Text, text_utf8 : string,
 | 
			
		||||
shaper_shape_from_text :: #force_inline proc( ctx : ^Shaper_Context, parser_info : Parser_Font_Info, info : ^Shaper_Info, output :^Shaped_Text, text_utf8 : string,
 | 
			
		||||
	ascent, descent, line_gap : i32, size, size_scale : f32 )
 | 
			
		||||
{
 | 
			
		||||
	profile(#procedure)
 | 
			
		||||
@@ -72,7 +72,7 @@ shaper_shape_from_text :: #force_inline proc( ctx : ^Shaper_Context, parser_info
 | 
			
		||||
	line_height    := ((ascent - descent + line_gap) * size_scale)
 | 
			
		||||
 | 
			
		||||
	position : Vec2
 | 
			
		||||
	shape_run :: #force_inline proc( parser_info : ^Parser_Font_Info, buffer : harfbuzz.Buffer, script : harfbuzz.Script, font : harfbuzz.Font, output : ^Shaped_Text,
 | 
			
		||||
	shape_run :: #force_inline proc( parser_info : Parser_Font_Info, buffer : harfbuzz.Buffer, script : harfbuzz.Script, font : harfbuzz.Font, output : ^Shaped_Text,
 | 
			
		||||
		position : ^Vec2, max_line_width: ^f32, line_count: ^int,
 | 
			
		||||
		ascent, descent, line_gap, size, size_scale: f32,
 | 
			
		||||
		snap_shape_pos : b32, adv_snap_small_font_threshold : f32 )
 | 
			
		||||
 
 | 
			
		||||
@@ -225,6 +225,13 @@ startup :: proc( ctx : ^Context, parser_kind : Parser_Kind = .STB_TrueType,
 | 
			
		||||
	atlas.region_d.offset.x = atlas.width / 2
 | 
			
		||||
	atlas.region_d.offset.y = 0
 | 
			
		||||
 | 
			
		||||
	atlas.regions = {
 | 
			
		||||
		& atlas.region_a,
 | 
			
		||||
		& atlas.region_b,
 | 
			
		||||
		& atlas.region_c,
 | 
			
		||||
		& atlas.region_d,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	lru_init( & shape_cache.state, i32(shape_cache_params.capacity) )
 | 
			
		||||
 | 
			
		||||
	shape_cache.storage, error = make( [dynamic]Shaped_Text, shape_cache_params.capacity )
 | 
			
		||||
@@ -471,36 +478,6 @@ draw_text :: #force_inline proc( ctx : ^Context, font : Font_ID, text_utf8 : str
 | 
			
		||||
	return true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
draw_text_mono_latin :: #force_inline proc( ctx : ^Context, font : Font_ID, text_utf8 : string, position, scale : Vec2 ) -> b32
 | 
			
		||||
{
 | 
			
		||||
	profile(#procedure)
 | 
			
		||||
	assert( ctx != nil )
 | 
			
		||||
	assert( font >= 0 && int(font) < len(ctx.entries) )
 | 
			
		||||
 | 
			
		||||
	ctx.cursor_pos = {}
 | 
			
		||||
 | 
			
		||||
	position := position
 | 
			
		||||
	if ctx.snap_width  > 0 do position.x = ceil(position.x * ctx.snap_width ) / ctx.snap_width
 | 
			
		||||
	if ctx.snap_height > 0 do position.y = ceil(position.y * ctx.snap_height) / ctx.snap_height
 | 
			
		||||
 | 
			
		||||
	entry := & ctx.entries[ font ]
 | 
			
		||||
 | 
			
		||||
	ChunkType   :: enum u32 { Visible, Formatting }
 | 
			
		||||
	chunk_kind  : ChunkType
 | 
			
		||||
	chunk_start : int = 0
 | 
			
		||||
	chunk_end   : int = 0
 | 
			
		||||
 | 
			
		||||
	text_utf8_bytes := transmute([]u8) text_utf8
 | 
			
		||||
	text_chunk      : string
 | 
			
		||||
 | 
			
		||||
	text_chunk = transmute(string) text_utf8_bytes[ : ]
 | 
			
		||||
	if len(text_chunk) > 0 {
 | 
			
		||||
		shaped        := shape_text_cached( ctx, font, text_chunk, entry, shape_text_uncached_latin )
 | 
			
		||||
		ctx.cursor_pos = draw_text_shape( ctx, font, entry, shaped, position, scale, ctx.snap_width, ctx.snap_height )
 | 
			
		||||
	}
 | 
			
		||||
	return true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ve_fontcache_Draw_List
 | 
			
		||||
get_draw_list :: #force_inline proc( ctx : ^Context, optimize_before_returning := true ) -> ^Draw_List {
 | 
			
		||||
	assert( ctx != nil )
 | 
			
		||||
 
 | 
			
		||||
@@ -31,3 +31,4 @@ profile_begin :: #force_inline proc "contextless" ( name : string, loc := #calle
 | 
			
		||||
profile_end :: #force_inline proc "contextless" () {
 | 
			
		||||
	spall._buffer_end( & Module_Context.ctx, & Module_Context.buffer)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -6,7 +6,7 @@ import ve         "codebase:font/VEFontCache"
 | 
			
		||||
import sokol_gfx  "thirdparty:sokol/gfx"
 | 
			
		||||
 | 
			
		||||
Font_Provider_Use_Freetype :: false
 | 
			
		||||
Font_Largest_Px_Size       :: 72
 | 
			
		||||
Font_Largest_Px_Size       :: 152
 | 
			
		||||
Font_Size_Interval         :: 2
 | 
			
		||||
 | 
			
		||||
Font_Default            :: FontID { 0, "" }
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user