[HTML5] Port JavaScript inline code to libraries.

The API is implemented in javascript, and generates C functions that can
be called from godot.
This allows much cleaner code replacing all `EM_ASM` calls in our C++
code with plain C function calls.
This also gets rid of few hacks and comes with few optimizations (e.g.
custom cursor shapes should be much faster now).
This commit is contained in:
Fabio Alessandrelli 2020-10-23 18:33:20 +02:00
parent 54cda5c3b8
commit e2083871eb
33 changed files with 1995 additions and 1461 deletions

View file

@ -36,20 +36,11 @@
#include <emscripten/emscripten.h>
#include <stdlib.h>
#include "godot_js.h"
static OS_JavaScript *os = nullptr;
static uint64_t target_ticks = 0;
extern "C" EMSCRIPTEN_KEEPALIVE void _request_quit_callback(char *p_filev[], int p_filec) {
DisplayServerJavaScript *ds = DisplayServerJavaScript::get_singleton();
if (ds) {
Variant event = int(DisplayServer::WINDOW_EVENT_CLOSE_REQUEST);
Variant *eventp = &event;
Variant ret;
Callable::CallError ce;
ds->window_event_callback.call((const Variant **)&eventp, 1, ret, ce);
}
}
void exit_callback() {
emscripten_cancel_main_loop(); // After this, we can exit!
Main::cleanup();
@ -59,6 +50,10 @@ void exit_callback() {
emscripten_force_exit(exit_code); // No matter that we call cancel_main_loop, regular "exit" will not work, forcing.
}
void cleanup_after_sync() {
emscripten_set_main_loop(exit_callback, -1, false);
}
void main_loop_callback() {
uint64_t current_ticks = os->get_ticks_usec();
@ -74,68 +69,14 @@ void main_loop_callback() {
target_ticks += (uint64_t)(1000000 / target_fps);
}
if (os->main_loop_iterate()) {
emscripten_cancel_main_loop(); // Cancel current loop and wait for finalize_async.
/* clang-format off */
EM_ASM({
// This will contain the list of operations that need to complete before cleanup.
Module.async_finish = [
// Always contains at least one async promise, to avoid firing immediately if nothing is added.
new Promise(function(accept, reject) {
setTimeout(accept, 0);
})
];
});
/* clang-format on */
os->get_main_loop()->finish();
os->finalize_async(); // Will add all the async finish functions.
/* clang-format off */
EM_ASM({
Promise.all(Module.async_finish).then(function() {
Module.async_finish = [];
return new Promise(function(accept, reject) {
if (!Module.idbfs) {
accept();
return;
}
FS.syncfs(function(error) {
if (error) {
err('Failed to save IDB file system: ' + error.message);
}
accept();
});
});
}).then(function() {
ccall("cleanup_after_sync", null, []);
});
});
/* clang-format on */
emscripten_cancel_main_loop(); // Cancel current loop and wait for cleanup_after_sync.
godot_js_os_finish_async(cleanup_after_sync);
}
}
extern "C" EMSCRIPTEN_KEEPALIVE void cleanup_after_sync() {
emscripten_set_main_loop(exit_callback, -1, false);
}
/// When calling main, it is assumed FS is setup and synced.
int main(int argc, char *argv[]) {
// Configure locale.
char locale_ptr[16];
/* clang-format off */
EM_ASM({
stringToUTF8(Module['locale'], $0, 16);
}, locale_ptr);
/* clang-format on */
setenv("LANG", locale_ptr, true);
// Ensure the canvas ID.
/* clang-format off */
EM_ASM({
stringToUTF8("#" + Module['canvas'].id, $0, 255);
}, DisplayServerJavaScript::canvas_id);
/* clang-format on */
os = new OS_JavaScript();
os->set_idb_available((bool)EM_ASM_INT({ return Module.idbfs }));
// We must override main when testing is enabled
TEST_MAIN_OVERRIDE
@ -147,14 +88,6 @@ int main(int argc, char *argv[]) {
Main::start();
os->get_main_loop()->init();
// Expose method for requesting quit.
/* clang-format off */
EM_ASM({
Module['request_quit'] = function() {
ccall("_request_quit_callback", null, []);
};
});
/* clang-format on */
emscripten_set_main_loop(main_loop_callback, -1, false);
// Immediately run the first iteration.
// We are inside an animation frame, we want to immediately draw on the newly setup canvas.