SDL fails to create OpenGL context at seemingly random intervals

Started by
10 comments, last by DrDeath3191 7 years, 1 month ago

Hello everyone,

I was going around and making sure my project would work under all configurations (32/64 bit, Debug/Release) in Visual Studio C++ 2012. Mostly, things seemed to work alright after updating some lib files. The sole exception seems to be 32-bit Release mode.

In that particular configuration, SDL sometimes fails to create an OpenGL context, citing that the handle is invalid. It doesn't even have the decency to fail consistently.

Here's the code in question:


std::condition_variable var;
std::mutex windowMutex;
std::unique_lock<std::mutex> lock(windowMutex);
bool windowFailed = false;

windowThread = std::thread([this, &var, &windowFailed] {

	SDL_Init(SDL_INIT_TIMER | SDL_INIT_EVENTS | SDL_INIT_GAMECONTROLLER 
		| SDL_INIT_JOYSTICK	|SDL_INIT_HAPTIC);
	window = SDL_CreateWindow("Game", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
			1280, 720, SDL_WINDOW_SHOWN | SDL_WINDOW_OPENGL);

	if (window == NULL) {
		windowFailed = true;
		var.notify_one();
		return;
	}
	var.notify_one();

	//this runs an infinite loop of waiting for input
	inputBuffer.update();
});

var.wait(lock, [this, &windowFailed] {
    return window != nullptr || windowFailed;
});
	
glcontext = SDL_GL_CreateContext(window);
if (!glcontext) {
	std::cout<<"OpenGl context creation failed! "<<SDL_GetError()<<std::endl;
}
//... other code not consequential to the topic

The more astute among you may have realized that I am initializing SDL and creating a window in a separate thread. I would prefer to handle input in a separate thread from the logic and rendering, especially since SDL_PollEvents uses far more CPU than SDL_WaitEvents. Input in SDL is required to be handled in the thread which creates the window, which is why it is set up as you see it.

I will note that not using a thread for this purpose seems to fix the issue. I would prefer to avoid this solution if possible, for the reasons stated above.

So, is there a better solution than putting the input handling code back on the main thread, or will I have to buck up and do it?

Advertisement

...is it failing with a valid window being passed into SDL_GL_CreateContext?

...is it failing with a valid window being passed into SDL_GL_CreateContext?

The window does appear to be valid, or at the very least not null.

There's a long history of issues with OpenGL and multithreading. While generally it is supported, it requires special actions to make it work right.

Simply start without separate threads for rendering. Run rendering in same thread that creates window and hosts app's main loop. Move only heavy processing tasks into threads.

Did you just ran thread to init sdl and create window? That's really weird.

He's likely doing so as WaitEvent must be called from the same thread as the video was initialized on. I couldn't find official documentation on it that quickly, but I'm fairly certain the GL context must be created on the same thread as the window as well as other GL functions. You could do game logic on the separate thread and do rendering and input on the main thread in that way, but that's far from ideal.

In what sense are you seeing high CPU usage from PollEvent actually? I'm not really sure how you're measuring the difference either, since WaitEvent is a fully blocking call. I also can't imagine the CPU usage of PollEvent being of actual significance to frametime.

I'd recommend seeing if you can solve the issue without threads, but to directly answer your code question, it looks like you're not following the instructions on std::condition_variable (http://en.cppreference.com/w/cpp/thread/condition_variable)

The thread that intends to modify the variable has to

* acquire a std::mutex (typically via std::lock_guard)
* perform the modification while the lock is held
* execute notify_one or notify_all on the std::condition_variable (the lock does not need to be held for notification)

Even if the shared variable is atomic, it must be modified under the mutex in order to correctly publish the modification to the waiting thread.

Sorry for the late reply; Breath of the Wild called to me.

Did you just ran thread to init sdl and create window? That's really weird.

SDL requires than input is handled on the same thread that initialized SDL_Video and created the window, thus if I want input on another thread, I need to initialize and create a window on that thread.

In what sense are you seeing high CPU usage from PollEvent actually? I'm not really sure how you're measuring the difference either, since WaitEvent is a fully blocking call. I also can't imagine the CPU usage of PollEvent being of actual significance to frametime.

I will admit that I have not actually measured; however, I have seen it written time and again that PollEvents results in high CPU usage. While that might not affect frame-time, it could affect someone's battery life if playing on something other than a desktop. I figured that I would attempt to create the most efficient option possible.

Taking into account the responses, I decided to nix the input thread concept; it seemed to work for the most part, but its proving to be more hassle than its worth.

SDL requires than input is handled on the same thread that initialized SDL_Video and created the window, thus if I want input on another thread, I need to initialize and create a window on that thread.

Well, OpenGL requires that every call you make to it be done on the same thread.

Oh, it's not a de jure requirement but if you're familiar with some of the underlying driver code from certain hardware manufacturers, you would see that it's definitely a de facto requirement.

Stephen M. Webb
Professional Free Software Developer

I will admit that I have not actually measured;

Blasphemy!

I have seen it written time and again that PollEvents results in high CPU usage.

I did a little search on this, but I mostly see people just spinning in a loop, only doing PollEvent on every iteration. Yes, this will cause 100% CPU usage and all used by PollEvent, because, well, it's the only thing you're doing. In contrast, you will likely be polling your input just once a frame after/before doing everything else. You just do a while loop to poll all the input (if there is any) and continue to the next frame. I can't imagine there being another cause either. I've been using the exact same thing for years, but I can't recall ever having it even seen in my profiler (i.e. sorted by usage, so very very low on the list).

Just don't wait for input, just check if there is any and handle that if there is. That's also why WaitEvent is generally not that useful for a game, along with having to have the input handled on the same thread as the video driver be initialized.

But it seems you have also concluded yourself how this becomes problematic.

I think the point about battery life is moot, as the issue in question is whether your game needs to continuously update itself as time passes (e.g. Tetris, Doom, ...) or if it only updates due to player input (e.g. chess). Even in the latter case, you might have animations you want to run when the user is thinking.

Moving input processing to an intensive background thread doesn't improve the battery life situation. It perhaps makes it worse as now you're potentially saturating multiple cores, ones which could be put into a low power state if they were otherwise idle.

This topic is closed to new replies.

Advertisement