From bbea45e6dd8a364cda8d4ca6b4777a138d7e8b94 Mon Sep 17 00:00:00 2001 From: Christian Semmler Date: Wed, 2 Jul 2025 18:58:41 -0700 Subject: [PATCH] Add runtime option to disable OffscreenCanvas in web port --- ISLE/emscripten/emscripten.patch | 141 +++++++++++++++++++++++++++++++ docker/emscripten/Dockerfile | 6 +- 2 files changed, 144 insertions(+), 3 deletions(-) create mode 100644 ISLE/emscripten/emscripten.patch diff --git a/ISLE/emscripten/emscripten.patch b/ISLE/emscripten/emscripten.patch new file mode 100644 index 00000000..899d72eb --- /dev/null +++ b/ISLE/emscripten/emscripten.patch @@ -0,0 +1,141 @@ +diff --git a/src/lib/libpthread.js b/src/lib/libpthread.js +index 6d979627e..97e3f8684 100644 +--- a/src/lib/libpthread.js ++++ b/src/lib/libpthread.js +@@ -697,7 +697,7 @@ var LibraryPThread = { + { + transferredCanvasNames = UTF8ToString(transferredCanvasNames).trim(); + } +- transferredCanvasNames = transferredCanvasNames ? transferredCanvasNames.split(',') : []; ++ transferredCanvasNames = transferredCanvasNames && !Module['disableOffscreenCanvases'] ? transferredCanvasNames.split(',') : []; + #if GL_DEBUG + dbg(`pthread_create: transferredCanvasNames="${transferredCanvasNames}"`); + #endif +diff --git a/src/lib/libwasmfs_fetch.js b/src/lib/libwasmfs_fetch.js +index e8c9f7e21..caf1971d2 100644 +--- a/src/lib/libwasmfs_fetch.js ++++ b/src/lib/libwasmfs_fetch.js +@@ -38,36 +38,7 @@ addToLibrary({ + var chunkSize = __wasmfs_fetch_get_chunk_size(file); + offset ??= 0; + len ??= chunkSize; +- // In which chunk does the seeked range start? E.g., 5-14 with chunksize 8 will start in chunk 0. +- if (!(file in wasmFS$JSMemoryRanges)) { +- var fileInfo = await fetch(url, {method:'HEAD', headers:{'Range': 'bytes=0-'}}); +- if (fileInfo.ok && +- fileInfo.headers.has('Content-Length') && +- fileInfo.headers.get('Accept-Ranges') == 'bytes' && +- (parseInt(fileInfo.headers.get('Content-Length'), 10) > chunkSize*2)) { +- var size = parseInt(fileInfo.headers.get('Content-Length'), 10); +- wasmFS$JSMemoryRanges[file] = { +- size, +- chunks: [], +- chunkSize: chunkSize +- }; +- len = Math.min(len, size-offset); +- } else { +- // may as well/forced to download the whole file +- var wholeFileReq = await fetch(url); +- if (!wholeFileReq.ok) { +- throw wholeFileReq; +- } +- var wholeFileData = new Uint8Array(await wholeFileReq.arrayBuffer()); +- var text = new TextDecoder().decode(wholeFileData); +- wasmFS$JSMemoryRanges[file] = { +- size: wholeFileData.byteLength, +- chunks: [wholeFileData], +- chunkSize: wholeFileData.byteLength +- }; +- return Promise.resolve(); +- } +- } ++ + var firstChunk = (offset / chunkSize) | 0; + // In which chunk does the seeked range end? E.g., 5-14 with chunksize 8 will end in chunk 1, as will 5-16 (since byte 16 isn't requested). + // This will always give us a chunk >= firstChunk since len > 0. +@@ -76,7 +47,7 @@ addToLibrary({ + var i; + // Do we have all the chunks already? If so, we don't need to do any fetches. + for (i = firstChunk; i <= lastChunk; i++) { +- if (!wasmFS$JSMemoryRanges[file].chunks[i]) { ++ if (!(file in wasmFS$JSMemoryRanges) || !wasmFS$JSMemoryRanges[file].chunks[i]) { + allPresent = false; + break; + } +@@ -90,16 +61,37 @@ addToLibrary({ + // one request for all the chunks we need, rather than one + // request per chunk. + var start = firstChunk * chunkSize; ++ ++ // Out of bounds. No request necessary. ++ if ((file in wasmFS$JSMemoryRanges) && start >= wasmFS$JSMemoryRanges[file].size) { ++ return Promise.resolve(); ++ } ++ + // We must fetch *up to* the last byte of the last chunk. + var end = (lastChunk+1) * chunkSize; +- var response = await fetch(url, {headers:{'Range': `bytes=${start}-${end-1}`}}); ++ var response = await fetch(url, {headers:{'Range': `bytes=${start}-${end-1}`, 'Accept-Language': wasmFS$language}}); + if (!response.ok) { + throw response; + } +- var bytes = await response['bytes'](); ++ ++ const buffer = await response.arrayBuffer(); ++ const bytes = new Uint8Array(buffer); ++ if (!(file in wasmFS$JSMemoryRanges)) { ++ var size = Math.max( ++ parseInt(response.headers.get('Content-Range').split('/')[1], 10), ++ bytes.length ++ ); ++ wasmFS$JSMemoryRanges[file] = { ++ size, ++ chunks: [], ++ chunkSize: chunkSize ++ }; ++ } ++ + for (i = firstChunk; i <= lastChunk; i++) { + wasmFS$JSMemoryRanges[file].chunks[i] = bytes.slice(i*chunkSize-start,(i+1)*chunkSize-start); + } ++ + return Promise.resolve(); + } + +@@ -156,14 +148,31 @@ addToLibrary({ + return readLength; + }, + getSize: async (file) => { +- try { +- await getFileRange(file, 0, 0); +- } catch (failedResponse) { +- return 0; ++ if (!(file in wasmFS$JSMemoryRanges)) { ++ try { ++ await getFileRange(file, undefined, undefined); ++ } catch (failedResponse) { ++ return 0; ++ } + } + return wasmFS$JSMemoryRanges[file].size; + }, + }; ++ ++ wasmFS$language = await (async () => { ++ try { ++ const fileHandle = await (await navigator.storage.getDirectory()).getFileHandle('isle.ini'); ++ const content = await (await fileHandle.getFile()).text(); ++ const match = content.match(/^Language\s*=\s*(.*)/m); ++ ++ if (match && match[1]) { ++ return match[1].trim(); ++ } ++ } catch (e) { ++ console.warn("Could not read 'isle.ini' or 'Language' key not found. Falling back to 'en'.", e); ++ } ++ ++ return 'en'; ++ })(); + }, +- + }); diff --git a/docker/emscripten/Dockerfile b/docker/emscripten/Dockerfile index f234e9ed..a213093c 100644 --- a/docker/emscripten/Dockerfile +++ b/docker/emscripten/Dockerfile @@ -16,10 +16,10 @@ RUN chown -R emscripten:emscripten /src USER emscripten -COPY ISLE/emscripten/libwasmfs_fetch.js.patch /tmp/ +COPY ISLE/emscripten/emscripten.patch /tmp/ RUN cd /emsdk/upstream/emscripten && \ - git apply --check /tmp/libwasmfs_fetch.js.patch && \ - git apply /tmp/libwasmfs_fetch.js.patch + git apply --check /tmp/emscripten.patch && \ + git apply /tmp/emscripten.patch COPY --chown=emscripten:emscripten 3rdparty/ ./3rdparty/ COPY --chown=emscripten:emscripten LEGO1/ ./LEGO1/