2021-05-10 08:23:02 +08:00
|
|
|
#include <blah/app.h>
|
|
|
|
#include <blah/common.h>
|
|
|
|
#include <blah/time.h>
|
|
|
|
#include <blah/numerics/point.h>
|
|
|
|
#include <blah/graphics/target.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;
|
2021-02-22 10:43:42 +08:00
|
|
|
on_log = nullptr;
|
2020-08-26 15:38:01 +08:00
|
|
|
}
|
|
|
|
|
2021-01-06 12:44:02 +08:00
|
|
|
namespace
|
|
|
|
{
|
|
|
|
Config app_config;
|
|
|
|
bool app_is_running = false;
|
|
|
|
bool app_is_exiting = false;
|
2021-03-21 08:33:04 +08:00
|
|
|
u64 time_last;
|
|
|
|
u64 time_accumulator = 0;
|
2021-01-04 16:58:24 +08:00
|
|
|
|
2021-01-06 12:44:02 +08:00
|
|
|
void app_iterate()
|
2021-01-04 16:58:24 +08:00
|
|
|
{
|
2021-01-06 12:44:02 +08:00
|
|
|
// poll system events
|
|
|
|
PlatformBackend::frame();
|
2021-01-04 16:58:24 +08:00
|
|
|
|
2021-01-06 12:44:02 +08:00
|
|
|
// update at a fixed timerate
|
|
|
|
// TODO: allow a non-fixed step update?
|
|
|
|
{
|
2021-03-21 08:33:04 +08:00
|
|
|
u64 time_target = (u64)((1.0 / app_config.target_framerate) * Time::ticks_per_second);
|
|
|
|
u64 time_curr = PlatformBackend::ticks();
|
|
|
|
u64 time_diff = time_curr - time_last;
|
2021-01-04 16:58:24 +08:00
|
|
|
time_last = time_curr;
|
|
|
|
time_accumulator += time_diff;
|
|
|
|
|
2021-01-06 12:44:02 +08:00
|
|
|
// do not let us run too fast
|
|
|
|
while (time_accumulator < time_target)
|
|
|
|
{
|
2021-02-06 16:13:50 +08:00
|
|
|
int milliseconds = (int)(time_target - time_accumulator) / (Time::ticks_per_second / 1000);
|
|
|
|
PlatformBackend::sleep(milliseconds);
|
2021-01-04 16:58:24 +08:00
|
|
|
|
2021-02-06 16:13:50 +08:00
|
|
|
time_curr = PlatformBackend::ticks();
|
2021-01-06 12:44:02 +08:00
|
|
|
time_diff = time_curr - time_last;
|
|
|
|
time_last = time_curr;
|
|
|
|
time_accumulator += time_diff;
|
|
|
|
}
|
2021-01-04 16:58:24 +08:00
|
|
|
|
2021-01-06 12:44:02 +08:00
|
|
|
// Do not allow us to fall behind too many updates
|
|
|
|
// (otherwise we'll get spiral of death)
|
2021-03-21 08:33:04 +08:00
|
|
|
u64 time_maximum = app_config.max_updates * time_target;
|
2021-01-06 12:44:02 +08:00
|
|
|
if (time_accumulator > time_maximum)
|
|
|
|
time_accumulator = time_maximum;
|
2021-01-04 16:58:24 +08:00
|
|
|
|
2021-01-06 12:44:02 +08:00
|
|
|
// do as many updates as we can
|
|
|
|
while (time_accumulator >= time_target)
|
2021-01-04 16:58:24 +08:00
|
|
|
{
|
2021-01-06 12:44:02 +08:00
|
|
|
time_accumulator -= time_target;
|
|
|
|
|
|
|
|
Time::delta = (1.0f / app_config.target_framerate);
|
|
|
|
|
|
|
|
if (Time::pause_timer > 0)
|
|
|
|
{
|
|
|
|
Time::pause_timer -= Time::delta;
|
2021-02-06 16:13:50 +08:00
|
|
|
if (Time::pause_timer <= -0.0001)
|
2021-01-06 12:44:02 +08:00
|
|
|
Time::delta = -Time::pause_timer;
|
|
|
|
else
|
|
|
|
continue;
|
|
|
|
}
|
2021-01-04 16:58:24 +08:00
|
|
|
|
2021-02-06 16:13:50 +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
|
|
|
|
2021-01-06 12:44:02 +08:00
|
|
|
InputBackend::frame();
|
|
|
|
GraphicsBackend::frame();
|
2021-01-04 16:58:24 +08:00
|
|
|
|
2021-01-06 12:44:02 +08:00
|
|
|
if (app_config.on_update != nullptr)
|
|
|
|
app_config.on_update();
|
|
|
|
}
|
2021-01-04 16:58:24 +08:00
|
|
|
}
|
|
|
|
|
2021-01-06 12:44:02 +08:00
|
|
|
// render
|
|
|
|
{
|
|
|
|
GraphicsBackend::before_render();
|
2021-01-04 16:58:24 +08:00
|
|
|
|
2021-01-06 12:44:02 +08:00
|
|
|
if (app_config.on_render != nullptr)
|
|
|
|
app_config.on_render();
|
2021-01-04 16:58:24 +08:00
|
|
|
|
2021-01-06 12:44:02 +08:00
|
|
|
GraphicsBackend::after_render();
|
|
|
|
PlatformBackend::present();
|
|
|
|
}
|
2021-01-04 16:58:24 +08:00
|
|
|
}
|
2021-01-06 12:44:02 +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-02-06 16:13:50 +08:00
|
|
|
time_last = PlatformBackend::ticks();
|
2021-01-04 16:58:24 +08:00
|
|
|
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
|
|
|
|
2021-01-06 12:44:02 +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__
|
2021-01-06 12:44:02 +08:00
|
|
|
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)
|
2021-01-06 12:44:02 +08:00
|
|
|
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;
|
2021-02-06 16:13:50 +08:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
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.
|
2021-05-10 08:23:02 +08:00
|
|
|
class BackBuffer final : public Target
|
2020-12-27 06:44:48 +08:00
|
|
|
{
|
2021-05-10 08:23:02 +08:00
|
|
|
Attachments empty_textures;
|
2020-12-27 06:44:48 +08:00
|
|
|
|
2021-05-10 08:23:02 +08:00
|
|
|
Attachments& textures() override
|
2020-12-27 06:44:48 +08:00
|
|
|
{
|
2021-05-10 08:23:02 +08:00
|
|
|
BLAH_ASSERT(false, "Backbuffer doesn't have any textures");
|
|
|
|
return empty_textures;
|
2020-12-27 06:44:48 +08:00
|
|
|
}
|
|
|
|
|
2021-05-10 08:23:02 +08:00
|
|
|
const Attachments& textures() const override
|
2020-12-27 06:44:48 +08:00
|
|
|
{
|
2021-05-10 08:23:02 +08:00
|
|
|
BLAH_ASSERT(false, "Backbuffer doesn't have any textures");
|
|
|
|
return empty_textures;
|
2020-12-27 06:44:48 +08:00
|
|
|
}
|
|
|
|
|
2021-05-07 12:48:06 +08:00
|
|
|
int width() const override
|
2020-12-27 06:44:48 +08:00
|
|
|
{
|
|
|
|
return App::draw_width();
|
|
|
|
}
|
|
|
|
|
2021-05-07 12:48:06 +08:00
|
|
|
int height() const override
|
2020-12-27 06:44:48 +08:00
|
|
|
{
|
|
|
|
return App::draw_height();
|
|
|
|
}
|
|
|
|
|
2021-05-07 12:48:06 +08:00
|
|
|
void clear(Color color, float depth, u8 stencil, ClearMask mask) override
|
2020-12-27 06:44:48 +08:00
|
|
|
{
|
2021-02-22 15:00:37 +08:00
|
|
|
GraphicsBackend::clear_backbuffer(color, depth, stencil, mask);
|
2020-12-27 06:44:48 +08:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2021-05-10 08:23:02 +08:00
|
|
|
extern const TargetRef App::backbuffer = TargetRef(new BackBuffer());
|