1
0
forked from 0ad/0ad

Refactors window creation in CVideoMode.

Comments By: phosit, Stan
Differential Revision: https://code.wildfiregames.com/D4866
This was SVN commit r27388.
This commit is contained in:
Vladislav Belov 2023-01-09 00:13:52 +00:00
parent 408c93744c
commit fc7c78b8cd
2 changed files with 187 additions and 91 deletions

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2022 Wildfire Games. /* Copyright (C) 2023 Wildfire Games.
* This file is part of 0 A.D. * This file is part of 0 A.D.
* *
* 0 A.D. is free software: you can redistribute it and/or modify * 0 A.D. is free software: you can redistribute it and/or modify
@ -23,6 +23,7 @@
#include "gui/GUIManager.h" #include "gui/GUIManager.h"
#include "lib/config2.h" #include "lib/config2.h"
#include "lib/external_libraries/libsdl.h" #include "lib/external_libraries/libsdl.h"
#include "lib/sysdep/os.h"
#include "lib/tex/tex.h" #include "lib/tex/tex.h"
#include "ps/CConsole.h" #include "ps/CConsole.h"
#include "ps/CLogger.h" #include "ps/CLogger.h"
@ -35,8 +36,11 @@
#include "renderer/backend/dummy/DeviceForward.h" #include "renderer/backend/dummy/DeviceForward.h"
#include "renderer/backend/gl/DeviceForward.h" #include "renderer/backend/gl/DeviceForward.h"
#include "renderer/backend/IDevice.h" #include "renderer/backend/IDevice.h"
#include "renderer/backend/vulkan/DeviceForward.h"
#include "renderer/Renderer.h" #include "renderer/Renderer.h"
#include <string_view>
namespace namespace
{ {
@ -48,6 +52,49 @@ int DEFAULT_FULLSCREEN_H = 768;
const wchar_t DEFAULT_CURSOR_NAME[] = L"default-arrow"; const wchar_t DEFAULT_CURSOR_NAME[] = L"default-arrow";
Renderer::Backend::Backend GetFallbackBackend(const Renderer::Backend::Backend backend)
{
Renderer::Backend::Backend fallback = Renderer::Backend::Backend::DUMMY;
// We use a switch instead of a list to have compile-time checks for missed
// values and because a linear priority list doesn't work for general case.
switch (backend)
{
case Renderer::Backend::Backend::GL:
fallback = Renderer::Backend::Backend::GL_ARB;
break;
case Renderer::Backend::Backend::GL_ARB:
fallback = Renderer::Backend::Backend::DUMMY;
break;
case Renderer::Backend::Backend::DUMMY:
break;
case Renderer::Backend::Backend::VULKAN:
fallback = Renderer::Backend::Backend::GL;
break;
}
return fallback;
}
std::string_view GetBackendName(const Renderer::Backend::Backend backend)
{
std::string_view name{"Unknown"};
switch (backend)
{
case Renderer::Backend::Backend::GL:
name = "GL";
break;
case Renderer::Backend::Backend::GL_ARB:
name = "GL ARB";
break;
case Renderer::Backend::Backend::DUMMY:
name = "Dummy";
break;
case Renderer::Backend::Backend::VULKAN:
name = "Vulkan";
break;
}
return name;
}
} // anonymous namespace } // anonymous namespace
#if OS_WIN #if OS_WIN
@ -239,8 +286,15 @@ void CVideoMode::ReadConfig()
m_Backend = Renderer::Backend::Backend::GL_ARB; m_Backend = Renderer::Backend::Backend::GL_ARB;
else if (rendererBackend == "dummy") else if (rendererBackend == "dummy")
m_Backend = Renderer::Backend::Backend::DUMMY; m_Backend = Renderer::Backend::Backend::DUMMY;
else if (rendererBackend == "vulkan")
m_Backend = Renderer::Backend::Backend::VULKAN;
else else
m_Backend = Renderer::Backend::Backend::GL; m_Backend = Renderer::Backend::Backend::GL;
#if OS_WIN
if (m_ConfigEnableHiDPI)
wutil_EnableHiDPIOnWindows();
#endif
} }
bool CVideoMode::SetVideoMode(int w, int h, int bpp, bool fullscreen) bool CVideoMode::SetVideoMode(int w, int h, int bpp, bool fullscreen)
@ -262,14 +316,79 @@ bool CVideoMode::SetVideoMode(int w, int h, int bpp, bool fullscreen)
if (!m_Window) if (!m_Window)
{ {
const bool isGLBackend =
m_Backend == Renderer::Backend::Backend::GL ||
m_Backend == Renderer::Backend::Backend::GL_ARB;
if (isGLBackend)
{
SDL_GL_SetAttribute(SDL_GL_ACCELERATED_VISUAL, 1);
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8);
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
bool debugContext = false;
CFG_GET_VAL("renderer.backend.debugcontext", debugContext);
if (debugContext)
SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_DEBUG_FLAG);
bool forceGLVersion = false;
CFG_GET_VAL("forceglversion", forceGLVersion);
if (forceGLVersion)
{
CStr forceGLProfile = "compatibility";
int forceGLMajorVersion = 3;
int forceGLMinorVersion = 0;
CFG_GET_VAL("forceglprofile", forceGLProfile);
CFG_GET_VAL("forceglmajorversion", forceGLMajorVersion);
CFG_GET_VAL("forceglminorversion", forceGLMinorVersion);
int profile = SDL_GL_CONTEXT_PROFILE_COMPATIBILITY;
if (forceGLProfile == "es")
profile = SDL_GL_CONTEXT_PROFILE_ES;
else if (forceGLProfile == "core")
profile = SDL_GL_CONTEXT_PROFILE_CORE;
else if (forceGLProfile != "compatibility")
LOGWARNING("Unknown force GL profile '%s', compatibility profile is used", forceGLProfile.c_str());
if (forceGLMajorVersion < 1 || forceGLMinorVersion < 0)
{
LOGERROR("Unsupported force GL version: %d.%d", forceGLMajorVersion, forceGLMinorVersion);
}
else
{
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, profile);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, forceGLMajorVersion);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, forceGLMinorVersion);
}
}
else
{
#if CONFIG2_GLES
// Require GLES 2.0
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
#else
// Some macOS and MESA drivers might not create a context even if they can
// with the core profile. So disable it for a while until we can guarantee
// its creation.
#if OS_WIN #if OS_WIN
if (m_ConfigEnableHiDPI) SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
wutil_EnableHiDPIOnWindows();
#endif #endif
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1);
#endif
}
}
// Note: these flags only take affect in SDL_CreateWindow // Note: these flags only take affect in SDL_CreateWindow
flags |= SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE; flags |= SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE;
if (m_ConfigEnableHiDPI) if (m_ConfigEnableHiDPI)
flags |= SDL_WINDOW_ALLOW_HIGHDPI; flags |= SDL_WINDOW_ALLOW_HIGHDPI;
if (isGLBackend)
flags |= SDL_WINDOW_OPENGL;
else if (m_Backend == Renderer::Backend::Backend::VULKAN)
flags |= SDL_WINDOW_VULKAN;
m_WindowedX = m_WindowedY = SDL_WINDOWPOS_CENTERED_DISPLAY(m_ConfigDisplay); m_WindowedX = m_WindowedY = SDL_WINDOWPOS_CENTERED_DISPLAY(m_ConfigDisplay);
m_Window = SDL_CreateWindow(main_window_name, m_WindowedX, m_WindowedY, w, h, flags); m_Window = SDL_CreateWindow(main_window_name, m_WindowedX, m_WindowedY, w, h, flags);
@ -287,6 +406,20 @@ bool CVideoMode::SetVideoMode(int w, int h, int bpp, bool fullscreen)
} }
else else
{ {
if (isGLBackend)
{
int depthSize = 24;
SDL_GL_GetAttribute(SDL_GL_DEPTH_SIZE, &depthSize);
if (depthSize > 16)
{
// Fall back to a smaller depth buffer
// (The rendering may be ugly but this helps when running in VMware)
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 16);
return SetVideoMode(w, h, bpp, fullscreen);
}
}
LOGERROR("SetVideoMode failed in SDL_CreateWindow: %dx%d:%d %d (\"%s\")", LOGERROR("SetVideoMode failed in SDL_CreateWindow: %dx%d:%d %d (\"%s\")",
w, h, bpp, fullscreen ? 1 : 0, SDL_GetError()); w, h, bpp, fullscreen ? 1 : 0, SDL_GetError());
return false; return false;
@ -305,12 +438,16 @@ bool CVideoMode::SetVideoMode(int w, int h, int bpp, bool fullscreen)
wutil_SetAppWindow(m_Window); wutil_SetAppWindow(m_Window);
#endif #endif
if (!CreateBackendDevice(true)) if (!TryCreateBackendDevice(m_Window))
{ {
LOGERROR("SetVideoMode failed in backend device creation: %dx%d:%d %d", DowngradeBackendSettingAfterCreationFailure();
w, h, bpp, fullscreen ? 1 : 0); SDL_DestroyWindow(m_Window);
return false; m_Window = nullptr;
return SetVideoMode(w, h, bpp, fullscreen);
} }
if (isGLBackend)
SDL_GL_SetSwapInterval(m_ConfigVSync ? 1 : 0);
} }
else else
{ {
@ -403,78 +540,9 @@ bool CVideoMode::InitSDL()
h = std::min(h, m_PreferredH); h = std::min(h, m_PreferredH);
} }
int bpp = GetBestBPP(); const int bpp = GetBestBPP();
SDL_GL_SetAttribute(SDL_GL_ACCELERATED_VISUAL, 1);
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8);
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
bool debugContext = false;
CFG_GET_VAL("renderer.backend.debugcontext", debugContext);
if (debugContext)
SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_DEBUG_FLAG);
bool forceGLVersion = false;
CFG_GET_VAL("forceglversion", forceGLVersion);
if (forceGLVersion)
{
CStr forceGLProfile = "compatibility";
int forceGLMajorVersion = 3;
int forceGLMinorVersion = 0;
CFG_GET_VAL("forceglprofile", forceGLProfile);
CFG_GET_VAL("forceglmajorversion", forceGLMajorVersion);
CFG_GET_VAL("forceglminorversion", forceGLMinorVersion);
int profile = SDL_GL_CONTEXT_PROFILE_COMPATIBILITY;
if (forceGLProfile == "es")
profile = SDL_GL_CONTEXT_PROFILE_ES;
else if (forceGLProfile == "core")
profile = SDL_GL_CONTEXT_PROFILE_CORE;
else if (forceGLProfile != "compatibility")
LOGWARNING("Unknown force GL profile '%s', compatibility profile is used", forceGLProfile.c_str());
if (forceGLMajorVersion < 1 || forceGLMinorVersion < 0)
{
LOGERROR("Unsupported force GL version: %d.%d", forceGLMajorVersion, forceGLMinorVersion);
}
else
{
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, profile);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, forceGLMajorVersion);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, forceGLMinorVersion);
}
}
else
{
#if CONFIG2_GLES
// Require GLES 2.0
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
#else
// Some macOS and MESA drivers might not create a context even if they can
// with the core profile. So disable it for a while until we can guarantee
// its creation.
#if OS_WIN
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
#endif
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1);
#endif
}
if (!SetVideoMode(w, h, bpp, m_ConfigFullscreen)) if (!SetVideoMode(w, h, bpp, m_ConfigFullscreen))
{ return false;
// Fall back to a smaller depth buffer
// (The rendering may be ugly but this helps when running in VMware)
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 16);
if (!SetVideoMode(w, h, bpp, m_ConfigFullscreen))
return false;
}
SDL_GL_SetSwapInterval(m_ConfigVSync ? 1 : 0);
// Work around a bug in the proprietary Linux ATI driver (at least versions 8.16.20 and 8.14.13). // Work around a bug in the proprietary Linux ATI driver (at least versions 8.16.20 and 8.14.13).
// The driver appears to register its own atexit hook on context creation. // The driver appears to register its own atexit hook on context creation.
@ -530,20 +598,45 @@ void CVideoMode::Shutdown()
bool CVideoMode::CreateBackendDevice(const bool createSDLContext) bool CVideoMode::CreateBackendDevice(const bool createSDLContext)
{ {
if (m_Backend == Renderer::Backend::Backend::DUMMY) if (!createSDLContext && m_Backend == Renderer::Backend::Backend::VULKAN)
m_Backend = Renderer::Backend::Backend::GL;
SDL_Window* window = createSDLContext ? m_Window : nullptr;
while (m_Backend != Renderer::Backend::Backend::DUMMY)
{ {
m_BackendDevice = Renderer::Backend::Dummy::CreateDevice(m_Window); if (TryCreateBackendDevice(window))
return true;
DowngradeBackendSettingAfterCreationFailure();
}
return TryCreateBackendDevice(window);
}
bool CVideoMode::TryCreateBackendDevice(SDL_Window* window)
{
switch (m_Backend)
{
case Renderer::Backend::Backend::GL:
m_BackendDevice = Renderer::Backend::GL::CreateDevice(window, false);
break;
case Renderer::Backend::Backend::GL_ARB:
m_BackendDevice = Renderer::Backend::GL::CreateDevice(window, true);
break;
case Renderer::Backend::Backend::DUMMY:
m_BackendDevice = Renderer::Backend::Dummy::CreateDevice(window);
ENSURE(m_BackendDevice); ENSURE(m_BackendDevice);
return true; break;
case Renderer::Backend::Backend::VULKAN:
m_BackendDevice = Renderer::Backend::Vulkan::CreateDevice(window);
break;
} }
m_BackendDevice = Renderer::Backend::GL::CreateDevice(createSDLContext ? m_Window : nullptr, m_Backend == Renderer::Backend::Backend::GL_ARB); return static_cast<bool>(m_BackendDevice);
if (!m_BackendDevice && m_Backend == Renderer::Backend::Backend::GL) }
{
LOGERROR("Unable to create device for GL backend, switching to ARB."); void CVideoMode::DowngradeBackendSettingAfterCreationFailure()
m_Backend = Renderer::Backend::Backend::GL_ARB; {
return CreateBackendDevice(createSDLContext); const Renderer::Backend::Backend fallback = GetFallbackBackend(m_Backend);
} LOGERROR("Unable to create device for %s backend, switching to %s.",
return !!m_BackendDevice; GetBackendName(m_Backend), GetBackendName(fallback));
m_Backend = fallback;
} }
bool CVideoMode::ResizeWindow(int w, int h) bool CVideoMode::ResizeWindow(int w, int h)

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2022 Wildfire Games. /* Copyright (C) 2023 Wildfire Games.
* This file is part of 0 A.D. * This file is part of 0 A.D.
* *
* 0 A.D. is free software: you can redistribute it and/or modify * 0 A.D. is free software: you can redistribute it and/or modify
@ -125,6 +125,9 @@ private:
int GetBestBPP(); int GetBestBPP();
bool SetVideoMode(int w, int h, int bpp, bool fullscreen); bool SetVideoMode(int w, int h, int bpp, bool fullscreen);
bool TryCreateBackendDevice(SDL_Window* window);
void DowngradeBackendSettingAfterCreationFailure();
/** /**
* Remember whether Init has been called. (This isn't used for anything * Remember whether Init has been called. (This isn't used for anything
* important, just for verifying that the callers call our methods in * important, just for verifying that the callers call our methods in