added App flags, implemented v-sync and fixed timestep flags

This commit is contained in:
Noel Berry 2022-08-21 17:41:29 -07:00
parent 05b58706d7
commit c94e372e7d
7 changed files with 159 additions and 76 deletions

View File

@ -11,6 +11,15 @@ namespace Blah
// Application Logging Functions // Application Logging Functions
using AppLogFn = Func<void, const char*, Log::Category>; using AppLogFn = Func<void, const char*, Log::Category>;
// 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 // Application Configuration
struct Config struct Config
{ {
@ -34,10 +43,13 @@ namespace Blah
// defaults to 5. // defaults to 5.
int max_updates = 5; int max_updates = 5;
// target framerate. // target framerate when running with Fixed Timestep
// defaults to 60. // defaults to 60.
int target_framerate = 60; int target_framerate = 60;
// default starting flags
u32 flags = Flags::VSync | Flags::Resizable | Flags::FixedTimestep;
// Callback on application startup // Callback on application startup
AppEventFn on_startup = nullptr; AppEventFn on_startup = nullptr;
@ -113,9 +125,23 @@ namespace Blah
// If the window is currently focused or has mouse input // If the window is currently focused or has mouse input
bool focused(); bool focused();
// Toggles fullscreen if supported on the platform. // Toggles a specific flag
// Otherwise this function does nothing. void set_flag(u32 flag, bool enabled);
void fullscreen(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 // Retrieves the Renderer Information
const RendererInfo& renderer(); const RendererInfo& renderer();

View File

@ -28,6 +28,7 @@ namespace
bool app_is_exiting = false; bool app_is_exiting = false;
u64 app_time_last; u64 app_time_last;
u64 app_time_accumulator = 0; u64 app_time_accumulator = 0;
u32 app_flags = 0;
TargetRef app_backbuffer; TargetRef app_backbuffer;
void get_drawable_size(int* w, int* h) void get_drawable_size(int* w, int* h)
@ -90,6 +91,7 @@ bool App::run(const Config* c)
// default values // default values
app_is_running = true; app_is_running = true;
app_is_exiting = false; app_is_exiting = false;
app_flags = app_config.flags;
app_backbuffer = TargetRef(new BackBuffer()); app_backbuffer = TargetRef(new BackBuffer());
// initialize the system // 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 + poll the platform once
Input::Internal::init(); Input::Internal::init();
Input::Internal::step_state(); Input::Internal::step_state();
@ -164,14 +170,26 @@ bool App::is_running()
void App::Internal::iterate() void App::Internal::iterate()
{ {
// update at a fixed timerate static const auto step = []()
// TODO: allow a non-fixed step update? {
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_target = (u64)((1.0 / app_config.target_framerate) * Time::ticks_per_second);
u64 time_curr = App::Internal::platform->ticks(); u64 ticks_curr = App::Internal::platform->ticks();
u64 time_diff = time_curr - app_time_last; u64 ticks_diff = ticks_curr - app_time_last;
app_time_last = time_curr; app_time_last = ticks_curr;
app_time_accumulator += time_diff; app_time_accumulator += ticks_diff;
// do not let us run too fast // do not let us run too fast
while (app_time_accumulator < time_target) 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); int milliseconds = (int)(time_target - app_time_accumulator) / (Time::ticks_per_second / 1000);
App::Internal::platform->sleep(milliseconds); App::Internal::platform->sleep(milliseconds);
time_curr = App::Internal::platform->ticks(); ticks_curr = App::Internal::platform->ticks();
time_diff = time_curr - app_time_last; ticks_diff = ticks_curr - app_time_last;
app_time_last = time_curr; app_time_last = ticks_curr;
app_time_accumulator += time_diff; app_time_accumulator += ticks_diff;
} }
// Do not allow us to fall behind too many updates // Do not allow us to fall behind too many updates
@ -212,23 +230,39 @@ void App::Internal::iterate()
Time::previous_seconds = Time::seconds; Time::previous_seconds = Time::seconds;
Time::seconds += Time::delta; Time::seconds += Time::delta;
Input::Internal::step_state(); step();
platform->update(Input::state); }
Input::Internal::update_bindings(); }
renderer->update(); // 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) Time::delta = ticks_diff / (float)Time::ticks_per_second;
app_config.on_update();
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(); renderer->before_render();
if (app_config.on_render != nullptr) if (app_config.on_render != nullptr)
app_config.on_render(); app_config.on_render();
renderer->after_render(); renderer->after_render();
platform->present(); platform->present();
} }
@ -353,10 +387,28 @@ bool App::focused()
return Internal::platform->get_focused(); return Internal::platform->get_focused();
} }
void App::fullscreen(bool enabled) void App::set_flag(u32 flag, bool enabled)
{ {
BLAH_ASSERT_RUNNING(); 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() const RendererInfo& App::renderer()

View File

@ -35,6 +35,9 @@ namespace Blah
// Called to present the window contents // Called to present the window contents
virtual void present() = 0; 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 // Gets the Application Window Title in UTF-8
virtual const char* get_title() = 0; virtual const char* get_title() = 0;
@ -50,9 +53,6 @@ namespace Blah
// Gets whether the Window has focus // Gets whether the Window has focus
virtual bool get_focused() = 0; 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 // Gets the Application Window Size, in Screen Coordinates
virtual void get_size(int* width, int* height) = 0; virtual void get_size(int* width, int* height) = 0;

View File

@ -89,12 +89,12 @@ namespace Blah
void update(InputState& state) override; void update(InputState& state) override;
void sleep(int milliseconds) override; void sleep(int milliseconds) override;
void present() override; void present() override;
void set_app_flags(u32 flags) override;
const char* get_title() override; const char* get_title() override;
void set_title(const char* title) override; void set_title(const char* title) override;
void get_position(int* x, int* y) override; void get_position(int* x, int* y) override;
void set_position(int x, int y) override; void set_position(int x, int y) override;
bool get_focused() override; bool get_focused() override;
void set_fullscreen(bool enabled) override;
void get_size(int* width, int* height) override; void get_size(int* width, int* height) override;
void set_size(int width, int height) override; void set_size(int width, int height) override;
void get_draw_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; 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 // enable OpenGL
if (config.renderer_type == RendererType::OpenGL) if (config.renderer_type == RendererType::OpenGL)
@ -192,22 +192,12 @@ bool SDL2_Platform::init(const Config& config)
return false; return false;
} }
// set window properties
SDL_SetWindowResizable(window, SDL_TRUE);
SDL_SetWindowMinimumSize(window, 256, 256);
return true; return true;
} }
void SDL2_Platform::ready() 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() void SDL2_Platform::shutdown()
@ -446,9 +436,7 @@ void SDL2_Platform::sleep(int milliseconds)
void SDL2_Platform::present() void SDL2_Platform::present()
{ {
if (App::renderer().type == RendererType::OpenGL) if (App::renderer().type == RendererType::OpenGL)
{
SDL_GL_SwapWindow(window); SDL_GL_SwapWindow(window);
}
// display the window // display the window
// this avoids a short black screen on macoS // 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() const char* SDL2_Platform::get_title()
{ {
return SDL_GetWindowTitle(window); 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; 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) void SDL2_Platform::get_size(int* width, int* height)
{ {
SDL_GetWindowSize(window, width, height); SDL_GetWindowSize(window, width, height);

View File

@ -99,7 +99,7 @@ namespace Blah
void get_position(int* x, int* y) override; void get_position(int* x, int* y) override;
void set_position(int x, int y) override; void set_position(int x, int y) override;
bool get_focused() 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 get_size(int* width, int* height) override;
void set_size(int width, int height) override; void set_size(int width, int height) override;
void get_draw_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; return true;
} }
void Win32_Platform::set_fullscreen(bool enabled) void Win32_Platform::set_app_flags(u32 flags)
{ {
if (fullscreen == enabled) // toggle fullscreen
return;
fullscreen = enabled;
if (fullscreen)
{ {
GetWindowRect(hwnd, &windowed_position); bool enabled = (flags & Flags::Fullscreen) != 0;
if (fullscreen == enabled)
return;
fullscreen = enabled;
int w = GetSystemMetrics(SM_CXSCREEN); if (fullscreen)
int h = GetSystemMetrics(SM_CYSCREEN); {
SetWindowLongPtr(hwnd, GWL_STYLE, WS_VISIBLE | WS_POPUP); GetWindowRect(hwnd, &windowed_position);
SetWindowPos(hwnd, HWND_TOP, 0, 0, w, h, 0);
ShowWindow(hwnd, SW_SHOW); int w = GetSystemMetrics(SM_CXSCREEN);
} int h = GetSystemMetrics(SM_CYSCREEN);
else SetWindowLongPtr(hwnd, GWL_STYLE, WS_VISIBLE | WS_POPUP);
{ SetWindowPos(hwnd, HWND_TOP, 0, 0, w, h, 0);
SetWindowLongPtr(hwnd, GWL_STYLE, WS_OVERLAPPEDWINDOW); ShowWindow(hwnd, SW_SHOW);
SetWindowPos(hwnd, HWND_TOP, }
windowed_position.left, else
windowed_position.top, {
windowed_position.right - windowed_position.left, SetWindowLongPtr(hwnd, GWL_STYLE, WS_OVERLAPPEDWINDOW);
windowed_position.bottom - windowed_position.top, 0); SetWindowPos(hwnd, HWND_TOP,
ShowWindow(hwnd, SW_SHOW); 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) void Win32_Platform::get_size(int* width, int* height)

View File

@ -32,6 +32,9 @@ namespace Blah
// Called after renderings ends // Called after renderings ends
virtual void after_render() = 0; 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. // Optional implementation to get the drawable backbuffer size in pixels.
// Not all implementations will use this so it can be up to the Platform. // 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; } virtual bool get_draw_size(int* w, int* h) { return false; }

View File

@ -932,7 +932,8 @@ namespace Blah
void Renderer_D3D11::after_render() 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"); BLAH_ASSERT(SUCCEEDED(hr), "Failed to Present swap chain");
} }