I want to call OpenGL functions from a render thread. I've got a simple queue of std::function<void()>
The idea is to queue commands from the main thread and when Flush()
is called, notify the render thread and execute commands that were queued before Flush()
struct CommandQueue
{
void Submit(std::function<void()> command)
{
commandQueue.push_back(std::move(command));
}
bool Execute()
{
std::unique_lock<std::mutex> lock(mutex);
cv.wait(lock, [this](){ return !commandsToExecute.empty() || quit; });
auto cmds = std::move(commandsToExecute);
lock.unlock();
if(cmds.empty()) {
return false;
}
for(auto& cmd : cmds) {
cmd();
}
return true;
}
void Flush()
{
{
std::lock_guard<std::mutex> guard(mutex);
commandsToExecute = std::move(commandQueue);
}
cv.notify_one();
}
void Quit()
{
{
std::lock_guard<std::mutex> guard(mutex);
quit = true;
}
cv.notify_one();
}
bool quit = false;
std::mutex mutex;
std::condition_variable cv;
std::vector<std::function<void()>> commandQueue;
std::vector<std::function<void()>> commandsToExecute;
};
OpenGL context is created on the render thread
static void OpenGLLogMessage(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar* message, const void* userParam)
{
if(severity != GL_DEBUG_SEVERITY_NOTIFICATION) {
std::cout << message << std::endl;
}
}
int main(int argc, char* args[])
{
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_DEBUG_FLAG);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 6);
SDL_Window* window = SDL_CreateWindow("Window", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 1600, 900, SDL_WINDOW_SHOWN | SDL_WINDOW_OPENGL);
SDL_GLContext context = nullptr;
CommandQueue queue;
queue.Submit([&](){
context = SDL_GL_CreateContext(window);
gladLoadGLLoader(SDL_GL_GetProcAddress);
glEnable(GL_DEBUG_OUTPUT);
glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
glDebugMessageCallback(OpenGLLogMessage, nullptr);
glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, nullptr, GL_TRUE);
});
std::thread renderThread([&](){
while(true) {
if(!queue.Execute()) {
break;
}
}
});
bool quit = false;
SDL_Event event;
while(!quit) {
while(SDL_PollEvent(&event)) {
if(event.type == SDL_QUIT) {
quit = true;
}
}
//does not cause error
queue.Submit([&](){
SDL_GL_MakeCurrent(window, context);
});
queue.Submit([]() {
//exception thrown
glViewport(0, 0, 1600, 900);
});
queue.Submit([]() {
//exception thrown
glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
});
queue.Submit([]() {
//exception thrown
glClear(GL_COLOR_BUFFER_BIT);
});
//does not cause error
queue.Submit([&](){
SDL_GL_SwapWindow(window);
});
queue.Flush();
}
queue.Flush();
queue.Quit();
renderThread.join();
return 0;
}
I am stuck and I have no idea why it does not work. The error I get:
@Update: I've found out that
queue.Submit([]() {
//exception thrown
glViewport(0, 0, 1600, 900);
});
is executed before
queue.Submit([&](){
context = SDL_GL_CreateContext(window);
gladLoadGLLoader(SDL_GL_GetProcAddress);
glEnable(GL_DEBUG_OUTPUT);
glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
glDebugMessageCallback(OpenGLLogMessage, nullptr);
glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, nullptr, GL_TRUE);
});
and that may cause this error. How, though, can it be executed earlier when the queue goes linearly through the commands? And commands are queued also on one thread, so no shared states exist.
What do I do wrong? How can I fix it?