Minor lifting in the render side, getting ready to just hunker down and take notes on whats next
* VEFontCache needs to get fixed up (possibly bring back ELFHash) * Problably going to do conversion early to odin's array and map usage * Need get the quad tree setup for the ui so that I can do fast and efficient traversal for the layer based rendering.
This commit is contained in:
parent
0d9623c340
commit
c93c0ed567
@ -325,7 +325,7 @@ startup :: proc( prof : ^SpallProfiler, persistent_mem, frame_mem, transient_mem
|
||||
ui_startup( & workspace.ui, cache_allocator = persistent_slab_allocator() )
|
||||
}
|
||||
|
||||
debug.path_lorem = str_fmt("C:/projects/SectrPrototype/examples/Lorem Ipsum.txt", allocator = persistent_slab_allocator())
|
||||
debug.path_lorem = str_fmt("C:/projects/SectrPrototype/examples/Lorem_Ispsum (197).txt", allocator = persistent_slab_allocator())
|
||||
|
||||
alloc_error : AllocatorError; success : bool
|
||||
debug.lorem_content, success = os.read_entire_file( debug.path_lorem, persistent_slab_allocator() )
|
||||
|
@ -19,133 +19,6 @@ RenderState :: struct {
|
||||
pass_actions : PassActions,
|
||||
}
|
||||
|
||||
#region("Helpers")
|
||||
|
||||
draw_filled_circle :: proc(x, y, radius: f32, edges: int)
|
||||
{
|
||||
if edges < 3 do return // Need at least 3 edges to form a shape
|
||||
|
||||
triangles := make([]gp.Triangle, edges)
|
||||
center := gp.Point{x, y}
|
||||
edge_quotient := 1 / f32(edges)
|
||||
angle_factor := 2 * math.PI * edge_quotient
|
||||
for edge_id in 0..< edges
|
||||
{
|
||||
angle1 := f32(edge_id ) * angle_factor
|
||||
angle2 := f32(edge_id +1) * angle_factor
|
||||
|
||||
p1 := gp.Point{
|
||||
x + radius * math.cos(angle1),
|
||||
y + radius * math.sin(angle1),
|
||||
}
|
||||
p2 := gp.Point{
|
||||
x + radius * math.cos(angle2),
|
||||
y + radius * math.sin(angle2),
|
||||
}
|
||||
triangles[edge_id] = gp.Triangle{center, p1, p2}
|
||||
}
|
||||
|
||||
gp.draw_filled_triangles(raw_data(triangles), u32(len(triangles)))
|
||||
}
|
||||
|
||||
draw_rect :: proc( rect : Range2, color : RGBA8 ) {
|
||||
using rect
|
||||
render_set_color( color )
|
||||
|
||||
size := max - min
|
||||
position := min
|
||||
gp.draw_filled_rect( position.x, position.y, size.x, size.y )
|
||||
}
|
||||
|
||||
draw_rect_border :: proc( rect : Range2, border_width: f32)
|
||||
{
|
||||
rect_size := rect.max - rect.min
|
||||
border_width := lalg.min(border_width, min(rect_size.x, rect_size.y) * 0.5)
|
||||
|
||||
top := gp.Rect{ rect.min.x, rect.min.y, rect_size.x, border_width }
|
||||
bottom := gp.Rect{ rect.min.x, rect.max.y - border_width, rect_size.x, border_width }
|
||||
left := gp.Rect{ rect.min.x, rect.min.y + border_width, border_width, rect_size.y - 2 * border_width }
|
||||
right := gp.Rect{ rect.max.x - border_width, rect.min.y + border_width, border_width, rect_size.y - 2 * border_width }
|
||||
|
||||
borders := []gp.Rect{ top, bottom, left, right }
|
||||
gp.draw_filled_rects( raw_data(borders), u32(len(borders)) )
|
||||
}
|
||||
|
||||
// Draw text using a string and normalized render coordinates
|
||||
draw_text_string_pos_norm :: proc( content : string, id : FontID, size : f32, pos : Vec2, color := Color_White, scale : f32 = 1.0 )
|
||||
{
|
||||
state := get_state(); using state
|
||||
width := app_window.extent.x * 2
|
||||
height := app_window.extent.y * 2
|
||||
|
||||
ve_id := font_provider_resolve_draw_id( id, size )
|
||||
color_norm := normalize_rgba8(color)
|
||||
|
||||
ve.set_colour( & font_provider_data.ve_font_cache, color_norm )
|
||||
ve.draw_text( & font_provider_data.ve_font_cache, ve_id, content, pos, Vec2{1 / width, 1 / height} * scale )
|
||||
return
|
||||
}
|
||||
|
||||
// Draw text using a string and extent-based screen coordinates
|
||||
draw_text_string_pos_extent :: proc( content : string, id : FontID, size : f32, pos : Vec2, color := Color_White )
|
||||
{
|
||||
// profile(#procedure)
|
||||
state := get_state(); using state
|
||||
screen_size := app_window.extent * 2
|
||||
render_pos := screen_to_render_pos(pos)
|
||||
normalized_pos := render_pos * (1.0 / screen_size)
|
||||
draw_text_string_pos_norm( content, id, size, normalized_pos, color )
|
||||
}
|
||||
|
||||
draw_text_string_pos_extent_zoomed :: proc( content : string, id : FontID, size : f32, pos : Vec2, cam : Camera, color := Color_White )
|
||||
{
|
||||
// profile(#procedure)
|
||||
cam_offset := Vec2 {
|
||||
cam.position.x,
|
||||
cam.position.y,
|
||||
}
|
||||
|
||||
pos_offset := pos + cam_offset * cam.zoom
|
||||
|
||||
cam_zoom_ratio := 1 / cam.zoom
|
||||
|
||||
state := get_state(); using state
|
||||
screen_size := app_window.extent * 2
|
||||
render_pos := screen_to_render_pos(pos_offset)
|
||||
normalized_pos := render_pos * (1.0 / screen_size)
|
||||
draw_text_string_pos_norm( content, id, size, normalized_pos, color, cam.zoom )
|
||||
}
|
||||
|
||||
// TODO(Ed): Eventually the workspace will need a viewport for drawing text
|
||||
|
||||
render_flush_gp :: #force_inline proc()
|
||||
{
|
||||
gfx.begin_pass( gfx.Pass { action = get_state().render_data.pass_actions.empty_action, swapchain = sokol_glue.swapchain() })
|
||||
gp.flush()
|
||||
gfx.end_pass()
|
||||
}
|
||||
|
||||
@(deferred_none=gp.reset_transform)
|
||||
render_set_camera :: #force_inline proc( cam : Camera )
|
||||
{
|
||||
gp.translate( cam.position.x * cam.zoom, cam.position.y * cam.zoom )
|
||||
gp.scale( cam.zoom, cam.zoom )
|
||||
}
|
||||
|
||||
render_set_color :: #force_inline proc( color : RGBA8 ) {
|
||||
color := normalize_rgba8(color);
|
||||
gp.set_color( color.r, color.g, color.b, color.a )
|
||||
}
|
||||
|
||||
render_set_view_space :: #force_inline proc( extent : Extents2 )
|
||||
{
|
||||
size := extent * 2
|
||||
gp.viewport(0, 0, i32(size.x), i32(size.y))
|
||||
gp.project( -extent.x, extent.x, extent.y, -extent.y )
|
||||
}
|
||||
|
||||
#endregion("Helpers")
|
||||
|
||||
render :: proc()
|
||||
{
|
||||
profile(#procedure)
|
||||
@ -224,59 +97,15 @@ render_mode_2d_workspace :: proc()
|
||||
render_set_camera(cam)
|
||||
|
||||
ui := & project.workspace.ui
|
||||
render_list := array_to_slice( ui.render_list )
|
||||
|
||||
text_enqueued : b32 = false
|
||||
shape_enqueued : b32 = false
|
||||
|
||||
for entry, id in render_list
|
||||
{
|
||||
already_passed_signal := id > 0 && render_list[ id - 1 ].layer_signal
|
||||
if !already_passed_signal && entry.layer_signal
|
||||
{
|
||||
profile("render ui layer")
|
||||
render_flush_gp()
|
||||
if text_enqueued do render_text_layer()
|
||||
continue
|
||||
}
|
||||
using entry
|
||||
|
||||
profile("enqueue box")
|
||||
|
||||
GP_Render:
|
||||
{
|
||||
// profile("draw_shapes")
|
||||
if style.bg_color.a != 0
|
||||
{
|
||||
draw_rect( bounds, style.bg_color )
|
||||
shape_enqueued = true
|
||||
}
|
||||
|
||||
if style.border_color.a != 0 && border_width > 0 {
|
||||
render_set_color( style.border_color )
|
||||
draw_rect_border( bounds, 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)
|
||||
|
||||
render_set_color(Color_Blue)
|
||||
draw_filled_circle(bounds.max.x, bounds.max.y, 3, 24)
|
||||
shape_enqueued = true
|
||||
}
|
||||
}
|
||||
|
||||
if len(text.str) > 0 && style.font.key != 0 {
|
||||
draw_text_string_pos_extent_zoomed( text.str, default_font, font_size, computed.text_pos, cam, style.text_color )
|
||||
text_enqueued = true
|
||||
}
|
||||
when UI_Render_Method == .Layers {
|
||||
render_list := array_to_slice( ui.render_list )
|
||||
render_ui_via_box_list( render_list, & cam )
|
||||
}
|
||||
when UI_Render_Method == .Depth_First
|
||||
{
|
||||
render_ui_via_box_tree( ui.root, & cam )
|
||||
}
|
||||
|
||||
if shape_enqueued do render_flush_gp()
|
||||
if text_enqueued do render_text_layer()
|
||||
}
|
||||
|
||||
render_mode_screenspace :: proc()
|
||||
@ -441,69 +270,19 @@ render_screen_ui :: proc()
|
||||
text_enqueued : b32 = false
|
||||
shape_enqueued : b32 = false
|
||||
|
||||
render_list := array_to_slice( ui.render_list )
|
||||
for entry, id in render_list
|
||||
{
|
||||
if entry.layer_signal
|
||||
{
|
||||
profile("render ui layer")
|
||||
render_flush_gp()
|
||||
if text_enqueued do render_text_layer()
|
||||
continue
|
||||
}
|
||||
using entry
|
||||
|
||||
profile("enqueue box")
|
||||
|
||||
GP_Render:
|
||||
{
|
||||
profile("draw_shapes")
|
||||
|
||||
draw_rect :: proc( rect : Range2, color : RGBA8 ) {
|
||||
using rect
|
||||
render_set_color( color )
|
||||
|
||||
size := max - min
|
||||
position := min
|
||||
gp.draw_filled_rect( position.x, position.y, size.x, size.y )
|
||||
}
|
||||
|
||||
if style.bg_color.a != 0
|
||||
{
|
||||
draw_rect( bounds, style.bg_color )
|
||||
shape_enqueued = true
|
||||
}
|
||||
|
||||
if style.border_color.a != 0 && border_width > 0 {
|
||||
render_set_color( style.border_color )
|
||||
draw_rect_border( bounds, 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)
|
||||
|
||||
render_set_color(Color_Blue)
|
||||
draw_filled_circle(bounds.max.x, bounds.max.y, 3, 24)
|
||||
shape_enqueued = true
|
||||
}
|
||||
}
|
||||
|
||||
if len(text.str) > 0 && style.font.key != 0 {
|
||||
draw_text_string_pos_extent( text.str, default_font, font_size, computed.text_pos, style.text_color )
|
||||
text_enqueued = true
|
||||
}
|
||||
when UI_Render_Method == .Layers {
|
||||
render_list := array_to_slice( ui.render_list )
|
||||
render_ui_via_box_list( render_list )
|
||||
}
|
||||
when UI_Render_Method == .Depth_First
|
||||
{
|
||||
render_ui_via_box_tree( ui.root )
|
||||
}
|
||||
|
||||
if shape_enqueued do render_flush_gp()
|
||||
if text_enqueued do render_text_layer()
|
||||
}
|
||||
|
||||
render_text_layer :: proc()
|
||||
{
|
||||
profile("VEFontCache: render text layer")
|
||||
// profile("VEFontCache: render text layer")
|
||||
|
||||
Bindings :: gfx.Bindings
|
||||
Range :: gfx.Range
|
||||
@ -681,3 +460,254 @@ render_text_layer :: proc()
|
||||
gfx.end_pass()
|
||||
}
|
||||
}
|
||||
|
||||
render_ui_via_box_tree :: proc( root : ^UI_Box, cam : ^Camera = nil )
|
||||
{
|
||||
debug := get_state().debug
|
||||
default_font := get_state().default_font
|
||||
|
||||
for box := root.first; box != nil; box = ui_box_tranverse_next_depth_based( box )
|
||||
{
|
||||
text_enqueued : b32 = false
|
||||
shape_enqueued : b32 = false
|
||||
|
||||
border_width := box.layout.border_width
|
||||
computed := box.computed
|
||||
font_size := box.layout.font_size
|
||||
style := box.style
|
||||
text := box.text
|
||||
|
||||
using computed
|
||||
|
||||
// profile("enqueue box")
|
||||
|
||||
GP_Render:
|
||||
{
|
||||
// profile("draw_shapes")
|
||||
if style.bg_color.a != 0
|
||||
{
|
||||
draw_rect( bounds, style.bg_color )
|
||||
shape_enqueued = true
|
||||
}
|
||||
|
||||
if style.border_color.a != 0 && border_width > 0 {
|
||||
render_set_color( style.border_color )
|
||||
draw_rect_border( bounds, 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)
|
||||
|
||||
render_set_color(Color_Blue)
|
||||
draw_filled_circle(bounds.max.x, bounds.max.y, 3, 24)
|
||||
shape_enqueued = true
|
||||
}
|
||||
}
|
||||
|
||||
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 do render_flush_gp()
|
||||
if text_enqueued do render_text_layer()
|
||||
}
|
||||
}
|
||||
|
||||
render_ui_via_box_list :: proc( render_list : []UI_RenderBoxInfo, cam : ^Camera = nil )
|
||||
{
|
||||
debug := get_state().debug
|
||||
default_font := get_state().default_font
|
||||
|
||||
text_enqueued : b32 = false
|
||||
shape_enqueued : b32 = false
|
||||
|
||||
for entry, id in render_list
|
||||
{
|
||||
already_passed_signal := id > 0 && render_list[ id - 1 ].layer_signal
|
||||
if !already_passed_signal && entry.layer_signal
|
||||
{
|
||||
profile("render ui layer")
|
||||
render_flush_gp()
|
||||
if text_enqueued do render_text_layer()
|
||||
continue
|
||||
}
|
||||
using entry
|
||||
|
||||
profile("enqueue box")
|
||||
|
||||
GP_Render:
|
||||
{
|
||||
// profile("draw_shapes")
|
||||
if style.bg_color.a != 0
|
||||
{
|
||||
draw_rect( bounds, style.bg_color )
|
||||
shape_enqueued = true
|
||||
}
|
||||
|
||||
if style.border_color.a != 0 && border_width > 0 {
|
||||
render_set_color( style.border_color )
|
||||
draw_rect_border( bounds, 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)
|
||||
|
||||
render_set_color(Color_Blue)
|
||||
draw_filled_circle(bounds.max.x, bounds.max.y, 3, 24)
|
||||
shape_enqueued = true
|
||||
}
|
||||
}
|
||||
|
||||
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 do render_flush_gp()
|
||||
if text_enqueued do render_text_layer()
|
||||
}
|
||||
|
||||
#region("Helpers")
|
||||
|
||||
draw_filled_circle :: proc(x, y, radius: f32, edges: int)
|
||||
{
|
||||
if edges < 3 do return // Need at least 3 edges to form a shape
|
||||
|
||||
triangles := make([]gp.Triangle, edges)
|
||||
center := gp.Point{x, y}
|
||||
edge_quotient := 1 / f32(edges)
|
||||
angle_factor := 2 * math.PI * edge_quotient
|
||||
for edge_id in 0..< edges
|
||||
{
|
||||
angle1 := f32(edge_id ) * angle_factor
|
||||
angle2 := f32(edge_id +1) * angle_factor
|
||||
|
||||
p1 := gp.Point{
|
||||
x + radius * math.cos(angle1),
|
||||
y + radius * math.sin(angle1),
|
||||
}
|
||||
p2 := gp.Point{
|
||||
x + radius * math.cos(angle2),
|
||||
y + radius * math.sin(angle2),
|
||||
}
|
||||
triangles[edge_id] = gp.Triangle{center, p1, p2}
|
||||
}
|
||||
|
||||
gp.draw_filled_triangles(raw_data(triangles), u32(len(triangles)))
|
||||
}
|
||||
|
||||
draw_rect :: proc( rect : Range2, color : RGBA8 ) {
|
||||
using rect
|
||||
render_set_color( color )
|
||||
|
||||
size := max - min
|
||||
position := min
|
||||
gp.draw_filled_rect( position.x, position.y, size.x, size.y )
|
||||
}
|
||||
|
||||
draw_rect_border :: proc( rect : Range2, border_width: f32)
|
||||
{
|
||||
rect_size := rect.max - rect.min
|
||||
border_width := lalg.min(border_width, min(rect_size.x, rect_size.y) * 0.5)
|
||||
|
||||
top := gp.Rect{ rect.min.x, rect.min.y, rect_size.x, border_width }
|
||||
bottom := gp.Rect{ rect.min.x, rect.max.y - border_width, rect_size.x, border_width }
|
||||
left := gp.Rect{ rect.min.x, rect.min.y + border_width, border_width, rect_size.y - 2 * border_width }
|
||||
right := gp.Rect{ rect.max.x - border_width, rect.min.y + border_width, border_width, rect_size.y - 2 * border_width }
|
||||
|
||||
borders := []gp.Rect{ top, bottom, left, right }
|
||||
gp.draw_filled_rects( raw_data(borders), u32(len(borders)) )
|
||||
}
|
||||
|
||||
// Draw text using a string and normalized render coordinates
|
||||
draw_text_string_pos_norm :: proc( content : string, id : FontID, size : f32, pos : Vec2, color := Color_White, scale : f32 = 1.0 )
|
||||
{
|
||||
state := get_state(); using state
|
||||
width := app_window.extent.x * 2
|
||||
height := app_window.extent.y * 2
|
||||
|
||||
ve_id := font_provider_resolve_draw_id( id, size )
|
||||
color_norm := normalize_rgba8(color)
|
||||
|
||||
ve.set_colour( & font_provider_data.ve_font_cache, color_norm )
|
||||
ve.draw_text( & font_provider_data.ve_font_cache, ve_id, content, pos, Vec2{1 / width, 1 / height} * scale )
|
||||
return
|
||||
}
|
||||
|
||||
// Draw text using a string and extent-based screen coordinates
|
||||
draw_text_string_pos_extent :: proc( content : string, id : FontID, size : f32, pos : Vec2, color := Color_White )
|
||||
{
|
||||
// profile(#procedure)
|
||||
state := get_state(); using state
|
||||
screen_size := app_window.extent * 2
|
||||
render_pos := screen_to_render_pos(pos)
|
||||
normalized_pos := render_pos * (1.0 / screen_size)
|
||||
draw_text_string_pos_norm( content, id, size, normalized_pos, color )
|
||||
}
|
||||
|
||||
draw_text_string_pos_extent_zoomed :: proc( content : string, id : FontID, size : f32, pos : Vec2, cam : Camera, color := Color_White )
|
||||
{
|
||||
// profile(#procedure)
|
||||
cam_offset := Vec2 {
|
||||
cam.position.x,
|
||||
cam.position.y,
|
||||
}
|
||||
|
||||
pos_offset := pos + cam_offset * cam.zoom
|
||||
|
||||
cam_zoom_ratio := 1 / cam.zoom
|
||||
|
||||
state := get_state(); using state
|
||||
screen_size := app_window.extent * 2
|
||||
render_pos := screen_to_render_pos(pos_offset)
|
||||
normalized_pos := render_pos * (1.0 / screen_size)
|
||||
draw_text_string_pos_norm( content, id, size * cam.zoom, normalized_pos, color, cam.zoom )
|
||||
}
|
||||
|
||||
// TODO(Ed): Eventually the workspace will need a viewport for drawing text
|
||||
|
||||
render_flush_gp :: #force_inline proc()
|
||||
{
|
||||
gfx.begin_pass( gfx.Pass { action = get_state().render_data.pass_actions.empty_action, swapchain = sokol_glue.swapchain() })
|
||||
gp.flush()
|
||||
gfx.end_pass()
|
||||
}
|
||||
|
||||
@(deferred_none=gp.reset_transform)
|
||||
render_set_camera :: #force_inline proc( cam : Camera )
|
||||
{
|
||||
gp.translate( cam.position.x * cam.zoom, cam.position.y * cam.zoom )
|
||||
gp.scale( cam.zoom, cam.zoom )
|
||||
}
|
||||
|
||||
render_set_color :: #force_inline proc( color : RGBA8 ) {
|
||||
color := normalize_rgba8(color);
|
||||
gp.set_color( color.r, color.g, color.b, color.a )
|
||||
}
|
||||
|
||||
render_set_view_space :: #force_inline proc( extent : Extents2 )
|
||||
{
|
||||
size := extent * 2
|
||||
gp.viewport(0, 0, i32(size.x), i32(size.y))
|
||||
gp.project( -extent.x, extent.x, extent.y, -extent.y )
|
||||
}
|
||||
|
||||
#endregion("Helpers")
|
||||
|
@ -167,6 +167,7 @@ update :: proc( delta_time : f64 ) -> b32
|
||||
config.cam_max_zoom = 30
|
||||
config.cam_zoom_sensitivity_digital = 0.04
|
||||
// config.cam_min_zoom = 0.04
|
||||
config.cam_zoom_sensitivity_smooth = 0.02
|
||||
config.cam_zoom_mode = .Smooth
|
||||
switch config.cam_zoom_mode
|
||||
{
|
||||
|
@ -8,7 +8,7 @@ import sokol_glue "thirdparty:sokol/glue"
|
||||
|
||||
|
||||
Font_Provider_Use_Freetype :: false
|
||||
Font_Largest_Px_Size :: 72
|
||||
Font_Largest_Px_Size :: 132
|
||||
Font_Size_Interval :: 2
|
||||
|
||||
Font_Default :: FontID { 0, "" }
|
||||
@ -217,8 +217,8 @@ font_provider_startup :: proc()
|
||||
})
|
||||
|
||||
glyph_rt_sampler = sokol_gfx.make_sampler( SamplerDescription {
|
||||
min_filter = Filter.NEAREST,
|
||||
mag_filter = Filter.NEAREST,
|
||||
min_filter = Filter.LINEAR,
|
||||
mag_filter = Filter.LINEAR,
|
||||
mipmap_filter = Filter.NONE,
|
||||
wrap_u = .CLAMP_TO_EDGE,
|
||||
wrap_v = .CLAMP_TO_EDGE,
|
||||
@ -354,8 +354,8 @@ font_provider_startup :: proc()
|
||||
verify( sokol_gfx.query_image_state(atlas_rt_depth) < ResourceState.FAILED, "Failed to make atlas_rt_depth")
|
||||
|
||||
atlas_rt_sampler = sokol_gfx.make_sampler( SamplerDescription {
|
||||
min_filter = Filter.NEAREST,
|
||||
mag_filter = Filter.NEAREST,
|
||||
min_filter = Filter.LINEAR,
|
||||
mag_filter = Filter.LINEAR,
|
||||
mipmap_filter = Filter.NONE,
|
||||
wrap_u = .CLAMP_TO_EDGE,
|
||||
wrap_v = .CLAMP_TO_EDGE,
|
||||
|
@ -84,6 +84,13 @@ UI_RenderBoxInfo :: struct {
|
||||
layer_signal : b32,
|
||||
}
|
||||
|
||||
UI_RenderMethod :: enum u32 {
|
||||
Depth_First,
|
||||
Layers,
|
||||
}
|
||||
|
||||
UI_Render_Method :: UI_RenderMethod.Depth_First
|
||||
|
||||
// TODO(Ed): Rename to UI_Context
|
||||
UI_State :: struct {
|
||||
// TODO(Ed) : Use these?
|
||||
@ -96,6 +103,7 @@ UI_State :: struct {
|
||||
prev_cache : ^HMapZPL( UI_Box ),
|
||||
curr_cache : ^HMapZPL( UI_Box ),
|
||||
|
||||
// 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),
|
||||
@ -214,186 +222,191 @@ ui_graph_build_end :: proc( ui : ^UI_State )
|
||||
computed.content = computed.bounds
|
||||
}
|
||||
|
||||
|
||||
// Auto-layout and initial render_queue generation
|
||||
render_queue := array_to_slice(ui.render_queue)
|
||||
for current := root.first; current != nil; current = ui_box_tranverse_next( current )
|
||||
for current := root.first; current != nil; current = ui_box_tranverse_next_depth_based( current )
|
||||
{
|
||||
if ! current.computed.fresh {
|
||||
ui_box_compute_layout( current )
|
||||
}
|
||||
|
||||
// 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 UI_Render_Method == .Layers
|
||||
{
|
||||
|
||||
if entry.layer_id >= i32(ui.render_queue.num) {
|
||||
append( & ui.render_queue, UI_RenderLayer {})
|
||||
render_queue = array_to_slice(ui.render_queue)
|
||||
}
|
||||
// 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,
|
||||
}
|
||||
|
||||
// else if layer.last == nil {
|
||||
// layer.first.next = entry
|
||||
// entry.prev = layer.first
|
||||
// layer.last = entry
|
||||
// }
|
||||
if entry.layer_id >= i32(ui.render_queue.num) {
|
||||
append( & ui.render_queue, UI_RenderLayer {})
|
||||
render_queue = array_to_slice(ui.render_queue)
|
||||
}
|
||||
|
||||
// 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 )
|
||||
// else if layer.last == nil {
|
||||
// layer.first.next = entry
|
||||
// entry.prev = layer.first
|
||||
// layer.last = entry
|
||||
// }
|
||||
|
||||
// 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
|
||||
// push_back to next layer
|
||||
layer := & render_queue[entry.layer_id]
|
||||
if layer.first == nil {
|
||||
layer.first = entry
|
||||
layer.last = entry
|
||||
}
|
||||
else {
|
||||
parent_entry.last = entry
|
||||
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 )
|
||||
}
|
||||
// dll_fl_append( parent_entry, entry )
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
when UI_Render_Method == .Layers
|
||||
{
|
||||
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
|
||||
// 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
|
||||
{
|
||||
for neighbor := entry.next; neighbor != nil; neighbor = neighbor.next
|
||||
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
|
||||
{
|
||||
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 )
|
||||
for neighbor := entry.next; neighbor != nil; neighbor = neighbor.next
|
||||
{
|
||||
pop_layer := render_queue[child.layer_id]
|
||||
child.layer_id += 1
|
||||
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 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]
|
||||
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
|
||||
|
||||
// pop from current layer
|
||||
if child == pop_layer.first {
|
||||
pop_layer.first = nil
|
||||
}
|
||||
if child == pop_layer.last {
|
||||
pop_layer.last = child.prev
|
||||
}
|
||||
// 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]
|
||||
|
||||
// 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
|
||||
}
|
||||
// pop entry from layer
|
||||
prev := entry.prev
|
||||
prev.next = entry.next
|
||||
if entry == pop_layer.last {
|
||||
pop_layer.last = prev
|
||||
}
|
||||
|
||||
// else if push_layer.last == nil {
|
||||
// push_layer.first.next = child
|
||||
// child.prev = push_layer.first
|
||||
// push_layer.last = child
|
||||
// }
|
||||
// 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
|
||||
// }
|
||||
|
||||
} // 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
|
||||
// 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_queue := array_to_slice(ui.render_queue)
|
||||
render_list := array_to_slice(ui.render_list)
|
||||
|
||||
get_state().ui_context = nil
|
||||
}
|
||||
|
||||
|
@ -118,7 +118,7 @@ ui_prev_cached_box :: #force_inline proc( box : ^UI_Box ) -> ^UI_Box { return hm
|
||||
|
||||
// TODO(Ed): Rename to ui_box_tranverse_view_next
|
||||
// Traveral pritorizes immeidate children
|
||||
ui_box_tranverse_next :: proc "contextless" ( box : ^ UI_Box, bypass_intersection_test := false ) -> (^ UI_Box)
|
||||
ui_box_tranverse_next_depth_based :: proc "contextless" ( box : ^ UI_Box, bypass_intersection_test := false ) -> (^ UI_Box)
|
||||
{
|
||||
using state := get_state()
|
||||
// If current has children, do them first
|
||||
|
@ -181,7 +181,7 @@ ui_compute_children_bounding_area :: proc ( box : ^UI_Box )
|
||||
|
||||
ui_box_compute_layout_children :: proc( box : ^UI_Box )
|
||||
{
|
||||
for current := box.first; current != nil && current.prev != box; current = ui_box_tranverse_next( current )
|
||||
for current := box.first; current != nil && current.prev != box; current = ui_box_tranverse_next_depth_based( current )
|
||||
{
|
||||
if current == box do return
|
||||
if current.computed.fresh do continue
|
||||
|
Loading…
Reference in New Issue
Block a user