mirror of
git://git.tartarus.org/simon/puzzles.git
synced 2025-04-21 16:05:44 -07:00
Update web puzzles to current WASM-based Emscripten.
I presume this will improve performance. Also, if I've understood correctly, WASM-based compiled web code is capable of automatically growing its memory, which the previous asm.js build of the puzzles could not do, and occasionally caused people to complain that if they tried to play a _really big_ game in their browser, the JS would eventually freeze because the emulated memory ran out. I've been putting off doing this for ages because my previous Emscripten build setup was so finicky that I didn't like to meddle with it. But now that the new cmake system in this source tree makes things generally easier, and particularly since I've just found out that the up-to-date Emscripten is available as a Docker image (namely "emscripten/emsdk"), this seemed like a good moment to give it a try. The source and build changes required for this update weren't too onerous. I was half expecting a huge API upheaval, and indeed there was _some_ change, but very little: - in the JS initPuzzle function, move the call to Module.callMain() into Module.onRuntimeInitialized instead of doing it at the top level, because New Emscripten's .js output likes to load the accompanying .wasm file asynchronously, so you can't call the WASM main() until it actually exists. - in the JS-side library code, replace all uses of Emscripten's Pointer_stringify() function with the new name UTF8ToString(). (The new version also has an ASCIIToString(), so I guess the reason for the name change is that now you get to choose which character set you meant. I need to use UTF-8, so that the × and ÷ signs in Keen will work.) - set EXTRA_EXPORTED_RUNTIME_METHODS=[cwrap,callMain] on the emcc link command line, otherwise they aren't available for my JS setup code to call. - (removed -s ASM_JS=1 from the link options, though I'm not actually sure it made any difference one way or the other in the new WASM world) - be prepared for a set of .wasm files to show up as build products alongside the .js ones. - stop building with -DCMAKE_BUILD_TYPE=Release! I'm not sure why that was needed, but if I leave that flag on my cmake command line, the output .js file fails to embed my emccpre.js, so the initial call to initPuzzle() fails from the HTML wrapper page, meaning nothing at all happens.
This commit is contained in:
25
Buildscr
25
Buildscr
@ -131,19 +131,28 @@ endif
|
|||||||
# delegation.
|
# delegation.
|
||||||
ifneq "$(NOJS)" yes then
|
ifneq "$(NOJS)" yes then
|
||||||
delegate emscripten
|
delegate emscripten
|
||||||
in puzzles do emcmake cmake -B build-emscripten -DCMAKE_BUILD_TYPE=Release $(web_unfinished_option) .
|
in puzzles do emcmake cmake -B build-emscripten $(web_unfinished_option) .
|
||||||
in puzzles/build-emscripten do make -j$(nproc) VERBOSE=1
|
in puzzles/build-emscripten do make -j$(nproc) VERBOSE=1
|
||||||
return puzzles/build-emscripten/*.js
|
return puzzles/build-emscripten/*.js
|
||||||
|
return puzzles/build-emscripten/*.wasm
|
||||||
return puzzles/build-emscripten/unfinished/group.js
|
return puzzles/build-emscripten/unfinished/group.js
|
||||||
|
return puzzles/build-emscripten/unfinished/group.wasm
|
||||||
enddelegate
|
enddelegate
|
||||||
|
|
||||||
# Build a set of wrapping HTML pages for easy testing of the
|
# Build a set of wrapping HTML pages for easy testing of the
|
||||||
# Javascript puzzles. These aren't quite the same as the versions that
|
# Javascript puzzles.
|
||||||
# will go on my live website, because those ones will substitute in a
|
#
|
||||||
# different footer, and not have to link to the .js files with the
|
# These aren't quite the same as the HTML pages that will go on my
|
||||||
# ../js/ prefix. But these ones should be good enough to just open
|
# live website. The live ones will substitute in a different footer
|
||||||
# using a file:// URL in a browser after running a build, and make
|
# that links back to the main puzzles page, and they'll have a
|
||||||
# sure the main functionality works.
|
# different filesystem layout so that ther links to the .js files
|
||||||
|
# won't need the ../js/ prefix used below.
|
||||||
|
#
|
||||||
|
# But these test pages should be good enough to just open after
|
||||||
|
# running a build, to make sure the main functionality works.
|
||||||
|
# Unfortunately, due to some kind of WASM loading restriction, this
|
||||||
|
# can't be done using a file:// URL; you have to actually point an
|
||||||
|
# HTTP or HTTPS server at the build output directory.
|
||||||
in puzzles do mkdir jstest
|
in puzzles do mkdir jstest
|
||||||
in puzzles/jstest do ../html/jspage.pl --jspath=../js/ /dev/null ../html/*.html
|
in puzzles/jstest do ../html/jspage.pl --jspath=../js/ /dev/null ../html/*.html
|
||||||
endif
|
endif
|
||||||
@ -182,7 +191,9 @@ ifneq "$(NOJAVA)" yes then
|
|||||||
endif
|
endif
|
||||||
ifneq "$(NOJS)" yes then
|
ifneq "$(NOJS)" yes then
|
||||||
deliver puzzles/build-emscripten/*.js js/$@
|
deliver puzzles/build-emscripten/*.js js/$@
|
||||||
|
deliver puzzles/build-emscripten/*.wasm js/$@
|
||||||
deliver puzzles/build-emscripten/unfinished/*.js js/$@
|
deliver puzzles/build-emscripten/unfinished/*.js js/$@
|
||||||
|
deliver puzzles/build-emscripten/unfinished/*.wasm js/$@
|
||||||
deliver puzzles/jstest/*.html jstest/$@
|
deliver puzzles/jstest/*.html jstest/$@
|
||||||
deliver puzzles/html/*.html html/$@
|
deliver puzzles/html/*.html html/$@
|
||||||
deliver puzzles/html/*.pl html/$@
|
deliver puzzles/html/*.pl html/$@
|
||||||
|
@ -29,7 +29,9 @@ set(emcc_export_list
|
|||||||
list(TRANSFORM emcc_export_list PREPEND \")
|
list(TRANSFORM emcc_export_list PREPEND \")
|
||||||
list(TRANSFORM emcc_export_list APPEND \")
|
list(TRANSFORM emcc_export_list APPEND \")
|
||||||
string(JOIN "," emcc_export_string ${emcc_export_list})
|
string(JOIN "," emcc_export_string ${emcc_export_list})
|
||||||
set(CMAKE_C_LINK_FLAGS "-s ASM_JS=1 -s EXPORTED_FUNCTIONS='[${emcc_export_string}]'")
|
set(CMAKE_C_LINK_FLAGS "\
|
||||||
|
-s EXPORTED_FUNCTIONS='[${emcc_export_string}]' \
|
||||||
|
-s EXTRA_EXPORTED_RUNTIME_METHODS='[cwrap,callMain]'")
|
||||||
|
|
||||||
set(build_cli_programs FALSE)
|
set(build_cli_programs FALSE)
|
||||||
|
|
||||||
|
46
emcclib.js
46
emcclib.js
@ -23,7 +23,7 @@ mergeInto(LibraryManager.library, {
|
|||||||
* Unused in production, but handy in development.
|
* Unused in production, but handy in development.
|
||||||
*/
|
*/
|
||||||
js_debug: function(ptr) {
|
js_debug: function(ptr) {
|
||||||
console.log(Pointer_stringify(ptr));
|
console.log(UTF8ToString(ptr));
|
||||||
},
|
},
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -34,7 +34,7 @@ mergeInto(LibraryManager.library, {
|
|||||||
* in a configuration dialog).
|
* in a configuration dialog).
|
||||||
*/
|
*/
|
||||||
js_error_box: function(ptr) {
|
js_error_box: function(ptr) {
|
||||||
alert(Pointer_stringify(ptr));
|
alert(UTF8ToString(ptr));
|
||||||
},
|
},
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -68,7 +68,7 @@ mergeInto(LibraryManager.library, {
|
|||||||
* clicked.
|
* clicked.
|
||||||
*/
|
*/
|
||||||
js_add_preset: function(menuid, ptr, value) {
|
js_add_preset: function(menuid, ptr, value) {
|
||||||
var name = Pointer_stringify(ptr);
|
var name = UTF8ToString(ptr);
|
||||||
var item = document.createElement("li");
|
var item = document.createElement("li");
|
||||||
item.setAttribute("data-index", value);
|
item.setAttribute("data-index", value);
|
||||||
var tick = document.createElement("span");
|
var tick = document.createElement("span");
|
||||||
@ -96,7 +96,7 @@ mergeInto(LibraryManager.library, {
|
|||||||
* js_add_preset or this function.
|
* js_add_preset or this function.
|
||||||
*/
|
*/
|
||||||
js_add_preset_submenu: function(menuid, ptr, value) {
|
js_add_preset_submenu: function(menuid, ptr, value) {
|
||||||
var name = Pointer_stringify(ptr);
|
var name = UTF8ToString(ptr);
|
||||||
var item = document.createElement("li");
|
var item = document.createElement("li");
|
||||||
// We still create a transparent tick element, even though it
|
// We still create a transparent tick element, even though it
|
||||||
// won't ever be selected, to make submenu titles line up
|
// won't ever be selected, to make submenu titles line up
|
||||||
@ -167,13 +167,13 @@ mergeInto(LibraryManager.library, {
|
|||||||
* the random seed permalink.
|
* the random seed permalink.
|
||||||
*/
|
*/
|
||||||
js_update_permalinks: function(desc, seed) {
|
js_update_permalinks: function(desc, seed) {
|
||||||
desc = Pointer_stringify(desc);
|
desc = UTF8ToString(desc);
|
||||||
permalink_desc.href = "#" + desc;
|
permalink_desc.href = "#" + desc;
|
||||||
|
|
||||||
if (seed == 0) {
|
if (seed == 0) {
|
||||||
permalink_seed.style.display = "none";
|
permalink_seed.style.display = "none";
|
||||||
} else {
|
} else {
|
||||||
seed = Pointer_stringify(seed);
|
seed = UTF8ToString(seed);
|
||||||
permalink_seed.href = "#" + seed;
|
permalink_seed.href = "#" + seed;
|
||||||
permalink_seed.style.display = "inline";
|
permalink_seed.style.display = "inline";
|
||||||
}
|
}
|
||||||
@ -277,7 +277,7 @@ mergeInto(LibraryManager.library, {
|
|||||||
* Draw a rectangle.
|
* Draw a rectangle.
|
||||||
*/
|
*/
|
||||||
js_canvas_draw_rect: function(x, y, w, h, colptr) {
|
js_canvas_draw_rect: function(x, y, w, h, colptr) {
|
||||||
ctx.fillStyle = Pointer_stringify(colptr);
|
ctx.fillStyle = UTF8ToString(colptr);
|
||||||
ctx.fillRect(x, y, w, h);
|
ctx.fillRect(x, y, w, h);
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -314,7 +314,7 @@ mergeInto(LibraryManager.library, {
|
|||||||
* Postscriptish drawing frameworks).
|
* Postscriptish drawing frameworks).
|
||||||
*/
|
*/
|
||||||
js_canvas_draw_line: function(x1, y1, x2, y2, width, colour) {
|
js_canvas_draw_line: function(x1, y1, x2, y2, width, colour) {
|
||||||
colour = Pointer_stringify(colour);
|
colour = UTF8ToString(colour);
|
||||||
|
|
||||||
ctx.beginPath();
|
ctx.beginPath();
|
||||||
ctx.moveTo(x1 + 0.5, y1 + 0.5);
|
ctx.moveTo(x1 + 0.5, y1 + 0.5);
|
||||||
@ -345,13 +345,13 @@ mergeInto(LibraryManager.library, {
|
|||||||
getValue(pointptr+8*i+4, 'i32') + 0.5);
|
getValue(pointptr+8*i+4, 'i32') + 0.5);
|
||||||
ctx.closePath();
|
ctx.closePath();
|
||||||
if (fill != 0) {
|
if (fill != 0) {
|
||||||
ctx.fillStyle = Pointer_stringify(fill);
|
ctx.fillStyle = UTF8ToString(fill);
|
||||||
ctx.fill();
|
ctx.fill();
|
||||||
}
|
}
|
||||||
ctx.lineWidth = '1';
|
ctx.lineWidth = '1';
|
||||||
ctx.lineCap = 'round';
|
ctx.lineCap = 'round';
|
||||||
ctx.lineJoin = 'round';
|
ctx.lineJoin = 'round';
|
||||||
ctx.strokeStyle = Pointer_stringify(outline);
|
ctx.strokeStyle = UTF8ToString(outline);
|
||||||
ctx.stroke();
|
ctx.stroke();
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -366,13 +366,13 @@ mergeInto(LibraryManager.library, {
|
|||||||
ctx.beginPath();
|
ctx.beginPath();
|
||||||
ctx.arc(x + 0.5, y + 0.5, r, 0, 2*Math.PI);
|
ctx.arc(x + 0.5, y + 0.5, r, 0, 2*Math.PI);
|
||||||
if (fill != 0) {
|
if (fill != 0) {
|
||||||
ctx.fillStyle = Pointer_stringify(fill);
|
ctx.fillStyle = UTF8ToString(fill);
|
||||||
ctx.fill();
|
ctx.fill();
|
||||||
}
|
}
|
||||||
ctx.lineWidth = '1';
|
ctx.lineWidth = '1';
|
||||||
ctx.lineCap = 'round';
|
ctx.lineCap = 'round';
|
||||||
ctx.lineJoin = 'round';
|
ctx.lineJoin = 'round';
|
||||||
ctx.strokeStyle = Pointer_stringify(outline);
|
ctx.strokeStyle = UTF8ToString(outline);
|
||||||
ctx.stroke();
|
ctx.stroke();
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -397,7 +397,7 @@ mergeInto(LibraryManager.library, {
|
|||||||
* per (font,height) pair.
|
* per (font,height) pair.
|
||||||
*/
|
*/
|
||||||
js_canvas_find_font_midpoint: function(height, font) {
|
js_canvas_find_font_midpoint: function(height, font) {
|
||||||
font = Pointer_stringify(font);
|
font = UTF8ToString(font);
|
||||||
|
|
||||||
// Reuse cached value if possible
|
// Reuse cached value if possible
|
||||||
if (midpoint_cache[font] !== undefined)
|
if (midpoint_cache[font] !== undefined)
|
||||||
@ -451,12 +451,12 @@ mergeInto(LibraryManager.library, {
|
|||||||
* function to do it for us with almost no extra effort.
|
* function to do it for us with almost no extra effort.
|
||||||
*/
|
*/
|
||||||
js_canvas_draw_text: function(x, y, halign, colptr, fontptr, text) {
|
js_canvas_draw_text: function(x, y, halign, colptr, fontptr, text) {
|
||||||
ctx.font = Pointer_stringify(fontptr);
|
ctx.font = UTF8ToString(fontptr);
|
||||||
ctx.fillStyle = Pointer_stringify(colptr);
|
ctx.fillStyle = UTF8ToString(colptr);
|
||||||
ctx.textAlign = (halign == 0 ? 'left' :
|
ctx.textAlign = (halign == 0 ? 'left' :
|
||||||
halign == 1 ? 'center' : 'right');
|
halign == 1 ? 'center' : 'right');
|
||||||
ctx.textBaseline = 'alphabetic';
|
ctx.textBaseline = 'alphabetic';
|
||||||
ctx.fillText(Pointer_stringify(text), x, y);
|
ctx.fillText(UTF8ToString(text), x, y);
|
||||||
},
|
},
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -542,7 +542,7 @@ mergeInto(LibraryManager.library, {
|
|||||||
* Set the text in the status bar.
|
* Set the text in the status bar.
|
||||||
*/
|
*/
|
||||||
js_canvas_set_statusbar: function(ptr) {
|
js_canvas_set_statusbar: function(ptr) {
|
||||||
var text = Pointer_stringify(ptr);
|
var text = UTF8ToString(ptr);
|
||||||
statusbar.replaceChild(document.createTextNode(text),
|
statusbar.replaceChild(document.createTextNode(text),
|
||||||
statusbar.lastChild);
|
statusbar.lastChild);
|
||||||
},
|
},
|
||||||
@ -574,7 +574,7 @@ mergeInto(LibraryManager.library, {
|
|||||||
* overlay on top of the rest of the puzzle web page.
|
* overlay on top of the rest of the puzzle web page.
|
||||||
*/
|
*/
|
||||||
js_dialog_init: function(titletext) {
|
js_dialog_init: function(titletext) {
|
||||||
dialog_init(Pointer_stringify(titletext));
|
dialog_init(UTF8ToString(titletext));
|
||||||
},
|
},
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -584,10 +584,10 @@ mergeInto(LibraryManager.library, {
|
|||||||
* construction.
|
* construction.
|
||||||
*/
|
*/
|
||||||
js_dialog_string: function(index, title, initialtext) {
|
js_dialog_string: function(index, title, initialtext) {
|
||||||
dlg_form.appendChild(document.createTextNode(Pointer_stringify(title)));
|
dlg_form.appendChild(document.createTextNode(UTF8ToString(title)));
|
||||||
var editbox = document.createElement("input");
|
var editbox = document.createElement("input");
|
||||||
editbox.type = "text";
|
editbox.type = "text";
|
||||||
editbox.value = Pointer_stringify(initialtext);
|
editbox.value = UTF8ToString(initialtext);
|
||||||
dlg_form.appendChild(editbox);
|
dlg_form.appendChild(editbox);
|
||||||
dlg_form.appendChild(document.createElement("br"));
|
dlg_form.appendChild(document.createElement("br"));
|
||||||
|
|
||||||
@ -607,9 +607,9 @@ mergeInto(LibraryManager.library, {
|
|||||||
* gives the separator.
|
* gives the separator.
|
||||||
*/
|
*/
|
||||||
js_dialog_choices: function(index, title, choicelist, initvalue) {
|
js_dialog_choices: function(index, title, choicelist, initvalue) {
|
||||||
dlg_form.appendChild(document.createTextNode(Pointer_stringify(title)));
|
dlg_form.appendChild(document.createTextNode(UTF8ToString(title)));
|
||||||
var dropdown = document.createElement("select");
|
var dropdown = document.createElement("select");
|
||||||
var choicestr = Pointer_stringify(choicelist);
|
var choicestr = UTF8ToString(choicelist);
|
||||||
var items = choicestr.slice(1).split(choicestr[0]);
|
var items = choicestr.slice(1).split(choicestr[0]);
|
||||||
var options = [];
|
var options = [];
|
||||||
for (var i in items) {
|
for (var i in items) {
|
||||||
@ -653,7 +653,7 @@ mergeInto(LibraryManager.library, {
|
|||||||
dlg_form.appendChild(checkbox);
|
dlg_form.appendChild(checkbox);
|
||||||
var checkboxlabel = document.createElement("label");
|
var checkboxlabel = document.createElement("label");
|
||||||
checkboxlabel.setAttribute("for", checkbox.id);
|
checkboxlabel.setAttribute("for", checkbox.id);
|
||||||
checkboxlabel.textContent = Pointer_stringify(title);
|
checkboxlabel.textContent = UTF8ToString(title);
|
||||||
dlg_form.appendChild(checkboxlabel);
|
dlg_form.appendChild(checkboxlabel);
|
||||||
dlg_form.appendChild(document.createElement("br"));
|
dlg_form.appendChild(document.createElement("br"));
|
||||||
|
|
||||||
|
@ -486,6 +486,7 @@ function initPuzzle() {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Module.onRuntimeInitialized = function() {
|
||||||
// Run the C setup function, passing argv[1] as the fragment
|
// Run the C setup function, passing argv[1] as the fragment
|
||||||
// identifier (so that permalinks of the form puzzle.html#game-id
|
// identifier (so that permalinks of the form puzzle.html#game-id
|
||||||
// can launch the specified id).
|
// can launch the specified id).
|
||||||
@ -497,4 +498,5 @@ function initPuzzle() {
|
|||||||
// show the div containing the actual puzzle.
|
// show the div containing the actual puzzle.
|
||||||
document.getElementById("apology").style.display = "none";
|
document.getElementById("apology").style.display = "none";
|
||||||
document.getElementById("puzzle").style.display = "inline";
|
document.getElementById("puzzle").style.display = "inline";
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user