Return-To-The-Roots / s25client

Return To The Roots (Settlers II(R) Clone)
http://www.rttr.info
GNU General Public License v2.0
479 stars 77 forks source link

Setting framerate target to 0 segfaults in WSL #1687

Open kubaau opened 3 months ago

kubaau commented 3 months ago

TLDR workaround: Manually change ~/.s25rttr/CONFIG.INI section [video] parameter framerate=0 to something other than zero.

Using the Windows Subsystem for Linux and running the game segfaults immediately with a cryptic error:

Starting the game
Loading "/...../s25client/build/share/s25rttr/RTTR/assets/base/splash.bmp": done in 12ms
X Error of failed request:  GLXBadContextTag
  Major opcode of failed request:  148 (GLX)
  Minor opcode of failed request:  16 (X_GLXVendorPrivate)
  Serial number of failed request:  264
  Current serial number in output stream:  265

Running under debugger shows this stack trace:

swrast_dri.so![Unknown/Just-In-Time compiled code] (Unknown Source:0)
libvideoSDL2.so!VideoSDL2::CleanUp(VideoSDL2 * const this) (s25client\extras\videoDrivers\SDL2\VideoSDL2.cpp:94)
libvideoSDL2.so!VideoSDL2::~VideoSDL2(VideoSDL2 * const this) (s25client\extras\videoDrivers\SDL2\VideoSDL2.cpp:66)
libvideoSDL2.so!VideoSDL2::~VideoSDL2(VideoSDL2 * const this) (s25client\extras\videoDrivers\SDL2\VideoSDL2.cpp:67)
libvideoSDL2.so!FreeVideoInstance(IVideoDriver * driver) (s25client\extras\videoDrivers\SDL2\VideoSDL2.cpp:54)
std::__uniq_ptr_impl<IVideoDriver, void (*)(IVideoDriver*)>::reset(std::__uniq_ptr_impl<IVideoDriver, void (*)(IVideoDriver*)> * const this, std::__uniq_ptr_impl<IVideoDriver, void (*)(IVideoDriver*)>::pointer __p) (\usr\include\c++\11\bits\unique_ptr.h:182)
std::unique_ptr<IVideoDriver, void (*)(IVideoDriver*)>::reset(std::unique_ptr<IVideoDriver, void (*)(IVideoDriver*)> * const this, std::unique_ptr<IVideoDriver, void (*)(IVideoDriver*)>::pointer __p) (\usr\include\c++\11\bits\unique_ptr.h:456)
VideoDriverWrapper::UnloadDriver(VideoDriverWrapper * const this) (s25client\libs\s25main\drivers\VideoDriverWrapper.cpp:82)
VideoDriverWrapper::~VideoDriverWrapper(VideoDriverWrapper * const this) (s25client\libs\s25main\drivers\VideoDriverWrapper.cpp:39)
Singleton<VideoDriverWrapper, SingletonPolicies::WithLongevity>::DestroySingleton() (s25client\external\libutil\libs\common\include\s25util\SingletonImp.hpp:52)
SingletonPolicies::LifetimeTracker::~LifetimeTracker(SingletonPolicies::LifetimeTracker * const this) (s25client\external\libutil\libs\common\src\SingletonPolicies.cpp:38)
Singleton<SingletonPolicies::LifetimeTracker, SingletonPolicies::DefaultLifetime>::DestroySingleton() (s25client\external\libutil\libs\common\include\s25util\SingletonImp.hpp:52)
libc.so.6!__run_exit_handlers(int status, struct exit_function_list ** listp, _Bool run_list_atexit, _Bool run_dtors) (exit.c:113)
libc.so.6!__GI_exit(int status) (exit.c:143)
libX11.so.6!_XDefaultError (Unknown Source:0)
libX11.so.6!_XError (Unknown Source:0)
libX11.so.6![Unknown/Just-In-Time compiled code] (Unknown Source:0)
libX11.so.6!_XEventsQueued (Unknown Source:0)
libX11.so.6!XFlush (Unknown Source:0)
libGLX_mesa.so.0![Unknown/Just-In-Time compiled code] (Unknown Source:0)
VideoDriverWrapper::setHwVSync(VideoDriverWrapper * const this, bool enabled) (s25client\libs\s25main\drivers\VideoDriverWrapper.cpp:297)
VideoDriverWrapper::setTargetFramerate(VideoDriverWrapper * const this, int target) (s25client\libs\s25main\drivers\VideoDriverWrapper.cpp:192)
Desktop::Desktop(Desktop * const this, glArchivItem_Bitmap * background) (s25client\libs\s25main\desktops\Desktop.cpp:33)
dskSplash::dskSplash(dskSplash * const this, std::unique_ptr<glArchivItem_Bitmap, std::default_delete<glArchivItem_Bitmap> > splashImg) (s25client\libs\s25main\desktops\dskSplash.cpp:27)
std::make_unique<dskSplash, std::unique_ptr<glArchivItem_Bitmap, std::default_delete<glArchivItem_Bitmap> > >() (\usr\include\c++\11\bits\unique_ptr.h:962)
GameManager::ShowSplashscreen(GameManager * const this) (s25client\libs\s25main\GameManager.cpp:172)
GameManager::Start(GameManager * const this) (s25client\libs\s25main\GameManager.cpp:75)
(anonymous namespace)::InitGame(GameManager & gameManager) (s25client\libs\s25client\s25client.cpp:430)
(anonymous namespace)::RunProgram(boost::program_options::variables_map & options) (s25client\libs\s25client\s25client.cpp:473)
main(int argc, char ** argv) (s25client\libs\s25client\s25client.cpp:567)

The segfault happens because some bad context is passed to SDL_GL_DeleteContext(context);, but what actually causes this segfault is this function:

bool VideoDriverWrapper::setHwVSync(bool enabled)
{
    if(!wglSwapIntervalEXT)
        return false;
    return wglSwapIntervalEXT(enabled ? 1 : 0) != 0;
}

If you manually set framerate != 0 in CONFIG.INI then the game launches fine, but changing the "Limit Framerate" setting in game to "Dynamic" also immediately segfaults in the same location, although now the offending function that results in a bad context is:

void VideoDriverWrapper::ClearScreen()
{
    glClear(GL_COLOR_BUFFER_BIT);
}

In any case, it seems that setting the framerate limit to match hardware refresh rate is not supported in WSL.

I think either:

Since the second option can be quite complicated and the third option may not be feasible, probably the workaround can suffice, but I'm still reporting this for interested parties. Maybe it is also possible to modify some WSL configuration to fix this, but I have no idea.

kubaau commented 3 months ago

Changing glXSwapIntervalSGI to glXSwapIntervalMESA does not crash in WSL but does not achieve the desired effect (or does it?). It seems to act as if no frame limit was set.

Flow86 commented 3 months ago

WSL is some "difficult" terrain here, it might be that the wsl GL driver reports things as working but they arent. disabling vsync all together might work

(e.g see https://stackoverflow.com/questions/26437078/sfml-vsync-always-on)

Flamefire commented 3 months ago

So on WSL we use glXSwapIntervalSGI.

glXSwapIntervalMESA works. The pointer returned for glXSwapIntervalSGI is valid, so SFML can't detect this issue without taking an approach similiar to SDL's.

glXSwapIntervalMESA does not crash in WSL but does not achieve the desired effect

Those seem to contradict. I'm also wondering what it is that SDL does. But doesn't look like we can/should do anything due to "SFML [and hence we] can't detect this issue "

Why running in WSL anyway when there are native builds for Windows? I'd say the use case is rare enough for us to ignore or refer to the workaround.

If we can detect WSL (likely only at runtime) we could not set wglSwapIntervalEXT to not use hardware VSync though