VEFontCache: Remove draw_cached_glyph, formatting
No longer needed as draw_text_batched handles it
This commit is contained in:
parent
93e43c2b19
commit
a666300f41
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
{
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user