Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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