Got render_list proper setup for UI_State rendering...
Still need a spacial indexing method to prevent redundant enqueues to the drawlist (esp for text) I'm in the process of trying to see if I can keep getting vefontcache even more performant... Making it multi-threaded can help but there might be some single-thread per still possible..
This commit is contained in:
parent
c7d465d06e
commit
5559d62826
@ -139,7 +139,7 @@ pool_list_erase :: proc( pool : ^Pool_List, iter : Pool_ListIter )
|
||||
}
|
||||
}
|
||||
|
||||
pool_list_move_to_front :: #force_inline proc( pool : ^Pool_List, iter : Pool_ListIter )
|
||||
pool_list_move_to_front :: #force_inline proc "contextless" ( pool : ^Pool_List, iter : Pool_ListIter )
|
||||
{
|
||||
using pool
|
||||
|
||||
@ -156,7 +156,7 @@ pool_list_move_to_front :: #force_inline proc( pool : ^Pool_List, iter : Pool_Li
|
||||
front = iter
|
||||
}
|
||||
|
||||
pool_list_peek_back :: #force_inline proc ( pool : ^Pool_List ) -> Pool_ListValue {
|
||||
pool_list_peek_back :: #force_inline proc ( pool : ^Pool_List ) -> Pool_ListValue #no_bounds_check {
|
||||
assert( pool.back != - 1 )
|
||||
value := pool.items[ pool.back ].value
|
||||
return value
|
||||
@ -217,7 +217,7 @@ lru_find :: #force_inline proc "contextless" ( cache : ^LRU_Cache, key : u64, mu
|
||||
return link, success
|
||||
}
|
||||
|
||||
lru_get :: #force_inline proc( cache: ^LRU_Cache, key : u64 ) -> i32 {
|
||||
lru_get :: #force_inline proc ( cache: ^LRU_Cache, key : u64 ) -> i32 #no_bounds_check {
|
||||
if link, ok := &cache.table[ key ]; ok {
|
||||
pool_list_move_to_front(&cache.key_queue, link.ptr)
|
||||
return link.value
|
||||
@ -233,7 +233,7 @@ lru_get_next_evicted :: #force_inline proc ( cache : ^LRU_Cache ) -> u64 {
|
||||
return 0xFFFFFFFFFFFFFFFF
|
||||
}
|
||||
|
||||
lru_peek :: #force_inline proc ( cache : ^LRU_Cache, key : u64, must_find := false ) -> i32 {
|
||||
lru_peek :: #force_inline proc "contextless" ( cache : ^LRU_Cache, key : u64, must_find := false ) -> i32 {
|
||||
iter, success := lru_find( cache, key, must_find )
|
||||
if success == false {
|
||||
return -1
|
||||
@ -243,6 +243,7 @@ lru_peek :: #force_inline proc ( cache : ^LRU_Cache, key : u64, must_find := fal
|
||||
|
||||
lru_put :: #force_inline proc( cache : ^LRU_Cache, key : u64, value : i32 ) -> u64
|
||||
{
|
||||
profile(#procedure)
|
||||
if link, ok := & cache.table[ key ]; ok {
|
||||
pool_list_move_to_front( & cache.key_queue, link.ptr )
|
||||
link.value = value
|
||||
|
@ -27,7 +27,7 @@ Atlas :: struct {
|
||||
width : i32,
|
||||
height : i32,
|
||||
|
||||
glyph_padding : i32, // Padding to add to bounds_<width/height>_scaled for choosing which atlas region.
|
||||
glyph_padding : f32, // Padding to add to bounds_<width/height>_scaled for choosing which atlas region.
|
||||
glyph_over_scalar : f32, // Scalar to apply to bounds_<width/height>_scaled for choosing which atlas region.
|
||||
|
||||
region_a : Atlas_Region,
|
||||
@ -36,8 +36,9 @@ Atlas :: struct {
|
||||
region_d : Atlas_Region,
|
||||
}
|
||||
|
||||
atlas_bbox :: proc( atlas : ^Atlas, region : Atlas_Region_Kind, local_idx : i32 ) -> (position, size: Vec2)
|
||||
atlas_bbox :: #force_inline proc "contextless" ( atlas : ^Atlas, region : Atlas_Region_Kind, local_idx : i32 ) -> (position, size: Vec2)
|
||||
{
|
||||
profile(#procedure)
|
||||
switch region
|
||||
{
|
||||
case .A:
|
||||
@ -85,22 +86,21 @@ atlas_bbox :: proc( atlas : ^Atlas, region : Atlas_Region_Kind, local_idx : i32
|
||||
return
|
||||
}
|
||||
|
||||
decide_codepoint_region :: proc(ctx : ^Context, entry : ^Entry, glyph_index : Glyph ) -> (region_kind : Atlas_Region_Kind, region : ^Atlas_Region, over_sample : Vec2)
|
||||
decide_codepoint_region :: #force_inline proc (ctx : ^Context, entry : ^Entry, glyph_index : Glyph ) -> (region_kind : Atlas_Region_Kind, region : ^Atlas_Region, over_sample : Vec2)
|
||||
{
|
||||
profile(#procedure)
|
||||
if parser_is_glyph_empty(&entry.parser_info, glyph_index) {
|
||||
return .None, nil, {}
|
||||
}
|
||||
|
||||
bounds_0, bounds_1 := parser_get_glyph_box(&entry.parser_info, glyph_index)
|
||||
bounds_width := f32(bounds_1.x - bounds_0.x)
|
||||
bounds_height := f32(bounds_1.y - bounds_0.y)
|
||||
bounds_size := vec2(bounds_1) - vec2(bounds_0)
|
||||
|
||||
atlas := & ctx.atlas
|
||||
glyph_buffer := & ctx.glyph_buffer
|
||||
glyph_padding := f32( atlas.glyph_padding ) * 2
|
||||
atlas := & ctx.atlas
|
||||
glyph_buffer := & ctx.glyph_buffer
|
||||
glyph_padding_dbl := atlas.glyph_padding * 2
|
||||
|
||||
bounds_width_scaled := i32(bounds_width * entry.size_scale * atlas.glyph_over_scalar + glyph_padding)
|
||||
bounds_height_scaled := i32(bounds_height * entry.size_scale * atlas.glyph_over_scalar + glyph_padding)
|
||||
bounds_size_scaled := bounds_size * entry.size_scale * atlas.glyph_over_scalar + glyph_padding_dbl
|
||||
|
||||
// Use a lookup table for faster region selection
|
||||
region_lookup := [4]struct { kind: Atlas_Region_Kind, region: ^Atlas_Region } {
|
||||
@ -110,15 +110,15 @@ decide_codepoint_region :: proc(ctx : ^Context, entry : ^Entry, glyph_index : Gl
|
||||
{ .D, & atlas.region_d },
|
||||
}
|
||||
|
||||
for region in region_lookup do if bounds_width_scaled <= region.region.width && bounds_height_scaled <= region.region.height {
|
||||
for region in region_lookup do if bounds_size_scaled.x <= f32(region.region.width) && bounds_size_scaled.y <= f32(region.region.height) {
|
||||
return region.kind, region.region, glyph_buffer.over_sample
|
||||
}
|
||||
|
||||
if bounds_width_scaled <= glyph_buffer.width \
|
||||
&& bounds_height_scaled <= glyph_buffer.height {
|
||||
if bounds_size_scaled.x <= f32(glyph_buffer.width) \
|
||||
&& bounds_size_scaled.y <= f32(glyph_buffer.height) {
|
||||
over_sample = \
|
||||
bounds_width_scaled <= glyph_buffer.width / 2 &&
|
||||
bounds_height_scaled <= glyph_buffer.height / 2 ? \
|
||||
bounds_size_scaled.x <= f32(glyph_buffer.width / 2) &&
|
||||
bounds_size_scaled.y <= f32(glyph_buffer.height / 2) ? \
|
||||
{2.0, 2.0} \
|
||||
: {1.0, 1.0}
|
||||
return .E, nil, over_sample
|
||||
|
@ -53,9 +53,9 @@ Glyph_Draw_Buffer :: struct {
|
||||
draw_list : Draw_List,
|
||||
}
|
||||
|
||||
blit_quad :: proc( draw_list : ^Draw_List, p0 : Vec2 = {0, 0}, p1 : Vec2 = {1, 1}, uv0 : Vec2 = {0, 0}, uv1 : Vec2 = {1, 1} )
|
||||
blit_quad :: #force_inline proc ( draw_list : ^Draw_List, p0 : Vec2 = {0, 0}, p1 : Vec2 = {1, 1}, uv0 : Vec2 = {0, 0}, uv1 : Vec2 = {1, 1} )
|
||||
{
|
||||
// profile(#procedure)
|
||||
profile(#procedure)
|
||||
// logf("Blitting: xy0: %0.2f, %0.2f xy1: %0.2f, %0.2f uv0: %0.2f, %0.2f uv1: %0.2f, %0.2f",
|
||||
// p0.x, p0.y, p1.x, p1.y, uv0.x, uv0.y, uv1.x, uv1.y);
|
||||
v_offset := cast(u32) len(draw_list.vertices)
|
||||
@ -89,173 +89,175 @@ blit_quad :: proc( draw_list : ^Draw_List, p0 : Vec2 = {0, 0}, p1 : Vec2 = {1, 1
|
||||
}
|
||||
|
||||
// TODO(Ed): glyph caching cannot be handled in a 'font parser' abstraction. Just going to have explicit procedures to grab info neatly...
|
||||
cache_glyph_freetype :: proc(ctx: ^Context, font: Font_ID, glyph_index: Glyph, entry: ^Entry, bounds_0, bounds_1: Vec2, scale, translate: Vec2) -> b32
|
||||
{
|
||||
draw_filled_path_freetype :: proc( draw_list : ^Draw_List, outside_point : Vec2, path : []Vertex,
|
||||
scale := Vec2 { 1, 1 },
|
||||
translate := Vec2 { 0, 0 },
|
||||
debug_print_verbose : b32 = false
|
||||
)
|
||||
{
|
||||
if debug_print_verbose {
|
||||
log("outline_path:")
|
||||
for point in path {
|
||||
vec := point.pos * scale + translate
|
||||
logf(" %0.2f %0.2f", vec.x, vec.y )
|
||||
}
|
||||
}
|
||||
// cache_glyph_freetype :: proc(ctx: ^Context, font: Font_ID, glyph_index: Glyph, entry: ^Entry, bounds_0, bounds_1: Vec2, scale, translate: Vec2) -> b32
|
||||
// {
|
||||
// draw_filled_path_freetype :: proc( draw_list : ^Draw_List, outside_point : Vec2, path : []Vertex,
|
||||
// scale := Vec2 { 1, 1 },
|
||||
// translate := Vec2 { 0, 0 },
|
||||
// debug_print_verbose : b32 = false
|
||||
// )
|
||||
// {
|
||||
// if debug_print_verbose {
|
||||
// log("outline_path:")
|
||||
// for point in path {
|
||||
// vec := point.pos * scale + translate
|
||||
// logf(" %0.2f %0.2f", vec.x, vec.y )
|
||||
// }
|
||||
// }
|
||||
|
||||
v_offset := cast(u32) len(draw_list.vertices)
|
||||
for point in path
|
||||
{
|
||||
transformed_point := Vertex {
|
||||
pos = point.pos * scale + translate,
|
||||
u = 0,
|
||||
v = 0
|
||||
}
|
||||
append( & draw_list.vertices, transformed_point )
|
||||
}
|
||||
// v_offset := cast(u32) len(draw_list.vertices)
|
||||
// for point in path
|
||||
// {
|
||||
// transformed_point := Vertex {
|
||||
// pos = point.pos * scale + translate,
|
||||
// u = 0,
|
||||
// v = 0
|
||||
// }
|
||||
// append( & draw_list.vertices, transformed_point )
|
||||
// }
|
||||
|
||||
if len(path) > 2
|
||||
{
|
||||
indices := & draw_list.indices
|
||||
for index : u32 = 1; index < cast(u32) len(path) - 1; index += 1 {
|
||||
to_add := [3]u32 {
|
||||
v_offset,
|
||||
v_offset + index,
|
||||
v_offset + index + 1
|
||||
}
|
||||
append( indices, ..to_add[:] )
|
||||
}
|
||||
// if len(path) > 2
|
||||
// {
|
||||
// indices := & draw_list.indices
|
||||
// for index : u32 = 1; index < cast(u32) len(path) - 1; index += 1 {
|
||||
// to_add := [3]u32 {
|
||||
// v_offset,
|
||||
// v_offset + index,
|
||||
// v_offset + index + 1
|
||||
// }
|
||||
// append( indices, ..to_add[:] )
|
||||
// }
|
||||
|
||||
// Close the path by connecting the last vertex to the first two
|
||||
to_add := [3]u32 {
|
||||
v_offset,
|
||||
v_offset + cast(u32)(len(path) - 1),
|
||||
v_offset + 1
|
||||
}
|
||||
append( indices, ..to_add[:] )
|
||||
}
|
||||
}
|
||||
// // Close the path by connecting the last vertex to the first two
|
||||
// to_add := [3]u32 {
|
||||
// v_offset,
|
||||
// v_offset + cast(u32)(len(path) - 1),
|
||||
// v_offset + 1
|
||||
// }
|
||||
// append( indices, ..to_add[:] )
|
||||
// }
|
||||
// }
|
||||
|
||||
if glyph_index == Glyph(0) {
|
||||
return false
|
||||
}
|
||||
// if glyph_index == Glyph(0) {
|
||||
// return false
|
||||
// }
|
||||
|
||||
face := entry.parser_info.freetype_info
|
||||
error := freetype.load_glyph(face, u32(glyph_index), {.No_Bitmap, .No_Scale})
|
||||
if error != .Ok {
|
||||
return false
|
||||
}
|
||||
// face := entry.parser_info.freetype_info
|
||||
// error := freetype.load_glyph(face, u32(glyph_index), {.No_Bitmap, .No_Scale})
|
||||
// if error != .Ok {
|
||||
// return false
|
||||
// }
|
||||
|
||||
glyph := face.glyph
|
||||
if glyph.format != .Outline {
|
||||
return false
|
||||
}
|
||||
// glyph := face.glyph
|
||||
// if glyph.format != .Outline {
|
||||
// return false
|
||||
// }
|
||||
|
||||
outline := &glyph.outline
|
||||
if outline.n_points == 0 {
|
||||
return false
|
||||
}
|
||||
// outline := &glyph.outline
|
||||
// if outline.n_points == 0 {
|
||||
// return false
|
||||
// }
|
||||
|
||||
draw := Draw_Call_Default
|
||||
draw.pass = Frame_Buffer_Pass.Glyph
|
||||
draw.start_index = cast(u32) len(ctx.draw_list.indices)
|
||||
// draw := Draw_Call_Default
|
||||
// draw.pass = Frame_Buffer_Pass.Glyph
|
||||
// draw.start_index = cast(u32) len(ctx.draw_list.indices)
|
||||
|
||||
contours := slice.from_ptr(cast( [^]i16) outline.contours, int(outline.n_contours))
|
||||
points := slice.from_ptr(cast( [^]freetype.Vector) outline.points, int(outline.n_points))
|
||||
tags := slice.from_ptr(cast( [^]u8) outline.tags, int(outline.n_points))
|
||||
// contours := slice.from_ptr(cast( [^]i16) outline.contours, int(outline.n_contours))
|
||||
// points := slice.from_ptr(cast( [^]freetype.Vector) outline.points, int(outline.n_points))
|
||||
// tags := slice.from_ptr(cast( [^]u8) outline.tags, int(outline.n_points))
|
||||
|
||||
path := &ctx.temp_path
|
||||
clear(path)
|
||||
// path := &ctx.temp_path
|
||||
// clear(path)
|
||||
|
||||
outside := Vec2{ bounds_0.x - 21, bounds_0.y - 33 }
|
||||
// outside := Vec2{ bounds_0.x - 21, bounds_0.y - 33 }
|
||||
|
||||
start_index: int = 0
|
||||
for contour_index in 0 ..< int(outline.n_contours)
|
||||
{
|
||||
end_index := int(contours[contour_index]) + 1
|
||||
prev_point : Vec2
|
||||
first_point : Vec2
|
||||
// start_index: int = 0
|
||||
// for contour_index in 0 ..< int(outline.n_contours)
|
||||
// {
|
||||
// end_index := int(contours[contour_index]) + 1
|
||||
// prev_point : Vec2
|
||||
// first_point : Vec2
|
||||
|
||||
for idx := start_index; idx < end_index; idx += 1
|
||||
{
|
||||
current_pos := Vec2 { f32( points[idx].x ), f32( points[idx].y ) }
|
||||
if ( tags[idx] & 1 ) == 0
|
||||
{
|
||||
// If current point is off-curve
|
||||
if (idx == start_index || (tags[ idx - 1 ] & 1) != 0)
|
||||
{
|
||||
// current is the first or following an on-curve point
|
||||
prev_point = current_pos
|
||||
}
|
||||
else
|
||||
{
|
||||
// current and previous are off-curve, calculate midpoint
|
||||
midpoint := (prev_point + current_pos) * 0.5
|
||||
append( path, Vertex { pos = midpoint } ) // Add midpoint as on-curve point
|
||||
if idx < end_index - 1
|
||||
{
|
||||
// perform interp from prev_point to current_pos via midpoint
|
||||
step := 1.0 / entry.curve_quality
|
||||
for alpha : f32 = 0.0; alpha <= 1.0; alpha += step
|
||||
{
|
||||
bezier_point := eval_point_on_bezier3( prev_point, midpoint, current_pos, alpha )
|
||||
append( path, Vertex{ pos = bezier_point } )
|
||||
}
|
||||
}
|
||||
// for idx := start_index; idx < end_index; idx += 1
|
||||
// {
|
||||
// current_pos := Vec2 { f32( points[idx].x ), f32( points[idx].y ) }
|
||||
// if ( tags[idx] & 1 ) == 0
|
||||
// {
|
||||
// // If current point is off-curve
|
||||
// if (idx == start_index || (tags[ idx - 1 ] & 1) != 0)
|
||||
// {
|
||||
// // current is the first or following an on-curve point
|
||||
// prev_point = current_pos
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// // current and previous are off-curve, calculate midpoint
|
||||
// midpoint := (prev_point + current_pos) * 0.5
|
||||
// append( path, Vertex { pos = midpoint } ) // Add midpoint as on-curve point
|
||||
// if idx < end_index - 1
|
||||
// {
|
||||
// // perform interp from prev_point to current_pos via midpoint
|
||||
// step := 1.0 / entry.curve_quality
|
||||
// for alpha : f32 = 0.0; alpha <= 1.0; alpha += step
|
||||
// {
|
||||
// bezier_point := eval_point_on_bezier3( prev_point, midpoint, current_pos, alpha )
|
||||
// append( path, Vertex{ pos = bezier_point } )
|
||||
// }
|
||||
// }
|
||||
|
||||
prev_point = current_pos
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if idx == start_index {
|
||||
first_point = current_pos
|
||||
}
|
||||
if prev_point != (Vec2{}) {
|
||||
// there was an off-curve point before this
|
||||
append(path, Vertex{ pos = prev_point}) // Ensure previous off-curve is handled
|
||||
}
|
||||
append(path, Vertex{ pos = current_pos})
|
||||
prev_point = {}
|
||||
}
|
||||
}
|
||||
// prev_point = current_pos
|
||||
// }
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// if idx == start_index {
|
||||
// first_point = current_pos
|
||||
// }
|
||||
// if prev_point != (Vec2{}) {
|
||||
// // there was an off-curve point before this
|
||||
// append(path, Vertex{ pos = prev_point}) // Ensure previous off-curve is handled
|
||||
// }
|
||||
// append(path, Vertex{ pos = current_pos})
|
||||
// prev_point = {}
|
||||
// }
|
||||
// }
|
||||
|
||||
// ensure the contour is closed
|
||||
if path[0].pos != path[ len(path) - 1 ].pos {
|
||||
append(path, Vertex{pos = path[0].pos})
|
||||
}
|
||||
draw_filled_path(&ctx.draw_list, bounds_0, path[:], scale, translate, ctx.debug_print_verbose)
|
||||
clear(path)
|
||||
start_index = end_index
|
||||
}
|
||||
// // ensure the contour is closed
|
||||
// if path[0].pos != path[ len(path) - 1 ].pos {
|
||||
// append(path, Vertex{pos = path[0].pos})
|
||||
// }
|
||||
// draw_filled_path(&ctx.draw_list, bounds_0, path[:], scale, translate)
|
||||
// // draw_filled_path(&ctx.draw_list, bounds_0, path[:], scale, translate, ctx.debug_print_verbose)
|
||||
// clear(path)
|
||||
// start_index = end_index
|
||||
// }
|
||||
|
||||
if len(path) > 0 {
|
||||
draw_filled_path(&ctx.draw_list, outside, path[:], scale, translate, ctx.debug_print_verbose)
|
||||
}
|
||||
// if len(path) > 0 {
|
||||
// // draw_filled_path(&ctx.draw_list, outside, path[:], scale, translate, ctx.debug_print_verbose)
|
||||
// draw_filled_path(&ctx.draw_list, outside, path[:], scale, translate)
|
||||
// }
|
||||
|
||||
draw.end_index = cast(u32) len(ctx.draw_list.indices)
|
||||
if draw.end_index > draw.start_index {
|
||||
append( & ctx.draw_list.calls, draw)
|
||||
}
|
||||
// draw.end_index = cast(u32) len(ctx.draw_list.indices)
|
||||
// if draw.end_index > draw.start_index {
|
||||
// append( & ctx.draw_list.calls, draw)
|
||||
// }
|
||||
|
||||
return true
|
||||
}
|
||||
// return true
|
||||
// }
|
||||
|
||||
// TODO(Ed): Is it better to cache the glyph vertices for when it must be re-drawn (directly or two atlas)?
|
||||
cache_glyph :: proc(ctx : ^Context, font : Font_ID, glyph_index : Glyph, entry : ^Entry, bounds_0, bounds_1 : Vec2, scale, translate : Vec2) -> b32
|
||||
{
|
||||
// profile(#procedure)
|
||||
profile(#procedure)
|
||||
if glyph_index == Glyph(0) {
|
||||
return false
|
||||
}
|
||||
|
||||
// Glyph shape handling are not abstractable between freetype and stb_truetype
|
||||
if entry.parser_info.kind == .Freetype {
|
||||
result := cache_glyph_freetype( ctx, font, glyph_index, entry, bounds_0, bounds_1, scale, translate )
|
||||
return result
|
||||
}
|
||||
// if entry.parser_info.kind == .Freetype {
|
||||
// result := cache_glyph_freetype( ctx, font, glyph_index, entry, bounds_0, bounds_1, scale, translate )
|
||||
// return result
|
||||
// }
|
||||
|
||||
shape, error := parser_get_glyph_shape(&entry.parser_info, glyph_index)
|
||||
assert(error == .None)
|
||||
@ -277,7 +279,8 @@ cache_glyph :: proc(ctx : ^Context, font : Font_ID, glyph_index : Glyph, entry :
|
||||
{
|
||||
case .Move:
|
||||
if len(path) > 0 {
|
||||
draw_filled_path(&ctx.draw_list, outside, path[:], scale, translate, ctx.debug_print_verbose)
|
||||
// draw_filled_path(&ctx.draw_list, outside, path[:], scale, translate, ctx.debug_print_verbose)
|
||||
draw_filled_path(&ctx.draw_list, outside, path[:], scale, translate)
|
||||
clear(path)
|
||||
}
|
||||
fallthrough
|
||||
@ -310,7 +313,8 @@ cache_glyph :: proc(ctx : ^Context, font : Font_ID, glyph_index : Glyph, entry :
|
||||
}
|
||||
|
||||
if len(path) > 0 {
|
||||
draw_filled_path(&ctx.draw_list, outside, path[:], scale, translate, ctx.debug_print_verbose)
|
||||
// draw_filled_path(&ctx.draw_list, outside, path[:], scale, translate, ctx.debug_print_verbose)
|
||||
draw_filled_path(&ctx.draw_list, outside, path[:], scale, translate)
|
||||
}
|
||||
|
||||
draw.end_index = u32(len(ctx.draw_list.indices))
|
||||
@ -337,7 +341,7 @@ cache_glyph_to_atlas :: proc( ctx : ^Context,
|
||||
region : ^Atlas_Region,
|
||||
over_sample : Vec2 )
|
||||
{
|
||||
// profile(#procedure)
|
||||
profile(#procedure)
|
||||
|
||||
// Get hb_font text metrics. These are unscaled!
|
||||
bounds_0, bounds_1 := parser_get_glyph_box( & entry.parser_info, glyph_index )
|
||||
@ -354,8 +358,8 @@ cache_glyph_to_atlas :: proc( ctx : ^Context,
|
||||
{
|
||||
if region.next_idx < region.state.capacity
|
||||
{
|
||||
evicted := lru_put( & region.state, lru_code, i32(region.next_idx) )
|
||||
atlas_index = i32(region.next_idx)
|
||||
evicted := lru_put( & region.state, lru_code, region.next_idx )
|
||||
atlas_index = region.next_idx
|
||||
region.next_idx += 1
|
||||
assert( evicted == lru_code )
|
||||
}
|
||||
@ -460,20 +464,19 @@ check_glyph_in_atlas :: #force_inline proc( ctx : ^Context, font : Font_ID, entr
|
||||
over_sample : Vec2
|
||||
) -> b32
|
||||
{
|
||||
// profile(#procedure)
|
||||
profile(#procedure)
|
||||
assert( glyph_index != -1 )
|
||||
|
||||
// E region can't batch
|
||||
if region_kind == .E || region_kind == .None do return false
|
||||
if ctx.temp_codepoint_seen_num > 1024 do return false
|
||||
// TODO(Ed): Why 1024?
|
||||
if region_kind == .E || region_kind == .None do return false
|
||||
if ctx.temp_codepoint_seen_num > i32(cap(ctx.temp_codepoint_seen)) do return false
|
||||
|
||||
if atlas_index == - 1
|
||||
{
|
||||
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 )
|
||||
seen, success := ctx.temp_codepoint_seen[next_evict_codepoint]
|
||||
seen, success := ctx.temp_codepoint_seen[next_evict_codepoint]
|
||||
assert(success != false)
|
||||
|
||||
if (seen) {
|
||||
@ -503,7 +506,7 @@ directly_draw_massive_glyph :: proc( ctx : ^Context,
|
||||
bounds_size : Vec2,
|
||||
over_sample, position, scale : Vec2 )
|
||||
{
|
||||
// profile(#procedure)
|
||||
profile(#procedure)
|
||||
flush_glyph_buffer_to_atlas( ctx )
|
||||
|
||||
glyph_padding := f32(ctx.atlas.glyph_padding)
|
||||
@ -567,22 +570,23 @@ directly_draw_massive_glyph :: proc( ctx : ^Context,
|
||||
// ve_fontcache_draw_filled_path
|
||||
draw_filled_path :: proc( draw_list : ^Draw_List, outside_point : Vec2, path : []Vertex,
|
||||
scale := Vec2 { 1, 1 },
|
||||
translate := Vec2 { 0, 0 },
|
||||
debug_print_verbose : b32 = false
|
||||
)
|
||||
translate := Vec2 { 0, 0 }
|
||||
// debug_print_verbose : b32 = false
|
||||
) #no_bounds_check
|
||||
{
|
||||
if debug_print_verbose
|
||||
{
|
||||
log("outline_path:")
|
||||
for point in path {
|
||||
vec := point.pos * scale + translate
|
||||
logf(" %0.2f %0.2f", vec.x, vec.y )
|
||||
}
|
||||
}
|
||||
profile(#procedure)
|
||||
// if debug_print_verbose
|
||||
// {
|
||||
// log("outline_path:")
|
||||
// for point in path {
|
||||
// vec := point.pos * scale + translate
|
||||
// logf(" %0.2f %0.2f", vec.x, vec.y )
|
||||
// }
|
||||
// }
|
||||
|
||||
v_offset := cast(u32) len(draw_list.vertices)
|
||||
for point in path {
|
||||
point := point
|
||||
point := point
|
||||
point.pos = point.pos * scale + translate
|
||||
append( & draw_list.vertices, point )
|
||||
}
|
||||
@ -613,14 +617,16 @@ draw_text_batch :: proc(ctx: ^Context, entry: ^Entry, shaped: ^Shaped_Text,
|
||||
position, scale : Vec2,
|
||||
snap_width, snap_height : f32 )
|
||||
{
|
||||
profile(#procedure)
|
||||
flush_glyph_buffer_to_atlas(ctx)
|
||||
|
||||
atlas := & ctx.atlas
|
||||
atlas_size := Vec2{ f32(atlas.width), f32(atlas.height) }
|
||||
glyph_padding := f32(atlas.glyph_padding)
|
||||
glyph_padding := atlas.glyph_padding
|
||||
|
||||
for index := batch_start_idx; index < batch_end_idx; index += 1
|
||||
{
|
||||
profile("glyph")
|
||||
glyph_index := shaped.glyphs[index]
|
||||
|
||||
if glyph_index == 0 || parser_is_glyph_empty( & entry.parser_info, glyph_index) do continue
|
||||
@ -645,39 +651,40 @@ draw_text_batch :: proc(ctx: ^Context, entry: ^Entry, shaped: ^Shaped_Text,
|
||||
}
|
||||
else if atlas_index != -1
|
||||
{
|
||||
profile("derive manual")
|
||||
call := Draw_Call_Default
|
||||
call.pass = .Target
|
||||
call.colour = ctx.colour
|
||||
call.start_index = u32(len(ctx.draw_list.indices))
|
||||
|
||||
// Draw cached 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 - 0.5 )
|
||||
dst := glyph_translate + bounds_0_scaled * scale
|
||||
dst_scale := glyph_scale * scale
|
||||
|
||||
textspace_x_form( & slot_position, & glyph_scale, atlas_size )
|
||||
|
||||
call := Draw_Call_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 )
|
||||
|
||||
call.end_index = u32(len(ctx.draw_list.indices))
|
||||
|
||||
append(&ctx.draw_list.calls, call)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Helper for draw_text, all raw text content should be confirmed to be either formatting or visible shapes before getting cached.
|
||||
draw_text_shape :: proc( ctx : ^Context,
|
||||
draw_text_shape :: #force_inline proc( ctx : ^Context,
|
||||
font : Font_ID,
|
||||
entry : ^Entry,
|
||||
shaped : ^Shaped_Text,
|
||||
position, scale : Vec2,
|
||||
snap_width, snap_height : f32
|
||||
) -> (cursor_pos : Vec2)
|
||||
) -> (cursor_pos : Vec2) #no_bounds_check
|
||||
{
|
||||
// profile(#procedure)
|
||||
profile(#procedure)
|
||||
batch_start_idx : i32 = 0
|
||||
for index : i32 = 0; index < cast(i32) len(shaped.glyphs); index += 1
|
||||
{
|
||||
@ -710,9 +717,51 @@ draw_text_shape :: proc( ctx : ^Context,
|
||||
return
|
||||
}
|
||||
|
||||
flush_glyph_buffer_to_atlas :: proc( ctx : ^Context )
|
||||
// Helper for draw_text_latin_mono
|
||||
draw_text_mono_latin_batch :: #force_inline proc( ctx : ^Context,
|
||||
font : Font_ID,
|
||||
entry : ^Entry,
|
||||
shaped : ^Shaped_Text,
|
||||
position, scale : Vec2,
|
||||
snap_width, snap_height : f32
|
||||
) -> (cursor_pos : Vec2) #no_bounds_check
|
||||
{
|
||||
// profile(#procedure)
|
||||
profile(#procedure)
|
||||
batch_start_idx : i32 = 0
|
||||
for index : i32 = 0; index < cast(i32) len(shaped.glyphs); index += 1
|
||||
{
|
||||
glyph_index := shaped.glyphs[ index ]
|
||||
if is_empty( ctx, entry, 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 := cast(i32) -1
|
||||
|
||||
if region_kind != .E do atlas_index = lru_get( & region.state, lru_code )
|
||||
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
|
||||
|
||||
// First batch the other cached glyphs
|
||||
// flush_glyph_buffer_to_atlas(ctx)
|
||||
draw_text_batch( ctx, entry, shaped, batch_start_idx, index, position, scale, snap_width, snap_height )
|
||||
reset_batch_codepoint_state( ctx )
|
||||
|
||||
cache_glyph_to_atlas( ctx, font, glyph_index, lru_code, atlas_index, entry, region_kind, region, over_sample )
|
||||
mark_batch_codepoint_seen( ctx, lru_code)
|
||||
batch_start_idx = index
|
||||
}
|
||||
|
||||
draw_text_batch( ctx, entry, shaped, batch_start_idx, cast(i32) len(shaped.glyphs), position, scale, snap_width , snap_height )
|
||||
reset_batch_codepoint_state( ctx )
|
||||
|
||||
cursor_pos = position + shaped.end_cursor_pos * scale
|
||||
return
|
||||
}
|
||||
|
||||
flush_glyph_buffer_to_atlas :: #force_inline proc( ctx : ^Context )
|
||||
{
|
||||
profile(#procedure)
|
||||
// Flush Draw_Calls to draw list
|
||||
merge_draw_list( & ctx.draw_list, & ctx.glyph_buffer.clear_draw_list )
|
||||
merge_draw_list( & ctx.draw_list, & ctx.glyph_buffer.draw_list)
|
||||
@ -733,9 +782,9 @@ flush_glyph_buffer_to_atlas :: proc( ctx : ^Context )
|
||||
}
|
||||
|
||||
// ve_fontcache_merge_Draw_List
|
||||
merge_draw_list :: proc( dst, src : ^Draw_List )
|
||||
merge_draw_list :: #force_inline proc ( #no_alias dst, src : ^Draw_List )
|
||||
{
|
||||
// profile(#procedure)
|
||||
profile(#procedure)
|
||||
error : Allocator_Error
|
||||
|
||||
v_offset := cast(u32) len( dst.vertices )
|
||||
@ -744,13 +793,13 @@ merge_draw_list :: proc( dst, src : ^Draw_List )
|
||||
assert( error == .None )
|
||||
|
||||
i_offset := cast(u32) len(dst.indices)
|
||||
for index : int = 0; index < len(src.indices); index += 1 {
|
||||
for index : i32 = 0; index < cast(i32) len(src.indices); index += 1 {
|
||||
ignored : int
|
||||
ignored, error = append( & dst.indices, src.indices[index] + v_offset )
|
||||
assert( error == .None )
|
||||
}
|
||||
|
||||
for index : int = 0; index < len(src.calls); index += 1 {
|
||||
for index : i32 = 0; index < cast(i32) len(src.calls); index += 1 {
|
||||
src_call := src.calls[ index ]
|
||||
src_call.start_index += i_offset
|
||||
src_call.end_index += i_offset
|
||||
@ -759,9 +808,9 @@ merge_draw_list :: proc( dst, src : ^Draw_List )
|
||||
}
|
||||
}
|
||||
|
||||
optimize_draw_list :: proc(draw_list: ^Draw_List, call_offset: int)
|
||||
optimize_draw_list :: proc (draw_list: ^Draw_List, call_offset: int)
|
||||
{
|
||||
// profile(#procedure)
|
||||
profile(#procedure)
|
||||
assert(draw_list != nil)
|
||||
|
||||
can_merge_draw_calls :: #force_inline proc "contextless" ( a, b : ^Draw_Call ) -> bool {
|
||||
|
@ -105,4 +105,23 @@ vec2_64 :: proc {
|
||||
vec2_64_from_vec2,
|
||||
}
|
||||
|
||||
import "../../grime"
|
||||
|
||||
DISABLE_PROFILING :: false
|
||||
|
||||
@(deferred_none = profile_end, disabled = DISABLE_PROFILING)
|
||||
profile :: #force_inline proc "contextless" ( name : string, loc := #caller_location ) {
|
||||
grime.profile_begin(name, loc)
|
||||
}
|
||||
|
||||
@(disabled = DISABLE_PROFILING)
|
||||
profile_begin :: #force_inline proc "contextless" ( name : string, loc := #caller_location ) {
|
||||
grime.profile_begin(name, loc)
|
||||
}
|
||||
|
||||
@(disabled = DISABLE_PROFILING)
|
||||
profile_end :: #force_inline proc "contextless" () {
|
||||
grime.profile_end()
|
||||
}
|
||||
|
||||
//#endregion("Proc overload mappings")
|
||||
|
@ -21,20 +21,20 @@ vec2i_from_vec2 :: #force_inline proc "contextless" ( v2 : Vec2 ) -> Vec2
|
||||
|
||||
// This buffer is used below excluisvely to prevent any allocator recusion when verbose logging from allocators.
|
||||
// This means a single line is limited to 4k buffer
|
||||
// Logger_Allocator_Buffer : [4 * Kilobyte]u8
|
||||
Logger_Allocator_Buffer : [4 * Kilobyte]u8
|
||||
|
||||
log :: proc( msg : string, level := core_log.Level.Info, loc := #caller_location ) {
|
||||
// temp_arena : Arena; arena_init(& temp_arena, Logger_Allocator_Buffer[:])
|
||||
// context.allocator = arena_allocator(& temp_arena)
|
||||
// context.temp_allocator = arena_allocator(& temp_arena)
|
||||
temp_arena : Arena; arena_init(& temp_arena, Logger_Allocator_Buffer[:])
|
||||
context.allocator = arena_allocator(& temp_arena)
|
||||
context.temp_allocator = arena_allocator(& temp_arena)
|
||||
|
||||
core_log.log( level, msg, location = loc )
|
||||
}
|
||||
|
||||
logf :: proc( fmt : string, args : ..any, level := core_log.Level.Info, loc := #caller_location ) {
|
||||
// temp_arena : Arena; arena_init(& temp_arena, Logger_Allocator_Buffer[:])
|
||||
// context.allocator = arena_allocator(& temp_arena)
|
||||
// context.temp_allocator = arena_allocator(& temp_arena)
|
||||
temp_arena : Arena; arena_init(& temp_arena, Logger_Allocator_Buffer[:])
|
||||
context.allocator = arena_allocator(& temp_arena)
|
||||
context.temp_allocator = arena_allocator(& temp_arena)
|
||||
|
||||
core_log.logf( level, fmt, ..args, location = loc )
|
||||
}
|
||||
@ -73,7 +73,7 @@ reset_batch_codepoint_state :: #force_inline proc( ctx : ^Context ) {
|
||||
|
||||
USE_F64_PRECISION_ON_X_FORM_OPS :: false
|
||||
|
||||
screenspace_x_form :: #force_inline proc "contextless" ( position, scale : ^Vec2, size : Vec2 )
|
||||
screenspace_x_form :: #force_inline proc "contextless" ( #no_alias position, scale : ^Vec2, size : Vec2 )
|
||||
{
|
||||
when USE_F64_PRECISION_ON_X_FORM_OPS
|
||||
{
|
||||
@ -101,7 +101,7 @@ screenspace_x_form :: #force_inline proc "contextless" ( position, scale : ^Vec2
|
||||
}
|
||||
}
|
||||
|
||||
textspace_x_form :: #force_inline proc "contextless" ( position, scale : ^Vec2, size : Vec2 )
|
||||
textspace_x_form :: #force_inline proc "contextless" ( #no_alias position, scale : ^Vec2, size : Vec2 )
|
||||
{
|
||||
when USE_F64_PRECISION_ON_X_FORM_OPS
|
||||
{
|
||||
@ -123,7 +123,7 @@ textspace_x_form :: #force_inline proc "contextless" ( position, scale : ^Vec2,
|
||||
}
|
||||
}
|
||||
|
||||
USE_MANUAL_SIMD_FOR_BEZIER_OPS :: false
|
||||
USE_MANUAL_SIMD_FOR_BEZIER_OPS :: true
|
||||
|
||||
when ! USE_MANUAL_SIMD_FOR_BEZIER_OPS
|
||||
{
|
||||
|
@ -113,6 +113,7 @@ parser_unload_font :: proc( font : ^Parser_Font_Info )
|
||||
|
||||
parser_find_glyph_index :: #force_inline proc "contextless" ( font : ^Parser_Font_Info, codepoint : rune ) -> (glyph_index : Glyph)
|
||||
{
|
||||
profile(#procedure)
|
||||
switch font.kind
|
||||
{
|
||||
case .Freetype:
|
||||
@ -131,7 +132,7 @@ parser_find_glyph_index :: #force_inline proc "contextless" ( font : ^Parser_Fon
|
||||
return Glyph(-1)
|
||||
}
|
||||
|
||||
parser_free_shape :: proc( font : ^Parser_Font_Info, shape : Parser_Glyph_Shape )
|
||||
parser_free_shape :: #force_inline proc( font : ^Parser_Font_Info, shape : Parser_Glyph_Shape )
|
||||
{
|
||||
switch font.kind
|
||||
{
|
||||
@ -219,10 +220,12 @@ parser_get_font_vertical_metrics :: #force_inline proc "contextless" ( font : ^P
|
||||
return
|
||||
}
|
||||
|
||||
parser_get_glyph_box :: #force_inline proc ( font : ^Parser_Font_Info, glyph_index : Glyph ) -> (bounds_0, bounds_1 : Vec2i)
|
||||
parser_get_glyph_box :: #force_inline proc "contextless" ( font : ^Parser_Font_Info, glyph_index : Glyph ) -> (bounds_0, bounds_1 : Vec2i)
|
||||
{
|
||||
profile(#procedure)
|
||||
switch font.kind
|
||||
{
|
||||
|
||||
case .Freetype:
|
||||
freetype.load_glyph( font.freetype_info, c.uint(glyph_index), { .No_Bitmap, .No_Hinting, .No_Scale } )
|
||||
|
||||
@ -234,15 +237,15 @@ parser_get_glyph_box :: #force_inline proc ( font : ^Parser_Font_Info, glyph_ind
|
||||
case .STB_TrueType:
|
||||
x0, y0, x1, y1 : i32
|
||||
success := cast(bool) stbtt.GetGlyphBox( & font.stbtt_info, i32(glyph_index), & x0, & y0, & x1, & y1 )
|
||||
assert( success )
|
||||
// assert( success )
|
||||
|
||||
bounds_0 = { i32(x0), i32(y0) }
|
||||
bounds_1 = { i32(x1), i32(y1) }
|
||||
bounds_0 = { x0, y0 }
|
||||
bounds_1 = { x1, y1 }
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
parser_get_glyph_shape :: proc( font : ^Parser_Font_Info, glyph_index : Glyph ) -> (shape : Parser_Glyph_Shape, error : Allocator_Error)
|
||||
parser_get_glyph_shape :: #force_inline proc ( font : ^Parser_Font_Info, glyph_index : Glyph ) -> (shape : Parser_Glyph_Shape, error : Allocator_Error)
|
||||
{
|
||||
switch font.kind
|
||||
{
|
||||
|
@ -1,10 +1,10 @@
|
||||
package vefontcache
|
||||
|
||||
Shaped_Text :: struct {
|
||||
glyphs : [dynamic]Glyph,
|
||||
positions : [dynamic]Vec2,
|
||||
end_cursor_pos : Vec2,
|
||||
size : Vec2,
|
||||
glyphs : [dynamic]Glyph,
|
||||
positions : [dynamic]Vec2,
|
||||
end_cursor_pos : Vec2,
|
||||
size : Vec2,
|
||||
}
|
||||
|
||||
Shaped_Text_Cache :: struct {
|
||||
@ -19,9 +19,11 @@ shape_lru_hash :: #force_inline proc "contextless" ( hash : ^u64, bytes : []byte
|
||||
}
|
||||
}
|
||||
|
||||
shape_text_cached :: proc( ctx : ^Context, font : Font_ID, text_utf8 : string, entry : ^Entry ) -> ^Shaped_Text
|
||||
ShapedTextUncachedProc :: #type proc( ctx : ^Context, font : Font_ID, text_utf8 : string, entry : ^Entry, output : ^Shaped_Text )
|
||||
|
||||
shape_text_cached :: proc( ctx : ^Context, font : Font_ID, text_utf8 : string, entry : ^Entry, shape_text_uncached : ShapedTextUncachedProc ) -> ^Shaped_Text #no_bounds_check
|
||||
{
|
||||
// profile(#procedure)
|
||||
profile(#procedure)
|
||||
font := font
|
||||
font_bytes := slice_ptr( transmute(^byte) & font, size_of(Font_ID) )
|
||||
text_bytes := transmute( []byte) text_utf8
|
||||
@ -59,9 +61,9 @@ shape_text_cached :: proc( ctx : ^Context, font : Font_ID, text_utf8 : string, e
|
||||
return & shape_cache.storage[ shape_cache_idx ]
|
||||
}
|
||||
|
||||
shape_text_uncached :: proc( ctx : ^Context, font : Font_ID, text_utf8 : string, entry : ^Entry, output : ^Shaped_Text )
|
||||
shape_text_uncached_advanced :: #force_inline proc( ctx : ^Context, font : Font_ID, text_utf8 : string, entry : ^Entry, output : ^Shaped_Text )
|
||||
{
|
||||
// profile(#procedure)
|
||||
profile(#procedure)
|
||||
assert( ctx != nil )
|
||||
assert( font >= 0 && int(font) < len(ctx.entries) )
|
||||
|
||||
@ -74,58 +76,68 @@ shape_text_uncached :: proc( ctx : ^Context, font : Font_ID, text_utf8 : string,
|
||||
line_gap := f32(line_gap_i32)
|
||||
line_height := (ascent - descent + line_gap) * entry.size_scale
|
||||
|
||||
if ctx.use_advanced_shaper
|
||||
{
|
||||
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 )
|
||||
return
|
||||
}
|
||||
else
|
||||
{
|
||||
// Note(Original Author):
|
||||
// We use our own fallback dumbass text shaping.
|
||||
// WARNING: PLEASE USE HARFBUZZ. GOOD TEXT SHAPING IS IMPORTANT FOR INTERNATIONALISATION.
|
||||
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 )
|
||||
}
|
||||
|
||||
line_count : int = 1
|
||||
max_line_width : f32 = 0
|
||||
position : Vec2
|
||||
shape_text_uncached_latin :: 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) )
|
||||
|
||||
prev_codepoint : rune
|
||||
for codepoint in text_utf8
|
||||
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'
|
||||
{
|
||||
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)
|
||||
}
|
||||
|
||||
append( & output.glyphs, parser_find_glyph_index( & entry.parser_info, codepoint ))
|
||||
advance, _ := parser_get_codepoint_horizontal_metrics( & entry.parser_info, codepoint )
|
||||
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_empty := parser_is_glyph_empty( & entry.parser_info,glyph_index )
|
||||
if ! is_empty
|
||||
{
|
||||
append( & output.glyphs, glyph_index)
|
||||
append( & output.positions, Vec2 {
|
||||
floor(position.x),
|
||||
floor(position.y)
|
||||
})
|
||||
|
||||
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
|
||||
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
|
||||
}
|
||||
|
@ -37,7 +37,7 @@ shaper_shutdown :: proc( ctx : ^Shaper_Context )
|
||||
}
|
||||
}
|
||||
|
||||
shaper_load_font :: proc( ctx : ^Shaper_Context, label : string, data : []byte, user_data : rawptr ) -> (info : Shaper_Info)
|
||||
shaper_load_font :: proc( ctx : ^Shaper_Context, label : string, data : []byte, user_data : rawptr = nil ) -> (info : Shaper_Info)
|
||||
{
|
||||
using info
|
||||
blob = harfbuzz.blob_create( raw_data(data), cast(c.uint) len(data), harfbuzz.Memory_Mode.READONLY, user_data, nil )
|
||||
@ -54,10 +54,10 @@ shaper_unload_font :: proc( ctx : ^Shaper_Info )
|
||||
if blob != nil do harfbuzz.blob_destroy( blob )
|
||||
}
|
||||
|
||||
shaper_shape_from_text :: proc( ctx : ^Shaper_Context, info : ^Shaper_Info, output :^Shaped_Text, text_utf8 : string,
|
||||
shaper_shape_from_text :: #force_inline proc( ctx : ^Shaper_Context, info : ^Shaper_Info, output :^Shaped_Text, text_utf8 : string,
|
||||
ascent, descent, line_gap : i32, size, size_scale : f32 )
|
||||
{
|
||||
// profile(#procedure)
|
||||
profile(#procedure)
|
||||
current_script := harfbuzz.Script.UNKNOWN
|
||||
hb_ucfunc := harfbuzz.unicode_funcs_get_default()
|
||||
harfbuzz.buffer_clear_contents( ctx.hb_buffer )
|
||||
@ -71,12 +71,13 @@ shaper_shape_from_text :: proc( ctx : ^Shaper_Context, info : ^Shaper_Info, outp
|
||||
line_count := 1
|
||||
line_height := ((ascent - descent + line_gap) * size_scale)
|
||||
|
||||
position, vertical_position : f32
|
||||
shape_run :: proc( buffer : harfbuzz.Buffer, script : harfbuzz.Script, font : harfbuzz.Font, output : ^Shaped_Text,
|
||||
position, vertical_position, max_line_width: ^f32, line_count: ^int,
|
||||
position : Vec2
|
||||
shape_run :: #force_inline proc( buffer : harfbuzz.Buffer, script : harfbuzz.Script, font : harfbuzz.Font, output : ^Shaped_Text,
|
||||
position : ^Vec2, max_line_width: ^f32, line_count: ^int,
|
||||
ascent, descent, line_gap, size, size_scale: f32,
|
||||
snap_shape_pos : b32, adv_snap_small_font_threshold : f32 )
|
||||
{
|
||||
profile(#procedure)
|
||||
// Set script and direction. We use the system's default langauge.
|
||||
// script = HB_SCRIPT_LATIN
|
||||
harfbuzz.buffer_set_script( buffer, script )
|
||||
@ -102,10 +103,10 @@ shaper_shape_from_text :: proc( ctx : ^Shaper_Context, info : ^Shaper_Info, outp
|
||||
|
||||
if hb_glyph.cluster > 0
|
||||
{
|
||||
(max_line_width^) = max( max_line_width^, position^ )
|
||||
(position^) = 0.0
|
||||
(vertical_position^) -= line_height
|
||||
(vertical_position^) = floor(vertical_position^)
|
||||
(max_line_width^) = max( max_line_width^, position.x )
|
||||
position.x = 0.0
|
||||
position.y -= line_height
|
||||
position.y = floor(position.y)
|
||||
(line_count^) += 1
|
||||
continue
|
||||
}
|
||||
@ -116,26 +117,24 @@ shaper_shape_from_text :: proc( ctx : ^Shaper_Context, info : ^Shaper_Info, outp
|
||||
|
||||
append( & output.glyphs, glyph_id )
|
||||
|
||||
pos := position^
|
||||
v_pos := vertical_position^
|
||||
offset_x := f32(hb_gposition.x_offset) * size_scale
|
||||
offset_y := f32(hb_gposition.y_offset) * size_scale
|
||||
pos += offset_x
|
||||
v_pos += offset_y
|
||||
glyph_pos := position^
|
||||
offset := Vec2 { f32(hb_gposition.x_offset), f32(hb_gposition.y_offset) } * size_scale
|
||||
glyph_pos += offset
|
||||
|
||||
if snap_shape_pos {
|
||||
pos = ceil(pos)
|
||||
v_pos = ceil(v_pos)
|
||||
glyph_pos = ceil(glyph_pos)
|
||||
}
|
||||
append( & output.positions, Vec2 {pos, v_pos})
|
||||
append( & output.positions, glyph_pos)
|
||||
|
||||
(position^) += f32(hb_gposition.x_advance) * size_scale
|
||||
(vertical_position^) += f32(hb_gposition.y_advance) * size_scale
|
||||
(max_line_width^) = max(max_line_width^, position^)
|
||||
advance := Vec2 {
|
||||
f32(hb_gposition.x_advance) * size_scale,
|
||||
f32(hb_gposition.y_advance) * size_scale
|
||||
}
|
||||
(position^) += advance
|
||||
(max_line_width^) = max(max_line_width^, position.x)
|
||||
}
|
||||
|
||||
output.end_cursor_pos.x = position^
|
||||
output.end_cursor_pos.y = vertical_position^
|
||||
output.end_cursor_pos = position^
|
||||
harfbuzz.buffer_clear_contents( buffer )
|
||||
}
|
||||
|
||||
@ -162,7 +161,7 @@ shaper_shape_from_text :: proc( ctx : ^Shaper_Context, info : ^Shaper_Info, outp
|
||||
// End current run since we've encountered a script change.
|
||||
shape_run(
|
||||
ctx.hb_buffer, current_script, info.font, output,
|
||||
& position, & vertical_position, & max_line_width, & line_count,
|
||||
& position, & max_line_width, & line_count,
|
||||
ascent, descent, line_gap, size, size_scale,
|
||||
ctx.snap_glyph_position, ctx.adv_snap_small_font_threshold
|
||||
)
|
||||
@ -173,7 +172,7 @@ shaper_shape_from_text :: proc( ctx : ^Shaper_Context, info : ^Shaper_Info, outp
|
||||
// End the last run if needed
|
||||
shape_run(
|
||||
ctx.hb_buffer, current_script, info.font, output,
|
||||
& position, & vertical_position, & max_line_width, & line_count,
|
||||
& position, & max_line_width, & line_count,
|
||||
ascent, descent, line_gap, size, size_scale,
|
||||
ctx.snap_glyph_position, ctx.adv_snap_small_font_threshold
|
||||
)
|
||||
|
@ -7,7 +7,7 @@ package vefontcache
|
||||
|
||||
import "base:runtime"
|
||||
|
||||
Font_ID :: distinct i64
|
||||
Font_ID :: distinct i32
|
||||
Glyph :: distinct i32
|
||||
|
||||
Entry :: struct {
|
||||
@ -37,7 +37,7 @@ Context :: struct {
|
||||
|
||||
temp_path : [dynamic]Vertex,
|
||||
temp_codepoint_seen : map[u64]bool,
|
||||
temp_codepoint_seen_num : int,
|
||||
temp_codepoint_seen_num : i32,
|
||||
|
||||
snap_width : f32,
|
||||
snap_height : f32,
|
||||
@ -63,7 +63,6 @@ Context :: struct {
|
||||
debug_print_verbose : b32,
|
||||
}
|
||||
|
||||
|
||||
Init_Atlas_Region_Params :: struct {
|
||||
width : u32,
|
||||
height : u32,
|
||||
@ -113,7 +112,7 @@ Init_Glyph_Draw_Params :: struct {
|
||||
|
||||
Init_Glyph_Draw_Params_Default :: Init_Glyph_Draw_Params {
|
||||
over_sample = Vec2 { 4, 4 },
|
||||
buffer_batch = 4,
|
||||
buffer_batch = 8,
|
||||
draw_padding = Init_Atlas_Params_Default.glyph_padding,
|
||||
}
|
||||
|
||||
@ -135,8 +134,8 @@ Init_Shape_Cache_Params :: struct {
|
||||
}
|
||||
|
||||
Init_Shape_Cache_Params_Default :: Init_Shape_Cache_Params {
|
||||
capacity = 8 * 1024,
|
||||
reserve_length = 256,
|
||||
capacity = 10 * 1024,
|
||||
reserve_length = 10 * 1024,
|
||||
}
|
||||
|
||||
//#region("lifetime")
|
||||
@ -148,10 +147,10 @@ startup :: proc( ctx : ^Context, parser_kind : Parser_Kind = .STB_TrueType,
|
||||
glyph_draw_params := Init_Glyph_Draw_Params_Default,
|
||||
shape_cache_params := Init_Shape_Cache_Params_Default,
|
||||
shaper_params := Init_Shaper_Params_Default,
|
||||
default_curve_quality : u32 = 3,
|
||||
entires_reserve : u32 = 512,
|
||||
default_curve_quality : u32 = 2,
|
||||
entires_reserve : u32 = 256,
|
||||
temp_path_reserve : u32 = 1024,
|
||||
temp_codepoint_seen_reserve : u32 = 2048,
|
||||
temp_codepoint_seen_reserve : u32 = 1024,
|
||||
)
|
||||
{
|
||||
assert( ctx != nil, "Must provide a valid context" )
|
||||
@ -179,13 +178,13 @@ startup :: proc( ctx : ^Context, parser_kind : Parser_Kind = .STB_TrueType,
|
||||
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, len = 0, cap = 4 * Kilobyte )
|
||||
draw_list.vertices, error = make( [dynamic]Vertex, len = 0, cap = 1 * Kilobyte )
|
||||
assert(error == .None, "VEFontCache.init : Failed to allocate draw_list.vertices")
|
||||
|
||||
draw_list.indices, error = make( [dynamic]u32, len = 0, cap = 8 * Kilobyte )
|
||||
draw_list.indices, error = make( [dynamic]u32, len = 0, cap = 2 * Kilobyte )
|
||||
assert(error == .None, "VEFontCache.init : Failed to allocate draw_list.indices")
|
||||
|
||||
draw_list.calls, error = make( [dynamic]Draw_Call, len = 0, cap = 512 )
|
||||
draw_list.calls, error = make( [dynamic]Draw_Call, len = 0, cap = 128 )
|
||||
assert(error == .None, "VEFontCache.init : Failed to allocate draw_list.calls")
|
||||
|
||||
init_atlas_region :: proc( region : ^Atlas_Region, params : Init_Atlas_Params, region_params : Init_Atlas_Region_Params, factor : Vec2i, expected_cap : i32 )
|
||||
@ -215,7 +214,7 @@ startup :: proc( ctx : ^Context, parser_kind : Parser_Kind = .STB_TrueType,
|
||||
|
||||
atlas.width = i32(atlas_params.width)
|
||||
atlas.height = i32(atlas_params.height)
|
||||
atlas.glyph_padding = i32(atlas_params.glyph_padding)
|
||||
atlas.glyph_padding = f32(atlas_params.glyph_padding)
|
||||
atlas.glyph_over_scalar = atlas_params.glyph_over_scalar
|
||||
|
||||
atlas.region_a.offset = {0, 0}
|
||||
@ -394,7 +393,7 @@ load_font :: proc( ctx : ^Context, label : string, data : []byte, size_px : f32,
|
||||
used = true
|
||||
|
||||
parser_info = parser_load_font( & parser_ctx, label, data )
|
||||
shaper_info = shaper_load_font( & shaper_ctx, label, data, transmute(rawptr) id )
|
||||
shaper_info = shaper_load_font( & shaper_ctx, label, data )
|
||||
|
||||
size = size_px
|
||||
size_scale = parser_scale( & parser_info, size )
|
||||
@ -442,9 +441,9 @@ configure_snap :: #force_inline proc( ctx : ^Context, snap_width, snap_height :
|
||||
get_cursor_pos :: #force_inline proc( ctx : ^Context ) -> Vec2 { assert(ctx != nil); return ctx.cursor_pos }
|
||||
set_colour :: #force_inline proc( ctx : ^Context, colour : Colour ) { assert(ctx != nil); ctx.colour = colour }
|
||||
|
||||
draw_text :: proc( ctx : ^Context, font : Font_ID, text_utf8 : string, position, scale : Vec2 ) -> b32
|
||||
draw_text :: #force_inline proc( ctx : ^Context, font : Font_ID, text_utf8 : string, position, scale : Vec2 ) -> b32
|
||||
{
|
||||
// profile(#procedure)
|
||||
profile(#procedure)
|
||||
assert( ctx != nil )
|
||||
assert( font >= 0 && int(font) < len(ctx.entries) )
|
||||
|
||||
@ -466,20 +465,50 @@ draw_text :: proc( ctx : ^Context, font : Font_ID, text_utf8 : string, position,
|
||||
|
||||
text_chunk = transmute(string) text_utf8_bytes[ : ]
|
||||
if len(text_chunk) > 0 {
|
||||
shaped := shape_text_cached( ctx, font, text_chunk, entry )
|
||||
shaped := shape_text_cached( ctx, font, text_chunk, entry, shape_text_uncached_advanced )
|
||||
ctx.cursor_pos = draw_text_shape( ctx, font, entry, shaped, position, scale, ctx.snap_width, ctx.snap_height )
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
draw_text_mono_latin :: #force_inline proc( ctx : ^Context, font : Font_ID, text_utf8 : string, position, scale : Vec2 ) -> b32
|
||||
{
|
||||
profile(#procedure)
|
||||
assert( ctx != nil )
|
||||
assert( font >= 0 && int(font) < len(ctx.entries) )
|
||||
|
||||
ctx.cursor_pos = {}
|
||||
|
||||
position := position
|
||||
if ctx.snap_width > 0 do position.x = ceil(position.x * ctx.snap_width ) / ctx.snap_width
|
||||
if ctx.snap_height > 0 do position.y = ceil(position.y * ctx.snap_height) / ctx.snap_height
|
||||
|
||||
entry := & ctx.entries[ font ]
|
||||
|
||||
ChunkType :: enum u32 { Visible, Formatting }
|
||||
chunk_kind : ChunkType
|
||||
chunk_start : int = 0
|
||||
chunk_end : int = 0
|
||||
|
||||
text_utf8_bytes := transmute([]u8) text_utf8
|
||||
text_chunk : string
|
||||
|
||||
text_chunk = transmute(string) text_utf8_bytes[ : ]
|
||||
if len(text_chunk) > 0 {
|
||||
shaped := shape_text_cached( ctx, font, text_chunk, entry, shape_text_uncached_latin )
|
||||
ctx.cursor_pos = draw_text_shape( ctx, font, entry, shaped, position, scale, ctx.snap_width, ctx.snap_height )
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// ve_fontcache_Draw_List
|
||||
get_draw_list :: proc( ctx : ^Context, optimize_before_returning := true ) -> ^Draw_List {
|
||||
get_draw_list :: #force_inline proc( ctx : ^Context, optimize_before_returning := true ) -> ^Draw_List {
|
||||
assert( ctx != nil )
|
||||
if optimize_before_returning do optimize_draw_list( & ctx.draw_list, 0 )
|
||||
return & ctx.draw_list
|
||||
}
|
||||
|
||||
get_draw_list_layer :: proc( ctx : ^Context, optimize_before_returning := true ) -> (vertices : []Vertex, indices : []u32, calls : []Draw_Call) {
|
||||
get_draw_list_layer :: #force_inline proc( ctx : ^Context, optimize_before_returning := true ) -> (vertices : []Vertex, indices : []u32, calls : []Draw_Call) {
|
||||
assert( ctx != nil )
|
||||
if optimize_before_returning do optimize_draw_list( & ctx.draw_list, ctx.draw_layer.calls_offset )
|
||||
vertices = ctx.draw_list.vertices[ ctx.draw_layer.vertices_offset : ]
|
||||
@ -489,7 +518,7 @@ get_draw_list_layer :: proc( ctx : ^Context, optimize_before_returning := true )
|
||||
}
|
||||
|
||||
// ve_fontcache_flush_Draw_List
|
||||
flush_draw_list :: proc( ctx : ^Context ) {
|
||||
flush_draw_list :: #force_inline proc( ctx : ^Context ) {
|
||||
assert( ctx != nil )
|
||||
using ctx
|
||||
clear_draw_list( & draw_list )
|
||||
@ -498,7 +527,7 @@ flush_draw_list :: proc( ctx : ^Context ) {
|
||||
draw_layer.calls_offset = 0
|
||||
}
|
||||
|
||||
flush_draw_list_layer :: proc( ctx : ^Context ) {
|
||||
flush_draw_list_layer :: #force_inline proc( ctx : ^Context ) {
|
||||
assert( ctx != nil )
|
||||
using ctx
|
||||
draw_layer.vertices_offset = len(draw_list.vertices)
|
||||
@ -510,14 +539,14 @@ flush_draw_list_layer :: proc( ctx : ^Context ) {
|
||||
|
||||
//#region("metrics")
|
||||
|
||||
measure_text_size :: proc( ctx : ^Context, font : Font_ID, text_utf8 : string ) -> (measured : Vec2)
|
||||
measure_text_size :: #force_inline proc( ctx : ^Context, font : Font_ID, text_utf8 : string ) -> (measured : Vec2)
|
||||
{
|
||||
// profile(#procedure)
|
||||
assert( ctx != nil )
|
||||
assert( font >= 0 && int(font) < len(ctx.entries) )
|
||||
|
||||
entry := &ctx.entries[font]
|
||||
shaped := shape_text_cached(ctx, font, text_utf8, entry)
|
||||
shaped := shape_text_cached(ctx, font, text_utf8, entry, shape_text_uncached_advanced )
|
||||
return shaped.size
|
||||
}
|
||||
|
||||
|
@ -248,7 +248,6 @@ hmap_chained_set :: proc( self : HMapChained($Type), key : u64, value : Type ) -
|
||||
block = slice_ptr(transmute([^]byte) raw_mem, slot_size)
|
||||
// block, error = pool_grab(pool, false)
|
||||
next := transmute( ^HMapChainedSlot(Type)) raw_data(block)
|
||||
next^ = {}
|
||||
|
||||
slot.next = next
|
||||
slot.next^ = {}
|
||||
|
@ -15,15 +15,19 @@ set_profiler_module_context :: #force_inline proc "contextless" ( ctx : ^SpallPr
|
||||
Module_Context = ctx
|
||||
}
|
||||
|
||||
@(deferred_none = profile_end)
|
||||
DISABLE_PROFILING :: false
|
||||
|
||||
@(deferred_none = profile_end, disabled = DISABLE_PROFILING)
|
||||
profile :: #force_inline proc "contextless" ( name : string, loc := #caller_location ) {
|
||||
spall._buffer_begin( & Module_Context.ctx, & Module_Context.buffer, name, "", loc )
|
||||
}
|
||||
|
||||
@(disabled = DISABLE_PROFILING)
|
||||
profile_begin :: #force_inline proc "contextless" ( name : string, loc := #caller_location ) {
|
||||
spall._buffer_begin( & Module_Context.ctx, & Module_Context.buffer, name, "", loc )
|
||||
}
|
||||
|
||||
@(disabled = DISABLE_PROFILING)
|
||||
profile_end :: #force_inline proc "contextless" () {
|
||||
spall._buffer_end( & Module_Context.ctx, & Module_Context.buffer)
|
||||
}
|
||||
|
@ -310,7 +310,7 @@ startup :: proc( prof : ^SpallProfiler, persistent_mem, frame_mem, transient_mem
|
||||
{
|
||||
profile("screen ui")
|
||||
|
||||
ui_startup( & screen_ui.base, cache_allocator = persistent_slab_allocator() )
|
||||
ui_startup( & screen_ui.base, cache_table_size = 2 * Kilo, cache_allocator = persistent_slab_allocator() )
|
||||
ui_floating_startup( & screen_ui.floating, 1 * Kilobyte, 1 * Kilobyte, persistent_slab_allocator(), "screen ui floating manager" )
|
||||
|
||||
using screen_ui
|
||||
@ -348,7 +348,7 @@ startup :: proc( prof : ^SpallProfiler, persistent_mem, frame_mem, transient_mem
|
||||
// }
|
||||
|
||||
// Setup workspace UI state
|
||||
ui_startup( & workspace.ui, cache_allocator = persistent_slab_allocator() )
|
||||
ui_startup( & workspace.ui, cache_table_size = 8 * Kilo, cache_allocator = persistent_slab_allocator() )
|
||||
}
|
||||
|
||||
// debug.path_lorem = str_fmt("C:/projects/SectrPrototype/examples/Lorem Ipsum (197).txt", allocator = persistent_slab_allocator())
|
||||
|
@ -106,8 +106,9 @@ render_mode_2d_workspace :: proc( screen_extent : Vec2, cam : Camera, input : In
|
||||
|
||||
cam := cam
|
||||
when UI_Render_Method == .Layers {
|
||||
render_list := array_to_slice( ui.render_list )
|
||||
render_ui_via_box_list( render_list, screen_extent, ve_ctx, ve_render, & cam )
|
||||
render_list_box := array_to_slice( ui.render_list_box )
|
||||
render_list_text := array_to_slice( ui.render_list_text )
|
||||
render_ui_via_box_list( render_list_box, render_list_text, screen_extent, ve_ctx, ve_render, & cam )
|
||||
}
|
||||
when UI_Render_Method == .Depth_First
|
||||
{
|
||||
@ -281,8 +282,10 @@ render_screen_ui :: proc( screen_extent : Extents2, ui : ^UI_State, ve_ctx : ^ve
|
||||
render_set_view_space(screen_extent)
|
||||
|
||||
when UI_Render_Method == .Layers {
|
||||
render_list := array_to_slice( ui.render_list )
|
||||
render_ui_via_box_list( render_list, screen_extent, ve_ctx, ve_render )
|
||||
render_list_box := array_to_slice( ui.render_list_box )
|
||||
render_list_text := array_to_slice( ui.render_list_text )
|
||||
render_ui_via_box_list( render_list_box, render_list_text, screen_extent, ve_ctx, ve_render )
|
||||
// render_ui_via_box_list( render_list, screen_extent, ve_ctx, ve_render )
|
||||
}
|
||||
when UI_Render_Method == .Depth_First
|
||||
{
|
||||
@ -468,7 +471,8 @@ render_ui_via_box_tree :: proc( ui : ^UI_State, screen_extent : Vec2, ve_ctx : ^
|
||||
}
|
||||
|
||||
previous_layer : i32 = 0
|
||||
for box := ui.root.first; box != nil; box = ui_box_tranverse_next_depth_first( box, bypass_intersection_test = false, ctx = ui )
|
||||
for box := ui_box_tranverse_next_depth_first( ui.root, bypass_intersection_test = true, ctx = ui ); box != nil;
|
||||
box = ui_box_tranverse_next_depth_first( box, bypass_intersection_test = true, ctx = ui )
|
||||
{
|
||||
if box.ancestors != previous_layer {
|
||||
if shape_enqueued do render_flush_gp()
|
||||
@ -555,68 +559,102 @@ render_ui_via_box_tree :: proc( ui : ^UI_State, screen_extent : Vec2, ve_ctx : ^
|
||||
if text_enqueued do render_text_layer( screen_extent, ve_ctx, ve_render )
|
||||
}
|
||||
|
||||
render_ui_via_box_list :: proc( render_list : []UI_RenderBoxInfo, screen_extent : Vec2, ve_ctx : ^ve.Context, ve_render : VE_RenderData, cam : ^Camera = nil )
|
||||
render_ui_via_box_list :: proc( box_list : []UI_RenderBoxInfo, text_list : []UI_RenderTextInfo, screen_extent : Vec2, ve_ctx : ^ve.Context, ve_render : VE_RenderData, cam : ^Camera = nil )
|
||||
{
|
||||
profile(#procedure)
|
||||
debug := get_state().debug
|
||||
default_font := get_state().default_font
|
||||
|
||||
text_enqueued : b32 = false
|
||||
shape_enqueued : b32 = false
|
||||
cam_zoom_ratio := cam != nil ? 1.0 / cam.zoom : 1.0
|
||||
circle_radius := cam != nil ? cam_zoom_ratio * 3 : 3
|
||||
|
||||
for entry, id in render_list
|
||||
box_id : i32 = 0
|
||||
text_id : i32 = 0
|
||||
|
||||
layer_left : b32 = true
|
||||
for layer_left
|
||||
{
|
||||
already_passed_signal := id > 0 && render_list[ id - 1 ].layer_signal
|
||||
if !already_passed_signal && entry.layer_signal
|
||||
profile("layer")
|
||||
shape_enqueued : b32 = false
|
||||
box_layer_done : b32 = false
|
||||
for box_id < cast(i32) len(box_list) && ! box_layer_done
|
||||
{
|
||||
// profile("render ui layer")
|
||||
render_flush_gp()
|
||||
if text_enqueued do render_text_layer( screen_extent, ve_ctx, ve_render )
|
||||
continue
|
||||
}
|
||||
using entry
|
||||
profile("GP_Render")
|
||||
box_layer_done = b32(box_id > 0) && box_list[ box_id - 1 ].layer_signal
|
||||
|
||||
// profile("enqueue box")
|
||||
entry := box_list[box_id]
|
||||
|
||||
GP_Render:
|
||||
{
|
||||
// profile("draw_shapes")
|
||||
if style.bg_color.a != 0
|
||||
corner_radii_total : f32 = 0
|
||||
for radius in entry.corner_radii do corner_radii_total += radius
|
||||
|
||||
if entry.bg_color.a != 0
|
||||
{
|
||||
render_set_color( style.bg_color )
|
||||
draw_rect( bounds )
|
||||
render_set_color( entry.bg_color )
|
||||
if corner_radii_total > 0 do draw_rect_rounded( entry.bounds, entry.corner_radii, 16 )
|
||||
else do draw_rect( entry.bounds)
|
||||
shape_enqueued = true
|
||||
}
|
||||
|
||||
if style.border_color.a != 0 && border_width > 0 {
|
||||
render_set_color( style.border_color )
|
||||
draw_rect_border( bounds, border_width )
|
||||
if entry.border_color.a != 0 && entry.border_width > 0
|
||||
{
|
||||
render_set_color( entry.border_color )
|
||||
|
||||
if corner_radii_total > 0 do draw_rect_rounded_border( entry.bounds, entry.corner_radii, entry.border_width, 16 )
|
||||
else do draw_rect_border( entry.bounds, entry.border_width )
|
||||
shape_enqueued = true
|
||||
}
|
||||
|
||||
if debug.draw_ui_box_bounds_points
|
||||
{
|
||||
render_set_color(Color_Red)
|
||||
draw_filled_circle(bounds.min.x, bounds.min.y, 3, 24)
|
||||
draw_filled_circle(entry.bounds.min.x, entry.bounds.min.y, circle_radius, 24)
|
||||
|
||||
render_set_color(Color_Blue)
|
||||
draw_filled_circle(bounds.max.x, bounds.max.y, 3, 24)
|
||||
draw_filled_circle(entry.bounds.max.x, entry.bounds.max.y, circle_radius, 24)
|
||||
shape_enqueued = true
|
||||
}
|
||||
|
||||
box_id += 1
|
||||
}
|
||||
|
||||
if len(text.str) > 0 && style.font.key != 0 {
|
||||
if cam != nil {
|
||||
draw_text_string_pos_extent_zoomed( text.str, default_font, font_size, computed.text_pos, cam^, style.text_color )
|
||||
}
|
||||
else {
|
||||
draw_text_string_pos_extent( text.str, default_font, font_size, computed.text_pos, style.text_color )
|
||||
}
|
||||
text_enqueued = true
|
||||
if shape_enqueued {
|
||||
profile("render ui box_layer")
|
||||
render_flush_gp()
|
||||
shape_enqueued = false
|
||||
}
|
||||
|
||||
text_enqueued : b32 = false
|
||||
text_layer_done : b32 = false
|
||||
for text_id < cast(i32) len(text_list) && ! text_layer_done
|
||||
{
|
||||
profile("Text_Render")
|
||||
text_layer_done = b32(text_id > 0) && text_list[ text_id - 1 ].layer_signal
|
||||
|
||||
entry := text_list[text_id]
|
||||
|
||||
font := entry.font.key != 0 ? entry.font : default_font
|
||||
if len(entry.text) > 0
|
||||
{
|
||||
if cam != nil {
|
||||
draw_text_string_pos_extent_zoomed( entry.text, font, entry.font_size, entry.position, cam^, entry.color )
|
||||
}
|
||||
else {
|
||||
draw_text_string_pos_extent( entry.text, font, entry.font_size, entry.position, entry.color )
|
||||
}
|
||||
text_enqueued = true
|
||||
}
|
||||
|
||||
text_id += 1
|
||||
}
|
||||
|
||||
if text_enqueued {
|
||||
profile("render ui text layer")
|
||||
if text_enqueued do render_text_layer( screen_extent, ve_ctx, ve_render )
|
||||
text_enqueued = false
|
||||
}
|
||||
|
||||
layer_left = box_id < cast(i32) len(box_list) && text_id < cast(i32) len(text_list)
|
||||
}
|
||||
|
||||
if shape_enqueued do render_flush_gp()
|
||||
if text_enqueued do render_text_layer( screen_extent, ve_ctx, ve_render )
|
||||
}
|
||||
|
||||
#region("Helpers")
|
||||
|
@ -46,8 +46,6 @@ font_provider_startup :: proc( ctx : ^FontProviderContext )
|
||||
ve.startup( & ve_ctx, .STB_TrueType, allocator = persistent_slab_allocator() )
|
||||
ve_ctx.glyph_buffer.over_sample = { 4,4 }
|
||||
log("VEFontCached initialized")
|
||||
// provider_data.ve_ctx.debug_print = true
|
||||
// provider_data.ve_ctx.debug_print_verbose = true
|
||||
font_provider_setup_sokol_gfx_objects( & render, ve_ctx )
|
||||
}
|
||||
|
||||
|
@ -64,7 +64,7 @@ UI_Style_Stack_Size :: 512
|
||||
UI_Parent_Stack_Size :: 512
|
||||
// UI_Built_Boxes_Array_Size :: 8
|
||||
UI_Built_Boxes_Array_Size :: 56 * Kilobyte
|
||||
UI_BoxCache_TableSize :: 8 * Kilobyte
|
||||
UI_BoxCache_TableSize :: 8 * Kilobyte
|
||||
|
||||
UI_RenderEntry :: struct {
|
||||
info : UI_RenderBoxInfo,
|
||||
@ -75,14 +75,32 @@ UI_RenderEntry :: struct {
|
||||
|
||||
UI_RenderLayer :: DLL_NodeFL(UI_RenderEntry)
|
||||
|
||||
// UI_RenderBoxInfo :: struct {
|
||||
// using computed : UI_Computed,
|
||||
// using style : UI_Style,
|
||||
// text : StrRunesPair,
|
||||
// font_size : UI_Scalar,
|
||||
// border_width : UI_Scalar,
|
||||
// label : StrRunesPair,
|
||||
// layer_signal : b32,
|
||||
// }
|
||||
|
||||
UI_RenderBoxInfo :: struct {
|
||||
using computed : UI_Computed,
|
||||
using style : UI_Style,
|
||||
text : StrRunesPair,
|
||||
font_size : UI_Scalar,
|
||||
border_width : UI_Scalar,
|
||||
label : StrRunesPair,
|
||||
layer_signal : b32,
|
||||
bounds : Range2,
|
||||
corner_radii : [Corner.Count]f32,
|
||||
bg_color : RGBA8,
|
||||
border_color : RGBA8,
|
||||
border_width : UI_Scalar,
|
||||
layer_signal : b8,
|
||||
}
|
||||
|
||||
UI_RenderTextInfo :: struct {
|
||||
text : string,
|
||||
position : Vec2,
|
||||
color : RGBA8,
|
||||
font : FontID,
|
||||
font_size : f32,
|
||||
layer_signal : b8
|
||||
}
|
||||
|
||||
UI_RenderMethod :: enum u32 {
|
||||
@ -90,7 +108,7 @@ UI_RenderMethod :: enum u32 {
|
||||
Layers,
|
||||
}
|
||||
|
||||
UI_Render_Method :: UI_RenderMethod.Depth_First
|
||||
UI_Render_Method :: UI_RenderMethod.Layers
|
||||
|
||||
// TODO(Ed): Rename to UI_Context
|
||||
UI_State :: struct {
|
||||
@ -104,10 +122,15 @@ UI_State :: struct {
|
||||
prev_cache : ^HMapChained( UI_Box ),
|
||||
curr_cache : ^HMapChained( UI_Box ),
|
||||
|
||||
// TODO(Ed): DO WE ACTUALLY NEED THIS?
|
||||
spacial_indexing_method : UI_SpacialIndexingMethod,
|
||||
|
||||
// For rendering via a set of layers organized into a single command list
|
||||
// render_queue_builder : SubArena,
|
||||
render_queue : Array(UI_RenderLayer),
|
||||
render_list : Array(UI_RenderBoxInfo),
|
||||
// render_queue : Array(UI_RenderLayer),
|
||||
// render_list : Array(UI_RenderBoxInfo),
|
||||
render_list_box : Array(UI_RenderBoxInfo),
|
||||
render_list_text : Array(UI_RenderTextInfo),
|
||||
|
||||
null_box : ^UI_Box, // This was used with the Linked list interface...
|
||||
root : ^UI_Box,
|
||||
@ -138,13 +161,13 @@ UI_State :: struct {
|
||||
|
||||
#region("Lifetime")
|
||||
|
||||
ui_startup :: proc( ui : ^ UI_State, cache_allocator : Allocator /* , cache_reserve_size : u64 */ )
|
||||
ui_startup :: proc( ui : ^ UI_State, spacial_indexing_method : UI_SpacialIndexingMethod = .QuadTree, cache_allocator : Allocator, cache_table_size : uint )
|
||||
{
|
||||
ui := ui
|
||||
ui^ = {}
|
||||
|
||||
for & cache in ui.caches {
|
||||
box_cache, allocation_error := make( HMapChained(UI_Box), UI_BoxCache_TableSize, cache_allocator )
|
||||
box_cache, allocation_error := make( HMapChained(UI_Box), cache_table_size, cache_allocator )
|
||||
verify( allocation_error == AllocatorError.None, "Failed to allocate box cache" )
|
||||
cache = box_cache
|
||||
}
|
||||
@ -153,11 +176,17 @@ ui_startup :: proc( ui : ^ UI_State, cache_allocator : Allocator /* , cache_rese
|
||||
|
||||
allocation_error : AllocatorError
|
||||
|
||||
ui.render_queue, allocation_error = make( Array(UI_RenderLayer), 32, cache_allocator )
|
||||
verify( allocation_error == AllocatorError.None, "Failed to allcate render_queue")
|
||||
// ui.render_queue, allocation_error = make( Array(UI_RenderLayer), 32, cache_allocator )
|
||||
// verify( allocation_error == AllocatorError.None, "Failed to allcate render_queue")
|
||||
|
||||
ui.render_list, allocation_error = make( Array(UI_RenderBoxInfo), UI_Built_Boxes_Array_Size, cache_allocator, fixed_cap = true )
|
||||
verify( allocation_error == AllocatorError.None, "Failed to allocate rener_list" )
|
||||
// ui.render_list, allocation_error = make( Array(UI_RenderBoxInfo), UI_Built_Boxes_Array_Size, cache_allocator, fixed_cap = true )
|
||||
// verify( allocation_error == AllocatorError.None, "Failed to allocate rener_list" )
|
||||
|
||||
ui.render_list_box, allocation_error = make( Array(UI_RenderBoxInfo), UI_Built_Boxes_Array_Size, cache_allocator, fixed_cap = true )
|
||||
verify( allocation_error == AllocatorError.None, "Failed to allocate rener_list_box" )
|
||||
|
||||
ui.render_list_text, allocation_error = make( Array(UI_RenderTextInfo), UI_Built_Boxes_Array_Size, cache_allocator, fixed_cap = true )
|
||||
verify( allocation_error == AllocatorError.None, "Failed to allocate rener_list_box" )
|
||||
|
||||
log("ui_startup completed")
|
||||
}
|
||||
@ -168,8 +197,10 @@ ui_reload :: proc( ui : ^ UI_State, cache_allocator : Allocator )
|
||||
for & cache in ui.caches {
|
||||
hmap_chained_reload( cache, cache_allocator)
|
||||
}
|
||||
ui.render_queue.backing = cache_allocator
|
||||
ui.render_list.backing = cache_allocator
|
||||
// ui.render_queue.backing = cache_allocator
|
||||
// ui.render_list.backing = cache_allocator
|
||||
ui.render_list_box.backing = cache_allocator
|
||||
ui.render_list_text.backing = cache_allocator
|
||||
}
|
||||
|
||||
// TODO(Ed) : Is this even needed?
|
||||
@ -186,8 +217,10 @@ ui_graph_build_begin :: proc( ui : ^ UI_State, bounds : Vec2 = {} )
|
||||
|
||||
stack_clear( & layout_combo_stack )
|
||||
stack_clear( & style_combo_stack )
|
||||
array_clear( render_queue )
|
||||
array_clear( render_list )
|
||||
// array_clear( render_queue )
|
||||
// array_clear( render_list )
|
||||
array_clear( render_list_box )
|
||||
array_clear( render_list_text )
|
||||
|
||||
curr_cache, prev_cache = swap( curr_cache, prev_cache )
|
||||
|
||||
@ -210,6 +243,7 @@ ui_graph_build_end :: proc( ui : ^UI_State )
|
||||
state := get_state()
|
||||
|
||||
ui_parent_pop() // Should be ui_context.root
|
||||
assert(stack_peek(& ui.parent_stack) == nil)
|
||||
|
||||
Post_Build_Graph_Traversal:
|
||||
{
|
||||
@ -225,196 +259,68 @@ ui_graph_build_end :: proc( ui : ^UI_State )
|
||||
computed.content = computed.bounds
|
||||
}
|
||||
|
||||
previous_layer : i32 = 1
|
||||
|
||||
// Auto-layout and initial render_queue generation
|
||||
profile_begin("Auto-layout and render_queue generation")
|
||||
render_queue := array_to_slice(ui.render_queue)
|
||||
for current := root.first; current != nil; current = ui_box_traverse_next_breadth_first( current, bypass_intersection_test = true )
|
||||
// render_queue := array_to_slice(ui.render_queue)
|
||||
for current := ui.root.first; current != nil;
|
||||
current = ui_box_tranverse_next_depth_first( current, bypass_intersection_test = true, ctx = ui )
|
||||
{
|
||||
if ! current.computed.fresh {
|
||||
ui_box_compute_layout( current )
|
||||
}
|
||||
|
||||
when UI_Render_Method == .Layers
|
||||
{
|
||||
if ! intersects_range2(ui_view_bounds(ui), current.computed.bounds) {
|
||||
continue
|
||||
}
|
||||
|
||||
// TODO(Ed): Eventually put this into a sub-arena
|
||||
entry, error := new(UI_RenderEntry)
|
||||
(entry^) = UI_RenderEntry {
|
||||
info = {
|
||||
current.computed,
|
||||
current.style,
|
||||
current.text,
|
||||
current.layout.font_size,
|
||||
current.layout.border_width,
|
||||
current.label,
|
||||
false,
|
||||
},
|
||||
layer_id = current.ancestors -1,
|
||||
}
|
||||
when true {
|
||||
// entry : UI_RenderBoxInfo = {
|
||||
// current.computed,
|
||||
// current.style,
|
||||
// current.text,
|
||||
// current.layout.font_size,
|
||||
// current.layout.border_width,
|
||||
// current.label,
|
||||
// false,
|
||||
// }
|
||||
entry_box := UI_RenderBoxInfo {
|
||||
bounds = current.computed.bounds,
|
||||
corner_radii = current.style.corner_radii,
|
||||
bg_color = current.style.bg_color,
|
||||
border_color = current.style.border_color,
|
||||
border_width = current.layout.border_width,
|
||||
}
|
||||
entry_text := UI_RenderTextInfo {
|
||||
text = current.text.str,
|
||||
position = current.computed.text_pos,
|
||||
color = current.style.text_color,
|
||||
font = current.style.font,
|
||||
font_size = current.layout.font_size,
|
||||
}
|
||||
|
||||
if entry.layer_id >= i32(ui.render_queue.num) {
|
||||
append( & ui.render_queue, UI_RenderLayer {})
|
||||
render_queue = array_to_slice(ui.render_queue)
|
||||
}
|
||||
if current.ancestors != previous_layer {
|
||||
entry_box .layer_signal = true
|
||||
entry_text.layer_signal = true
|
||||
}
|
||||
|
||||
// else if layer.last == nil {
|
||||
// layer.first.next = entry
|
||||
// entry.prev = layer.first
|
||||
// layer.last = entry
|
||||
// }
|
||||
|
||||
// push_back to next layer
|
||||
layer := & render_queue[entry.layer_id]
|
||||
if layer.first == nil {
|
||||
layer.first = entry
|
||||
layer.last = entry
|
||||
}
|
||||
else {
|
||||
layer.last.next = entry
|
||||
entry.prev = layer.last
|
||||
layer.last = entry
|
||||
}
|
||||
// dll_full_push_back( layer, entry, nil )
|
||||
|
||||
// If there is a parent entry, give it a reference to the child entry
|
||||
parent_entry : ^UI_RenderEntry
|
||||
if entry.layer_id > 0 {
|
||||
parent_layer := & render_queue[entry.layer_id - 1]
|
||||
parent_entry = parent_layer.last
|
||||
entry.parent = parent_entry
|
||||
|
||||
if parent_entry.first == nil {
|
||||
parent_entry.first = entry
|
||||
parent_entry.last = entry
|
||||
}
|
||||
else {
|
||||
parent_entry.last = entry
|
||||
}
|
||||
// dll_fl_append( parent_entry, entry )
|
||||
}
|
||||
array_append(& ui.render_list_box, entry_box)
|
||||
array_append(& ui.render_list_text, entry_text)
|
||||
previous_layer = current.ancestors
|
||||
}
|
||||
}
|
||||
profile_end()
|
||||
|
||||
profile("render_list generation")
|
||||
when UI_Render_Method == .Layers
|
||||
{
|
||||
// render_queue overlap corrections & render_list generation
|
||||
render_queue = array_to_slice(ui.render_queue)
|
||||
for layer_id : i32 = 0; layer_id < i32(ui.render_queue.num); layer_id += 1
|
||||
{
|
||||
layer := & ui.render_queue.data[ layer_id ]
|
||||
append( & ui.render_list, UI_RenderBoxInfo { layer_signal = true })
|
||||
|
||||
to_increment, error := make( Array(^UI_RenderEntry), 4 * Kilo )
|
||||
verify( error == .None, "Faied to make to_increment array.")
|
||||
|
||||
to_inc_last_iterated : i32 = 0
|
||||
for entry := layer.first; entry != nil; entry = entry.next
|
||||
{
|
||||
for neighbor := entry.next; neighbor != nil; neighbor = neighbor.next
|
||||
{
|
||||
if ! overlap_range2( entry.info.computed.bounds, neighbor.info.computed.bounds) do continue
|
||||
append( & to_increment, neighbor )
|
||||
} // for neighbor := entry.next; neighbor != nil; neighbor = neighbor.next
|
||||
|
||||
if entry == to_increment.data[ to_inc_last_iterated ] {
|
||||
to_inc_last_iterated += 1
|
||||
}
|
||||
else {
|
||||
// This entry stayed in this layer, we can append the value
|
||||
array_append_value( & ui.render_list, entry.info )
|
||||
}
|
||||
} // for entry := layer.first; entry != nil; entry = entry.next
|
||||
|
||||
// Move overlaping entries & their children's by 1 layer
|
||||
to_inc_slice := array_to_slice(to_increment)
|
||||
for entry in to_inc_slice
|
||||
{
|
||||
pop_layer := render_queue[entry.layer_id]
|
||||
entry.layer_id += 1
|
||||
if entry.layer_id >= i32(ui.render_queue.num) {
|
||||
append( & ui.render_queue, UI_RenderLayer {} )
|
||||
render_queue = array_to_slice(ui.render_queue)
|
||||
}
|
||||
push_layer := render_queue[entry.layer_id]
|
||||
|
||||
// pop entry from layer
|
||||
prev := entry.prev
|
||||
prev.next = entry.next
|
||||
if entry == pop_layer.last {
|
||||
pop_layer.last = prev
|
||||
}
|
||||
|
||||
// push entry to next layer
|
||||
if push_layer.first == nil {
|
||||
push_layer.first = entry
|
||||
push_layer.last = entry
|
||||
}
|
||||
else {
|
||||
push_layer.last.next = entry
|
||||
entry.prev = push_layer.last
|
||||
push_layer.last = entry
|
||||
entry.next = nil
|
||||
}
|
||||
// else if push_layer.last == nil {
|
||||
// push_layer.last = entry
|
||||
// entry.prev = push_layer.first
|
||||
// push_layer.first.next = entry
|
||||
// entry.next = nil
|
||||
// }
|
||||
|
||||
// increment children's layers
|
||||
if entry.first != nil
|
||||
{
|
||||
for child := entry.first; child != nil; child = ui_render_entry_tranverse( child )
|
||||
{
|
||||
pop_layer := render_queue[child.layer_id]
|
||||
child.layer_id += 1
|
||||
|
||||
if child.layer_id >= i32(ui.render_queue.num) {
|
||||
append( & ui.render_queue, UI_RenderLayer {})
|
||||
render_queue = array_to_slice(ui.render_queue)
|
||||
}
|
||||
push_layer := render_queue[child.layer_id]
|
||||
|
||||
// pop from current layer
|
||||
if child == pop_layer.first {
|
||||
pop_layer.first = nil
|
||||
}
|
||||
if child == pop_layer.last {
|
||||
pop_layer.last = child.prev
|
||||
}
|
||||
|
||||
// push_back to next layer
|
||||
if push_layer.first == nil {
|
||||
push_layer.first = child
|
||||
push_layer.last = child
|
||||
}
|
||||
else {
|
||||
push_layer.last.next = child
|
||||
child.prev = push_layer.last
|
||||
push_layer.last = child
|
||||
}
|
||||
|
||||
// else if push_layer.last == nil {
|
||||
// push_layer.first.next = child
|
||||
// child.prev = push_layer.first
|
||||
// push_layer.last = child
|
||||
// }
|
||||
|
||||
} // for child := neighbor.first; child != nil; child = ui_render_entry_traverse_depth( child )
|
||||
} // if entry.first != nil
|
||||
} // for entry in to_inc_slice
|
||||
} // for & layer in render_queue
|
||||
}
|
||||
|
||||
render_list := array_to_slice(ui.render_list)
|
||||
// render_list := array_to_slice(ui.render_list)
|
||||
render_list_box := array_to_slice(ui.render_list_box)
|
||||
render_list_text := array_to_slice(ui.render_list_text)
|
||||
}
|
||||
|
||||
get_state().ui_context = nil
|
||||
}
|
||||
|
||||
// TODO(Ed): Review usage if at all.
|
||||
ui_render_entry_tranverse :: proc( entry : ^UI_RenderEntry ) -> ^UI_RenderEntry
|
||||
{
|
||||
// using state := get_state()
|
||||
|
29
code/sectr/ui/core/collision.odin
Normal file
29
code/sectr/ui/core/collision.odin
Normal file
@ -0,0 +1,29 @@
|
||||
package sectr
|
||||
|
||||
UI_SpacialIndexingMethod :: enum(i32) {
|
||||
QuadTree,
|
||||
SpacialHash,
|
||||
}
|
||||
|
||||
ui_collision_register :: proc( box : ^UI_Box )
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
ui_collision_query :: proc ( box : ^UI_Box ) -> DLL_NodePN(UI_Box) {
|
||||
return {}
|
||||
}
|
||||
|
||||
QuadTree_Tile :: struct {
|
||||
|
||||
}
|
||||
|
||||
QuadTree :: struct
|
||||
{
|
||||
boundary : Range2,
|
||||
|
||||
}
|
||||
|
||||
SpacialHashMap :: struct {
|
||||
|
||||
}
|
@ -216,11 +216,17 @@ ui_box_compute_layout :: proc( box : ^UI_Box,
|
||||
else if .Order_Children_Bottom_To_Top in layout.flags {
|
||||
ui_layout_children_vertically( box, .Bottom_To_Top )
|
||||
}
|
||||
|
||||
if computed.fresh {
|
||||
ui_collision_register( box )
|
||||
}
|
||||
}
|
||||
|
||||
ui_compute_children_overall_bounds :: proc ( box : ^UI_Box ) -> ( children_bounds : Range2 )
|
||||
{
|
||||
for current := box.first; current != nil && current.prev != box; current = ui_box_tranverse_next_depth_first( current, parent_limit = box )
|
||||
// for current := box.first; current != nil && current.prev != box; current = ui_box_tranverse_next_depth_first( current, parent_limit = box )
|
||||
for current := ui_box_tranverse_next_depth_first( box, parent_limit = box, bypass_intersection_test = false ); current != nil;
|
||||
current = ui_box_tranverse_next_depth_first( current, parent_limit = box, bypass_intersection_test = false )
|
||||
{
|
||||
if current == box do return
|
||||
if ! current.computed.fresh do ui_box_compute_layout( current )
|
||||
@ -236,7 +242,9 @@ ui_compute_children_overall_bounds :: proc ( box : ^UI_Box ) -> ( children_bound
|
||||
ui_box_compute_layout_children :: proc( box : ^UI_Box )
|
||||
{
|
||||
// for current := box.first; current != nil && current.prev != box; current = ui_box_tranverse_next_depth_first( current, parent_limit = box )
|
||||
for current := box.first; current != nil && current.prev != box; current = ui_box_traverse_next_breadth_first( current )
|
||||
// for current := box.first; current != nil && current.prev != box; current = ui_box_traverse_next_breadth_first( current )
|
||||
for current := ui_box_tranverse_next_depth_first( box, parent_limit = box, bypass_intersection_test = false ); current != nil;
|
||||
current = ui_box_tranverse_next_depth_first( current, parent_limit = box, bypass_intersection_test = false )
|
||||
{
|
||||
if current == box do return
|
||||
if current.computed.fresh do continue
|
||||
|
@ -202,6 +202,7 @@ test_whitespace_ast :: proc( default_layout : ^UI_Layout, frame_style_default :
|
||||
line_id += 1
|
||||
continue
|
||||
}
|
||||
profile("line")
|
||||
|
||||
ui_layout( text_layout )
|
||||
line_hbox := ui_widget(str_fmt( "line %v", line_id ), {.Mouse_Clickable})
|
||||
|
@ -153,7 +153,7 @@ ui_hbox_begin :: proc( direction : UI_LayoutDirection_X, label : string, flags :
|
||||
ui_hbox_end :: proc( hbox : UI_HBox, width_ref : ^f32 = nil, compute_layout := false )
|
||||
{
|
||||
// profile(#procedure)
|
||||
if compute_layout do ui_box_compute_layout(hbox.box, dont_mark_fresh = true)
|
||||
// if compute_layout do ui_box_compute_layout(hbox.box, dont_mark_fresh = true)
|
||||
// ui_layout_children_horizontally( hbox.box, hbox.direction, width_ref )
|
||||
}
|
||||
|
||||
@ -773,7 +773,7 @@ ui_vbox_begin :: proc( direction : UI_LayoutDirection_Y, label : string, flags :
|
||||
vbox.direction = direction
|
||||
vbox.box = ui_box_make( flags, label )
|
||||
vbox.signal = ui_signal_from_box( vbox.box )
|
||||
if compute_layout do ui_box_compute_layout(vbox, dont_mark_fresh = true)
|
||||
// if compute_layout do ui_box_compute_layout(vbox, dont_mark_fresh = true)
|
||||
switch direction {
|
||||
case .Top_To_Bottom:
|
||||
vbox.layout.flags |= { .Order_Children_Top_To_Bottom }
|
||||
@ -786,7 +786,7 @@ ui_vbox_begin :: proc( direction : UI_LayoutDirection_Y, label : string, flags :
|
||||
// Auto-layout children
|
||||
ui_vbox_end :: proc( vbox : UI_VBox, height_ref : ^f32 = nil, compute_layout := false ) {
|
||||
// profile(#procedure)
|
||||
if compute_layout do ui_box_compute_layout(vbox, dont_mark_fresh = true)
|
||||
// if compute_layout do ui_box_compute_layout(vbox, dont_mark_fresh = true)
|
||||
// ui_layout_children_vertically( vbox.box, vbox.direction, height_ref )
|
||||
}
|
||||
|
||||
|
@ -202,9 +202,9 @@ push-location $path_root
|
||||
$build_args += $flag_use_separate_modules
|
||||
$build_args += $flag_thread_count + $CoreCount_Physical
|
||||
# $build_args += $flag_optimize_none
|
||||
$build_args += $flag_optimize_minimal
|
||||
# $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