VEFontCache: look into to trying to cache the draw_list for a shape
plus other misc changes
This commit is contained in:
		| @@ -28,11 +28,11 @@ PoolList :: struct { | ||||
| pool_list_init :: proc( pool : ^PoolList, capacity : u32, dbg_name : string = "" ) | ||||
| { | ||||
| 	error : AllocatorError | ||||
| 	pool.items, error = make( [dynamic]PoolListItem, u64(capacity) ) | ||||
| 	pool.items, error = make( [dynamic]PoolListItem, int(capacity) ) | ||||
| 	assert( error == .None, "VEFontCache.pool_list_init : Failed to allocate items array") | ||||
| 	resize( & pool.items, capacity ) | ||||
|  | ||||
| 	pool.free_list, error = make( [dynamic]PoolListIter, u64(capacity) ) | ||||
| 	pool.free_list, error = make( [dynamic]PoolListIter, len = 0, cap = int(capacity) ) | ||||
| 	assert( error == .None, "VEFontCache.pool_list_init : Failed to allocate free_list array") | ||||
| 	resize( & pool.free_list, capacity ) | ||||
|  | ||||
| @@ -55,7 +55,7 @@ pool_list_init :: proc( pool : ^PoolList, capacity : u32, dbg_name : string = "" | ||||
|  | ||||
| pool_list_free :: proc( pool : ^PoolList ) | ||||
| { | ||||
|  // TODO(Ed): Implement | ||||
| 	// TODO(Ed): Implement | ||||
| } | ||||
|  | ||||
| pool_list_reload :: proc( pool : ^PoolList, allocator : Allocator ) | ||||
| @@ -160,7 +160,7 @@ LRU_init :: proc( cache : ^LRU_Cache, capacity : u32, dbg_name : string = "" ) { | ||||
|  | ||||
| LRU_free :: proc( cache : ^LRU_Cache ) | ||||
| { | ||||
|  // TODO(Ed): Implement | ||||
| 	// TODO(Ed): Implement | ||||
| } | ||||
|  | ||||
| LRU_reload :: #force_inline proc( cache : ^LRU_Cache, allocator : Allocator ) | ||||
|   | ||||
| @@ -27,3 +27,10 @@ TODO Additional Features: | ||||
| * Ability to set a draw transform, viewport and projection | ||||
|   * By default the library's position is in unsigned normalized render space | ||||
| * Allow curve_quality to be set on a per-font basis | ||||
|  | ||||
| TODO Optimization: | ||||
|  | ||||
| * Look into caching the draw_list for each shape instead of the glyphs/positions  | ||||
|   * Each shape is already constrained to a Entry which is restricted to already a size-class for the glyphs | ||||
|   * Caching a glyph to atlas or generating the draw command for a glyph quad to screen is expensive for large batches. | ||||
| * Attempt to look into chunking shapes again if caching the draw_list for a shape is found to be optimal | ||||
|   | ||||
| @@ -161,25 +161,26 @@ startup :: proc( ctx : ^Context, parser_kind : ParserKind, | ||||
| 	ctx.curve_quality = curve_quality | ||||
|  | ||||
| 	error : AllocatorError | ||||
| 	entries, error = make( [dynamic]Entry, entires_reserve ) | ||||
| 	entries, error = make( [dynamic]Entry, len = 0, cap = entires_reserve ) | ||||
| 	assert(error == .None, "VEFontCache.init : Failed to allocate entries") | ||||
|  | ||||
| 	temp_path, error = make( [dynamic]Vec2, temp_path_reserve ) | ||||
| 	temp_path, error = make( [dynamic]Vec2, len = 0, cap = temp_path_reserve ) | ||||
| 	assert(error == .None, "VEFontCache.init : Failed to allocate temp_path") | ||||
|  | ||||
| 	temp_codepoint_seen, error = make( map[u64]bool, uint(temp_codepoint_seen_reserve) ) | ||||
| 	assert(error == .None, "VEFontCache.init : Failed to allocate temp_path") | ||||
|  | ||||
| 	draw_list.vertices, error = make( [dynamic]Vertex, 4 * Kilobyte ) | ||||
| 	draw_list.vertices, error = make( [dynamic]Vertex, len = 0, cap = 4 * Kilobyte ) | ||||
| 	assert(error == .None, "VEFontCache.init : Failed to allocate draw_list.vertices") | ||||
|  | ||||
| 	draw_list.indices, error = make( [dynamic]u32, 8 * Kilobyte ) | ||||
| 	draw_list.indices, error = make( [dynamic]u32, len = 0, cap = 8 * Kilobyte ) | ||||
| 	assert(error == .None, "VEFontCache.init : Failed to allocate draw_list.indices") | ||||
|  | ||||
| 	draw_list.calls, error = make( [dynamic]DrawCall, 512 ) | ||||
| 	draw_list.calls, error = make( [dynamic]DrawCall, len = 0, cap = 512 ) | ||||
| 	assert(error == .None, "VEFontCache.init : Failed to allocate draw_list.calls") | ||||
|  | ||||
| 	init_atlas_region :: proc( region : ^AtlasRegion, params : InitAtlasParams, region_params : InitAtlasRegionParams, factor : Vec2i, expected_cap : i32 ) { | ||||
| 	init_atlas_region :: proc( region : ^AtlasRegion, params : InitAtlasParams, region_params : InitAtlasRegionParams, factor : Vec2i, expected_cap : i32 ) | ||||
| 	{ | ||||
| 		using region | ||||
|  | ||||
| 		next_idx = 0; | ||||
| @@ -225,11 +226,20 @@ startup :: proc( ctx : ^Context, parser_kind : ParserKind, | ||||
| 	for idx : u32 = 0; idx < shape_cache_params.capacity; idx += 1 { | ||||
| 		stroage_entry := & shape_cache.storage[idx] | ||||
| 		using stroage_entry | ||||
| 		glyphs, error = make( [dynamic]Glyph, shape_cache_params.reserve_length ) | ||||
| 		glyphs, error = make( [dynamic]Glyph, len = 0, cap = shape_cache_params.reserve_length ) | ||||
| 		assert( error == .None, "VEFontCache.init : Failed to allocate glyphs array for shape cache storage" ) | ||||
|  | ||||
| 		positions, error = make( [dynamic]Vec2, shape_cache_params.reserve_length ) | ||||
| 		positions, error = make( [dynamic]Vec2, len = 0, cap = shape_cache_params.reserve_length ) | ||||
| 		assert( error == .None, "VEFontCache.init : Failed to allocate positions array for shape cache storage" ) | ||||
|  | ||||
| 		draw_list.calls, error = make( [dynamic]DrawCall, len = 0, cap = glyph_draw_params.buffer_batch * 2 ) | ||||
| 		assert( error == .None, "VEFontCache.init : Failed to allocate calls for draw_list" ) | ||||
|  | ||||
| 		draw_list.indices, error = make( [dynamic]u32, len = 0, cap = glyph_draw_params.buffer_batch * 2 * 6 ) | ||||
| 		assert( error == .None, "VEFontCache.init : Failed to allocate indices array for draw_list" ) | ||||
|  | ||||
| 		draw_list.vertices, error = make( [dynamic]Vertex, len = 0, cap = glyph_draw_params.buffer_batch * 2 * 4 ) | ||||
| 		assert( error == .None, "VEFontCache.init : Failed to allocate vertices array for draw_list" ) | ||||
| 	} | ||||
|  | ||||
| 	// Note(From original author): We can actually go over VE_FONTCACHE_GLYPHDRAW_BUFFER_BATCH batches due to smart packing! | ||||
| @@ -241,22 +251,22 @@ startup :: proc( ctx : ^Context, parser_kind : ParserKind, | ||||
| 		height        = atlas.region_d.height * u32(over_sample.y) | ||||
| 		draw_padding  = glyph_draw_params.draw_padding | ||||
|  | ||||
| 		draw_list.calls, error = make( [dynamic]DrawCall, cast(u64) glyph_draw_params.buffer_batch * 2 ) | ||||
| 		draw_list.calls, error = make( [dynamic]DrawCall, len = 0, cap = glyph_draw_params.buffer_batch * 2 ) | ||||
| 		assert( error == .None, "VEFontCache.init : Failed to allocate calls for draw_list" ) | ||||
|  | ||||
| 		draw_list.indices, error = make( [dynamic]u32, cast(u64) glyph_draw_params.buffer_batch * 2 * 6 ) | ||||
| 		draw_list.indices, error = make( [dynamic]u32, len = 0, cap = glyph_draw_params.buffer_batch * 2 * 6 ) | ||||
| 		assert( error == .None, "VEFontCache.init : Failed to allocate indices array for draw_list" ) | ||||
|  | ||||
| 		draw_list.vertices, error = make( [dynamic]Vertex, glyph_draw_params.buffer_batch * 2 * 4 ) | ||||
| 		draw_list.vertices, error = make( [dynamic]Vertex, len = 0, cap = glyph_draw_params.buffer_batch * 2 * 4 ) | ||||
| 		assert( error == .None, "VEFontCache.init : Failed to allocate vertices array for draw_list" ) | ||||
|  | ||||
| 		clear_draw_list.calls, error = make( [dynamic]DrawCall, cast(u64) glyph_draw_params.buffer_batch * 2 ) | ||||
| 		clear_draw_list.calls, error = make( [dynamic]DrawCall, len = 0, cap = glyph_draw_params.buffer_batch * 2 ) | ||||
| 		assert( error == .None, "VEFontCache.init : Failed to allocate calls for calls for clear_draw_list" ) | ||||
|  | ||||
| 		clear_draw_list.indices, error = make( [dynamic]u32, cast(u64) glyph_draw_params.buffer_batch * 2 * 4 ) | ||||
| 		clear_draw_list.indices, error = make( [dynamic]u32, len = 0, cap = glyph_draw_params.buffer_batch * 2 * 4 ) | ||||
| 		assert( error == .None, "VEFontCache.init : Failed to allocate calls for indices array for clear_draw_list" ) | ||||
|  | ||||
| 		clear_draw_list.vertices, error = make( [dynamic]Vertex, glyph_draw_params.buffer_batch * 2 * 4 ) | ||||
| 		clear_draw_list.vertices, error = make( [dynamic]Vertex, len = 0, cap = glyph_draw_params.buffer_batch * 2 * 4 ) | ||||
| 		assert( error == .None, "VEFontCache.init : Failed to allocate vertices array for clear_draw_list" ) | ||||
| 	} | ||||
|  | ||||
| @@ -395,7 +405,7 @@ configure_snap :: #force_inline proc( ctx : ^Context, snap_width, snap_height : | ||||
| get_cursor_pos :: #force_inline proc "contextless" ( ctx : ^Context                  ) -> Vec2 { return ctx.cursor_pos } | ||||
| set_colour     :: #force_inline proc "contextless" ( ctx : ^Context, colour : Colour )         { ctx.colour = colour } | ||||
|  | ||||
| draw_text :: proc( ctx : ^Context, font : FontID, text_utf8 : string, position : Vec2, scale : Vec2 ) -> b32 | ||||
| draw_text :: proc( ctx : ^Context, font : FontID, text_utf8 : string, position, scale : Vec2 ) -> b32 | ||||
| { | ||||
| 	// profile(#procedure) | ||||
| 	assert( ctx != nil ) | ||||
| @@ -471,24 +481,9 @@ measure_text_size :: proc( ctx : ^Context, font : FontID, text_utf8 : string ) - | ||||
| 	assert( ctx != nil ) | ||||
| 	assert( font >= 0 && int(font) < len(ctx.entries) ) | ||||
|  | ||||
| 	atlas   := ctx.atlas | ||||
| 	entry   := & ctx.entries[ font ] | ||||
| 	shaped  := shape_text_cached( ctx, font, text_utf8, entry ) | ||||
| 	padding := cast(f32) atlas.glyph_padding | ||||
|  | ||||
| 	for index : i32 = 0; index < i32(len(shaped.glyphs)); index += 1 | ||||
| 	{ | ||||
| 		glyph_index := shaped.glyphs[ index ] | ||||
| 		if is_empty( ctx, entry, glyph_index ) do continue | ||||
|  | ||||
| 		bounds_0, bounds_1 := parser_get_glyph_box( & entry.parser_info, glyph_index ) | ||||
| 		bounds_size := bounds_1 - bounds_0 | ||||
|  | ||||
| 		glyph_size := Vec2 { f32(bounds_size.x), f32(bounds_size.y) } * entry.size_scale | ||||
| 		measured.y = max(measured.y, glyph_size.y) | ||||
| 	} | ||||
| 	measured.x = shaped.end_cursor_pos.x | ||||
| 	return measured | ||||
| 	entry  := &ctx.entries[font] | ||||
| 	shaped := shape_text_cached(ctx, font, text_utf8, entry) | ||||
| 	return shaped.size | ||||
| } | ||||
|  | ||||
| get_font_vertical_metrics :: #force_inline proc ( ctx : ^Context, font : FontID ) -> ( ascent, descent, line_gap : i32 ) | ||||
|   | ||||
										
											Binary file not shown.
										
									
								
							| @@ -60,25 +60,25 @@ blit_quad :: proc( draw_list : ^DrawList, p0 : Vec2 = {0, 0}, p1 : Vec2 = {1, 1} | ||||
| 		{p0.x, p0.y}, | ||||
| 		uv0.x, uv0.y | ||||
| 	} | ||||
| 	append_elem( & draw_list.vertices, vertex ) | ||||
| 	append( & draw_list.vertices, vertex ) | ||||
|  | ||||
| 	vertex = Vertex { | ||||
| 		{p0.x, p1.y}, | ||||
| 		uv0.x, uv1.y | ||||
| 	} | ||||
| 	append_elem( & draw_list.vertices, vertex ) | ||||
| 	append( & draw_list.vertices, vertex ) | ||||
|  | ||||
| 	vertex = Vertex { | ||||
| 		{p1.x, p0.y}, | ||||
| 		uv1.x, uv0.y | ||||
| 	} | ||||
| 	append_elem( & draw_list.vertices, vertex ) | ||||
| 	append( & draw_list.vertices, vertex ) | ||||
|  | ||||
| 	vertex = Vertex { | ||||
| 		{p1.x, p1.y}, | ||||
| 		uv1.x, uv1.y | ||||
| 	} | ||||
| 	append_elem( & draw_list.vertices, vertex ) | ||||
| 	append( & draw_list.vertices, vertex ) | ||||
|  | ||||
| 	quad_indices : []u32 = { | ||||
| 		0, 1, 2, | ||||
| @@ -332,7 +332,8 @@ cache_glyph_to_atlas :: proc( ctx : ^Context, | ||||
| 	cache_glyph( ctx, font, glyph_index, entry, vec2(bounds_0), vec2(bounds_1), glyph_draw_scale, glyph_draw_translate ) | ||||
| } | ||||
|  | ||||
| can_batch_glyph :: #force_inline proc( ctx : ^Context, font : FontID, entry : ^Entry, glyph_index : Glyph, | ||||
| // 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 : FontID, entry : ^Entry, glyph_index : Glyph, | ||||
| 	lru_code    : u64, | ||||
| 	atlas_index : i32, | ||||
| 	region_kind : AtlasRegionKind, | ||||
| @@ -438,7 +439,7 @@ directly_draw_massive_glyph :: proc( ctx : ^Context, | ||||
| 	append( & ctx.draw_list.calls, call ) | ||||
| } | ||||
|  | ||||
| draw_cached_glyph :: proc( ctx : ^Context, | ||||
| draw_cached_glyph :: proc( ctx : ^Context, shaped : ^ShapedText, | ||||
| 	entry              : ^Entry, | ||||
| 	glyph_index        : Glyph, | ||||
| 	lru_code           : u64, | ||||
| @@ -486,6 +487,24 @@ draw_cached_glyph :: proc( ctx : ^Context, | ||||
|  | ||||
| 	textspace_x_form( & slot_position, & glyph_scale, atlas_size ) | ||||
|  | ||||
| 	// Shape call setup | ||||
| 	if false | ||||
| 	{ | ||||
| 		call := DrawCall_Default | ||||
| 		{ | ||||
| 			using call | ||||
| 			pass        = .Target | ||||
| 			colour      = ctx.colour | ||||
| 			start_index = cast(u32) len(shaped.draw_list.indices) | ||||
|  | ||||
| 			blit_quad( & shaped.draw_list, | ||||
| 				dst,           dst           + dst_scale, | ||||
| 				slot_position, slot_position + glyph_scale ) | ||||
| 			end_index   = cast(u32) len(shaped.draw_list.indices) | ||||
| 		} | ||||
| 		append( & shaped.draw_list.calls, call ) | ||||
| 	} | ||||
|  | ||||
| 	// Add the glyph drawcall | ||||
| 	call := DrawCall_Default | ||||
| 	{ | ||||
| @@ -500,6 +519,7 @@ draw_cached_glyph :: proc( ctx : ^Context, | ||||
| 		end_index   = cast(u32) len(ctx.draw_list.indices) | ||||
| 	} | ||||
| 	append( & ctx.draw_list.calls, call ) | ||||
|  | ||||
| 	return true | ||||
| } | ||||
|  | ||||
| @@ -575,7 +595,7 @@ draw_text_batch :: proc( ctx : ^Context, entry : ^Entry, shaped : ^ShapedText, | ||||
| 		shaped_position := shaped.positions[index] | ||||
| 		glyph_translate := position + shaped_position * scale | ||||
|  | ||||
| 		glyph_cached := draw_cached_glyph( ctx, | ||||
| 		glyph_cached := draw_cached_glyph( ctx, shaped, | ||||
| 			entry,       glyph_index, | ||||
| 			lru_code,    atlas_index, | ||||
| 			vec2(bounds_0), vec2(bounds_1), | ||||
| @@ -594,6 +614,9 @@ draw_text_shape :: proc( ctx : ^Context, | ||||
| 	snap_width, snap_height : f32 | ||||
| ) -> (cursor_pos : Vec2) | ||||
| { | ||||
| 	draw_hash   := shape_draw_hash( shaped, position, scale ) | ||||
| 	dirty_shape := ! (len(shaped.draw_list.calls) > 0) || draw_hash != shaped.draw_hash | ||||
|  | ||||
| 	// position := position //+ ctx.cursor_pos * scale | ||||
| 	// profile(#procedure) | ||||
| 	batch_start_idx : i32 = 0 | ||||
| @@ -607,7 +630,10 @@ draw_text_shape :: proc( ctx : ^Context, | ||||
| 		atlas_index                      := cast(i32) -1 | ||||
|  | ||||
| 		if region_kind != .E do atlas_index = LRU_get( & region.state, lru_code ) | ||||
| 		if can_batch_glyph( ctx, font, entry, glyph_index, lru_code, atlas_index, region_kind, region, over_sample ) do continue | ||||
| 		if check_glyph_in_atlas( ctx, font, entry, glyph_index, lru_code, atlas_index, region_kind, region, over_sample ) do continue | ||||
|  | ||||
| 		// We can no longer directly append the shape as it has missing glyphs in the atlas | ||||
| 		dirty_shape = true | ||||
|  | ||||
| 		// Glyph has not been catched, needs to be directly drawn. | ||||
|  | ||||
| @@ -621,10 +647,18 @@ draw_text_shape :: proc( ctx : ^Context, | ||||
| 		batch_start_idx = index | ||||
| 	} | ||||
|  | ||||
| 	// flush_glyph_buffer_to_atlas(ctx) | ||||
| 	draw_text_batch( ctx, entry, shaped, batch_start_idx, cast(i32) len(shaped.glyphs), position, scale, snap_width , snap_height ) | ||||
| 	// if dirty_shape { | ||||
| 		flush_glyph_buffer_to_atlas(ctx) | ||||
| 		draw_text_batch( ctx, entry, shaped, batch_start_idx, cast(i32) len(shaped.glyphs), position, scale, snap_width , snap_height ) | ||||
| 	// 	shaped.draw_hash = draw_hash | ||||
| 	// } | ||||
| 	// else { | ||||
| 		// flush_glyph_buffer_to_atlas( ctx ) | ||||
| 		// merge_draw_list( & ctx.draw_list, & shaped.draw_list ) | ||||
| 	// } | ||||
| 	reset_batch_codepoint_state( ctx ) | ||||
| 	cursor_pos = position + shaped.end_cursor_pos * scae | ||||
|  | ||||
| 	cursor_pos = position + shaped.end_cursor_pos * scale | ||||
| 	return | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -50,13 +50,6 @@ font_glyph_lru_code :: #force_inline proc "contextless" ( font : FontID, glyph_i | ||||
| 	return | ||||
| } | ||||
|  | ||||
| shape_lru_hash :: #force_inline proc "contextless" ( label : string ) -> u64 { | ||||
| 	hash : u64 | ||||
| 	for str_byte in transmute([]byte) label { | ||||
| 		hash = ((hash << 8) + hash) + u64(str_byte) | ||||
| 	} | ||||
| 	return hash | ||||
| } | ||||
|  | ||||
| // For a provided alpha value, | ||||
| // allows the function to calculate the position of a point along the curve at any given fraction of its total length | ||||
|   | ||||
| @@ -1,11 +1,13 @@ | ||||
| package VEFontCache | ||||
|  | ||||
| import "core:math" | ||||
|  | ||||
| ShapedText :: struct { | ||||
| 	draw_list      : DrawList, | ||||
| 	glyphs         : [dynamic]Glyph, | ||||
| 	positions      : [dynamic]Vec2, | ||||
| 	end_cursor_pos : Vec2, | ||||
| 	size           : Vec2, | ||||
| 	storage_hash   : u64, | ||||
| 	draw_hash      : u64, | ||||
| } | ||||
|  | ||||
| ShapedTextCache :: struct { | ||||
| @@ -14,36 +16,68 @@ ShapedTextCache :: struct { | ||||
| 	next_cache_id : i32, | ||||
| } | ||||
|  | ||||
|  | ||||
| shape_draw_hash :: #force_inline proc "contextless" ( shaped : ^ShapedText, pos, scale : Vec2 ) -> (draw_hash : u64) | ||||
| { | ||||
| 	pos   := pos | ||||
| 	scale := scale | ||||
| 	pos_bytes   := slice_ptr( transmute(^byte) & pos,   size_of(Vec2)) | ||||
| 	scale_bytes := slice_ptr( transmute(^byte) & scale, size_of(Vec2)) | ||||
|  | ||||
| 	draw_hash = shaped.storage_hash | ||||
| 	shape_lru_hash( & shaped.draw_hash, pos_bytes ) | ||||
| 	shape_lru_hash( & shaped.draw_hash, scale_bytes ) | ||||
| 	return | ||||
| } | ||||
|  | ||||
| // shape_lru_hash_og :: #force_inline proc "contextless" ( label : string ) -> u64 { | ||||
| // 	hash : u64 | ||||
| // 	for str_byte in transmute([]byte) label { | ||||
| // 		hash = ((hash << 8) + hash) + u64(str_byte) | ||||
| // 	} | ||||
| // 	return hash | ||||
| // } | ||||
|  | ||||
| shape_lru_hash :: #force_inline proc "contextless" ( hash : ^u64, bytes : []byte ) { | ||||
| 	for value in bytes { | ||||
| 		(hash^) = (( (hash^) << 8) + (hash^) ) + u64(value) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| shape_text_cached :: proc( ctx : ^Context, font : FontID, text_utf8 : string, entry : ^Entry ) -> ^ShapedText | ||||
| { | ||||
| 	// profile(#procedure) | ||||
| 	@static buffer : [64 * Kilobyte]byte | ||||
| 	font        := font | ||||
| 	font_bytes  := slice_ptr( transmute(^byte) & font,  size_of(FontID) ) | ||||
| 	text_bytes  := transmute( []byte) text_utf8 | ||||
|  | ||||
| 	font            := font | ||||
| 	text_size       := len(text_utf8) | ||||
| 	sice_end_offset := size_of(FontID) + len(text_utf8) | ||||
| 	lru_code : u64 | ||||
| 	shape_lru_hash( & lru_code, font_bytes ) | ||||
| 	shape_lru_hash( & lru_code, text_bytes ) | ||||
|  | ||||
| 	buffer_slice := buffer[:] | ||||
| 	font_bytes   := slice_ptr( transmute(^byte) & font, size_of(FontID) ) | ||||
| 	copy( buffer_slice, font_bytes ) | ||||
| 	// @static buffer : [64 * Kilobyte]byte | ||||
| 	// text_size       := len(text_utf8) | ||||
| 	// sice_end_offset := size_of(FontID) + len(text_utf8) | ||||
|  | ||||
| 	text_bytes             := transmute( []byte) text_utf8 | ||||
| 	buffer_slice_post_font := buffer[ size_of(FontID) : sice_end_offset ] | ||||
| 	copy( buffer_slice_post_font, text_bytes ) | ||||
| 	// buffer_slice := buffer[:] | ||||
| 	// copy( buffer_slice, font_bytes ) | ||||
|  | ||||
| 	hash := shape_lru_hash( transmute(string) buffer[: sice_end_offset ] ) | ||||
| 	// buffer_slice_post_font := buffer[ size_of(FontID) : sice_end_offset ] | ||||
| 	// copy( buffer_slice_post_font, text_bytes ) | ||||
|  | ||||
| 	// lru_code := shape_lru_hash_og( transmute(string) buffer[: sice_end_offset ] ) | ||||
|  | ||||
| 	shape_cache := & ctx.shape_cache | ||||
| 	state       := & ctx.shape_cache.state | ||||
|  | ||||
| 	shape_cache_idx := LRU_get( state, hash ) | ||||
| 	shape_cache_idx := LRU_get( state, lru_code ) | ||||
| 	if shape_cache_idx == -1 | ||||
| 	{ | ||||
| 		if shape_cache.next_cache_id < i32(state.capacity) { | ||||
| 			shape_cache_idx            = shape_cache.next_cache_id | ||||
| 			shape_cache.next_cache_id += 1 | ||||
| 			evicted := LRU_put( state, hash, shape_cache_idx ) | ||||
| 			assert( evicted == hash ) | ||||
| 			evicted := LRU_put( state, lru_code, shape_cache_idx ) | ||||
| 			assert( evicted == lru_code ) | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| @@ -53,16 +87,17 @@ shape_text_cached :: proc( ctx : ^Context, font : FontID, text_utf8 : string, en | ||||
| 			shape_cache_idx = LRU_peek( state, next_evict_idx, must_find = true ) | ||||
| 			assert( shape_cache_idx != - 1 ) | ||||
|  | ||||
| 			LRU_put( state, hash, shape_cache_idx ) | ||||
| 			LRU_put( state, lru_code, shape_cache_idx ) | ||||
| 		} | ||||
|  | ||||
| 		shape_text_uncached( ctx, font, text_utf8, entry, & shape_cache.storage[ shape_cache_idx ] ) | ||||
| 		shape_entry := & shape_cache.storage[ shape_cache_idx ] | ||||
| 		shape_entry.storage_hash = lru_code | ||||
| 		shape_text_uncached( ctx, font, text_utf8, entry, shape_entry ) | ||||
| 	} | ||||
|  | ||||
| 	return & shape_cache.storage[ shape_cache_idx ] | ||||
| } | ||||
|  | ||||
| // TODO(Ed): Make position rounding an option | ||||
| shape_text_uncached :: proc( ctx : ^Context, font : FontID, text_utf8 : string, entry : ^Entry, output : ^ShapedText ) | ||||
| { | ||||
| 	// profile(#procedure) | ||||
| @@ -71,15 +106,21 @@ shape_text_uncached :: proc( ctx : ^Context, font : FontID, text_utf8 : string, | ||||
|  | ||||
| 	use_full_text_shape := ctx.text_shape_adv | ||||
|  | ||||
| 	clear_draw_list( & output.draw_list ) | ||||
| 	clear( & output.glyphs ) | ||||
| 	clear( & output.positions ) | ||||
|  | ||||
| 	ascent, descent, line_gap := parser_get_font_vertical_metrics( & entry.parser_info ) | ||||
| 	ascent_i32, descent_i32, line_gap_i32 := parser_get_font_vertical_metrics( & entry.parser_info ) | ||||
| 	ascent      := f32(ascent_i32) | ||||
| 	descent     := f32(descent_i32) | ||||
| 	line_gap    := f32(line_gap_i32) | ||||
| 	line_height := (ascent - descent + line_gap) * entry.size_scale | ||||
|  | ||||
| 	if use_full_text_shape | ||||
| 	{ | ||||
| 		// assert( entry.shaper_info != nil ) | ||||
| 		shaper_shape_from_text( & ctx.shaper_ctx, & entry.shaper_info, output, text_utf8, ascent, descent, line_gap, entry.size, entry.size_scale ) | ||||
| 		shaper_shape_from_text( & ctx.shaper_ctx, & entry.shaper_info, output, text_utf8, ascent_i32, descent_i32, line_gap_i32, entry.size, entry.size_scale ) | ||||
| 		// TODO(Ed): Need to be able to provide the text height as well | ||||
| 		return | ||||
| 	} | ||||
| 	else | ||||
| @@ -87,13 +128,10 @@ shape_text_uncached :: proc( ctx : ^Context, font : FontID, text_utf8 : string, | ||||
| 		// Note(Original Author): | ||||
| 		// We use our own fallback dumbass text shaping. | ||||
| 		// WARNING: PLEASE USE HARFBUZZ. GOOD TEXT SHAPING IS IMPORTANT FOR INTERNATIONALISATION. | ||||
| 		ascent   := f32(ascent) | ||||
| 		descent  := f32(descent) | ||||
| 		line_gap := f32(line_gap) | ||||
|  | ||||
| 		position           : Vec2 | ||||
| 		advance            : i32 = 0 | ||||
| 		to_left_side_glyph : i32 = 0 | ||||
| 		line_count     : int = 1 | ||||
| 		max_line_width : f32 = 0 | ||||
| 		position       : Vec2 | ||||
|  | ||||
| 		prev_codepoint : rune | ||||
| 		for codepoint in text_utf8 | ||||
| @@ -104,29 +142,34 @@ shape_text_uncached :: proc( ctx : ^Context, font : FontID, text_utf8 : string, | ||||
| 			} | ||||
| 			if codepoint == '\n' | ||||
| 			{ | ||||
| 				position.x  = 0.0 | ||||
| 				position.y -= (ascent - descent + line_gap) * entry.size_scale | ||||
| 				position.y  = ceil(position.y) | ||||
| 				line_count    += 1 | ||||
| 				max_line_width = max(max_line_width, position.x) | ||||
| 				position.x     = 0.0 | ||||
| 				position.y    -= line_height | ||||
| 				position.y     = ceil(position.y) | ||||
| 				prev_codepoint = rune(0) | ||||
| 				continue | ||||
| 			} | ||||
| 			if abs( entry.size ) <= Advance_Snap_Smallfont_Size { | ||||
| 				position.x = math.ceil( position.x ) | ||||
| 				position.x = ceil( position.x ) | ||||
| 			} | ||||
|  | ||||
| 			append( & output.glyphs, parser_find_glyph_index( & entry.parser_info, codepoint )) | ||||
| 			advance, to_left_side_glyph = parser_get_codepoint_horizontal_metrics( & entry.parser_info, codepoint ) | ||||
| 			advance, _ := parser_get_codepoint_horizontal_metrics( & entry.parser_info, codepoint ) | ||||
|  | ||||
| 			append( & output.positions, Vec2 { | ||||
| 				ceil(position.x), | ||||
| 				position.y | ||||
| 			}) | ||||
| 			// append( & output.positions, position ) | ||||
|  | ||||
| 			position.x    += f32(advance) * entry.size_scale | ||||
| 			prev_codepoint = codepoint | ||||
| 		} | ||||
|  | ||||
| 		output.end_cursor_pos = position | ||||
| 		max_line_width        = max(max_line_width, position.x) | ||||
|  | ||||
| 		output.size.x = max_line_width | ||||
| 		output.size.y = f32(line_count) * line_height | ||||
| 	} | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user