mirror of
https://github.com/ddnet/ddnet.git
synced 2024-10-19 23:38:19 +00:00
b4423551d4
After the error message popup is shown for graphics assertions, the client window was destroyed but the process was not terminated properly. Now the graphics assertion error is handled like a normal assertion error, as those are also shown in an error message popup and correctly cause the client to break into the debugger or terminate. However, calling `dbg_assert` while already owning the lock that `WaitForIdle` waits on will cause a deadlock, so error handling must be delayed until after the lock is released. The buffer size for assertion messages is increased, as it was not sufficient for some graphics assertions. The `ICommandProcessor::GetError` and `ICommandProcessor::GetWarning` functions are marked as `const`.
1636 lines
45 KiB
C++
1636 lines
45 KiB
C++
#include <base/detect.h>
|
|
|
|
#ifndef CONF_BACKEND_OPENGL_ES
|
|
#include <GL/glew.h>
|
|
#endif
|
|
|
|
#include <SDL.h>
|
|
#include <SDL_messagebox.h>
|
|
|
|
#include <base/math.h>
|
|
#include <cstdlib>
|
|
|
|
#include <engine/shared/config.h>
|
|
#include <engine/shared/localization.h>
|
|
|
|
#include <base/tl/threading.h>
|
|
|
|
#if defined(CONF_VIDEORECORDER)
|
|
#include <engine/shared/video.h>
|
|
#endif
|
|
|
|
#include "backend_sdl.h"
|
|
|
|
#if defined(CONF_HEADLESS_CLIENT)
|
|
#include "backend/null/backend_null.h"
|
|
#endif
|
|
|
|
#if !defined(CONF_BACKEND_OPENGL_ES)
|
|
#include "backend/opengl/backend_opengl3.h"
|
|
#endif
|
|
|
|
#if defined(CONF_BACKEND_OPENGL_ES3) || defined(CONF_BACKEND_OPENGL_ES)
|
|
#include "backend/opengles/backend_opengles3.h"
|
|
#endif
|
|
|
|
#if defined(CONF_BACKEND_VULKAN)
|
|
#include "backend/vulkan/backend_vulkan.h"
|
|
#endif
|
|
|
|
#include "graphics_threaded.h"
|
|
|
|
#include <engine/graphics.h>
|
|
|
|
class IStorage;
|
|
|
|
// ------------ CGraphicsBackend_Threaded
|
|
|
|
void CGraphicsBackend_Threaded::ThreadFunc(void *pUser)
|
|
{
|
|
auto *pSelf = (CGraphicsBackend_Threaded *)pUser;
|
|
std::unique_lock<std::mutex> Lock(pSelf->m_BufferSwapMutex);
|
|
// notify, that the thread started
|
|
pSelf->m_Started = true;
|
|
pSelf->m_BufferSwapCond.notify_all();
|
|
while(!pSelf->m_Shutdown)
|
|
{
|
|
pSelf->m_BufferSwapCond.wait(Lock, [&pSelf] { return pSelf->m_pBuffer != nullptr || pSelf->m_Shutdown; });
|
|
if(pSelf->m_pBuffer)
|
|
{
|
|
#ifdef CONF_PLATFORM_MACOS
|
|
CAutoreleasePool AutoreleasePool;
|
|
#endif
|
|
pSelf->m_pProcessor->RunBuffer(pSelf->m_pBuffer);
|
|
|
|
pSelf->m_pBuffer = nullptr;
|
|
pSelf->m_BufferInProcess.store(false, std::memory_order_relaxed);
|
|
pSelf->m_BufferSwapCond.notify_all();
|
|
|
|
#if defined(CONF_VIDEORECORDER)
|
|
if(IVideo::Current())
|
|
IVideo::Current()->NextVideoFrameThread();
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
|
|
CGraphicsBackend_Threaded::CGraphicsBackend_Threaded(TTranslateFunc &&TranslateFunc) :
|
|
m_TranslateFunc(std::move(TranslateFunc))
|
|
{
|
|
m_pBuffer = nullptr;
|
|
m_pProcessor = nullptr;
|
|
m_Shutdown = true;
|
|
m_BufferInProcess.store(false, std::memory_order_relaxed);
|
|
}
|
|
|
|
void CGraphicsBackend_Threaded::StartProcessor(ICommandProcessor *pProcessor)
|
|
{
|
|
dbg_assert(m_Shutdown, "Processor was already not shut down.");
|
|
m_Shutdown = false;
|
|
m_pProcessor = pProcessor;
|
|
std::unique_lock<std::mutex> Lock(m_BufferSwapMutex);
|
|
m_pThread = thread_init(ThreadFunc, this, "Graphics thread");
|
|
// wait for the thread to start
|
|
m_BufferSwapCond.wait(Lock, [this]() -> bool { return m_Started; });
|
|
}
|
|
|
|
void CGraphicsBackend_Threaded::StopProcessor()
|
|
{
|
|
dbg_assert(!m_Shutdown, "Processor was already shut down.");
|
|
m_Shutdown = true;
|
|
{
|
|
std::unique_lock<std::mutex> Lock(m_BufferSwapMutex);
|
|
m_Warning = m_pProcessor->GetWarning();
|
|
m_BufferSwapCond.notify_all();
|
|
}
|
|
thread_wait(m_pThread);
|
|
}
|
|
|
|
void CGraphicsBackend_Threaded::RunBuffer(CCommandBuffer *pBuffer)
|
|
{
|
|
SGFXErrorContainer Error;
|
|
#ifdef CONF_WEBASM
|
|
// run everything single threaded for now, context binding in a thread seems to not work as of now
|
|
Error = m_pProcessor->GetError();
|
|
if(Error.m_ErrorType == GFX_ERROR_TYPE_NONE)
|
|
{
|
|
RunBufferSingleThreadedUnsafe(pBuffer);
|
|
}
|
|
#else
|
|
WaitForIdle();
|
|
{
|
|
std::unique_lock<std::mutex> Lock(m_BufferSwapMutex);
|
|
Error = m_pProcessor->GetError();
|
|
if(Error.m_ErrorType == GFX_ERROR_TYPE_NONE)
|
|
{
|
|
m_pBuffer = pBuffer;
|
|
m_BufferInProcess.store(true, std::memory_order_relaxed);
|
|
m_BufferSwapCond.notify_all();
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// Process error after lock is released to prevent deadlock
|
|
if(Error.m_ErrorType != GFX_ERROR_TYPE_NONE)
|
|
{
|
|
ProcessError(Error);
|
|
}
|
|
}
|
|
|
|
void CGraphicsBackend_Threaded::RunBufferSingleThreadedUnsafe(CCommandBuffer *pBuffer)
|
|
{
|
|
m_pProcessor->RunBuffer(pBuffer);
|
|
}
|
|
|
|
bool CGraphicsBackend_Threaded::IsIdle() const
|
|
{
|
|
return !m_BufferInProcess.load(std::memory_order_relaxed);
|
|
}
|
|
|
|
void CGraphicsBackend_Threaded::WaitForIdle()
|
|
{
|
|
std::unique_lock<std::mutex> Lock(m_BufferSwapMutex);
|
|
m_BufferSwapCond.wait(Lock, [this]() { return m_pBuffer == nullptr; });
|
|
}
|
|
|
|
void CGraphicsBackend_Threaded::ProcessError(const SGFXErrorContainer &Error)
|
|
{
|
|
std::string VerboseStr = "Graphics Assertion:";
|
|
for(const auto &ErrStr : Error.m_vErrors)
|
|
{
|
|
VerboseStr.append("\n");
|
|
if(ErrStr.m_RequiresTranslation)
|
|
VerboseStr.append(m_TranslateFunc(ErrStr.m_Err.c_str(), ""));
|
|
else
|
|
VerboseStr.append(ErrStr.m_Err);
|
|
}
|
|
dbg_assert(false, VerboseStr.c_str());
|
|
}
|
|
|
|
bool CGraphicsBackend_Threaded::GetWarning(std::vector<std::string> &WarningStrings)
|
|
{
|
|
if(m_Warning.m_WarningType != GFX_WARNING_TYPE_NONE)
|
|
{
|
|
m_Warning.m_WarningType = GFX_WARNING_TYPE_NONE;
|
|
WarningStrings = m_Warning.m_vWarnings;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// ------------ CCommandProcessorFragment_General
|
|
|
|
void CCommandProcessorFragment_General::Cmd_Signal(const CCommandBuffer::SCommand_Signal *pCommand)
|
|
{
|
|
pCommand->m_pSemaphore->Signal();
|
|
}
|
|
|
|
bool CCommandProcessorFragment_General::RunCommand(const CCommandBuffer::SCommand *pBaseCommand)
|
|
{
|
|
switch(pBaseCommand->m_Cmd)
|
|
{
|
|
case CCommandBuffer::CMD_NOP: break;
|
|
case CCommandBuffer::CMD_SIGNAL: Cmd_Signal(static_cast<const CCommandBuffer::SCommand_Signal *>(pBaseCommand)); break;
|
|
default: return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// ------------ CCommandProcessorFragment_SDL
|
|
void CCommandProcessorFragment_SDL::Cmd_Init(const SCommand_Init *pCommand)
|
|
{
|
|
m_GLContext = pCommand->m_GLContext;
|
|
m_pWindow = pCommand->m_pWindow;
|
|
if(m_GLContext)
|
|
SDL_GL_MakeCurrent(m_pWindow, m_GLContext);
|
|
}
|
|
|
|
void CCommandProcessorFragment_SDL::Cmd_Shutdown(const SCommand_Shutdown *pCommand)
|
|
{
|
|
if(m_GLContext)
|
|
SDL_GL_MakeCurrent(NULL, NULL);
|
|
}
|
|
|
|
void CCommandProcessorFragment_SDL::Cmd_Swap(const CCommandBuffer::SCommand_Swap *pCommand)
|
|
{
|
|
if(m_GLContext)
|
|
SDL_GL_SwapWindow(m_pWindow);
|
|
}
|
|
|
|
void CCommandProcessorFragment_SDL::Cmd_VSync(const CCommandBuffer::SCommand_VSync *pCommand)
|
|
{
|
|
if(m_GLContext)
|
|
*pCommand->m_pRetOk = SDL_GL_SetSwapInterval(pCommand->m_VSync) == 0;
|
|
}
|
|
|
|
void CCommandProcessorFragment_SDL::Cmd_WindowCreateNtf(const CCommandBuffer::SCommand_WindowCreateNtf *pCommand)
|
|
{
|
|
m_pWindow = SDL_GetWindowFromID(pCommand->m_WindowID);
|
|
// Android destroys windows when they are not visible, so we get the new one and work with that
|
|
// The graphic context does not need to be recreated, just unbound see @see SCommand_WindowDestroyNtf
|
|
#ifdef CONF_PLATFORM_ANDROID
|
|
if(m_GLContext)
|
|
SDL_GL_MakeCurrent(m_pWindow, m_GLContext);
|
|
dbg_msg("gfx", "render surface created.");
|
|
#endif
|
|
}
|
|
|
|
void CCommandProcessorFragment_SDL::Cmd_WindowDestroyNtf(const CCommandBuffer::SCommand_WindowDestroyNtf *pCommand)
|
|
{
|
|
// Unbind the graphic context from the window, so it does not get destroyed
|
|
#ifdef CONF_PLATFORM_ANDROID
|
|
dbg_msg("gfx", "render surface destroyed.");
|
|
if(m_GLContext)
|
|
SDL_GL_MakeCurrent(NULL, NULL);
|
|
#endif
|
|
}
|
|
|
|
CCommandProcessorFragment_SDL::CCommandProcessorFragment_SDL() = default;
|
|
|
|
bool CCommandProcessorFragment_SDL::RunCommand(const CCommandBuffer::SCommand *pBaseCommand)
|
|
{
|
|
switch(pBaseCommand->m_Cmd)
|
|
{
|
|
case CCommandBuffer::CMD_WINDOW_CREATE_NTF: Cmd_WindowCreateNtf(static_cast<const CCommandBuffer::SCommand_WindowCreateNtf *>(pBaseCommand)); break;
|
|
case CCommandBuffer::CMD_WINDOW_DESTROY_NTF: Cmd_WindowDestroyNtf(static_cast<const CCommandBuffer::SCommand_WindowDestroyNtf *>(pBaseCommand)); break;
|
|
case CCommandBuffer::CMD_SWAP: Cmd_Swap(static_cast<const CCommandBuffer::SCommand_Swap *>(pBaseCommand)); break;
|
|
case CCommandBuffer::CMD_VSYNC: Cmd_VSync(static_cast<const CCommandBuffer::SCommand_VSync *>(pBaseCommand)); break;
|
|
case CCommandBuffer::CMD_MULTISAMPLING: break;
|
|
case CMD_INIT: Cmd_Init(static_cast<const SCommand_Init *>(pBaseCommand)); break;
|
|
case CMD_SHUTDOWN: Cmd_Shutdown(static_cast<const SCommand_Shutdown *>(pBaseCommand)); break;
|
|
case CCommandProcessorFragment_GLBase::CMD_PRE_INIT: break;
|
|
case CCommandProcessorFragment_GLBase::CMD_POST_SHUTDOWN: break;
|
|
default: return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// ------------ CCommandProcessor_SDL_GL
|
|
|
|
void CCommandProcessor_SDL_GL::HandleError()
|
|
{
|
|
switch(m_Error.m_ErrorType)
|
|
{
|
|
case GFX_ERROR_TYPE_INIT:
|
|
m_Error.m_vErrors.emplace_back(SGFXErrorContainer::SError{true, Localizable("Failed during initialization. Try to change gfx_backend to OpenGL or Vulkan in settings_ddnet.cfg in the config directory and try again.", "Graphics error")});
|
|
break;
|
|
case GFX_ERROR_TYPE_OUT_OF_MEMORY_IMAGE:
|
|
[[fallthrough]];
|
|
case GFX_ERROR_TYPE_OUT_OF_MEMORY_BUFFER:
|
|
[[fallthrough]];
|
|
case GFX_ERROR_TYPE_OUT_OF_MEMORY_STAGING:
|
|
m_Error.m_vErrors.emplace_back(SGFXErrorContainer::SError{true, Localizable("Out of VRAM. Try removing custom assets (skins, entities, etc.), especially those with high resolution.", "Graphics error")});
|
|
break;
|
|
case GFX_ERROR_TYPE_RENDER_RECORDING:
|
|
m_Error.m_vErrors.emplace_back(SGFXErrorContainer::SError{true, Localizable("An error during command recording occurred. Try to update your GPU drivers.", "Graphics error")});
|
|
break;
|
|
case GFX_ERROR_TYPE_RENDER_CMD_FAILED:
|
|
m_Error.m_vErrors.emplace_back(SGFXErrorContainer::SError{true, Localizable("A render command failed. Try to update your GPU drivers.", "Graphics error")});
|
|
break;
|
|
case GFX_ERROR_TYPE_RENDER_SUBMIT_FAILED:
|
|
m_Error.m_vErrors.emplace_back(SGFXErrorContainer::SError{true, Localizable("Submitting the render commands failed. Try to update your GPU drivers.", "Graphics error")});
|
|
break;
|
|
case GFX_ERROR_TYPE_SWAP_FAILED:
|
|
m_Error.m_vErrors.emplace_back(SGFXErrorContainer::SError{true, Localizable("Failed to swap framebuffers. Try to update your GPU drivers.", "Graphics error")});
|
|
break;
|
|
case GFX_ERROR_TYPE_UNKNOWN:
|
|
[[fallthrough]];
|
|
default:
|
|
m_Error.m_vErrors.emplace_back(SGFXErrorContainer::SError{true, Localizable("Unknown error. Try to change gfx_backend to OpenGL or Vulkan in settings_ddnet.cfg in the config directory and try again.", "Graphics error")});
|
|
break;
|
|
}
|
|
}
|
|
|
|
void CCommandProcessor_SDL_GL::HandleWarning()
|
|
{
|
|
switch(m_Warning.m_WarningType)
|
|
{
|
|
case GFX_WARNING_TYPE_INIT_FAILED:
|
|
m_Warning.m_vWarnings.emplace_back(Localizable("Could not initialize the given graphics backend, reverting to the default backend now.", "Graphics error"));
|
|
break;
|
|
case GFX_WARNING_TYPE_INIT_FAILED_MISSING_INTEGRATED_GPU_DRIVER:
|
|
m_Warning.m_vWarnings.emplace_back(Localizable("Could not initialize the given graphics backend, this is probably because you didn't install the driver of the integrated graphics card.", "Graphics error"));
|
|
break;
|
|
case GFX_WARNING_MISSING_EXTENSION:
|
|
// ignore this warning for now
|
|
return;
|
|
case GFX_WARNING_LOW_ON_MEMORY:
|
|
// ignore this warning for now
|
|
return;
|
|
default:
|
|
dbg_msg("gfx", "unhandled warning %d", (int)m_Warning.m_WarningType);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void CCommandProcessor_SDL_GL::RunBuffer(CCommandBuffer *pBuffer)
|
|
{
|
|
m_pGLBackend->StartCommands(pBuffer->m_CommandCount, pBuffer->m_RenderCallCount);
|
|
|
|
for(CCommandBuffer::SCommand *pCommand = pBuffer->Head(); pCommand; pCommand = pCommand->m_pNext)
|
|
{
|
|
auto Res = m_pGLBackend->RunCommand(pCommand);
|
|
if(Res == ERunCommandReturnTypes::RUN_COMMAND_COMMAND_HANDLED)
|
|
{
|
|
continue;
|
|
}
|
|
else if(Res == ERunCommandReturnTypes::RUN_COMMAND_COMMAND_ERROR)
|
|
{
|
|
m_Error = m_pGLBackend->GetError();
|
|
HandleError();
|
|
return;
|
|
}
|
|
else if(Res == ERunCommandReturnTypes::RUN_COMMAND_COMMAND_WARNING)
|
|
{
|
|
m_Warning = m_pGLBackend->GetWarning();
|
|
HandleWarning();
|
|
return;
|
|
}
|
|
|
|
if(m_SDL.RunCommand(pCommand))
|
|
continue;
|
|
|
|
if(m_General.RunCommand(pCommand))
|
|
continue;
|
|
|
|
dbg_msg("gfx", "unknown command %d", pCommand->m_Cmd);
|
|
}
|
|
|
|
m_pGLBackend->EndCommands();
|
|
}
|
|
|
|
CCommandProcessor_SDL_GL::CCommandProcessor_SDL_GL(EBackendType BackendType, int GLMajor, int GLMinor, int GLPatch)
|
|
{
|
|
m_BackendType = BackendType;
|
|
|
|
#if defined(CONF_HEADLESS_CLIENT)
|
|
m_pGLBackend = new CCommandProcessorFragment_Null();
|
|
#else
|
|
if(BackendType == BACKEND_TYPE_OPENGL_ES)
|
|
{
|
|
#if defined(CONF_BACKEND_OPENGL_ES) || defined(CONF_BACKEND_OPENGL_ES3)
|
|
if(GLMajor < 3)
|
|
{
|
|
m_pGLBackend = new CCommandProcessorFragment_OpenGLES();
|
|
}
|
|
else
|
|
{
|
|
m_pGLBackend = new CCommandProcessorFragment_OpenGLES3();
|
|
}
|
|
#endif
|
|
}
|
|
else if(BackendType == BACKEND_TYPE_OPENGL)
|
|
{
|
|
#if !defined(CONF_BACKEND_OPENGL_ES)
|
|
if(GLMajor < 2)
|
|
{
|
|
m_pGLBackend = new CCommandProcessorFragment_OpenGL();
|
|
}
|
|
if(GLMajor == 2)
|
|
{
|
|
m_pGLBackend = new CCommandProcessorFragment_OpenGL2();
|
|
}
|
|
if(GLMajor == 3 && GLMinor == 0)
|
|
{
|
|
m_pGLBackend = new CCommandProcessorFragment_OpenGL3();
|
|
}
|
|
else if((GLMajor == 3 && GLMinor == 3) || GLMajor >= 4)
|
|
{
|
|
m_pGLBackend = new CCommandProcessorFragment_OpenGL3_3();
|
|
}
|
|
#endif
|
|
}
|
|
else if(BackendType == BACKEND_TYPE_VULKAN)
|
|
{
|
|
#if defined(CONF_BACKEND_VULKAN)
|
|
m_pGLBackend = CreateVulkanCommandProcessorFragment();
|
|
#endif
|
|
}
|
|
#endif
|
|
}
|
|
|
|
CCommandProcessor_SDL_GL::~CCommandProcessor_SDL_GL()
|
|
{
|
|
delete m_pGLBackend;
|
|
}
|
|
|
|
const SGFXErrorContainer &CCommandProcessor_SDL_GL::GetError() const
|
|
{
|
|
return m_Error;
|
|
}
|
|
|
|
void CCommandProcessor_SDL_GL::ErroneousCleanup()
|
|
{
|
|
return m_pGLBackend->ErroneousCleanup();
|
|
}
|
|
|
|
const SGFXWarningContainer &CCommandProcessor_SDL_GL::GetWarning() const
|
|
{
|
|
return m_Warning;
|
|
}
|
|
|
|
// ------------ CGraphicsBackend_SDL_GL
|
|
|
|
static bool BackendInitGlew(EBackendType BackendType, int &GlewMajor, int &GlewMinor, int &GlewPatch)
|
|
{
|
|
if(BackendType == BACKEND_TYPE_OPENGL)
|
|
{
|
|
#ifndef CONF_BACKEND_OPENGL_ES
|
|
// support graphic cards that are pretty old(and linux)
|
|
glewExperimental = GL_TRUE;
|
|
#ifdef CONF_GLEW_HAS_CONTEXT_INIT
|
|
if(GLEW_OK != glewContextInit())
|
|
#else
|
|
if(GLEW_OK != glewInit())
|
|
#endif
|
|
return false;
|
|
|
|
#ifdef GLEW_VERSION_4_6
|
|
if(GLEW_VERSION_4_6)
|
|
{
|
|
GlewMajor = 4;
|
|
GlewMinor = 6;
|
|
GlewPatch = 0;
|
|
return true;
|
|
}
|
|
#endif
|
|
#ifdef GLEW_VERSION_4_5
|
|
if(GLEW_VERSION_4_5)
|
|
{
|
|
GlewMajor = 4;
|
|
GlewMinor = 5;
|
|
GlewPatch = 0;
|
|
return true;
|
|
}
|
|
#endif
|
|
// Don't allow GL 3.3, if the driver doesn't support at least OpenGL 4.5
|
|
#ifndef CONF_FAMILY_WINDOWS
|
|
if(GLEW_VERSION_4_4)
|
|
{
|
|
GlewMajor = 4;
|
|
GlewMinor = 4;
|
|
GlewPatch = 0;
|
|
return true;
|
|
}
|
|
if(GLEW_VERSION_4_3)
|
|
{
|
|
GlewMajor = 4;
|
|
GlewMinor = 3;
|
|
GlewPatch = 0;
|
|
return true;
|
|
}
|
|
if(GLEW_VERSION_4_2)
|
|
{
|
|
GlewMajor = 4;
|
|
GlewMinor = 2;
|
|
GlewPatch = 0;
|
|
return true;
|
|
}
|
|
if(GLEW_VERSION_4_1)
|
|
{
|
|
GlewMajor = 4;
|
|
GlewMinor = 1;
|
|
GlewPatch = 0;
|
|
return true;
|
|
}
|
|
if(GLEW_VERSION_4_0)
|
|
{
|
|
GlewMajor = 4;
|
|
GlewMinor = 0;
|
|
GlewPatch = 0;
|
|
return true;
|
|
}
|
|
if(GLEW_VERSION_3_3)
|
|
{
|
|
GlewMajor = 3;
|
|
GlewMinor = 3;
|
|
GlewPatch = 0;
|
|
return true;
|
|
}
|
|
#endif
|
|
if(GLEW_VERSION_3_0)
|
|
{
|
|
GlewMajor = 3;
|
|
GlewMinor = 0;
|
|
GlewPatch = 0;
|
|
return true;
|
|
}
|
|
if(GLEW_VERSION_2_1)
|
|
{
|
|
GlewMajor = 2;
|
|
GlewMinor = 1;
|
|
GlewPatch = 0;
|
|
return true;
|
|
}
|
|
if(GLEW_VERSION_2_0)
|
|
{
|
|
GlewMajor = 2;
|
|
GlewMinor = 0;
|
|
GlewPatch = 0;
|
|
return true;
|
|
}
|
|
if(GLEW_VERSION_1_5)
|
|
{
|
|
GlewMajor = 1;
|
|
GlewMinor = 5;
|
|
GlewPatch = 0;
|
|
return true;
|
|
}
|
|
if(GLEW_VERSION_1_4)
|
|
{
|
|
GlewMajor = 1;
|
|
GlewMinor = 4;
|
|
GlewPatch = 0;
|
|
return true;
|
|
}
|
|
if(GLEW_VERSION_1_3)
|
|
{
|
|
GlewMajor = 1;
|
|
GlewMinor = 3;
|
|
GlewPatch = 0;
|
|
return true;
|
|
}
|
|
if(GLEW_VERSION_1_2_1)
|
|
{
|
|
GlewMajor = 1;
|
|
GlewMinor = 2;
|
|
GlewPatch = 1;
|
|
return true;
|
|
}
|
|
if(GLEW_VERSION_1_2)
|
|
{
|
|
GlewMajor = 1;
|
|
GlewMinor = 2;
|
|
GlewPatch = 0;
|
|
return true;
|
|
}
|
|
if(GLEW_VERSION_1_1)
|
|
{
|
|
GlewMajor = 1;
|
|
GlewMinor = 1;
|
|
GlewPatch = 0;
|
|
return true;
|
|
}
|
|
#endif
|
|
}
|
|
else if(BackendType == BACKEND_TYPE_OPENGL_ES)
|
|
{
|
|
// just assume the version we need
|
|
GlewMajor = 3;
|
|
GlewMinor = 0;
|
|
GlewPatch = 0;
|
|
|
|
return true;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static int IsVersionSupportedGlew(EBackendType BackendType, int VersionMajor, int VersionMinor, int VersionPatch, int GlewMajor, int GlewMinor, int GlewPatch)
|
|
{
|
|
int InitError = 0;
|
|
|
|
if(BackendType == BACKEND_TYPE_OPENGL)
|
|
{
|
|
if(VersionMajor >= 4 && GlewMajor < 4)
|
|
{
|
|
InitError = -1;
|
|
}
|
|
else if(VersionMajor >= 3 && GlewMajor < 3)
|
|
{
|
|
InitError = -1;
|
|
}
|
|
else if(VersionMajor == 3 && GlewMajor == 3)
|
|
{
|
|
if(VersionMinor >= 3 && GlewMinor < 3)
|
|
{
|
|
InitError = -1;
|
|
}
|
|
if(VersionMinor >= 2 && GlewMinor < 2)
|
|
{
|
|
InitError = -1;
|
|
}
|
|
if(VersionMinor >= 1 && GlewMinor < 1)
|
|
{
|
|
InitError = -1;
|
|
}
|
|
if(VersionMinor >= 0 && GlewMinor < 0)
|
|
{
|
|
InitError = -1;
|
|
}
|
|
}
|
|
else if(VersionMajor >= 2 && GlewMajor < 2)
|
|
{
|
|
InitError = -1;
|
|
}
|
|
else if(VersionMajor == 2 && GlewMajor == 2)
|
|
{
|
|
if(VersionMinor >= 1 && GlewMinor < 1)
|
|
{
|
|
InitError = -1;
|
|
}
|
|
if(VersionMinor >= 0 && GlewMinor < 0)
|
|
{
|
|
InitError = -1;
|
|
}
|
|
}
|
|
else if(VersionMajor >= 1 && GlewMajor < 1)
|
|
{
|
|
InitError = -1;
|
|
}
|
|
else if(VersionMajor == 1 && GlewMajor == 1)
|
|
{
|
|
if(VersionMinor >= 5 && GlewMinor < 5)
|
|
{
|
|
InitError = -1;
|
|
}
|
|
if(VersionMinor >= 4 && GlewMinor < 4)
|
|
{
|
|
InitError = -1;
|
|
}
|
|
if(VersionMinor >= 3 && GlewMinor < 3)
|
|
{
|
|
InitError = -1;
|
|
}
|
|
if(VersionMinor >= 2 && GlewMinor < 2)
|
|
{
|
|
InitError = -1;
|
|
}
|
|
else if(VersionMinor == 2 && GlewMinor == 2)
|
|
{
|
|
if(VersionPatch >= 1 && GlewPatch < 1)
|
|
{
|
|
InitError = -1;
|
|
}
|
|
if(VersionPatch >= 0 && GlewPatch < 0)
|
|
{
|
|
InitError = -1;
|
|
}
|
|
}
|
|
if(VersionMinor >= 1 && GlewMinor < 1)
|
|
{
|
|
InitError = -1;
|
|
}
|
|
if(VersionMinor >= 0 && GlewMinor < 0)
|
|
{
|
|
InitError = -1;
|
|
}
|
|
}
|
|
}
|
|
|
|
return InitError;
|
|
}
|
|
|
|
EBackendType CGraphicsBackend_SDL_GL::DetectBackend()
|
|
{
|
|
EBackendType RetBackendType = BACKEND_TYPE_OPENGL;
|
|
#if defined(CONF_BACKEND_VULKAN)
|
|
const char *pEnvDriver = SDL_getenv("DDNET_DRIVER");
|
|
if(pEnvDriver && str_comp_nocase(pEnvDriver, "GLES") == 0)
|
|
RetBackendType = BACKEND_TYPE_OPENGL_ES;
|
|
else if(pEnvDriver && str_comp_nocase(pEnvDriver, "Vulkan") == 0)
|
|
RetBackendType = BACKEND_TYPE_VULKAN;
|
|
else if(pEnvDriver && str_comp_nocase(pEnvDriver, "OpenGL") == 0)
|
|
RetBackendType = BACKEND_TYPE_OPENGL;
|
|
else if(pEnvDriver == nullptr)
|
|
{
|
|
// load the config backend
|
|
const char *pConfBackend = g_Config.m_GfxBackend;
|
|
if(str_comp_nocase(pConfBackend, "GLES") == 0)
|
|
RetBackendType = BACKEND_TYPE_OPENGL_ES;
|
|
else if(str_comp_nocase(pConfBackend, "Vulkan") == 0)
|
|
RetBackendType = BACKEND_TYPE_VULKAN;
|
|
else if(str_comp_nocase(pConfBackend, "OpenGL") == 0)
|
|
RetBackendType = BACKEND_TYPE_OPENGL;
|
|
}
|
|
#else
|
|
RetBackendType = BACKEND_TYPE_OPENGL;
|
|
#endif
|
|
#if !defined(CONF_BACKEND_OPENGL_ES) && !defined(CONF_BACKEND_OPENGL_ES3)
|
|
if(RetBackendType == BACKEND_TYPE_OPENGL_ES)
|
|
RetBackendType = BACKEND_TYPE_OPENGL;
|
|
#elif defined(CONF_BACKEND_OPENGL_ES)
|
|
if(RetBackendType == BACKEND_TYPE_OPENGL)
|
|
RetBackendType = BACKEND_TYPE_OPENGL_ES;
|
|
#endif
|
|
return RetBackendType;
|
|
}
|
|
|
|
void CGraphicsBackend_SDL_GL::ClampDriverVersion(EBackendType BackendType)
|
|
{
|
|
if(BackendType == BACKEND_TYPE_OPENGL)
|
|
{
|
|
// clamp the versions to existing versions(only for OpenGL major <= 3)
|
|
if(g_Config.m_GfxGLMajor == 1)
|
|
{
|
|
g_Config.m_GfxGLMinor = clamp(g_Config.m_GfxGLMinor, 1, 5);
|
|
if(g_Config.m_GfxGLMinor == 2)
|
|
g_Config.m_GfxGLPatch = clamp(g_Config.m_GfxGLPatch, 0, 1);
|
|
else
|
|
g_Config.m_GfxGLPatch = 0;
|
|
}
|
|
else if(g_Config.m_GfxGLMajor == 2)
|
|
{
|
|
g_Config.m_GfxGLMinor = clamp(g_Config.m_GfxGLMinor, 0, 1);
|
|
g_Config.m_GfxGLPatch = 0;
|
|
}
|
|
else if(g_Config.m_GfxGLMajor == 3)
|
|
{
|
|
g_Config.m_GfxGLMinor = clamp(g_Config.m_GfxGLMinor, 0, 3);
|
|
if(g_Config.m_GfxGLMinor < 3)
|
|
g_Config.m_GfxGLMinor = 0;
|
|
g_Config.m_GfxGLPatch = 0;
|
|
}
|
|
}
|
|
else if(BackendType == BACKEND_TYPE_OPENGL_ES)
|
|
{
|
|
#if !defined(CONF_BACKEND_OPENGL_ES3)
|
|
// Make sure GLES is set to 1.0 (which is equivalent to OpenGL 1.3), if its not set to >= 3.0(which is equivalent to OpenGL 3.3)
|
|
if(g_Config.m_GfxGLMajor < 3)
|
|
{
|
|
g_Config.m_GfxGLMajor = 1;
|
|
g_Config.m_GfxGLMinor = 0;
|
|
g_Config.m_GfxGLPatch = 0;
|
|
|
|
// GLES also doesn't know GL_QUAD
|
|
g_Config.m_GfxQuadAsTriangle = 1;
|
|
}
|
|
#else
|
|
g_Config.m_GfxGLMajor = 3;
|
|
g_Config.m_GfxGLMinor = 0;
|
|
g_Config.m_GfxGLPatch = 0;
|
|
#endif
|
|
}
|
|
else if(BackendType == BACKEND_TYPE_VULKAN)
|
|
{
|
|
#if defined(CONF_BACKEND_VULKAN)
|
|
g_Config.m_GfxGLMajor = gs_BackendVulkanMajor;
|
|
g_Config.m_GfxGLMinor = gs_BackendVulkanMinor;
|
|
g_Config.m_GfxGLPatch = 0;
|
|
#endif
|
|
}
|
|
}
|
|
|
|
bool CGraphicsBackend_SDL_GL::ShowMessageBox(unsigned Type, const char *pTitle, const char *pMsg)
|
|
{
|
|
if(m_pProcessor != nullptr)
|
|
{
|
|
m_pProcessor->ErroneousCleanup();
|
|
}
|
|
// TODO: Remove this workaround when https://github.com/libsdl-org/SDL/issues/3750 is
|
|
// fixed and pass the window to SDL_ShowSimpleMessageBox to make the popup modal instead
|
|
// of destroying the window before opening the popup.
|
|
if(m_pWindow != nullptr)
|
|
{
|
|
SDL_DestroyWindow(m_pWindow);
|
|
m_pWindow = nullptr;
|
|
}
|
|
return SDL_ShowSimpleMessageBox(Type, pTitle, pMsg, nullptr) == 0;
|
|
}
|
|
|
|
bool CGraphicsBackend_SDL_GL::IsModernAPI(EBackendType BackendType)
|
|
{
|
|
if(BackendType == BACKEND_TYPE_OPENGL)
|
|
return (g_Config.m_GfxGLMajor == 3 && g_Config.m_GfxGLMinor == 3) || g_Config.m_GfxGLMajor >= 4;
|
|
else if(BackendType == BACKEND_TYPE_OPENGL_ES)
|
|
return g_Config.m_GfxGLMajor >= 3;
|
|
else if(BackendType == BACKEND_TYPE_VULKAN)
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
bool CGraphicsBackend_SDL_GL::GetDriverVersion(EGraphicsDriverAgeType DriverAgeType, int &Major, int &Minor, int &Patch, const char *&pName, EBackendType BackendType)
|
|
{
|
|
if(BackendType == BACKEND_TYPE_AUTO)
|
|
BackendType = m_BackendType;
|
|
if(BackendType == BACKEND_TYPE_OPENGL)
|
|
{
|
|
pName = "OpenGL";
|
|
#ifndef CONF_BACKEND_OPENGL_ES
|
|
if(DriverAgeType == GRAPHICS_DRIVER_AGE_TYPE_LEGACY)
|
|
{
|
|
Major = 1;
|
|
Minor = 4;
|
|
Patch = 0;
|
|
return true;
|
|
}
|
|
else if(DriverAgeType == GRAPHICS_DRIVER_AGE_TYPE_DEFAULT)
|
|
{
|
|
Major = 3;
|
|
Minor = 0;
|
|
Patch = 0;
|
|
return true;
|
|
}
|
|
else if(DriverAgeType == GRAPHICS_DRIVER_AGE_TYPE_MODERN)
|
|
{
|
|
Major = 3;
|
|
Minor = 3;
|
|
Patch = 0;
|
|
return true;
|
|
}
|
|
#endif
|
|
}
|
|
else if(BackendType == BACKEND_TYPE_OPENGL_ES)
|
|
{
|
|
pName = "GLES";
|
|
#ifdef CONF_BACKEND_OPENGL_ES
|
|
if(DriverAgeType == GRAPHICS_DRIVER_AGE_TYPE_LEGACY)
|
|
{
|
|
Major = 1;
|
|
Minor = 0;
|
|
Patch = 0;
|
|
return true;
|
|
}
|
|
else if(DriverAgeType == GRAPHICS_DRIVER_AGE_TYPE_DEFAULT)
|
|
{
|
|
Major = 3;
|
|
Minor = 0;
|
|
Patch = 0;
|
|
// there isn't really a default one
|
|
return false;
|
|
}
|
|
#endif
|
|
#ifdef CONF_BACKEND_OPENGL_ES3
|
|
if(DriverAgeType == GRAPHICS_DRIVER_AGE_TYPE_MODERN)
|
|
{
|
|
Major = 3;
|
|
Minor = 0;
|
|
Patch = 0;
|
|
return true;
|
|
}
|
|
#endif
|
|
}
|
|
else if(BackendType == BACKEND_TYPE_VULKAN)
|
|
{
|
|
pName = "Vulkan";
|
|
#ifdef CONF_BACKEND_VULKAN
|
|
if(DriverAgeType == GRAPHICS_DRIVER_AGE_TYPE_DEFAULT)
|
|
{
|
|
Major = gs_BackendVulkanMajor;
|
|
Minor = gs_BackendVulkanMinor;
|
|
Patch = 0;
|
|
return true;
|
|
}
|
|
#else
|
|
return false;
|
|
#endif
|
|
}
|
|
return false;
|
|
}
|
|
|
|
const char *CGraphicsBackend_SDL_GL::GetScreenName(int Screen) const
|
|
{
|
|
const char *pName = SDL_GetDisplayName(Screen);
|
|
return pName == nullptr ? "unknown/error" : pName;
|
|
}
|
|
|
|
static void DisplayToVideoMode(CVideoMode *pVMode, SDL_DisplayMode *pMode, int HiDPIScale, int RefreshRate)
|
|
{
|
|
pVMode->m_CanvasWidth = pMode->w * HiDPIScale;
|
|
pVMode->m_CanvasHeight = pMode->h * HiDPIScale;
|
|
pVMode->m_WindowWidth = pMode->w;
|
|
pVMode->m_WindowHeight = pMode->h;
|
|
pVMode->m_RefreshRate = RefreshRate;
|
|
pVMode->m_Red = SDL_BITSPERPIXEL(pMode->format);
|
|
pVMode->m_Green = SDL_BITSPERPIXEL(pMode->format);
|
|
pVMode->m_Blue = SDL_BITSPERPIXEL(pMode->format);
|
|
pVMode->m_Format = pMode->format;
|
|
}
|
|
|
|
void CGraphicsBackend_SDL_GL::GetVideoModes(CVideoMode *pModes, int MaxModes, int *pNumModes, int HiDPIScale, int MaxWindowWidth, int MaxWindowHeight, int ScreenID)
|
|
{
|
|
SDL_DisplayMode DesktopMode;
|
|
int maxModes = SDL_GetNumDisplayModes(ScreenID);
|
|
int numModes = 0;
|
|
|
|
// Only collect fullscreen modes when requested, that makes sure in windowed mode no refresh rates are shown that aren't supported without
|
|
// fullscreen anyway(except fullscreen desktop)
|
|
bool IsFullscreenDestkop = m_pWindow != NULL && (((SDL_GetWindowFlags(m_pWindow) & SDL_WINDOW_FULLSCREEN_DESKTOP) == SDL_WINDOW_FULLSCREEN_DESKTOP) || g_Config.m_GfxFullscreen == 3);
|
|
bool CollectFullscreenModes = m_pWindow == NULL || ((SDL_GetWindowFlags(m_pWindow) & SDL_WINDOW_FULLSCREEN) != 0 && !IsFullscreenDestkop);
|
|
|
|
if(SDL_GetDesktopDisplayMode(ScreenID, &DesktopMode) < 0)
|
|
{
|
|
dbg_msg("gfx", "unable to get display mode: %s", SDL_GetError());
|
|
}
|
|
|
|
constexpr int ModeCount = 256;
|
|
SDL_DisplayMode aModes[ModeCount];
|
|
int NumModes = 0;
|
|
for(int i = 0; i < maxModes && NumModes < ModeCount; i++)
|
|
{
|
|
SDL_DisplayMode mode;
|
|
if(SDL_GetDisplayMode(ScreenID, i, &mode) < 0)
|
|
{
|
|
dbg_msg("gfx", "unable to get display mode: %s", SDL_GetError());
|
|
continue;
|
|
}
|
|
|
|
aModes[NumModes] = mode;
|
|
++NumModes;
|
|
}
|
|
|
|
auto &&ModeInsert = [&](SDL_DisplayMode &mode) {
|
|
if(numModes < MaxModes)
|
|
{
|
|
// if last mode was equal, ignore this one --- in fullscreen this can really only happen if the screen
|
|
// supports different color modes
|
|
// in non fullscren these are the modes that show different refresh rate, but are basically the same
|
|
if(numModes > 0 && pModes[numModes - 1].m_WindowWidth == mode.w && pModes[numModes - 1].m_WindowHeight == mode.h && (pModes[numModes - 1].m_RefreshRate == mode.refresh_rate || (mode.refresh_rate != DesktopMode.refresh_rate && !CollectFullscreenModes)))
|
|
return;
|
|
|
|
DisplayToVideoMode(&pModes[numModes], &mode, HiDPIScale, !CollectFullscreenModes ? DesktopMode.refresh_rate : mode.refresh_rate);
|
|
numModes++;
|
|
}
|
|
};
|
|
|
|
for(int i = 0; i < NumModes; i++)
|
|
{
|
|
SDL_DisplayMode &mode = aModes[i];
|
|
|
|
if(mode.w > MaxWindowWidth || mode.h > MaxWindowHeight)
|
|
continue;
|
|
|
|
ModeInsert(mode);
|
|
|
|
if(IsFullscreenDestkop)
|
|
break;
|
|
|
|
if(numModes >= MaxModes)
|
|
break;
|
|
}
|
|
*pNumModes = numModes;
|
|
}
|
|
|
|
void CGraphicsBackend_SDL_GL::GetCurrentVideoMode(CVideoMode &CurMode, int HiDPIScale, int MaxWindowWidth, int MaxWindowHeight, int ScreenID)
|
|
{
|
|
SDL_DisplayMode DPMode;
|
|
// if "real" fullscreen, obtain the video mode for that
|
|
if((SDL_GetWindowFlags(m_pWindow) & SDL_WINDOW_FULLSCREEN_DESKTOP) == SDL_WINDOW_FULLSCREEN)
|
|
{
|
|
if(SDL_GetCurrentDisplayMode(ScreenID, &DPMode))
|
|
{
|
|
dbg_msg("gfx", "unable to get display mode: %s", SDL_GetError());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(SDL_GetDesktopDisplayMode(ScreenID, &DPMode) < 0)
|
|
{
|
|
dbg_msg("gfx", "unable to get display mode: %s", SDL_GetError());
|
|
}
|
|
else
|
|
{
|
|
int Width = 0;
|
|
int Height = 0;
|
|
SDL_GL_GetDrawableSize(m_pWindow, &Width, &Height);
|
|
DPMode.w = Width;
|
|
DPMode.h = Height;
|
|
}
|
|
}
|
|
DisplayToVideoMode(&CurMode, &DPMode, HiDPIScale, DPMode.refresh_rate);
|
|
}
|
|
|
|
CGraphicsBackend_SDL_GL::CGraphicsBackend_SDL_GL(TTranslateFunc &&TranslateFunc) :
|
|
CGraphicsBackend_Threaded(std::move(TranslateFunc))
|
|
{
|
|
mem_zero(m_aErrorString, std::size(m_aErrorString));
|
|
}
|
|
|
|
int CGraphicsBackend_SDL_GL::Init(const char *pName, int *pScreen, int *pWidth, int *pHeight, int *pRefreshRate, int *pFsaaSamples, int Flags, int *pDesktopWidth, int *pDesktopHeight, int *pCurrentWidth, int *pCurrentHeight, IStorage *pStorage)
|
|
{
|
|
#if defined(CONF_HEADLESS_CLIENT)
|
|
int InitError = 0;
|
|
const char *pErrorStr = NULL;
|
|
int GlewMajor = 0;
|
|
int GlewMinor = 0;
|
|
int GlewPatch = 0;
|
|
IsVersionSupportedGlew(m_BackendType, g_Config.m_GfxGLMajor, g_Config.m_GfxGLMinor, g_Config.m_GfxGLPatch, GlewMajor, GlewMinor, GlewPatch);
|
|
BackendInitGlew(m_BackendType, GlewMajor, GlewMinor, GlewPatch);
|
|
#else
|
|
// print sdl version
|
|
{
|
|
SDL_version Compiled;
|
|
SDL_version Linked;
|
|
|
|
SDL_VERSION(&Compiled);
|
|
SDL_GetVersion(&Linked);
|
|
dbg_msg("sdl", "SDL version %d.%d.%d (compiled = %d.%d.%d)", Linked.major, Linked.minor, Linked.patch,
|
|
Compiled.major, Compiled.minor, Compiled.patch);
|
|
}
|
|
|
|
if(!SDL_WasInit(SDL_INIT_VIDEO))
|
|
{
|
|
if(SDL_InitSubSystem(SDL_INIT_VIDEO) < 0)
|
|
{
|
|
dbg_msg("gfx", "unable to init SDL video: %s", SDL_GetError());
|
|
return EGraphicsBackendErrorCodes::GRAPHICS_BACKEND_ERROR_CODE_SDL_INIT_FAILED;
|
|
}
|
|
}
|
|
|
|
EBackendType OldBackendType = m_BackendType;
|
|
m_BackendType = DetectBackend();
|
|
// little fallback for Vulkan
|
|
if(OldBackendType != BACKEND_TYPE_AUTO)
|
|
{
|
|
if(m_BackendType == BACKEND_TYPE_VULKAN)
|
|
{
|
|
// try default opengl settings
|
|
str_copy(g_Config.m_GfxBackend, "OpenGL");
|
|
g_Config.m_GfxGLMajor = 3;
|
|
g_Config.m_GfxGLMinor = 0;
|
|
g_Config.m_GfxGLPatch = 0;
|
|
// do another analysis round too, just in case
|
|
g_Config.m_Gfx3DTextureAnalysisRan = 0;
|
|
g_Config.m_GfxDriverIsBlocked = 0;
|
|
|
|
SDL_setenv("DDNET_DRIVER", "OpenGL", 1);
|
|
m_BackendType = DetectBackend();
|
|
}
|
|
}
|
|
|
|
ClampDriverVersion(m_BackendType);
|
|
|
|
bool UseModernGL = IsModernAPI(m_BackendType);
|
|
|
|
bool IsOpenGLFamilyBackend = m_BackendType == BACKEND_TYPE_OPENGL || m_BackendType == BACKEND_TYPE_OPENGL_ES;
|
|
|
|
if(IsOpenGLFamilyBackend)
|
|
{
|
|
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, g_Config.m_GfxGLMajor);
|
|
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, g_Config.m_GfxGLMinor);
|
|
}
|
|
|
|
dbg_msg("gfx", "Created %s %d.%d context.", ((m_BackendType == BACKEND_TYPE_VULKAN) ? "Vulkan" : "OpenGL"), g_Config.m_GfxGLMajor, g_Config.m_GfxGLMinor);
|
|
|
|
if(m_BackendType == BACKEND_TYPE_OPENGL)
|
|
{
|
|
if(g_Config.m_GfxGLMajor == 3 && g_Config.m_GfxGLMinor == 0)
|
|
{
|
|
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_COMPATIBILITY);
|
|
}
|
|
else if(UseModernGL)
|
|
{
|
|
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
|
|
}
|
|
}
|
|
else if(m_BackendType == BACKEND_TYPE_OPENGL_ES)
|
|
{
|
|
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);
|
|
}
|
|
|
|
if(IsOpenGLFamilyBackend)
|
|
{
|
|
*pFsaaSamples = std::clamp(*pFsaaSamples, 0, 8);
|
|
}
|
|
|
|
// set screen
|
|
SDL_Rect ScreenPos;
|
|
m_NumScreens = SDL_GetNumVideoDisplays();
|
|
if(m_NumScreens > 0)
|
|
{
|
|
*pScreen = clamp(*pScreen, 0, m_NumScreens - 1);
|
|
if(SDL_GetDisplayBounds(*pScreen, &ScreenPos) != 0)
|
|
{
|
|
dbg_msg("gfx", "unable to retrieve screen information: %s", SDL_GetError());
|
|
return EGraphicsBackendErrorCodes::GRAPHICS_BACKEND_ERROR_CODE_SDL_SCREEN_INFO_REQUEST_FAILED;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
dbg_msg("gfx", "unable to retrieve number of screens: %s", SDL_GetError());
|
|
return EGraphicsBackendErrorCodes::GRAPHICS_BACKEND_ERROR_CODE_SDL_SCREEN_REQUEST_FAILED;
|
|
}
|
|
|
|
// store desktop resolution for settings reset button
|
|
SDL_DisplayMode DisplayMode;
|
|
if(SDL_GetDesktopDisplayMode(*pScreen, &DisplayMode))
|
|
{
|
|
dbg_msg("gfx", "unable to get desktop resolution: %s", SDL_GetError());
|
|
return EGraphicsBackendErrorCodes::GRAPHICS_BACKEND_ERROR_CODE_SDL_SCREEN_RESOLUTION_REQUEST_FAILED;
|
|
}
|
|
|
|
bool IsDesktopChanged = *pDesktopWidth == 0 || *pDesktopHeight == 0 || *pDesktopWidth != DisplayMode.w || *pDesktopHeight != DisplayMode.h;
|
|
|
|
*pDesktopWidth = DisplayMode.w;
|
|
*pDesktopHeight = DisplayMode.h;
|
|
|
|
// fetch supported video modes
|
|
bool SupportedResolution = false;
|
|
|
|
CVideoMode aModes[256];
|
|
int ModesCount = 0;
|
|
int IndexOfResolution = -1;
|
|
GetVideoModes(aModes, std::size(aModes), &ModesCount, 1, *pDesktopWidth, *pDesktopHeight, *pScreen);
|
|
|
|
for(int i = 0; i < ModesCount; i++)
|
|
{
|
|
if(*pWidth == aModes[i].m_WindowWidth && *pHeight == aModes[i].m_WindowHeight && (*pRefreshRate == aModes[i].m_RefreshRate || *pRefreshRate == 0))
|
|
{
|
|
SupportedResolution = true;
|
|
IndexOfResolution = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// set flags
|
|
int SdlFlags = SDL_WINDOW_INPUT_GRABBED | SDL_WINDOW_INPUT_FOCUS | SDL_WINDOW_MOUSE_FOCUS;
|
|
SdlFlags |= (IsOpenGLFamilyBackend) ? SDL_WINDOW_OPENGL : SDL_WINDOW_VULKAN;
|
|
if(Flags & IGraphicsBackend::INITFLAG_HIGHDPI)
|
|
SdlFlags |= SDL_WINDOW_ALLOW_HIGHDPI;
|
|
if(Flags & IGraphicsBackend::INITFLAG_RESIZABLE)
|
|
SdlFlags |= SDL_WINDOW_RESIZABLE;
|
|
if(Flags & IGraphicsBackend::INITFLAG_BORDERLESS)
|
|
SdlFlags |= SDL_WINDOW_BORDERLESS;
|
|
if(Flags & IGraphicsBackend::INITFLAG_FULLSCREEN)
|
|
SdlFlags |= SDL_WINDOW_FULLSCREEN;
|
|
else if(Flags & (IGraphicsBackend::INITFLAG_DESKTOP_FULLSCREEN))
|
|
SdlFlags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
|
|
|
|
bool IsFullscreen = (SdlFlags & SDL_WINDOW_FULLSCREEN) != 0 || g_Config.m_GfxFullscreen == 3;
|
|
// use desktop resolution as default resolution, clamp resolution if users's display is smaller than we remembered
|
|
// if the user starts in fullscreen, and the resolution was not found use the desktop one
|
|
if((IsFullscreen && !SupportedResolution) || *pWidth == 0 || *pHeight == 0 || (IsDesktopChanged && (!SupportedResolution || !IsFullscreen) && (*pWidth > *pDesktopWidth || *pHeight > *pDesktopHeight)))
|
|
{
|
|
*pWidth = *pDesktopWidth;
|
|
*pHeight = *pDesktopHeight;
|
|
*pRefreshRate = DisplayMode.refresh_rate;
|
|
}
|
|
|
|
// if in fullscreen and refresh rate wasn't set yet, just use the one from the found list
|
|
if(*pRefreshRate == 0 && SupportedResolution)
|
|
{
|
|
*pRefreshRate = aModes[IndexOfResolution].m_RefreshRate;
|
|
}
|
|
else if(*pRefreshRate == 0)
|
|
{
|
|
*pRefreshRate = DisplayMode.refresh_rate;
|
|
}
|
|
|
|
// set gl attributes
|
|
if(IsOpenGLFamilyBackend)
|
|
{
|
|
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
|
|
if(*pFsaaSamples)
|
|
{
|
|
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1);
|
|
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, *pFsaaSamples);
|
|
}
|
|
else
|
|
{
|
|
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 0);
|
|
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 0);
|
|
}
|
|
}
|
|
|
|
m_pWindow = SDL_CreateWindow(
|
|
pName,
|
|
SDL_WINDOWPOS_CENTERED_DISPLAY(*pScreen),
|
|
SDL_WINDOWPOS_CENTERED_DISPLAY(*pScreen),
|
|
*pWidth,
|
|
*pHeight,
|
|
SdlFlags);
|
|
|
|
// set caption
|
|
if(m_pWindow == NULL)
|
|
{
|
|
dbg_msg("gfx", "unable to create window: %s", SDL_GetError());
|
|
if(m_BackendType == BACKEND_TYPE_VULKAN)
|
|
return EGraphicsBackendErrorCodes::GRAPHICS_BACKEND_ERROR_CODE_GL_CONTEXT_FAILED;
|
|
else
|
|
return EGraphicsBackendErrorCodes::GRAPHICS_BACKEND_ERROR_CODE_SDL_WINDOW_CREATE_FAILED;
|
|
}
|
|
|
|
int GlewMajor = 0;
|
|
int GlewMinor = 0;
|
|
int GlewPatch = 0;
|
|
|
|
if(IsOpenGLFamilyBackend)
|
|
{
|
|
m_GLContext = SDL_GL_CreateContext(m_pWindow);
|
|
|
|
if(m_GLContext == NULL)
|
|
{
|
|
SDL_DestroyWindow(m_pWindow);
|
|
m_pWindow = nullptr;
|
|
dbg_msg("gfx", "unable to create graphic context: %s", SDL_GetError());
|
|
return EGraphicsBackendErrorCodes::GRAPHICS_BACKEND_ERROR_CODE_GL_CONTEXT_FAILED;
|
|
}
|
|
|
|
if(!BackendInitGlew(m_BackendType, GlewMajor, GlewMinor, GlewPatch))
|
|
{
|
|
SDL_GL_DeleteContext(m_GLContext);
|
|
SDL_DestroyWindow(m_pWindow);
|
|
m_pWindow = nullptr;
|
|
return EGraphicsBackendErrorCodes::GRAPHICS_BACKEND_ERROR_CODE_UNKNOWN;
|
|
}
|
|
}
|
|
|
|
int InitError = 0;
|
|
const char *pErrorStr = NULL;
|
|
|
|
InitError = IsVersionSupportedGlew(m_BackendType, g_Config.m_GfxGLMajor, g_Config.m_GfxGLMinor, g_Config.m_GfxGLPatch, GlewMajor, GlewMinor, GlewPatch);
|
|
|
|
// SDL_GL_GetDrawableSize reports HiDPI resolution even with SDL_WINDOW_ALLOW_HIGHDPI not set, which is wrong
|
|
if(SdlFlags & SDL_WINDOW_ALLOW_HIGHDPI && IsOpenGLFamilyBackend)
|
|
SDL_GL_GetDrawableSize(m_pWindow, pCurrentWidth, pCurrentHeight);
|
|
else
|
|
SDL_GetWindowSize(m_pWindow, pCurrentWidth, pCurrentHeight);
|
|
|
|
if(IsOpenGLFamilyBackend)
|
|
{
|
|
SDL_GL_SetSwapInterval(Flags & IGraphicsBackend::INITFLAG_VSYNC ? 1 : 0);
|
|
SDL_GL_MakeCurrent(NULL, NULL);
|
|
}
|
|
|
|
if(InitError != 0)
|
|
{
|
|
if(m_GLContext)
|
|
SDL_GL_DeleteContext(m_GLContext);
|
|
SDL_DestroyWindow(m_pWindow);
|
|
m_pWindow = nullptr;
|
|
|
|
// try setting to glew supported version
|
|
g_Config.m_GfxGLMajor = GlewMajor;
|
|
g_Config.m_GfxGLMinor = GlewMinor;
|
|
g_Config.m_GfxGLPatch = GlewPatch;
|
|
|
|
return EGraphicsBackendErrorCodes::GRAPHICS_BACKEND_ERROR_CODE_GL_VERSION_FAILED;
|
|
}
|
|
#endif // CONF_HEADLESS_CLIENT
|
|
|
|
// start the command processor
|
|
dbg_assert(m_pProcessor == nullptr, "Processor was not cleaned up properly.");
|
|
m_pProcessor = new CCommandProcessor_SDL_GL(m_BackendType, g_Config.m_GfxGLMajor, g_Config.m_GfxGLMinor, g_Config.m_GfxGLPatch);
|
|
StartProcessor(m_pProcessor);
|
|
|
|
// issue init commands for OpenGL and SDL
|
|
CCommandBuffer CmdBuffer(1024, 512);
|
|
CCommandProcessorFragment_GLBase::SCommand_PreInit CmdPre;
|
|
CmdPre.m_pWindow = m_pWindow;
|
|
CmdPre.m_Width = *pCurrentWidth;
|
|
CmdPre.m_Height = *pCurrentHeight;
|
|
CmdPre.m_pVendorString = m_aVendorString;
|
|
CmdPre.m_pVersionString = m_aVersionString;
|
|
CmdPre.m_pRendererString = m_aRendererString;
|
|
CmdPre.m_pGPUList = &m_GPUList;
|
|
CmdBuffer.AddCommandUnsafe(CmdPre);
|
|
RunBufferSingleThreadedUnsafe(&CmdBuffer);
|
|
CmdBuffer.Reset();
|
|
|
|
// run sdl first to have the context in the thread
|
|
CCommandProcessorFragment_SDL::SCommand_Init CmdSDL;
|
|
CmdSDL.m_pWindow = m_pWindow;
|
|
CmdSDL.m_GLContext = m_GLContext;
|
|
CmdBuffer.AddCommandUnsafe(CmdSDL);
|
|
RunBuffer(&CmdBuffer);
|
|
WaitForIdle();
|
|
CmdBuffer.Reset();
|
|
|
|
if(InitError == 0)
|
|
{
|
|
CCommandProcessorFragment_GLBase::SCommand_Init CmdGL;
|
|
CmdGL.m_pWindow = m_pWindow;
|
|
CmdGL.m_Width = *pCurrentWidth;
|
|
CmdGL.m_Height = *pCurrentHeight;
|
|
CmdGL.m_pTextureMemoryUsage = &m_TextureMemoryUsage;
|
|
CmdGL.m_pBufferMemoryUsage = &m_BufferMemoryUsage;
|
|
CmdGL.m_pStreamMemoryUsage = &m_StreamMemoryUsage;
|
|
CmdGL.m_pStagingMemoryUsage = &m_StagingMemoryUsage;
|
|
CmdGL.m_pGPUList = &m_GPUList;
|
|
CmdGL.m_pReadPresentedImageDataFunc = &m_ReadPresentedImageDataFunc;
|
|
CmdGL.m_pStorage = pStorage;
|
|
CmdGL.m_pCapabilities = &m_Capabilites;
|
|
CmdGL.m_pInitError = &InitError;
|
|
CmdGL.m_RequestedMajor = g_Config.m_GfxGLMajor;
|
|
CmdGL.m_RequestedMinor = g_Config.m_GfxGLMinor;
|
|
CmdGL.m_RequestedPatch = g_Config.m_GfxGLPatch;
|
|
CmdGL.m_GlewMajor = GlewMajor;
|
|
CmdGL.m_GlewMinor = GlewMinor;
|
|
CmdGL.m_GlewPatch = GlewPatch;
|
|
CmdGL.m_pErrStringPtr = &pErrorStr;
|
|
CmdGL.m_pVendorString = m_aVendorString;
|
|
CmdGL.m_pVersionString = m_aVersionString;
|
|
CmdGL.m_pRendererString = m_aRendererString;
|
|
CmdGL.m_RequestedBackend = m_BackendType;
|
|
CmdBuffer.AddCommandUnsafe(CmdGL);
|
|
|
|
RunBuffer(&CmdBuffer);
|
|
WaitForIdle();
|
|
CmdBuffer.Reset();
|
|
}
|
|
|
|
if(InitError != 0)
|
|
{
|
|
if(InitError != -2)
|
|
{
|
|
// shutdown the context, as it might have been initialized
|
|
CCommandProcessorFragment_GLBase::SCommand_Shutdown CmdGL;
|
|
CmdBuffer.AddCommandUnsafe(CmdGL);
|
|
RunBuffer(&CmdBuffer);
|
|
WaitForIdle();
|
|
CmdBuffer.Reset();
|
|
}
|
|
|
|
CCommandProcessorFragment_SDL::SCommand_Shutdown Cmd;
|
|
CmdBuffer.AddCommandUnsafe(Cmd);
|
|
RunBuffer(&CmdBuffer);
|
|
WaitForIdle();
|
|
CmdBuffer.Reset();
|
|
|
|
CCommandProcessorFragment_GLBase::SCommand_PostShutdown CmdPost;
|
|
CmdBuffer.AddCommandUnsafe(CmdPost);
|
|
RunBufferSingleThreadedUnsafe(&CmdBuffer);
|
|
CmdBuffer.Reset();
|
|
|
|
// stop and delete the processor
|
|
StopProcessor();
|
|
delete m_pProcessor;
|
|
m_pProcessor = nullptr;
|
|
|
|
if(m_GLContext)
|
|
SDL_GL_DeleteContext(m_GLContext);
|
|
SDL_DestroyWindow(m_pWindow);
|
|
m_pWindow = nullptr;
|
|
|
|
// try setting to version string's supported version
|
|
if(InitError == -2)
|
|
{
|
|
g_Config.m_GfxGLMajor = m_Capabilites.m_ContextMajor;
|
|
g_Config.m_GfxGLMinor = m_Capabilites.m_ContextMinor;
|
|
g_Config.m_GfxGLPatch = m_Capabilites.m_ContextPatch;
|
|
}
|
|
|
|
if(pErrorStr != NULL)
|
|
{
|
|
str_copy(m_aErrorString, pErrorStr);
|
|
}
|
|
|
|
return EGraphicsBackendErrorCodes::GRAPHICS_BACKEND_ERROR_CODE_GL_VERSION_FAILED;
|
|
}
|
|
|
|
{
|
|
CCommandBuffer::SCommand_Update_Viewport CmdSDL2;
|
|
CmdSDL2.m_X = 0;
|
|
CmdSDL2.m_Y = 0;
|
|
|
|
CmdSDL2.m_Width = *pCurrentWidth;
|
|
CmdSDL2.m_Height = *pCurrentHeight;
|
|
CmdSDL2.m_ByResize = true;
|
|
CmdBuffer.AddCommandUnsafe(CmdSDL2);
|
|
RunBuffer(&CmdBuffer);
|
|
WaitForIdle();
|
|
CmdBuffer.Reset();
|
|
}
|
|
|
|
// return
|
|
return EGraphicsBackendErrorCodes::GRAPHICS_BACKEND_ERROR_CODE_NONE;
|
|
}
|
|
|
|
int CGraphicsBackend_SDL_GL::Shutdown()
|
|
{
|
|
// issue a shutdown command
|
|
CCommandBuffer CmdBuffer(1024, 512);
|
|
CCommandProcessorFragment_GLBase::SCommand_Shutdown CmdGL;
|
|
CmdBuffer.AddCommandUnsafe(CmdGL);
|
|
RunBuffer(&CmdBuffer);
|
|
WaitForIdle();
|
|
CmdBuffer.Reset();
|
|
|
|
CCommandProcessorFragment_SDL::SCommand_Shutdown Cmd;
|
|
CmdBuffer.AddCommandUnsafe(Cmd);
|
|
RunBuffer(&CmdBuffer);
|
|
WaitForIdle();
|
|
CmdBuffer.Reset();
|
|
|
|
CCommandProcessorFragment_GLBase::SCommand_PostShutdown CmdPost;
|
|
CmdBuffer.AddCommandUnsafe(CmdPost);
|
|
RunBufferSingleThreadedUnsafe(&CmdBuffer);
|
|
CmdBuffer.Reset();
|
|
|
|
// stop and delete the processor
|
|
StopProcessor();
|
|
delete m_pProcessor;
|
|
m_pProcessor = nullptr;
|
|
|
|
if(m_GLContext != nullptr)
|
|
SDL_GL_DeleteContext(m_GLContext);
|
|
SDL_DestroyWindow(m_pWindow);
|
|
m_pWindow = nullptr;
|
|
|
|
SDL_QuitSubSystem(SDL_INIT_VIDEO);
|
|
return 0;
|
|
}
|
|
|
|
uint64_t CGraphicsBackend_SDL_GL::TextureMemoryUsage() const
|
|
{
|
|
return m_TextureMemoryUsage;
|
|
}
|
|
|
|
uint64_t CGraphicsBackend_SDL_GL::BufferMemoryUsage() const
|
|
{
|
|
return m_BufferMemoryUsage;
|
|
}
|
|
|
|
uint64_t CGraphicsBackend_SDL_GL::StreamedMemoryUsage() const
|
|
{
|
|
return m_StreamMemoryUsage;
|
|
}
|
|
|
|
uint64_t CGraphicsBackend_SDL_GL::StagingMemoryUsage() const
|
|
{
|
|
return m_StagingMemoryUsage;
|
|
}
|
|
|
|
const TTWGraphicsGPUList &CGraphicsBackend_SDL_GL::GetGPUs() const
|
|
{
|
|
return m_GPUList;
|
|
}
|
|
|
|
void CGraphicsBackend_SDL_GL::Minimize()
|
|
{
|
|
SDL_MinimizeWindow(m_pWindow);
|
|
}
|
|
|
|
void CGraphicsBackend_SDL_GL::Maximize()
|
|
{
|
|
// TODO: SDL
|
|
}
|
|
|
|
void CGraphicsBackend_SDL_GL::SetWindowParams(int FullscreenMode, bool IsBorderless, bool AllowResizing)
|
|
{
|
|
if(FullscreenMode > 0)
|
|
{
|
|
bool IsDesktopFullscreen = FullscreenMode == 2;
|
|
#ifndef CONF_FAMILY_WINDOWS
|
|
// special mode for windows only
|
|
IsDesktopFullscreen |= FullscreenMode == 3;
|
|
#endif
|
|
if(FullscreenMode == 1)
|
|
{
|
|
#if defined(CONF_PLATFORM_MACOS) || defined(CONF_PLATFORM_HAIKU)
|
|
// Todo SDL: remove this when fixed (game freezes when losing focus in fullscreen)
|
|
SDL_SetWindowFullscreen(m_pWindow, SDL_WINDOW_FULLSCREEN_DESKTOP);
|
|
#else
|
|
SDL_SetWindowFullscreen(m_pWindow, SDL_WINDOW_FULLSCREEN);
|
|
#endif
|
|
SDL_SetWindowResizable(m_pWindow, SDL_TRUE);
|
|
}
|
|
else if(IsDesktopFullscreen)
|
|
{
|
|
SDL_SetWindowFullscreen(m_pWindow, SDL_WINDOW_FULLSCREEN_DESKTOP);
|
|
SDL_SetWindowResizable(m_pWindow, SDL_TRUE);
|
|
}
|
|
else
|
|
{
|
|
SDL_SetWindowFullscreen(m_pWindow, 0);
|
|
SDL_SetWindowBordered(m_pWindow, SDL_TRUE);
|
|
SDL_SetWindowResizable(m_pWindow, SDL_FALSE);
|
|
SDL_DisplayMode DPMode;
|
|
if(SDL_GetDesktopDisplayMode(g_Config.m_GfxScreen, &DPMode) < 0)
|
|
{
|
|
dbg_msg("gfx", "unable to get display mode: %s", SDL_GetError());
|
|
}
|
|
else
|
|
{
|
|
ResizeWindow(DPMode.w, DPMode.h, DPMode.refresh_rate);
|
|
SDL_SetWindowPosition(m_pWindow, SDL_WINDOWPOS_CENTERED_DISPLAY(g_Config.m_GfxScreen), SDL_WINDOWPOS_CENTERED_DISPLAY(g_Config.m_GfxScreen));
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
SDL_SetWindowFullscreen(m_pWindow, 0);
|
|
SDL_SetWindowBordered(m_pWindow, SDL_bool(!IsBorderless));
|
|
SDL_SetWindowResizable(m_pWindow, SDL_TRUE);
|
|
}
|
|
}
|
|
|
|
bool CGraphicsBackend_SDL_GL::SetWindowScreen(int Index)
|
|
{
|
|
if(Index < 0 || Index >= m_NumScreens)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
SDL_Rect ScreenPos;
|
|
if(SDL_GetDisplayBounds(Index, &ScreenPos) != 0)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
SDL_SetWindowPosition(m_pWindow,
|
|
SDL_WINDOWPOS_CENTERED_DISPLAY(Index),
|
|
SDL_WINDOWPOS_CENTERED_DISPLAY(Index));
|
|
|
|
return UpdateDisplayMode(Index);
|
|
}
|
|
|
|
bool CGraphicsBackend_SDL_GL::UpdateDisplayMode(int Index)
|
|
{
|
|
SDL_DisplayMode DisplayMode;
|
|
if(SDL_GetDesktopDisplayMode(Index, &DisplayMode) < 0)
|
|
{
|
|
dbg_msg("gfx", "unable to get display mode: %s", SDL_GetError());
|
|
return false;
|
|
}
|
|
|
|
g_Config.m_GfxDesktopWidth = DisplayMode.w;
|
|
g_Config.m_GfxDesktopHeight = DisplayMode.h;
|
|
|
|
return true;
|
|
}
|
|
|
|
int CGraphicsBackend_SDL_GL::GetWindowScreen()
|
|
{
|
|
return SDL_GetWindowDisplayIndex(m_pWindow);
|
|
}
|
|
|
|
int CGraphicsBackend_SDL_GL::WindowActive()
|
|
{
|
|
return m_pWindow && SDL_GetWindowFlags(m_pWindow) & SDL_WINDOW_INPUT_FOCUS;
|
|
}
|
|
|
|
int CGraphicsBackend_SDL_GL::WindowOpen()
|
|
{
|
|
return m_pWindow && SDL_GetWindowFlags(m_pWindow) & SDL_WINDOW_SHOWN;
|
|
}
|
|
|
|
void CGraphicsBackend_SDL_GL::SetWindowGrab(bool Grab)
|
|
{
|
|
SDL_SetWindowGrab(m_pWindow, Grab ? SDL_TRUE : SDL_FALSE);
|
|
}
|
|
|
|
bool CGraphicsBackend_SDL_GL::ResizeWindow(int w, int h, int RefreshRate)
|
|
{
|
|
// don't call resize events when the window is at fullscreen desktop
|
|
if(!m_pWindow || (SDL_GetWindowFlags(m_pWindow) & SDL_WINDOW_FULLSCREEN_DESKTOP) == SDL_WINDOW_FULLSCREEN_DESKTOP)
|
|
return false;
|
|
|
|
// if the window is at fullscreen use SDL_SetWindowDisplayMode instead, suggested by SDL
|
|
if(SDL_GetWindowFlags(m_pWindow) & SDL_WINDOW_FULLSCREEN)
|
|
{
|
|
#ifdef CONF_FAMILY_WINDOWS
|
|
// in windows make the window windowed mode first, this prevents strange window glitches (other games probably do something similar)
|
|
SetWindowParams(0, true, true);
|
|
#endif
|
|
SDL_DisplayMode SetMode = {};
|
|
SDL_DisplayMode ClosestMode = {};
|
|
SetMode.format = 0;
|
|
SetMode.w = w;
|
|
SetMode.h = h;
|
|
SetMode.refresh_rate = RefreshRate;
|
|
SDL_SetWindowDisplayMode(m_pWindow, SDL_GetClosestDisplayMode(g_Config.m_GfxScreen, &SetMode, &ClosestMode));
|
|
#ifdef CONF_FAMILY_WINDOWS
|
|
// now change it back to fullscreen, this will restore the above set state, bcs SDL saves fullscreen modes apart from other video modes (as of SDL 2.0.16)
|
|
// see implementation of SDL_SetWindowDisplayMode
|
|
SetWindowParams(1, false, true);
|
|
#endif
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
SDL_SetWindowSize(m_pWindow, w, h);
|
|
if(SDL_GetWindowFlags(m_pWindow) & SDL_WINDOW_MAXIMIZED)
|
|
// remove maximize flag
|
|
SDL_RestoreWindow(m_pWindow);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void CGraphicsBackend_SDL_GL::GetViewportSize(int &w, int &h)
|
|
{
|
|
SDL_GL_GetDrawableSize(m_pWindow, &w, &h);
|
|
}
|
|
|
|
void CGraphicsBackend_SDL_GL::NotifyWindow()
|
|
{
|
|
// Minimum version 2.0.16, after version 2.0.22 the naming is changed to 2.24.0 etc.
|
|
#if SDL_MAJOR_VERSION > 2 || (SDL_MAJOR_VERSION == 2 && SDL_MINOR_VERSION == 0 && SDL_PATCHLEVEL >= 16) || (SDL_MAJOR_VERSION == 2 && SDL_MINOR_VERSION > 0)
|
|
if(SDL_FlashWindow(m_pWindow, SDL_FlashOperation::SDL_FLASH_UNTIL_FOCUSED) != 0)
|
|
{
|
|
// fails if SDL hasn't implemented it
|
|
return;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void CGraphicsBackend_SDL_GL::WindowDestroyNtf(uint32_t WindowID)
|
|
{
|
|
}
|
|
|
|
void CGraphicsBackend_SDL_GL::WindowCreateNtf(uint32_t WindowID)
|
|
{
|
|
m_pWindow = SDL_GetWindowFromID(WindowID);
|
|
}
|
|
|
|
TGLBackendReadPresentedImageData &CGraphicsBackend_SDL_GL::GetReadPresentedImageDataFuncUnsafe()
|
|
{
|
|
return m_ReadPresentedImageDataFunc;
|
|
}
|
|
|
|
IGraphicsBackend *CreateGraphicsBackend(TTranslateFunc &&TranslateFunc) { return new CGraphicsBackend_SDL_GL(std::move(TranslateFunc)); }
|