More codepath simplification, convert lru to track u32
This commit is contained in:
parent
c4c3bba20c
commit
f6e7741bf3
@ -7,7 +7,7 @@ The choice was made to keep the LRU cache implementation as close to the origina
|
||||
import "base:runtime"
|
||||
|
||||
Pool_ListIter :: i32
|
||||
Pool_ListValue :: u64
|
||||
Pool_ListValue :: u32
|
||||
|
||||
Pool_List_Item :: struct {
|
||||
prev : Pool_ListIter,
|
||||
@ -183,14 +183,14 @@ LRU_Link :: struct {
|
||||
LRU_Cache :: struct {
|
||||
capacity : i32,
|
||||
num : i32,
|
||||
table : map[u64]LRU_Link,
|
||||
table : map[u32]LRU_Link,
|
||||
key_queue : Pool_List,
|
||||
}
|
||||
|
||||
lru_init :: proc( cache : ^LRU_Cache, capacity : i32, dbg_name : string = "" ) {
|
||||
error : Allocator_Error
|
||||
cache.capacity = capacity
|
||||
cache.table, error = make( map[u64]LRU_Link, uint(capacity) )
|
||||
cache.table, error = make( map[u32]LRU_Link, 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 )
|
||||
@ -212,12 +212,12 @@ lru_clear :: proc ( cache : ^LRU_Cache ) {
|
||||
cache.num = 0
|
||||
}
|
||||
|
||||
lru_find :: #force_inline proc "contextless" ( cache : LRU_Cache, key : u64, must_find := false ) -> (LRU_Link, bool) {
|
||||
lru_find :: #force_inline proc "contextless" ( cache : LRU_Cache, key : u32, must_find := false ) -> (LRU_Link, bool) {
|
||||
link, success := cache.table[key]
|
||||
return link, success
|
||||
}
|
||||
|
||||
lru_get :: #force_inline proc ( cache: ^LRU_Cache, key : u64 ) -> i32 #no_bounds_check {
|
||||
lru_get :: #force_inline proc ( cache: ^LRU_Cache, key : u32 ) -> i32 #no_bounds_check {
|
||||
if link, ok := &cache.table[ key ]; ok {
|
||||
pool_list_move_to_front(&cache.key_queue, link.ptr)
|
||||
return link.value
|
||||
@ -225,15 +225,15 @@ lru_get :: #force_inline proc ( cache: ^LRU_Cache, key : u64 ) -> i32 #no_bounds
|
||||
return -1
|
||||
}
|
||||
|
||||
lru_get_next_evicted :: #force_inline proc ( cache : LRU_Cache ) -> u64 {
|
||||
lru_get_next_evicted :: #force_inline proc ( cache : LRU_Cache ) -> u32 {
|
||||
if cache.key_queue.size >= cache.capacity {
|
||||
evict := pool_list_peek_back( cache.key_queue )
|
||||
return evict
|
||||
}
|
||||
return 0xFFFFFFFFFFFFFFFF
|
||||
return 0xFFFFFFFF
|
||||
}
|
||||
|
||||
lru_peek :: #force_inline proc "contextless" ( cache : LRU_Cache, key : u64, must_find := false ) -> i32 {
|
||||
lru_peek :: #force_inline proc "contextless" ( cache : LRU_Cache, key : u32, must_find := false ) -> i32 {
|
||||
iter, success := lru_find( cache, key, must_find )
|
||||
if success == false {
|
||||
return -1
|
||||
@ -241,7 +241,7 @@ lru_peek :: #force_inline proc "contextless" ( cache : LRU_Cache, key : u64, mus
|
||||
return iter.value
|
||||
}
|
||||
|
||||
lru_put :: #force_inline proc( cache : ^LRU_Cache, key : u64, value : i32 ) -> u64
|
||||
lru_put :: #force_inline proc( cache : ^LRU_Cache, key : u32, value : i32 ) -> u32
|
||||
{
|
||||
// profile(#procedure)
|
||||
if link, ok := & cache.table[ key ]; ok {
|
||||
@ -266,7 +266,7 @@ lru_put :: #force_inline proc( cache : ^LRU_Cache, key : u64, value : i32 ) -> u
|
||||
return evict
|
||||
}
|
||||
|
||||
lru_refresh :: proc( cache : ^LRU_Cache, key : u64 ) {
|
||||
lru_refresh :: proc( cache : ^LRU_Cache, key : u32 ) {
|
||||
link, success := lru_find( cache ^, key )
|
||||
pool_list_erase( & cache.key_queue, link.ptr )
|
||||
pool_list_push_front( & cache.key_queue, key )
|
||||
|
@ -68,7 +68,7 @@ atlas_decide_region :: #force_inline proc "contextless" (atlas : Atlas, glyph_bu
|
||||
}
|
||||
|
||||
// Grab an atlas LRU cache slot.
|
||||
atlas_reserve_slot :: #force_inline proc ( region : ^Atlas_Region, lru_code : u64 ) -> (atlas_index : i32)
|
||||
atlas_reserve_slot :: #force_inline proc ( region : ^Atlas_Region, lru_code : u32 ) -> (atlas_index : i32)
|
||||
{
|
||||
if region.next_idx < region.state.capacity
|
||||
{
|
||||
@ -80,7 +80,7 @@ atlas_reserve_slot :: #force_inline proc ( region : ^Atlas_Region, lru_code : u6
|
||||
else
|
||||
{
|
||||
next_evict_codepoint := lru_get_next_evicted( region.state )
|
||||
assert( next_evict_codepoint != 0xFFFFFFFFFFFFFFFF )
|
||||
assert( next_evict_codepoint != 0xFFFFFFFF)
|
||||
|
||||
atlas_index = lru_peek( region.state, next_evict_codepoint, must_find = true )
|
||||
assert( atlas_index != -1 )
|
||||
@ -92,37 +92,3 @@ atlas_reserve_slot :: #force_inline proc ( region : ^Atlas_Region, lru_code : u6
|
||||
assert( lru_get( & region.state, lru_code ) != - 1 )
|
||||
return
|
||||
}
|
||||
|
||||
check_and_reserve_slot_in_atlas :: #force_inline proc( ctx : Context, glyph_index : Glyph,
|
||||
lru_code : u64,
|
||||
atlas_index : ^i32,
|
||||
region : ^Atlas_Region,
|
||||
) -> (found, should_cache : b8 )
|
||||
{
|
||||
profile(#procedure)
|
||||
assert( glyph_index != -1 )
|
||||
|
||||
if ctx.temp_codepoint_seen_num > i32(cap(ctx.temp_codepoint_seen)) do return
|
||||
|
||||
if (atlas_index ^) == - 1
|
||||
{
|
||||
// Check to see if we reached capacity for the atlas
|
||||
if region.next_idx > 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 )
|
||||
success : bool
|
||||
found, success = ctx.temp_codepoint_seen[next_evict_codepoint]
|
||||
assert(success != false)
|
||||
if (found) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
should_cache = true
|
||||
(atlas_index ^) = atlas_reserve_slot(region, lru_code)
|
||||
}
|
||||
|
||||
found = true
|
||||
return
|
||||
}
|
@ -32,7 +32,7 @@ Glyph_Pack_Entry :: struct #packed {
|
||||
position : Vec2,
|
||||
|
||||
index : Glyph,
|
||||
lru_code : u64,
|
||||
lru_code : u32,
|
||||
atlas_index : i32,
|
||||
in_atlas : b8,
|
||||
should_cache : b8,
|
||||
@ -270,8 +270,8 @@ cache_glyph_to_atlas :: #force_no_inline proc (
|
||||
profile(#procedure)
|
||||
|
||||
batch_x := cast(f32) glyph_buf_Batch_x ^
|
||||
buffer_padding_scaled := glyph_padding * over_sample
|
||||
buffer_bounds_scale := bounds_size_scaled * over_sample
|
||||
buffer_padding_scaled := glyph_padding * over_sample
|
||||
buffer_bounds_scale := (bounds_size_scaled) * over_sample
|
||||
|
||||
// Allocate a glyph glyph render target region (FBO)
|
||||
buffer_x_allocation := buffer_bounds_scale.x + buffer_padding_scaled.x + 2.0
|
||||
@ -284,7 +284,7 @@ cache_glyph_to_atlas :: #force_no_inline proc (
|
||||
|
||||
region_pos := region_pos
|
||||
dst_glyph_position := region_pos
|
||||
dst_glyph_size := bounds_size_scaled + glyph_padding
|
||||
dst_glyph_size := (bounds_size_scaled) + glyph_padding
|
||||
dst_size := region_size
|
||||
to_screen_space( & dst_glyph_position, & dst_glyph_size, atlas_size )
|
||||
to_screen_space( & region_pos, & dst_size, atlas_size )
|
||||
@ -334,50 +334,6 @@ cache_glyph_to_atlas :: #force_no_inline proc (
|
||||
generate_glyph_pass_draw_list( draw_list, temp_path, glyph_shape, curve_quality, bounds, glyph_transform.scale, glyph_transform.pos )
|
||||
}
|
||||
|
||||
generate_oversized_draw_list :: #force_no_inline proc( draw_list : ^Draw_List, temp_path : ^[dynamic]Vertex,
|
||||
colour : Colour,
|
||||
curve_quality : f32,
|
||||
glyph_shape : Parser_Glyph_Shape,
|
||||
bounds : Range2,
|
||||
glyph_pass_transform : Transform,
|
||||
target_quad : Glyph_Draw_Quad,
|
||||
)
|
||||
{
|
||||
profile(#procedure)
|
||||
|
||||
calls : [2]Draw_Call
|
||||
draw_to_target := & calls[0]
|
||||
{
|
||||
using draw_to_target
|
||||
pass = .Target_Uncached
|
||||
colour = colour
|
||||
start_index = u32(len(draw_list.indices))
|
||||
|
||||
blit_quad( draw_list,
|
||||
target_quad.dst_pos, target_quad.dst_pos + target_quad.dst_scale,
|
||||
target_quad.src_pos, target_quad.src_pos + target_quad.src_scale )
|
||||
|
||||
end_index = u32(len(draw_list.indices))
|
||||
}
|
||||
clear_glyph_update := & calls[1]
|
||||
{
|
||||
// Clear glyph render target (FBO)
|
||||
clear_glyph_update.pass = .Glyph
|
||||
clear_glyph_update.start_index = 0
|
||||
clear_glyph_update.end_index = 0
|
||||
clear_glyph_update.clear_before_draw = true
|
||||
}
|
||||
append( & draw_list.calls, ..calls[:] )
|
||||
|
||||
generate_glyph_pass_draw_list( draw_list, temp_path,
|
||||
glyph_shape,
|
||||
curve_quality,
|
||||
bounds,
|
||||
glyph_pass_transform.scale,
|
||||
glyph_pass_transform.pos
|
||||
)
|
||||
}
|
||||
|
||||
generate_shape_draw_list :: #force_no_inline proc( ctx : ^Context,
|
||||
entry : Entry,
|
||||
shaped : Shaped_Text,
|
||||
@ -440,11 +396,6 @@ generate_shape_draw_list :: #force_no_inline proc( ctx : ^Context,
|
||||
glyph.bounds = parser_get_bounds( entry.parser_info, glyph.index )
|
||||
glyph.bounds_scaled = { glyph.bounds.p0 * entry.size_scale, glyph.bounds.p1 * entry.size_scale }
|
||||
glyph.bounds_size = glyph.bounds.p1 - glyph.bounds.p0
|
||||
}
|
||||
for & glyph, index in glyph_pack
|
||||
{
|
||||
// glyph.bounds_target_scaled = glyph.bounds_scaled * target_scale
|
||||
|
||||
glyph.bounds_size_scaled = glyph.bounds_size * entry.size_scale
|
||||
glyph.scale = glyph.bounds_size_scaled + atlas.glyph_padding
|
||||
}
|
||||
@ -456,7 +407,6 @@ generate_shape_draw_list :: #force_no_inline proc( ctx : ^Context,
|
||||
for & glyph, index in glyph_pack
|
||||
{
|
||||
glyph.region_kind = atlas_decide_region( atlas ^, glyph_buffer_size, glyph.bounds_size_scaled )
|
||||
// glyph.over_sample = glyph_buffer.over_sample
|
||||
}
|
||||
profile_end()
|
||||
|
||||
@ -526,10 +476,10 @@ generate_shape_draw_list :: #force_no_inline proc( ctx : ^Context,
|
||||
// Quad to for drawing atlas slot to target
|
||||
glyph := & glyph_pack[id]
|
||||
quad := & glyph.draw_quad
|
||||
quad.dst_pos = glyph.position + glyph.bounds_scaled.p0 * target_scale
|
||||
quad.dst_scale = glyph.scale * target_scale
|
||||
quad.src_scale = glyph.scale
|
||||
quad.src_pos = glyph.region_pos
|
||||
quad.dst_pos = glyph.position + (glyph.bounds_scaled.p0) * target_scale
|
||||
quad.dst_scale = (glyph.scale) * target_scale
|
||||
quad.src_scale = (glyph.scale)
|
||||
quad.src_pos = (glyph.region_pos)
|
||||
to_text_space( & quad.src_pos, & quad.src_scale, atlas_size )
|
||||
}
|
||||
for id, index in sub_slice(to_cache)
|
||||
@ -538,16 +488,16 @@ generate_shape_draw_list :: #force_no_inline proc( ctx : ^Context,
|
||||
|
||||
// Quad to for drawing atlas slot to target
|
||||
quad := & glyph.draw_quad
|
||||
quad.dst_pos = glyph.position + glyph.bounds_scaled.p0 * target_scale
|
||||
quad.dst_scale = glyph.scale * target_scale
|
||||
quad.src_scale = glyph.scale
|
||||
quad.src_pos = glyph.region_pos
|
||||
quad.dst_pos = glyph.position + (glyph.bounds_scaled.p0) * target_scale
|
||||
quad.dst_scale = (glyph.scale) * target_scale
|
||||
quad.src_scale = (glyph.scale)
|
||||
quad.src_pos = (glyph.region_pos)
|
||||
to_text_space( & quad.src_pos, & quad.src_scale, atlas_size )
|
||||
|
||||
// The glyph buffer space transform for generate_glyph_pass_draw_list
|
||||
transform := & glyph.draw_transform
|
||||
transform.scale = entry.size_scale * glyph_buffer.over_sample
|
||||
transform.pos = -1 * glyph.bounds.p0 * transform.scale + atlas.glyph_padding
|
||||
transform.pos = -1 * (glyph.bounds.p0) * transform.scale + atlas.glyph_padding
|
||||
// Unlike with oversized, this cannot be finished here as its final value is dependent on glyph_buffer.batch_x allocation
|
||||
}
|
||||
for id, index in sub_slice(oversized)
|
||||
@ -601,14 +551,11 @@ generate_shape_draw_list :: #force_no_inline proc( ctx : ^Context,
|
||||
}
|
||||
|
||||
profile_begin("to_cache: caching to atlas")
|
||||
|
||||
// profile_begin("font parser shape generation")
|
||||
for id, index in sub_slice(to_cache) {
|
||||
error : Allocator_Error
|
||||
glyph_pack[id].shape, error = parser_get_glyph_shape(entry.parser_info, glyph_pack[id].index)
|
||||
assert(error == .None)
|
||||
}
|
||||
// profile_end()
|
||||
|
||||
for id, index in sub_slice(to_cache)
|
||||
{
|
||||
@ -640,21 +587,6 @@ generate_shape_draw_list :: #force_no_inline proc( ctx : ^Context,
|
||||
glyph.region_size,
|
||||
entry.curve_quality,
|
||||
)
|
||||
|
||||
// call := Draw_Call_Default
|
||||
// call.pass = .Target
|
||||
// call.colour = ctx.colour
|
||||
|
||||
// profile("glyph")
|
||||
// call.start_index = u32(len(draw_list.indices))
|
||||
|
||||
// quad := glyph_pack[id].draw_quad
|
||||
// blit_quad(draw_list,
|
||||
// quad.dst_pos, quad.dst_pos + quad.dst_scale,
|
||||
// quad.src_pos, quad.src_pos + quad.src_scale
|
||||
// )
|
||||
// call.end_index = u32(len(draw_list.indices))
|
||||
// append(& draw_list.calls, call)
|
||||
}
|
||||
flush_glyph_buffer_draw_list(draw_list, & glyph_buffer.draw_list, & glyph_buffer.clear_draw_list, & glyph_buffer.batch_x)
|
||||
|
||||
@ -667,25 +599,7 @@ generate_shape_draw_list :: #force_no_inline proc( ctx : ^Context,
|
||||
ctx.colour.r = 1.0
|
||||
ctx.colour.g = 1.0
|
||||
ctx.colour.b = 1.0
|
||||
// flush_glyph_buffer_draw_list(draw_list, & glyph_buffer.draw_list, & glyph_buffer.clear_draw_list, & glyph_buffer.batch_x)
|
||||
// generate_cached_draw_list( draw_list, glyph_pack[:], sub_slice(cached), ctx.colour )
|
||||
for id, index in sub_slice(cached)
|
||||
{
|
||||
call := Draw_Call_Default
|
||||
call.pass = .Target
|
||||
call.colour = ctx.colour
|
||||
|
||||
profile("glyph")
|
||||
call.start_index = u32(len(draw_list.indices))
|
||||
|
||||
quad := glyph_pack[id].draw_quad
|
||||
blit_quad(draw_list,
|
||||
quad.dst_pos, quad.dst_pos + quad.dst_scale,
|
||||
quad.src_pos, quad.src_pos + quad.src_scale
|
||||
)
|
||||
call.end_index = u32(len(draw_list.indices))
|
||||
append(& draw_list.calls, call)
|
||||
}
|
||||
generate_cached_draw_list( draw_list, glyph_pack[:], sub_slice(cached), ctx.colour )
|
||||
reset_batch_codepoint_state( ctx )
|
||||
profile_end()
|
||||
|
||||
@ -698,22 +612,45 @@ generate_shape_draw_list :: #force_no_inline proc( ctx : ^Context,
|
||||
|
||||
for id, index in sub_slice(oversized)
|
||||
{
|
||||
flush_glyph_buffer_draw_list(draw_list, & glyph_buffer.draw_list, & glyph_buffer.clear_draw_list, & glyph_buffer.batch_x)
|
||||
|
||||
generate_glyph_pass_draw_list( draw_list, & ctx.temp_path,
|
||||
glyph_pack[id].shape,
|
||||
entry.curve_quality,
|
||||
glyph_pack[id].bounds,
|
||||
glyph_pack[id].draw_transform.scale,
|
||||
glyph_pack[id].draw_transform.pos
|
||||
)
|
||||
|
||||
ctx.colour.r = 0.0
|
||||
ctx.colour.g = 0.0
|
||||
ctx.colour.b = 1.0
|
||||
flush_glyph_buffer_draw_list(draw_list, & glyph_buffer.draw_list, & glyph_buffer.clear_draw_list, & glyph_buffer.batch_x)
|
||||
|
||||
glyph := glyph_pack[id]
|
||||
generate_oversized_draw_list(
|
||||
draw_list,
|
||||
& ctx.temp_path,
|
||||
ctx.colour,
|
||||
entry.curve_quality,
|
||||
glyph.shape,
|
||||
glyph.bounds,
|
||||
glyph.draw_transform,
|
||||
glyph.draw_quad,
|
||||
)
|
||||
target_quad := glyph_pack[id].draw_quad
|
||||
|
||||
calls : [2]Draw_Call
|
||||
draw_to_target := & calls[0]
|
||||
{
|
||||
using draw_to_target
|
||||
pass = .Target_Uncached
|
||||
colour = colour
|
||||
start_index = u32(len(draw_list.indices))
|
||||
|
||||
blit_quad( draw_list,
|
||||
target_quad.dst_pos, target_quad.dst_pos + target_quad.dst_scale,
|
||||
target_quad.src_pos, target_quad.src_pos + target_quad.src_scale )
|
||||
|
||||
end_index = u32(len(draw_list.indices))
|
||||
}
|
||||
clear_glyph_update := & calls[1]
|
||||
{
|
||||
// Clear glyph render target (FBO)
|
||||
clear_glyph_update.pass = .Glyph
|
||||
clear_glyph_update.start_index = 0
|
||||
clear_glyph_update.end_index = 0
|
||||
clear_glyph_update.clear_before_draw = true
|
||||
}
|
||||
append( & draw_list.calls, ..calls[:] )
|
||||
}
|
||||
profile_end()
|
||||
|
||||
|
@ -11,6 +11,9 @@ Vec2 :: [2]f32
|
||||
Vec2i :: [2]i32
|
||||
Vec2_64 :: [2]f64
|
||||
|
||||
djb8_hash_32 :: #force_inline proc "contextless" ( hash : ^u32, bytes : []byte ) { for value in bytes do (hash^) = (( (hash^) << 8) + (hash^) ) + u32(value) }
|
||||
djb8_hash :: #force_inline proc "contextless" ( hash : ^u64, bytes : []byte ) { for value in bytes do (hash^) = (( (hash^) << 8) + (hash^) ) + u64(value) }
|
||||
|
||||
vec2_from_scalar :: #force_inline proc "contextless" ( scalar : f32 ) -> Vec2 { return { scalar, scalar }}
|
||||
vec2_64_from_vec2 :: #force_inline proc "contextless" ( v2 : Vec2 ) -> Vec2_64 { return { f64(v2.x), f64(v2.y) }}
|
||||
vec2_from_vec2i :: #force_inline proc "contextless" ( v2i : Vec2i ) -> Vec2 { return { f32(v2i.x), f32(v2i.y) }}
|
||||
@ -54,8 +57,8 @@ reload_map :: proc( self : ^map [$KeyType] $EntryType, allocator : Allocator ) {
|
||||
raw.allocator = allocator
|
||||
}
|
||||
|
||||
font_glyph_lru_code :: #force_inline proc "contextless" ( font : Font_ID, glyph_index : Glyph ) -> (lru_code : u64) {
|
||||
lru_code = u64(glyph_index) + ( ( 0x100000000 * u64(font) ) & 0xFFFFFFFF00000000 )
|
||||
font_glyph_lru_code :: #force_inline proc "contextless" ( font : Font_ID, glyph_index : Glyph ) -> (lru_code : u32) {
|
||||
lru_code = u32(glyph_index) + ( ( 0x10000 * u32(font) ) & 0xFFFF0000 )
|
||||
return
|
||||
}
|
||||
|
||||
@ -66,7 +69,7 @@ is_glyph_empty :: #force_inline proc ( ctx : ^Context, entry : ^Entry, glyph_ind
|
||||
return false
|
||||
}
|
||||
|
||||
mark_batch_codepoint_seen :: #force_inline proc "contextless" ( ctx : ^Context, lru_code : u64 ) {
|
||||
mark_batch_codepoint_seen :: #force_inline proc "contextless" ( ctx : ^Context, lru_code : u32 ) {
|
||||
ctx.temp_codepoint_seen[lru_code] = true
|
||||
ctx.temp_codepoint_seen_num += 1
|
||||
}
|
||||
@ -137,11 +140,6 @@ when ! USE_MANUAL_SIMD_FOR_BEZIER_OPS
|
||||
// ve_fontcache_eval_bezier (quadratic)
|
||||
eval_point_on_bezier3 :: #force_inline proc "contextless" ( p0, p1, p2 : Vec2, alpha : f32 ) -> Vec2
|
||||
{
|
||||
// p0 := vec2_64(p0)
|
||||
// p1 := vec2_64(p1)
|
||||
// p2 := vec2_64(p2)
|
||||
// alpha := f64(alpha)
|
||||
|
||||
weight_start := (1 - alpha) * (1 - alpha)
|
||||
weight_control := 2.0 * (1 - alpha) * alpha
|
||||
weight_end := alpha * alpha
|
||||
@ -159,12 +157,6 @@ when ! USE_MANUAL_SIMD_FOR_BEZIER_OPS
|
||||
// ve_fontcache_eval_bezier (cubic)
|
||||
eval_point_on_bezier4 :: #force_inline proc "contextless" ( p0, p1, p2, p3 : Vec2, alpha : f32 ) -> Vec2
|
||||
{
|
||||
// p0 := vec2_64(p0)
|
||||
// p1 := vec2_64(p1)
|
||||
// p2 := vec2_64(p2)
|
||||
// p3 := vec2_64(p3)
|
||||
// alpha := f64(alpha)
|
||||
|
||||
weight_start := (1 - alpha) * (1 - alpha) * (1 - alpha)
|
||||
weight_c_a := 3 * (1 - alpha) * (1 - alpha) * alpha
|
||||
weight_c_b := 3 * (1 - alpha) * alpha * alpha
|
||||
|
@ -1,149 +0,0 @@
|
||||
package vefontcache
|
||||
|
||||
Shaped_Text :: struct {
|
||||
glyphs : [dynamic]Glyph,
|
||||
positions : [dynamic]Vec2,
|
||||
end_cursor_pos : Vec2,
|
||||
size : Vec2,
|
||||
entry : ^Entry,
|
||||
font : Font_ID,
|
||||
}
|
||||
|
||||
Shaped_Text_Cache :: struct {
|
||||
storage : [dynamic]Shaped_Text,
|
||||
state : LRU_Cache,
|
||||
next_cache_id : i32,
|
||||
}
|
||||
|
||||
shape_lru_hash :: #force_inline proc "contextless" ( hash : ^u64, bytes : []byte ) {
|
||||
for value in bytes {
|
||||
(hash^) = (( (hash^) << 8) + (hash^) ) + u64(value)
|
||||
}
|
||||
}
|
||||
|
||||
ShapedTextUncachedProc :: #type proc( ctx : ^Context, font : Font_ID, text_utf8 : string, entry : Entry, output : ^Shaped_Text )
|
||||
|
||||
shaper_shape_text_cached :: #force_inline proc( ctx : ^Context, font : Font_ID, text_utf8 : string, entry : Entry, shape_text_uncached : $ShapedTextUncachedProc ) -> (shaped_text : Shaped_Text)
|
||||
{
|
||||
profile(#procedure)
|
||||
font := font
|
||||
font_bytes := slice_ptr( transmute(^byte) & font, size_of(Font_ID) )
|
||||
text_bytes := transmute( []byte) text_utf8
|
||||
|
||||
lru_code : u64
|
||||
shape_lru_hash( & lru_code, font_bytes )
|
||||
shape_lru_hash( & lru_code, text_bytes )
|
||||
|
||||
shape_cache := & ctx.shape_cache
|
||||
state := & ctx.shape_cache.state
|
||||
|
||||
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, lru_code, shape_cache_idx )
|
||||
}
|
||||
else
|
||||
{
|
||||
next_evict_idx := lru_get_next_evicted( state ^ )
|
||||
// assert( next_evict_idx != 0xFFFFFFFFFFFFFFFF )
|
||||
|
||||
shape_cache_idx = lru_peek( state ^, next_evict_idx, must_find = true )
|
||||
// assert( shape_cache_idx != - 1 )
|
||||
|
||||
lru_put( state, lru_code, shape_cache_idx )
|
||||
}
|
||||
|
||||
storage_entry := & shape_cache.storage[ shape_cache_idx ]
|
||||
shape_text_uncached( ctx, font, text_utf8, entry, storage_entry )
|
||||
|
||||
shaped_text = storage_entry ^
|
||||
return
|
||||
}
|
||||
|
||||
shaped_text = shape_cache.storage[ shape_cache_idx ]
|
||||
return
|
||||
}
|
||||
|
||||
shaper_shape_text_uncached_advanced :: #force_inline proc( ctx : ^Context, font : Font_ID, text_utf8 : string, entry : Entry, output : ^Shaped_Text )
|
||||
{
|
||||
profile(#procedure)
|
||||
assert( ctx != nil )
|
||||
assert( font >= 0 && int(font) < len(ctx.entries) )
|
||||
|
||||
clear( & output.glyphs )
|
||||
clear( & output.positions )
|
||||
|
||||
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
|
||||
|
||||
shaper_shape_from_text( & ctx.shaper_ctx, entry.parser_info, entry.shaper_info, output, text_utf8, ascent_i32, descent_i32, line_gap_i32, entry.size, entry.size_scale )
|
||||
}
|
||||
|
||||
shaper_shape_from_text_latin :: #force_inline proc( ctx : ^Context, font : Font_ID, text_utf8 : string, entry : Entry, output : ^Shaped_Text )
|
||||
{
|
||||
profile(#procedure)
|
||||
assert( ctx != nil )
|
||||
assert( font >= 0 && int(font) < len(ctx.entries) )
|
||||
|
||||
clear( & output.glyphs )
|
||||
clear( & output.positions )
|
||||
|
||||
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
|
||||
|
||||
line_count : int = 1
|
||||
max_line_width : f32 = 0
|
||||
position : Vec2
|
||||
|
||||
prev_codepoint : rune
|
||||
for codepoint, index in text_utf8
|
||||
{
|
||||
if prev_codepoint > 0 {
|
||||
kern := parser_get_codepoint_kern_advance( entry.parser_info, prev_codepoint, codepoint )
|
||||
position.x += f32(kern) * entry.size_scale
|
||||
}
|
||||
if codepoint == '\n'
|
||||
{
|
||||
line_count += 1
|
||||
max_line_width = max(max_line_width, position.x)
|
||||
position.x = 0.0
|
||||
position.y -= line_height
|
||||
position.y = position.y
|
||||
prev_codepoint = rune(0)
|
||||
continue
|
||||
}
|
||||
if abs( entry.size ) <= ctx.shaper_ctx.adv_snap_small_font_threshold {
|
||||
position.x = ceil(position.x)
|
||||
}
|
||||
|
||||
glyph_index := parser_find_glyph_index( entry.parser_info, codepoint )
|
||||
is_glyph_empty := parser_is_glyph_empty( entry.parser_info,glyph_index )
|
||||
if ! is_glyph_empty
|
||||
{
|
||||
append( & output.glyphs, glyph_index)
|
||||
append( & output.positions, Vec2 {
|
||||
floor(position.x),
|
||||
floor(position.y)
|
||||
})
|
||||
}
|
||||
|
||||
advance, _ := parser_get_codepoint_horizontal_metrics( entry.parser_info, codepoint )
|
||||
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
|
||||
}
|
@ -6,6 +6,25 @@ Note(Ed): The only reason I didn't directly use harfbuzz is because hamza exists
|
||||
import "core:c"
|
||||
import "thirdparty:harfbuzz"
|
||||
|
||||
shape_lru_code :: djb8_hash_32
|
||||
|
||||
Shaped_Text :: struct {
|
||||
glyphs : [dynamic]Glyph,
|
||||
positions : [dynamic]Vec2,
|
||||
end_cursor_pos : Vec2,
|
||||
size : Vec2,
|
||||
entry : ^Entry,
|
||||
font : Font_ID,
|
||||
}
|
||||
|
||||
Shaped_Text_Cache :: struct {
|
||||
storage : [dynamic]Shaped_Text,
|
||||
state : LRU_Cache,
|
||||
next_cache_id : i32,
|
||||
}
|
||||
|
||||
Shaper_Shape_Text_Uncached_Proc :: #type proc( ctx : ^Context, font : Font_ID, text_utf8 : string, entry : Entry, output : ^Shaped_Text )
|
||||
|
||||
Shaper_Kind :: enum {
|
||||
Naive = 0,
|
||||
Harfbuzz = 1,
|
||||
@ -185,3 +204,127 @@ shaper_shape_from_text :: #force_inline proc( ctx : ^Shaper_Context, parser_info
|
||||
output.size.y = f32(line_count) * line_height
|
||||
return
|
||||
}
|
||||
shaper_shape_text_uncached_advanced :: #force_inline proc( ctx : ^Context, font : Font_ID, text_utf8 : string, entry : Entry, output : ^Shaped_Text )
|
||||
{
|
||||
profile(#procedure)
|
||||
assert( ctx != nil )
|
||||
assert( font >= 0 && int(font) < len(ctx.entries) )
|
||||
|
||||
clear( & output.glyphs )
|
||||
clear( & output.positions )
|
||||
|
||||
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
|
||||
|
||||
shaper_shape_from_text( & ctx.shaper_ctx, entry.parser_info, entry.shaper_info, output, text_utf8, ascent_i32, descent_i32, line_gap_i32, entry.size, entry.size_scale )
|
||||
}
|
||||
|
||||
shaper_shape_from_text_latin :: #force_inline proc( ctx : ^Context, font : Font_ID, text_utf8 : string, entry : Entry, output : ^Shaped_Text )
|
||||
{
|
||||
profile(#procedure)
|
||||
assert( ctx != nil )
|
||||
assert( font >= 0 && int(font) < len(ctx.entries) )
|
||||
|
||||
clear( & output.glyphs )
|
||||
clear( & output.positions )
|
||||
|
||||
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
|
||||
|
||||
line_count : int = 1
|
||||
max_line_width : f32 = 0
|
||||
position : Vec2
|
||||
|
||||
prev_codepoint : rune
|
||||
for codepoint, index in text_utf8
|
||||
{
|
||||
if prev_codepoint > 0 {
|
||||
kern := parser_get_codepoint_kern_advance( entry.parser_info, prev_codepoint, codepoint )
|
||||
position.x += f32(kern) * entry.size_scale
|
||||
}
|
||||
if codepoint == '\n'
|
||||
{
|
||||
line_count += 1
|
||||
max_line_width = max(max_line_width, position.x)
|
||||
position.x = 0.0
|
||||
position.y -= line_height
|
||||
position.y = position.y
|
||||
prev_codepoint = rune(0)
|
||||
continue
|
||||
}
|
||||
if abs( entry.size ) <= ctx.shaper_ctx.adv_snap_small_font_threshold {
|
||||
position.x = ceil(position.x)
|
||||
}
|
||||
|
||||
glyph_index := parser_find_glyph_index( entry.parser_info, codepoint )
|
||||
is_glyph_empty := parser_is_glyph_empty( entry.parser_info,glyph_index )
|
||||
if ! is_glyph_empty
|
||||
{
|
||||
append( & output.glyphs, glyph_index)
|
||||
append( & output.positions, Vec2 {
|
||||
floor(position.x),
|
||||
floor(position.y)
|
||||
})
|
||||
}
|
||||
|
||||
advance, _ := parser_get_codepoint_horizontal_metrics( entry.parser_info, codepoint )
|
||||
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
|
||||
}
|
||||
|
||||
shaper_shape_text_cached :: #force_inline proc( ctx : ^Context, font : Font_ID, text_utf8 : string, entry : Entry, shape_text_uncached : $Shaper_Shape_Text_Uncached_Proc ) -> (shaped_text : Shaped_Text)
|
||||
{
|
||||
profile(#procedure)
|
||||
font := font
|
||||
font_bytes := slice_ptr( transmute(^byte) & font, size_of(Font_ID) )
|
||||
text_bytes := transmute( []byte) text_utf8
|
||||
|
||||
lru_code : u32
|
||||
shape_lru_code( & lru_code, font_bytes )
|
||||
shape_lru_code( & lru_code, text_bytes )
|
||||
|
||||
shape_cache := & ctx.shape_cache
|
||||
state := & ctx.shape_cache.state
|
||||
|
||||
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, lru_code, shape_cache_idx )
|
||||
}
|
||||
else
|
||||
{
|
||||
next_evict_idx := lru_get_next_evicted( state ^ )
|
||||
assert( next_evict_idx != 0xFFFFFFFF )
|
||||
|
||||
shape_cache_idx = lru_peek( state ^, next_evict_idx, must_find = true )
|
||||
assert( shape_cache_idx != - 1 )
|
||||
|
||||
lru_put( state, lru_code, shape_cache_idx )
|
||||
}
|
||||
|
||||
storage_entry := & shape_cache.storage[ shape_cache_idx ]
|
||||
shape_text_uncached( ctx, font, text_utf8, entry, storage_entry )
|
||||
|
||||
shaped_text = storage_entry ^
|
||||
return
|
||||
}
|
||||
|
||||
shaped_text = shape_cache.storage[ shape_cache_idx ]
|
||||
return
|
||||
}
|
||||
|
@ -40,7 +40,7 @@ Context :: struct {
|
||||
entries : [dynamic]Entry,
|
||||
|
||||
temp_path : [dynamic]Vertex,
|
||||
temp_codepoint_seen : map[u64]b8,
|
||||
temp_codepoint_seen : map[u32]b8,
|
||||
temp_codepoint_seen_num : i32,
|
||||
|
||||
snap_width : f32,
|
||||
@ -183,7 +183,7 @@ startup :: proc( ctx : ^Context, parser_kind : Parser_Kind = .STB_TrueType,
|
||||
temp_path, error = make( [dynamic]Vertex, len = 0, cap = temp_path_reserve )
|
||||
assert(error == .None, "VEFontCache.init : Failed to allocate temp_path")
|
||||
|
||||
temp_codepoint_seen, error = make( map[u64]b8, uint(temp_codepoint_seen_reserve) )
|
||||
temp_codepoint_seen, error = make( map[u32]b8, uint(temp_codepoint_seen_reserve) )
|
||||
assert(error == .None, "VEFontCache.init : Failed to allocate temp_path")
|
||||
|
||||
draw_list.vertices, error = make( [dynamic]Vertex, len = 0, cap = 8 * Kilobyte )
|
||||
|
@ -322,7 +322,7 @@ update :: proc( delta_time : f64 ) -> b32
|
||||
flags = frame_style_flags,
|
||||
anchor = {},
|
||||
// alignment = { 0.5, 0.5 },
|
||||
font_size = 14,
|
||||
font_size = 16,
|
||||
text_alignment = { 0.0, 0.0 },
|
||||
// corner_radii = { 0.2, 0.2, 0.2, 0.2 },
|
||||
pos = { 0, 0 },
|
||||
|
@ -201,10 +201,10 @@ push-location $path_root
|
||||
# $build_args += $flag_micro_architecture_native
|
||||
$build_args += $flag_use_separate_modules
|
||||
$build_args += $flag_thread_count + $CoreCount_Physical
|
||||
# $build_args += $flag_optimize_none
|
||||
$build_args += $flag_optimize_none
|
||||
# $build_args += $flag_optimize_minimal
|
||||
# $build_args += $flag_optimize_speed
|
||||
$build_args += $falg_optimize_aggressive
|
||||
# $build_args += $falg_optimize_aggressive
|
||||
$build_args += $flag_debug
|
||||
$build_args += $flag_pdb_name + $pdb
|
||||
$build_args += $flag_subsystem + 'windows'
|
||||
|
Loading…
Reference in New Issue
Block a user