Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[3.2] [HTML5] Synchronous main, better persistence, handlers fixes, optional full screen. #42266

Merged
merged 6 commits into from
Oct 2, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 20 additions & 9 deletions misc/dist/html/full-size.html
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@
const EXECUTABLE_NAME = '$GODOT_BASENAME';
const MAIN_PACK = '$GODOT_BASENAME.pck';
const INDETERMINATE_STATUS_STEP_MS = 100;
const FULL_WINDOW = $GODOT_FULL_WINDOW;

var canvas = document.getElementById('canvas');
var statusProgress = document.getElementById('status-progress');
Expand All @@ -155,6 +156,9 @@

var initializing = true;
var statusMode = 'hidden';
var lastWidth = 0;
var lastHeight = 0;
var lastScale = 0;

var animationCallbacks = [];
function animate(time) {
Expand All @@ -164,16 +168,23 @@
requestAnimationFrame(animate);

function adjustCanvasDimensions() {
var scale = window.devicePixelRatio || 1;
var width = window.innerWidth;
var height = window.innerHeight;
canvas.width = width * scale;
canvas.height = height * scale;
canvas.style.width = width + "px";
canvas.style.height = height + "px";
const scale = window.devicePixelRatio || 1;
if (lastWidth != window.innerWidth || lastHeight != window.innerHeight || lastScale != scale) {
lastScale = scale;
lastWidth = window.innerWidth;
lastHeight = window.innerHeight;
canvas.width = Math.floor(lastWidth * scale);
canvas.height = Math.floor(lastHeight * scale);
canvas.style.width = lastWidth + "px";
canvas.style.height = lastHeight + "px";
}
}
if (FULL_WINDOW) {
animationCallbacks.push(adjustCanvasDimensions);
adjustCanvasDimensions();
} else {
engine.setCanvasResizedOnStart(true);
}
animationCallbacks.push(adjustCanvasDimensions);
adjustCanvasDimensions();

setStatusMode = function setStatusMode(mode) {

Expand Down
20 changes: 14 additions & 6 deletions platform/javascript/engine/engine.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ Function('return this')()['Engine'] = (function() {
this.resizeCanvasOnStart = false;
this.onExecute = null;
this.onExit = null;
this.persistentPaths = [];
};

Engine.prototype.init = /** @param {string=} basePath */ function(basePath) {
Expand All @@ -56,12 +57,14 @@ Function('return this')()['Engine'] = (function() {
config['locateFile'] = Utils.createLocateRewrite(loadPath);
config['instantiateWasm'] = Utils.createInstantiatePromise(loadPromise);
Godot(config).then(function(module) {
me.rtenv = module;
if (unloadAfterInit) {
unload();
}
resolve();
config = null;
module['initFS'](me.persistentPaths).then(function(fs_err) {
me.rtenv = module;
if (unloadAfterInit) {
unload();
}
resolve();
config = null;
});
});
});
return initPromise;
Expand Down Expand Up @@ -220,6 +223,10 @@ Function('return this')()['Engine'] = (function() {
this.rtenv['copyToFS'](path, buffer);
}

Engine.prototype.setPersistentPaths = function(persistentPaths) {
this.persistentPaths = persistentPaths;
};

// Closure compiler exported engine methods.
/** @export */
Engine['isWebGLAvailable'] = Utils.isWebGLAvailable;
Expand All @@ -241,5 +248,6 @@ Function('return this')()['Engine'] = (function() {
Engine.prototype['setOnExecute'] = Engine.prototype.setOnExecute;
Engine.prototype['setOnExit'] = Engine.prototype.setOnExit;
Engine.prototype['copyToFS'] = Engine.prototype.copyToFS;
Engine.prototype['setPersistentPaths'] = Engine.prototype.setPersistentPaths;
return Engine;
})();
2 changes: 2 additions & 0 deletions platform/javascript/export/export.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,7 @@ void EditorExportPlatformJavaScript::_fix_html(Vector<uint8_t> &p_html, const Re
current_line = current_line.replace("$GODOT_BASENAME", p_name);
current_line = current_line.replace("$GODOT_PROJECT_NAME", ProjectSettings::get_singleton()->get_setting("application/config/name"));
current_line = current_line.replace("$GODOT_HEAD_INCLUDE", p_preset->get("html/head_include"));
current_line = current_line.replace("$GODOT_FULL_WINDOW", p_preset->get("html/full_window_size") ? "true" : "false");
current_line = current_line.replace("$GODOT_DEBUG_ENABLED", p_debug ? "true" : "false");
str_export += current_line + "\n";
}
Expand Down Expand Up @@ -290,6 +291,7 @@ void EditorExportPlatformJavaScript::get_export_options(List<ExportOption> *r_op
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "vram_texture_compression/for_mobile"), false)); // ETC or ETC2, depending on renderer
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "html/custom_html_shell", PROPERTY_HINT_FILE, "*.html"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "html/head_include", PROPERTY_HINT_MULTILINE_TEXT), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "html/full_window_size"), true));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/release", PROPERTY_HINT_GLOBAL_FILE, "*.zip"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/debug", PROPERTY_HINT_GLOBAL_FILE, "*.zip"), ""));
}
Expand Down
70 changes: 34 additions & 36 deletions platform/javascript/javascript_main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,12 @@ extern "C" EMSCRIPTEN_KEEPALIVE void _drop_files_callback(char *p_filev[], int p
os->get_main_loop()->drop_files(files);
}

extern "C" EMSCRIPTEN_KEEPALIVE void _request_quit_callback(char *p_filev[], int p_filec) {
if (os && os->get_main_loop()) {
os->get_main_loop()->notification(MainLoop::NOTIFICATION_WM_QUIT_REQUEST);
}
}

void exit_callback() {
emscripten_cancel_main_loop(); // After this, we can exit!
Main::cleanup();
Expand Down Expand Up @@ -88,26 +94,36 @@ void main_loop_callback() {
/* 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 */
}
}

extern "C" EMSCRIPTEN_KEEPALIVE void cleanup_after_sync() {
emscripten_set_main_loop(exit_callback, -1, false);
}

extern "C" EMSCRIPTEN_KEEPALIVE void main_after_fs_sync(char *p_idbfs_err) {
// Set IDBFS status
String idbfs_err = String::utf8(p_idbfs_err);
if (!idbfs_err.empty()) {
print_line("IndexedDB not available: " + idbfs_err);
}
os->set_idb_available(idbfs_err.empty());
int main(int argc, char *argv[]) {
os = new OS_JavaScript(argc, argv);

// Set canvas ID
char canvas_ptr[256];
Expand All @@ -127,40 +143,22 @@ extern "C" EMSCRIPTEN_KEEPALIVE void main_after_fs_sync(char *p_idbfs_err) {
/* clang-format on */
setenv("LANG", locale_ptr, true);

Main::setup2();
// Set IDBFS status
os->set_idb_available((bool)EM_ASM_INT({ return Module.idbfs }));

Main::setup(argv[0], argc - 1, &argv[1]);
// Ease up compatibility.
ResourceLoader::set_abort_on_missing_resources(false);
Main::start();
os->get_main_loop()->init();
// Immediately run the first iteration.
// We are inside an animation frame, we want to immediately draw on the newly setup canvas.
main_loop_callback();
emscripten_resume_main_loop();
}

int main(int argc, char *argv[]) {
// Create and mount userfs immediately.
// Expose method for requesting quit.
EM_ASM({
FS.mkdir('/userfs');
FS.mount(IDBFS, {}, '/userfs');
Module['request_quit'] = function() {
ccall("_request_quit_callback", null, []);
};
});
os = new OS_JavaScript(argc, argv);
Main::setup(argv[0], argc - 1, &argv[1], false);
emscripten_set_main_loop(main_loop_callback, -1, false);
emscripten_pause_main_loop(); // Will need to wait for FS sync.

// Sync from persistent state into memory and then
// run the 'main_after_fs_sync' function.
/* clang-format off */
EM_ASM({
FS.syncfs(true, function(err) {
requestAnimationFrame(function() {
ccall('main_after_fs_sync', null, ['string'], [err ? err.message : ""]);
});
});
});
/* clang-format on */

return 0;
// Continued async in main_after_fs_sync() from the syncfs() callback.
// Immediately run the first iteration.
// We are inside an animation frame, we want to immediately draw on the newly setup canvas.
main_loop_callback();
}
75 changes: 74 additions & 1 deletion platform/javascript/native/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,38 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/

Module['initFS'] = function(persistentPaths) {
FS.mkdir('/userfs');
FS.mount(IDBFS, {}, '/userfs');

function createRecursive(dir) {
try {
FS.stat(dir);
} catch (e) {
if (e.errno !== ERRNO_CODES.ENOENT) {
throw e;
}
FS.mkdirTree(dir);
}
}

persistentPaths.forEach(function(path) {
createRecursive(path);
FS.mount(IDBFS, {}, path);
});
return new Promise(function(resolve, reject) {
FS.syncfs(true, function(err) {
if (err) {
Module.idbfs = false;
console.log("IndexedDB not available: " + err.message);
} else {
Module.idbfs = true;
}
resolve(err);
});
});
};

Module['copyToFS'] = function(path, buffer) {
var p = path.lastIndexOf("/");
var dir = "/";
Expand All @@ -37,7 +69,7 @@ Module['copyToFS'] = function(path, buffer) {
try {
FS.stat(dir);
} catch (e) {
if (e.errno !== ERRNO_CODES.ENOENT) { // 'ENOENT', see https://github.com/emscripten-core/emscripten/blob/master/system/lib/libc/musl/arch/emscripten/bits/errno.h
if (e.errno !== ERRNO_CODES.ENOENT) {
throw e;
}
FS.mkdirTree(dir);
Expand Down Expand Up @@ -202,3 +234,44 @@ Module.drop_handler = (function() {
});
}
})();

function EventHandlers() {
function Handler(target, event, method, capture) {
this.target = target;
this.event = event;
this.method = method;
this.capture = capture;
}

var listeners = [];

function has(target, event, method, capture) {
return listeners.findIndex(function(e) {
return e.target === target && e.event === event && e.method === method && e.capture == capture;
}) !== -1;
}

this.add = function(target, event, method, capture) {
if (has(target, event, method, capture)) {
return;
}
listeners.push(new Handler(target, event, method, capture));
target.addEventListener(event, method, capture);
};

this.remove = function(target, event, method, capture) {
if (!has(target, event, method, capture)) {
return;
}
target.removeEventListener(event, method, capture);
};

this.clear = function() {
listeners.forEach(function(h) {
h.target.removeEventListener(h.event, h.method, h.capture);
});
listeners.length = 0;
};
}

Module.listeners = new EventHandlers();
Loading