emscripten: use emterpreter for displaying loading screen

This commit is contained in:
Sebastian Krzyszkowiak 2019-01-03 23:22:26 +01:00
parent 7d9adc8d14
commit f357d75591
No known key found for this signature in database
GPG key ID: E8F235CF3BDBC3FF
4 changed files with 93 additions and 75 deletions

View file

@ -194,7 +194,7 @@ if (NOT LIBSUPERDERPY_CONFIG_INCLUDED)
set(CMAKE_EXECUTABLE_SUFFIX ".bc") set(CMAKE_EXECUTABLE_SUFFIX ".bc")
set(CMAKE_SHARED_LIBRARY_SUFFIX ".so") set(CMAKE_SHARED_LIBRARY_SUFFIX ".so")
set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -s SIDE_MODULE=1 -s EXPORT_ALL=1") set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -s SIDE_MODULE=1 -s EXPORT_ALL=1")
set(EMSCRIPTEN_FLAGS -s FULL_ES2=1 -s ERROR_ON_UNDEFINED_SYMBOLS=1 -s NO_EXIT_RUNTIME=0 -s PRECISE_F32=1 -s EXPORT_ALL=1 -s EXTRA_EXPORTED_RUNTIME_METHODS=[\"Pointer_stringify\"]) set(EMSCRIPTEN_FLAGS -s FULL_ES2=1 -s ERROR_ON_UNDEFINED_SYMBOLS=1 -s EMTERPRETIFY=1 -s EMTERPRETIFY_ASYNC=1 -s EMTERPRETIFY_WHITELIST=[\"_libsuperderpy_emscripten_mainloop\",\"_libsuperderpy_mainloop\",\"_MainloopTick\",\"_GamestateLoadingThread\"] -s PRECISE_F32=1 -s EXPORT_ALL=1 -s EXTRA_EXPORTED_RUNTIME_METHODS=[\"Pointer_stringify\"])
set(LIBSUPERDERPY_EMSCRIPTEN_MODE "asm.js" CACHE STRING "Emscripten compilation mode (JavaScript or WebAssembly)") set(LIBSUPERDERPY_EMSCRIPTEN_MODE "asm.js" CACHE STRING "Emscripten compilation mode (JavaScript or WebAssembly)")
set_property(CACHE LIBSUPERDERPY_EMSCRIPTEN_MODE PROPERTY STRINGS "asm.js;wasm") set_property(CACHE LIBSUPERDERPY_EMSCRIPTEN_MODE PROPERTY STRINGS "asm.js;wasm")

View file

@ -57,7 +57,7 @@ SYMBOL_INTERNAL void DrawGamestates(struct Game* game) {
tmp = tmp->next; tmp = tmp->next;
} }
if (game->_priv.loading.in_progress) { if (game->_priv.loading.in_progress && game->loading.shown) {
// same as above, but for the loading gamestate // same as above, but for the loading gamestate
game->_priv.current_gamestate = NULL; game->_priv.current_gamestate = NULL;
SetFramebufferAsTarget(game); SetFramebufferAsTarget(game);
@ -249,11 +249,13 @@ SYMBOL_INTERNAL void* GamestateLoadingThread(void* arg) {
data->gamestate->data = data->gamestate->api->load(data->game, &GamestateProgress); data->gamestate->data = data->gamestate->api->load(data->game, &GamestateProgress);
if (data->game->_priv.loading.progress != data->gamestate->progress_count) { if (data->game->_priv.loading.progress != data->gamestate->progress_count) {
PrintConsole(data->game, "[%s] WARNING: Gamestate_ProgressCount does not match the number of progress invokations (%d)!", data->gamestate->name, data->game->_priv.loading.progress); PrintConsole(data->game, "[%s] WARNING: Gamestate_ProgressCount does not match the number of progress invokations (%d)!", data->gamestate->name, data->game->_priv.loading.progress);
#ifndef LIBSUPERDERPY_SINGLE_THREAD
if (data->game->config.debug.enabled) { if (data->game->config.debug.enabled) {
PrintConsole(data->game, "(sleeping for 3 seconds...)"); PrintConsole(data->game, "(sleeping for 3 seconds...)");
data->game->_priv.showconsole = true; data->game->_priv.showconsole = true;
al_rest(3.0); al_rest(3.0);
} }
#endif
} }
data->bitmap_flags = al_get_new_bitmap_flags(); data->bitmap_flags = al_get_new_bitmap_flags();
data->game->_priv.loading.in_progress = false; data->game->_priv.loading.in_progress = false;
@ -297,11 +299,12 @@ SYMBOL_INTERNAL void GamestateProgress(struct Game* game) {
#else #else
al_convert_memory_bitmaps(); al_convert_memory_bitmaps();
double delta = al_get_time() - game->_priv.loading.time; double delta = al_get_time() - game->_priv.loading.time;
if (game->_priv.loading.current->show_loading) { game->time += delta; // TODO: ability to disable passing time during loading
game->_priv.loading.gamestate->api->logic(game, game->_priv.loading.gamestate->data, delta);
DrawGamestates(game);
}
game->_priv.loading.time += delta; game->_priv.loading.time += delta;
if (game->loading.shown) {
game->_priv.loading.gamestate->api->logic(game, game->_priv.loading.gamestate->data, delta);
}
DrawGamestates(game);
DrawConsole(game); DrawConsole(game);
al_flip_display(); al_flip_display();
#endif #endif

View file

@ -277,11 +277,15 @@ SYMBOL_EXPORT struct Game* libsuperderpy_init(int argc, char** argv, const char*
} }
int samplerate = strtol(GetConfigOptionDefault(game, "SuperDerpy", "samplerate", "44100"), NULL, 10); int samplerate = strtol(GetConfigOptionDefault(game, "SuperDerpy", "samplerate", "44100"), NULL, 10);
#ifdef __EMSCRIPTEN__
game->audio.v = al_create_voice(samplerate, ALLEGRO_AUDIO_DEPTH_FLOAT32, ALLEGRO_CHANNEL_CONF_2);
#else
game->audio.v = al_create_voice(samplerate, ALLEGRO_AUDIO_DEPTH_INT16, ALLEGRO_CHANNEL_CONF_2); game->audio.v = al_create_voice(samplerate, ALLEGRO_AUDIO_DEPTH_INT16, ALLEGRO_CHANNEL_CONF_2);
if (!game->audio.v) { if (!game->audio.v) {
// fallback // fallback
game->audio.v = al_create_voice(samplerate, ALLEGRO_AUDIO_DEPTH_FLOAT32, ALLEGRO_CHANNEL_CONF_2); game->audio.v = al_create_voice(samplerate, ALLEGRO_AUDIO_DEPTH_FLOAT32, ALLEGRO_CHANNEL_CONF_2);
} }
#endif
game->audio.mixer = al_create_mixer(samplerate, ALLEGRO_AUDIO_DEPTH_FLOAT32, ALLEGRO_CHANNEL_CONF_2); game->audio.mixer = al_create_mixer(samplerate, ALLEGRO_AUDIO_DEPTH_FLOAT32, ALLEGRO_CHANNEL_CONF_2);
game->audio.fx = al_create_mixer(samplerate, ALLEGRO_AUDIO_DEPTH_FLOAT32, ALLEGRO_CHANNEL_CONF_2); game->audio.fx = al_create_mixer(samplerate, ALLEGRO_AUDIO_DEPTH_FLOAT32, ALLEGRO_CHANNEL_CONF_2);
game->audio.music = al_create_mixer(samplerate, ALLEGRO_AUDIO_DEPTH_FLOAT32, ALLEGRO_CHANNEL_CONF_2); game->audio.music = al_create_mixer(samplerate, ALLEGRO_AUDIO_DEPTH_FLOAT32, ALLEGRO_CHANNEL_CONF_2);

View file

@ -183,6 +183,66 @@ static inline void HandleDebugEvent(struct Game* game, ALLEGRO_EVENT* ev) {
} }
} }
static inline bool MainloopEvents(struct Game* game) {
do {
ALLEGRO_EVENT ev;
if (game->_priv.paused) {
// there's no frame flipping when paused, so avoid pointless busylooping
al_wait_for_event(game->_priv.event_queue, &ev);
} else if (!al_get_next_event(game->_priv.event_queue, &ev)) {
break;
}
#ifdef LIBSUPERDERPY_IMGUI
ImGui_ImplAllegro5_ProcessEvent(&ev);
switch (ev.type) {
case ALLEGRO_EVENT_KEY_CHAR:
case ALLEGRO_EVENT_KEY_DOWN:
case ALLEGRO_EVENT_KEY_UP:
if (igGetIO()->WantCaptureKeyboard) {
continue;
}
break;
case ALLEGRO_EVENT_MOUSE_AXES:
case ALLEGRO_EVENT_MOUSE_BUTTON_DOWN:
case ALLEGRO_EVENT_MOUSE_BUTTON_UP:
case ALLEGRO_EVENT_TOUCH_BEGIN:
case ALLEGRO_EVENT_TOUCH_CANCEL:
case ALLEGRO_EVENT_TOUCH_END:
case ALLEGRO_EVENT_TOUCH_MOVE:
if (igGetIO()->WantCaptureMouse) {
continue;
}
break;
default:
break;
}
#endif
if (game->_priv.params.handlers.event) {
if ((*game->_priv.params.handlers.event)(game, &ev)) {
continue;
}
}
if (ev.type == ALLEGRO_EVENT_DISPLAY_CLOSE) {
EventGamestates(game, &ev);
return false;
}
HandleEvent(game, &ev);
if (game->config.debug.enabled) {
HandleDebugEvent(game, &ev);
}
EventGamestates(game, &ev);
} while (!al_is_event_queue_empty(game->_priv.event_queue));
return true;
}
static inline bool MainloopTick(struct Game* game) { static inline bool MainloopTick(struct Game* game) {
if (game->_priv.paused) { if (game->_priv.paused) {
return true; return true;
@ -248,27 +308,31 @@ static inline bool MainloopTick(struct Game* game) {
game->_priv.loading.time = time; game->_priv.loading.time = time;
CalculateProgress(game); CalculateProgress(game);
if (tmp->show_loading) {
game->loading.shown = true;
DrawGamestates(game);
DrawConsole(game);
al_flip_display();
}
#ifndef LIBSUPERDERPY_SINGLE_THREAD #ifndef LIBSUPERDERPY_SINGLE_THREAD
al_run_detached_thread(GamestateLoadingThread, &data); al_run_detached_thread(GamestateLoadingThread, &data);
while (game->_priv.loading.in_progress) { while (game->_priv.loading.in_progress) {
double delta = al_get_time() - game->_priv.loading.time; double delta = al_get_time() - game->_priv.loading.time;
if (tmp->show_loading) {
game->loading.shown = true;
(*game->_priv.loading.gamestate->api->logic)(game, game->_priv.loading.gamestate->data, delta);
DrawGamestates(game);
}
game->_priv.loading.time += delta;
game->time += delta; // TODO: ability to disable passing time during loading game->time += delta; // TODO: ability to disable passing time during loading
game->_priv.loading.time += delta;
if (game->loading.shown) {
(*game->_priv.loading.gamestate->api->logic)(game, game->_priv.loading.gamestate->data, delta);
}
DrawGamestates(game);
if (game->_priv.texture_sync) { if (game->_priv.texture_sync) {
al_convert_memory_bitmaps(); al_convert_memory_bitmaps();
game->_priv.texture_sync = false; game->_priv.texture_sync = false;
al_signal_cond(game->_priv.texture_sync_cond); al_signal_cond(game->_priv.texture_sync_cond);
game->_priv.loading.time = al_get_time(); game->_priv.loading.time = al_get_time(); // TODO: rethink time management during loading
} }
DrawConsole(game); DrawConsole(game);
al_flip_display(); al_flip_display();
#ifndef LIBSUPERDERPY_SINGLE_THREAD
if (game->_priv.bsod_sync) { if (game->_priv.bsod_sync) {
al_set_target_bitmap(NULL); al_set_target_bitmap(NULL);
game->_priv.bsod_sync = false; game->_priv.bsod_sync = false;
@ -280,10 +344,12 @@ static inline bool MainloopTick(struct Game* game) {
al_wait_cond(game->_priv.bsod_cond, game->_priv.bsod_mutex); al_wait_cond(game->_priv.bsod_cond, game->_priv.bsod_mutex);
} }
al_unlock_mutex(game->_priv.bsod_mutex); al_unlock_mutex(game->_priv.bsod_mutex);
#endif
} }
#else #else
GamestateLoadingThread(&data); GamestateLoadingThread(&data);
#ifdef __EMSCRIPTEN__
emscripten_sleep(0);
#endif
al_convert_memory_bitmaps(); al_convert_memory_bitmaps();
#endif #endif
@ -316,6 +382,10 @@ static inline bool MainloopTick(struct Game* game) {
if (game->_priv.loading.loaded) { if (game->_priv.loading.loaded) {
ReloadShaders(game, false); ReloadShaders(game, false);
MainloopEvents(game); // consume queued events
#ifdef __EMSCRIPTEN__
emscripten_sleep(0);
#endif
} }
bool gameActive = false; bool gameActive = false;
@ -328,6 +398,7 @@ static inline bool MainloopTick(struct Game* game) {
game->_priv.current_gamestate = tmp; game->_priv.current_gamestate = tmp;
tmp->started = true; tmp->started = true;
tmp->pending_start = false; tmp->pending_start = false;
(*tmp->api->start)(game, tmp->data); (*tmp->api->start)(game, tmp->data);
al_resume_timer(game->_priv.timer); al_resume_timer(game->_priv.timer);
game->_priv.timestamp = al_get_time(); game->_priv.timestamp = al_get_time();
@ -371,66 +442,6 @@ static inline bool MainloopTick(struct Game* game) {
return true; return true;
} }
static inline bool MainloopEvents(struct Game* game) {
do {
ALLEGRO_EVENT ev;
if (game->_priv.paused) {
// there's no frame flipping when paused, so avoid pointless busylooping
al_wait_for_event(game->_priv.event_queue, &ev);
} else if (!al_get_next_event(game->_priv.event_queue, &ev)) {
break;
}
#ifdef LIBSUPERDERPY_IMGUI
ImGui_ImplAllegro5_ProcessEvent(&ev);
switch (ev.type) {
case ALLEGRO_EVENT_KEY_CHAR:
case ALLEGRO_EVENT_KEY_DOWN:
case ALLEGRO_EVENT_KEY_UP:
if (igGetIO()->WantCaptureKeyboard) {
continue;
}
break;
case ALLEGRO_EVENT_MOUSE_AXES:
case ALLEGRO_EVENT_MOUSE_BUTTON_DOWN:
case ALLEGRO_EVENT_MOUSE_BUTTON_UP:
case ALLEGRO_EVENT_TOUCH_BEGIN:
case ALLEGRO_EVENT_TOUCH_CANCEL:
case ALLEGRO_EVENT_TOUCH_END:
case ALLEGRO_EVENT_TOUCH_MOVE:
if (igGetIO()->WantCaptureMouse) {
continue;
}
break;
default:
break;
}
#endif
if (game->_priv.params.handlers.event) {
if ((*game->_priv.params.handlers.event)(game, &ev)) {
continue;
}
}
if (ev.type == ALLEGRO_EVENT_DISPLAY_CLOSE) {
EventGamestates(game, &ev);
return false;
}
HandleEvent(game, &ev);
if (game->config.debug.enabled) {
HandleDebugEvent(game, &ev);
}
EventGamestates(game, &ev);
} while (!al_is_event_queue_empty(game->_priv.event_queue));
return true;
}
SYMBOL_EXPORT bool libsuperderpy_mainloop(struct Game* game) { SYMBOL_EXPORT bool libsuperderpy_mainloop(struct Game* game) {
ClearGarbage(game); ClearGarbage(game);
return MainloopEvents(game) && MainloopTick(game); return MainloopEvents(game) && MainloopTick(game);