emscripten-core / emscripten-core/emscripten
Black screen using SDL2 OpenGL ES 3, problem with strings
- Dominant language
- C++
- Stars
- 27.6k
- Forks
- 3.6k
- Avg merge
- 1d 1h
- Merged PRs (30d)
- 105
Description
I have already posted about this issue in the Discord and in the mailing list to no avail so far. It's frustrating because my project takes 5-10 minutes to compile and so only being able to debug this via trial and error it is a considerable waste of time for me to try to deal with this on my own. As a result, I've already spent days (if not weeks) on what should be a simple issue that someone who has experience with web assembly should be able to resolve relatively quickly. That said, no one in those other places has managed to figure it out so I'm posting here for additional help.
I have been stuck on a very simple issue for a couple weeks and need someone who knows what they are doing. I'm trying to use SDL2 and WebGL2 (OpenGL ES 3) for a C++ game, and sometimes it works, but sometimes it doesn't, depending on seemingly arbitrary code. Please help.
compile command:
`em++ -D EMSCRIPTEN main.cpp -o game.html -s NO_EXIT_RUNTIME=1 -s OFFSCREEN_FRAMEBUFFER=1 -s USE_WEBGL2=1 -s FULL_ES3=1 -s USE_SDL_IMAGE=2 -s SDL2_IMAGE_FORMATS=["png"] -s EXPORTED_RUNTIME_METHODS="['cwrap', 'ccall']" -s USE_SDL=2 -s USE_SDL_MIXER=2 -s USE_SDL_TTF=2 -s ALLOW_MEMORY_GROWTH=1 --std=c++17`
run command:
```
start chrome http://localhost:8000/game.html
python -m http.server
```
main.cpp:
```c++
#include
#include
#include
#include
#ifdef EMSCRIPTEN
#include
#include
#include
#else
#include
#endif
SDL_Window* window;
SDL_GLContext mainContext;
void Log(const std::string& message)
{
// If you DON'T comment out this line, the screen will NOT go black,
// but you will receive this error:
// Could not create EGL context (context attributes are not supported)
// SDL_GL_SetSwapInterval failed. No OpenGL context has been made current
//std::cout << message << std::endl;
}
void InitOpenGL(const int screenWidth, const int screenHeight, SDL_Window* window, SDL_GLContext& mainContext)
{
SDL_Init( SDL_INIT_VIDEO );
TTF_Init();
std::cout << "Init OpenGL" << std::endl;
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2);
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
window = SDL_CreateWindow("...", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, screenWidth, screenHeight, SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN);
// If you comment out the below line, the screen will go black
Log("" + std::string(SDL_GetError()) + std::string(SDL_GetError()));
EmscriptenWebGLContextAttributes attrs;
attrs.antialias = true;
attrs.majorVersion = 3;
attrs.minorVersion = 2;
attrs.alpha = true;
EMSCRIPTEN_WEBGL_CONTEXT_HANDLE webgl_context = emscripten_webgl_create_context("#canvas", &attrs);
emscripten_webgl_make_context_current(webgl_context);
mainContext = SDL_GL_CreateContext(window);
Log("" + std::string(SDL_GetError()));
std::cout << "GL_VERSION: " << glGetString(GL_VERSION) << std::endl;
if (SDL_GL_SetSwapInterval(1) != 0)
{
Log("ERROR: SDL_GL_SetSwapInterval failed. " + std::string(SDL_GetError()));
}
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LESS);
glDepthMask(false);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glViewport(0, 0, screenWidth, screenHeight);
glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
SDL_GL_SwapWindow(window);
std::cout << "End of OpenGL init" << std::endl;
}
void Render()
{
static bool showMessage = false;
if (!showMessage)
{
std::cout << "...Rendering..." << std::endl;
std::cout << "GL_VERSION: " << glGetString(GL_VERSION) << std::endl;
showMessage = true;
}
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glClearColor(1.0f, 0.1f, 1.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glUseProgram(0);
SDL_GL_SwapWindow(window);
}
void MainLoop()
{
Render();
}
void BeforeMainLoop()
{
std::cout << "Attempting to set and enter the main loop..." << std::endl;
emscripten_set_main_loop((em_callback_func)MainLoop, 0, 0);
}
int main()
{
InitOpenGL(1280, 720, window, mainContext);
BeforeMainLoop();
return 1;
}
```
Basically, I have a simple program (just over 100 lines of code) that changes the background color of the screen by calling glClear(). Probably the most basic OpenGL program one can write. When the color is pink, that means it's working, and when it's black, it's not working. In the above link there is main.cpp which works correctly. I have been doing trial and error for this past week commenting out random lines of code to isolate the problem, but it seems like changing arbitrary lines of code stops it from working. I've noticed that it happens when calling some SDL functions, but even just commenting out printing to the console is enough to break it. See main.cpp for details.
I don't know if this is just an issue with me not understanding how this works or if this is an actual bug in emscripten or the SDL port. it's frustrating because when it works, it works all the way (I can use shaders and textures to have a working app), but if I change just a tiny little thing (even just commenting out an unrelated line of code), suddenly nothing works at all. I have no idea where to even start debugging this.
So I know this is getting long, but I'm very confused. Since technically my code works, but it's one step away from breaking, and I have no idea what that step is or why. My ultimate goal is to copy and paste this code into a larger project, but every time I do so, it results in a black screen with no indication of what I did wrong.
I would imagine that there has to be a way to do this since people are obviously making WebGL games in C++, but since there are no tutorials online for how to do that it becomes impossible to even set up a simple "hello world" script. I purchased a book on how to make games in web assembly but they use the SDL renderer and not OpenGL rendering. Other OpenGL web assembly tutorials just use OpenGL without SDL. Surely there must be a way to use them together as I have done for my desktop app? Is this an issue with my code or build commands, or is it a problem with emscripten or the SDL port? My code is pretty minimal so I'm leaning towards the latter, but unfortunately if that's the case then that means I can only wait around for someone to fix it, which isn't ideal. I would rather it be something wrong on my end so I can just fix it myself and move on. I would really like to know that my game can run on the web using emscripten, and it would be disappointing if it couldn't.
Recently I've discovered more causes for the black screen and wonder if maybe the issue is related to strings in some way. If I add a class that takes a string as a parameter in the constructor, and then ONLY if I instantiate it, like so:
```c++
class Test
{
public:
Test(std::string test) {} ;
};
int main()
{
Test test("test");
InitOpenGL(1280, 720, window, mainContext);
BeforeMainLoop();
return 1;
}
```
Then that also results in a black screen. Instantiating a vector of strings also results in a black screen, like so:
`std::vector languages = { "english", "japanese" };`
And my original problem with the Log function also happens to involve strings on some level. If I change the constructor from a string to a const char* then it works. But changing all instances of strings to const char* is not ideal (my program needs strings), and I actually tried that with the Log function and that did not help, so I'm still looking for a solution. So hopefully this information is helpful for someone to figure out what is wrong and how to fix it.
Contributor guide
Assessment
This issue has not been assessed yet.