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
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
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();

View File

@ -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()

View File

@ -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;

View File

@ -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);

View File

@ -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,11 +474,13 @@ bool Win32_Platform::get_focused()
return true;
}
void Win32_Platform::set_fullscreen(bool enabled)
void Win32_Platform::set_app_flags(u32 flags)
{
// toggle fullscreen
{
bool enabled = (flags & Flags::Fullscreen) != 0;
if (fullscreen == enabled)
return;
fullscreen = enabled;
if (fullscreen)
@ -503,6 +505,10 @@ void Win32_Platform::set_fullscreen(bool enabled)
}
}
// toggle resizable
// TODO: ...
}
void Win32_Platform::get_size(int* width, int* height)
{
RECT rect;

View File

@ -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; }

View File

@ -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");
}