From 1533a14a1bbf106e88ecc08ff375d863bec3e410 Mon Sep 17 00:00:00 2001 From: Ed_ Date: Tue, 25 Jun 2024 19:13:41 -0400 Subject: [PATCH] misc changes * draw_text_string_pos_extent_zoomed can now oversample text futher (if desired) * render_ui_via_box_tree has a rudimentary render pass layering optimization Add support for the slab allocator to accept arbitrary alignments (odin's map container needs it) Messing around with 64-byte alignment as the default for the allocator... --- code/grime/array.odin | 4 +-- code/grime/hot_reload.odin | 6 ++-- code/grime/memory.odin | 5 +++ code/grime/pool_allocator.odin | 11 +++--- code/grime/profiler.odin | 6 ++-- code/grime/slab_allocator.odin | 13 +++++-- code/grime/string_cache.odin | 11 +++--- code/sectr/app/state.odin | 3 ++ code/sectr/engine/client_api.odin | 9 +++-- code/sectr/engine/render.odin | 57 +++++++++++++++++++++---------- code/sectr/engine/update.odin | 8 ++--- code/sectr/font/provider.odin | 10 +++--- code/sectr/ui/core/box.odin | 2 +- 13 files changed, 94 insertions(+), 51 deletions(-) diff --git a/code/grime/array.odin b/code/grime/array.odin index 9b533a3..e523294 100644 --- a/code/grime/array.odin +++ b/code/grime/array.odin @@ -181,12 +181,12 @@ array_append_at_slice :: proc( using self : ^Array( $ Type ), items : []Type, id return AllocatorError.None } -array_back :: proc( self : Array($Type) ) -> Type { +array_back :: #force_inline proc "contextless" ( self : Array($Type) ) -> Type { value := self.data[self.num - 1] return value } -array_push_back :: proc( using self : Array( $ Type)) -> b32 { +array_push_back :: #force_inline proc "contextless" ( using self : Array( $ Type)) -> b32 { if num == capacity { return false } diff --git a/code/grime/hot_reload.odin b/code/grime/hot_reload.odin index bd84cd0..89c063c 100644 --- a/code/grime/hot_reload.odin +++ b/code/grime/hot_reload.odin @@ -3,16 +3,16 @@ package grime import "base:runtime" reload_array :: proc( self : ^[dynamic]$Type, allocator : Allocator ) { - raw := transmute(runtime.Raw_Dynamic_Array) self + raw := transmute( ^runtime.Raw_Dynamic_Array) self raw.allocator = allocator } reload_queue :: proc( self : ^Queue($Type), allocator : Allocator ) { - raw_array := transmute(runtime.Raw_Dynamic_Array) self.data + raw_array := transmute( ^runtime.Raw_Dynamic_Array) self.data raw_array.allocator = allocator } reload_map :: proc( self : ^map [$KeyType] $EntryType, allocator : Allocator ) { - raw := transmute(runtime.Raw_Map) self + raw := transmute( ^runtime.Raw_Map) self raw.allocator = allocator } diff --git a/code/grime/memory.odin b/code/grime/memory.odin index bc424e4..586c186 100644 --- a/code/grime/memory.odin +++ b/code/grime/memory.odin @@ -87,6 +87,11 @@ memory_aign_forward :: #force_inline proc( address, alignment : uintptr) -> uint return aligned_address } + +// align_up :: proc(address: uintptr, alignment: uintptr) -> uintptr { +// return (address + alignment - 1) & ~(alignment - 1) +// } + //endregion Memory Math swap :: #force_inline proc( a, b : ^ $Type ) -> ( ^ Type, ^ Type ) { return b, a } diff --git a/code/grime/pool_allocator.odin b/code/grime/pool_allocator.odin index c46d1b1..3f345f7 100644 --- a/code/grime/pool_allocator.odin +++ b/code/grime/pool_allocator.odin @@ -353,11 +353,12 @@ pool_validate_ownership :: proc( using self : Pool, block : [] byte ) -> b32 { misalignment := (block_address - start) % uintptr(block_size) if misalignment != 0 { - ensure(false, "pool_validate_ownership: This data is within this pool's buckets, however its not aligned to the start of a block") - log(str_fmt("Block address: %p Misalignment: %p closest: %p", - transmute(rawptr)block_address, - transmute(rawptr)misalignment, - rawptr(block_address - misalignment))) + // TODO(Ed): We cannot use thsi to validate that the data is within the pool bucket as we can provide the user different alignments + // ensure(false, "pool_validate_ownership: This data is within this pool's buckets, however its not aligned to the start of a block") + // log(str_fmt("Block address: %p Misalignment: %p closest: %p", + // transmute(rawptr)block_address, + // transmute(rawptr)misalignment, + // rawptr(block_address - misalignment))) } within_bucket = true diff --git a/code/grime/profiler.odin b/code/grime/profiler.odin index d0bec4e..3facc2c 100644 --- a/code/grime/profiler.odin +++ b/code/grime/profiler.odin @@ -17,13 +17,13 @@ set_profiler_module_context :: #force_inline proc "contextless" ( ctx : ^SpallPr @(deferred_none = profile_end) profile :: #force_inline proc "contextless" ( name : string, loc := #caller_location ) { - spall._buffer_begin( & Module_Context.ctx, & Module_Context.buffer, name, "", loc ) + // spall._buffer_begin( & Module_Context.ctx, & Module_Context.buffer, name, "", loc ) } profile_begin :: #force_inline proc "contextless" ( name : string, loc := #caller_location ) { - spall._buffer_begin( & Module_Context.ctx, & Module_Context.buffer, name, "", loc ) + // spall._buffer_begin( & Module_Context.ctx, & Module_Context.buffer, name, "", loc ) } profile_end :: #force_inline proc "contextless" () { - spall._buffer_end( & Module_Context.ctx, & Module_Context.buffer) + // spall._buffer_end( & Module_Context.ctx, & Module_Context.buffer) } diff --git a/code/grime/slab_allocator.odin b/code/grime/slab_allocator.odin index 7d6de40..8c7dd0f 100644 --- a/code/grime/slab_allocator.odin +++ b/code/grime/slab_allocator.odin @@ -129,19 +129,24 @@ slab_alloc :: proc( self : Slab, loc := #caller_location ) -> ( data : []byte, alloc_error : AllocatorError ) { + // profile(#procedure) pool : Pool id : u32 = 0 for ; id < self.pools.idx; id += 1 { pool = self.pools.items[id] - if pool.block_size >= size && pool.alignment >= alignment { + adjusted_alignment := clamp(alignment, pool.alignment, alignment) + aligned_size := size + (adjusted_alignment - 1) + if pool.block_size >= aligned_size { break } } verify( id < self.pools.idx, "There is not a size class in the slab's policy to satisfy the requested allocation", location = loc ) verify( pool.header != nil, "Requested alloc not supported by the slab allocator", location = loc ) + adjusted_alignment := clamp(alignment, pool.alignment, alignment) + block : []byte slab_validate_pools( self ) block, alloc_error = pool_grab(pool) @@ -153,7 +158,11 @@ slab_alloc :: proc( self : Slab, } // log( str_fmt_tmp("%v: Retrieved block: %p %d", self.dbg_name, raw_data(block), len(block) )) - data = byte_slice(raw_data(block), size) + // Align the block + block_start := uintptr(raw_data(block)) + aligned_start := memory_aign_forward(block_start, uintptr(adjusted_alignment)) + + data = byte_slice(transmute(rawptr) aligned_start, size) if zero_memory { slice.zero(data) } diff --git a/code/grime/string_cache.odin b/code/grime/string_cache.odin index 406cb3b..425f93e 100644 --- a/code/grime/string_cache.odin +++ b/code/grime/string_cache.odin @@ -39,14 +39,15 @@ Module_String_Cache : ^StringCache str_cache_init :: proc( table_allocator, slabs_allocator : Allocator ) -> (cache : StringCache) { - alignment := uint(mem.DEFAULT_ALIGNMENT) + // alignment := uint(mem.DEFAULT_ALIGNMENT) + alignment := uint(64) policy : SlabPolicy policy_ptr := & policy - push( policy_ptr, SlabSizeClass { 64 * Kilobyte, 8, alignment }) - push( policy_ptr, SlabSizeClass { 64 * Kilobyte, 16, alignment }) - push( policy_ptr, SlabSizeClass { 128 * Kilobyte, 32, alignment }) - push( policy_ptr, SlabSizeClass { 128 * Kilobyte, 64, alignment }) + // push( policy_ptr, SlabSizeClass { 64 * Kilobyte, 8, alignment }) + // push( policy_ptr, SlabSizeClass { 64 * Kilobyte, 16, alignment }) + // push( policy_ptr, SlabSizeClass { 128 * Kilobyte, 32, alignment }) + push( policy_ptr, SlabSizeClass { 640 * Kilobyte, 64, alignment }) push( policy_ptr, SlabSizeClass { 64 * Kilobyte, 128, alignment }) push( policy_ptr, SlabSizeClass { 64 * Kilobyte, 256, alignment }) push( policy_ptr, SlabSizeClass { 64 * Kilobyte, 512, alignment }) diff --git a/code/sectr/app/state.odin b/code/sectr/app/state.odin index 2e13971..134bc88 100644 --- a/code/sectr/app/state.odin +++ b/code/sectr/app/state.odin @@ -154,11 +154,14 @@ AppConfig :: struct { engine_refresh_hz : uint, + timing_fps_moving_avg_alpha : f32, ui_resize_border_width : f32, color_theme : AppColorTheme, + + font_size_canvas_scalar : f32, } AppWindow :: struct { diff --git a/code/sectr/engine/client_api.odin b/code/sectr/engine/client_api.odin index 0bd6060..0909028 100644 --- a/code/sectr/engine/client_api.odin +++ b/code/sectr/engine/client_api.odin @@ -68,7 +68,8 @@ startup :: proc( prof : ^SpallProfiler, persistent_mem, frame_mem, transient_mem // Setup Persistent Slabs & String Cache { - alignment := uint(mem.DEFAULT_ALIGNMENT) + // alignment := uint(mem.DEFAULT_ALIGNMENT) + alignment := uint(64) // Doing the cache line policy_ptr := & default_slab_policy push( policy_ptr, SlabSizeClass { 128 * Kilobyte, 1 * Kilobyte, alignment }) @@ -150,6 +151,8 @@ startup :: proc( prof : ^SpallProfiler, persistent_mem, frame_mem, transient_mem ui_resize_border_width = 5 color_theme = App_Thm_Dusk + + font_size_canvas_scalar = 2.0 } Desired_OS_Scheduler_MS :: 1 @@ -325,8 +328,8 @@ 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 (197).txt", allocator = persistent_slab_allocator()) - // debug.path_lorem = str_fmt("C:/projects/SectrPrototype/examples/Lorem Ipsum (1022).txt", allocator = persistent_slab_allocator()) + // debug.path_lorem = str_fmt("C:/projects/SectrPrototype/examples/Lorem Ipsum (197).txt", allocator = persistent_slab_allocator()) + debug.path_lorem = str_fmt("C:/projects/SectrPrototype/examples/Lorem Ipsum (1022).txt", allocator = persistent_slab_allocator()) alloc_error : AllocatorError; success : bool debug.lorem_content, success = os.read_entire_file( debug.path_lorem, persistent_slab_allocator() ) diff --git a/code/sectr/engine/render.odin b/code/sectr/engine/render.odin index 789e81b..0b354bc 100644 --- a/code/sectr/engine/render.odin +++ b/code/sectr/engine/render.odin @@ -182,16 +182,17 @@ render_mode_screenspace :: proc() screen_corners := screen_get_corners() position := screen_corners.top_left + position.x += 2 position.y -= debug.draw_debug_text_y content := str_fmt( format, ..args ) text_size := measure_text_size( content, default_font, 14.0, 0.0 ) debug_draw_text( content, position, 14.0 ) - debug.draw_debug_text_y += text_size.y + 4 + debug.draw_debug_text_y += text_size.y + 3 } profile("debug_text_vis") - fps_size : f32 = 16.0 + fps_size : f32 = 14.0 fps_msg := str_fmt( "FPS: %0.2f", fps_avg) fps_msg_size := measure_text_size( fps_msg, default_font, fps_size, 0.0 ) fps_msg_pos := screen_get_corners().top_right - { fps_msg_size.x, fps_msg_size.y } @@ -218,7 +219,7 @@ render_mode_screenspace :: proc() iter_obj := iterator( & mouse_events ); iter := & iter_obj for event := next( iter ); event != nil; event = next( iter ) { - if id >= 4 do break + if id >= 2 do break id += 1 debug_text("Mouse Event: %v", event ) @@ -227,10 +228,10 @@ render_mode_screenspace :: proc() if debug.mouse_vis { debug_text("Mouse scroll: %v", input.mouse.scroll ) - debug_text("Mouse Delta : %v", input.mouse.delta ) - debug_text("Mouse Position (Render) : %v", input.mouse.raw_pos ) - debug_text("Mouse Position (Screen) : %v", input.mouse.pos ) - debug_text("Mouse Position (Workspace View): %v", screen_to_ws_view_pos(input.mouse.pos) ) + debug_text("Mouse Delta : %0.2f", input.mouse.delta ) + debug_text("Mouse Position (Render) : %0.2f", input.mouse.raw_pos ) + debug_text("Mouse Position (Screen) : %0.2f", input.mouse.pos ) + debug_text("Mouse Position (Workspace View): %0.2f", screen_to_ws_view_pos(input.mouse.pos) ) } if true @@ -269,6 +270,15 @@ render_mode_screenspace :: proc() } } + if true { + state.config.font_size_canvas_scalar = 1.0 + zoom_adjust_size := 16 * state.project.workspace.cam.zoom + over_sample := zoom_adjust_size < 12 ? 1.0 : f32(state.config.font_size_canvas_scalar) + debug_text("font_size_canvas_scalar: %v", config.font_size_canvas_scalar) + ve_id, resolved_size := font_provider_resolve_draw_id( default_font, zoom_adjust_size * over_sample ) + debug_text("font_size resolved: %v px", resolved_size) + } + render_text_layer() } @@ -492,10 +502,18 @@ render_ui_via_box_tree :: proc( root : ^UI_Box, cam : ^Camera = nil ) cam_zoom_ratio := cam != nil ? 1.0 / cam.zoom : 1.0 circle_radius := cam != nil ? cam_zoom_ratio * 3 : 3 + text_enqueued : b32 = false + shape_enqueued : b32 = false + + previous_layer : i32 = 0 for box := root.first; box != nil; box = ui_box_tranverse_next_depth_based( box ) { - text_enqueued : b32 = false - shape_enqueued : b32 = false + if box.ancestors != previous_layer { + if shape_enqueued do render_flush_gp() + if text_enqueued do render_text_layer() + shape_enqueued = false + text_enqueued = false + } border_width := box.layout.border_width computed := box.computed @@ -509,7 +527,7 @@ render_ui_via_box_tree :: proc( root : ^UI_Box, cam : ^Camera = nil ) GP_Render: { - // profile("draw_shapes") + profile("draw_shapes") if style.bg_color.a != 0 { draw_rect( bounds, style.bg_color ) @@ -555,9 +573,11 @@ render_ui_via_box_tree :: proc( root : ^UI_Box, cam : ^Camera = nil ) text_enqueued = true } - if shape_enqueued do render_flush_gp() - if text_enqueued do render_text_layer() + previous_layer = box.ancestors } + + 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 ) @@ -693,7 +713,7 @@ draw_text_string_pos_norm :: proc( content : string, id : FontID, size : f32, po // 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) + profile(#procedure) state := get_state(); using state screen_size := app_window.extent * 2 render_pos := screen_to_render_pos(pos) @@ -703,9 +723,9 @@ draw_text_string_pos_extent :: proc( content : string, id : FontID, size : f32, draw_text_string_pos_extent_zoomed :: proc( content : string, id : FontID, size : f32, pos : Vec2, cam : Camera, color := Color_White ) { + profile(#procedure) state := get_state(); using state - // profile(#procedure) cam_offset := Vec2 { cam.position.x, cam.position.y, @@ -719,10 +739,10 @@ draw_text_string_pos_extent_zoomed :: proc( content : string, id : FontID, size render_pos := ws_view_to_render_pos(pos) normalized_pos := render_pos * screen_scale - // Oversample font-size for any render under a camera - over_sample : f32 = 2.0 - zoom_adjust_size := size * cam.zoom + + // Over-sample font-size for any render under a camera + over_sample : f32 = zoom_adjust_size < 12 ? 1.0 : f32(state.config.font_size_canvas_scalar) zoom_adjust_size *= over_sample ve_id, resolved_size := font_provider_resolve_draw_id( id, zoom_adjust_size ) @@ -737,7 +757,7 @@ draw_text_string_pos_extent_zoomed :: proc( content : string, id : FontID, size text_scale.y = clamp( text_scale.y, 0, screen_size.y ) } - // Downsample back + // Down-sample back text_scale /= over_sample color_norm := normalize_rgba8(color) @@ -749,6 +769,7 @@ draw_text_string_pos_extent_zoomed :: proc( content : string, id : FontID, size render_flush_gp :: #force_inline proc() { + profile(#procedure) gfx.begin_pass( gfx.Pass { action = get_state().render_data.pass_actions.empty_action, swapchain = sokol_glue.swapchain() }) gp.flush() gfx.end_pass() diff --git a/code/sectr/engine/update.odin b/code/sectr/engine/update.odin index c1702fa..64be990 100644 --- a/code/sectr/engine/update.odin +++ b/code/sectr/engine/update.odin @@ -165,10 +165,10 @@ update :: proc( delta_time : f64 ) -> b32 } config.cam_max_zoom = 10 - config.cam_min_zoom = 0.10 + config.cam_min_zoom = 0.05 config.cam_zoom_sensitivity_digital = 0.05 config.cam_zoom_sensitivity_smooth = 2.0 - config.cam_zoom_mode = .Digital + config.cam_zoom_mode = .Smooth switch config.cam_zoom_mode { case .Smooth: @@ -247,9 +247,9 @@ update :: proc( delta_time : f64 ) -> b32 config.ui_resize_border_width = 2.5 // test_hover_n_click() // test_draggable() - test_text_box() + // test_text_box() // test_parenting( & default_layout, & frame_style_default ) - // test_whitespace_ast( & default_layout, & frame_style_default ) + test_whitespace_ast( & default_layout, & frame_style_default ) } //endregion Workspace Imgui Tick diff --git a/code/sectr/font/provider.odin b/code/sectr/font/provider.odin index 092ecc9..30353b2 100644 --- a/code/sectr/font/provider.odin +++ b/code/sectr/font/provider.odin @@ -8,7 +8,7 @@ import sokol_glue "thirdparty:sokol/glue" Font_Provider_Use_Freetype :: false -Font_Largest_Px_Size :: 110 +Font_Largest_Px_Size :: 172 Font_Size_Interval :: 2 Font_Default :: FontID { 0, "" } @@ -119,14 +119,14 @@ font_provider_startup :: proc() screen_shader = sokol_gfx.make_shader(ve_draw_text_shader_desc(backend) ) draw_list_vbuf = sokol_gfx.make_buffer( BufferDesciption { - size = size_of([4]f32) * Kilo * 512, + size = size_of([4]f32) * Kilo * 1024, usage = BufferUsage.STREAM, type = BufferType.VERTEXBUFFER, }) verify( sokol_gfx.query_buffer_state( draw_list_vbuf) < ResourceState.FAILED, "Failed to make draw_list_vbuf" ) draw_list_ibuf = sokol_gfx.make_buffer( BufferDesciption { - size = size_of(u32) * Kilo * 256, + size = size_of(u32) * Kilo * 512, usage = BufferUsage.STREAM, type = BufferType.INDEXBUFFER, }) @@ -563,7 +563,7 @@ font_load :: proc(path_file : string, def.path_file = path_file def.default_size = default_size - for font_size : i32 = Font_Size_Interval; font_size <= Font_Largest_Px_Size; font_size += Font_Size_Interval + for font_size : i32 = clamp( Font_Size_Interval, 10, Font_Size_Interval ); font_size <= Font_Largest_Px_Size; font_size += Font_Size_Interval { // logf("Loading at size %v", font_size) id := (font_size / Font_Size_Interval) + (font_size % Font_Size_Interval) @@ -585,7 +585,7 @@ font_provider_resolve_draw_id :: proc( id : FontID, size := Font_Use_Default_Siz def := hmap_chained_get( font_provider_data.font_cache, id.key ) size := size == 0.0 ? f32(def.default_size) : size even_size := math.round(size * (1.0 / f32(Font_Size_Interval))) * f32(Font_Size_Interval) - resolved_size = clamp( i32( even_size), 14, Font_Largest_Px_Size ) + resolved_size = clamp( i32( even_size), 12, Font_Largest_Px_Size ) id := (resolved_size / Font_Size_Interval) + (resolved_size % Font_Size_Interval) ve_id = def.size_table[ id - 1 ] diff --git a/code/sectr/ui/core/box.odin b/code/sectr/ui/core/box.odin index c2b023f..d9b56ad 100644 --- a/code/sectr/ui/core/box.odin +++ b/code/sectr/ui/core/box.odin @@ -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_depth_based :: proc "contextless" ( box : ^ UI_Box, bypass_intersection_test := false ) -> (^ UI_Box) +ui_box_tranverse_next_depth_based :: #force_inline proc "contextless" ( box : ^ UI_Box, bypass_intersection_test := false ) -> (^ UI_Box) { using state := get_state() // If current has children, do them first