Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
bottle2 opened this issue Apr 8, 2024 · 5 comments
Open

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

bottle2 opened this issue Apr 8, 2024 · 5 comments

Comments

@bottle2
Copy link

bottle2 commented Apr 8, 2024

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
Copy link
Collaborator

sbc100 commented Apr 8, 2024

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

@Daft-Freak
Copy link
Collaborator

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
Copy link
Author

bottle2 commented Apr 8, 2024

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
Copy link
Author

bottle2 commented Apr 9, 2024

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
Copy link
Collaborator

sbc100 commented Apr 9, 2024

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 ...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants