VEFontCache: Remove draw_cached_glyph, formatting

No longer needed as draw_text_batched handles it
This commit is contained in:
Edward R. Gonzalez 2024-06-29 20:33:07 -04:00
parent 93e43c2b19
commit a666300f41
7 changed files with 70 additions and 164 deletions

View File

@ -53,13 +53,11 @@ pool_list_init :: proc( pool : ^PoolList, capacity : u32, dbg_name : string = ""
back = -1
}
pool_list_free :: proc( pool : ^PoolList )
{
pool_list_free :: proc( pool : ^PoolList ) {
// TODO(Ed): Implement
}
pool_list_reload :: proc( pool : ^PoolList, allocator : Allocator )
{
pool_list_reload :: proc( pool : ^PoolList, allocator : Allocator ) {
reload_array( & pool.items, allocator )
reload_array( & pool.free_list, allocator )
}
@ -175,23 +173,15 @@ LRU_init :: proc( cache : ^LRU_Cache, capacity : u32, dbg_name : string = "" ) {
pool_list_init( & cache.key_queue, capacity, dbg_name = dbg_name )
}
LRU_free :: proc( cache : ^LRU_Cache )
{
LRU_free :: proc( cache : ^LRU_Cache ) {
// TODO(Ed): Implement
}
LRU_reload :: #force_inline proc( cache : ^LRU_Cache, allocator : Allocator )
{
LRU_reload :: #force_inline proc( cache : ^LRU_Cache, allocator : Allocator ) {
reload_map( & cache.table, allocator )
pool_list_reload( & cache.key_queue, allocator )
}
LRU_hash_key :: #force_inline proc( key : u64 ) -> ( hash : u64 ) {
bytes := transmute( [8]byte ) key
hash = fnv64a( bytes[:] )
return
}
LRU_find :: #force_inline proc "contextless" ( cache : ^LRU_Cache, key : u64, must_find := false ) -> (LRU_Link, bool) {
link, success := cache.table[key]
return link, success
@ -205,8 +195,7 @@ LRU_get :: #force_inline proc( cache: ^LRU_Cache, key : u64 ) -> i32 {
return -1
}
LRU_get_next_evicted :: #force_inline proc ( cache : ^LRU_Cache ) -> u64
{
LRU_get_next_evicted :: #force_inline proc ( cache : ^LRU_Cache ) -> u64 {
if cache.key_queue.size >= cache.capacity {
evict := pool_list_peek_back( & cache.key_queue )
return evict

View File

@ -6,9 +6,16 @@ Its original purpose was for use in game engines, however its rendeirng quality
See: [docs/Readme.md](docs/Readme.md) for the library's interface
## Changes from orignal
* Font Parser & Glyph shaper are abstracted to their own interface
* Font face parser info encapsulated in parser_info struct.
* ve_fontcache_loadfile not ported (ust use core:os or os2, then call load_font)
* Macro defines have been coverted (mostly) to runtime parameters
## TODOs
### (Making it a more idiomatic library):
### Thirdparty support:
* Setup freetype, harfbuzz, depedency management within the library
@ -28,6 +35,7 @@ See: [docs/Readme.md](docs/Readme.md) for the library's interface
* Support for harfbuzz
* Ability to set a draw transform, viewport and projection
* By default the library's position is in unsigned normalized render space
* Could implement a similar design to sokol_gp's interface
* Allow curve_quality to be set on a per-font basis
### Optimization:
@ -43,9 +51,10 @@ See: [docs/Readme.md](docs/Readme.md) for the library's interface
* glyph_draw_buffer
* shape_cache
* This would need to converge to the singlar draw_list on a per layer basis (then user reqeusts a draw_list layer there could a yield to wait for the jobs to finish); if the interface expects the user to issue the commands single-threaded unless, we just assume the user is going to feed the gpu the commands & data through separate threads as well (not ideal ux).
* How the contexts are given jobs should be left up to the user (can recommend a screen quadrant based approach in demo examples)
Failed Attempts:
* Attempted to chunk the text to more granular 'shapes' from `draw_list` before doing the actual call to `draw_text_shape`. This lead to a larger performance cost due to the additional iteration across the text string.
* Attempted to cache the shape draw_list for future calls. Led to larger performance cost due to additional iteration in the `merge_draw_list`.
* Attempted to cache the shape draw_list for future calls. Led to larger performance cost due to additional iteration in the `merge_draw_list`.
* The shapes glyphs must still be traversed to identify if the glyph is cached. This arguably could be handled in `shape_text_uncached`, however that would require a significan't amount of refactoring to identify... (and would be more unergonomic when shapers libs are processing the text)

View File

@ -1,14 +1,11 @@
/*
A port of (https://github.com/hypernewbie/VEFontCache) to Odin.
Status:
This port is heavily tied to the grime package in SectrPrototype.
Changes:
- Font Parser & Glyph Shaper are abstracted to their own interface
- Font Face parser info stored separately from entries
- ve_fontcache_loadfile not ported (just use odin's core:os or os2), then call load_font
- Macro defines have been made into runtime parameters
- Macro defines have been made (mostly) into runtime parameters
*/
package VEFontCache

View File

@ -15,6 +15,8 @@ Freetype implementation supports specifying a FT_Memory handle which is a pointe
};
```
This library (seems) to perform best if the text commands are fed in 'whitespace aware chunks', where instead of feeding it entire blobs of text, the user identfies the "words" in the text and feeding the visible and whitespce chunks derived from this to draw_text as separate calls. This improves the caching of the text shapes. The downside is there has to be a time where the text is parsed into tokens beforehand so that the this iteration does not have to occur continously.
### startup
Initializes a provided context.

View File

@ -106,23 +106,8 @@ cache_glyph :: proc(ctx : ^Context, font : FontID, glyph_index : Glyph, entry :
path := &ctx.temp_path
clear(path)
append_bezier_curve :: #force_inline proc(path: ^[dynamic]Vertex, p0, p1, p2: Vec2, quality: u32) {
step := 1.0 / f32(quality)
for index := u32(1); index <= quality; index += 1 {
alpha := f32(index) * step
append( path, Vertex { pos = eval_point_on_bezier3(p0, p1, p2, alpha) } )
}
}
append_bezier_curve_cubic :: #force_inline proc(path: ^[dynamic]Vertex, p0, p1, p2, p3: Vec2, quality: u32) {
step := 1.0 / f32(quality)
for index := u32(1); index <= quality; index += 1 {
alpha := f32(index) * step
append( path, Vertex { pos = eval_point_on_bezier4(p0, p1, p2, p3, alpha) } )
}
}
for edge in shape do #partial switch edge.type {
for edge in shape do #partial switch edge.type
{
case .Move:
if len(path) > 0 {
draw_filled_path(&ctx.draw_list, outside, path[:], scale, translate, ctx.debug_print_verbose)
@ -138,7 +123,11 @@ cache_glyph :: proc(ctx : ^Context, font : FontID, glyph_index : Glyph, entry :
p0 := path[ len(path) - 1].pos
p1 := Vec2{ f32(edge.contour_x0), f32(edge.contour_y0) }
p2 := Vec2{ f32(edge.x), f32(edge.y) }
append_bezier_curve( path, p0, p1, p2, ctx.curve_quality )
step := 1.0 / f32(quality)
for index := u32(1); index <= quality; index += 1 {
alpha := f32(index) * step
append( path, Vertex { pos = eval_point_on_bezier3(p0, p1, p2, alpha) } )
}
case .Cubic:
assert( len(path) > 0)
@ -146,7 +135,11 @@ cache_glyph :: proc(ctx : ^Context, font : FontID, glyph_index : Glyph, entry :
p1 := Vec2{ f32(edge.contour_x0), f32(edge.contour_y0) }
p2 := Vec2{ f32(edge.contour_x1), f32(edge.contour_y1) }
p3 := Vec2{ f32(edge.x), f32(edge.y) }
append_bezier_curve_cubic( path, p0, p1, p2, p3, ctx.curve_quality )
step := 1.0 / f32(quality)
for index := u32(1); index <= quality; index += 1 {
alpha := f32(index) * step
append( path, Vertex { pos = eval_point_on_bezier4(p0, p1, p2, p3, alpha) } )
}
}
if len(path) > 0 {
@ -405,90 +398,6 @@ directly_draw_massive_glyph :: proc( ctx : ^Context,
append( & ctx.draw_list.calls, ..calls[:] )
}
draw_cached_glyph :: proc( ctx : ^Context, shaped : ^ShapedText,
entry : ^Entry,
glyph_index : Glyph,
lru_code : u64,
atlas_index : i32,
bounds_0, bounds_1 : Vec2,
region_kind : AtlasRegionKind,
region : ^AtlasRegion,
over_sample : Vec2,
position, scale : Vec2
) -> b32
{
// profile(#procedure)
bounds_size := Vec2 {
f32(bounds_1.x - bounds_0.x),
f32(bounds_1.y - bounds_0.y),
}
// E region is special case and not cached to atlas
if region_kind == .E
{
directly_draw_massive_glyph( ctx, entry, glyph_index, bounds_0, bounds_1, bounds_size, over_sample, position, scale )
return true
}
// Is this codepoint cached?
if atlas_index == - 1 {
return false
}
atlas := & ctx.atlas
atlas_size := Vec2 { f32(atlas.width), f32(atlas.height) }
glyph_padding := f32(atlas.glyph_padding)
// Figure out the source bounding box in the atlas texture
slot_position, _ := atlas_bbox( atlas, region_kind, atlas_index )
glyph_scale := bounds_size * entry.size_scale + glyph_padding
bounds_0_scaled := bounds_0 * entry.size_scale //- { 0.5, 0.5 }
bounds_0_scaled = ceil(bounds_0_scaled)
dst := position + (bounds_0_scaled - glyph_padding) * scale
dst_scale := glyph_scale * scale
textspace_x_form( & slot_position, & glyph_scale, atlas_size )
// Shape call setup
when 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 )
}
else
{
// Add the glyph drawcall
call := DrawCall_Default
{
using call
pass = .Target
colour = ctx.colour
start_index = cast(u32) len(ctx.draw_list.indices)
blit_quad( & ctx.draw_list,
dst, dst + dst_scale,
slot_position, slot_position + glyph_scale )
end_index = cast(u32) len(ctx.draw_list.indices)
}
append( & ctx.draw_list.calls, call )
}
return true
}
// Constructs a triangle fan to fill a shape using the provided path
// outside_point represents the center point of the fan.
//
@ -551,49 +460,50 @@ draw_text_batch :: proc(ctx: ^Context, entry: ^Entry, shaped: ^ShapedText,
for index := batch_start_idx; index < batch_end_idx; index += 1
{
glyph_index := shaped.glyphs[index]
glyph_index := shaped.glyphs[index]
if glyph_index == 0 || parser_is_glyph_empty( & entry.parser_info, glyph_index) do continue
if glyph_index == 0 || parser_is_glyph_empty( & entry.parser_info, 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 := 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 := Vec2 { vbounds_1.x - vbounds_0.x, vbounds_1.y - vbounds_0.y }
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 := Vec2 { vbounds_1.x - vbounds_0.x, vbounds_1.y - vbounds_0.y }
shaped_position := shaped.positions[index]
glyph_translate := position + shaped_position * scale
shaped_position := shaped.positions[index]
glyph_translate := position + shaped_position * scale
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
{
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 )
dst := glyph_translate + (bounds_0_scaled - glyph_padding) * scale
dst_scale := glyph_scale * scale
textspace_x_form( & slot_position, & 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
{
// Draw cacxhed 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 )
dst := glyph_translate + (bounds_0_scaled - glyph_padding) * scale
dst_scale := glyph_scale * scale
textspace_x_form( & slot_position, & glyph_scale, atlas_size )
call := DrawCall_Default
call.pass = .Target
call.colour = ctx.colour
call.start_index = u32(len(ctx.draw_list.indices))
call := DrawCall_Default
call.pass = .Target
call.colour = ctx.colour
call.start_index = u32(len(ctx.draw_list.indices))
blit_quad(&ctx.draw_list,
dst, dst + dst_scale,
slot_position, slot_position + glyph_scale )
blit_quad(&ctx.draw_list,
dst, dst + dst_scale,
slot_position, slot_position + glyph_scale )
call.end_index = u32(len(ctx.draw_list.indices))
append(&ctx.draw_list.calls, call)
}
call.end_index = u32(len(ctx.draw_list.indices))
append(&ctx.draw_list.calls, call)
}
}
}

View File

@ -120,7 +120,7 @@ textspace_x_form :: #force_inline proc "contextless" ( position, scale : ^Vec2,
}
}
Use_SIMD_For_Bezier_Ops :: true
Use_SIMD_For_Bezier_Ops :: false
when ! Use_SIMD_For_Bezier_Ops
{

View File

@ -73,8 +73,7 @@ parser_init :: proc( ctx : ^ParserContext )
// assert( error == .None, "VEFontCache.parser_init: Failed to allocate fonts array" )
}
parser_shutdown :: proc( ctx : ^ParserContext )
{
parser_shutdown :: proc( ctx : ^ParserContext ) {
// TODO(Ed): Implement
}