emscripten: modularize js/wasm, split from html shell
This commit is contained in:
parent
164a9fbd05
commit
2a17e82f9d
5 changed files with 135 additions and 141 deletions
|
@ -1,5 +1,6 @@
|
|||
|
||||
em_preamble = files('preamble.js')
|
||||
em_postamble = files('postamble.js')
|
||||
em_shell = files('shell.html')
|
||||
|
||||
if host_machine.system() == 'emscripten'
|
||||
|
@ -12,4 +13,10 @@ if host_machine.system() == 'emscripten'
|
|||
install_dir : bindir,
|
||||
install_tag : 'runtime',
|
||||
)
|
||||
install_data(
|
||||
em_shell,
|
||||
rename : 'taisei.html',
|
||||
install_dir : bindir,
|
||||
install_tag : 'runtime',
|
||||
)
|
||||
endif
|
||||
|
|
|
@ -1,118 +1,26 @@
|
|||
|
||||
function E(id) { return document.getElementById(id); }
|
||||
Module['totalDependencies'] = 0;
|
||||
Module['monitorRunDependencies'] = function(left) {
|
||||
Module['totalDependencies'] = Math.max(Module['totalDependencies'], left);
|
||||
Module['setStatus'](
|
||||
left ? 'Preparing… (' + (Module['totalDependencies']-left) + '/' + Module['totalDependencies'] + ')'
|
||||
: 'All downloads complete.');
|
||||
};
|
||||
|
||||
var statusElement = E('status');
|
||||
var progressElement = E('progress');
|
||||
var spinnerElement = E('spinner');
|
||||
var canvasElement = E('canvas');
|
||||
var canvasContainerElement = E('canvasContainer');
|
||||
var logToggleElement = E('logToggle');
|
||||
var logToggleContainerElement = E('logToggleContainer');
|
||||
var logContainerElement = E('logContainer');
|
||||
var logOutputElement = E('output');
|
||||
var dlMessage = statusElement.innerText;
|
||||
logToggleElement.checked = false;
|
||||
|
||||
window['toggleLog'] = function toggleLog() {
|
||||
logContainerElement.hidden = !logToggleElement.checked;
|
||||
logOutputElement.scrollTop = logOutputElement.scrollHeight;
|
||||
}
|
||||
|
||||
var glContext = canvasElement.getContext('webgl2', {
|
||||
'alpha' : false,
|
||||
'antialias' : false,
|
||||
'depth' : false,
|
||||
'powerPreference' : 'high-performance',
|
||||
'premultipliedAlpha' : true,
|
||||
'preserveDrawingBuffer' : false,
|
||||
'stencil' : false,
|
||||
});
|
||||
|
||||
if(!glContext) {
|
||||
throw "Could not create a WebGL 2 context";
|
||||
}
|
||||
|
||||
// glContext = WebGLDebugUtils.makeDebugContext(glContext);
|
||||
Module['initFilesystem'] = function() {
|
||||
FS.mkdir('/persistent');
|
||||
FS.mount(IDBFS, {}, '/persistent');
|
||||
};
|
||||
|
||||
// WebGL extensions have to be explicitly enabled to make the functionality available.
|
||||
// Note that Emscripten will always report all *supported* extensions in GL_EXTENSIONS,
|
||||
// regardless of whether they are enabled or not. This is non-conformant from the GLES
|
||||
// perspective. The easiest way to fix that is to enable all of them here.
|
||||
var glContext = Module['preinitializedWebGLContext'];
|
||||
glContext.getSupportedExtensions().forEach(function(ext) {
|
||||
glContext.getExtension(ext);
|
||||
});
|
||||
|
||||
canvasElement.addEventListener("webglcontextlost", function(e) {
|
||||
alert('WebGL context lost. You will need to reload the page.');
|
||||
e.preventDefault();
|
||||
}, false);
|
||||
|
||||
logOutputElement.value = ''; // clear browser cache
|
||||
|
||||
Module = {
|
||||
'preRun': [function() {
|
||||
ENV["TAISEI_NOASYNC"] = "1";
|
||||
ENV["TAISEI_NOUNLOAD"] = "1";
|
||||
ENV["TAISEI_PREFER_SDL_VIDEODRIVERS"] = "emscripten";
|
||||
ENV["TAISEI_RENDERER"] = "gles30";
|
||||
|
||||
FS.mkdir('/persistent');
|
||||
FS.mount(IDBFS, {}, '/persistent');
|
||||
}],
|
||||
'postRun': [],
|
||||
'onFirstFrame': function() {
|
||||
canvasContainerElement.hidden = false;
|
||||
logToggleContainerElement.style.display = "inline-block";
|
||||
Module['setStatus']('', true);
|
||||
},
|
||||
'print': function(text) {
|
||||
if (arguments.length > 1) text = Array.prototype.slice.call(arguments).join(' ');
|
||||
console.log(text);
|
||||
logOutputElement.value += text + "\n";
|
||||
logOutputElement.scrollTop = logOutputElement.scrollHeight; // focus on bottom
|
||||
},
|
||||
'printErr': function(text) {
|
||||
if (arguments.length > 1) text = Array.prototype.slice.call(arguments).join(' ');
|
||||
console.error(text);
|
||||
},
|
||||
'canvas': canvasElement,
|
||||
'preinitializedWebGLContext': glContext,
|
||||
'setStatus': function(text, force) {
|
||||
var ss = Module['setStatus'];
|
||||
if (!text && !force) return;
|
||||
if (!ss.last) ss.last = { time: Date.now(), text: '' };
|
||||
if (text === ss.last.text) return;
|
||||
var m = text.match(/([^(]+)\((\d+(\.\d+)?)\/(\d+)\)/);
|
||||
var now = Date.now();
|
||||
if (m && now - ss.last.time < 30) return; // if this is a progress update, skip it if too soon
|
||||
ss.last.time = now;
|
||||
ss.last.text = text;
|
||||
if (m) {
|
||||
text = m[1];
|
||||
progressElement.value = parseInt(m[2])*100;
|
||||
progressElement.max = parseInt(m[4])*100;
|
||||
progressElement.hidden = false;
|
||||
spinnerElement.hidden = !canvasElement.hidden;
|
||||
} else {
|
||||
progressElement.value = null;
|
||||
progressElement.max = null;
|
||||
progressElement.hidden = true;
|
||||
if (!text) spinnerElement.hidden = true;
|
||||
}
|
||||
statusElement.innerText = text.replace(/^Downloading(?: data)?\.\.\./, dlMessage).replace('...', '…');
|
||||
console.log("[STATUS] " + statusElement.innerText);
|
||||
},
|
||||
'totalDependencies': 0,
|
||||
'monitorRunDependencies': function(left) {
|
||||
Module['totalDependencies'] = Math.max(Module['totalDependencies'], left);
|
||||
Module['setStatus'](left ? 'Preparing… (' + (Module['totalDependencies']-left) + '/' + Module['totalDependencies'] + ')' : 'All downloads complete.');
|
||||
}
|
||||
};
|
||||
|
||||
window.onerror = function(error) {
|
||||
Module['setStatus']('Error: ' + error);
|
||||
};
|
||||
|
||||
function SyncFS(is_load, ccptr) {
|
||||
FS.syncfs(is_load, function(err) {
|
||||
Module['ccall'](
|
||||
|
@ -124,35 +32,8 @@ function SyncFS(is_load, ccptr) {
|
|||
});
|
||||
}
|
||||
|
||||
(function() {
|
||||
// Try to enable audio playback as soon as possible.
|
||||
// It must happen inside an input event handler.
|
||||
// https://github.com/emscripten-core/emscripten/issues/6511
|
||||
// https://github.com/emscripten-ports/SDL2/issues/57
|
||||
|
||||
function resumeAudio() {
|
||||
var sdl2 = Module['SDL2'];
|
||||
|
||||
if(typeof sdl2 === 'undefined') {
|
||||
return;
|
||||
}
|
||||
|
||||
if(sdl2.audioContext.state == 'suspended') {
|
||||
sdl2.audioContext.resume();
|
||||
}
|
||||
|
||||
if(sdl2.audioContext.state == 'running') {
|
||||
canvasElement.removeEventListener('click', resumeAudio);
|
||||
document.removeEventListener('keydown', resumeAudio);
|
||||
}
|
||||
}
|
||||
|
||||
canvasElement.addEventListener('click', resumeAudio);
|
||||
document.addEventListener('keydown', resumeAudio);
|
||||
})();
|
||||
|
||||
if(typeof dynCall === 'undefined') {
|
||||
dynCall = window['dynCall'] = Module['dynCall'] = function dynCall(sig, ptr, args) {
|
||||
dynCall = Module['dynCall'] = function dynCall(sig, ptr, args) {
|
||||
return wasmTable.get(ptr).apply(this, args);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
<link rel="icon" type="image/x-icon" href="favicon.ico"/>
|
||||
<link rel="preload" href="taisei.wasm" as="fetch" crossorigin/>
|
||||
<title>Taisei Project — Web version (Experimental!)</title>
|
||||
<script id="taisei-script" src="taisei.js" async></script>
|
||||
<style>
|
||||
body {
|
||||
background-image: url('background.webp');
|
||||
|
@ -211,6 +212,108 @@
|
|||
Powered by <a href="https://emscripten.org/">Emscripten</a>
|
||||
</div>
|
||||
<!--<script type="text/javascript" src="webgl-debug.js"></script>-->
|
||||
{{{ SCRIPT }}}
|
||||
<script type="application/javascript">
|
||||
function E(id) { return document.getElementById(id); }
|
||||
|
||||
var statusElement = E('status');
|
||||
var progressElement = E('progress');
|
||||
var spinnerElement = E('spinner');
|
||||
var canvasElement = E('canvas');
|
||||
var canvasContainerElement = E('canvasContainer');
|
||||
var logToggleElement = E('logToggle');
|
||||
var logToggleContainerElement = E('logToggleContainer');
|
||||
var logContainerElement = E('logContainer');
|
||||
var logOutputElement = E('output');
|
||||
var taiseiScriptElement = E('taisei-script');
|
||||
var dlMessage = statusElement.innerText;
|
||||
logToggleElement.checked = false;
|
||||
|
||||
var setStatus = (() => {
|
||||
var ss = {};
|
||||
return (text, force) => {
|
||||
if (!text && !force) return;
|
||||
if (!ss.last) ss.last = { time: Date.now(), text: '' };
|
||||
if (text === ss.last.text) return;
|
||||
var m = text.match(/([^(]+)\((\d+(\.\d+)?)\/(\d+)\)/);
|
||||
var now = Date.now();
|
||||
if (m && now - ss.last.time < 30) return; // if this is a progress update, skip it if too soon
|
||||
ss.last.time = now;
|
||||
ss.last.text = text;
|
||||
if (m) {
|
||||
text = m[1];
|
||||
progressElement.value = parseInt(m[2])*100;
|
||||
progressElement.max = parseInt(m[4])*100;
|
||||
progressElement.hidden = false;
|
||||
spinnerElement.hidden = !canvasElement.hidden;
|
||||
} else {
|
||||
progressElement.value = null;
|
||||
progressElement.max = null;
|
||||
progressElement.hidden = true;
|
||||
if (!text) spinnerElement.hidden = true;
|
||||
}
|
||||
statusElement.innerText = text.replace(/^Downloading(?: data)?\.\.\./, dlMessage).replace('...', '…');
|
||||
console.log("[STATUS] " + statusElement.innerText);
|
||||
};
|
||||
})();
|
||||
|
||||
window.onerror = function(error) {
|
||||
setStatus('Error: ' + error);
|
||||
};
|
||||
|
||||
window['toggleLog'] = function toggleLog() {
|
||||
logContainerElement.hidden = !logToggleElement.checked;
|
||||
logOutputElement.scrollTop = logOutputElement.scrollHeight;
|
||||
}
|
||||
|
||||
var glContext = canvasElement.getContext('webgl2', {
|
||||
'alpha' : false,
|
||||
'antialias' : false,
|
||||
'depth' : false,
|
||||
'powerPreference' : 'high-performance',
|
||||
'premultipliedAlpha' : true,
|
||||
'preserveDrawingBuffer' : false,
|
||||
'stencil' : false,
|
||||
});
|
||||
|
||||
if(!glContext) {
|
||||
throw "Could not create a WebGL 2 context";
|
||||
}
|
||||
|
||||
// glContext = WebGLDebugUtils.makeDebugContext(glContext);
|
||||
|
||||
canvasElement.addEventListener("webglcontextlost", function(e) {
|
||||
alert('WebGL context lost. You will need to reload the page.');
|
||||
e.preventDefault();
|
||||
}, false);
|
||||
|
||||
logOutputElement.value = ''; // clear browser cache
|
||||
|
||||
var taisei;
|
||||
taiseiScriptElement.addEventListener('load', async() => {
|
||||
taisei = await createTaisei({
|
||||
'canvas': canvasElement,
|
||||
'preinitializedWebGLContext': glContext,
|
||||
'onFirstFrame': function() {
|
||||
canvasContainerElement.hidden = false;
|
||||
logToggleContainerElement.style.display = "inline-block";
|
||||
setStatus('', true);
|
||||
},
|
||||
'print': function(text) {
|
||||
if (arguments.length > 1) text = Array.prototype.slice.call(arguments).join(' ');
|
||||
console.log(text);
|
||||
logOutputElement.value += text + "\n";
|
||||
logOutputElement.scrollTop = logOutputElement.scrollHeight; // focus on bottom
|
||||
},
|
||||
'printErr': function(text) {
|
||||
if (arguments.length > 1) text = Array.prototype.slice.call(arguments).join(' ');
|
||||
console.error(text);
|
||||
},
|
||||
'setStatus': setStatus,
|
||||
});
|
||||
|
||||
taisei.initFilesystem();
|
||||
taisei.callMain();
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -285,7 +285,7 @@ if host_machine.system() == 'emscripten'
|
|||
|
||||
em_debug = is_debug_build
|
||||
em_link_outputs = []
|
||||
em_link_output_suffixes = ['html', 'wasm', 'js'] # first element is significant
|
||||
em_link_output_suffixes = ['js', 'wasm'] # first element is significant
|
||||
em_data_dir = config.get_unquoted('TAISEI_BUILDCONF_DATA_PATH')
|
||||
em_common_args = []
|
||||
em_link_args = [
|
||||
|
@ -299,10 +299,11 @@ if host_machine.system() == 'emscripten'
|
|||
'-s', 'DYNAMIC_EXECUTION=0',
|
||||
'-s', 'ENVIRONMENT=web',
|
||||
'-s', 'EXIT_RUNTIME=0',
|
||||
'-s', 'EXPORT_NAME=createTaisei',
|
||||
'-s', 'EXPORTED_FUNCTIONS=["_main", "_vfs_sync_callback"]',
|
||||
'-s', 'EXPORTED_RUNTIME_METHODS=["ccall"]',
|
||||
'-s', 'FETCH',
|
||||
'-s', 'EXPORTED_RUNTIME_METHODS=["ccall","callMain"]',
|
||||
'-s', 'FETCH_SUPPORT_INDEXEDDB=0',
|
||||
'-s', 'FETCH',
|
||||
'-s', 'FILESYSTEM=1',
|
||||
'-s', 'FORCE_FILESYSTEM=1',
|
||||
'-s', 'GL_ENABLE_GET_PROC_ADDRESS',
|
||||
|
@ -312,12 +313,12 @@ if host_machine.system() == 'emscripten'
|
|||
'-s', 'GL_SUPPORT_SIMPLE_ENABLE_EXTENSIONS=0',
|
||||
'-s', 'IGNORE_MISSING_MAIN=0',
|
||||
'-s', 'INITIAL_MEMORY=268435456',
|
||||
'-s', 'INVOKE_RUN=0',
|
||||
'-s', 'LLD_REPORT_UNDEFINED',
|
||||
'-s', 'MAX_WEBGL_VERSION=2',
|
||||
'-s', 'MIN_WEBGL_VERSION=2',
|
||||
'-s', 'MODULARIZE=0',
|
||||
'-s', 'MODULARIZE=1',
|
||||
'-s', 'STACK_SIZE=1MB',
|
||||
'-s', 'STRICT_JS=1',
|
||||
'-s', 'SUPPORT_BIG_ENDIAN=1',
|
||||
'-s', 'WASM=1',
|
||||
'-lGL',
|
||||
|
@ -386,12 +387,12 @@ if host_machine.system() == 'emscripten'
|
|||
link_whole : libtaisei,
|
||||
)
|
||||
|
||||
taisei_html = custom_target(em_link_outputs[0],
|
||||
taisei_js = custom_target(em_link_outputs[0],
|
||||
command : [
|
||||
meson.get_compiler('cpp').cmd_array(),
|
||||
taisei,
|
||||
'--pre-js', em_preamble,
|
||||
'--shell-file', em_shell,
|
||||
'--post-js', em_postamble,
|
||||
get_option('c_args'),
|
||||
get_option('c_link_args'),
|
||||
em_common_args,
|
||||
|
@ -406,7 +407,7 @@ if host_machine.system() == 'emscripten'
|
|||
console : true,
|
||||
)
|
||||
|
||||
bindist_deps += taisei_html
|
||||
bindist_deps += taisei_js
|
||||
elif host_machine.system() == 'nx'
|
||||
taisei_elf_name = '@0@.elf'.format(taisei_basename)
|
||||
taisei_elf = executable(taisei_elf_name, taisei_src, taisei_main_src, version_deps,
|
||||
|
|
|
@ -13,6 +13,8 @@
|
|||
#include "rwops/rwops_dummy.h"
|
||||
|
||||
#include <dirent.h>
|
||||
#include <emscripten/em_js.h>
|
||||
#include <emscripten/emscripten.h>
|
||||
#include <emscripten/fetch.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
|
Loading…
Reference in a new issue