Change wasm/js/runtime.mjs to a normal .js file; Add interfaces and functions to a global odin variable

This commit is contained in:
gingerBill
2022-05-21 11:39:35 +01:00
parent 3bb31093fa
commit c20b5cbd10

View File

@@ -1,7 +1,9 @@
(function() {
class WasmMemoryInterface {
constructor() {
this.memory = null;
this.exports = null;
this.listenerMap = {};
}
setMemory(memory) {
@@ -10,7 +12,6 @@ class WasmMemoryInterface {
setExports(exports) {
this.exports = exports;
this.listenerMap = {};
}
get mem() {
@@ -90,292 +91,8 @@ class WasmMemoryInterface {
storeUint(addr, value) { this.mem.setUint32 (addr, value, true); }
};
function odinSetupDefaultImports(wasmMemoryInterface, consoleElement) {
const MAX_INFO_CONSOLE_LINES = 512;
let infoConsoleLines = new Array();
const addConsoleLine = (line) => {
if (!line) {
return;
}
if (line.endsWith("\n")) {
line = line.substring(0, line.length-1);
} else if (infoConsoleLines.length > 0) {
let prev_line = infoConsoleLines.pop();
line = prev_line.concat(line);
}
infoConsoleLines.push(line);
if (infoConsoleLines.length > MAX_INFO_CONSOLE_LINES) {
infoConsoleLines.shift();
}
let data = "";
for (let i = 0; i < infoConsoleLines.length; i++) {
if (i != 0) {
data = data.concat("\n");
}
data = data.concat(infoConsoleLines[i]);
}
if (consoleElement) {
let info = consoleElement;
info.innerHTML = data;
info.scrollTop = info.scrollHeight;
}
};
let event_temp_data = {};
return {
"env": {},
"odin_env": {
write: (fd, ptr, len) => {
const str = wasmMemoryInterface.loadString(ptr, len);
if (fd == 1) {
addConsoleLine(str);
return;
} else if (fd == 2) {
addConsoleLine(str);
return;
} else {
throw new Error("Invalid fd to 'write'" + stripNewline(str));
}
},
trap: () => { throw new Error() },
alert: (ptr, len) => { alert(wasmMemoryInterface.loadString(ptr, len)) },
abort: () => { Module.abort() },
evaluate: (str_ptr, str_len) => { eval.call(null, wasmMemoryInterface.loadString(str_ptr, str_len)); },
time_now: () => {
return performance.now() * 1e6;
},
sqrt: (x) => Math.sqrt(x),
sin: (x) => Math.sin(x),
cos: (x) => Math.cos(x),
pow: (x) => Math.pow(x),
fmuladd: (x, y, z) => x*y + z,
ln: (x) => Math.log(x),
exp: (x) => Math.exp(x),
ldexp: (x) => Math.ldexp(x),
},
"odin_dom": {
init_event_raw: (ep) => {
const W = 4;
let offset = ep;
let off = (amount, alignment) => {
if (alignment === undefined) {
alignment = Math.min(amount, W);
}
if (offset % alignment != 0) {
offset += alignment - (offset%alignment);
}
let x = offset;
offset += amount;
return x;
};
let wmi = wasmMemoryInterface;
let e = event_temp_data.event;
wmi.storeU32(off(4), event_temp_data.name_code);
if (e.target == document) {
wmi.storeU32(off(4), 1);
} else if (e.target == window) {
wmi.storeU32(off(4), 2);
} else {
wmi.storeU32(off(4), 0);
}
if (e.currentTarget == document) {
wmi.storeU32(off(4), 1);
} else if (e.currentTarget == window) {
wmi.storeU32(off(4), 2);
} else {
wmi.storeU32(off(4), 0);
}
wmi.storeUint(off(W), event_temp_data.id_ptr);
wmi.storeUint(off(W), event_temp_data.id_len);
wmi.storeF64(off(8), e.timeStamp*1e-3);
wmi.storeU8(off(1), e.eventPhase);
wmi.storeU8(off(1), !!e.bubbles);
wmi.storeU8(off(1), !!e.cancelable);
wmi.storeU8(off(1), !!e.composed);
wmi.storeU8(off(1), !!e.isComposing);
wmi.storeU8(off(1), !!e.isTrusted);
let base = off(0, 8);
if (e instanceof MouseEvent) {
wmi.storeI64(off(8), e.screenX);
wmi.storeI64(off(8), e.screenY);
wmi.storeI64(off(8), e.clientX);
wmi.storeI64(off(8), e.clientY);
wmi.storeI64(off(8), e.offsetX);
wmi.storeI64(off(8), e.offsetY);
wmi.storeI64(off(8), e.pageX);
wmi.storeI64(off(8), e.pageY);
wmi.storeI64(off(8), e.movementX);
wmi.storeI64(off(8), e.movementY);
wmi.storeU8(off(1), !!e.ctrlKey);
wmi.storeU8(off(1), !!e.shiftKey);
wmi.storeU8(off(1), !!e.altKey);
wmi.storeU8(off(1), !!e.metaKey);
wmi.storeI16(off(2), e.button);
wmi.storeU16(off(2), e.buttons);
} else if (e instanceof KeyboardEvent) {
let keyOffset = off(W*2, W);
let codeOffet = off(W*2, W);
wmi.storeU8(off(1), e.location);
wmi.storeU8(off(1), !!e.ctrlKey);
wmi.storeU8(off(1), !!e.shiftKey);
wmi.storeU8(off(1), !!e.altKey);
wmi.storeU8(off(1), !!e.metaKey);
wmi.storeU8(off(1), !!e.repeat);
} else if (e instanceof WheelEvent) {
wmi.storeF64(off(8), e.deltaX);
wmi.storeF64(off(8), e.deltaY);
wmi.storeF64(off(8), e.deltaZ);
wmi.storeU32(off(4), e.deltaMode);
} else if (e instanceof Event) {
if ('scrollX' in e) {
wmi.storeF64(off(8), e.scrollX);
wmi.storeF64(off(8), e.scrollY);
}
}
},
add_event_listener: (id_ptr, id_len, name_ptr, name_len, name_code, data, callback, use_capture) => {
let id = wasmMemoryInterface.loadString(id_ptr, id_len);
let name = wasmMemoryInterface.loadString(name_ptr, name_len);
let element = document.getElementById(id);
if (element == undefined) {
return false;
}
let listener = (e) => {
const odin_ctx = wasmMemoryInterface.exports.default_context_ptr();
event_temp_data.id_ptr = id_ptr;
event_temp_data.id_len = id_len;
event_temp_data.event = e;
event_temp_data.name_code = name_code;
// console.log(e);
wasmMemoryInterface.exports.odin_dom_do_event_callback(data, callback, odin_ctx);
};
wasmMemoryInterface.listenerMap[{data: data, callback: callback}] = listener;
element.addEventListener(name, listener, !!use_capture);
return true;
},
remove_event_listener: (id_ptr, id_len, name_ptr, name_len, data, callback) => {
let id = wasmMemoryInterface.loadString(id_ptr, id_len);
let name = wasmMemoryInterface.loadString(name_ptr, name_len);
let element = document.getElementById(id);
if (element == undefined) {
return false;
}
let listener = wasmMemoryInterface.listenerMap[{data: data, callback: callback}];
if (listener == undefined) {
return false;
}
element.removeEventListener(name, listener);
return true;
},
add_window_event_listener: (name_ptr, name_len, name_code, data, callback, use_capture) => {
let name = wasmMemoryInterface.loadString(name_ptr, name_len);
let element = window;
let listener = (e) => {
const odin_ctx = wasmMemoryInterface.exports.default_context_ptr();
event_temp_data.id_ptr = 0;
event_temp_data.id_len = 0;
event_temp_data.event = e;
event_temp_data.name_code = name_code;
// console.log(e);
wasmMemoryInterface.exports.odin_dom_do_event_callback(data, callback, odin_ctx);
};
wasmMemoryInterface.listenerMap[{data: data, callback: callback}] = listener;
element.addEventListener(name, listener, !!use_capture);
return true;
},
remove_window_event_listener: (name_ptr, name_len, data, callback) => {
let name = wasmMemoryInterface.loadString(name_ptr, name_len);
let element = window;
let listener = wasmMemoryInterface.listenerMap[{data: data, callback: callback}];
if (listener == undefined) {
return false;
}
element.removeEventListener(name, listener);
return true;
},
event_stop_propagation: () => {
if (event_temp_data && event_temp_data.event) {
event_temp_data.event.eventStopPropagation();
}
},
event_stop_immediate_propagation: () => {
if (event_temp_data && event_temp_data.event) {
event_temp_data.event.eventStopImmediatePropagation();
}
},
event_prevent_default: () => {
if (event_temp_data && event_temp_data.event) {
event_temp_data.event.eventPreventDefault();
}
},
get_element_value_f64: (id_ptr, id_len) => {
let id = wasmMemoryInterface.loadString(id_ptr, id_len);
let element = document.getElementById(id);
return element ? element.value : 0;
},
get_element_value_string: (id_ptr, id_len, buf_ptr, buf_len) => {
let id = wasmMemoryInterface.loadString(id_ptr, id_len);
let element = document.getElementById(id);
if (element) {
let str = element.value;
if (buf_len > 0 && buf_ptr) {
let n = Math.min(buf_len, str.length);
str = str.substring(0, n);
this.mem.loadBytes(buf_ptr, buf_len).set(new TextEncoder("utf-8").encode(str))
return n;
}
}
return 0;
},
get_element_min_max: (ptr_array2_f64, id_ptr, id_len) => {
let id = wasmMemoryInterface.loadString(id_ptr, id_len);
let element = document.getElementById(id);
if (element) {
let values = wasmMemoryInterface.loadF64Array(ptr_array2_f64, 2);
values[0] = element.min;
values[1] = element.max;
}
},
set_element_value: (id_ptr, id_len, value) => {
let id = wasmMemoryInterface.loadString(id_ptr, id_len);
let element = document.getElementById(id);
if (element) {
element.value = value;
}
},
},
};
};
class WebGLInterface {
constructor(wasmMemoryInterface, canvasElement, contextSettings) {
constructor(wasmMemoryInterface) {
this.wasmMemoryInterface = wasmMemoryInterface;
this.ctxElement = null;
this.ctx = null;
@@ -400,8 +117,6 @@ class WebGLInterface {
this.transformFeedbacks = [];
this.syncs = [];
this.programInfos = {};
this.setCurrentContext(canvasElement, contextSettings);
}
get mem() {
@@ -1457,4 +1172,346 @@ class WebGLInterface {
};
export {WasmMemoryInterface, odinSetupDefaultImports, WebGLInterface};
function odinSetupDefaultImports(wasmMemoryInterface, consoleElement) {
const MAX_INFO_CONSOLE_LINES = 512;
let infoConsoleLines = new Array();
const addConsoleLine = (line) => {
if (!line) {
return;
}
if (line.endsWith("\n")) {
line = line.substring(0, line.length-1);
} else if (infoConsoleLines.length > 0) {
let prev_line = infoConsoleLines.pop();
line = prev_line.concat(line);
}
infoConsoleLines.push(line);
if (infoConsoleLines.length > MAX_INFO_CONSOLE_LINES) {
infoConsoleLines.shift();
}
let data = "";
for (let i = 0; i < infoConsoleLines.length; i++) {
if (i != 0) {
data = data.concat("\n");
}
data = data.concat(infoConsoleLines[i]);
}
if (consoleElement) {
let info = consoleElement;
info.innerHTML = data;
info.scrollTop = info.scrollHeight;
}
};
let event_temp_data = {};
let webglContext = new WebGLInterface(wasmMemoryInterface);
return {
"env": {},
"odin_env": {
write: (fd, ptr, len) => {
const str = wasmMemoryInterface.loadString(ptr, len);
if (fd == 1) {
addConsoleLine(str);
return;
} else if (fd == 2) {
addConsoleLine(str);
return;
} else {
throw new Error("Invalid fd to 'write'" + stripNewline(str));
}
},
trap: () => { throw new Error() },
alert: (ptr, len) => { alert(wasmMemoryInterface.loadString(ptr, len)) },
abort: () => { Module.abort() },
evaluate: (str_ptr, str_len) => { eval.call(null, wasmMemoryInterface.loadString(str_ptr, str_len)); },
time_now: () => {
return performance.now() * 1e6;
},
sqrt: (x) => Math.sqrt(x),
sin: (x) => Math.sin(x),
cos: (x) => Math.cos(x),
pow: (x) => Math.pow(x),
fmuladd: (x, y, z) => x*y + z,
ln: (x) => Math.log(x),
exp: (x) => Math.exp(x),
ldexp: (x) => Math.ldexp(x),
},
"odin_dom": {
init_event_raw: (ep) => {
const W = 4;
let offset = ep;
let off = (amount, alignment) => {
if (alignment === undefined) {
alignment = Math.min(amount, W);
}
if (offset % alignment != 0) {
offset += alignment - (offset%alignment);
}
let x = offset;
offset += amount;
return x;
};
let wmi = wasmMemoryInterface;
let e = event_temp_data.event;
wmi.storeU32(off(4), event_temp_data.name_code);
if (e.target == document) {
wmi.storeU32(off(4), 1);
} else if (e.target == window) {
wmi.storeU32(off(4), 2);
} else {
wmi.storeU32(off(4), 0);
}
if (e.currentTarget == document) {
wmi.storeU32(off(4), 1);
} else if (e.currentTarget == window) {
wmi.storeU32(off(4), 2);
} else {
wmi.storeU32(off(4), 0);
}
wmi.storeUint(off(W), event_temp_data.id_ptr);
wmi.storeUint(off(W), event_temp_data.id_len);
wmi.storeF64(off(8), e.timeStamp*1e-3);
wmi.storeU8(off(1), e.eventPhase);
wmi.storeU8(off(1), !!e.bubbles);
wmi.storeU8(off(1), !!e.cancelable);
wmi.storeU8(off(1), !!e.composed);
wmi.storeU8(off(1), !!e.isComposing);
wmi.storeU8(off(1), !!e.isTrusted);
let base = off(0, 8);
if (e instanceof MouseEvent) {
wmi.storeI64(off(8), e.screenX);
wmi.storeI64(off(8), e.screenY);
wmi.storeI64(off(8), e.clientX);
wmi.storeI64(off(8), e.clientY);
wmi.storeI64(off(8), e.offsetX);
wmi.storeI64(off(8), e.offsetY);
wmi.storeI64(off(8), e.pageX);
wmi.storeI64(off(8), e.pageY);
wmi.storeI64(off(8), e.movementX);
wmi.storeI64(off(8), e.movementY);
wmi.storeU8(off(1), !!e.ctrlKey);
wmi.storeU8(off(1), !!e.shiftKey);
wmi.storeU8(off(1), !!e.altKey);
wmi.storeU8(off(1), !!e.metaKey);
wmi.storeI16(off(2), e.button);
wmi.storeU16(off(2), e.buttons);
} else if (e instanceof KeyboardEvent) {
let keyOffset = off(W*2, W);
let codeOffet = off(W*2, W);
wmi.storeU8(off(1), e.location);
wmi.storeU8(off(1), !!e.ctrlKey);
wmi.storeU8(off(1), !!e.shiftKey);
wmi.storeU8(off(1), !!e.altKey);
wmi.storeU8(off(1), !!e.metaKey);
wmi.storeU8(off(1), !!e.repeat);
} else if (e instanceof WheelEvent) {
wmi.storeF64(off(8), e.deltaX);
wmi.storeF64(off(8), e.deltaY);
wmi.storeF64(off(8), e.deltaZ);
wmi.storeU32(off(4), e.deltaMode);
} else if (e instanceof Event) {
if ('scrollX' in e) {
wmi.storeF64(off(8), e.scrollX);
wmi.storeF64(off(8), e.scrollY);
}
}
},
add_event_listener: (id_ptr, id_len, name_ptr, name_len, name_code, data, callback, use_capture) => {
let id = wasmMemoryInterface.loadString(id_ptr, id_len);
let name = wasmMemoryInterface.loadString(name_ptr, name_len);
let element = document.getElementById(id);
if (element == undefined) {
return false;
}
let listener = (e) => {
const odin_ctx = wasmMemoryInterface.exports.default_context_ptr();
event_temp_data.id_ptr = id_ptr;
event_temp_data.id_len = id_len;
event_temp_data.event = e;
event_temp_data.name_code = name_code;
// console.log(e);
wasmMemoryInterface.exports.odin_dom_do_event_callback(data, callback, odin_ctx);
};
wasmMemoryInterface.listenerMap[{data: data, callback: callback}] = listener;
element.addEventListener(name, listener, !!use_capture);
return true;
},
remove_event_listener: (id_ptr, id_len, name_ptr, name_len, data, callback) => {
let id = wasmMemoryInterface.loadString(id_ptr, id_len);
let name = wasmMemoryInterface.loadString(name_ptr, name_len);
let element = document.getElementById(id);
if (element == undefined) {
return false;
}
let listener = wasmMemoryInterface.listenerMap[{data: data, callback: callback}];
if (listener == undefined) {
return false;
}
element.removeEventListener(name, listener);
return true;
},
add_window_event_listener: (name_ptr, name_len, name_code, data, callback, use_capture) => {
let name = wasmMemoryInterface.loadString(name_ptr, name_len);
let element = window;
let listener = (e) => {
const odin_ctx = wasmMemoryInterface.exports.default_context_ptr();
event_temp_data.id_ptr = 0;
event_temp_data.id_len = 0;
event_temp_data.event = e;
event_temp_data.name_code = name_code;
// console.log(e);
wasmMemoryInterface.exports.odin_dom_do_event_callback(data, callback, odin_ctx);
};
wasmMemoryInterface.listenerMap[{data: data, callback: callback}] = listener;
element.addEventListener(name, listener, !!use_capture);
return true;
},
remove_window_event_listener: (name_ptr, name_len, data, callback) => {
let name = wasmMemoryInterface.loadString(name_ptr, name_len);
let element = window;
let listener = wasmMemoryInterface.listenerMap[{data: data, callback: callback}];
if (listener == undefined) {
return false;
}
element.removeEventListener(name, listener);
return true;
},
event_stop_propagation: () => {
if (event_temp_data && event_temp_data.event) {
event_temp_data.event.eventStopPropagation();
}
},
event_stop_immediate_propagation: () => {
if (event_temp_data && event_temp_data.event) {
event_temp_data.event.eventStopImmediatePropagation();
}
},
event_prevent_default: () => {
if (event_temp_data && event_temp_data.event) {
event_temp_data.event.eventPreventDefault();
}
},
get_element_value_f64: (id_ptr, id_len) => {
let id = wasmMemoryInterface.loadString(id_ptr, id_len);
let element = document.getElementById(id);
return element ? element.value : 0;
},
get_element_value_string: (id_ptr, id_len, buf_ptr, buf_len) => {
let id = wasmMemoryInterface.loadString(id_ptr, id_len);
let element = document.getElementById(id);
if (element) {
let str = element.value;
if (buf_len > 0 && buf_ptr) {
let n = Math.min(buf_len, str.length);
str = str.substring(0, n);
this.mem.loadBytes(buf_ptr, buf_len).set(new TextEncoder("utf-8").encode(str))
return n;
}
}
return 0;
},
get_element_min_max: (ptr_array2_f64, id_ptr, id_len) => {
let id = wasmMemoryInterface.loadString(id_ptr, id_len);
let element = document.getElementById(id);
if (element) {
let values = wasmMemoryInterface.loadF64Array(ptr_array2_f64, 2);
values[0] = element.min;
values[1] = element.max;
}
},
set_element_value: (id_ptr, id_len, value) => {
let id = wasmMemoryInterface.loadString(id_ptr, id_len);
let element = document.getElementById(id);
if (element) {
element.value = value;
}
},
},
"webgl": webglContext.getWebGL1Interface(),
"webgl2": webglContext.getWebGL2Interface(),
};
};
async function runWasmCanvas(wasmPath, consoleElement, extraForeignImports) {
let wasmMemoryInterface = new WasmMemoryInterface();
let imports = odinSetupDefaultImports(wasmMemoryInterface, consoleElement);
let exports = {};
if (extraForeignImports !== undefined) {
imports = {
...imports,
...extraForeignImports,
};
}
const response = await fetch(wasmPath);
const file = await response.arrayBuffer();
const wasm = await WebAssembly.instantiate(file, imports);
exports = wasm.instance.exports;
wasmMemoryInterface.setExports(exports);
wasmMemoryInterface.setMemory(exports.memory);
exports._start();
if (exports.step) {
const odin_ctx = exports.default_context_ptr();
let prevTimeStamp = undefined;
const step = (currTimeStamp) => {
if (prevTimeStamp == undefined) {
prevTimeStamp = currTimeStamp;
}
const dt = (currTimeStamp - prevTimeStamp)*0.001;
prevTimeStamp = currTimeStamp;
exports.step(dt, odin_ctx);
window.requestAnimationFrame(step);
};
window.requestAnimationFrame(step);
}
exports._end();
return;
};
window.odin = {
// Interface Types
WasmMemoryInterface: WasmMemoryInterface,
WebGLInterface: WebGLInterface,
// Functions
setupDefaultImports: odinSetupDefaultImports,
runWasmCanvas: runWasmCanvas,
};
})();