Swapped LRU and temp_codepoint_seen to use odin's vendor hash map
Starting to get serious about profiling this procedure and optimizing performance. I also want to make it more ideomatic now...
This commit is contained in:
		| @@ -146,14 +146,14 @@ LRU_Link :: struct { | ||||
| LRU_Cache :: struct { | ||||
| 	capacity  : u32, | ||||
| 	num       : u32, | ||||
| 	table     : HMapChained(LRU_Link), | ||||
| 	table     :  map[u64]LRU_Link, | ||||
| 	key_queue : PoolList, | ||||
| } | ||||
|  | ||||
| LRU_init :: proc( cache : ^LRU_Cache, capacity : u32, dbg_name : string = "" ) { | ||||
| 	error : AllocatorError | ||||
| 	cache.capacity     = capacity | ||||
| 	cache.table, error = make( HMapChained(LRU_Link), hmap_closest_prime( uint(capacity)) ) | ||||
| 	cache.table, error = make( map[u64]LRU_Link, hmap_closest_prime( uint(capacity)) ) | ||||
| 	assert( error == .None, "VEFontCache.LRU_init : Failed to allocate cache's table") | ||||
|  | ||||
| 	pool_list_init( & cache.key_queue, capacity, dbg_name = dbg_name ) | ||||
| @@ -166,7 +166,7 @@ LRU_free :: proc( cache : ^LRU_Cache ) | ||||
|  | ||||
| LRU_reload :: proc( cache : ^LRU_Cache, allocator : Allocator ) | ||||
| { | ||||
| 	hmap_chained_reload( cache.table, allocator ) | ||||
| 	reload_map( & cache.table, allocator ) | ||||
| 	pool_list_reload( & cache.key_queue, allocator ) | ||||
| } | ||||
|  | ||||
| @@ -177,19 +177,21 @@ LRU_hash_key :: #force_inline proc( key : u64 ) -> ( hash : u64 ) { | ||||
| 	return | ||||
| } | ||||
|  | ||||
| LRU_find :: proc( cache : ^LRU_Cache, key : u64, must_find := false ) -> ^LRU_Link { | ||||
| 	hash := LRU_hash_key( key ) | ||||
| 	link := get( cache.table, hash ) | ||||
| LRU_find :: proc( cache : ^LRU_Cache, key : u64, must_find := false ) -> (LRU_Link, bool) { | ||||
| 	// hash := LRU_hash_key( key ) | ||||
| 	// link := get( cache.table, hash ) | ||||
| 	// if link == nil && must_find { | ||||
| 	// 	runtime.debug_trap() | ||||
| 	// 	link = get( cache.table, hash ) | ||||
| 	// } | ||||
| 	return link | ||||
|  | ||||
| 	link, success := cache.table[key] | ||||
| 	return link, success | ||||
| } | ||||
|  | ||||
| LRU_get :: proc( cache : ^LRU_Cache, key : u64 ) -> i32 { | ||||
| 	iter := LRU_find( cache, key ) | ||||
| 	if iter == nil { | ||||
| 	iter, success := LRU_find( cache, key ) | ||||
| 	if success == false { | ||||
| 		return -1 | ||||
| 	} | ||||
| 	LRU_refresh( cache, key ) | ||||
| @@ -206,8 +208,8 @@ LRU_get_next_evicted :: proc( cache : ^LRU_Cache ) -> u64 | ||||
| } | ||||
|  | ||||
| LRU_peek :: proc( cache : ^LRU_Cache, key : u64, must_find := false ) -> i32 { | ||||
| 	iter := LRU_find( cache, key, must_find ) | ||||
| 	if iter == nil { | ||||
| 	iter, success := LRU_find( cache, key, must_find ) | ||||
| 	if success == false { | ||||
| 		return -1 | ||||
| 	} | ||||
| 	return iter.value | ||||
| @@ -215,9 +217,9 @@ LRU_peek :: proc( cache : ^LRU_Cache, key : u64, must_find := false ) -> i32 { | ||||
|  | ||||
| LRU_put :: proc( cache : ^LRU_Cache, key : u64,  value : i32 ) -> u64 | ||||
| { | ||||
| 	hash_key := LRU_hash_key( key ) | ||||
| 	iter     := get( cache.table, hash_key ) | ||||
| 	if iter != nil { | ||||
| 	// hash_key := LRU_hash_key( key ) | ||||
| 	iter, success := cache.table[key] | ||||
| 	if success { | ||||
| 		LRU_refresh( cache, key ) | ||||
| 		iter.value = value | ||||
| 		return key | ||||
| @@ -227,11 +229,11 @@ LRU_put :: proc( cache : ^LRU_Cache, key : u64,  value : i32 ) -> u64 | ||||
| 	if cache.key_queue.size >= cache.capacity { | ||||
| 		evict = pool_list_pop_back( & cache.key_queue ) | ||||
|  | ||||
| 		evict_hash := LRU_hash_key( evict ) | ||||
| 		// evict_hash := LRU_hash_key( evict ) | ||||
| 		// if cache.table.dbg_name != "" { | ||||
| 		// 	logf("%v: Evicted   %v with hash: %v", cache.table.dbg_name, evict, evict_hash) | ||||
| 		// } | ||||
| 		hmap_chained_remove( cache.table, evict_hash ) | ||||
| 		delete_key( & cache.table, evict ) | ||||
| 		cache.num -= 1 | ||||
| 	} | ||||
|  | ||||
| @@ -240,17 +242,16 @@ LRU_put :: proc( cache : ^LRU_Cache, key : u64,  value : i32 ) -> u64 | ||||
| 	// 	logf("%v: Pushed   %v with hash: %v", cache.table.dbg_name, key, hash_key ) | ||||
| 	// } | ||||
|  | ||||
| 	set( cache.table, hash_key, LRU_Link { | ||||
| 	cache.table[key] = LRU_Link { | ||||
| 		value = value, | ||||
| 		ptr   = cache.key_queue.front | ||||
| 	}) | ||||
|  | ||||
| 	} | ||||
| 	cache.num += 1 | ||||
| 	return evict | ||||
| } | ||||
|  | ||||
| LRU_refresh :: proc( cache : ^LRU_Cache, key : u64 ) { | ||||
| 	link := LRU_find( cache, key ) | ||||
| 	link, success := LRU_find( cache, key ) | ||||
| 	// if cache.table.dbg_name != "" { | ||||
| 	// 	logf("%v: Refreshed %v", cache.table.dbg_name, key) | ||||
| 	// } | ||||
|   | ||||
| @@ -59,7 +59,7 @@ Context :: struct { | ||||
| 	entries : Array(Entry), | ||||
|  | ||||
| 	temp_path               : Array(Vec2), | ||||
| 	temp_codepoint_seen     : HMapZPL(bool), | ||||
| 	temp_codepoint_seen     : map[u64]bool, | ||||
| 	temp_codepoint_seen_num : u32, | ||||
|  | ||||
| 	snap_width  : u32, | ||||
| @@ -152,7 +152,7 @@ init :: proc( ctx : ^Context, parser_kind : ParserKind, | ||||
| 	curve_quality               : u32 = 12, | ||||
| 	entires_reserve             : u32 = Kilobyte, | ||||
| 	temp_path_reserve           : u32 = Kilobyte, | ||||
| 	temp_codepoint_seen_reserve : u32 = 512, | ||||
| 	temp_codepoint_seen_reserve : u32 = 1024, | ||||
| ) | ||||
| { | ||||
| 	assert( ctx != nil, "Must provide a valid context" ) | ||||
| @@ -173,7 +173,7 @@ init :: proc( ctx : ^Context, parser_kind : ParserKind, | ||||
| 	temp_path, error = make( Array(Vec2), u64(temp_path_reserve) ) | ||||
| 	assert(error == .None, "VEFontCache.init : Failed to allocate temp_path") | ||||
|  | ||||
| 	temp_codepoint_seen, error = make( HMapZPL(bool), u64(hmap_closest_prime( uint(temp_codepoint_seen_reserve))) ) | ||||
| 	temp_codepoint_seen, error = make( map[u64]bool )//, hmap_closest_prime( uint(temp_codepoint_seen_reserve)) ) | ||||
| 	assert(error == .None, "VEFontCache.init : Failed to allocate temp_path") | ||||
|  | ||||
| 	draw_list.vertices, error = make( Array(Vertex), 4 * Kilobyte ) | ||||
| @@ -279,7 +279,7 @@ hot_reload :: proc( ctx : ^Context, allocator : Allocator ) | ||||
|  | ||||
| 	entries.backing   = allocator | ||||
| 	temp_path.backing = allocator | ||||
| 	hmap_zpl_reload( & ctx.temp_codepoint_seen, allocator ) | ||||
| 	reload_map( & ctx.temp_codepoint_seen, allocator ) | ||||
|  | ||||
| 	draw_list.vertices.backing  = allocator | ||||
| 	draw_list.indices.backing   = allocator | ||||
| @@ -397,6 +397,7 @@ unload_font :: proc( ctx : ^Context, font : FontID ) | ||||
|  | ||||
| cache_glyph :: proc( ctx : ^Context, font : FontID, glyph_index : Glyph, scale, translate : Vec2  ) -> b32 | ||||
| { | ||||
| 	profile(#procedure) | ||||
| 	assert( ctx != nil ) | ||||
| 	assert( font >= 0 && u64(font) < ctx.entries.num ) | ||||
| 	entry := & ctx.entries.data[ font ] | ||||
| @@ -519,6 +520,7 @@ cache_glyph :: proc( ctx : ^Context, font : FontID, glyph_index : Glyph, scale, | ||||
|  | ||||
| cache_glyph_to_atlas :: proc( ctx : ^Context, font : FontID, glyph_index : Glyph ) | ||||
| { | ||||
| 	profile(#procedure) | ||||
| 	assert( ctx != nil ) | ||||
| 	assert( font >= 0 && font < FontID(ctx.entries.num) ) | ||||
| 	entry := & ctx.entries.data[ font ] | ||||
| @@ -650,6 +652,7 @@ is_empty :: proc( ctx : ^Context, entry : ^Entry, glyph_index : Glyph ) -> b32 | ||||
|  | ||||
| measure_text_size :: proc( ctx : ^Context, font : FontID, text_utf8 : string ) -> (measured : Vec2) | ||||
| { | ||||
| 	profile(#procedure) | ||||
| 	assert( ctx != nil ) | ||||
| 	assert( font >= 0 && font < FontID(ctx.entries.num) ) | ||||
|  | ||||
|   | ||||
| @@ -90,6 +90,7 @@ atlas_bbox :: proc( atlas : ^Atlas, region : AtlasRegionKind, local_idx : i32 ) | ||||
|  | ||||
| can_batch_glyph :: proc( ctx : ^Context, font : FontID, entry : ^Entry, glyph_index : Glyph ) -> b32 | ||||
| { | ||||
| 	profile(#procedure) | ||||
| 	assert( ctx != nil ) | ||||
| 	assert( entry.id == font ) | ||||
|  | ||||
| @@ -111,10 +112,10 @@ can_batch_glyph :: proc( ctx : ^Context, font : FontID, entry : ^Entry, glyph_in | ||||
| 		if region.next_idx > u32( region.state.capacity) { | ||||
| 			// We will evict LRU. We must predict which LRU will get evicted, and if it's something we've seen then we need to take slowpath and flush batch. | ||||
| 			next_evict_codepoint := LRU_get_next_evicted( & region.state ) | ||||
| 			seen := get( & ctx.temp_codepoint_seen, next_evict_codepoint ) | ||||
| 			assert(seen != nil) | ||||
| 			seen, success := ctx.temp_codepoint_seen[next_evict_codepoint] | ||||
| 			assert(success != false) | ||||
|  | ||||
| 			if (seen^) { | ||||
| 			if (seen) { | ||||
| 				return false | ||||
| 			} | ||||
| 		} | ||||
| @@ -123,7 +124,7 @@ can_batch_glyph :: proc( ctx : ^Context, font : FontID, entry : ^Entry, glyph_in | ||||
| 	} | ||||
|  | ||||
| 	assert( LRU_get( & region.state, lru_code ) != -1 ) | ||||
| 	set( & ctx.temp_codepoint_seen, lru_code, true ) | ||||
| 	ctx.temp_codepoint_seen[lru_code] = true | ||||
| 	ctx.temp_codepoint_seen_num += 1 | ||||
| 	return true | ||||
| } | ||||
|   | ||||
| @@ -48,6 +48,7 @@ GlyphDrawBuffer :: struct { | ||||
|  | ||||
| blit_quad :: proc( draw_list : ^DrawList, p0 : Vec2 = {0, 0}, p1 : Vec2 = {1, 1}, uv0 : Vec2 = {0, 0}, uv1 : Vec2 = {1, 1} ) | ||||
| { | ||||
| 	profile(#procedure) | ||||
| 	// logf("Blitting: xy0: %0.2f, %0.2f xy1: %0.2f, %0.2f uv0: %0.2f, %0.2f uv1: %0.2f, %0.2f", | ||||
| 		// p0.x, p0.y, p1.x, p1.y, uv0.x, uv0.y, uv1.x, uv1.y); | ||||
| 	v_offset := cast(u32) draw_list.vertices.num | ||||
| @@ -97,6 +98,7 @@ clear_draw_list :: proc( draw_list : ^DrawList ) { | ||||
|  | ||||
| directly_draw_massive_glyph :: proc( ctx : ^Context, entry : ^Entry, glyph : Glyph, bounds_0 : Vec2i, bounds_width, bounds_height : i32, over_sample, position, scale : Vec2 ) | ||||
| { | ||||
| 	profile(#procedure) | ||||
| 	flush_glyph_buffer_to_atlas( ctx ) | ||||
|  | ||||
| 	// Draw un-antialiased glyph to update FBO. | ||||
| @@ -154,6 +156,7 @@ directly_draw_massive_glyph :: proc( ctx : ^Context, entry : ^Entry, glyph : Gly | ||||
|  | ||||
| draw_cached_glyph :: proc( ctx : ^Context, entry : ^Entry, glyph_index : Glyph, position, scale : Vec2 ) -> b32 | ||||
| { | ||||
| 	profile(#procedure) | ||||
| 	// Glyph not in current font | ||||
| 	if glyph_index == 0                                          do return true | ||||
| 	if parser_is_glyph_empty( & entry.parser_info, glyph_index ) do return true | ||||
| @@ -283,6 +286,7 @@ draw_filled_path :: proc( draw_list : ^DrawList, outside_point : Vec2, path : [] | ||||
| // 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) | ||||
| 	assert( ctx != nil ) | ||||
| 	assert( font >= 0 && font < FontID(ctx.entries.num) ) | ||||
|  | ||||
| @@ -411,6 +415,7 @@ draw_text_batch :: proc( ctx : ^Context, entry : ^Entry, shaped : ^ShapedText, b | ||||
| 	flush_glyph_buffer_to_atlas( ctx ) | ||||
| 	for index := batch_start_idx; index < batch_end_idx; index += 1 | ||||
| 	{ | ||||
| 		profile(#procedure) | ||||
| 		glyph_index       := shaped.glyphs.data[ index ] | ||||
| 		shaped_position   := shaped.positions.data[index] | ||||
| 		glyph_translate   := position + shaped_position * scale | ||||
| @@ -423,6 +428,7 @@ 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) | ||||
| { | ||||
| 	profile(#procedure) | ||||
| 	batch_start_idx : i32 = 0 | ||||
| 	for index : i32 = 0; index < i32(shaped.glyphs.num); index += 1 | ||||
| 	{ | ||||
| @@ -437,7 +443,7 @@ draw_text_shape :: proc( ctx : ^Context, font : FontID, entry : ^Entry, shaped : | ||||
| 		cache_glyph_to_atlas( ctx, font, glyph_index ) | ||||
|  | ||||
| 		lru_code := font_glyph_lru_code(font, glyph_index) | ||||
| 		set( & ctx.temp_codepoint_seen, lru_code, true ) | ||||
| 		ctx.temp_codepoint_seen[lru_code] = true | ||||
| 		ctx.temp_codepoint_seen_num += 1 | ||||
|  | ||||
| 		batch_start_idx = index | ||||
| @@ -457,6 +463,7 @@ flush_draw_list :: proc( ctx : ^Context ) { | ||||
|  | ||||
| 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) | ||||
| @@ -491,15 +498,16 @@ flush_layer :: proc( draw_list : ^DrawList ) {} | ||||
| // ve_fontcache_merge_drawlist | ||||
| merge_draw_list :: proc( dst, src : ^DrawList ) | ||||
| { | ||||
| 	profile(#procedure) | ||||
| 	error : AllocatorError | ||||
|  | ||||
| 	v_offset := cast(u32) dst.vertices.num | ||||
| 	for index : u32 = 0; index < cast(u32) src.vertices.num; index += 1 { | ||||
| 		error = append( & dst.vertices, src.vertices.data[index] ) | ||||
| 		assert( error == .None ) | ||||
| 	} | ||||
| 	// error = append( & dst.vertices, src.vertices ) | ||||
| 	// assert( error == .None ) | ||||
| 	// for index : u32 = 0; index < cast(u32) src.vertices.num; index += 1 { | ||||
| 	// 	error = append( & dst.vertices, src.vertices.data[index] ) | ||||
| 	// 	assert( error == .None ) | ||||
| 	// } | ||||
| 	error = append( & dst.vertices, src.vertices ) | ||||
| 	assert( error == .None ) | ||||
|  | ||||
| 	i_offset := cast(u32) dst.indices.num | ||||
| 	for index : u32 = 0; index < cast(u32) src.indices.num; index += 1 { | ||||
| @@ -518,6 +526,7 @@ merge_draw_list :: proc( dst, src : ^DrawList ) | ||||
|  | ||||
| optimize_draw_list :: proc( draw_list : ^DrawList, call_offset : u64 ) | ||||
| { | ||||
| 	profile(#procedure) | ||||
| 	assert( draw_list != nil ) | ||||
|  | ||||
| 	calls := array_to_slice(draw_list.calls) | ||||
|   | ||||
| @@ -62,6 +62,8 @@ hmap_zpl_reload :: grime.hmap_zpl_reload | ||||
| hmap_zpl_remove :: grime.hmap_zpl_remove | ||||
| hmap_zpl_set    :: grime.hmap_zpl_set | ||||
|  | ||||
| reload_map :: grime.reload_map | ||||
|  | ||||
| // Pool :: grime.Pool | ||||
|  | ||||
| StackFixed :: grime.StackFixed | ||||
| @@ -77,6 +79,8 @@ stack_push_contextless :: grime.stack_push_contextless | ||||
| log  :: grime.log | ||||
| logf :: grime.logf | ||||
|  | ||||
| profile :: grime.profile | ||||
|  | ||||
| //#region("Proc overload mappings") | ||||
|  | ||||
| append :: proc { | ||||
| @@ -110,6 +114,7 @@ make :: proc { | ||||
| 	array_init, | ||||
| 	hmap_chained_init, | ||||
| 	hmap_zpl_init, | ||||
| 	make_map, | ||||
| } | ||||
|  | ||||
| // reload :: proc { | ||||
|   | ||||
| @@ -13,7 +13,6 @@ font_glyph_lru_code :: #force_inline proc( font : FontID, glyph_index : Glyph ) | ||||
| 	// copy( buffer[ len(font_bytes) :], glyph_bytes ) | ||||
| 	// hash := fnv64a( transmute([]byte) buffer[: size_of(FontID) + size_of(Glyph) ] ) | ||||
| 	// lru_code = hash | ||||
|  | ||||
| 	lru_code = u64(glyph_index) + ( ( 0x100000000 * u64(font) ) & 0xFFFFFFFF00000000 ) | ||||
| 	return | ||||
| } | ||||
| @@ -31,10 +30,10 @@ shape_lru_hash :: #force_inline proc( label : string ) -> u64 { | ||||
| // ve_fontcache_eval_bezier (quadratic) | ||||
| eval_point_on_bezier3 :: proc( p0, p1, p2 : Vec2, alpha : f32 ) -> Vec2 | ||||
| { | ||||
| 	p0    := vec2_64_from_vec2(p0) | ||||
| 	p1    := vec2_64_from_vec2(p1) | ||||
| 	p2    := vec2_64_from_vec2(p2) | ||||
| 	alpha := f64(alpha) | ||||
| 	// p0    := vec2_64_from_vec2(p0) | ||||
| 	// p1    := vec2_64_from_vec2(p1) | ||||
| 	// p2    := vec2_64_from_vec2(p2) | ||||
| 	// alpha := f64(alpha) | ||||
|  | ||||
| 	weight_start   := (1 - alpha) * (1 - alpha) | ||||
| 	weight_control := 2.0 * (1 - alpha) * alpha | ||||
| @@ -53,11 +52,11 @@ eval_point_on_bezier3 :: proc( p0, p1, p2 : Vec2, alpha : f32 ) -> Vec2 | ||||
| // ve_fontcache_eval_bezier (cubic) | ||||
| eval_point_on_bezier4 :: proc( p0, p1, p2, p3 : Vec2, alpha : f32 ) -> Vec2 | ||||
| { | ||||
| 	p0    := vec2_64_from_vec2(p0) | ||||
| 	p1    := vec2_64_from_vec2(p1) | ||||
| 	p2    := vec2_64_from_vec2(p2) | ||||
| 	p3    := vec2_64_from_vec2(p3) | ||||
| 	alpha := f64(alpha) | ||||
| 	// p0    := vec2_64_from_vec2(p0) | ||||
| 	// p1    := vec2_64_from_vec2(p1) | ||||
| 	// p2    := vec2_64_from_vec2(p2) | ||||
| 	// p3    := vec2_64_from_vec2(p3) | ||||
| 	// alpha := f64(alpha) | ||||
|  | ||||
| 	weight_start := (1 - alpha) * (1 - alpha) * (1 - alpha) | ||||
| 	weight_c_a   := 3 * (1 - alpha) * (1 - alpha) * alpha | ||||
| @@ -74,7 +73,7 @@ eval_point_on_bezier4 :: proc( p0, p1, p2, p3 : Vec2, alpha : f32 ) -> Vec2 | ||||
| } | ||||
|  | ||||
| reset_batch_codepoint_state :: proc( ctx : ^Context ) { | ||||
| 	clear( & ctx.temp_codepoint_seen ) | ||||
| 	clear_map( & ctx.temp_codepoint_seen ) | ||||
| 	ctx.temp_codepoint_seen_num = 0 | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -210,9 +210,6 @@ parser_get_glyph_box :: proc( font : ^ParserFontInfo, glyph_index : Glyph ) -> ( | ||||
|  | ||||
| 		case .STB_TrueType: | ||||
| 			x0, y0, x1, y1 : i32 | ||||
| 			// { | ||||
| 			// 	success := stbtt.InitFont( & font.stbtt_info, raw_data(font.data), 0 ) | ||||
| 			// } | ||||
| 			success := cast(bool) stbtt.GetGlyphBox( & font.stbtt_info, i32(glyph_index), & x0, & y0, & x1, & y1 ) | ||||
| 			assert( success ) | ||||
|  | ||||
|   | ||||
| @@ -16,6 +16,7 @@ ShapedTextCache :: struct { | ||||
|  | ||||
| shape_text_cached :: proc( ctx : ^Context, font : FontID, text_utf8 : string ) -> ^ShapedText | ||||
| { | ||||
| 	profile(#procedure) | ||||
| 	@static buffer : [64 * Kilobyte]byte | ||||
|  | ||||
| 	font := font | ||||
| @@ -61,6 +62,7 @@ shape_text_cached :: proc( ctx : ^Context, font : FontID, text_utf8 : string ) - | ||||
|  | ||||
| shape_text_uncached :: proc( ctx : ^Context, font : FontID, output : ^ShapedText, text_utf8 : string ) | ||||
| { | ||||
| 	profile(#procedure) | ||||
| 	assert( ctx != nil ) | ||||
| 	assert( font >= 0 && font < FontID(ctx.entries.num) ) | ||||
|  | ||||
|   | ||||
| @@ -70,6 +70,7 @@ shaper_unload_font :: proc( ctx : ^ShaperInfo ) | ||||
| shaper_shape_from_text :: proc( ctx : ^ShaperContext, info : ^ShaperInfo, output :^ShapedText, text_utf8 : string, | ||||
| 	ascent, descent, line_gap : i32, size, size_scale : f32 ) | ||||
| { | ||||
| 	profile(#procedure) | ||||
| 	current_script := harfbuzz.Script.UNKNOWN | ||||
| 	hb_ucfunc      := harfbuzz.unicode_funcs_get_default() | ||||
| 	harfbuzz.buffer_clear_contents( ctx.hb_buffer ) | ||||
|   | ||||
		Reference in New Issue
	
	Block a user