Skip to content

Commit

Permalink
Fix LEGACY_GL_EMULATION + MAIN_MODULE
Browse files Browse the repository at this point in the history
Fixes: #22431
  • Loading branch information
sbc100 committed Aug 27, 2024
1 parent f104079 commit 2a87c73
Show file tree
Hide file tree
Showing 4 changed files with 83 additions and 42 deletions.
1 change: 1 addition & 0 deletions embuilder.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@
'libmimalloc-mt',
'libGL',
'libGL-getprocaddr',
'libGL-emu-getprocaddr',
'libGL-emu-webgl2-ofb-getprocaddr',
'libGL-webgl2-ofb-getprocaddr',
'libGL-ww-getprocaddr',
Expand Down
104 changes: 65 additions & 39 deletions src/library_glemu.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,18 @@
*/

{{{
globalThis.copySigs = (func) => {
if (!RELOCATABLE) return '';
return ` _${func}.sig = _emscripten_${func}.sig = orig_${func}.sig;`;
};
globalThis.fromPtr = (arg) => {
if (CAN_ADDRESS_2GB) {
return `${arg} >>>= 0`;
} else if (MEMORY64) {
return `${arg} = Number(${arg})`;
}
return '';
}
};
null;
}}}

Expand Down Expand Up @@ -199,7 +203,7 @@ var LibraryGLEmulation = {
0x80A0: 1 // GL_SAMPLE_COVERAGE
};

var glEnable = _glEnable;
var orig_glEnable = _glEnable;
_glEnable = _emscripten_glEnable = (cap) => {
// Clean up the renderer on any change to the rendering state. The optimization of
// skipping renderer setup is aimed at the case of multiple glDraw* right after each other
Expand Down Expand Up @@ -247,10 +251,11 @@ var LibraryGLEmulation = {
} else if (!(cap in validCapabilities)) {
return;
}
glEnable(cap);
orig_glEnable(cap);
};
{{{ copySigs('glEnable') }}}

var glDisable = _glDisable;
var orig_glDisable = _glDisable;
_glDisable = _emscripten_glDisable = (cap) => {
GLImmediate.lastRenderer?.cleanup();
if (cap == 0xB60 /* GL_FOG */) {
Expand Down Expand Up @@ -296,9 +301,11 @@ var LibraryGLEmulation = {
} else if (!(cap in validCapabilities)) {
return;
}
glDisable(cap);
orig_glDisable(cap);
};
{{{ copySigs('glDisable') }}}

var orig_glIsEnabled = _glIsEnabled;
_glIsEnabled = _emscripten_glIsEnabled = (cap) => {
if (cap == 0xB60 /* GL_FOG */) {
return GLEmulation.fogEnabled ? 1 : 0;
Expand All @@ -317,8 +324,9 @@ var LibraryGLEmulation = {
}
return GLctx.isEnabled(cap);
};
{{{ copySigs('glIsEnabled') }}}

var glGetBooleanv = _glGetBooleanv;
var orig_glGetBooleanv = _glGetBooleanv;
_glGetBooleanv = _emscripten_glGetBooleanv = (pname, p) => {
var attrib = GLEmulation.getAttributeFromCapability(pname);
if (attrib !== null) {
Expand All @@ -327,10 +335,11 @@ var LibraryGLEmulation = {
{{{ makeSetValue('p', '0', 'result === true ? 1 : 0', 'i8') }}};
return;
}
glGetBooleanv(pname, p);
orig_glGetBooleanv(pname, p);
};
{{{ copySigs('glGetBooleanv') }}}

var glGetIntegerv = _glGetIntegerv;
var orig_glGetIntegerv = _glGetIntegerv;
_glGetIntegerv = _emscripten_glGetIntegerv = (pname, params) => {
{{{ fromPtr('params') }}}
switch (pname) {
Expand Down Expand Up @@ -409,10 +418,11 @@ var LibraryGLEmulation = {
return;
}
}
glGetIntegerv(pname, params);
orig_glGetIntegerv(pname, params);
};
{{{ copySigs('glGetIntegerv') }}}

var glGetString = _glGetString;
var orig_glGetString = _glGetString;
_glGetString = _emscripten_glGetString = (name_) => {
if (GL.stringCache[name_]) return GL.stringCache[name_];
switch (name_) {
Expand All @@ -424,8 +434,9 @@ var LibraryGLEmulation = {
);
return GL.stringCache[name_] = {{{ to64('ret') }}};
}
return glGetString(name_);
return orig_glGetString(name_);
};
{{{ copySigs('glGetString') }}}

// Do some automatic rewriting to work around GLSL differences. Note that this must be done in
// tandem with the rest of the program, by itself it cannot suffice.
Expand All @@ -435,15 +446,16 @@ var LibraryGLEmulation = {
GL.shaderSources = {};
GL.shaderOriginalSources = {};
#endif
var glCreateShader = _glCreateShader;
var orig_glCreateShader = _glCreateShader;
_glCreateShader = _emscripten_glCreateShader = (shaderType) => {
var id = glCreateShader(shaderType);
var id = orig_glCreateShader(shaderType);
GL.shaderInfos[id] = {
type: shaderType,
ftransform: false
};
return id;
};
{{{ copySigs('glCreateShader') }}}

function ensurePrecision(source) {
if (!/precision +(low|medium|high)p +float *;/.test(source)) {
Expand All @@ -452,7 +464,7 @@ var LibraryGLEmulation = {
return source;
}

var glShaderSource = _glShaderSource;
var orig_glShaderSource = _glShaderSource;
_glShaderSource = _emscripten_glShaderSource = (shader, count, string, length) => {
{{{ fromPtr('string') }}}
{{{ fromPtr('length') }}}
Expand Down Expand Up @@ -566,8 +578,9 @@ var LibraryGLEmulation = {
#endif
GLctx.shaderSource(GL.shaders[shader], source);
};
{{{ copySigs('glShaderSource') }}}

var glCompileShader = _glCompileShader;
var orig_glCompileShader = _glCompileShader;
_glCompileShader = _emscripten_glCompileShader = (shader) => {
GLctx.compileShader(GL.shaders[shader]);
#if GL_DEBUG
Expand All @@ -580,16 +593,18 @@ var LibraryGLEmulation = {
}
#endif
};
{{{ copySigs('glCompileShader') }}}

GL.programShaders = {};
var glAttachShader = _glAttachShader;
var orig_glAttachShader = _glAttachShader;
_glAttachShader = _emscripten_glAttachShader = (program, shader) => {
GL.programShaders[program] ||= [];
GL.programShaders[program].push(shader);
glAttachShader(program, shader);
orig_glAttachShader(program, shader);
};
{{{ copySigs('glAttachShader') }}}

var glDetachShader = _glDetachShader;
var orig_glDetachShader = _glDetachShader;
_glDetachShader = _emscripten_glDetachShader = (program, shader) => {
var programShader = GL.programShaders[program];
if (!programShader) {
Expand All @@ -598,10 +613,11 @@ var LibraryGLEmulation = {
}
var index = programShader.indexOf(shader);
programShader.splice(index, 1);
glDetachShader(program, shader);
orig_glDetachShader(program, shader);
};
{{{ copySigs('glDetachShader') }}}

var glUseProgram = _glUseProgram;
var orig_glUseProgram = _glUseProgram;
_glUseProgram = _emscripten_glUseProgram = (program) => {
#if GL_DEBUG
if (GL.debug) {
Expand All @@ -618,38 +634,42 @@ var LibraryGLEmulation = {
GLImmediate.currentRenderer = null; // This changes the FFP emulation shader program, need to recompute that.
GL.currProgram = program;
GLImmediate.fixedFunctionProgram = 0;
glUseProgram(program);
orig_glUseProgram(program);
}
}
{{{ copySigs('glUseProgram') }}}

var glDeleteProgram = _glDeleteProgram;
var orig_glDeleteProgram = _glDeleteProgram;
_glDeleteProgram = _emscripten_glDeleteProgram = (program) => {
glDeleteProgram(program);
orig_glDeleteProgram(program);
if (program == GL.currProgram) {
GLImmediate.currentRenderer = null; // This changes the FFP emulation shader program, need to recompute that.
GL.currProgram = 0;
}
};
{{{ copySigs('glDeleteProgram') }}}

// If attribute 0 was not bound, bind it to 0 for WebGL performance reasons. Track if 0 is free for that.
var zeroUsedPrograms = {};
var glBindAttribLocation = _glBindAttribLocation;
var orig_glBindAttribLocation = _glBindAttribLocation;
_glBindAttribLocation = _emscripten_glBindAttribLocation = (program, index, name) => {
if (index == 0) zeroUsedPrograms[program] = true;
glBindAttribLocation(program, index, name);
orig_glBindAttribLocation(program, index, name);
};
{{{ copySigs('glBindAttribLocation') }}}

var glLinkProgram = _glLinkProgram;
var orig_glLinkProgram = _glLinkProgram;
_glLinkProgram = _emscripten_glLinkProgram = (program) => {
if (!(program in zeroUsedPrograms)) {
GLctx.bindAttribLocation(GL.programs[program], 0, 'a_position');
}
glLinkProgram(program);
orig_glLinkProgram(program);
};
{{{ copySigs('glLinkProgram') }}}

var glBindBuffer = _glBindBuffer;
var orig_glBindBuffer = _glBindBuffer;
_glBindBuffer = _emscripten_glBindBuffer = (target, buffer) => {
glBindBuffer(target, buffer);
orig_glBindBuffer(target, buffer);
if (target == GLctx.ARRAY_BUFFER) {
if (GLEmulation.currentVao) {
#if ASSERTIONS
Expand All @@ -661,8 +681,9 @@ var LibraryGLEmulation = {
if (GLEmulation.currentVao) GLEmulation.currentVao.elementArrayBuffer = buffer;
}
};
{{{ copySigs('glBindBuffer') }}}

var glGetFloatv = _glGetFloatv;
var orig_glGetFloatv = _glGetFloatv;
_glGetFloatv = _emscripten_glGetFloatv = (pname, params) => {
{{{ fromPtr('params') }}}
if (pname == 0xBA6) { // GL_MODELVIEW_MATRIX
Expand All @@ -689,39 +710,44 @@ var LibraryGLEmulation = {
} else if (pname == 0xBC2) { // GL_ALPHA_TEST_REF
{{{ makeSetValue('params', '0', 'GLEmulation.alphaTestRef', 'float') }}};
} else {
glGetFloatv(pname, params);
orig_glGetFloatv(pname, params);
}
};
{{{ copySigs('glGetFloatv') }}}

var glHint = _glHint;
var orig_glHint = _glHint;
_glHint = _emscripten_glHint = (target, mode) => {
if (target == 0x84EF) { // GL_TEXTURE_COMPRESSION_HINT
return;
}
glHint(target, mode);
orig_glHint(target, mode);
};
{{{ copySigs('glHint') }}}

var glEnableVertexAttribArray = _glEnableVertexAttribArray;
var orig_glEnableVertexAttribArray = _glEnableVertexAttribArray;
_glEnableVertexAttribArray = _emscripten_glEnableVertexAttribArray = (index) => {
glEnableVertexAttribArray(index);
orig_glEnableVertexAttribArray(index);
GLEmulation.enabledVertexAttribArrays[index] = 1;
if (GLEmulation.currentVao) GLEmulation.currentVao.enabledVertexAttribArrays[index] = 1;
};
{{{ copySigs('glEnableVertexAttribArray') }}}

var glDisableVertexAttribArray = _glDisableVertexAttribArray;
var orig_glDisableVertexAttribArray = _glDisableVertexAttribArray;
_glDisableVertexAttribArray = _emscripten_glDisableVertexAttribArray = (index) => {
glDisableVertexAttribArray(index);
orig_glDisableVertexAttribArray(index);
delete GLEmulation.enabledVertexAttribArrays[index];
if (GLEmulation.currentVao) delete GLEmulation.currentVao.enabledVertexAttribArrays[index];
};
{{{ copySigs('glDisableVertexAttribArray') }}}

var glVertexAttribPointer = _glVertexAttribPointer;
var orig_glVertexAttribPointer = _glVertexAttribPointer;
_glVertexAttribPointer = _emscripten_glVertexAttribPointer = (index, size, type, normalized, stride, pointer) => {
glVertexAttribPointer(index, size, type, normalized, stride, pointer);
orig_glVertexAttribPointer(index, size, type, normalized, stride, pointer);
if (GLEmulation.currentVao) { // TODO: avoid object creation here? likely not hot though
GLEmulation.currentVao.vertexAttribPointers[index] = [index, size, type, normalized, stride, pointer];
}
};
{{{ copySigs('glVertexAttribPointer') }}}
},

getAttributeFromCapability(cap) {
Expand Down
20 changes: 17 additions & 3 deletions test/test_other.py
Original file line number Diff line number Diff line change
Expand Up @@ -2340,6 +2340,20 @@ def test_dylink_dependencies(self):
self.run_process(cmd + ['-L.'])
self.run_js('a.out.js')

def test_dylink_LEGACY_GL_EMULATION(self):
# LEGACY_GL_EMULATION wraps JS library functions. This test ensure that when it does
# so it preserves the `.sig` attributes needed by dynamic linking.
create_file('test.c', r'''
#include <GLES2/gl2.h>
#include <stdio.h>

int main() {
printf("glUseProgram: %p\n", &glUseProgram);
printf("done\n");
return 0;
}''')
self.do_runf('test.c', 'done\n', emcc_args=['-sLEGACY_GL_EMULATION', '-sMAIN_MODULE=2'])

def test_js_link(self):
create_file('main.c', '''
#include <stdio.h>
Expand Down Expand Up @@ -2725,15 +2739,15 @@ def test_undefined_data_symbols(self):

def test_GetProcAddress_LEGACY_GL_EMULATION(self):
# without legacy gl emulation, getting a proc from there should fail
self.do_other_test('test_GetProcAddress_LEGACY_GL_EMULATION.cpp', args=['0'], emcc_args=['-sLEGACY_GL_EMULATION=0', '-sGL_ENABLE_GET_PROC_ADDRESS'])
self.do_other_test('test_GetProcAddress_LEGACY_GL_EMULATION.c', args=['0'], emcc_args=['-sLEGACY_GL_EMULATION=0', '-sGL_ENABLE_GET_PROC_ADDRESS'])
# with it, it should work
self.do_other_test('test_GetProcAddress_LEGACY_GL_EMULATION.cpp', args=['1'], emcc_args=['-sLEGACY_GL_EMULATION', '-sGL_ENABLE_GET_PROC_ADDRESS'])
self.do_other_test('test_GetProcAddress_LEGACY_GL_EMULATION.c', args=['1'], emcc_args=['-sLEGACY_GL_EMULATION', '-sGL_ENABLE_GET_PROC_ADDRESS'])

# Verifies that is user is building without -sGL_ENABLE_GET_PROC_ADDRESS, then
# at link time they should get a helpful error message guiding them to enable
# the option.
def test_get_proc_address_error_message(self):
err = self.expect_fail([EMCC, '-sGL_ENABLE_GET_PROC_ADDRESS=0', test_file('other/test_GetProcAddress_LEGACY_GL_EMULATION.cpp')])
err = self.expect_fail([EMCC, '-sGL_ENABLE_GET_PROC_ADDRESS=0', test_file('other/test_GetProcAddress_LEGACY_GL_EMULATION.c')])
self.assertContained('error: linker: Undefined symbol: SDL_GL_GetProcAddress(). Please pass -sGL_ENABLE_GET_PROC_ADDRESS at link time to link in SDL_GL_GetProcAddress().', err)

def test_prepost(self):
Expand Down

0 comments on commit 2a87c73

Please sign in to comment.