blah/src/app.cpp

351 lines
7.3 KiB
C++
Raw Normal View History

#include <blah/app.h>
#include <blah/common.h>
#include <blah/time.h>
#include <blah/graphics/target.h>
#include "internal/platform.h"
#include "internal/renderer.h"
#include "internal/input.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;
#define BLAH_ASSERT_RUNNING() BLAH_ASSERT(app_is_running, "The App is not running (call App::run)")
2020-08-26 15:38:01 +08:00
namespace
{
// A dummy Frame Buffer that represents the Back Buffer
// it doesn't actually contain any textures or details.
class BackBuffer final : public Target
{
Attachments empty_textures;
Attachments& textures() override
{
BLAH_ASSERT(false, "Backbuffer doesn't have any textures");
return empty_textures;
}
const Attachments& textures() const override
{
BLAH_ASSERT(false, "Backbuffer doesn't have any textures");
return empty_textures;
}
int width() const override
{
int w, h;
Platform::get_draw_size(&w, &h);
return w;
}
int height() const override
{
int w, h;
Platform::get_draw_size(&w, &h);
return h;
}
void clear(Color color, float depth, u8 stencil, ClearMask mask) override
{
if (Renderer::instance)
Renderer::instance->clear_backbuffer(color, depth, stencil, mask);
}
};
Config app_config;
bool app_is_running = false;
bool app_is_exiting = false;
u64 app_time_last;
u64 app_time_accumulator = 0;
TargetRef app_backbuffer;
2021-01-04 16:58:24 +08:00
void app_iterate()
2021-01-04 16:58:24 +08:00
{
// update at a fixed timerate
// TODO: allow a non-fixed step update?
{
u64 time_target = (u64)((1.0 / app_config.target_framerate) * Time::ticks_per_second);
u64 time_curr = Platform::ticks();
u64 time_diff = time_curr - app_time_last;
app_time_last = time_curr;
app_time_accumulator += time_diff;
2021-01-04 16:58:24 +08:00
// do not let us run too fast
while (app_time_accumulator < time_target)
{
int milliseconds = (int)(time_target - app_time_accumulator) / (Time::ticks_per_second / 1000);
Platform::sleep(milliseconds);
2021-01-04 16:58:24 +08:00
time_curr = Platform::ticks();
time_diff = time_curr - app_time_last;
app_time_last = time_curr;
app_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)
u64 time_maximum = app_config.max_updates * time_target;
if (app_time_accumulator > time_maximum)
app_time_accumulator = time_maximum;
2021-01-04 16:58:24 +08:00
// do as many updates as we can
while (app_time_accumulator >= time_target)
2021-01-04 16:58:24 +08:00
{
app_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.0001)
Time::delta = -Time::pause_timer;
else
continue;
}
2021-01-04 16:58:24 +08:00
Time::previous_ticks = Time::ticks;
Time::ticks += time_target;
Time::previous_seconds = Time::seconds;
Time::seconds += Time::delta;
2021-01-04 16:58:24 +08:00
Input::update_state();
Platform::update(Input::state);
Input::update_bindings();
Renderer::instance->update();
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
{
Renderer::instance->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
Renderer::instance->after_render();
Platform::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");
// copy config over
2020-08-26 15:38:01 +08:00
app_config = *c;
// exit the application by default
if (!app_config.on_exit_request)
app_config.on_exit_request = App::exit;
// default renderer type
if (app_config.renderer_type == RendererType::None)
app_config.renderer_type = Renderer::default_type();
// default values
2020-08-26 15:38:01 +08:00
app_is_running = true;
app_is_exiting = false;
app_backbuffer = TargetRef(new BackBuffer());
2020-08-26 15:38:01 +08:00
// initialize the system
if (!Platform::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
{
// instantiate
Renderer::instance = Renderer::try_make_renderer(app_config.renderer_type);
// wasn't able to make any
if (Renderer::instance == nullptr)
{
Log::error("Renderer implementation was not found");
return false;
}
if (!Renderer::instance->init())
{
Log::error("Renderer failed to initialize");
delete Renderer::instance;
return false;
}
2020-08-26 15:38:01 +08:00
}
// input
Input::init();
2020-08-26 15:38:01 +08:00
// prepare by updating input & platform once
Input::update_state();
Platform::update(Input::state);
2020-08-26 15:38:01 +08:00
// startup
if (app_config.on_startup != nullptr)
app_config.on_startup();
app_time_last = Platform::ticks();
app_time_accumulator = 0;
2020-08-26 15:38:01 +08:00
// display window
Platform::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();
Renderer::instance->shutdown();
Platform::shutdown();
2020-08-26 15:38:01 +08:00
// clear static state
app_is_running = false;
app_is_exiting = false;
app_backbuffer = nullptr;
delete Renderer::instance;
Renderer::instance = nullptr;
Time::ticks = 0;
Time::seconds = 0;
Time::previous_ticks = 0;
Time::previous_seconds = 0;
2020-08-26 15:38:01 +08:00
Time::delta = 0;
return true;
}
void App::exit()
{
BLAH_ASSERT_RUNNING();
2020-08-26 15:38:01 +08:00
if (!app_is_exiting && app_is_running)
app_is_exiting = true;
}
const Config& App::config()
2020-08-26 15:38:01 +08:00
{
BLAH_ASSERT_RUNNING();
return app_config;
2020-08-26 15:38:01 +08:00
}
const char* App::path()
{
BLAH_ASSERT_RUNNING();
return Platform::app_path();
2020-08-26 15:38:01 +08:00
}
const char* App::user_path()
{
BLAH_ASSERT_RUNNING();
return Platform::user_path();
2020-08-26 15:38:01 +08:00
}
2021-05-10 10:46:08 +08:00
const char* App::get_title()
{
BLAH_ASSERT_RUNNING();
return Platform::get_title();
2021-05-10 10:46:08 +08:00
}
void App::set_title(const char* title)
{
BLAH_ASSERT_RUNNING();
Platform::set_title(title);
2021-05-10 10:46:08 +08:00
}
Point App::get_position()
{
BLAH_ASSERT_RUNNING();
2021-05-10 10:46:08 +08:00
Point result;
Platform::get_position(&result.x, &result.y);
2021-05-10 10:46:08 +08:00
return result;
}
void App::set_position(Point point)
{
BLAH_ASSERT_RUNNING();
Platform::set_position(point.x, point.y);
2021-05-10 10:46:08 +08:00
}
Point App::get_size()
{
BLAH_ASSERT_RUNNING();
2021-05-10 10:46:08 +08:00
Point result;
Platform::get_size(&result.x, &result.y);
2021-05-10 10:46:08 +08:00
return result;
}
void App::set_size(Point point)
{
BLAH_ASSERT_RUNNING();
Platform::set_size(point.x, point.y);
2021-05-10 10:46:08 +08:00
}
Point App::get_backbuffer_size()
2020-08-26 15:38:01 +08:00
{
BLAH_ASSERT_RUNNING();
if (app_backbuffer)
return Point(app_backbuffer->width(), app_backbuffer->height());
return Point(0, 0);
2020-08-26 15:38:01 +08:00
}
float App::content_scale()
{
BLAH_ASSERT_RUNNING();
return Platform::get_content_scale();
2020-08-26 15:38:01 +08:00
}
bool App::focused()
{
BLAH_ASSERT_RUNNING();
return Platform::get_focused();
}
void App::fullscreen(bool enabled)
2020-12-27 06:44:48 +08:00
{
BLAH_ASSERT_RUNNING();
Platform::set_fullscreen(enabled);
2020-12-27 06:44:48 +08:00
}
const RendererFeatures& App::renderer()
2020-12-27 06:44:48 +08:00
{
BLAH_ASSERT_RUNNING();
BLAH_ASSERT_RENDERER();
return Renderer::instance->features;
2020-12-27 06:44:48 +08:00
}
const TargetRef& App::backbuffer()
{
BLAH_ASSERT_RUNNING();
return app_backbuffer;
}
void System::open_url(const char* url)
2020-12-27 06:44:48 +08:00
{
BLAH_ASSERT_RUNNING();
Platform::open_url(url);
}