emscripten-core / emscripten

Emscripten: An LLVM-to-WebAssembly Compiler
Other
25.84k stars 3.31k forks source link

simple SDL program throws JavaScript exception: SDL2.ctx is null #21716

Open bottle2 opened 7 months ago

bottle2 commented 7 months ago

good night

when I $ emrun random.html, both inside Firefox and Edge on Windows 10, the HTML shell says "Exception thrown, see JavaScript console".

then I open console. I hit the browser's reload webpage button while holding SHIFT. it is impossible to refresh the webpage using keyboard for some reason.

the console says:

Uncaught TypeError: SDL2.ctx is null
    75510 http://localhost:6931/index.html:812
    runMainThreadEmAsm http://localhost:6931/index.html:5159
    _emscripten_asm_const_int_sync_on_main_thread http://localhost:6931/index.html:5161
    x http://localhost:6931/index.html:8422
    _main http://localhost:6931/index.html:9111
    callMain http://localhost:6931/index.html:9188
    doRun http://localhost:6931/index.html:9227
    run http://localhost:6931/index.html:9238
    setTimeout handler*run http://localhost:6931/index.html:9234
    runCaller http://localhost:6931/index.html:9167
    removeRunDependency http://localhost:6931/index.html:629
    receiveInstance http://localhost:6931/index.html:758
    receiveInstantiationResult http://localhost:6931/index.html:770
    promise callback*instantiateArrayBuffer http://localhost:6931/index.html:719
    instantiateAsync http://localhost:6931/index.html:727
    createWasm http://localhost:6931/index.html:789
    <anonymous> http://localhost:6931/index.html:9109
index.html:812:350

I click on it. I scroll left. I find SDL2.ctx = Module['createContext'](Module['canvas'], false, true);

now some commands I ran on my own inside browser console:

>> Module.SDL2
Object { ctx: null, ctxCanvas: canvas#canvas.emscripten }
>> Module.SDL2.ctx
null
>> Module.createContext
function createContext(canvas, useWebGL, setInModule, webGLContextAttributes)
>> Module.canvas
<canvas id="canvas" class="emscripten" oncontextmenu="event.preventDefault()" tabindex="-1" width="800" height="600" style="cursor: default;">
>> Module.createContext(Module.canvas, false, true)
null
>> Module.createContext(Module.canvas, false, false)
null
>> Module.createContext(Module.canvas, true, false)
WebGLRenderingContext { vertexAttribDivisor: vertexAttribDivisor(index, divisor), drawArraysInstanced: drawArraysInstanced(mode, first, count, primcount), drawElementsInstanced: drawElementsInstanced(mode, count, type, indices, primcount), createVertexArray: createVertexArray(), deleteVertexArray: deleteVertexArray(vao), bindVertexArray: bindVertexArray(vao), isVertexArray: isVertexArray(vao), drawBuffers: drawBuffers(n, bufs), disjointTimerQueryExt: null, multiDrawWebgl: null }
>> Module.createContext(Module.canvas, true, true)
WebGLRenderingContext { vertexAttribDivisor: vertexAttribDivisor(index, divisor), drawArraysInstanced: drawArraysInstanced(mode, first, count, primcount), drawElementsInstanced: drawElementsInstanced(mode, count, type, indices, primcount), createVertexArray: createVertexArray(), deleteVertexArray: deleteVertexArray(vao), bindVertexArray: bindVertexArray(vao), isVertexArray: isVertexArray(vao), drawBuffers: drawBuffers(n, bufs), disjointTimerQueryExt: null, multiDrawWebgl: null }

in the future I might do a binary search on Emscripten versions

Version of emscripten/emsdk:

$ emcc -v
emcc (Emscripten gcc/clang-like replacement + linker emulating GNU ld) 3.1.56-git (cf90417346b78455089e64eb909d71d091ecc055)
clang version 19.0.0git
Target: wasm32-unknown-emscripten
Thread model: posix
InstalledDir: C:/msys64/clang64/opt/emscripten-llvm/bin
$ emcc -v
emcc (Emscripten gcc/clang-like replacement + linker emulating GNU ld) 3.1.56 (cf90417346b78455089e64eb909d71d091ecc055)
clang version 19.0.0git (https:/github.com/llvm/llvm-project 34ba90745fa55777436a2429a51a3799c83c6d4c)
Target: wasm32-unknown-emscripten
Thread model: posix
InstalledDir: /home/dead-boy/emsdk/upstream/bin

Full link command and output with -v appended:

$ EMCC_DEBUG=1 emcc -Os -sASYNCIFY -sSINGLE_FILE -sUSE_SDL=2 random.c -o random.html -v &>clang64-emcc.txt

$ EMCC_DEBUG=1 emcc -Os -sASYNCIFY -sSINGLE_FILE -sUSE_SDL=2 random.c -o random.html -v &>wsl2.txt

$ cat random.c
#include <stdbool.h>
#include <stdlib.h>
#include <time.h>

#include <SDL.h>

#define TRY(IT) \
if ((IT)) { SDL_LogError(SDL_LOG_CATEGORY_ERROR, "%s\n", SDL_GetError()); \
            exit(EXIT_FAILURE); } else (void)0

static inline bool window_resize_maybe(SDL_Event event, int *width, int *height)
{
    if (event.type != SDL_WINDOWEVENT) return false;
    if (SDL_WINDOWEVENT_RESIZED      != event.window.event &&
        SDL_WINDOWEVENT_SIZE_CHANGED != event.window.event) return false;

    *width  = event.window.data1;
    *height = event.window.data2;

    return true;
}

static int window_width  = 800;
static int window_height = 600;

static void set_pixel(void *data, int x, int y, uint8_t r, uint8_t g, uint8_t b)
{
    if (x >= window_width || y >= window_height) return;
    SDL_Surface *surface = data;
    *(uint32_t *)(((unsigned char *)surface->pixels) + x * surface->format->BytesPerPixel + y * surface->pitch) = SDL_MapRGB(surface->format, r, g, b);
}

int main(int argc, char *argv[])
{
    (void)argc;
    (void)argv;

    srand(time(NULL));

    atexit(SDL_Quit);

    TRY(SDL_InitSubSystem(SDL_INIT_VIDEO | SDL_INIT_EVENTS));

    SDL_Window *window = NULL;

    TRY(!(window = SDL_CreateWindow(
        "Toy Raytracing",
        SDL_WINDOWPOS_UNDEFINED,
        SDL_WINDOWPOS_UNDEFINED,
        window_width, window_height,
        SDL_WINDOW_RESIZABLE
    )));
    // TODO Deal with Apple's high-DPI stuff.

    SDL_Renderer *renderer = NULL;
    TRY(!(renderer = SDL_CreateRenderer(window, -1, 0)));

    SDL_Surface *surface = NULL;
    TRY(!(surface = SDL_GetWindowSurface(window)));

    for (bool is_playing = true; is_playing; )
    {
        for (SDL_Event event; SDL_PollEvent(&event); )
        {
            if (SDL_QUIT == event.type)
                is_playing = false;
            else if (SDL_KEYDOWN == event.type && 'q' == event.key.keysym.sym)
                TRY(SDL_PushEvent(&(SDL_Event){.type = SDL_QUIT}) < 0);
        else if (window_resize_maybe(event, &window_width, &window_height))
            {
                TRY(!(surface = SDL_GetWindowSurface(window)));
            }
        }

        TRY(SDL_LockSurface(surface));

        #define DESIRED_FRAME 1000 / (float)24
        uint64_t timeout = SDL_GetTicks64() + DESIRED_FRAME;
        while (SDL_GetTicks64() < timeout)
            set_pixel(surface, rand() % window_width, rand() % window_height, rand() % 256, rand() % 256, rand() % 256);

        SDL_UnlockSurface(surface);
        SDL_UpdateWindowSurface(window);
    }

    SDL_DestroyWindow(window);

    SDL_QuitSubSystem(SDL_INIT_VIDEO | SDL_INIT_EVENTS);

    return 0;
}

sorry for not providing minimal reproducible example, I'm tired

sbc100 commented 7 months ago

@Daft-Freak does anything stand out to you as being incorrect here?

Daft-Freak commented 7 months ago

Hmm, SDL_CreateRenderer probably chose the GLES renderer (and created a webgl context), SDL_UpdateWindowSurface would then try to create a 2d context and fail.

As this code doesn't use the renderer, not creating one would avoid this. Requesting the software renderer could also be a workaround.

bottle2 commented 7 months ago

in 6 hours I'll time to come up with a MRE, try the two suggested workarounds and pinpoint which version contains the regression

bottle2 commented 7 months ago

Hmm, SDL_CreateRenderer probably chose the GLES renderer (and created a webgl context), SDL_UpdateWindowSurface would then try to create a 2d context and fail.

According to https://wiki.libsdl.org/SDL2/SDL_GetWindowSurface: "You may not combine this with 3D or the rendering API on this window." Thus I erred on my side by calling SDL_CreateRenderer(). I removed the improper call which is also useless, and the Web build worked just fine! Maybe it should return NULL and appropriately set SDL_GetError() to improve UX. Otherwise, you may close this issue.

Thanks for your herculean efforts.

sbc100 commented 7 months ago

Yup sounds like we could improve here perhaps. I'll leave this open in case somebody would like to try and fix. Although I guess its really an upsteam SDL issue ...