From 49a4e117a25c008c7b4fe0f5f06b8d552b6ea1ab Mon Sep 17 00:00:00 2001 From: Ed_ Date: Fri, 15 Mar 2024 00:02:28 -0400 Subject: [PATCH] Got "Line LODs" working for the whitespace AST test, first step toward hboxes Yet another issue with memory persists (seems to be related to slab free) --- code/api.odin | 10 +- code/collision.odin | 36 ++++--- code/env.odin | 15 +-- code/grime.odin | 5 + code/grime_hashmap_zpl.odin | 2 +- code/math.odin | 14 ++- code/render.odin | 17 ++++ code/space.odin | 45 +++++---- code/tick_render.odin | 53 +++++----- code/tick_update.odin | 168 ++++++++++++++++++++----------- code/ui.odin | 16 ++- code/ui_layout.odin | 17 ++-- code/ui_signal.odin | 2 +- code/ui_widgets.odin | 9 +- examples/Lorem Ipsum.txt | 196 ++++++++++++++++++++++++++++++++++++ scripts/build.ps1 | 4 +- 16 files changed, 463 insertions(+), 146 deletions(-) create mode 100644 code/render.odin diff --git a/code/api.odin b/code/api.odin index 3fb9239..8e0ff04 100644 --- a/code/api.odin +++ b/code/api.odin @@ -91,8 +91,8 @@ startup :: proc( prof : ^SpallProfiler, persistent_mem, frame_mem, transient_mem push( policy_ptr, SlabSizeClass { 2 * Megabyte, 256 * Kilobyte, alignment }) push( policy_ptr, SlabSizeClass { 2 * Megabyte, 512 * Kilobyte, alignment }) push( policy_ptr, SlabSizeClass { 2 * Megabyte, 1 * Megabyte, alignment }) - push( policy_ptr, SlabSizeClass { 2 * Megabyte, 2 * Megabyte, alignment }) - push( policy_ptr, SlabSizeClass { 4 * Megabyte, 4 * Megabyte, alignment }) + push( policy_ptr, SlabSizeClass { 2 * Megabyte, 2 * Megabyte, alignment }) + push( policy_ptr, SlabSizeClass { 4 * Megabyte, 4 * Megabyte, alignment }) // push( policy_ptr, SlabSizeClass { 8 * Megabyte, 8 * Megabyte, alignment }) // push( policy_ptr, SlabSizeClass { 16 * Megabyte, 16 * Megabyte, alignment }) // push( policy_ptr, SlabSizeClass { 32 * Megabyte, 32 * Megabyte, alignment }) @@ -135,6 +135,8 @@ startup :: proc( prof : ^SpallProfiler, persistent_mem, frame_mem, transient_mem engine_refresh_hz = 30 + timing_fps_moving_avg_alpha = 0.9 + ui_resize_border_width = 5 } @@ -359,6 +361,10 @@ tick :: proc( host_delta_time : f64, host_delta_ns : Duration ) -> b32 } } + config.timing_fps_moving_avg_alpha = 0.99 + frametime_avg_ms = mov_avg_exp( f64(config.timing_fps_moving_avg_alpha), frametime_elapsed_ms, frametime_avg_ms ) + fps_avg = 1 / (frametime_avg_ms * MS_To_S) + if frametime_elapsed_ms > 60.0 { log( str_fmt_tmp("Big tick! %v ms", frametime_elapsed_ms), LogLevel.Warning ) } diff --git a/code/collision.odin b/code/collision.odin index cf6b3d8..484b412 100644 --- a/code/collision.odin +++ b/code/collision.odin @@ -2,14 +2,23 @@ package sectr import "core:math/linalg" -pos_within_range2 :: proc( pos : Vec2, range : Range2 ) -> b32 { - within_x := pos.x > range.min.x && pos.x < range.max.x - within_y := pos.y > range.min.y && pos.y < range.max.y - return b32(within_x && within_y) +// AABB: Separating Axis Theorem +intersects_range2 :: #force_inline proc "contextless" ( a, b: Range2 ) -> bool +{ + // Check if there's no overlap on the x-axis + if a.max.x < b.min.x || b.max.x < a.min.x { + return false; // No overlap on x-axis means no intersection + } + // Check if there's no overlap on the y-axis + if a.max.y < b.min.y || b.max.y < a.min.y { + return false; // No overlap on y-axis means no intersection + } + // If neither of the above conditions are true, there's at least a partial overlap + return true; } // TODO(Ed): Do we need this? Also does it even work (looks unfinished)? -is_within_screenspace :: proc( pos : Vec2 ) -> b32 { +is_within_screenspace :: #force_inline proc "contextless" ( pos : Vec2 ) -> b32 { state := get_state(); using state screen_extent := state.app_window.extent cam := & project.workspace.cam @@ -18,13 +27,14 @@ is_within_screenspace :: proc( pos : Vec2 ) -> b32 { return within_x_bounds && within_y_bounds } -within_range2 :: #force_inline proc ( a, b : Range2 ) -> bool { - a_half_size := size_range2( a ) * 0.5 - b_half_size := size_range2( b ) * 0.5 - a_center := a.p0 + { a_half_size.x, -a_half_size.y } - b_center := b.p0 + { b_half_size.x, -b_half_size.y } - - within_x := abs(a_center.x - b_center.x) <= (a_half_size.x + b_half_size.y) - within_y := abs(a_center.y - b_center.y) <= (a_half_size.y + b_half_size.y) +within_range2 :: #force_inline proc "contextless" ( a, b : Range2 ) -> bool { + within_x := b.min.x >= a.min.x && b.max.x <= a.max.x + within_y := b.min.y >= a.min.y && b.max.y <= a.max.y return within_x && within_y } + +pos_within_range2 :: #force_inline proc "contextless" ( pos : Vec2, range : Range2 ) -> b32 { + within_x := pos.x > range.min.x && pos.x < range.max.x + within_y := pos.y > range.min.y && pos.y < range.max.y + return b32(within_x && within_y) +} diff --git a/code/env.odin b/code/env.odin index 11de102..4d31a58 100644 --- a/code/env.odin +++ b/code/env.odin @@ -137,6 +137,8 @@ AppConfig :: struct { engine_refresh_hz : uint, + timing_fps_moving_avg_alpha : f32, + ui_resize_border_width : f32, } @@ -169,12 +171,13 @@ State :: struct { sleep_is_granular : b32, - frametime_delta_seconds : f64, - frametime_delta_ms : f64, - frametime_delta_ns : Duration, - frametime_target_ms : f64, - - frametime_elapsed_ms : f64, + frametime_delta_seconds : f64, + frametime_delta_ms : f64, + frametime_delta_ns : Duration, + frametime_target_ms : f64, + frametime_elapsed_ms : f64, + frametime_avg_ms : f64, + fps_avg : f64, font_firacode : FontID, font_squidgy_slimes : FontID, diff --git a/code/grime.odin b/code/grime.odin index 1676f66..b683a48 100644 --- a/code/grime.odin +++ b/code/grime.odin @@ -104,6 +104,11 @@ draw_text :: proc { draw_text_string_cached, } +mov_avg_exp :: proc { + mov_avg_exp_f32, + mov_avg_exp_f64, +} + get_bounds :: proc { view_get_bounds, } diff --git a/code/grime_hashmap_zpl.odin b/code/grime_hashmap_zpl.odin index 4e24eb1..aab2727 100644 --- a/code/grime_hashmap_zpl.odin +++ b/code/grime_hashmap_zpl.odin @@ -82,7 +82,7 @@ zpl_hmap_destroy :: proc( using self : ^ HMapZPL( $ Type ) ) { } } -zpl_hmap_get :: proc( using self : ^ HMapZPL( $ Type ), key : u64 ) -> ^ Type +zpl_hmap_get :: proc ( using self : ^ HMapZPL( $ Type ), key : u64 ) -> ^ Type { // profile(#procedure) id := zpl_hmap_find( self, key ).entry_index diff --git a/code/math.odin b/code/math.odin index 2176af8..7a26418 100644 --- a/code/math.odin +++ b/code/math.odin @@ -7,11 +7,23 @@ Axis2 :: enum i32 { Count, } -is_power_of_two_u32 :: proc( value : u32 ) -> b32 +is_power_of_two_u32 :: #force_inline proc "contextless" ( value : u32 ) -> b32 { return value != 0 && ( value & ( value - 1 )) == 0 } +mov_avg_exp_f32 := #force_inline proc "contextless" ( alpha, delta_interval, last_value : f32 ) -> f32 +{ + result := (delta_interval * alpha) + (delta_interval * (1.0 - alpha)) + return result +} + +mov_avg_exp_f64 := #force_inline proc "contextless" ( alpha, delta_interval, last_value : f64 ) -> f64 +{ + result := (delta_interval * alpha) + (delta_interval * (1.0 - alpha)) + return result +} + import "core:math/linalg" Vec2 :: linalg.Vector2f32 diff --git a/code/render.odin b/code/render.odin new file mode 100644 index 0000000..55d9eb3 --- /dev/null +++ b/code/render.odin @@ -0,0 +1,17 @@ +package sectr + +import rl "vendor:raylib" + + +range2_to_rl_rect :: #force_inline proc "contextless"( range : Range2 ) -> rl.Rectangle +{ + rect := rl.Rectangle { + range.min.x, + range.max.y, + abs(range.max.x - range.min.x), + abs(range.max.y - range.min.y), + } + return rect +} + + diff --git a/code/space.odin b/code/space.odin index 8e2a6ac..8b8fb35 100644 --- a/code/space.odin +++ b/code/space.odin @@ -32,53 +32,53 @@ when ODIN_OS == OS_Type.Windows { // return points * // } -f32_cm_to_pixels :: proc(cm: f32) -> f32 { +f32_cm_to_pixels :: #force_inline proc "contextless"(cm: f32) -> f32 { screen_ppcm := get_state().app_window.ppcm return cm * screen_ppcm } -f32_pixels_to_cm :: proc(pixels: f32) -> f32 { +f32_pixels_to_cm :: #force_inline proc "contextless"(pixels: f32) -> f32 { screen_ppcm := get_state().app_window.ppcm cm_per_pixel := 1.0 / screen_ppcm return pixels * cm_per_pixel } -f32_points_to_pixels :: proc(points: f32) -> f32 { +f32_points_to_pixels :: #force_inline proc "contextless"(points: f32) -> f32 { screen_ppcm := get_state().app_window.ppcm cm_per_pixel := 1.0 / screen_ppcm return points * DPT_PPCM * cm_per_pixel } -f32_pixels_to_points :: proc(pixels: f32) -> f32 { +f32_pixels_to_points :: #force_inline proc "contextless"(pixels: f32) -> f32 { screen_ppcm := get_state().app_window.ppcm cm_per_pixel := 1.0 / screen_ppcm return pixels * cm_per_pixel * Points_Per_CM } -vec2_cm_to_pixels :: proc(v: Vec2) -> Vec2 { +vec2_cm_to_pixels :: #force_inline proc "contextless"(v: Vec2) -> Vec2 { screen_ppcm := get_state().app_window.ppcm return v * screen_ppcm } -vec2_pixels_to_cm :: proc(v: Vec2) -> Vec2 { +vec2_pixels_to_cm :: #force_inline proc "contextless"(v: Vec2) -> Vec2 { screen_ppcm := get_state().app_window.ppcm cm_per_pixel := 1.0 / screen_ppcm return v * cm_per_pixel } -vec2_points_to_pixels :: proc(vpoints: Vec2) -> Vec2 { +vec2_points_to_pixels :: #force_inline proc "contextless"(vpoints: Vec2) -> Vec2 { screen_ppcm := get_state().app_window.ppcm cm_per_pixel := 1.0 / screen_ppcm return vpoints * DPT_PPCM * cm_per_pixel } -range2_cm_to_pixels :: proc( range : Range2 ) -> Range2 { +range2_cm_to_pixels :: #force_inline proc "contextless"( range : Range2 ) -> Range2 { screen_ppcm := get_state().app_window.ppcm result := Range2 { pts = { range.min * screen_ppcm, range.max * screen_ppcm }} return result } -range2_pixels_to_cm :: proc( range : Range2 ) -> Range2 { +range2_pixels_to_cm :: #force_inline proc "contextless"( range : Range2 ) -> Range2 { screen_ppcm := get_state().app_window.ppcm cm_per_pixel := 1.0 / screen_ppcm result := Range2 { pts = { range.min * cm_per_pixel, range.max * cm_per_pixel }} @@ -132,7 +132,7 @@ screen_size :: proc "contextless" () -> AreaSize { return transmute(AreaSize) ( extent * 2.0 ) } -screen_get_corners :: proc() -> BoundsCorners2 { +screen_get_corners :: #force_inline proc "contextless"() -> BoundsCorners2 { state := get_state(); using state screen_extent := state.app_window.extent top_left := Vec2 { -screen_extent.x, screen_extent.y } @@ -142,16 +142,17 @@ screen_get_corners :: proc() -> BoundsCorners2 { return { top_left, top_right, bottom_left, bottom_right } } -view_get_bounds :: proc() -> Range2 { - state := get_state(); using state - cam := & project.workspace.cam - screen_extent := state.app_window.extent - top_left := Vec2 { cam.target.x, -cam.target.y } + Vec2 { -screen_extent.x, screen_extent.y} * (1/cam.zoom) - bottom_right := Vec2 { cam.target.x, -cam.target.y } + Vec2 { screen_extent.x, -screen_extent.y} * (1/cam.zoom) - return range2(top_left, bottom_right) +view_get_bounds :: #force_inline proc "contextless"() -> Range2 { + state := get_state(); using state + cam := & project.workspace.cam + screen_extent := state.app_window.extent + cam_zoom_ratio := 1.0 / cam.zoom + bottom_left := Vec2 { cam.target.x, -cam.target.y } + Vec2 { -screen_extent.x, -screen_extent.y} * cam_zoom_ratio + top_right := Vec2 { cam.target.x, -cam.target.y } + Vec2 { screen_extent.x, screen_extent.y} * cam_zoom_ratio + return range2( bottom_left, top_right ) } -view_get_corners :: proc() -> BoundsCorners2 { +view_get_corners :: #force_inline proc "contextless"() -> BoundsCorners2 { state := get_state(); using state cam := & project.workspace.cam cam_zoom_ratio := 1.0 / cam.zoom @@ -170,22 +171,22 @@ screen_to_world :: #force_inline proc "contextless" (pos: Vec2) -> Vec2 { return result } -screen_to_render :: proc(pos: Vec2) -> Vec2 { +screen_to_render :: #force_inline proc "contextless"(pos: Vec2) -> Vec2 { screen_extent := transmute(Vec2) get_state().project.workspace.cam.offset return pos + { screen_extent.x, -screen_extent.y } } -world_screen_extent :: proc() -> Extents2 { +world_screen_extent :: #force_inline proc "contextless"() -> Extents2 { state := get_state(); using state cam_zoom_ratio := 1.0 / project.workspace.cam.zoom return app_window.extent * cam_zoom_ratio } -world_to_screen_pos :: proc(position: Vec2) -> Vec2 { +world_to_screen_pos :: #force_inline proc "contextless"(position: Vec2) -> Vec2 { return { position.x, position.y * -1 } } -world_to_screen_no_zoom :: proc(position: Vec2) -> Vec2 { +world_to_screen_no_zoom :: #force_inline proc "contextless"(position: Vec2) -> Vec2 { state := get_state(); using state cam_zoom_ratio := 1.0 / state.project.workspace.cam.zoom return { position.x, position.y * -1 } * cam_zoom_ratio diff --git a/code/tick_render.odin b/code/tick_render.odin index 5d49644..09585e4 100644 --- a/code/tick_render.odin +++ b/code/tick_render.odin @@ -4,6 +4,7 @@ import "core:fmt" import rl "vendor:raylib" +//@(optimization_mode="speed") render :: proc() { profile(#procedure) @@ -23,7 +24,7 @@ render :: proc() //region Render Screenspace { profile("Render Screenspace") - fps_msg := str_fmt_tmp( "FPS: %f", 1 / (frametime_elapsed_ms * MS_To_S) ) + fps_msg := str_fmt_tmp( "FPS: %f", fps_avg) fps_msg_width := measure_text_size( fps_msg, default_font, 16.0, 0.0 ).x fps_msg_pos := screen_get_corners().top_right - { fps_msg_width, 0 } debug_draw_text( fps_msg, fps_msg_pos, 16.0, color = rl.GREEN ) @@ -77,21 +78,21 @@ render :: proc() ui := project.workspace.ui - debug_text("Box Count: %v", ui.built_box_count ) + // debug_text("Box Count: %v", ui.built_box_count ) hot_box := ui_box_from_key( ui.curr_cache, ui.hot ) active_box := ui_box_from_key( ui.curr_cache, ui.active ) if hot_box != nil { debug_text("Hot Box : %v", hot_box.label.str ) - debug_text("Hot Range2: %v", hot_box.computed.bounds.pts) + // debug_text("Hot Range2: %v", hot_box.computed.bounds.pts) } if active_box != nil{ - debug_text("Active Box: %v", active_box.label.str ) + // debug_text("Active Box: %v", active_box.label.str ) } // debug_text("Active Resizing: %v", ui.active_start_signal.resizing) view := view_get_bounds() - debug_text("View Bounds (World): %v", view.pts ) + // debug_text("View Bounds (World): %v", view.pts ) debug.draw_debug_text_y = 50 } @@ -122,9 +123,9 @@ render_mode_2d :: proc() }} view_rect := rl.Rectangle { render_view.min.x, - render_view.min.y, - render_view.max.x - render_view.min.x, - render_view.max.y - render_view.min.y, + render_view.max.y, + abs(render_view.max.x - render_view.min.x), + abs(render_view.max.y - render_view.min.y), } rl.DrawRectangleRounded( view_rect, 0.3, 9, { 255, 0, 0, 20 } ) } @@ -141,7 +142,7 @@ render_mode_2d :: proc() current := root.first for ; current != nil; current = ui_box_tranverse_next( current ) { - profile("Box") + // profile("Box") parent := current.parent style := current.style @@ -149,18 +150,25 @@ render_mode_2d :: proc() computed_size := computed.bounds.p1 - computed.bounds.p0 - if ! within_range2( view_bounds, computed.bounds ) { + if ! intersects_range2( view_bounds, computed.bounds ) { continue } // TODO(Ed) : Render Borders // profile_begin("Calculating Raylib rectangles") + render_anchors := range2( + world_to_screen_pos(computed.anchors.min), + world_to_screen_pos(computed.anchors.max), + ) + render_margins := range2( + world_to_screen_pos(computed.margins.min), + world_to_screen_pos(computed.margins.max), + ) render_bounds := range2( world_to_screen_pos(computed.bounds.min), world_to_screen_pos(computed.bounds.max), ) - render_padding := range2( world_to_screen_pos(computed.padding.min), world_to_screen_pos(computed.padding.max), @@ -170,24 +178,11 @@ render_mode_2d :: proc() world_to_screen_pos(computed.content.max), ) - rect_bounds := rl.Rectangle { - render_bounds.min.x, - render_bounds.max.y, - abs(render_bounds.max.x - render_bounds.min.x), - abs(render_bounds.max.y - render_bounds.min.y), - } - rect_padding := rl.Rectangle { - render_padding.min.x, - render_padding.max.y, - abs(render_padding.max.x - render_padding.min.x), - abs(render_padding.max.y - render_padding.min.y), - } - rect_content := rl.Rectangle { - render_content.min.x, - render_content.max.y, - abs(render_content.max.x - render_content.min.x), - abs(render_content.max.y - render_content.min.y), - } + rect_anchors := range2_to_rl_rect( render_anchors ) + rect_margins := range2_to_rl_rect( render_margins ) + rect_bounds := range2_to_rl_rect( render_bounds ) + rect_padding := range2_to_rl_rect( render_padding ) + rect_content := range2_to_rl_rect( render_content ) // profile_end() draw_rectangle :: #force_inline proc "contextless" ( rect : rl.Rectangle, style : UI_Style ) { diff --git a/code/tick_update.odin b/code/tick_update.odin index 4117d94..ec25111 100644 --- a/code/tick_update.odin +++ b/code/tick_update.odin @@ -4,6 +4,7 @@ import "base:runtime" import "core:math" import "core:math/linalg" import "core:os" +import str "core:strings" import rl "vendor:raylib" @@ -63,6 +64,7 @@ frametime_delta32 :: #force_inline proc "contextless" () -> f32 { return cast(f32) get_state().frametime_delta_seconds } +//@(optimization_mode="speed") update :: proc( delta_time : f64 ) -> b32 { profile(#procedure) @@ -205,7 +207,7 @@ update :: proc( delta_time : f64 ) -> b32 } default_layout := UI_Layout { anchor = {}, - alignment = { 0.0, 0.0 }, + alignment = { 0., 0.0 }, text_alignment = { 0.0, 0.0 }, // corner_radii = { 0.2, 0.2, 0.2, 0.2 }, pos = { 0, 0 }, @@ -240,7 +242,7 @@ update :: proc( delta_time : f64 ) -> b32 // test_text_box() // test_parenting() - if true + if false { // frame := ui_widget( "Frame", {} ) // ui_parent(frame) @@ -292,11 +294,11 @@ update :: proc( delta_time : f64 ) -> b32 } child_layout := default_layout - child_layout.size = range2({ 75, 75 }, { 0, 0 }) - child_layout.alignment = { 0.5, 0.0 } + child_layout.size = range2({ 0, 0 }, { 0, 0 }) + child_layout.alignment = { 0.5, 0.5 } child_layout.margins = { 20, 20, 20, 20 } child_layout.padding = {} - child_layout.anchor = range2({ 0.0, 0.0 }, { 0.0, 1.0 }) + child_layout.anchor = range2({ 0.0, 0.0 }, { 0.0, 0.0 }) child_layout.pos = { 0, 0 } child_theme := frame_style_default @@ -310,16 +312,19 @@ update :: proc( delta_time : f64 ) -> b32 } // Whitespace AST test - if false + if true { profile("Whitespace AST test") text_style := frame_style_default text_style.flags = { - .Size_To_Text, + .Origin_At_Anchor_Center, .Fixed_Position_X, .Fixed_Position_Y, // .Fixed_Width, .Fixed_Height, } + text_style.text_alignment = { 0.0, 0.5 } + text_style.alignment = { 0.0, 1.0 } + text_style.size.min = { 1600, 30 } text_theme := UI_StyleTheme { styles = { text_style, @@ -332,7 +337,7 @@ update :: proc( delta_time : f64 ) -> b32 text_theme.hot.bg_color = Color_Frame_Hover text_theme.active.bg_color = Color_Frame_Select - layout_text := default_layout + layout_text := text_style.layout ui_style_theme( text_theme ) @@ -347,65 +352,114 @@ update :: proc( delta_time : f64 ) -> b32 // index := 0 widgets : Array(UI_Widget) - widgets, alloc_error = array_init( UI_Widget, frame_slab_allocator() ) + widgets, alloc_error = array_init_reserve( UI_Widget, frame_slab_allocator(), Kilobyte * 4 ) widgets_ptr := & widgets label_id := 0 + line_id := 0 for line in array_to_slice_num( debug.lorem_parse.lines ) { - profile("WS AST Line") - - head := line.first - for ; head != nil; - { - ui_style_theme_set_layout( layout_text ) - widget : UI_Widget - - // We're assumping PWS_Token for now... - // Eventually I'm going to flatten this, its not worth doing it the way I am... - #partial switch head.type - { - case .Visible: - label := str_intern( str_fmt_alloc( "%v %v", head.content.str, label_id )) - widget = ui_text( label.str, head.content ) - label_id += 1 - - layout_text.pos.x += size_range2( widget.computed.bounds ).x - - case .Spaces: - label := str_intern( str_fmt_alloc( "%v %v", "space", label_id )) - widget = ui_space( label.str ) - label_id += 1 - - for idx in 1 ..< len( head.content.runes ) - { - // TODO(Ed): VIRTUAL WHITESPACE - // widget.style.layout.size.x += range2_size( widget.computed.bounds ) - } - layout_text.pos.x += size_range2( widget.computed.bounds ).x - - case .Tabs: - label := str_intern( str_fmt_alloc( "%v %v", "tab", label_id )) - widget = ui_tab( label.str ) - label_id += 1 - - for idx in 1 ..< len( head.content.runes ) - { - // widget.style.layout.size.x += range2_size( widget.computed.bounds ) - } - layout_text.pos.x += size_range2( widget.computed.bounds ).x - } - - array_append( widgets_ptr, widget ) - head = head.next + if line_id == 0 { + line_id += 1 + continue } - layout_text.pos.x = default_layout.pos.x - layout_text.pos.y -= 30 + ui_style_theme_set_layout( layout_text ) + line_hbox := ui_widget(str_fmt_alloc( "line %v", line_id ), {}) + + if line_hbox.key == ui.hot + { + line_hbox.text = StringCached {} + ui_parent(line_hbox) + + chunk_layout := layout_text + chunk_layout.alignment = { 0.0, 1.0 } + chunk_layout.anchor = range2({ 0.0, 0 }, { 0.0, 0 }) + chunk_layout.pos = {} + + chunk_style := text_style + chunk_style.flags = { .Fixed_Position_X, .Size_To_Text } + chunk_style.layout = chunk_layout + + chunk_theme := UI_StyleTheme { styles = { + chunk_style, + chunk_style, + chunk_style, + chunk_style, + }} + ui_style_theme( chunk_theme ) + + head := line.first + for ; head != nil; + { + ui_style_theme_set_layout( chunk_layout ) + widget : UI_Widget + + #partial switch head.type + { + case .Visible: + label := str_intern( str_fmt_alloc( "%v %v", head.content.str, label_id )) + widget = ui_text( label.str, head.content ) + label_id += 1 + + chunk_layout.pos.x += size_range2( widget.computed.bounds ).x + + case .Spaces: + label := str_intern( str_fmt_alloc( "%v %v", "space", label_id )) + widget = ui_text_spaces( label.str ) + label_id += 1 + + for idx in 1 ..< len( head.content.runes ) + { + // TODO(Ed): VIRTUAL WHITESPACE + // widget.style.layout.size.x += range2_size( widget.computed.bounds ) + } + chunk_layout.pos.x += size_range2( widget.computed.bounds ).x + + case .Tabs: + label := str_intern( str_fmt_alloc( "%v %v", "tab", label_id )) + widget = ui_text_tabs( label.str ) + label_id += 1 + + for idx in 1 ..< len( head.content.runes ) + { + // widget.style.layout.size.x += range2_size( widget.computed.bounds ) + } + chunk_layout.pos.x += size_range2( widget.computed.bounds ).x + } + + array_append( widgets_ptr, widget ) + head = head.next + } + + line_hbox.style.size.min.x = chunk_layout.pos.x + } + else + { + builder_backing : [16 * Kilobyte] byte + builder := str.builder_from_bytes( builder_backing[:] ) + + line_hbox.style.flags |= { .Size_To_Text } + + head := line.first.next + for ; head != nil; + { + str.write_string( & builder, head.content.str ) + head = head.next + } + + line_hbox.text = str_intern( to_string( builder ) ) + } + + array_append( widgets_ptr, line_hbox ) + + layout_text.pos.x = text_style.layout.pos.x + layout_text.pos.y += size_range2(line_hbox.computed.bounds).y + line_id += 1 } - label_id += 1 + label_id += 1 // Dummy action } } //endregion Imgui Tick diff --git a/code/ui.odin b/code/ui.odin index 45a0c4f..b64791b 100644 --- a/code/ui.odin +++ b/code/ui.odin @@ -183,6 +183,10 @@ UI_StyleFlag :: enum u32 { Fixed_Width, Fixed_Height, + // Sets the (0, 0) position of the child box to the parents anchor's center (post-margins bounds) + // By Default, the origin is at the top left of the anchor's bounds + Origin_At_Anchor_Center, + // Will size the box to its text. (Padding & Margins will thicken ) Size_To_Text, Text_Wrap, @@ -340,7 +344,7 @@ ui_reload :: proc( ui : ^ UI_State, cache_allocator : Allocator ) ui_shutdown :: proc() { } -ui_box_equal :: proc( a, b : ^ UI_Box ) -> b32 { +ui_box_equal :: #force_inline proc "contextless" ( a, b : ^ UI_Box ) -> b32 { BoxSize :: size_of(UI_Box) result : b32 = true @@ -349,10 +353,11 @@ ui_box_equal :: proc( a, b : ^ UI_Box ) -> b32 { return result } -ui_box_from_key :: proc( cache : ^HMapZPL(UI_Box), key : UI_Key ) -> (^UI_Box) { +ui_box_from_key :: #force_inline proc ( cache : ^HMapZPL(UI_Box), key : UI_Key ) -> (^UI_Box) { return zpl_hmap_get( cache, cast(u64) key ) } +//@(optimization_mode="speed") ui_box_make :: proc( flags : UI_BoxFlags, label : string ) -> (^ UI_Box) { // profile(#procedure) @@ -407,8 +412,10 @@ ui_box_make :: proc( flags : UI_BoxFlags, label : string ) -> (^ UI_Box) ui_box_tranverse_next :: proc "contextless" ( box : ^ UI_Box ) -> (^ UI_Box) { + // Check to make sure parent is present on the screen, if its not don't bother. // If current has children, do them first - if box.first != nil { + if intersects_range2( view_get_bounds(), box.computed.bounds) && box.first != nil + { return box.first } @@ -479,7 +486,8 @@ ui_graph_build :: proc( ui : ^ UI_State ) { ui_graph_build_begin( ui ) } -ui_key_from_string :: proc( value : string ) -> UI_Key +//@(optimization_mode="speed") +ui_key_from_string :: #force_inline proc "contextless" ( value : string ) -> UI_Key { // profile(#procedure) USE_RAD_DEBUGGERS_METHOD :: true diff --git a/code/ui_layout.odin b/code/ui_layout.odin index 89a3652..c4bfb14 100644 --- a/code/ui_layout.odin +++ b/code/ui_layout.odin @@ -3,6 +3,8 @@ package sectr import "core:math" import "core:math/linalg" +// Note(Ed): This is naturally pretty expensive +//@(optimization_mode="speed") ui_compute_layout :: proc() { profile(#procedure) @@ -21,7 +23,7 @@ ui_compute_layout :: proc() current := root.first for ; current != nil; { - profile("Layout Box") + // profile("Layout Box") style := current.style // These are used to choose via multiplication weather to apply @@ -118,15 +120,18 @@ ui_compute_layout :: proc() // 5. Determine relative position - // TODO(Ed): Let the user determine the coordinate space origin? - // rel_pos := margined_bounds_origin + alignment_offset + layout.pos - rel_pos := margined_bounds_origin + layout.pos + origin_center := margined_bounds_origin + origin_top_left := Vec2 { margined_bounds.min.x, margined_bounds.max.y } + + origin := .Origin_At_Anchor_Center in style.flags ? origin_center : origin_top_left + + rel_pos := origin + layout.pos if .Fixed_Position_X in style.flags { - rel_pos.x = parent_center.x + layout.pos.x + rel_pos.x = origin.x + layout.pos.x } if .Fixed_Position_Y in style.flags { - rel_pos.y = parent_center.y + layout.pos.y + rel_pos.y = origin.y + layout.pos.y } vec2_one := Vec2 { 1, 1 } diff --git a/code/ui_signal.odin b/code/ui_signal.odin index 31f08f9..95d9059 100644 --- a/code/ui_signal.odin +++ b/code/ui_signal.odin @@ -1,5 +1,6 @@ package sectr +//@(optimization_mode="speed") ui_signal_from_box :: proc ( box : ^ UI_Box ) -> UI_Signal { // profile(#procedure) @@ -182,7 +183,6 @@ ui_signal_from_box :: proc ( box : ^ UI_Box ) -> UI_Signal if ! was_active { box.prev_style = box.style box.style_delta = 0 - log( str_fmt_tmp("NEW ACTIVE: %v", box.label.str)) } box.style = stack_peek( & ui.theme_stack ).active } diff --git a/code/ui_widgets.odin b/code/ui_widgets.odin index 1098e51..8129b85 100644 --- a/code/ui_widgets.odin +++ b/code/ui_widgets.odin @@ -5,6 +5,7 @@ UI_Widget :: struct { using signal : UI_Signal, } +//@(optimization_mode="speed") ui_widget :: proc( label : string, flags : UI_BoxFlags ) -> (widget : UI_Widget) { // profile(#procedure) @@ -14,6 +15,7 @@ ui_widget :: proc( label : string, flags : UI_BoxFlags ) -> (widget : UI_Widget) return } +//@(optimization_mode="speed") ui_button :: proc( label : string, flags : UI_BoxFlags = {} ) -> (btn : UI_Widget) { // profile(#procedure) @@ -24,6 +26,7 @@ ui_button :: proc( label : string, flags : UI_BoxFlags = {} ) -> (btn : UI_Widge return } +//@(optimization_mode="speed") ui_text :: proc( label : string, content : StringCached, flags : UI_BoxFlags = {} ) -> UI_Widget { // profile(#procedure) @@ -36,7 +39,8 @@ ui_text :: proc( label : string, content : StringCached, flags : UI_BoxFlags = { return { box, signal } } -ui_space :: proc( label : string, flags : UI_BoxFlags = {} ) -> UI_Widget +//@(optimization_mode="speed") +ui_text_spaces :: proc( label : string, flags : UI_BoxFlags = {} ) -> UI_Widget { // profile(#procedure) state := get_state(); using state @@ -51,7 +55,8 @@ ui_space :: proc( label : string, flags : UI_BoxFlags = {} ) -> UI_Widget return { box, signal } } -ui_tab :: proc( label : string, flags : UI_BoxFlags = {} ) -> UI_Widget +//@(optimization_mode="speed") +ui_text_tabs :: proc( label : string, flags : UI_BoxFlags = {} ) -> UI_Widget { // profile(#procedure) state := get_state(); using state diff --git a/examples/Lorem Ipsum.txt b/examples/Lorem Ipsum.txt index 96238d2..9fdaacf 100644 --- a/examples/Lorem Ipsum.txt +++ b/examples/Lorem Ipsum.txt @@ -60,3 +60,199 @@ Curabitur egestas laoreet mauris sed cursus. Duis nec purus vel nibh venenatis dignissim. Nam eget aliquam eros, vitae pellentesque neque. Praesent mollis augue risus, ut commodo arcu auctor vel. + + +Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi laoreet bibendum finibus. +Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. +Aenean ut elementum leo, eu luctus lorem. +Sed sit amet dapibus odio, at porttitor magna. +Cras id justo et risus porta eleifend. +Donec fringilla porta faucibus. Maecenas metus dolor, ornare eu justo sed, mattis porta ante. +Maecenas faucibus odio nisl, non posuere felis laoreet et. +Nullam tincidunt varius lacus sit amet laoreet. +Proin sodales vestibulum dolor, non condimentum arcu sodales nec. +Sed a tortor pharetra ante vehicula porta. +Maecenas congue venenatis euismod. +Suspendisse dapibus id lorem efficitur faucibus. +Aliquam consectetur urna in turpis consectetur, eu vehicula diam egestas. + +Cras a semper metus, et porttitor tortor. +Aenean ac nisi consectetur, fermentum leo et, elementum odio. +Etiam imperdiet quam tellus, non suscipit lectus mattis ut. Nulla egestas urna vitae ex consectetur aliquam. +Maecenas vel luctus nibh. Morbi eget nisl justo. +Donec condimentum dolor id quam lacinia, vel ullamcorper mauris gravida. +Aliquam erat volutpat. Aliquam vitae neque venenatis, ultrices dui non, ornare velit. +Vivamus mollis ligula a ligula commodo ultrices. +Pellentesque ante felis, ultrices in risus eu, faucibus tincidunt ante. +Suspendisse potenti. Sed eget ligula mauris. Donec lorem est, porttitor auctor varius sed, lobortis nec eros. + +Integer urna ligula, auctor ac sapien et, volutpat elementum leo. +Ut commodo arcu a turpis tempor, id semper justo egestas. +Aliquam erat volutpat. +Sed placerat malesuada eros. +Suspendisse egestas auctor magna a aliquam. +Pellentesque interdum pretium hendrerit. +Sed eget libero massa. +Nam egestas viverra odio, et ultrices risus scelerisque vel. +Nunc sodales laoreet elementum. +Mauris et risus nec erat placerat lobortis. +Donec ultrices eleifend mi. +Nullam tempus, felis at sodales finibus, libero mauris luctus sem, vehicula dictum lectus quam et lectus. +Phasellus et ligula nisl. + +Sed dui enim, efficitur quis viverra nec, facilisis sed est. +Proin eget lectus diam. +Nullam in purus elementum, pharetra eros sed, volutpat metus. +Vestibulum pellentesque efficitur mauris, ut iaculis purus vehicula imperdiet. +Aliquam sit amet dolor id justo aliquam cursus. +Etiam mollis, tellus ut iaculis molestie, nunc libero feugiat arcu, vitae mattis neque leo a erat. +Pellentesque non ex interdum nulla faucibus tincidunt in vel magna. +Nam nec condimentum lacus, at pellentesque sem. +Pellentesque tristique pulvinar aliquet. +Sed varius dolor in sapien varius, sed blandit ipsum viverra. +Duis consectetur lacus dolor, non pellentesque enim pulvinar a. + +Proin id leo vel ligula hendrerit facilisis. +Sed fringilla tellus est, non pretium sem consectetur fermentum. +Nam a tellus augue. +Suspendisse quis odio nibh. +Cras pellentesque turpis a mauris euismod, vel posuere enim eleifend. +Phasellus purus ex, mollis at ante at, convallis interdum tellus. +Proin at porttitor mauris, ut egestas ligula. +Suspendisse ultricies commodo lorem, quis auctor turpis efficitur ut. +Curabitur egestas laoreet mauris sed cursus. +Duis nec purus vel nibh venenatis dignissim. +Nam eget aliquam eros, vitae pellentesque neque. +Praesent mollis augue risus, ut commodo arcu auctor vel. + + +Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi laoreet bibendum finibus. +Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. +Aenean ut elementum leo, eu luctus lorem. +Sed sit amet dapibus odio, at porttitor magna. +Cras id justo et risus porta eleifend. +Donec fringilla porta faucibus. Maecenas metus dolor, ornare eu justo sed, mattis porta ante. +Maecenas faucibus odio nisl, non posuere felis laoreet et. +Nullam tincidunt varius lacus sit amet laoreet. +Proin sodales vestibulum dolor, non condimentum arcu sodales nec. +Sed a tortor pharetra ante vehicula porta. +Maecenas congue venenatis euismod. +Suspendisse dapibus id lorem efficitur faucibus. +Aliquam consectetur urna in turpis consectetur, eu vehicula diam egestas. + +Cras a semper metus, et porttitor tortor. +Aenean ac nisi consectetur, fermentum leo et, elementum odio. +Etiam imperdiet quam tellus, non suscipit lectus mattis ut. Nulla egestas urna vitae ex consectetur aliquam. +Maecenas vel luctus nibh. Morbi eget nisl justo. +Donec condimentum dolor id quam lacinia, vel ullamcorper mauris gravida. +Aliquam erat volutpat. Aliquam vitae neque venenatis, ultrices dui non, ornare velit. +Vivamus mollis ligula a ligula commodo ultrices. +Pellentesque ante felis, ultrices in risus eu, faucibus tincidunt ante. +Suspendisse potenti. Sed eget ligula mauris. Donec lorem est, porttitor auctor varius sed, lobortis nec eros. + +Integer urna ligula, auctor ac sapien et, volutpat elementum leo. +Ut commodo arcu a turpis tempor, id semper justo egestas. +Aliquam erat volutpat. +Sed placerat malesuada eros. +Suspendisse egestas auctor magna a aliquam. +Pellentesque interdum pretium hendrerit. +Sed eget libero massa. +Nam egestas viverra odio, et ultrices risus scelerisque vel. +Nunc sodales laoreet elementum. +Mauris et risus nec erat placerat lobortis. +Donec ultrices eleifend mi. +Nullam tempus, felis at sodales finibus, libero mauris luctus sem, vehicula dictum lectus quam et lectus. +Phasellus et ligula nisl. + +Sed dui enim, efficitur quis viverra nec, facilisis sed est. +Proin eget lectus diam. +Nullam in purus elementum, pharetra eros sed, volutpat metus. +Vestibulum pellentesque efficitur mauris, ut iaculis purus vehicula imperdiet. +Aliquam sit amet dolor id justo aliquam cursus. +Etiam mollis, tellus ut iaculis molestie, nunc libero feugiat arcu, vitae mattis neque leo a erat. +Pellentesque non ex interdum nulla faucibus tincidunt in vel magna. +Nam nec condimentum lacus, at pellentesque sem. +Pellentesque tristique pulvinar aliquet. +Sed varius dolor in sapien varius, sed blandit ipsum viverra. +Duis consectetur lacus dolor, non pellentesque enim pulvinar a. + +Proin id leo vel ligula hendrerit facilisis. +Sed fringilla tellus est, non pretium sem consectetur fermentum. +Nam a tellus augue. +Suspendisse quis odio nibh. +Cras pellentesque turpis a mauris euismod, vel posuere enim eleifend. +Phasellus purus ex, mollis at ante at, convallis interdum tellus. +Proin at porttitor mauris, ut egestas ligula. +Suspendisse ultricies commodo lorem, quis auctor turpis efficitur ut. +Curabitur egestas laoreet mauris sed cursus. +Duis nec purus vel nibh venenatis dignissim. +Nam eget aliquam eros, vitae pellentesque neque. +Praesent mollis augue risus, ut commodo arcu auctor vel. + + +Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi laoreet bibendum finibus. +Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. +Aenean ut elementum leo, eu luctus lorem. +Sed sit amet dapibus odio, at porttitor magna. +Cras id justo et risus porta eleifend. +Donec fringilla porta faucibus. Maecenas metus dolor, ornare eu justo sed, mattis porta ante. +Maecenas faucibus odio nisl, non posuere felis laoreet et. +Nullam tincidunt varius lacus sit amet laoreet. +Proin sodales vestibulum dolor, non condimentum arcu sodales nec. +Sed a tortor pharetra ante vehicula porta. +Maecenas congue venenatis euismod. +Suspendisse dapibus id lorem efficitur faucibus. +Aliquam consectetur urna in turpis consectetur, eu vehicula diam egestas. + +Cras a semper metus, et porttitor tortor. +Aenean ac nisi consectetur, fermentum leo et, elementum odio. +Etiam imperdiet quam tellus, non suscipit lectus mattis ut. Nulla egestas urna vitae ex consectetur aliquam. +Maecenas vel luctus nibh. Morbi eget nisl justo. +Donec condimentum dolor id quam lacinia, vel ullamcorper mauris gravida. +Aliquam erat volutpat. Aliquam vitae neque venenatis, ultrices dui non, ornare velit. +Vivamus mollis ligula a ligula commodo ultrices. +Pellentesque ante felis, ultrices in risus eu, faucibus tincidunt ante. +Suspendisse potenti. Sed eget ligula mauris. Donec lorem est, porttitor auctor varius sed, lobortis nec eros. + +Integer urna ligula, auctor ac sapien et, volutpat elementum leo. +Ut commodo arcu a turpis tempor, id semper justo egestas. +Aliquam erat volutpat. +Sed placerat malesuada eros. +Suspendisse egestas auctor magna a aliquam. +Pellentesque interdum pretium hendrerit. +Sed eget libero massa. +Nam egestas viverra odio, et ultrices risus scelerisque vel. +Nunc sodales laoreet elementum. +Mauris et risus nec erat placerat lobortis. +Donec ultrices eleifend mi. +Nullam tempus, felis at sodales finibus, libero mauris luctus sem, vehicula dictum lectus quam et lectus. +Phasellus et ligula nisl. + +Sed dui enim, efficitur quis viverra nec, facilisis sed est. +Proin eget lectus diam. +Nullam in purus elementum, pharetra eros sed, volutpat metus. +Vestibulum pellentesque efficitur mauris, ut iaculis purus vehicula imperdiet. +Aliquam sit amet dolor id justo aliquam cursus. +Etiam mollis, tellus ut iaculis molestie, nunc libero feugiat arcu, vitae mattis neque leo a erat. +Pellentesque non ex interdum nulla faucibus tincidunt in vel magna. +Nam nec condimentum lacus, at pellentesque sem. +Pellentesque tristique pulvinar aliquet. +Sed varius dolor in sapien varius, sed blandit ipsum viverra. +Duis consectetur lacus dolor, non pellentesque enim pulvinar a. + +Proin id leo vel ligula hendrerit facilisis. +Sed fringilla tellus est, non pretium sem consectetur fermentum. +Nam a tellus augue. +Suspendisse quis odio nibh. +Cras pellentesque turpis a mauris euismod, vel posuere enim eleifend. +Phasellus purus ex, mollis at ante at, convallis interdum tellus. +Proin at porttitor mauris, ut egestas ligula. +Suspendisse ultricies commodo lorem, quis auctor turpis efficitur ut. +Curabitur egestas laoreet mauris sed cursus. +Duis nec purus vel nibh venenatis dignissim. +Nam eget aliquam eros, vitae pellentesque neque. +Praesent mollis augue risus, ut commodo arcu auctor vel. + + + + diff --git a/scripts/build.ps1 b/scripts/build.ps1 index 26afe7d..22e8f83 100644 --- a/scripts/build.ps1 +++ b/scripts/build.ps1 @@ -243,10 +243,10 @@ push-location $path_root # $build_args += $flag_micro_architecture_native # $build_args += $flag_use_separate_modules $build_args += $flag_thread_count + $CoreCount_Physical - $build_args += $flag_optimize_none + # $build_args += $flag_optimize_none # $build_args += $flag_optimize_minimal # $build_args += $flag_optimize_speed - # $build_args += $falg_optimize_aggressive + $build_args += $falg_optimize_aggressive $build_args += $flag_debug $build_args += $flag_pdb_name + $pdb $build_args += $flag_subsystem + 'windows'