msagent.js: misc cleanup

make wasm module stuff a bit less horrid and untyped so that it can maybe be reused for other things
This commit is contained in:
Lily Tsuru 2024-11-25 16:56:04 -05:00
parent d97004a6dd
commit 14642b74d7
6 changed files with 77 additions and 23 deletions

View file

@ -1,5 +1,6 @@
{
"name": "@msagent.js/core",
"description": "msagent.js core. Provides structures, parsing, and bitmap utilties for higher level usage",
"version": "0.1.0",
"packageManager": "yarn@4.2.2",
"devDependencies": {

View file

@ -1,21 +1,19 @@
// Please note that the "meaningless" shifts of 0 are to force
// the value to be a 32-bit integer. Do not remove them.
import { WasmModule, WebassemblyOwnMemoryExports } from './wasm_module';
let compressWasm: WebAssembly.WebAssemblyInstantiatedSource;
interface CompressWasmExports {
memory: WebAssembly.Memory;
agentDecompressWASM: any;
// WASM exports for the decompression module.
interface CompressWasmExports extends WebassemblyOwnMemoryExports {
agentDecompressWASM: (pSource: number, sourceLen: number, pDest: number, destLen: number) => number;
}
let compressWasm = new WasmModule<CompressWasmExports>(new URL('decompress.wasm', import.meta.url));
// Initalize the decompression module
export async function compressInit() {
let url = new URL('decompress.wasm', import.meta.url);
compressWasm = await WebAssembly.instantiateStreaming(fetch(url));
await compressWasm.Initalize();
}
function compressWasmGetExports() {
return compressWasm.instance.exports as any as CompressWasmExports;
return compressWasm.Exports;
}
function compressWASMGetMemory(): WebAssembly.Memory {
@ -32,14 +30,9 @@ function compressWASMGetMemory(): WebAssembly.Memory {
export function compressDecompress(src: Uint8Array, dest: Uint8Array) {
// Grow the WASM heap if needed. Funnily enough, this code is never hit in most
// ACSes, so IDK if it's even needed
let memory = compressWASMGetMemory();
if (memory.buffer.byteLength < src.length + dest.length) {
// A WebAssembly page is 64kb, so we need to grow at least that much
let npages = Math.floor((src.length + dest.length) / 65535) + 1;
console.log('Need to grow WASM heap', npages, 'pages', '(current byteLength is', memory.buffer.byteLength, ', we need', src.length + dest.length, ')');
memory.grow(npages);
}
compressWasm.growHeapTo(src.length + dest.length);
let memory = compressWASMGetMemory();
let copyBuffer = new Uint8Array(memory.buffer);
// Copy source to memory[0]. This will make things a bit simpler
@ -50,6 +43,7 @@ export function compressDecompress(src: Uint8Array, dest: Uint8Array) {
if (nrBytesDecompressed != dest.length) throw new Error(`decompression failed: ${nrBytesDecompressed} != ${dest.length}`);
// Dest will be memory[src.length..dest.length]
// The uncompressed data is located at memory[src.length..dest.length].
// Copy it into the destination buffer.
dest.set(copyBuffer.slice(src.length, src.length + dest.length), 0);
}

View file

@ -38,7 +38,8 @@ export function imageDrawToBuffer(imageEntry: AcsImageEntry, palette: RGBAColor[
bufStream.seek(dwAlign(bufStream.tell()), SeekDir.BEG);
}
// Next, draw the rows converted to RGBA, top down (so it's drawn as you'd expect)
// Next, draw the rows converted to RGBA, top down (so it's drawn correctly,
// and in the RGBA format we want to return)
for (let y = 0; y < imageEntry.image.height - 1; ++y) {
let row = rows[y];
for (let x = 0; x < imageEntry.image.width; ++x) {

View file

@ -0,0 +1,44 @@
export interface WebassemblyOwnMemoryExports extends WebAssembly.Exports {
memory: WebAssembly.Memory;
}
// A helper for WASM modules.
// This currently only works for modules that export their own memory,
// and do not import it.
//
// This covers all of the modules in the core so far, so
// this is perfectly fine, but it's still worth noting.
export class WasmModule<TExports extends WebassemblyOwnMemoryExports> {
private module: WebAssembly.WebAssemblyInstantiatedSource | null = null;
private url: URL;
constructor(url: URL) {
this.url = url;
}
async Initalize(): Promise<void> {
this.module = await WebAssembly.instantiateStreaming(fetch(this.url));
}
get Module() {
if (this.module == null) throw new Error('WasmModule<T> has not been initalized');
return this.module;
}
get Exports() {
let module = this.Module;
return module.instance.exports as TExports;
}
// Grows the WebAssembly heap if required.
growHeapTo(newSize: number) {
let exports = this.Exports;
let memory: WebAssembly.Memory = exports.memory;
if (memory.buffer.byteLength < newSize) {
// A WebAssembly page is 64kb, so we need to grow at least a single page,
// even if it would be relatively wasteful to do so.
let npages = Math.floor(newSize / 65535) + 1;
memory.grow(npages);
}
}
}

View file

@ -5,6 +5,11 @@ import { Agent } from './agent.js';
let acsDataCache = new Map<string, AcsData>();
// Purges the ACS cache.
//
// FIXME: A smarter way to do this would probably be instead reference count
// AcsData instances, then when an agent is disposed decrement reference count
// (or leave it at 0). Once it's 0 then a globally running interval can remove
// all keys that have no refcount.
export function agentPurgeACSCache() {
acsDataCache.clear();
}

View file

@ -3619,11 +3619,11 @@ __metadata:
linkType: hard
"node-abi@npm:^3.3.0":
version: 3.65.0
resolution: "node-abi@npm:3.65.0"
version: 3.67.0
resolution: "node-abi@npm:3.67.0"
dependencies:
semver: "npm:^7.3.5"
checksum: 10c0/112672015d8f27d6be2f18d64569f28f5d6a15a94cc510da513c69c3e3ab5df6dac196ef13ff115a8fadb69b554974c47ef89b4f6350a2b02de2bca5c23db1e5
checksum: 10c0/72ce2edbdfb84745bc201a4e48aa7146fd88a0d2c80046b6b17f28439c9a7683eab846f40f1e819349c31f7d9331ed5c50d1e741208d938dd5f38b29cab2275e
languageName: node
linkType: hard
@ -4246,7 +4246,7 @@ __metadata:
languageName: node
linkType: hard
"semver@npm:^7.3.5, semver@npm:^7.3.8, semver@npm:^7.5.2, semver@npm:^7.5.4, semver@npm:^7.6.0":
"semver@npm:^7.3.5, semver@npm:^7.5.2, semver@npm:^7.5.4, semver@npm:^7.6.0":
version: 7.6.2
resolution: "semver@npm:7.6.2"
bin:
@ -4255,6 +4255,15 @@ __metadata:
languageName: node
linkType: hard
"semver@npm:^7.3.8":
version: 7.6.3
resolution: "semver@npm:7.6.3"
bin:
semver: bin/semver.js
checksum: 10c0/88f33e148b210c153873cb08cfe1e281d518aaa9a666d4d148add6560db5cd3c582f3a08ccb91f38d5f379ead256da9931234ed122057f40bb5766e65e58adaf
languageName: node
linkType: hard
"seq-queue@npm:^0.0.5":
version: 0.0.5
resolution: "seq-queue@npm:0.0.5"