Description
I have ran into another issue while implementing an SDL3 backend for my fork of SFML.
The design of the library involves the creation of a shared GL context at the beginning of main
that allows resources such as textures and shaders to be reused by multiple GL contexts.
With a bespoke per-platform implementation of OpenGL contexts, this approach works well on every platform. Notably, I create a dummy context in this way for Emscripten + OpenGL ES:
EGLNativeWindowType dummyWindow{};
m_surface = eglCreateWindowSurface(m_display, m_config, *static_cast<EGLNativeWindowType*>(windowPtr), nullptr);
constexpr EGLint contextAttribs[]{EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE, EGL_NONE};
eglCreateContext(m_display, m_config, toShared, contextAttribs);
I'm in the process of removing all of these bespoke per-platform implementations with a single portable SDL3-based GL context implementation. Because every SDL3 OpenGL context requires to be associated with a window, I am using a hidden window to create my shared context:
sharedCtxWindow = SDL_CreateWindow("", 1, 1, SDL_WINDOW_OPENGL | SDL_WINDOW_HIDDEN);
This works great on every platform, but fails catastrophically on Emscripten. The hidden window is created successfully, but upon attempting to create the second window, an error like this one is produced in the browser:
Uncaught ErrnoError: File exists
ErrnoError http://localhost:6931/x.js:2007
mknod http://localhost:6931/x.js:2554
mkdir http://localhost:6931/x.js:2600
165439 http://localhost:6931/x.js:860
runMainThreadEmAsm http://localhost:6931/x.js:4098
_emscripten_asm_const_int_sync_on_main_thread http://localhost:6931/x.js:4100
createExportWrapper http://localhost:6931/x.js:677
callMain http://localhost:6931/x.js:9436
doRun http://localhost:6931/x.js:9489
run http://localhost:6931/x.js:9498
[x.js:2007:11](http://localhost:6931/x.js)
Unfortunately, it would be very difficult for me to change the design of the library, which revolves around the idea of a shared context installed at the beginning of main
. At this point I am considering not using SDL3 to create OpenGL contexts and keeping the bespoke implementations, which is a bit of a pain...
I have created a MVCE here:
#include <SDL3/SDL.h>
#include <SDL3/SDL_timer.h>
#include <iostream>
int main()
{
if (!SDL_Init(SDL_INIT_VIDEO))
{
std::cerr << "SDL_Init Error: " << SDL_GetError() << '\n';
return 1;
}
auto sharedCtxWindow = SDL_CreateWindow("", 1, 1, SDL_WINDOW_OPENGL | SDL_WINDOW_HIDDEN);
if (sharedCtxWindow == nullptr)
{
std::cerr << "Failed to create shared context hidden window:" << SDL_GetError() << '\n';
return 1;
}
auto sharedCtx = SDL_GL_CreateContext(sharedCtxWindow);
if (sharedCtx == nullptr)
{
std::cerr << "Failed to create shared GL context:" << SDL_GetError() << '\n';
return 1;
}
auto window = SDL_CreateWindow("Example", 640, 480, SDL_WINDOW_OPENGL);
if (window == nullptr)
{
std::cerr << "Failed to create window:" << SDL_GetError() << '\n';
return 1;
}
auto windowCtx = SDL_GL_CreateContext(window);
if (windowCtx == nullptr)
{
std::cerr << "Failed to create window GL context:" << SDL_GetError() << '\n';
return 1;
}
if (!SDL_GL_MakeCurrent(sharedCtxWindow, sharedCtx))
{
std::cerr << "Failed to activate shared GL context: " << SDL_GetError() << '\n';
return 1;
}
SDL_GL_SetAttribute(SDL_GL_SHARE_WITH_CURRENT_CONTEXT, 1);
if (!SDL_GL_MakeCurrent(window, windowCtx))
{
std::cerr << "Failed to activate window GL context: " << SDL_GetError() << '\n';
return 1;
}
SDL_Event event;
while (true)
{
while (SDL_PollEvent(&event))
{
if (event.type == SDL_EVENT_QUIT)
{
return 0;
}
}
SDL_GL_SwapWindow(window);
SDL_Delay(10);
}
}
To compile and run:
em++ sdlbug.cpp --use-port=sdl3 -o x.html
emrun ./x.html