lemmn
2016-09-19 16:35:34 UTC
I am using Windows 10 and visual studio 2015, and SDL 2.0.4.
I believe this is a minimal repro case.. I had to cut out a lot of code to get down to this, so I'm sorry if there is extra code here.
This program just draws a square on the screen and then should quit when you press the space bar.
Repro steps:
- run the program
- press alt-tab while the game is running
- notice the game program locking up
The bug happens when I hit alt-tab. If I do that, then the game becomes locked up at the call to PeekMessage() inside of WIN_PumpEvents(), with this callstack if I break:
Code:
[External Code]
SDL2.dll!SDL_WaitEventTimeout_REAL(SDL_Event * event, int timeout) Line 437 C
SDL2.dll!SDL_PollEvent_REAL(SDL_Event * event) Line 419 C
SDL2.dll!SDL_PollEvent(SDL_Event * a) Line 157 C
MyMusic.exe!SDL_main(int argc, char * * argv) Line 154 C++
MyMusic.exe!main_utf8(int argc, char * * argv) Line 127 C
MyMusic.exe!WinMain(HINSTANCE__ * hInst, HINSTANCE__ * hPrev, char * szCmdLine, int sw) Line 191 C
[External Code]
And the render thread is running happily.
This bug only happens in full screen. If I remove this code:
flags |= SDL_WINDOW_FULLSCREEN;
then the game runs fine, and I do not get any freeze on using alt-tab.
I have a feeling that the render thread might be the problem, but I must be able to render in a separate thread for this game. I followed this guide and everything seems to be working fine, apart from this freeze
https://github.com/vheuken/SDL-Render-Thread-Example/blob/master/main.cpp
Code:
#include "stdafx.h"
#include <vector>
#include <map>
#include <fstream>
#include <memory>
#include <limits>
#include <iomanip>
#include <thread>
#include <Windows.h>
#include <SDL.h>
// singleton to hold a bunch of global game state stuff
class Game
{
public:
Game()
{}
SDL_Rect m_windowedRect;
void SetUpScreen();
void TearDownScreen();
void StartRenderer();
void KillRenderer();
static Game& Get();
// window and screen
SDL_Window* m_Window;
SDL_GLContext m_Context;
bool m_QuitGame;
SDL_Renderer* m_Renderer = nullptr;
std::thread* m_RenderThread = nullptr;
};
static Game g_Game;
Game& Game::Get()
{
return g_Game;
}
void Game::SetUpScreen()
{
// make a sane default window position and size
if ( 0 == SDL_GetDisplayBounds( 0, &m_windowedRect ) )
{
m_windowedRect.x = m_windowedRect.w * 0.05f;
m_windowedRect.y = m_windowedRect.h * 0.05f;
m_windowedRect.w = m_windowedRect.w * 0.9f;
m_windowedRect.h = m_windowedRect.h * 0.9f;
}
else
{
m_windowedRect = { 10, 10, 1024, 768 };
}
int32_t flags = SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE;
flags |= SDL_WINDOW_FULLSCREEN;
SDL_Rect resolution;
resolution = m_windowedRect;
m_Window = SDL_CreateWindow( "hi friend", resolution.x, resolution.y, resolution.w, resolution.h, flags );
}
void Game::TearDownScreen()
{
SDL_DestroyWindow( m_Window );
m_Window = NULL;
}
void RenderThread();
void Game::StartRenderer()
{
m_RenderThread = new std::thread( RenderThread );
}
static bool stopRenderer;
void Game::KillRenderer()
{
stopRenderer = true;
m_RenderThread->join();
delete m_RenderThread;
m_RenderThread = nullptr;
}
void RenderThread()
{
SDL_GL_MakeCurrent( g_Game.m_Window, g_Game.m_Context ); // not sure what this does - it was happening in the example
g_Game.m_Renderer = SDL_CreateRenderer( g_Game.m_Window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC );
SDL_SetRenderDrawBlendMode( g_Game.m_Renderer, SDL_BLENDMODE_BLEND );
while( 1 )
{
if ( stopRenderer )
{
goto quit;
}
SDL_Rect rect{ 10, 10, 10, 10 };
SDL_SetRenderDrawColor( g_Game.m_Renderer, 0xff, 0xff, 0xff, 0xff );
SDL_RenderFillRect( g_Game.m_Renderer, &rect );
SDL_RenderPresent( g_Game.m_Renderer );
}
quit:
// shut down
SDL_DestroyRenderer( g_Game.m_Renderer );
g_Game.m_Renderer = NULL;
}
int main( int argc, char* argv[] )
{
SDL_Init( SDL_INIT_AUDIO | SDL_INIT_VIDEO );
SDL_SetHint( SDL_HINT_RENDER_SCALE_QUALITY, "linear" );
g_Game.SetUpScreen();
// not entirely sure this is needed but it was happening before creating the render thread in example
g_Game.m_Context = SDL_GL_GetCurrentContext();
SDL_GL_MakeCurrent( g_Game.m_Window, nullptr );
g_Game.StartRenderer();
// main loopy thing
while ( 1 )
{
if ( g_Game.m_QuitGame )
{
break;
}
SDL_Event e;
while ( SDL_PollEvent( &e ) )
{
if ( e.type == SDL_KEYDOWN )
{
if ( e.key.keysym.sym == SDLK_SPACE )
{
g_Game.m_QuitGame = true;
}
}
}
Sleep( 10 );
}
g_Game.KillRenderer();
g_Game.TearDownScreen();
SDL_Quit();
return 0;
}
I believe this is a minimal repro case.. I had to cut out a lot of code to get down to this, so I'm sorry if there is extra code here.
This program just draws a square on the screen and then should quit when you press the space bar.
Repro steps:
- run the program
- press alt-tab while the game is running
- notice the game program locking up
The bug happens when I hit alt-tab. If I do that, then the game becomes locked up at the call to PeekMessage() inside of WIN_PumpEvents(), with this callstack if I break:
Code:
[External Code]
SDL2.dll!WIN_PumpEvents(SDL_VideoDevice * _this) Line 977 C
SDL2.dll!SDL_PumpEvents_REAL() Line 405 CSDL2.dll!SDL_WaitEventTimeout_REAL(SDL_Event * event, int timeout) Line 437 C
SDL2.dll!SDL_PollEvent_REAL(SDL_Event * event) Line 419 C
SDL2.dll!SDL_PollEvent(SDL_Event * a) Line 157 C
MyMusic.exe!SDL_main(int argc, char * * argv) Line 154 C++
MyMusic.exe!main_utf8(int argc, char * * argv) Line 127 C
MyMusic.exe!WinMain(HINSTANCE__ * hInst, HINSTANCE__ * hPrev, char * szCmdLine, int sw) Line 191 C
[External Code]
And the render thread is running happily.
This bug only happens in full screen. If I remove this code:
flags |= SDL_WINDOW_FULLSCREEN;
then the game runs fine, and I do not get any freeze on using alt-tab.
I have a feeling that the render thread might be the problem, but I must be able to render in a separate thread for this game. I followed this guide and everything seems to be working fine, apart from this freeze
https://github.com/vheuken/SDL-Render-Thread-Example/blob/master/main.cpp
Code:
#include "stdafx.h"
#include <vector>
#include <map>
#include <fstream>
#include <memory>
#include <limits>
#include <iomanip>
#include <thread>
#include <Windows.h>
#include <SDL.h>
// singleton to hold a bunch of global game state stuff
class Game
{
public:
Game()
{}
SDL_Rect m_windowedRect;
void SetUpScreen();
void TearDownScreen();
void StartRenderer();
void KillRenderer();
static Game& Get();
// window and screen
SDL_Window* m_Window;
SDL_GLContext m_Context;
bool m_QuitGame;
SDL_Renderer* m_Renderer = nullptr;
std::thread* m_RenderThread = nullptr;
};
static Game g_Game;
Game& Game::Get()
{
return g_Game;
}
void Game::SetUpScreen()
{
// make a sane default window position and size
if ( 0 == SDL_GetDisplayBounds( 0, &m_windowedRect ) )
{
m_windowedRect.x = m_windowedRect.w * 0.05f;
m_windowedRect.y = m_windowedRect.h * 0.05f;
m_windowedRect.w = m_windowedRect.w * 0.9f;
m_windowedRect.h = m_windowedRect.h * 0.9f;
}
else
{
m_windowedRect = { 10, 10, 1024, 768 };
}
int32_t flags = SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE;
flags |= SDL_WINDOW_FULLSCREEN;
SDL_Rect resolution;
resolution = m_windowedRect;
m_Window = SDL_CreateWindow( "hi friend", resolution.x, resolution.y, resolution.w, resolution.h, flags );
}
void Game::TearDownScreen()
{
SDL_DestroyWindow( m_Window );
m_Window = NULL;
}
void RenderThread();
void Game::StartRenderer()
{
m_RenderThread = new std::thread( RenderThread );
}
static bool stopRenderer;
void Game::KillRenderer()
{
stopRenderer = true;
m_RenderThread->join();
delete m_RenderThread;
m_RenderThread = nullptr;
}
void RenderThread()
{
SDL_GL_MakeCurrent( g_Game.m_Window, g_Game.m_Context ); // not sure what this does - it was happening in the example
g_Game.m_Renderer = SDL_CreateRenderer( g_Game.m_Window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC );
SDL_SetRenderDrawBlendMode( g_Game.m_Renderer, SDL_BLENDMODE_BLEND );
while( 1 )
{
if ( stopRenderer )
{
goto quit;
}
SDL_Rect rect{ 10, 10, 10, 10 };
SDL_SetRenderDrawColor( g_Game.m_Renderer, 0xff, 0xff, 0xff, 0xff );
SDL_RenderFillRect( g_Game.m_Renderer, &rect );
SDL_RenderPresent( g_Game.m_Renderer );
}
quit:
// shut down
SDL_DestroyRenderer( g_Game.m_Renderer );
g_Game.m_Renderer = NULL;
}
int main( int argc, char* argv[] )
{
SDL_Init( SDL_INIT_AUDIO | SDL_INIT_VIDEO );
SDL_SetHint( SDL_HINT_RENDER_SCALE_QUALITY, "linear" );
g_Game.SetUpScreen();
// not entirely sure this is needed but it was happening before creating the render thread in example
g_Game.m_Context = SDL_GL_GetCurrentContext();
SDL_GL_MakeCurrent( g_Game.m_Window, nullptr );
g_Game.StartRenderer();
// main loopy thing
while ( 1 )
{
if ( g_Game.m_QuitGame )
{
break;
}
SDL_Event e;
while ( SDL_PollEvent( &e ) )
{
if ( e.type == SDL_KEYDOWN )
{
if ( e.key.keysym.sym == SDLK_SPACE )
{
g_Game.m_QuitGame = true;
}
}
}
Sleep( 10 );
}
g_Game.KillRenderer();
g_Game.TearDownScreen();
SDL_Quit();
return 0;
}