VEFontCache: runtime bugfixes
This commit is contained in:
		| @@ -118,7 +118,7 @@ LRU_init :: proc( cache : ^LRU_Cache, capacity : u32 ) { | ||||
| 	error : AllocatorError | ||||
| 	cache.capacity     = capacity | ||||
| 	cache.table, error = make( HMapChained(LRU_Link), hmap_closest_prime( uint(capacity)) ) | ||||
| 	assert( error != .None, "VEFontCache.LRU_init : Failed to allocate cache's table") | ||||
| 	assert( error == .None, "VEFontCache.LRU_init : Failed to allocate cache's table") | ||||
|  | ||||
| 	pool_list_init( & cache.key_queue, capacity ) | ||||
| } | ||||
|   | ||||
| @@ -298,14 +298,18 @@ init :: proc( ctx : ^Context, | ||||
| 	atlas.region_d.offset.x = atlas.width / 2 | ||||
|  | ||||
| 	LRU_init( & shape_cache.state, shape_cache_params.capacity ) | ||||
|  | ||||
| 	shape_cache.storage, error = make( Array(ShapedText), u64(shape_cache_params.reserve_length) ) | ||||
| 	assert(error == .None, "VEFontCache.init : Failed to allocate shape_cache.storage") | ||||
|  | ||||
| 	for idx : u32 = 0; idx < shape_cache_params.capacity; idx += 1 { | ||||
| 		stroage_entry := & shape_cache.storage.data[idx] | ||||
| 		using stroage_entry | ||||
| 		glyphs, error = make( Array(Glyph), cast(u64) shape_cache_params.reserve_length ) | ||||
| 		assert( error != .None, "VEFontCache.init : Failed to allocate glyphs array for shape cache storage" ) | ||||
| 		assert( error == .None, "VEFontCache.init : Failed to allocate glyphs array for shape cache storage" ) | ||||
|  | ||||
| 		positions, error = make( Array(Vec2), cast(u64) shape_cache_params.reserve_length ) | ||||
| 		assert( error != .None, "VEFontCache.init : Failed to allocate positions array for shape cache storage" ) | ||||
| 		assert( error == .None, "VEFontCache.init : Failed to allocate positions array for shape cache storage" ) | ||||
| 	} | ||||
|  | ||||
| 	// Note(From original author): We can actually go over VE_FONTCACHE_GLYPHDRAW_BUFFER_BATCH batches due to smart packing! | ||||
| @@ -318,22 +322,22 @@ init :: proc( ctx : ^Context, | ||||
| 		draw_padding  = glyph_draw_params.draw_padding | ||||
|  | ||||
| 		draw_list.calls, error = make( Array(DrawCall), cast(u64) glyph_draw_params.buffer_batch * 2 ) | ||||
| 		assert( error != .None, "VEFontCache.init : Failed to allocate calls for draw_list" ) | ||||
| 		assert( error == .None, "VEFontCache.init : Failed to allocate calls for draw_list" ) | ||||
|  | ||||
| 		draw_list.indices, error = make( Array(u32), cast(u64) glyph_draw_params.buffer_batch * 2 * 6 ) | ||||
| 		assert( error != .None, "VEFontCache.init : Failed to allocate indices array for draw_list" ) | ||||
| 		assert( error == .None, "VEFontCache.init : Failed to allocate indices array for draw_list" ) | ||||
|  | ||||
| 		draw_list.vertices, error = make( Array(Vertex), cast(u64) glyph_draw_params.buffer_batch * 2 * 4 ) | ||||
| 		assert( error != .None, "VEFontCache.init : Failed to allocate vertices array for draw_list" ) | ||||
| 		assert( error == .None, "VEFontCache.init : Failed to allocate vertices array for draw_list" ) | ||||
|  | ||||
| 		clear_draw_list.calls, error = make( Array(DrawCall), cast(u64) glyph_draw_params.buffer_batch * 2 ) | ||||
| 		assert( error != .None, "VEFontCache.init : Failed to allocate calls for calls for clear_draw_list" ) | ||||
| 		assert( error == .None, "VEFontCache.init : Failed to allocate calls for calls for clear_draw_list" ) | ||||
|  | ||||
| 		clear_draw_list.indices, error = make( Array(u32), cast(u64) glyph_draw_params.buffer_batch * 2 * 4 ) | ||||
| 		assert( error != .None, "VEFontCache.init : Failed to allocate calls for indices array for clear_draw_list" ) | ||||
| 		assert( error == .None, "VEFontCache.init : Failed to allocate calls for indices array for clear_draw_list" ) | ||||
|  | ||||
| 		clear_draw_list.vertices, error = make( Array(Vertex), cast(u64) glyph_draw_params.buffer_batch * 2 * 4 ) | ||||
| 		assert( error != .None, "VEFontCache.init : Failed to allocate vertices array for clear_draw_list" ) | ||||
| 		assert( error == .None, "VEFontCache.init : Failed to allocate vertices array for clear_draw_list" ) | ||||
| 	} | ||||
|  | ||||
| 	parser_init( & parser_ctx ) | ||||
| @@ -360,6 +364,7 @@ load_font :: proc( ctx : ^Context, label : string, data : []byte, size_px : f32 | ||||
| 	assert( ctx != nil ) | ||||
| 	assert( len(data) > 0 ) | ||||
| 	using ctx | ||||
| 	context.allocator = backing | ||||
|  | ||||
| 	id : i32 = -1 | ||||
| 	for index : i32 = 0; index < i32(entries.num); index += 1 { | ||||
| @@ -398,6 +403,7 @@ unload_font :: proc( ctx : ^Context, font : FontID ) | ||||
| { | ||||
| 	assert( ctx != nil ) | ||||
| 	assert( font >= 0 && u64(font) < ctx.entries.num ) | ||||
| 	context.allocator = ctx.backing | ||||
|  | ||||
| 	using ctx | ||||
| 	entry     := & entries.data[ font ] | ||||
| @@ -642,65 +648,10 @@ cache_glyph_to_atlas :: proc( ctx : ^Context, font : FontID, glyph_index : Glyph | ||||
| 		append( & atlas.draw_list.calls, call ) | ||||
| 	} | ||||
|  | ||||
|  | ||||
| 	// Render glyph to glyph_update_FBO | ||||
| 	cache_glyph( ctx, font, glyph_index, glyph_draw_scale, glyph_draw_translate ) | ||||
| } | ||||
|  | ||||
| directly_draw_massive_glyph :: proc( ctx : ^Context, entry : ^Entry, glyph : Glyph, bounds_0 : Vec2i, bounds_width, bounds_height : u32, over_sample, position, scale : Vec2 ) | ||||
| { | ||||
| 	flush_glyph_buffer_to_atlas( ctx ) | ||||
|  | ||||
| 	// Draw un-antialiased glyph to update FBO. | ||||
| 	glyph_draw_scale     := over_sample * entry.size_scale | ||||
| 	glyph_draw_translate := - Vec2{ f32(bounds_0.x), f32(bounds_0.y)} * glyph_draw_scale + Vec2{ f32(ctx.atlas.glyph_padding), f32(ctx.atlas.glyph_padding) } | ||||
| 	screenspace_x_form( & glyph_draw_translate, & glyph_draw_scale, f32(ctx.atlas.buffer_width), f32(ctx.atlas.buffer_height) ) | ||||
|  | ||||
| 	cache_glyph( ctx, entry.id, glyph, glyph_draw_scale, glyph_draw_translate ) | ||||
|  | ||||
| 	// Figure out the source rect. | ||||
| 	glyph_position   := Vec2 {} | ||||
| 	glyph_width      := f32(bounds_width)  * entry.size_scale * over_sample.x | ||||
| 	glyph_height     := f32(bounds_height) * entry.size_scale * over_sample.y | ||||
| 	glyph_dst_width  := f32(bounds_width)  * entry.size_scale | ||||
| 	glyph_dst_height := f32(bounds_height) * entry.size_scale | ||||
| 	glyph_width      += f32(2 * ctx.atlas.glyph_padding) | ||||
| 	glyph_height     += f32(2 * ctx.atlas.glyph_padding) | ||||
|  | ||||
| 	// Figure out the destination rect. | ||||
| 	bounds_scaled := Vec2 { | ||||
| 		cast(f32) i32(f32(bounds_0.x) * entry.size_scale - 0.5), | ||||
| 		cast(f32) i32(f32(bounds_0.y) * entry.size_scale - 0.5), | ||||
| 	} | ||||
| 	dst        := position + scale * bounds_scaled | ||||
| 	dst_width  := scale.x * glyph_dst_width | ||||
| 	dst_height := scale.y * glyph_dst_height | ||||
| 	dst.x      -= scale.x * f32(ctx.atlas.glyph_padding) | ||||
| 	dst.y      -= scale.y * f32(ctx.atlas.glyph_padding) | ||||
|  | ||||
| 	glyph_size := Vec2 { glyph_width, glyph_height } | ||||
| 	textspace_x_form( & glyph_position, & glyph_size, f32(ctx.atlas.buffer_width), f32(ctx.atlas.buffer_height) ) | ||||
|  | ||||
| 	// Add the glyph drawcall. | ||||
| 	call : DrawCall | ||||
| 	{ | ||||
| 		using call | ||||
| 		pass        = .Target_Unchanged | ||||
| 		colour      = ctx.colour | ||||
| 		start_index = u32(ctx.draw_list.indices.num) | ||||
| 		blit_quad( & ctx.draw_list, dst, dst + { dst_width, dst_height }, glyph_position, glyph_position + glyph_size ) | ||||
| 		end_index   = u32(ctx.draw_list.indices.num) | ||||
| 		append( & ctx.draw_list.calls, call ) | ||||
| 	} | ||||
|  | ||||
| 	// Clear glyph_update_FBO. | ||||
| 	call.pass              = .Glyph | ||||
| 	call.start_index       = 0 | ||||
| 	call.end_index         = 0 | ||||
| 	call.clear_before_draw = true | ||||
| 	append( & ctx.draw_list.calls, call ) | ||||
| } | ||||
|  | ||||
| is_empty :: proc( ctx : ^Context, entry : ^Entry, glyph_index : Glyph ) -> b32 | ||||
| { | ||||
| 	if glyph_index == 0 do return true | ||||
|   | ||||
| @@ -89,6 +89,60 @@ clear_draw_list :: proc( draw_list : ^DrawList ) { | ||||
| 	clear( draw_list.vertices ) | ||||
| } | ||||
|  | ||||
| directly_draw_massive_glyph :: proc( ctx : ^Context, entry : ^Entry, glyph : Glyph, bounds_0 : Vec2i, bounds_width, bounds_height : u32, over_sample, position, scale : Vec2 ) | ||||
| { | ||||
| 	flush_glyph_buffer_to_atlas( ctx ) | ||||
|  | ||||
| 	// Draw un-antialiased glyph to update FBO. | ||||
| 	glyph_draw_scale     := over_sample * entry.size_scale | ||||
| 	glyph_draw_translate := - Vec2{ f32(bounds_0.x), f32(bounds_0.y)} * glyph_draw_scale + Vec2{ f32(ctx.atlas.glyph_padding), f32(ctx.atlas.glyph_padding) } | ||||
| 	screenspace_x_form( & glyph_draw_translate, & glyph_draw_scale, f32(ctx.atlas.buffer_width), f32(ctx.atlas.buffer_height) ) | ||||
|  | ||||
| 	cache_glyph( ctx, entry.id, glyph, glyph_draw_scale, glyph_draw_translate ) | ||||
|  | ||||
| 	// Figure out the source rect. | ||||
| 	glyph_position   := Vec2 {} | ||||
| 	glyph_width      := f32(bounds_width)  * entry.size_scale * over_sample.x | ||||
| 	glyph_height     := f32(bounds_height) * entry.size_scale * over_sample.y | ||||
| 	glyph_dst_width  := f32(bounds_width)  * entry.size_scale | ||||
| 	glyph_dst_height := f32(bounds_height) * entry.size_scale | ||||
| 	glyph_width      += f32(2 * ctx.atlas.glyph_padding) | ||||
| 	glyph_height     += f32(2 * ctx.atlas.glyph_padding) | ||||
|  | ||||
| 	// Figure out the destination rect. | ||||
| 	bounds_scaled := Vec2 { | ||||
| 		cast(f32) i32(f32(bounds_0.x) * entry.size_scale - 0.5), | ||||
| 		cast(f32) i32(f32(bounds_0.y) * entry.size_scale - 0.5), | ||||
| 	} | ||||
| 	dst        := position + scale * bounds_scaled | ||||
| 	dst_width  := scale.x * glyph_dst_width | ||||
| 	dst_height := scale.y * glyph_dst_height | ||||
| 	dst.x      -= scale.x * f32(ctx.atlas.glyph_padding) | ||||
| 	dst.y      -= scale.y * f32(ctx.atlas.glyph_padding) | ||||
|  | ||||
| 	glyph_size := Vec2 { glyph_width, glyph_height } | ||||
| 	textspace_x_form( & glyph_position, & glyph_size, f32(ctx.atlas.buffer_width), f32(ctx.atlas.buffer_height) ) | ||||
|  | ||||
| 	// Add the glyph drawcall. | ||||
| 	call : DrawCall | ||||
| 	{ | ||||
| 		using call | ||||
| 		pass        = .Target_Unchanged | ||||
| 		colour      = ctx.colour | ||||
| 		start_index = u32(ctx.draw_list.indices.num) | ||||
| 		blit_quad( & ctx.draw_list, dst, dst + { dst_width, dst_height }, glyph_position, glyph_position + glyph_size ) | ||||
| 		end_index   = u32(ctx.draw_list.indices.num) | ||||
| 		append( & ctx.draw_list.calls, call ) | ||||
| 	} | ||||
|  | ||||
| 	// Clear glyph_update_FBO. | ||||
| 	call.pass              = .Glyph | ||||
| 	call.start_index       = 0 | ||||
| 	call.end_index         = 0 | ||||
| 	call.clear_before_draw = true | ||||
| 	append( & ctx.draw_list.calls, call ) | ||||
| } | ||||
|  | ||||
| draw_cached_glyph :: proc( ctx : ^Context, entry : ^Entry, glyph_index : Glyph, position, scale : Vec2 ) -> b32 | ||||
| { | ||||
| 	// Glyph not in current font | ||||
|   | ||||
| @@ -78,7 +78,7 @@ parser_load_font :: proc( ctx : ParserContext, label : string, data : []byte ) - | ||||
|  | ||||
| 	error : AllocatorError | ||||
| 	font, error = set( ctx.fonts, key, ParserFontInfo {} ) | ||||
| 	assert( error != .None, "VEFontCache.parser_load_font: Failed to set a new parser font info" ) | ||||
| 	assert( error == .None, "VEFontCache.parser_load_font: Failed to set a new parser font info" ) | ||||
| 	switch ctx.kind | ||||
| 	{ | ||||
| 		case .Freetype: | ||||
| @@ -434,7 +434,7 @@ parser_convert_conic_to_cubic_freetype :: proc( vertices : Array(ParserGlyphVert | ||||
| 	scratch_arena : Arena; arena_init(& scratch_arena, scratch[:]) | ||||
|  | ||||
| 	points, error := make( Array(freetype.Vector), 256, allocator = arena_allocator( &scratch_arena) ) | ||||
| 	assert(error != .None) | ||||
| 	assert(error == .None) | ||||
|  | ||||
| 	append( & points, p0) | ||||
| 	append( & points, p1) | ||||
|   | ||||
| @@ -44,7 +44,7 @@ shaper_load_font :: proc( ctx : ^ShaperContext, label : string, data : []byte, u | ||||
|  | ||||
| 	error : AllocatorError | ||||
| 	info, error = set( ctx.infos, key, ShaperInfo {} ) | ||||
| 	assert( error != .None, "VEFontCache.parser_load_font: Failed to set a new shaper info" ) | ||||
| 	assert( error == .None, "VEFontCache.parser_load_font: Failed to set a new shaper info" ) | ||||
|  | ||||
| 	using info | ||||
| 	blob = harfbuzz.blob_create( raw_data(data), cast(c.uint) len(data), harfbuzz.Memory_Mode.READONLY, user_data, nil ) | ||||
|   | ||||
| @@ -65,7 +65,7 @@ atlas_init :: proc( ctx : ^Context, width, height : i32, num_nodes : u32 = Init_ | ||||
| { | ||||
| 	error : AllocatorError | ||||
| 	ctx.atlas, error = make( Array(AtlasNode), u64(num_nodes), dbg_name = "font atlas" ) | ||||
| 	ensure(error != AllocatorError.None, "Failed to allocate font atlas") | ||||
| 	ensure(error == AllocatorError.None, "Failed to allocate font atlas") | ||||
|  | ||||
| 	ctx.width  = width | ||||
| 	ctx.height = height | ||||
|   | ||||
| @@ -68,7 +68,7 @@ render :: proc() | ||||
| 	} | ||||
|  | ||||
| 	// learnopengl.com/In-Practice/Text-Rendering | ||||
| 	if true | ||||
| 	when false | ||||
| 	{ | ||||
| 		profile("learngl_text_render_pass") | ||||
| 		using font_provider_data | ||||
|   | ||||
| @@ -21,11 +21,6 @@ Font_TTF_Default_Chars_Padding :: 4 | ||||
| Font_Load_Use_Default_Size :: -1 | ||||
| Font_Load_Gen_ID           :: "" | ||||
| 
 | ||||
| Font_Atlas_Packing_Method :: enum u32 { | ||||
| 	Raylib_Basic  = 0, // Basic packing algo | ||||
| 	Skyeline_Rect = 1, // stb_pack_rect | ||||
| } | ||||
| 
 | ||||
| FontID  :: struct { | ||||
| 	key   : u64, | ||||
| 	label : string, | ||||
| @@ -1 +0,0 @@ | ||||
| package sectr | ||||
| @@ -1,3 +1,98 @@ | ||||
| package sectr | ||||
|  | ||||
| import "codebase:font/VEFontCache" | ||||
| import "core:os" | ||||
| import ve        "codebase:font/VEFontCache" | ||||
| import sokol_gfx "thirdparty:sokol/gfx" | ||||
|  | ||||
|  | ||||
| Font_Provider_Use_Freetype :: false | ||||
| Font_Largest_Px_Size       :: 72 | ||||
| Font_Size_Interval         :: 2 | ||||
|  | ||||
| Font_Default            :: FontID { 0, "" } | ||||
| Font_Default_Point_Size :: 18.0 | ||||
|  | ||||
| Font_Load_Use_Default_Size :: -1 | ||||
| Font_Load_Gen_ID           :: "" | ||||
|  | ||||
| FontID  :: struct { | ||||
| 	key   : u64, | ||||
| 	label : string, | ||||
| } | ||||
|  | ||||
| FontDef :: struct { | ||||
| 	path_file : string, | ||||
| 	ve_id     : ve.FontID, | ||||
| } | ||||
|  | ||||
| FontProviderData :: struct | ||||
| { | ||||
| 	ve_font_cache : ve.Context, | ||||
| 	font_cache    : HMapChained(FontDef), | ||||
|  | ||||
| 	gfx_bindings  : sokol_gfx.Bindings, | ||||
| 	gfx_pipeline  : sokol_gfx.Pipeline, | ||||
| 	gfx_v_buffer  : sokol_gfx.Buffer, | ||||
| 	gfx_uv_buffer : sokol_gfx.Buffer, | ||||
| 	gfx_sampler   : sokol_gfx.Sampler, | ||||
| } | ||||
|  | ||||
| font_provider_startup :: proc() | ||||
| { | ||||
| 	profile(#procedure) | ||||
| 	state := get_state() | ||||
|  | ||||
| 	provider_data := state.font_provider_data; using provider_data | ||||
|  | ||||
| 	error : AllocatorError | ||||
| 	font_cache, error = make( HMapChained(FontDef), hmap_closest_prime(1 * Kilo), persistent_allocator() /*dbg_name = "font_cache"*/ ) | ||||
| 	verify( error == AllocatorError.None, "Failed to allocate font_cache" ) | ||||
|  | ||||
| 	ve.init( & provider_data.ve_font_cache, allocator = persistent_slab_allocator() ) | ||||
| 	log("VEFontCached initialized") | ||||
|  | ||||
| 	// TODO(Ed): Setup sokol hookup for VEFontCache | ||||
| } | ||||
|  | ||||
| font_provider_shutdown :: proc() | ||||
| { | ||||
| 	state := get_state() | ||||
| 	provider_data := state.font_provider_data; using provider_data | ||||
|  | ||||
| 	ve.shutdown( & provider_data.ve_font_cache ) | ||||
| } | ||||
|  | ||||
|  | ||||
| font_load :: proc(path_file : string, | ||||
| 	default_size : f32    = Font_Load_Use_Default_Size, | ||||
| 	desired_id   : string = Font_Load_Gen_ID | ||||
| ) -> FontID | ||||
| { | ||||
| 	profile(#procedure) | ||||
|  | ||||
| 	logf("Loading font: %v", path_file) | ||||
| 	provider_data := & get_state().font_provider_data; using provider_data | ||||
|  | ||||
| 	font_data, read_succeded : = os.read_entire_file( path_file ) | ||||
| 	verify( b32(read_succeded), str_fmt("Failed to read font file for: %v", path_file) ) | ||||
| 	font_data_size := cast(i32) len(font_data) | ||||
|  | ||||
| 	desired_id := desired_id | ||||
| 	// Use file name as key | ||||
| 	if len(desired_id) == 0 { | ||||
| 		// NOTE(Ed): This should never be used except for laziness so I'll be throwing a warning everytime. | ||||
| 		log("desired_key not provided, using file name. Give it a proper name!", LogLevel.Warning) | ||||
| 		// desired_id = cast(FontID) file_name_from_path(path_file) | ||||
| 		desired_id = file_name_from_path(path_file) | ||||
| 	} | ||||
|  | ||||
| 	key            := cast(u64) crc32( transmute([]byte) desired_id ) | ||||
| 	def, set_error := hmap_chained_set(font_cache, key, FontDef{}) | ||||
| 	verify( set_error == AllocatorError.None, "Failed to add new font entry to cache" ) | ||||
|  | ||||
| 	// TODO(Ed): Load even sizes from 8px to upper bound. | ||||
| 	def.ve_id = ve.load_font( & provider_data.ve_font_cache, desired_id, font_data, default_size ) | ||||
|  | ||||
| 	fid := FontID { key, desired_id } | ||||
| 	return fid | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user