From bc1a4fd988608e4e4f8f0dd49e23cea3bfd1c984 Mon Sep 17 00:00:00 2001 From: Philipp Muigg Date: Thu, 5 Jan 2023 17:23:17 +0100 Subject: [PATCH] opengl support for memory64 --- emcc.py | 9 +- src/embind/embind.js | 6 + src/library_egl.js | 2 + src/library_html5.js | 14 ++- src/library_strings.js | 3 + src/library_webgl.js | 262 ++++++++++++++++++++++------------------- src/library_webgl2.js | 62 +++++----- src/parseTools.js | 72 ++++++++++- 8 files changed, 266 insertions(+), 164 deletions(-) diff --git a/emcc.py b/emcc.py index d0d09f294acc3..58535f987ab48 100755 --- a/emcc.py +++ b/emcc.py @@ -2843,10 +2843,11 @@ def get_full_import_name(name): # check if we can address the 2GB mark and higher: either if we start at # 2GB, or if we allow growth to either any amount or to 2GB or more. - if settings.INITIAL_MEMORY > 2 * 1024 * 1024 * 1024 or \ - (settings.ALLOW_MEMORY_GROWTH and - (settings.MAXIMUM_MEMORY < 0 or - settings.MAXIMUM_MEMORY > 2 * 1024 * 1024 * 1024)): + if settings.MEMORY64 == 0 and \ + (settings.INITIAL_MEMORY > 2 * 1024 * 1024 * 1024 or + (settings.ALLOW_MEMORY_GROWTH and + (settings.MAXIMUM_MEMORY < 0 or + settings.MAXIMUM_MEMORY > 2 * 1024 * 1024 * 1024))): settings.CAN_ADDRESS_2GB = 1 settings.EMSCRIPTEN_VERSION = shared.EMSCRIPTEN_VERSION diff --git a/src/embind/embind.js b/src/embind/embind.js index 15c021ab7ce14..0fd3f47620bd3 100644 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -2,6 +2,8 @@ // Emscripten is available under two separate licenses, the MIT license and the // University of Illinois/NCSA Open Source License. Both these licenses can be // found in the LICENSE file. +// SPDX-FileCopyrightText: Portions Copyright 2023 Siemens +// Modified on February 2023 by Siemens and/or its affiliates to improve memory64 support /*global LibraryManager, mergeInto*/ @@ -445,7 +447,11 @@ var LibraryEmbind = { } else { throw new TypeError("Unknown boolean type size: " + name); } +#if MEMORY64 + return this['fromWireType'](heap[pointer / (1 << shift)]); +#else return this['fromWireType'](heap[pointer >> shift]); +#endif }, destructorFunction: null, // This type does not need a destructor }); diff --git a/src/library_egl.js b/src/library_egl.js index 423479536bcb1..abc30a468f728 100644 --- a/src/library_egl.js +++ b/src/library_egl.js @@ -2,6 +2,8 @@ * @license * Copyright 2012 The Emscripten Authors * SPDX-License-Identifier: MIT + * SPDX-FileCopyrightText: Portions Copyright 2023 Siemens + * Modified on February 2023 by Siemens and/or its affiliates to improve memory64 support */ /* diff --git a/src/library_html5.js b/src/library_html5.js index ed6887011566d..b8c56760fbfe4 100644 --- a/src/library_html5.js +++ b/src/library_html5.js @@ -2,6 +2,8 @@ * @license * Copyright 2014 The Emscripten Authors * SPDX-License-Identifier: MIT + * SPDX-FileCopyrightText: Portions Copyright 2023 Siemens + * Modified on February 2023 by Siemens and/or its affiliates to improve memory64 support */ var LibraryHTML5 = { @@ -314,7 +316,11 @@ var LibraryHTML5 = { // values we accept here, EMSCRIPTEN_EVENT_TARGET_* (which map to 0, 1, 2). // In other words, if cString > 2 then it's a pointer to a valid place in // memory, and points to a C string. +#if MEMORY64 + return cString > 2 ? UTF8ToString(Number(cString)) : cString; +#else return cString > 2 ? UTF8ToString(cString) : cString; +#endif }, $findEventTarget__deps: ['$maybeCStringToJsString', '$specialHTMLTargets'], @@ -519,10 +525,10 @@ var LibraryHTML5 = { if (targetThread) { var mouseEventData = _malloc( {{{ C_STRUCTS.EmscriptenMouseEvent.__size__ }}} ); // This allocated block is passed as satellite data to the proxied function call, so the call frees up the data block when done. fillMouseEventData(mouseEventData, e, target); - JSEvents.queueEventHandlerOnThread_iiii(targetThread, callbackfunc, eventTypeId, mouseEventData, userData); + JSEvents.queueEventHandlerOnThread_iipp(targetThread, callbackfunc, eventTypeId, mouseEventData, userData); } else #endif - if ({{{ makeDynCall('iiii', 'callbackfunc') }}}(eventTypeId, JSEvents.mouseEvent, userData)) e.preventDefault(); + if ({{{ makeDynCall('iipp', 'callbackfunc') }}}(eventTypeId, JSEvents.mouseEvent, userData)) e.preventDefault(); }; var eventHandler = { @@ -639,7 +645,7 @@ var LibraryHTML5 = { if (targetThread) JSEvents.queueEventHandlerOnThread_iiii(targetThread, callbackfunc, eventTypeId, wheelEvent, userData); else #endif - if ({{{ makeDynCall('iiii', 'callbackfunc') }}}(eventTypeId, wheelEvent, userData)) e.preventDefault(); + if ({{{ makeDynCall('iipp', 'callbackfunc') }}}(eventTypeId, wheelEvent, userData)) e.preventDefault(); }; #if MIN_IE_VERSION <= 8 || MIN_SAFARI_VERSION < 60100 // Browsers that do not support https://caniuse.com/#feat=mdn-api_wheelevent // The 'mousewheel' event as implemented in Safari 6.0.5 @@ -651,7 +657,7 @@ var LibraryHTML5 = { {{{ makeSetValue('JSEvents.wheelEvent', C_STRUCTS.EmscriptenWheelEvent.deltaY, 'wheelDeltaY', 'double') }}}; {{{ makeSetValue('JSEvents.wheelEvent', C_STRUCTS.EmscriptenWheelEvent.deltaZ, '0 /* Not available */', 'double') }}}; {{{ makeSetValue('JSEvents.wheelEvent', C_STRUCTS.EmscriptenWheelEvent.deltaMode, '0 /* DOM_DELTA_PIXEL */', 'i32') }}}; - var shouldCancel = {{{ makeDynCall('iiii', 'callbackfunc') }}}( eventTypeId, JSEvents.wheelEvent, userData); + var shouldCancel = {{{ makeDynCall('iipp', 'callbackfunc') }}}( eventTypeId, JSEvents.wheelEvent, userData); if (shouldCancel) { e.preventDefault(); } diff --git a/src/library_strings.js b/src/library_strings.js index 1fb4b9040b0a1..86a440d644768 100644 --- a/src/library_strings.js +++ b/src/library_strings.js @@ -157,6 +157,9 @@ mergeInto(LibraryManager.library, { #if CAN_ADDRESS_2GB outIdx >>>= 0; #endif +#if MEMORY64 + outIdx = Number(outIdx); +#endif #if ASSERTIONS assert(typeof str === 'string'); #endif diff --git a/src/library_webgl.js b/src/library_webgl.js index ebf20a9e155e7..cd21d5b9c8155 100644 --- a/src/library_webgl.js +++ b/src/library_webgl.js @@ -2,6 +2,8 @@ * @license * Copyright 2010 The Emscripten Authors * SPDX-License-Identifier: MIT + * SPDX-FileCopyrightText: Portions Copyright 2023 Siemens + * Modified on February 2023 by Siemens and/or its affiliates to improve memory64 support */ // Specifies the size of the GL temp buffer pool, in bytes. Must be a multiple @@ -376,7 +378,11 @@ var LibraryGL = { var source = ''; for (var i = 0; i < count; ++i) { var len = length ? {{{ makeGetValue('length', 'i*4', 'i32') }}} : -1; +#if MEMORY64 + source += UTF8ToString(Number({{{ makeGetValue('string', 'i*8', 'i64') }}}), len < 0 ? undefined : len); +#else source += UTF8ToString({{{ makeGetValue('string', 'i*4', 'i32') }}}, len < 0 ? undefined : len); +#endif } #if LEGACY_GL_EMULATION // Let's see if we need to enable the standard derivatives extension @@ -1426,7 +1432,7 @@ var LibraryGL = { }, glCompressedTexImage2D: function(target, level, internalFormat, width, height, border, imageSize, data) { -#if MAX_WEBGL_VERSION >= 2 +#if MAX_WEBGL_VERSION >= 2 && !MEMORY64 if ({{{ isCurrentContextWebGL2() }}}) { // WebGL 2 provides new garbage-free entry points to call to WebGL. Use those always when possible. if (GLctx.currentPixelUnpackBufferBinding || !imageSize) { GLctx.compressedTexImage2D(target, level, internalFormat, width, height, border, imageSize, data); @@ -1441,7 +1447,7 @@ var LibraryGL = { glCompressedTexSubImage2D: function(target, level, xoffset, yoffset, width, height, format, imageSize, data) { -#if MAX_WEBGL_VERSION >= 2 +#if MAX_WEBGL_VERSION >= 2 && !MEMORY64 if ({{{ isCurrentContextWebGL2() }}}) { // WebGL 2 provides new garbage-free entry points to call to WebGL. Use those always when possible. if (GLctx.currentPixelUnpackBufferBinding || !imageSize) { GLctx.compressedTexSubImage2D(target, level, xoffset, yoffset, width, height, format, imageSize, data); @@ -1504,13 +1510,12 @@ var LibraryGL = { $emscriptenWebGLGetTexPixelData: function(type, format, width, height, pixels, internalFormat) { var heap = heapObjectForWebGLType(type); var shift = heapAccessShiftForWebGLHeap(heap); - var byteSize = 1<> shift) << shift == pixels, 'Pointer to texture data passed to texture get function must be aligned to the byte size of the pixel type!'); + assert({{{ isPtrAligned('pixels', '(1 << shift)') }}}, 'Pointer to texture data passed to texture get function must be aligned to the byte size of the pixel type!'); #endif - return heap.subarray(pixels >> shift, pixels + bytes >> shift); + return heap.subarray({{{ ptrToIdx('pixels', 'shift') }}}, {{{ ptrToIdx('pixels + bytes', 'shift') }}}); }, glTexImage2D__deps: ['$emscriptenWebGLGetTexPixelData' @@ -1538,18 +1543,22 @@ var LibraryGL = { } } #endif + +#if MAX_WEBGL_VERSION >= 2 && !MEMORY64 if ({{{ isCurrentContextWebGL2() }}}) { // WebGL 2 provides new garbage-free entry points to call to WebGL. Use those always when possible. if (GLctx.currentPixelUnpackBufferBinding) { GLctx.texImage2D(target, level, internalFormat, width, height, border, format, type, pixels); } else if (pixels) { var heap = heapObjectForWebGLType(type); - GLctx.texImage2D(target, level, internalFormat, width, height, border, format, type, heap, pixels >> heapAccessShiftForWebGLHeap(heap)); + GLctx.texImage2D(target, level, internalFormat, width, height, border, format, type, heap, {{{ idxToMemory53(ptrToIdx('pixels', 'heapAccessShiftForWebGLHeap(heap)')) }}}); } else { GLctx.texImage2D(target, level, internalFormat, width, height, border, format, type, null); } return; } +#endif + #endif GLctx.texImage2D(target, level, internalFormat, width, height, border, format, type, pixels ? emscriptenWebGLGetTexPixelData(type, format, width, height, pixels, internalFormat) : null); }, @@ -1560,7 +1569,7 @@ var LibraryGL = { #endif ], glTexSubImage2D: function(target, level, xoffset, yoffset, width, height, format, type, pixels) { -#if MAX_WEBGL_VERSION >= 2 +#if MAX_WEBGL_VERSION >= 2 && !MEMORY64 #if WEBGL2_BACKWARDS_COMPATIBILITY_EMULATION if ({{{ isCurrentContextWebGL2() }}}) { // In WebGL 1 to do half float textures, one uses the type enum GL_HALF_FLOAT_OES, but in @@ -1569,18 +1578,22 @@ var LibraryGL = { if (type == 0x8d61/*GL_HALF_FLOAT_OES*/) type = 0x140B /*GL_HALF_FLOAT*/; } #endif + +#if MIN_WEBGL_VERSION >= 2 && !MEMORY64 if ({{{ isCurrentContextWebGL2() }}}) { // WebGL 2 provides new garbage-free entry points to call to WebGL. Use those always when possible. if (GLctx.currentPixelUnpackBufferBinding) { GLctx.texSubImage2D(target, level, xoffset, yoffset, width, height, format, type, pixels); } else if (pixels) { var heap = heapObjectForWebGLType(type); - GLctx.texSubImage2D(target, level, xoffset, yoffset, width, height, format, type, heap, pixels >> heapAccessShiftForWebGLHeap(heap)); + GLctx.texSubImage2D(target, level, xoffset, yoffset, width, height, format, type, heap, {{{ idxToMemory53(ptrToIdx('pixels', 'heapAccessShiftForWebGLHeap(heap)')) }}}); } else { GLctx.texSubImage2D(target, level, xoffset, yoffset, width, height, format, type, null); } return; } +#endif + #endif var pixelData = null; if (pixels) pixelData = emscriptenWebGLGetTexPixelData(type, format, width, height, pixels, 0); @@ -1593,13 +1606,13 @@ var LibraryGL = { #endif ], glReadPixels: function(x, y, width, height, format, type, pixels) { -#if MAX_WEBGL_VERSION >= 2 +#if MAX_WEBGL_VERSION >= 2 && !MEMORY64 if ({{{ isCurrentContextWebGL2() }}}) { // WebGL 2 provides new garbage-free entry points to call to WebGL. Use those always when possible. if (GLctx.currentPixelPackBufferBinding) { GLctx.readPixels(x, y, width, height, format, type, pixels); } else { var heap = heapObjectForWebGLType(type); - GLctx.readPixels(x, y, width, height, format, type, heap, pixels >> heapAccessShiftForWebGLHeap(heap)); + GLctx.readPixels(x, y, width, height, format, type, heap, {{{ idxToMemory53(ptrToIdx('pixels', 'heapAccessShiftForWebGLHeap(heap)')) }}}); } return; } @@ -1762,7 +1775,7 @@ var LibraryGL = { } #endif -#if MAX_WEBGL_VERSION >= 2 +#if MAX_WEBGL_VERSION >= 2 && !MEMORY64 if ({{{ isCurrentContextWebGL2() }}}) { // WebGL 2 provides new garbage-free entry points to call to WebGL. Use those always when possible. // If size is zero, WebGL would interpret uploading the whole input arraybuffer (starting from given offset), which would // not make sense in WebAssembly, so avoid uploading if size is zero. However we must still call bufferData to establish a @@ -1777,13 +1790,13 @@ var LibraryGL = { // N.b. here first form specifies a heap subarray, second form an integer size, so the ?: code here is polymorphic. It is advised to avoid // randomly mixing both uses in calling code, to avoid any potential JS engine JIT issues. GLctx.bufferData(target, data ? HEAPU8.subarray(data, data+size) : size, usage); -#if MAX_WEBGL_VERSION >= 2 +#if MAX_WEBGL_VERSION >= 2 && !MEMORY64 } #endif }, glBufferSubData: function(target, offset, size, data) { -#if MAX_WEBGL_VERSION >= 2 +#if MAX_WEBGL_VERSION >= 2 && !MEMORY64 if ({{{ isCurrentContextWebGL2() }}}) { // WebGL 2 provides new garbage-free entry points to call to WebGL. Use those always when possible. size && GLctx.bufferSubData(target, offset, HEAPU8, data, size); return; @@ -1793,7 +1806,7 @@ var LibraryGL = { }, // Queries EXT - glGenQueriesEXT__sig: 'vii', + glGenQueriesEXT__sig: 'vip', glGenQueriesEXT: function(n, ids) { for (var i = 0; i < n; i++) { var query = GLctx.disjointTimerQueryExt['createQueryEXT'](); @@ -1812,7 +1825,7 @@ var LibraryGL = { } }, - glDeleteQueriesEXT__sig: 'vii', + glDeleteQueriesEXT__sig: 'vip', glDeleteQueriesEXT: function(n, ids) { for (var i = 0; i < n; i++) { var id = {{{ makeGetValue('ids', 'i*4', 'i32') }}}; @@ -1854,7 +1867,7 @@ var LibraryGL = { GLctx.disjointTimerQueryExt['queryCounterEXT'](GL.queries[id], target); }, - glGetQueryivEXT__sig: 'viii', + glGetQueryivEXT__sig: 'viip', glGetQueryivEXT: function(target, pname, params) { if (!params) { // GLES2 specification does not specify how to behave if params is a null pointer. Since calling this function does not make sense @@ -1868,7 +1881,7 @@ var LibraryGL = { {{{ makeSetValue('params', '0', 'GLctx.disjointTimerQueryExt[\'getQueryEXT\'](target, pname)', 'i32') }}}; }, - glGetQueryObjectivEXT__sig: 'viii', + glGetQueryObjectivEXT__sig: 'viip', glGetQueryObjectivEXT: function(id, pname, params) { if (!params) { // GLES2 specification does not specify how to behave if params is a null pointer. Since calling this function does not make sense @@ -1894,7 +1907,7 @@ var LibraryGL = { }, glGetQueryObjectuivEXT: 'glGetQueryObjectivEXT', - glGetQueryObjecti64vEXT__sig: 'viii', + glGetQueryObjecti64vEXT__sig: 'viip', glGetQueryObjecti64vEXT__deps: ['$writeI53ToI64'], glGetQueryObjecti64vEXT: function(id, pname, params) { if (!params) { @@ -2247,7 +2260,12 @@ var LibraryGL = { err("glGetVertexAttribPointer on client-side array: not supported, bad data returned"); } #endif + +#if MEMORY64 + {{{ makeSetValue('pointer', '0', 'GLctx.getVertexAttribOffset(index, pname)', 'i64') }}}; +#else {{{ makeSetValue('pointer', '0', 'GLctx.getVertexAttribOffset(index, pname)', 'i32') }}}; +#endif }, glUniform1f__deps: ['$webglGetUniformLocation'], @@ -2322,19 +2340,19 @@ var LibraryGL = { glUniform1iv: function(location, count, value) { #if GL_ASSERTIONS GL.validateGLObjectID(GLctx.currentProgram.uniformLocsById, location, 'glUniform1iv', 'location'); - assert((value & 3) == 0, 'Pointer to integer data passed to glUniform1iv must be aligned to four bytes!'); + assert({{{ isPtrAligned('value', 4) }}}, 'Pointer to integer data passed to glUniform1iv must be aligned to four bytes!'); #endif #if MIN_WEBGL_VERSION >= 2 #if GL_ASSERTIONS assert(GL.currentContext.version >= 2); #endif - count && GLctx.uniform1iv(webglGetUniformLocation(location), HEAP32, value>>2, count); + GLctx.uniform1iv(webglGetUniformLocation(location), HEAP32, {{{ idxToMemory53(ptrToIdx('value', 2)) }}}, count); #else #if MAX_WEBGL_VERSION >= 2 if ({{{ isCurrentContextWebGL2() }}}) { // WebGL 2 provides new garbage-free entry points to call to WebGL. Use those always when possible. - count && GLctx.uniform1iv(webglGetUniformLocation(location), HEAP32, value>>2, count); + GLctx.uniform1iv(webglGetUniformLocation(location), HEAP32, {{{ idxToMemory53(ptrToIdx('value', 2)) }}}, count); return; } #endif @@ -2366,19 +2384,19 @@ var LibraryGL = { glUniform2iv: function(location, count, value) { #if GL_ASSERTIONS GL.validateGLObjectID(GLctx.currentProgram.uniformLocsById, location, 'glUniform2iv', 'location'); - assert((value & 3) == 0, 'Pointer to integer data passed to glUniform2iv must be aligned to four bytes!'); + assert({{{ isPtrAligned('value', 4) }}}, 'Pointer to integer data passed to glUniform2iv must be aligned to four bytes!'); #endif #if MIN_WEBGL_VERSION >= 2 #if GL_ASSERTIONS assert(GL.currentContext.version >= 2); #endif - count && GLctx.uniform2iv(webglGetUniformLocation(location), HEAP32, value>>2, count*2); + GLctx.uniform2iv(webglGetUniformLocation(location), HEAP32, {{{ idxToMemory53(ptrToIdx('value', 2)) }}}, count*2); #else #if MAX_WEBGL_VERSION >= 2 if ({{{ isCurrentContextWebGL2() }}}) { // WebGL 2 provides new garbage-free entry points to call to WebGL. Use those always when possible. - count && GLctx.uniform2iv(webglGetUniformLocation(location), HEAP32, value>>2, count*2); + GLctx.uniform2iv(webglGetUniformLocation(location), HEAP32, {{{ idxToMemory53(ptrToIdx('value', 2)) }}}, count*2); return; } #endif @@ -2411,19 +2429,19 @@ var LibraryGL = { glUniform3iv: function(location, count, value) { #if GL_ASSERTIONS GL.validateGLObjectID(GLctx.currentProgram.uniformLocsById, location, 'glUniform3iv', 'location'); - assert((value & 3) == 0, 'Pointer to integer data passed to glUniform3iv must be aligned to four bytes!'); + assert({{{ isPtrAligned('value', 4) }}}, 'Pointer to integer data passed to glUniform3iv must be aligned to four bytes!'); #endif #if MIN_WEBGL_VERSION >= 2 #if GL_ASSERTIONS assert(GL.currentContext.version >= 2); #endif - count && GLctx.uniform3iv(webglGetUniformLocation(location), HEAP32, value>>2, count*3); + GLctx.uniform3iv(webglGetUniformLocation(location), HEAP32, {{{ idxToMemory53(ptrToIdx('value', 2)) }}}, count*3); #else #if MAX_WEBGL_VERSION >= 2 if ({{{ isCurrentContextWebGL2() }}}) { // WebGL 2 provides new garbage-free entry points to call to WebGL. Use those always when possible. - count && GLctx.uniform3iv(webglGetUniformLocation(location), HEAP32, value>>2, count*3); + GLctx.uniform3iv(webglGetUniformLocation(location), HEAP32, {{{ idxToMemory53(ptrToIdx('value', 2)) }}}, count*3); return; } #endif @@ -2457,24 +2475,24 @@ var LibraryGL = { glUniform4iv: function(location, count, value) { #if GL_ASSERTIONS GL.validateGLObjectID(GLctx.currentProgram.uniformLocsById, location, 'glUniform4iv', 'location'); - assert((value & 3) == 0, 'Pointer to integer data passed to glUniform4iv must be aligned to four bytes!'); + assert({{{ isPtrAligned('value', 4) }}}, 'Pointer to integer data passed to glUniform4iv must be aligned to four bytes!'); #endif -#if MIN_WEBGL_VERSION >= 2 +#if MIN_WEBGL_VERSION >= 2 && !MEMORY64 #if GL_ASSERTIONS assert(GL.currentContext.version >= 2); #endif - count && GLctx.uniform4iv(webglGetUniformLocation(location), HEAP32, value>>2, count*4); + GLctx.uniform4iv(webglGetUniformLocation(location), HEAP32, {{{ idxToMemory53(ptrToIdx('value', 2)) }}}, count*4); #else -#if MAX_WEBGL_VERSION >= 2 +#if MAX_WEBGL_VERSION >= 2 && !MEMORY64 if ({{{ isCurrentContextWebGL2() }}}) { // WebGL 2 provides new garbage-free entry points to call to WebGL. Use those always when possible. - count && GLctx.uniform4iv(webglGetUniformLocation(location), HEAP32, value>>2, count*4); + GLctx.uniform4iv(webglGetUniformLocation(location), HEAP32, {{{ idxToMemory53(ptrToIdx('value', 2)) }}}, count*4); return; } #endif -#if GL_POOL_TEMP_BUFFERS +#if GL_POOL_TEMP_BUFFERS && !MEMORY64 if (count <= {{{ GL_POOL_TEMP_BUFFERS_SIZE / 4 }}}) { // avoid allocation when uploading few enough uniforms var view = miniTempWebGLIntBuffers[4*count-1]; @@ -2504,24 +2522,24 @@ var LibraryGL = { glUniform1fv: function(location, count, value) { #if GL_ASSERTIONS GL.validateGLObjectID(GLctx.currentProgram.uniformLocsById, location, 'glUniform1fv', 'location'); - assert((value & 3) == 0, 'Pointer to float data passed to glUniform1fv must be aligned to four bytes!'); + assert({{{ isPtrAligned('value', 4) }}}, 'Pointer to float data passed to glUniform1fv must be aligned to four bytes!'); #endif -#if MIN_WEBGL_VERSION >= 2 +#if MIN_WEBGL_VERSION >= 2 && !MEMORY64 #if GL_ASSERTIONS assert(GL.currentContext.version >= 2); #endif - count && GLctx.uniform1fv(webglGetUniformLocation(location), HEAPF32, value>>2, count); + GLctx.uniform1fv(webglGetUniformLocation(location), HEAPF32, {{{ idxToMemory53(ptrToIdx('value', 2)) }}}, count); #else -#if MAX_WEBGL_VERSION >= 2 +#if MAX_WEBGL_VERSION >= 2 && !MEMORY64 if ({{{ isCurrentContextWebGL2() }}}) { // WebGL 2 provides new garbage-free entry points to call to WebGL. Use those always when possible. - count && GLctx.uniform1fv(webglGetUniformLocation(location), HEAPF32, value>>2, count); + GLctx.uniform1fv(webglGetUniformLocation(location), HEAPF32, {{{ idxToMemory53(ptrToIdx('value', 2)) }}}, count); return; } #endif -#if GL_POOL_TEMP_BUFFERS +#if GL_POOL_TEMP_BUFFERS && !MEMORY64 if (count <= {{{ GL_POOL_TEMP_BUFFERS_SIZE }}}) { // avoid allocation when uploading few enough uniforms var view = miniTempWebGLFloatBuffers[count-1]; @@ -2548,24 +2566,24 @@ var LibraryGL = { glUniform2fv: function(location, count, value) { #if GL_ASSERTIONS GL.validateGLObjectID(GLctx.currentProgram.uniformLocsById, location, 'glUniform2fv', 'location'); - assert((value & 3) == 0, 'Pointer to float data passed to glUniform2fv must be aligned to four bytes!'); + assert({{{ isPtrAligned('value', 4) }}}, 'Pointer to float data passed to glUniform2fv must be aligned to four bytes!'); #endif -#if MIN_WEBGL_VERSION >= 2 +#if MIN_WEBGL_VERSION >= 2 && !MEMORY64 #if GL_ASSERTIONS assert(GL.currentContext.version >= 2); #endif - count && GLctx.uniform2fv(webglGetUniformLocation(location), HEAPF32, value>>2, count*2); + GLctx.uniform2fv(webglGetUniformLocation(location), HEAPF32, {{{ idxToMemory53(ptrToIdx('value', 2)) }}}, count*2); #else -#if MAX_WEBGL_VERSION >= 2 +#if MAX_WEBGL_VERSION >= 2 && !MEMORY64 if ({{{ isCurrentContextWebGL2() }}}) { // WebGL 2 provides new garbage-free entry points to call to WebGL. Use those always when possible. - count && GLctx.uniform2fv(webglGetUniformLocation(location), HEAPF32, value>>2, count*2); + GLctx.uniform2fv(webglGetUniformLocation(location), HEAPF32, {{{ idxToMemory53(ptrToIdx('value', 2)) }}}, count*2); return; } #endif -#if GL_POOL_TEMP_BUFFERS +#if GL_POOL_TEMP_BUFFERS && !MEMORY64 if (count <= {{{ GL_POOL_TEMP_BUFFERS_SIZE / 2 }}}) { // avoid allocation when uploading few enough uniforms var view = miniTempWebGLFloatBuffers[2*count-1]; @@ -2593,24 +2611,24 @@ var LibraryGL = { glUniform3fv: function(location, count, value) { #if GL_ASSERTIONS GL.validateGLObjectID(GLctx.currentProgram.uniformLocsById, location, 'glUniform3fv', 'location'); - assert((value & 3) == 0, 'Pointer to float data passed to glUniform3fv must be aligned to four bytes!' + value); + assert({{{ isPtrAligned('value', 4) }}}, 'Pointer to float data passed to glUniform3fv must be aligned to four bytes!'); #endif -#if MIN_WEBGL_VERSION >= 2 +#if MIN_WEBGL_VERSION >= 2 && !MEMORY64 #if GL_ASSERTIONS assert(GL.currentContext.version >= 2); #endif - count && GLctx.uniform3fv(webglGetUniformLocation(location), HEAPF32, value>>2, count*3); + GLctx.uniform3fv(webglGetUniformLocation(location), HEAPF32, {{{ idxToMemory53(ptrToIdx('value', 2)) }}}, count*3); #else -#if MAX_WEBGL_VERSION >= 2 +#if MAX_WEBGL_VERSION >= 2 && !MEMORY64 if ({{{ isCurrentContextWebGL2() }}}) { // WebGL 2 provides new garbage-free entry points to call to WebGL. Use those always when possible. - count && GLctx.uniform3fv(webglGetUniformLocation(location), HEAPF32, value>>2, count*3); + GLctx.uniform3fv(webglGetUniformLocation(location), HEAPF32, {{{ idxToMemory53(ptrToIdx('value', 2)) }}}, count*3); return; } #endif -#if GL_POOL_TEMP_BUFFERS +#if GL_POOL_TEMP_BUFFERS && !MEMORY64 if (count <= {{{ GL_POOL_TEMP_BUFFERS_SIZE / 3 }}}) { // avoid allocation when uploading few enough uniforms var view = miniTempWebGLFloatBuffers[3*count-1]; @@ -2639,36 +2657,35 @@ var LibraryGL = { glUniform4fv: function(location, count, value) { #if GL_ASSERTIONS GL.validateGLObjectID(GLctx.currentProgram.uniformLocsById, location, 'glUniform4fv', 'location'); - assert((value & 3) == 0, 'Pointer to float data passed to glUniform4fv must be aligned to four bytes!'); + assert({{{ isPtrAligned('value', 4) }}}, 'Pointer to float data passed to glUniform4fv must be aligned to four bytes!'); #endif -#if MIN_WEBGL_VERSION >= 2 +#if MIN_WEBGL_VERSION >= 2 && !MEMORY64 #if GL_ASSERTIONS assert(GL.currentContext.version >= 2); #endif - count && GLctx.uniform4fv(webglGetUniformLocation(location), HEAPF32, value>>2, count*4); + GLctx.uniform4fv(webglGetUniformLocation(location), HEAPF32, {{{ idxToMemory53(ptrToIdx('value', 2)) }}}, count*4); #else -#if MAX_WEBGL_VERSION >= 2 +#if MAX_WEBGL_VERSION >= 2 && !MEMORY64 if ({{{ isCurrentContextWebGL2() }}}) { // WebGL 2 provides new garbage-free entry points to call to WebGL. Use those always when possible. - count && GLctx.uniform4fv(webglGetUniformLocation(location), HEAPF32, value>>2, count*4); + GLctx.uniform4fv(webglGetUniformLocation(location), HEAPF32, {{{ idxToMemory53(ptrToIdx('value', 2)) }}}, count*4); return; } #endif -#if GL_POOL_TEMP_BUFFERS +#if GL_POOL_TEMP_BUFFERS && !MEMORY64 if (count <= {{{ GL_POOL_TEMP_BUFFERS_SIZE / 4 }}}) { // avoid allocation when uploading few enough uniforms var view = miniTempWebGLFloatBuffers[4*count-1]; - // hoist the heap out of the loop for size and for pthreads+growth. + // hoist the heap out of the loop for pthreads+growth. var heap = HEAPF32; - value >>= 2; + {{{ convertPtrToIdx('value', 2) }}}; for (var i = 0; i < 4 * count; i += 4) { - var dst = value + i; - view[i] = heap[dst]; - view[i + 1] = heap[dst + 1]; - view[i + 2] = heap[dst + 2]; - view[i + 3] = heap[dst + 3]; + view[i] = heap[value++]; + view[i + 1] = heap[value++]; + view[i + 2] = heap[value++]; + view[i + 3] = heap[value++]; } } else #endif @@ -2690,24 +2707,24 @@ var LibraryGL = { glUniformMatrix2fv: function(location, count, transpose, value) { #if GL_ASSERTIONS GL.validateGLObjectID(GLctx.currentProgram.uniformLocsById, location, 'glUniformMatrix2fv', 'location'); - assert((value & 3) == 0, 'Pointer to float data passed to glUniformMatrix2fv must be aligned to four bytes!'); + assert({{{ isPtrAligned('value', 4) }}}, 'Pointer to float data passed to glUniformMatrix2fv must be aligned to four bytes!'); #endif -#if MIN_WEBGL_VERSION >= 2 +#if MIN_WEBGL_VERSION >= 2 && !MEMORY64 #if GL_ASSERTIONS assert(GL.currentContext.version >= 2); #endif - count && GLctx.uniformMatrix2fv(webglGetUniformLocation(location), !!transpose, HEAPF32, value>>2, count*4); + GLctx.uniformMatrix2fv(webglGetUniformLocation(location), !!transpose, HEAPF32, {{{ idxToMemory53(ptrToIdx('value', 2)) }}}, count*4); #else -#if MAX_WEBGL_VERSION >= 2 +#if MAX_WEBGL_VERSION >= 2 && !MEMORY64 if ({{{ isCurrentContextWebGL2() }}}) { // WebGL 2 provides new garbage-free entry points to call to WebGL. Use those always when possible. - count && GLctx.uniformMatrix2fv(webglGetUniformLocation(location), !!transpose, HEAPF32, value>>2, count*4); + GLctx.uniformMatrix2fv(webglGetUniformLocation(location), !!transpose, HEAPF32, {{{ idxToMemory53(ptrToIdx('value', 2)) }}}, count*4); return; } #endif -#if GL_POOL_TEMP_BUFFERS +#if GL_POOL_TEMP_BUFFERS && !MEMORY64 if (count <= {{{ GL_POOL_TEMP_BUFFERS_SIZE / 4 }}}) { // avoid allocation when uploading few enough uniforms var view = miniTempWebGLFloatBuffers[4*count-1]; @@ -2737,24 +2754,24 @@ var LibraryGL = { glUniformMatrix3fv: function(location, count, transpose, value) { #if GL_ASSERTIONS GL.validateGLObjectID(GLctx.currentProgram.uniformLocsById, location, 'glUniformMatrix3fv', 'location'); - assert((value & 3) == 0, 'Pointer to float data passed to glUniformMatrix3fv must be aligned to four bytes!'); + assert({{{ isPtrAligned('value', 4) }}}, 'Pointer to float data passed to glUniformMatrix3fv must be aligned to four bytes!'); #endif -#if MIN_WEBGL_VERSION >= 2 +#if MIN_WEBGL_VERSION >= 2 && !MEMORY64 #if GL_ASSERTIONS assert(GL.currentContext.version >= 2); #endif - count && GLctx.uniformMatrix3fv(webglGetUniformLocation(location), !!transpose, HEAPF32, value>>2, count*9); + GLctx.uniformMatrix3fv(webglGetUniformLocation(location), !!transpose, HEAPF32, {{{ idxToMemory53(ptrToIdx('value', 2)) }}}, count*9); #else -#if MAX_WEBGL_VERSION >= 2 +#if MAX_WEBGL_VERSION >= 2 && !MEMORY64 if ({{{ isCurrentContextWebGL2() }}}) { // WebGL 2 provides new garbage-free entry points to call to WebGL. Use those always when possible. - count && GLctx.uniformMatrix3fv(webglGetUniformLocation(location), !!transpose, HEAPF32, value>>2, count*9); + GLctx.uniformMatrix3fv(webglGetUniformLocation(location), !!transpose, HEAPF32, {{{ idxToMemory53(ptrToIdx('value', 2)) }}}, count*9); return; } #endif -#if GL_POOL_TEMP_BUFFERS +#if GL_POOL_TEMP_BUFFERS && !MEMORY64 if (count <= {{{ GL_POOL_TEMP_BUFFERS_SIZE / 9 }}}) { // avoid allocation when uploading few enough uniforms var view = miniTempWebGLFloatBuffers[9*count-1]; @@ -2789,48 +2806,47 @@ var LibraryGL = { glUniformMatrix4fv: function(location, count, transpose, value) { #if GL_ASSERTIONS GL.validateGLObjectID(GLctx.currentProgram.uniformLocsById, location, 'glUniformMatrix4fv', 'location'); - assert((value & 3) == 0, 'Pointer to float data passed to glUniformMatrix4fv must be aligned to four bytes!'); + assert({{{ isPtrAligned('value', 4) }}}, 'Pointer to float data passed to glUniformMatrix4fv must be aligned to four bytes!'); #endif -#if MIN_WEBGL_VERSION >= 2 +#if MIN_WEBGL_VERSION >= 2 && !MEMORY64 #if GL_ASSERTIONS assert(GL.currentContext.version >= 2); #endif - count && GLctx.uniformMatrix4fv(webglGetUniformLocation(location), !!transpose, HEAPF32, value>>2, count*16); + GLctx.uniformMatrix4fv(webglGetUniformLocation(location), !!transpose, HEAPF32, {{{ idxToMemory53(ptrToIdx('value', 2)) }}}, count*16); #else -#if MAX_WEBGL_VERSION >= 2 +#if MAX_WEBGL_VERSION >= 2 && !MEMORY64 if ({{{ isCurrentContextWebGL2() }}}) { // WebGL 2 provides new garbage-free entry points to call to WebGL. Use those always when possible. - count && GLctx.uniformMatrix4fv(webglGetUniformLocation(location), !!transpose, HEAPF32, value>>2, count*16); + GLctx.uniformMatrix4fv(webglGetUniformLocation(location), !!transpose, HEAPF32, {{{ idxToMemory53(ptrToIdx('value', 2)) }}}, count*16); return; } #endif -#if GL_POOL_TEMP_BUFFERS +#if GL_POOL_TEMP_BUFFERS && !MEMORY64 if (count <= {{{ GL_POOL_TEMP_BUFFERS_SIZE / 16 }}}) { // avoid allocation when uploading few enough uniforms var view = miniTempWebGLFloatBuffers[16*count-1]; - // hoist the heap out of the loop for size and for pthreads+growth. + // hoist the heap out of the loop for pthreads+growth. var heap = HEAPF32; - value >>= 2; + {{{ convertPtrToIdx('value', 2) }}}; for (var i = 0; i < 16 * count; i += 16) { - var dst = value + i; - view[i] = heap[dst]; - view[i + 1] = heap[dst + 1]; - view[i + 2] = heap[dst + 2]; - view[i + 3] = heap[dst + 3]; - view[i + 4] = heap[dst + 4]; - view[i + 5] = heap[dst + 5]; - view[i + 6] = heap[dst + 6]; - view[i + 7] = heap[dst + 7]; - view[i + 8] = heap[dst + 8]; - view[i + 9] = heap[dst + 9]; - view[i + 10] = heap[dst + 10]; - view[i + 11] = heap[dst + 11]; - view[i + 12] = heap[dst + 12]; - view[i + 13] = heap[dst + 13]; - view[i + 14] = heap[dst + 14]; - view[i + 15] = heap[dst + 15]; + view[i] = heap[value++]; + view[i + 1] = heap[value++]; + view[i + 2] = heap[value++]; + view[i + 3] = heap[value++]; + view[i + 4] = heap[value++]; + view[i + 5] = heap[value++]; + view[i + 6] = heap[value++]; + view[i + 7] = heap[value++]; + view[i + 8] = heap[value++]; + view[i + 9] = heap[value++]; + view[i + 10] = heap[value++]; + view[i + 11] = heap[value++]; + view[i + 12] = heap[value++]; + view[i + 13] = heap[value++]; + view[i + 14] = heap[value++]; + view[i + 15] = heap[value++]; } } else #endif @@ -2879,38 +2895,40 @@ var LibraryGL = { glVertexAttrib1fv: function(index, v) { #if GL_ASSERTIONS - assert((v & 3) == 0, 'Pointer to float data passed to glVertexAttrib1fv must be aligned to four bytes!'); + assert({{{ isPtrAligned('v', 4) }}}, 'Pointer to float data passed to glVertexAttrib1fv must be aligned to four bytes!'); assert(v != 0, 'Null pointer passed to glVertexAttrib1fv!'); #endif - GLctx.vertexAttrib1f(index, HEAPF32[v>>2]); + GLctx.vertexAttrib1f(index, HEAPF32[{{{ ptrToIdx('v', 2) }}}]); }, glVertexAttrib2fv: function(index, v) { #if GL_ASSERTIONS - assert((v & 3) == 0, 'Pointer to float data passed to glVertexAttrib2fv must be aligned to four bytes!'); + assert({{{ isPtrAligned('v', 4) }}}, 'Pointer to float data passed to glVertexAttrib2fv must be aligned to four bytes!'); assert(v != 0, 'Null pointer passed to glVertexAttrib2fv!'); #endif - GLctx.vertexAttrib2f(index, HEAPF32[v>>2], HEAPF32[v+4>>2]); + {{{ convertPtrToIdx('v', 2) }}}; + GLctx.vertexAttrib2f(index, HEAPF32[v++], HEAPF32[v]); }, glVertexAttrib3fv: function(index, v) { #if GL_ASSERTIONS - assert((v & 3) == 0, 'Pointer to float data passed to glVertexAttrib3fv must be aligned to four bytes!'); + assert({{{ isPtrAligned('v', 4) }}}, 'Pointer to float data passed to glVertexAttrib3fv must be aligned to four bytes!'); assert(v != 0, 'Null pointer passed to glVertexAttrib3fv!'); #endif - GLctx.vertexAttrib3f(index, HEAPF32[v>>2], HEAPF32[v+4>>2], HEAPF32[v+8>>2]); + {{{ convertPtrToIdx('v', 2) }}}; + GLctx.vertexAttrib3f(index, HEAPF32[v++], HEAPF32[v++], HEAPF32[v]); }, glVertexAttrib4fv: function(index, v) { #if GL_ASSERTIONS - assert((v & 3) == 0, 'Pointer to float data passed to glVertexAttrib4fv must be aligned to four bytes!'); assert(v != 0, 'Null pointer passed to glVertexAttrib4fv!'); #endif - GLctx.vertexAttrib4f(index, HEAPF32[v>>2], HEAPF32[v+4>>2], HEAPF32[v+8>>2], HEAPF32[v+12>>2]); + {{{ convertPtrToIdx('v', 2) }}}; + GLctx.vertexAttrib4f(index, HEAPF32[v++], HEAPF32[v++], HEAPF32[v++], HEAPF32[v]); }, glGetAttribLocation: function(program, name) { @@ -3848,9 +3866,9 @@ var LibraryGL = { GLctx.multiDrawWebgl['multiDrawArraysWEBGL']( mode, HEAP32, - firsts >> 2, + {{{ idxToMemory53(ptrToIdx('firsts', 2)) }}}, HEAP32, - counts >> 2, + {{{ idxToMemory53(ptrToIdx('counts', 2)) }}}, drawcount); }, @@ -3860,11 +3878,11 @@ var LibraryGL = { GLctx.multiDrawWebgl['multiDrawArraysInstancedWEBGL']( mode, HEAP32, - firsts >> 2, + {{{ idxToMemory53(ptrToIdx('firsts', 2)) }}}, HEAP32, - counts >> 2, + {{{ idxToMemory53(ptrToIdx('counts', 2)) }}}, HEAP32, - instanceCounts >> 2, + {{{ idxToMemory53(ptrToIdx('instanceCounts', 2)) }}}, drawcount); }, @@ -3875,25 +3893,25 @@ var LibraryGL = { GLctx.multiDrawWebgl['multiDrawElementsWEBGL']( mode, HEAP32, - counts >> 2, + {{{ idxToMemory53(ptrToIdx('counts', 2)) }}}, type, HEAP32, - offsets >> 2, + {{{ idxToMemory53(ptrToIdx('offsets', 2)) }}}, drawcount); }, - glMultiDrawElementsInstancedWEBGL__sig: 'viiiiii', + glMultiDrawElementsInstancedWEBGL__sig: 'vipippi', glMultiDrawElementsInstancedANGLE: 'glMultiDrawElementsInstancedWEBGL', glMultiDrawElementsInstancedWEBGL: function(mode, counts, type, offsets, instanceCounts, drawcount) { GLctx.multiDrawWebgl['multiDrawElementsInstancedWEBGL']( mode, HEAP32, - counts >> 2, + {{{ idxToMemory53(ptrToIdx('counts', 2)) }}}, type, HEAP32, - offsets >> 2, + {{{ idxToMemory53(ptrToIdx('offsets', 2)) }}}, HEAP32, - instanceCounts >> 2, + {{{ idxToMemory53(ptrToIdx('instanceCounts', 2)) }}}, drawcount); }, diff --git a/src/library_webgl2.js b/src/library_webgl2.js index 060393a1c7d72..15805dc409085 100644 --- a/src/library_webgl2.js +++ b/src/library_webgl2.js @@ -2,6 +2,8 @@ * @license * Copyright 2010 The Emscripten Authors * SPDX-License-Identifier: MIT + * SPDX-FileCopyrightText: Portions Copyright 2023 Siemens + * Modified on February 2023 by Siemens and/or its affiliates to improve memory64 support */ var LibraryWebGL2 = { @@ -158,7 +160,7 @@ var LibraryWebGL2 = { GLctx.texImage3D(target, level, internalFormat, width, height, depth, border, format, type, pixels); } else if (pixels) { var heap = heapObjectForWebGLType(type); - GLctx.texImage3D(target, level, internalFormat, width, height, depth, border, format, type, heap, pixels >> heapAccessShiftForWebGLHeap(heap)); + GLctx.texImage3D(target, level, internalFormat, width, height, depth, border, format, type, heap, {{{ ptrToIdx('pixels', 'heapAccessShiftForWebGLHeap(heap)') }}}); } else { GLctx.texImage3D(target, level, internalFormat, width, height, depth, border, format, type, null); } @@ -170,7 +172,7 @@ var LibraryWebGL2 = { GLctx.texSubImage3D(target, level, xoffset, yoffset, zoffset, width, height, depth, format, type, pixels); } else if (pixels) { var heap = heapObjectForWebGLType(type); - GLctx.texSubImage3D(target, level, xoffset, yoffset, zoffset, width, height, depth, format, type, heap, pixels >> heapAccessShiftForWebGLHeap(heap)); + GLctx.texSubImage3D(target, level, xoffset, yoffset, zoffset, width, height, depth, format, type, heap, {{{ ptrToIdx('pixels', 'heapAccessShiftForWebGLHeap(heap)') }}}); } else { GLctx.texSubImage3D(target, level, xoffset, yoffset, zoffset, width, height, depth, format, type, null); } @@ -634,7 +636,7 @@ var LibraryWebGL2 = { assert((value & 3) == 0, 'Pointer to integer data passed to glClearBufferiv must be aligned to four bytes!'); #endif - GLctx.clearBufferiv(buffer, drawbuffer, HEAP32, value>>2); + GLctx.clearBufferiv(buffer, drawbuffer, HEAP32, {{{ ptrToIdx('value', 2) }}}); }, glClearBufferuiv: function(buffer, drawbuffer, value) { @@ -642,7 +644,7 @@ var LibraryWebGL2 = { assert((value & 3) == 0, 'Pointer to integer data passed to glClearBufferuiv must be aligned to four bytes!'); #endif - GLctx.clearBufferuiv(buffer, drawbuffer, HEAPU32, value>>2); + GLctx.clearBufferuiv(buffer, drawbuffer, {{{ ptrToIdx('value', 2) }}}); }, glClearBufferfv: function(buffer, drawbuffer, value) { @@ -650,7 +652,7 @@ var LibraryWebGL2 = { assert((value & 3) == 0, 'Pointer to float data passed to glClearBufferfv must be aligned to four bytes!'); #endif - GLctx.clearBufferfv(buffer, drawbuffer, HEAPF32, value>>2); + GLctx.clearBufferfv(buffer, drawbuffer, HEAPF32, {{{ ptrToIdx('value', 2) }}}); }, glFenceSync: function(condition, flags) { @@ -791,7 +793,7 @@ var LibraryWebGL2 = { GL.validateGLObjectID(GLctx.currentProgram.uniformLocsById, location, 'glUniform1uiv', 'location'); assert((value & 3) == 0, 'Pointer to integer data passed to glUniform1uiv must be aligned to four bytes!'); #endif - count && GLctx.uniform1uiv(webglGetUniformLocation(location), HEAPU32, value>>2, count); + GLctx.uniform1uiv(webglGetUniformLocation(location), HEAPU32, {{{ ptrToIdx('value', 2) }}}, count); }, glUniform2uiv__deps: ['$webglGetUniformLocation'], @@ -800,7 +802,7 @@ var LibraryWebGL2 = { GL.validateGLObjectID(GLctx.currentProgram.uniformLocsById, location, 'glUniform2uiv', 'location'); assert((value & 3) == 0, 'Pointer to integer data passed to glUniform2uiv must be aligned to four bytes!'); #endif - count && GLctx.uniform2uiv(webglGetUniformLocation(location), HEAPU32, value>>2, count*2); + GLctx.uniform2uiv(webglGetUniformLocation(location), HEAPU32, {{{ ptrToIdx('value', 2) }}}, count*2); }, glUniform3uiv__deps: ['$webglGetUniformLocation'], @@ -809,7 +811,7 @@ var LibraryWebGL2 = { GL.validateGLObjectID(GLctx.currentProgram.uniformLocsById, location, 'glUniform3uiv', 'location'); assert((value & 3) == 0, 'Pointer to integer data passed to glUniform3uiv must be aligned to four bytes!'); #endif - count && GLctx.uniform3uiv(webglGetUniformLocation(location), HEAPU32, value>>2, count*3); + GLctx.uniform3uiv(webglGetUniformLocation(location), HEAPU32, {{{ ptrToIdx('value', 2) }}}, count*3); }, glUniform4uiv__deps: ['$webglGetUniformLocation'], @@ -818,7 +820,7 @@ var LibraryWebGL2 = { GL.validateGLObjectID(GLctx.currentProgram.uniformLocsById, location, 'glUniform4uiv', 'location'); assert((value & 3) == 0, 'Pointer to integer data passed to glUniform4uiv must be aligned to four bytes!'); #endif - count && GLctx.uniform4uiv(webglGetUniformLocation(location), HEAPU32, value>>2, count*4); + GLctx.uniform4uiv(webglGetUniformLocation(location), HEAPU32, {{{ ptrToIdx('value', 2) }}}, count*4); }, glUniformMatrix2x3fv__deps: ['$webglGetUniformLocation'], @@ -827,7 +829,7 @@ var LibraryWebGL2 = { GL.validateGLObjectID(GLctx.currentProgram.uniformLocsById, location, 'glUniformMatrix2x3fv', 'location'); assert((value & 3) == 0, 'Pointer to float data passed to glUniformMatrix2x3fv must be aligned to four bytes!'); #endif - count && GLctx.uniformMatrix2x3fv(webglGetUniformLocation(location), !!transpose, HEAPF32, value>>2, count*6); + GLctx.uniformMatrix2x3fv(webglGetUniformLocation(location), !!transpose, HEAPF32, {{{ ptrToIdx('value', 2) }}}, count*6); }, glUniformMatrix3x2fv__deps: ['$webglGetUniformLocation'], @@ -836,7 +838,7 @@ var LibraryWebGL2 = { GL.validateGLObjectID(GLctx.currentProgram.uniformLocsById, location, 'glUniformMatrix3x2fv', 'location'); assert((value & 3) == 0, 'Pointer to float data passed to glUniformMatrix3x2fv must be aligned to four bytes!'); #endif - count && GLctx.uniformMatrix3x2fv(webglGetUniformLocation(location), !!transpose, HEAPF32, value>>2, count*6); + GLctx.uniformMatrix3x2fv(webglGetUniformLocation(location), !!transpose, HEAPF32, {{{ ptrToIdx('value', 2) }}}, count*6); }, glUniformMatrix2x4fv__deps: ['$webglGetUniformLocation'], @@ -845,7 +847,7 @@ var LibraryWebGL2 = { GL.validateGLObjectID(GLctx.currentProgram.uniformLocsById, location, 'glUniformMatrix2x4fv', 'location'); assert((value & 3) == 0, 'Pointer to float data passed to glUniformMatrix2x4fv must be aligned to four bytes!'); #endif - count && GLctx.uniformMatrix2x4fv(webglGetUniformLocation(location), !!transpose, HEAPF32, value>>2, count*8); + GLctx.uniformMatrix2x4fv(webglGetUniformLocation(location), !!transpose, HEAPF32, {{{ ptrToIdx('value', 2) }}}, count*8); }, glUniformMatrix4x2fv__deps: ['$webglGetUniformLocation'], @@ -854,7 +856,7 @@ var LibraryWebGL2 = { GL.validateGLObjectID(GLctx.currentProgram.uniformLocsById, location, 'glUniformMatrix4x2fv', 'location'); assert((value & 3) == 0, 'Pointer to float data passed to glUniformMatrix4x2fv must be aligned to four bytes!'); #endif - count && GLctx.uniformMatrix4x2fv(webglGetUniformLocation(location), !!transpose, HEAPF32, value>>2, count*8); + GLctx.uniformMatrix4x2fv(webglGetUniformLocation(location), !!transpose, HEAPF32, {{{ ptrToIdx('value', 2) }}}, count*8); }, glUniformMatrix3x4fv__deps: ['$webglGetUniformLocation'], @@ -863,7 +865,7 @@ var LibraryWebGL2 = { GL.validateGLObjectID(GLctx.currentProgram.uniformLocsById, location, 'glUniformMatrix3x4fv', 'location'); assert((value & 3) == 0, 'Pointer to float data passed to glUniformMatrix3x4fv must be aligned to four bytes!'); #endif - count && GLctx.uniformMatrix3x4fv(webglGetUniformLocation(location), !!transpose, HEAPF32, value>>2, count*12); + GLctx.uniformMatrix3x4fv(webglGetUniformLocation(location), !!transpose, HEAPF32, {{{ ptrToIdx('value', 2) }}}, count*12); }, glUniformMatrix4x3fv__deps: ['$webglGetUniformLocation'], @@ -872,7 +874,7 @@ var LibraryWebGL2 = { GL.validateGLObjectID(GLctx.currentProgram.uniformLocsById, location, 'glUniformMatrix4x3fv', 'location'); assert((value & 3) == 0, 'Pointer to float data passed to glUniformMatrix4x3fv must be aligned to four bytes!'); #endif - count && GLctx.uniformMatrix4x3fv(webglGetUniformLocation(location), !!transpose, HEAPF32, value>>2, count*12); + GLctx.uniformMatrix4x3fv(webglGetUniformLocation(location), !!transpose, HEAPF32, {{{ ptrToIdx('value', 2) }}}, count*12); }, glVertexAttribI4iv: function(index, v) { @@ -880,7 +882,8 @@ var LibraryWebGL2 = { assert((v & 3) == 0, 'Pointer to integer data passed to glVertexAttribI4iv must be aligned to four bytes!'); assert(v != 0, 'Null pointer passed to glVertexAttribI4iv!'); #endif - GLctx.vertexAttribI4i(index, HEAP32[v>>2], HEAP32[v+4>>2], HEAP32[v+8>>2], HEAP32[v+12>>2]); + {{{ convertPtrToIdx('v', 2) }}} + GLctx.vertexAttribI4i(index, HEAP32[v++], HEAP32[v++], HEAP32[v++], HEAP32[v]); }, glVertexAttribI4uiv: function(index, v) { @@ -888,7 +891,8 @@ var LibraryWebGL2 = { assert((v & 3) == 0, 'Pointer to integer data passed to glVertexAttribI4uiv must be aligned to four bytes!'); assert(v != 0, 'Null pointer passed to glVertexAttribI4uiv!'); #endif - GLctx.vertexAttribI4ui(index, HEAPU32[v>>2], HEAPU32[v+4>>2], HEAPU32[v+8>>2], HEAPU32[v+12>>2]); + {{{ convertPtrToIdx('v', 2) }}} + GLctx.vertexAttribI4ui(index, HEAPU32[v++], HEAPU32[v++], HEAPU32[v++], HEAPU32[v]); }, glProgramParameteri: function(program, pname, value) { @@ -964,7 +968,7 @@ var LibraryWebGL2 = { glDrawArraysInstancedBaseInstance: 'glDrawArraysInstancedBaseInstanceWEBGL', glDrawArraysInstancedBaseInstanceANGLE: 'glDrawArraysInstancedBaseInstanceWEBGL', - glDrawElementsInstancedBaseVertexBaseInstanceWEBGL__sig: 'viiiiiii', + glDrawElementsInstancedBaseVertexBaseInstanceWEBGL__sig: 'viiipiii', glDrawElementsInstancedBaseVertexBaseInstanceWEBGL: function(mode, count, type, offset, instanceCount, baseVertex, baseinstance) { GLctx.dibvbi['drawElementsInstancedBaseVertexBaseInstanceWEBGL'](mode, count, type, offset, instanceCount, baseVertex, baseinstance); }, @@ -980,37 +984,37 @@ var LibraryWebGL2 = { return webgl_enable_WEBGL_draw_instanced_base_vertex_base_instance(GL.contexts[ctx].GLctx); }, - glMultiDrawArraysInstancedBaseInstanceWEBGL__sig: 'viiiiii', + glMultiDrawArraysInstancedBaseInstanceWEBGL__sig: 'viipppi', glMultiDrawArraysInstancedBaseInstanceWEBGL: function(mode, firsts, counts, instanceCounts, baseInstances, drawCount) { GLctx.mdibvbi['multiDrawArraysInstancedBaseInstanceWEBGL']( mode, HEAP32, - firsts >> 2, + {{{ ptrToIdx('firsts', 2) }}}, HEAP32, - counts >> 2, + {{{ ptrToIdx('counts', 2) }}}, HEAP32, - instanceCounts >> 2, + {{{ ptrToIdx('instanceCounts', 2) }}}, HEAPU32, - baseInstances >> 2, + {{{ ptrToIdx('baseInstances', 2) }}}, drawCount); }, glMultiDrawArraysInstancedBaseInstanceANGLE: 'glMultiDrawArraysInstancedBaseInstanceWEBGL', - glMultiDrawElementsInstancedBaseVertexBaseInstanceWEBGL__sig: 'viiiiiiii', + glMultiDrawElementsInstancedBaseVertexBaseInstanceWEBGL__sig: 'vipippppi', glMultiDrawElementsInstancedBaseVertexBaseInstanceWEBGL: function(mode, counts, type, offsets, instanceCounts, baseVertices, baseInstances, drawCount) { GLctx.mdibvbi['multiDrawElementsInstancedBaseVertexBaseInstanceWEBGL']( mode, HEAP32, - counts >> 2, + {{{ ptrToIdx('counts', 2) }}}, type, HEAP32, - offsets >> 2, + {{{ ptrToIdx('offsets', 2) }}}, HEAP32, - instanceCounts >> 2, + {{{ ptrToIdx('instanceCounts', 2) }}}, HEAP32, - baseVertices >> 2, + {{{ ptrToIdx('baseVertices', 2) }}}, HEAPU32, - baseInstances >> 2, + {{{ ptrToIdx('baseInstances', 2) }}}, drawCount); }, glMultiDrawElementsInstancedBaseVertexBaseInstanceANGLE: 'glMultiDrawElementsInstancedBaseVertexBaseInstanceWEBGL', diff --git a/src/parseTools.js b/src/parseTools.js index 91608eef8da34..8aef6069fd9b3 100644 --- a/src/parseTools.js +++ b/src/parseTools.js @@ -2,6 +2,8 @@ * @license * Copyright 2010 The Emscripten Authors * SPDX-License-Identifier: MIT + * SPDX-FileCopyrightText: Portions Copyright 2023 Siemens + * Modified on February 2023 by Siemens and/or its affiliates to improve memory64 support * * Helpers and tools for use at compile time by JavaScript library files. * @@ -267,7 +269,11 @@ function getHeapOffset(offset, type) { const sz = getNativeTypeSize(type); const shifts = Math.log(sz) / Math.LN2; - return `((${offset})>>${shifts})`; + if (MEMORY64) { + return `((${offset})/(1<<${shifts}))`; + } else { + return `((${offset})>>${shifts})`; + } } function ensureDot(value) { @@ -391,8 +397,13 @@ function makeSetValueImpl(ptr, pos, value, type) { function makeHEAPView(which, start, end) { const size = parseInt(which.replace('U', '').replace('F', '')) / 8; - const mod = size == 1 ? '' : ('>>' + Math.log2(size)); - return `HEAP${which}.subarray((${start})${mod}, (${end})${mod})`; + if (MEMORY64) { + const mod = size == 1 ? '' : ('/' + size); + return `HEAP${which}.subarray((${start})${mod}, (${end})${mod})`; + } else { + const mod = size == 1 ? '' : ('>>' + Math.log2(size)); + return `HEAP${which}.subarray((${start})${mod}, (${end})${mod})`; + } } // Given two values and an operation, returns the result of that operation. @@ -887,8 +898,59 @@ function from64(x) { } function to64(x) { - if (!MEMORY64) return x; - return `BigInt(${x})`; + if (MEMORY64) return /^\d+$/.test(x) ? `${x}n` : `BigInt(${x})`; + return x; +} + +// Generates an expression that tests whether a pointer (either a BigInt or a signed int32 JS Number) is aligned to the given byte multiple. +function isPtrAligned(ptr, alignment) { + if (MEMORY64) return /^\d+$/.test(alignment) ? `BigInt(${ptr}) % ${alignment}n == 0` : `${ptr} % BigInt(${alignment}) == 0`; + return `${ptr} % ${alignment} == 0`; +} + +const MAX_MEMORY53 = 2 ** 53; // == 9007199254740992 + +// Converts a pointer (either a BigInt or a signed int32 JS Number) in-place to an index on the heap (a BigInt or an unsigned JS Number). +// N.B. in ASSERTIONS mode may generate two statements (in form "a;b;"). +function convertPtrToIdx(ptr, accessWidth) { + const assertPointerAlignment = ASSERTIONS ? `${isPtrAligned(ptr, accessWidth)};` : ''; + + let conversion; + if (MEMORY64) { + // if our address space would ever get larger than 53 bits, we could do something like this: + // if (MAXIMUM_MEMORY > MAX_MEMORY53) conversion = /^\d+$/.test(accessWidth) ? `${ptr} >>= ${accessWidth}n` : `${ptr} >>= BigInt(${accessWidth})`; + assert(MAXIMUM_MEMORY <= MAX_MEMORY53); + conversion = `${ptr} = Number(BigInt(${ptr}) >> BigInt(${accessWidth}))`; + } else if (MAXIMUM_MEMORY > 2 * 1024 * 1024 * 1024) { + conversion = `${ptr} >>>= ${accessWidth}`; + } else { + conversion = accessWidth ? `${ptr} >>= ${accessWidth}` : ''; + } + + return assertPointerAlignment + conversion; +} + +// Returns a pointer (either a BigInt or a signed int32 JS Number) shifted to an index on the heap (a BigInt or an unsigned JS Number). +function ptrToIdx(ptr, accessWidth) { + if (MEMORY64) { + assert(MAXIMUM_MEMORY <= MAX_MEMORY53); + return `Number(BigInt(${ptr}) >> BigInt(${accessWidth}))`; + } + if (MAXIMUM_MEMORY > 2 * 1024 * 1024 * 1024) return `${ptr} >>> ${accessWidth}`; + return accessWidth ? `${ptr} >> ${accessWidth}` : ptr; +} + +// Converts a given JS Number (e.g. a pointer offset) to a heap index type (either a BigInt or a JS Number) +function createHeapIdx(number) { + if (MAXIMUM_MEMORY > MAX_MEMORY53) return /^-?\d+$/.test(number) ? `${number}n` : `BigInt(${number})`; + return number; +} + +// Browsers might not support passing BigInts to different API entry points that take in addresses, so convert those to Number()s before calling into the browser APIs. +function idxToMemory53(heapIndex) { + if (MAXIMUM_MEMORY <= MAX_MEMORY53) return heapIndex; // If not building with large 64-bit memory, heap indices will not be BigInts. + if (ASSERTIONS) return `BigInt(Number(${heapIndex})) == ${heapIndex} ? abort('Invalid pointer that does not fit in 53-bits address space!') : Number(${heapIndex})`; + return `Number(${heapIndex})`; } // Add assertions to catch common errors when using the Promise object we