From c94e372e7dd8f69151079ef4947618ebb62df122 Mon Sep 17 00:00:00 2001 From: Noel Berry Date: Sun, 21 Aug 2022 17:41:29 -0700 Subject: [PATCH] added App flags, implemented v-sync and fixed timestep flags --- include/blah/app.h | 34 ++++++++++-- src/app.cpp | 94 +++++++++++++++++++++++++-------- src/internal/platform.h | 6 +-- src/internal/platform_sdl2.cpp | 41 +++++++------- src/internal/platform_win32.cpp | 54 ++++++++++--------- src/internal/renderer.h | 3 ++ src/internal/renderer_d3d11.cpp | 3 +- 7 files changed, 159 insertions(+), 76 deletions(-) diff --git a/include/blah/app.h b/include/blah/app.h index 142528a..16b8347 100644 --- a/include/blah/app.h +++ b/include/blah/app.h @@ -11,6 +11,15 @@ namespace Blah // Application Logging Functions using AppLogFn = Func; + // Application flags + namespace Flags + { + constexpr u32 FixedTimestep = 1 << 0; + constexpr u32 VSync = 1 << 1; + constexpr u32 Fullscreen = 1 << 2; + constexpr u32 Resizable = 1 << 3; + } + // Application Configuration struct Config { @@ -34,10 +43,13 @@ namespace Blah // defaults to 5. int max_updates = 5; - // target framerate. + // target framerate when running with Fixed Timestep // defaults to 60. int target_framerate = 60; + // default starting flags + u32 flags = Flags::VSync | Flags::Resizable | Flags::FixedTimestep; + // Callback on application startup AppEventFn on_startup = nullptr; @@ -113,9 +125,23 @@ namespace Blah // If the window is currently focused or has mouse input bool focused(); - // Toggles fullscreen if supported on the platform. - // Otherwise this function does nothing. - void fullscreen(bool enabled); + // Toggles a specific flag + void set_flag(u32 flag, bool enabled); + + // Gets whether a specific flag is enabled + bool get_flag(u32 flag); + + // Toggles the fullscreen flag + inline void set_fullscreen(bool enabled) { set_flag(Flags::Fullscreen, enabled); } + + // Toggles the V-Sync flag + inline void set_vsync(bool enabled) { set_flag(Flags::VSync, enabled); } + + // Toggles the resizable flag + inline void set_resizable(bool enabled) { set_flag(Flags::Resizable, enabled); } + + // Toggles whether to update with Fixed Timestep + inline void set_fixedtimestep(bool enabled) { set_flag(Flags::FixedTimestep, enabled); } // Retrieves the Renderer Information const RendererInfo& renderer(); diff --git a/src/app.cpp b/src/app.cpp index e5b4120..5b20e90 100644 --- a/src/app.cpp +++ b/src/app.cpp @@ -28,6 +28,7 @@ namespace bool app_is_exiting = false; u64 app_time_last; u64 app_time_accumulator = 0; + u32 app_flags = 0; TargetRef app_backbuffer; void get_drawable_size(int* w, int* h) @@ -90,6 +91,7 @@ bool App::run(const Config* c) // default values app_is_running = true; app_is_exiting = false; + app_flags = app_config.flags; app_backbuffer = TargetRef(new BackBuffer()); // initialize the system @@ -128,6 +130,10 @@ bool App::run(const Config* c) } } + // apply default flags + Internal::platform->set_app_flags(app_flags); + Internal::renderer->set_app_flags(app_flags); + // input + poll the platform once Input::Internal::init(); Input::Internal::step_state(); @@ -164,14 +170,26 @@ bool App::is_running() void App::Internal::iterate() { - // update at a fixed timerate - // TODO: allow a non-fixed step update? + static const auto step = []() + { + Input::Internal::step_state(); + platform->update(Input::state); + Input::Internal::update_bindings(); + renderer->update(); + if (app_config.on_update != nullptr) + app_config.on_update(); + }; + + bool is_fixed_timestep = get_flag(Flags::FixedTimestep); + + // Update in Fixed Timestep + if (is_fixed_timestep) { u64 time_target = (u64)((1.0 / app_config.target_framerate) * Time::ticks_per_second); - u64 time_curr = App::Internal::platform->ticks(); - u64 time_diff = time_curr - app_time_last; - app_time_last = time_curr; - app_time_accumulator += time_diff; + u64 ticks_curr = App::Internal::platform->ticks(); + u64 ticks_diff = ticks_curr - app_time_last; + app_time_last = ticks_curr; + app_time_accumulator += ticks_diff; // do not let us run too fast while (app_time_accumulator < time_target) @@ -179,10 +197,10 @@ void App::Internal::iterate() int milliseconds = (int)(time_target - app_time_accumulator) / (Time::ticks_per_second / 1000); App::Internal::platform->sleep(milliseconds); - time_curr = App::Internal::platform->ticks(); - time_diff = time_curr - app_time_last; - app_time_last = time_curr; - app_time_accumulator += time_diff; + ticks_curr = App::Internal::platform->ticks(); + ticks_diff = ticks_curr - app_time_last; + app_time_last = ticks_curr; + app_time_accumulator += ticks_diff; } // Do not allow us to fall behind too many updates @@ -212,23 +230,39 @@ void App::Internal::iterate() Time::previous_seconds = Time::seconds; Time::seconds += Time::delta; - Input::Internal::step_state(); - platform->update(Input::state); - Input::Internal::update_bindings(); - renderer->update(); + step(); + } + } + // Update with Variable Timestep + else + { + u64 ticks_curr = App::Internal::platform->ticks(); + u64 ticks_diff = ticks_curr - app_time_last; + app_time_last = ticks_curr; + app_time_accumulator += ticks_diff; - if (app_config.on_update != nullptr) - app_config.on_update(); + Time::delta = ticks_diff / (float)Time::ticks_per_second; + + if (Time::pause_timer > 0) + { + Time::pause_timer -= Time::delta; + } + else + { + Time::previous_ticks = Time::ticks; + Time::ticks += ticks_diff; + Time::previous_seconds = Time::seconds; + Time::seconds += Time::delta; + + step(); } } - // render + // Draw Frame { renderer->before_render(); - if (app_config.on_render != nullptr) app_config.on_render(); - renderer->after_render(); platform->present(); } @@ -353,10 +387,28 @@ bool App::focused() return Internal::platform->get_focused(); } -void App::fullscreen(bool enabled) +void App::set_flag(u32 flag, bool enabled) { BLAH_ASSERT_RUNNING(); - Internal::platform->set_fullscreen(enabled); + + u32 was = app_flags; + + if (enabled) + app_flags |= flag; + else + app_flags &= ~flag; + + if (was != app_flags) + { + Internal::platform->set_app_flags(app_flags); + Internal::renderer->set_app_flags(app_flags); + } +} + +bool App::get_flag(u32 flag) +{ + BLAH_ASSERT_RUNNING(); + return ((app_flags & flag) == flag); } const RendererInfo& App::renderer() diff --git a/src/internal/platform.h b/src/internal/platform.h index 3a603e7..2a25250 100644 --- a/src/internal/platform.h +++ b/src/internal/platform.h @@ -35,6 +35,9 @@ namespace Blah // Called to present the window contents virtual void present() = 0; + // Called when the App sets flags + virtual void set_app_flags(u32 flags) = 0; + // Gets the Application Window Title in UTF-8 virtual const char* get_title() = 0; @@ -50,9 +53,6 @@ namespace Blah // Gets whether the Window has focus virtual bool get_focused() = 0; - // Sets the Window Fullscreen if enabled is not 0 - virtual void set_fullscreen(bool enabled) = 0; - // Gets the Application Window Size, in Screen Coordinates virtual void get_size(int* width, int* height) = 0; diff --git a/src/internal/platform_sdl2.cpp b/src/internal/platform_sdl2.cpp index 56cde6e..d2f3b16 100644 --- a/src/internal/platform_sdl2.cpp +++ b/src/internal/platform_sdl2.cpp @@ -89,12 +89,12 @@ namespace Blah void update(InputState& state) override; void sleep(int milliseconds) override; void present() override; + void set_app_flags(u32 flags) override; const char* get_title() override; void set_title(const char* title) override; void get_position(int* x, int* y) override; void set_position(int x, int y) override; bool get_focused() override; - void set_fullscreen(bool enabled) override; void get_size(int* width, int* height) override; void set_size(int width, int height) override; void get_draw_size(int* width, int* height) override; @@ -158,7 +158,7 @@ bool SDL2_Platform::init(const Config& config) return false; } - int flags = SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_HIDDEN | SDL_WINDOW_RESIZABLE; + int flags = SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_HIDDEN; // enable OpenGL if (config.renderer_type == RendererType::OpenGL) @@ -192,22 +192,12 @@ bool SDL2_Platform::init(const Config& config) return false; } - // set window properties - SDL_SetWindowResizable(window, SDL_TRUE); - SDL_SetWindowMinimumSize(window, 256, 256); - return true; } void SDL2_Platform::ready() { -#ifndef __EMSCRIPTEN__ - // enable V-Sync - // TODO: - // This should be a toggle or controllable in some way - if (App::renderer().type == RendererType::OpenGL) - SDL_GL_SetSwapInterval(1); -#endif + } void SDL2_Platform::shutdown() @@ -446,9 +436,7 @@ void SDL2_Platform::sleep(int milliseconds) void SDL2_Platform::present() { if (App::renderer().type == RendererType::OpenGL) - { SDL_GL_SwapWindow(window); - } // display the window // this avoids a short black screen on macoS @@ -459,6 +447,21 @@ void SDL2_Platform::present() } } +void SDL2_Platform::set_app_flags(u32 flags) +{ + // Toggle Fullscreen + SDL_SetWindowFullscreen(window, ((flags & Flags::Fullscreen) != 0) ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0); + + // Toggle Resizable + SDL_SetWindowResizable(window, ((flags & Flags::Resizable) != 0) ? SDL_TRUE : SDL_FALSE); + + // Toggle V-Sync for OpenGL +#ifndef __EMSCRIPTEN__ + if (App::renderer().type == RendererType::OpenGL) + SDL_GL_SetSwapInterval(((flags & Flags::VSync) != 0) ? 1 : 0); +#endif +} + const char* SDL2_Platform::get_title() { return SDL_GetWindowTitle(window); @@ -485,14 +488,6 @@ bool SDL2_Platform::get_focused() return (flags & SDL_WINDOW_INPUT_FOCUS) != 0 && (flags & SDL_WINDOW_MINIMIZED) == 0; } -void SDL2_Platform::set_fullscreen(bool enabled) -{ - if (enabled) - SDL_SetWindowFullscreen(window, SDL_WINDOW_FULLSCREEN_DESKTOP); - else - SDL_SetWindowFullscreen(window, 0); -} - void SDL2_Platform::get_size(int* width, int* height) { SDL_GetWindowSize(window, width, height); diff --git a/src/internal/platform_win32.cpp b/src/internal/platform_win32.cpp index 5ae42e0..9f8ead6 100644 --- a/src/internal/platform_win32.cpp +++ b/src/internal/platform_win32.cpp @@ -99,7 +99,7 @@ namespace Blah void get_position(int* x, int* y) override; void set_position(int x, int y) override; bool get_focused() override; - void set_fullscreen(bool enabled) override; + void set_app_flags(u32 flags) override; void get_size(int* width, int* height) override; void set_size(int width, int height) override; void get_draw_size(int* width, int* height) override; @@ -474,33 +474,39 @@ bool Win32_Platform::get_focused() return true; } -void Win32_Platform::set_fullscreen(bool enabled) +void Win32_Platform::set_app_flags(u32 flags) { - if (fullscreen == enabled) - return; - - fullscreen = enabled; - - if (fullscreen) + // toggle fullscreen { - GetWindowRect(hwnd, &windowed_position); + bool enabled = (flags & Flags::Fullscreen) != 0; + if (fullscreen == enabled) + return; + fullscreen = enabled; - int w = GetSystemMetrics(SM_CXSCREEN); - int h = GetSystemMetrics(SM_CYSCREEN); - SetWindowLongPtr(hwnd, GWL_STYLE, WS_VISIBLE | WS_POPUP); - SetWindowPos(hwnd, HWND_TOP, 0, 0, w, h, 0); - ShowWindow(hwnd, SW_SHOW); - } - else - { - SetWindowLongPtr(hwnd, GWL_STYLE, WS_OVERLAPPEDWINDOW); - SetWindowPos(hwnd, HWND_TOP, - windowed_position.left, - windowed_position.top, - windowed_position.right - windowed_position.left, - windowed_position.bottom - windowed_position.top, 0); - ShowWindow(hwnd, SW_SHOW); + if (fullscreen) + { + GetWindowRect(hwnd, &windowed_position); + + int w = GetSystemMetrics(SM_CXSCREEN); + int h = GetSystemMetrics(SM_CYSCREEN); + SetWindowLongPtr(hwnd, GWL_STYLE, WS_VISIBLE | WS_POPUP); + SetWindowPos(hwnd, HWND_TOP, 0, 0, w, h, 0); + ShowWindow(hwnd, SW_SHOW); + } + else + { + SetWindowLongPtr(hwnd, GWL_STYLE, WS_OVERLAPPEDWINDOW); + SetWindowPos(hwnd, HWND_TOP, + windowed_position.left, + windowed_position.top, + windowed_position.right - windowed_position.left, + windowed_position.bottom - windowed_position.top, 0); + ShowWindow(hwnd, SW_SHOW); + } } + + // toggle resizable + // TODO: ... } void Win32_Platform::get_size(int* width, int* height) diff --git a/src/internal/renderer.h b/src/internal/renderer.h index 37e50bf..77df783 100644 --- a/src/internal/renderer.h +++ b/src/internal/renderer.h @@ -32,6 +32,9 @@ namespace Blah // Called after renderings ends virtual void after_render() = 0; + // Called when the App sets flags + virtual void set_app_flags(u32 flags) { } + // Optional implementation to get the drawable backbuffer size in pixels. // Not all implementations will use this so it can be up to the Platform. virtual bool get_draw_size(int* w, int* h) { return false; } diff --git a/src/internal/renderer_d3d11.cpp b/src/internal/renderer_d3d11.cpp index 8434979..ac1f215 100644 --- a/src/internal/renderer_d3d11.cpp +++ b/src/internal/renderer_d3d11.cpp @@ -932,7 +932,8 @@ namespace Blah void Renderer_D3D11::after_render() { - auto hr = swap_chain->Present(1, 0); + auto vsync = App::get_flag(Flags::VSync); + auto hr = swap_chain->Present(vsync ? 1 : 0, 0); BLAH_ASSERT(SUCCEEDED(hr), "Failed to Present swap chain"); }