From 368abefccfec18f04907149a7df83d672e0e75ba Mon Sep 17 00:00:00 2001 From: Ed_ Date: Wed, 22 May 2024 15:39:19 -0400 Subject: [PATCH] Restored some of the window awareness previously avail in raylib for the new sokol layer. Switched frametime_delta32 to frametime_avg_ms. I'll problably rename it and keept delta32 for actual delta. Reading over the sokol_app code I noticed it uses the avg frametime and definitely want to do that for spike alleviation... --- Sectr.sublime-project | 1 + code/sectr/app/state.odin | 3 +- code/sectr/engine/client_api_sokol.odin | 143 ++++++++---------- .../engine/client_api_sokol_callbacks.odin | 38 ++++- code/sectr/engine/update.odin | 2 +- thirdparty/sokol | 2 +- 6 files changed, 105 insertions(+), 84 deletions(-) diff --git a/Sectr.sublime-project b/Sectr.sublime-project index 367410a..a9c3743 100644 --- a/Sectr.sublime-project +++ b/Sectr.sublime-project @@ -12,6 +12,7 @@ "ols": { "enabled": true, + "enable_snippets": false, }, "odin": { diff --git a/code/sectr/app/state.odin b/code/sectr/app/state.odin index f2287b6..d516fd6 100644 --- a/code/sectr/app/state.odin +++ b/code/sectr/app/state.odin @@ -250,7 +250,8 @@ State :: struct { // The camera is considered the "context" for coodrinate space operations in rendering cam_context : Camera, - sokol_context : runtime.Context, + sokol_frame_count : i64, + sokol_context : runtime.Context, } get_state :: #force_inline proc "contextless" () -> ^ State { diff --git a/code/sectr/engine/client_api_sokol.odin b/code/sectr/engine/client_api_sokol.odin index e3abf52..795b307 100644 --- a/code/sectr/engine/client_api_sokol.odin +++ b/code/sectr/engine/client_api_sokol.odin @@ -170,41 +170,21 @@ startup :: proc( prof : ^SpallProfiler, persistent_mem, frame_mem, transient_mem sokol_app.pre_client_init(desc) sokol_app.client_init() + window := & state.app_window + window.extent.x = sokol_app.widthf() + window.extent.y = sokol_app.heightf() + + // TODO(Ed): We don't need monitor tracking until we have multi-window support (which I don't think I'll do for this prototype) + // Sokol doesn't provide it. + // config.current_monitor = sokol_app.monitor_id() + monitor_refresh_hz = sokol_app.refresh_rate() + // if config.engine_refresh_hz == 0 { // config.engine_refresh_hz = sokol_app.frame_duration() // } if config.engine_refresh_hz == 0 { - config.engine_refresh_hz = 165 + config.engine_refresh_hz = uint(monitor_refresh_hz) } - - // rl.Odin_SetMalloc( RL_MALLOC ) - - // rl.SetConfigFlags( { - // rl.ConfigFlag.WINDOW_RESIZABLE, - // rl.ConfigFlag.WINDOW_TOPMOST, - // }) - - // window_width : i32 = cast(i32) config.resolution_width - // window_height : i32 = cast(i32) config.resolution_height - // win_title : cstring = "Sectr Prototype" - // rl.InitWindow( window_width, window_height, win_title ) - // log( "Raylib initialized and window opened" ) - - // window := & state.app_window - // window.extent.x = f32(window_width) * 0.5 - // window.extent.y = f32(window_height) * 0.5 - - // We do not support non-uniform DPI. - // window.dpi_scale = rl.GetWindowScaleDPI().x - // window.ppcm = os_default_ppcm * window.dpi_scale - - // Determining current monitor and setting the target frametime based on it.. - // monitor_id = rl.GetCurrentMonitor() - // monitor_refresh_hz = rl.GetMonitorRefreshRate( monitor_id ) - - // if config.engine_refresh_hz == 0 { - // config.engine_refresh_hz = uint(monitor_refresh_hz) - // } } // Basic Font Setup @@ -351,7 +331,7 @@ reload :: proc( prof : ^SpallProfiler, persistent_mem, frame_mem, transient_mem, } @export -tick :: proc( host_delta_time : f64, host_delta_ns : Duration ) -> b32 +tick :: proc( host_delta_time_ms : f64, host_delta_ns : Duration ) -> b32 { should_close : b32 @@ -364,7 +344,17 @@ tick :: proc( host_delta_time : f64, host_delta_ns : Duration ) -> b32 state := get_state(); using state client_tick := time.tick_now() - { + tick_work_frame() + tick_frametime( & client_tick, host_delta_time_ms, host_delta_ns ) + return ! should_close +} + + +// Lifted out of tick so that sokol_app_frame_callback can do it as well. +tick_work_frame :: #force_inline proc() +{ + context.logger = to_odin_logger( & Memory_App.logger ) + state := get_state(); using state profile("Work frame") // Setup Frame Slab @@ -394,60 +384,61 @@ tick :: proc( host_delta_time : f64, host_delta_ns : Duration ) -> b32 // render() // rl.SwapScreenBuffer() - } +} - // Timing +// Lifted out of tick so that sokol_app_frame_callback can do it as well. +tick_frametime :: #force_inline proc( client_tick : ^time.Tick, host_delta_time_ms : f64, host_delta_ns : Duration ) +{ + state := get_state(); using state + + // profile("Client tick timing processing") + // config.engine_refresh_hz = uint(monitor_refresh_hz) + // config.engine_refresh_hz = 6 + frametime_target_ms = 1.0 / f64(config.engine_refresh_hz) * S_To_MS + sub_ms_granularity_required := frametime_target_ms <= Frametime_High_Perf_Threshold_MS + + frametime_delta_ns = time.tick_lap_time( client_tick ) + frametime_delta_ms = duration_ms( frametime_delta_ns ) + frametime_delta_seconds = duration_seconds( host_delta_ns ) + frametime_elapsed_ms = frametime_delta_ms + host_delta_time_ms + + if frametime_elapsed_ms < frametime_target_ms { - // profile("Client tick timing processing") - // config.engine_refresh_hz = uint(monitor_refresh_hz) - // config.engine_refresh_hz = 6 - frametime_target_ms = 1.0 / f64(config.engine_refresh_hz) * S_To_MS - sub_ms_granularity_required := frametime_target_ms <= Frametime_High_Perf_Threshold_MS + sleep_ms := frametime_target_ms - frametime_elapsed_ms + pre_sleep_tick := time.tick_now() - frametime_delta_ns = time.tick_lap_time( & client_tick ) - frametime_delta_ms = duration_ms( frametime_delta_ns ) - frametime_delta_seconds = duration_seconds( frametime_delta_ns ) - frametime_elapsed_ms = frametime_delta_ms + host_delta_time + if sleep_ms > 0 { + thread_sleep( cast(Duration) sleep_ms * MS_To_NS ) + // thread__highres_wait( sleep_ms ) + } - if frametime_elapsed_ms < frametime_target_ms - { - sleep_ms := frametime_target_ms - frametime_elapsed_ms - pre_sleep_tick := time.tick_now() + sleep_delta_ns := time.tick_lap_time( & pre_sleep_tick) + sleep_delta_ms := duration_ms( sleep_delta_ns ) - if sleep_ms > 0 { - thread_sleep( cast(Duration) sleep_ms * MS_To_NS ) - // thread__highres_wait( sleep_ms ) - } + if sleep_delta_ms < sleep_ms { + // log( str_fmt_tmp("frametime sleep was off by: %v ms", sleep_delta_ms - sleep_ms )) + } - sleep_delta_ns := time.tick_lap_time( & pre_sleep_tick) - sleep_delta_ms := duration_ms( sleep_delta_ns ) - - if sleep_delta_ms < sleep_ms { - // log( str_fmt_tmp("frametime sleep was off by: %v ms", sleep_delta_ms - sleep_ms )) - } + frametime_elapsed_ms += sleep_delta_ms + for ; frametime_elapsed_ms < frametime_target_ms; { + sleep_delta_ns = time.tick_lap_time( & pre_sleep_tick) + sleep_delta_ms = duration_ms( sleep_delta_ns ) frametime_elapsed_ms += sleep_delta_ms - for ; frametime_elapsed_ms < frametime_target_ms; { - sleep_delta_ns = time.tick_lap_time( & pre_sleep_tick) - sleep_delta_ms = duration_ms( sleep_delta_ns ) - - frametime_elapsed_ms += sleep_delta_ms - } } - - 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 ) - } - - profile_begin("sokol_app: post_client_tick") - sokol_app.post_client_frame() - profile_end() } - return ! should_close + + 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 ) + } + + profile_begin("sokol_app: post_client_tick") + sokol_app.post_client_frame() + profile_end() } @export diff --git a/code/sectr/engine/client_api_sokol_callbacks.odin b/code/sectr/engine/client_api_sokol_callbacks.odin index 57dabad..73e9434 100644 --- a/code/sectr/engine/client_api_sokol_callbacks.odin +++ b/code/sectr/engine/client_api_sokol_callbacks.odin @@ -1,6 +1,7 @@ package sectr import "base:runtime" +import "core:time" import str "core:strings" import sokol_app "thirdparty:sokol/app" @@ -11,10 +12,29 @@ sokol_app_init_callback :: proc "c" () { } // This is being filled in but we're directly controlling the lifetime of sokol_app's execution. -// Thus we have no need for it todo frame callbacks +// So this will only get called during resize events (on Win32 at least) sokol_app_frame_callback :: proc "c" () { context = get_state().sokol_context - log("sokol_app: SHOULD NOT HAVE CALLED THE FRAME CALLABCK") + + state := get_state() + + sokol_width := sokol_app.widthf() + sokol_height := sokol_app.heightf() + + window := & state.app_window + if int(window.extent.x) != int(sokol_width) || int(window.extent.y) != int(sokol_height) { + window.extent.x = sokol_width + window.extent.y = sokol_height + log("sokol_app: Event-based frame callback triggered (detected a resize") + } + + // sokol_app is the only good reference for a frame-time at this point. + sokol_delta_ms := sokol_app.frame_delta() + sokol_delta_ns := transmute(Duration) sokol_delta_ms * MS_To_NS + + client_tick := time.tick_now() + tick_work_frame() + tick_frametime( & client_tick, sokol_delta_ms, sokol_delta_ns ) } sokol_app_cleanup_callback :: proc "c" () { @@ -61,9 +81,17 @@ sokol_app_log_callback :: proc "c" ( cloned_fname = str.clone_from_cstring(filename_or_null, context.temp_allocator) } - logf( "%-80s %v : %s::%s", cloned_msg, cloned_fname, str.clone_from_cstring(tag), level = odin_level ) + cloned_tag := str.clone_from_cstring(tag, context.temp_allocator) + logf( "%-80s %s::%v", cloned_msg, cloned_tag, line_nr, level = odin_level ) } -sokol_app_event_callback :: proc "c" (event : ^sokol_app.Event) { - +sokol_app_event_callback :: proc "c" (event : ^sokol_app.Event) +{ + state := get_state(); using state + context = sokol_context + if event.type == sokol_app.Event_Type.DISPLAY_CHANGED { + logf("sokol_app - event: Display changed") + logf("refresh rate: %v", sokol_app.refresh_rate()); + monitor_refresh_hz := sokol_app.refresh_rate() + } } diff --git a/code/sectr/engine/update.odin b/code/sectr/engine/update.odin index 092ff30..791dfad 100644 --- a/code/sectr/engine/update.odin +++ b/code/sectr/engine/update.odin @@ -63,7 +63,7 @@ poll_debug_actions :: proc( actions : ^ DebugActions, input : ^ InputState ) } frametime_delta32 :: #force_inline proc "contextless" () -> f32 { - return cast(f32) get_state().frametime_delta_seconds + return cast(f32) get_state().frametime_avg_ms } update :: proc( delta_time : f64 ) -> b32 diff --git a/thirdparty/sokol b/thirdparty/sokol index 3e340da..bb8e308 160000 --- a/thirdparty/sokol +++ b/thirdparty/sokol @@ -1 +1 @@ -Subproject commit 3e340da1e09886c0e22244f223bdfb919e6810aa +Subproject commit bb8e3081b913d2df5aa184122ed911ad1be151ee