emscripten icon indicating copy to clipboard operation
emscripten copied to clipboard

Chrome passes browser.test_sdl2_canvas_proxy, but should actually fail

Open juj opened this issue 2 months ago • 6 comments

Debugging the failure of browser.test_sdl2_canvas_proxy in Firefox, it turns out that Chrome is behaving in a nonstandard manner in its WebGL2 implementation, which hides a SDL2 Emscripten port bug, and causes the test to accidentally pass.

In Firefox the test fails as is correct, unmasking the SDL2 bug.

Reported the Chrome bug in https://issues.chromium.org/issues/448407149

What is happening is that SDL2 attempts to compile the following fragment shader:

#extension GL_OES_EGL_image_external : require

#define mediump
#define highp
#define lowp
#define SDL_TEXCOORD_PRECISION

uniform samplerExternalOES u_texture;
varying mediump vec4 v_color;
varying SDL_TEXCOORD_PRECISION vec2 v_texCoord;

void main()
{
    gl_FragColor = texture2D(u_texture, v_texCoord);
    gl_FragColor *= v_color;
}

but GL_OES_EGL_image_external is not a ratified WebGL extension, and samplerExternalOES is not a valid keyword in WebGL. I.e. SDL2 is leaking a native EGL+OpenGLES2/3 extension shader into WebGL.

Still, Chrome happily compiles and links the above shader. That shader doesn't do anything in the browser.test_sdl2_canvas_proxy, it just is getting compiled in by SDL2, but never activated (it is not part of the test)

Firefox croaks on the above unrecognized shader, and because browser.test_sdl2_canvas_proxy is testing the ancient asynchronously proxied WebGL support, then there is this code in the proxying implementation:

    43: { name: 'getShaderParameter', func: () => {assert(ctx.getShaderParameter(objects[buffer[i++]], buffer[i++]), 'we cannot handle errors, we are async proxied WebGL') },

which assert-fails in Firefox, as it should. Chrome is silently passing past since it compiled the shader.

juj avatar Sep 30 '25 22:09 juj

After sidestepping the above assert(), there is still some issue at play with respect to the async proxied WebGL Worker mechanism and Emscripten's test harness : if I run the compiled test code outside Emscripten test harness in the emrun ad hoc server (with proxying enabled), the test will always pass.

But if I run the test case inside the Emscripten test harness, the rendered screen will always stay completely black.

If I disable --proxy-to-worker and @proxied directives in browser.test_sdl2_canvas_alpha, then the test also passes in Firefox in the Emscripten test harness. So the black screen issue is somehow related to proxying+Emscripten harness+Firefox. Maybe some kind of race condition.

juj avatar Sep 30 '25 23:09 juj

Oh, and there's a long standing issue that was never fixed that is needed to make the test get past SDL2 context init: https://github.com/emscripten-core/emscripten/issues/7100

That can be fixed with this patch:

diff --git a/test/browser/test_sdl2_canvas_proxy.c b/test/browser/test_sdl2_canvas_proxy.c
index 69ca32b8f..7a3d8b569 100644
--- a/test/browser/test_sdl2_canvas_proxy.c
+++ b/test/browser/test_sdl2_canvas_proxy.c
@@ -11,6 +11,8 @@
 #include <assert.h>
 #include <emscripten.h>

+void unusedRaf() {}
+
 int main(int argc, char **argv) {
   FILE *f = fopen("data.txt", "rb");
   assert(f);
@@ -27,8 +29,12 @@ int main(int argc, char **argv) {
   SDL_Window *window;
   SDL_Renderer *renderer;

+  emscripten_set_main_loop(unusedRaf, 0, 0);
+
   SDL_CreateWindowAndRenderer(600, 450, 0, &window, &renderer);

+  emscripten_set_main_loop_timing(EM_TIMING_RAF, 0);
+
   SDL_Surface *screen = SDL_CreateRGBSurface(0, 600, 450, 32, 0, 0, 0, 0);

   SDL_LockSurface(screen);
@@ -47,6 +53,12 @@ int main(int argc, char **argv) {

   // Don't quit - we need to reftest the canvas! SDL_Quit();

It seems to me that SDL2 users currently are all getting those emscripten_set_main_loop_timing: Cannot set timing mode for main loop since a main loop does not exist! Call emscripten_set_main_loop first to set one up. and Looks like you are rendering without using requestAnimationFrame for the main loop. You should use 0 for the frame rate in emscripten_set_main_loop in order to use requestAnimationFrame, as that can greatly improve your frame rates! warning spams.

In Emscripten test harness, that warning spam is turned into errors (by -Werror ?), so creating a SDL2 renderer doesn't work without those manual main loop tweaks sandwiched around SDL_CreateWindowAndRenderer().

juj avatar Sep 30 '25 23:09 juj

I don't see any way in which the Cannot set timing mode for main loop since a main loop does not exist warning could ever be an error so I guess that tests are depending on emscripten_set_main_loop_timing not running into this condition maybe?

sbc100 avatar Sep 30 '25 23:09 sbc100

I don't see any way in which the Cannot set timing mode for main loop since a main loop does not exist warning could ever be an error so I guess that tests are depending on emscripten_set_main_loop_timing not running into this condition maybe?

Err, you are right. I now re-checked and those set_main_loop_timing prints were not actually causing the error, but it was the proxying assert all along.

juj avatar Sep 30 '25 23:09 juj

I reopened https://github.com/emscripten-core/emscripten/issues/7100 so we can address that error spam

sbc100 avatar Sep 30 '25 23:09 sbc100

Turns out https://github.com/emscripten-core/emscripten/pull/25439 was all that was needed to fix the test in Firefox.

juj avatar Sep 30 '25 23:09 juj