blah/src/core/app.cpp

308 lines
6.2 KiB
C++
Raw Normal View History

#include <blah/core/app.h>
#include <blah/core/log.h>
#include <blah/core/time.h>
2020-08-26 15:38:01 +08:00
#include <blah/math/point.h>
#include <blah/graphics/framebuffer.h>
#include "../internal/platform_backend.h"
#include "../internal/graphics_backend.h"
#include "../internal/input_backend.h"
2020-08-26 15:38:01 +08:00
2021-01-04 16:58:24 +08:00
#ifdef __EMSCRIPTEN__
#include <emscripten.h>
#include <emscripten/html5.h>
#endif
2020-08-26 15:38:01 +08:00
using namespace Blah;
Config::Config()
{
name = nullptr;
width = 0;
height = 0;
target_framerate = 60;
max_updates = 5;
on_startup = nullptr;
on_shutdown = nullptr;
on_update = nullptr;
on_render = nullptr;
on_exit_request = App::exit;
on_info = nullptr;
on_warn = nullptr;
on_error = nullptr;
}
namespace
{
Config app_config;
bool app_is_running = false;
bool app_is_exiting = false;
uint64_t time_last;
uint64_t time_accumulator = 0;
2021-01-04 16:58:24 +08:00
void app_iterate()
2021-01-04 16:58:24 +08:00
{
// poll system events
PlatformBackend::frame();
2021-01-04 16:58:24 +08:00
// update at a fixed timerate
// TODO: allow a non-fixed step update?
{
uint64_t time_target = (uint64_t)((1.0f / app_config.target_framerate) * 1000);
uint64_t time_curr = PlatformBackend::time();
uint64_t time_diff = time_curr - time_last;
2021-01-04 16:58:24 +08:00
time_last = time_curr;
time_accumulator += time_diff;
// do not let us run too fast
while (time_accumulator < time_target)
{
PlatformBackend::sleep((int)(time_target - time_accumulator));
2021-01-04 16:58:24 +08:00
time_curr = PlatformBackend::time();
time_diff = time_curr - time_last;
time_last = time_curr;
time_accumulator += time_diff;
}
2021-01-04 16:58:24 +08:00
// Do not allow us to fall behind too many updates
// (otherwise we'll get spiral of death)
uint64_t time_maximum = app_config.max_updates * time_target;
if (time_accumulator > time_maximum)
time_accumulator = time_maximum;
2021-01-04 16:58:24 +08:00
// do as many updates as we can
while (time_accumulator >= time_target)
2021-01-04 16:58:24 +08:00
{
time_accumulator -= time_target;
Time::delta = (1.0f / app_config.target_framerate);
if (Time::pause_timer > 0)
{
Time::pause_timer -= Time::delta;
if (Time::pause_timer <= -0.0001f)
Time::delta = -Time::pause_timer;
else
continue;
}
2021-01-04 16:58:24 +08:00
Time::milliseconds += time_target;
Time::previous_elapsed = Time::elapsed;
Time::elapsed += Time::delta;
2021-01-04 16:58:24 +08:00
InputBackend::frame();
GraphicsBackend::frame();
2021-01-04 16:58:24 +08:00
if (app_config.on_update != nullptr)
app_config.on_update();
}
2021-01-04 16:58:24 +08:00
}
// render
{
GraphicsBackend::before_render();
2021-01-04 16:58:24 +08:00
if (app_config.on_render != nullptr)
app_config.on_render();
2021-01-04 16:58:24 +08:00
GraphicsBackend::after_render();
PlatformBackend::present();
}
2021-01-04 16:58:24 +08:00
}
2021-01-04 16:58:24 +08:00
}
2020-08-26 15:38:01 +08:00
bool App::run(const Config* c)
{
BLAH_ASSERT(!app_is_running, "The Application is already running");
BLAH_ASSERT(c != nullptr, "The Application requires a valid Config");
BLAH_ASSERT(c->name != nullptr, "The Application Name cannot be null");
BLAH_ASSERT(c->width > 0 && c->height > 0, "The Width and Height must be larget than 0");
BLAH_ASSERT(c->max_updates > 0, "Max Updates must be >= 1");
BLAH_ASSERT(c->target_framerate > 0, "Target Framerate must be >= 1");
app_config = *c;
app_is_running = true;
app_is_exiting = false;
// initialize the system
2020-12-24 08:16:09 +08:00
if (!PlatformBackend::init(&app_config))
2020-08-26 15:38:01 +08:00
{
2020-12-24 08:16:09 +08:00
Log::error("Failed to initialize Platform module");
2020-08-26 15:38:01 +08:00
return false;
}
// initialize graphics
2020-12-24 08:16:09 +08:00
if (!GraphicsBackend::init())
2020-08-26 15:38:01 +08:00
{
2020-12-24 08:16:09 +08:00
Log::error("Failed to initialize Graphics module");
2020-08-26 15:38:01 +08:00
return false;
}
// input
2020-12-24 08:16:09 +08:00
InputBackend::init();
2020-08-26 15:38:01 +08:00
// startup
if (app_config.on_startup != nullptr)
app_config.on_startup();
2021-01-04 16:58:24 +08:00
time_last = PlatformBackend::time();
time_accumulator = 0;
2020-08-26 15:38:01 +08:00
// display window
2020-12-24 08:16:09 +08:00
PlatformBackend::ready();
2020-08-26 15:38:01 +08:00
// Begin main loop
// Emscripten requires the main loop be separated into its own call
2021-01-04 16:58:24 +08:00
#ifdef __EMSCRIPTEN__
emscripten_set_main_loop(app_iterate, 0, 1);
2021-01-04 16:58:24 +08:00
#else
2020-08-26 15:38:01 +08:00
while (!app_is_exiting)
app_iterate();
2021-01-04 16:58:24 +08:00
#endif
2020-08-26 15:38:01 +08:00
// shutdown
if (app_config.on_shutdown != nullptr)
app_config.on_shutdown();
2020-12-24 08:16:09 +08:00
GraphicsBackend::shutdown();
PlatformBackend::shutdown();
2020-08-26 15:38:01 +08:00
// clear static state
app_is_running = false;
app_is_exiting = false;
Time::milliseconds = 0;
Time::elapsed = 0;
Time::delta = 0;
return true;
}
bool App::is_running()
{
return app_is_running;
}
void App::exit()
{
if (!app_is_exiting && app_is_running)
app_is_exiting = true;
}
const Config* App::config()
{
return &app_config;
}
const char* App::path()
{
2020-12-24 08:16:09 +08:00
return PlatformBackend::app_path();
2020-08-26 15:38:01 +08:00
}
const char* App::user_path()
{
2020-12-24 08:16:09 +08:00
return PlatformBackend::user_path();
2020-08-26 15:38:01 +08:00
}
int App::width()
{
int w, h;
2020-12-24 08:16:09 +08:00
PlatformBackend::get_size(&w, &h);
2020-08-26 15:38:01 +08:00
return w;
}
int App::height()
{
int w, h;
2020-12-24 08:16:09 +08:00
PlatformBackend::get_size(&w, &h);
2020-08-26 15:38:01 +08:00
return h;
}
int App::draw_width()
{
int w, h;
2020-12-24 08:16:09 +08:00
PlatformBackend::get_draw_size(&w, &h);
2020-08-26 15:38:01 +08:00
return w;
}
int App::draw_height()
{
int w, h;
2020-12-24 08:16:09 +08:00
PlatformBackend::get_draw_size(&w, &h);
2020-08-26 15:38:01 +08:00
return h;
}
float App::content_scale()
{
2020-12-24 08:16:09 +08:00
return PlatformBackend::get_content_scale();
2020-08-26 15:38:01 +08:00
}
void App::fullscreen(bool enabled)
{
2020-12-24 08:16:09 +08:00
PlatformBackend::set_fullscreen(enabled);
2020-12-27 06:44:48 +08:00
}
Renderer App::renderer()
{
return GraphicsBackend::renderer();
}
const RendererFeatures& Blah::App::renderer_features()
{
return GraphicsBackend::features();
}
namespace
{
2020-12-27 06:47:10 +08:00
// A dummy Frame Buffer that represents the Back Buffer
// it doesn't actually contain any textures or details.
2020-12-27 06:44:48 +08:00
class BackBuffer final : public FrameBuffer
{
Attachments empty_attachments;
TextureRef empty_texture;
virtual Attachments& attachments() override
{
BLAH_ASSERT(false, "Backbuffer doesn't have any attachments");
return empty_attachments;
}
virtual const Attachments& attachments() const override
{
BLAH_ASSERT(false, "Backbuffer doesn't have any attachments");
return empty_attachments;
}
virtual TextureRef& attachment(int index) override
{
BLAH_ASSERT(false, "Backbuffer doesn't have any attachments");
return empty_texture;
}
virtual const TextureRef& attachment(int index) const override
{
BLAH_ASSERT(false, "Backbuffer doesn't have any attachments");
return empty_texture;
}
virtual int width() const override
{
return App::draw_width();
}
virtual int height() const override
{
return App::draw_height();
}
virtual void clear(Color color) override
{
GraphicsBackend::clear_backbuffer(color);
}
};
}
extern const FrameBufferRef App::backbuffer = FrameBufferRef(new BackBuffer());