floooh / sokol

minimal cross-platform standalone C headers
https://floooh.github.io/sokol-html5
zlib License
7.07k stars 494 forks source link

Emscripten samples: weird corruption issues with WebGL2 shaders. #1114

Closed floooh closed 1 month ago

floooh commented 1 month ago

Two of the 16 Emscripten samples in sokol-samples don't work with WebGL2 shaders with very weird symptoms (bufferoffsets-emsc and offscreen-emsc).

To reproduce:

Replace the shaders here:

https://github.com/floooh/sokol-samples/blob/0502fa1fb840e502235518af94e7ab27b7a7adb3/html5/bufferoffsets-emsc.c#L70-L83

...with:

    sg_shader shd = sg_make_shader(&(sg_shader_desc){
        .attrs = {
            [0].name = "pos",
            [1].name = "color0"
        },
        .vs.source =
            "#version 300 es\n"
            "in vec2 pos;"
            "in vec3 color0;"
            "out vec4 color;"
            "void main() {"
            "  gl_Position = vec4(pos, 0.5, 1.0);\n"
            "  color = vec4(color0, 1.0);\n"
            "}\n",
        .fs.source =
            "#version 300 es\n",
            "precision mediump float;\n"
            "in vec4 color;\n"
            "out vec4 frag_color;\n"
            "void main() {\n"
            "  frag_color = color;\n"
            "}\n"
    });

...when building with webgl2-wasm-ninja-release this results in the following errors on the JS console:

image

...and when building with webgl2-wasm-ninja-debug this result in (and the sg_shader_desc struct that's going into sg_make_shader() seems to indeed have a junk .bytecode pointer!

image

...my first hunch was Emscripten stack size, but the error also happens with vastly increased stack size.

Next I though that maybe something corrupts the stack inside the sokol_gfx.h shader compilation code, but that would only explain followup errors, but here the first call to sg_make_shader() is already broken.

floooh commented 1 month ago

...same error when moving the sg_shader_desc out into a global variable.

floooh commented 1 month ago

...it gets crazier... moving the shd_desc out into a global variable:

static const sg_shader_desc shd_desc = {
    .attrs = {
        [0].name = "pos",
        [1].name = "color0"
    },
    .vs.source =
        "#version 300 es\n"
        "in vec2 pos;\n"
        "in vec3 color0;\n"
        "out vec4 color;\n"
        "void main() {\n"
        "  gl_Position = vec4(pos, 0.5, 1.0);\n"
        "  color = vec4(color0, 1.0);\n"
        "}\n",
    .fs.source =
        "#version 300 es\n",
        "precision mediump float;\n"
        "in vec4 color;\n"
        "out vec4 frag_color;\n"
        "void main() {\n"
        "  frag_color = color;\n"
        "}\n"
};

...and then adding this assertion at the start of the main function:

    assert(0 == shd_desc.vs.bytecode.ptr);
    assert(0 == shd_desc.fs.bytecode.ptr)

...the second assert for the fs.bytecode.ptr triggers! And even when that assert is right at the start of the main function! And somehow this depends on the shader code strings!

...almost looks like some weird Emscripten behaviour... let's check older versions...

PS: Hmm, no luck going all the way back to emsdk 3.1.30...

floooh commented 1 month ago

Ok, I'm officially a moron :D

What's wrong with this code (hint: the extra comma after the first line lol):

    .fs.source =
        "#version 300 es\n",
        "precision mediump float;\n"
        "in vec4 color;\n"
        "out vec4 frag_color;\n"
        "void main() {\n"
        "  frag_color = color;\n"
        "}\n"

...this was the reason why both the bufferoffset-emsc and offscreen-emsc samples failed. It's still interesting that it causes such absolutely weird errors in release mode though!