diff --git a/src/util/opengl_context_wgl.cpp b/src/util/opengl_context_wgl.cpp index b483cda5b..1a9893165 100644 --- a/src/util/opengl_context_wgl.cpp +++ b/src/util/opengl_context_wgl.cpp @@ -2,8 +2,8 @@ // SPDX-License-Identifier: CC-BY-NC-ND-4.0 #include "opengl_context_wgl.h" -#include "opengl_loader.h" #include "gpu_texture.h" +#include "opengl_loader.h" #include "common/assert.h" #include "common/dynamic_library.h" @@ -17,8 +17,6 @@ LOG_CHANNEL(GPUDevice); #pragma clang diagnostic ignored "-Wmicrosoft-cast" #endif -namespace dyn_libs { - #define OPENGL_FUNCTIONS(X) \ X(wglCreateContext) \ X(wglDeleteContext) \ @@ -28,64 +26,61 @@ namespace dyn_libs { X(wglMakeCurrent) \ X(wglShareLists) +namespace { +struct Locals +{ + DynamicLibrary opengl_library; + std::once_flag opengl_once_flag; + +#define DECLARE_OPENGL_FUNCTION(F) decltype(&::F) F; + OPENGL_FUNCTIONS(DECLARE_OPENGL_FUNCTION) +#undef DECLARE_OPENGL_FUNCTION +}; +} // namespace + static bool LoadOpenGLLibrary(Error* error); -static void CloseOpenGLLibrary(); static void* GetProcAddressCallback(const char* name); -static DynamicLibrary s_opengl_library; - -#define DECLARE_OPENGL_FUNCTION(F) static decltype(&::F) F; -OPENGL_FUNCTIONS(DECLARE_OPENGL_FUNCTION) -#undef DECLARE_OPENGL_FUNCTION -} // namespace dyn_libs +static Locals s_locals; -bool dyn_libs::LoadOpenGLLibrary(Error* error) +bool LoadOpenGLLibrary(Error* error) { - if (s_opengl_library.IsOpen()) + if (s_locals.opengl_library.IsOpen()) return true; - else if (!s_opengl_library.Open("opengl32.dll", error)) - return false; - - bool result = true; -#define RESOLVE_OPENGL_FUNCTION(F) result = result && s_opengl_library.GetSymbol(#F, &F); - OPENGL_FUNCTIONS(RESOLVE_OPENGL_FUNCTION); -#undef RESOLVE_OPENGL_FUNCTION - if (!result) - { - CloseOpenGLLibrary(); - Error::SetStringView(error, "One or more required functions from opengl32.dll is missing."); - return false; - } + std::call_once(s_locals.opengl_once_flag, [error]() { + if (!s_locals.opengl_library.Open("opengl32.dll", error)) + { + Error::AddPrefix(error, "Failed to load xcb: "); + return; + } - std::atexit(&CloseOpenGLLibrary); - return true; -} + static const DynamicLibrary::SymbolTable symbols[] = { +#define SYMBOL_ENTRY(F) {#F, reinterpret_cast(&s_locals.F)}, + OPENGL_FUNCTIONS(SYMBOL_ENTRY) +#undef SYMBOL_ENTRY + }; -void dyn_libs::CloseOpenGLLibrary() -{ -#define CLOSE_OPENGL_FUNCTION(F) F = nullptr; - OPENGL_FUNCTIONS(CLOSE_OPENGL_FUNCTION); -#undef CLOSE_OPENGL_FUNCTION + if (!s_locals.opengl_library.ResolveSymbols(symbols, error)) + DynamicLibrary::ClearSymbols(symbols); + }); - s_opengl_library.Close(); + return s_locals.opengl_library.IsOpen(); } -#undef OPENGL_FUNCTIONS - -void* dyn_libs::GetProcAddressCallback(const char* name) +void* GetProcAddressCallback(const char* name) { - void* addr = dyn_libs::wglGetProcAddress(name); + void* addr = s_locals.wglGetProcAddress(name); if (addr) return addr; // try opengl32.dll - return s_opengl_library.GetSymbolAddress(name); + return s_locals.opengl_library.GetSymbolAddress(name); } static bool ReloadWGL(HDC dc, Error* error) { - if (!gladLoadWGL(dc, [](const char* name) { return (GLADapiproc)dyn_libs::wglGetProcAddress(name); })) + if (!gladLoadWGL(dc, [](const char* name) { return (GLADapiproc)s_locals.wglGetProcAddress(name); })) { Error::SetStringView(error, "Loading GLAD WGL functions failed"); return false; @@ -100,10 +95,10 @@ OpenGLContextWGL::~OpenGLContextWGL() { if (m_rc) { - if (dyn_libs::wglGetCurrentContext() == m_rc) - dyn_libs::wglMakeCurrent(nullptr, nullptr); + if (s_locals.wglGetCurrentContext() == m_rc) + s_locals.wglMakeCurrent(nullptr, nullptr); - dyn_libs::wglDeleteContext(m_rc); + s_locals.wglDeleteContext(m_rc); } if (m_pbuffer) @@ -119,7 +114,7 @@ std::unique_ptr OpenGLContextWGL::Create(WindowInfo& wi, SurfaceH std::span versions_to_try, Error* error) { std::unique_ptr context = std::make_unique(); - if (!dyn_libs::LoadOpenGLLibrary(error) || !context->Initialize(wi, surface, versions_to_try, error)) + if (!LoadOpenGLLibrary(error) || !context->Initialize(wi, surface, versions_to_try, error)) context.reset(); return context; @@ -159,7 +154,7 @@ bool OpenGLContextWGL::Initialize(WindowInfo& wi, SurfaceHandle* surface, std::s void* OpenGLContextWGL::GetProcAddress(const char* name) { - return dyn_libs::GetProcAddressCallback(name); + return GetProcAddressCallback(name); } OpenGLContext::SurfaceHandle OpenGLContextWGL::CreateSurface(WindowInfo& wi, Error* error /*= nullptr*/) @@ -180,7 +175,7 @@ void OpenGLContextWGL::DestroySurface(SurfaceHandle handle) return; // current buffer? switch to pbuffer first - if (dyn_libs::wglGetCurrentDC() == static_cast(handle)) + if (s_locals.wglGetCurrentDC() == static_cast(handle)) MakeCurrent(nullptr); DeleteDC(static_cast(handle)); @@ -201,7 +196,7 @@ bool OpenGLContextWGL::SwapBuffers() bool OpenGLContextWGL::IsCurrent() const { - return (m_rc && dyn_libs::wglGetCurrentContext() == m_rc); + return (m_rc && s_locals.wglGetCurrentContext() == m_rc); } bool OpenGLContextWGL::MakeCurrent(SurfaceHandle surface, Error* error /* = nullptr */) @@ -212,9 +207,11 @@ bool OpenGLContextWGL::MakeCurrent(SurfaceHandle surface, Error* error /* = null else if (m_current_dc == new_dc) return true; - if (!dyn_libs::wglMakeCurrent(new_dc, m_rc)) + if (!s_locals.wglMakeCurrent(new_dc, m_rc)) { - ERROR_LOG("wglMakeCurrent() failed: {}", GetLastError()); + const DWORD err = GetLastError(); + ERROR_LOG("wglMakeCurrent() failed: {}", err); + Error::SetWin32(error, "wglMakeCurrent() failed: ", err); return false; } @@ -224,7 +221,7 @@ bool OpenGLContextWGL::MakeCurrent(SurfaceHandle surface, Error* error /* = null bool OpenGLContextWGL::DoneCurrent() { - if (!dyn_libs::wglMakeCurrent(m_current_dc, nullptr)) + if (!s_locals.wglMakeCurrent(m_current_dc, nullptr)) return false; m_current_dc = nullptr; @@ -388,16 +385,16 @@ HDC OpenGLContextWGL::GetPBufferDC(Error* error) ScopedGuard temp_rc_guard([&temp_rc, hdc]() { if (temp_rc) { - dyn_libs::wglMakeCurrent(hdc, nullptr); - dyn_libs::wglDeleteContext(temp_rc); + s_locals.wglMakeCurrent(hdc, nullptr); + s_locals.wglDeleteContext(temp_rc); } }); if (!GLAD_WGL_ARB_pbuffer) { // we're probably running completely surfaceless... need a temporary context. - temp_rc = dyn_libs::wglCreateContext(hdc); - if (!temp_rc || !dyn_libs::wglMakeCurrent(hdc, temp_rc)) + temp_rc = s_locals.wglCreateContext(hdc); + if (!temp_rc || !s_locals.wglMakeCurrent(hdc, temp_rc)) { Error::SetStringView(error, "Failed to create temporary context to load WGL for pbuffer."); return NULL; @@ -444,7 +441,7 @@ HDC OpenGLContextWGL::GetPBufferDC(Error* error) bool OpenGLContextWGL::CreateAnyContext(HDC hdc, HGLRC share_context, bool make_current, Error* error) { - m_rc = dyn_libs::wglCreateContext(hdc); + m_rc = s_locals.wglCreateContext(hdc); if (!m_rc) { Error::SetWin32(error, "wglCreateContext() failed: ", GetLastError()); @@ -453,7 +450,7 @@ bool OpenGLContextWGL::CreateAnyContext(HDC hdc, HGLRC share_context, bool make_ if (make_current) { - if (!dyn_libs::wglMakeCurrent(hdc, m_rc)) + if (!s_locals.wglMakeCurrent(hdc, m_rc)) { Error::SetWin32(error, "wglMakeCurrent() failed: ", GetLastError()); return false; @@ -469,7 +466,7 @@ bool OpenGLContextWGL::CreateAnyContext(HDC hdc, HGLRC share_context, bool make_ } } - if (share_context && !dyn_libs::wglShareLists(share_context, m_rc)) + if (share_context && !s_locals.wglShareLists(share_context, m_rc)) { Error::SetWin32(error, "wglShareLists() failed: ", GetLastError()); return false; @@ -542,10 +539,10 @@ bool OpenGLContextWGL::CreateVersionContext(const Version& version, HDC hdc, HGL // destroy and swap contexts if (m_rc) { - if (!dyn_libs::wglMakeCurrent(hdc, make_current ? new_rc : nullptr)) + if (!s_locals.wglMakeCurrent(hdc, make_current ? new_rc : nullptr)) { Error::SetWin32(error, "wglMakeCurrent() failed: ", GetLastError()); - dyn_libs::wglDeleteContext(new_rc); + s_locals.wglDeleteContext(new_rc); return false; } @@ -555,7 +552,7 @@ bool OpenGLContextWGL::CreateVersionContext(const Version& version, HDC hdc, HGL if (make_current && !ReloadWGL(hdc, error)) return false; - dyn_libs::wglDeleteContext(m_rc); + s_locals.wglDeleteContext(m_rc); } m_rc = new_rc; diff --git a/src/util/x11_tools.cpp b/src/util/x11_tools.cpp index c962a7492..b2269e653 100644 --- a/src/util/x11_tools.cpp +++ b/src/util/x11_tools.cpp @@ -12,13 +12,10 @@ #include #include +#include LOG_CHANNEL(WindowInfo); -namespace { - -namespace dyn_libs { - #define XCB_FUNCTIONS(X) \ X(xcb_get_geometry) \ X(xcb_get_geometry_reply) \ @@ -39,19 +36,7 @@ namespace dyn_libs { X(xcb_free_colormap_checked) \ X(xcb_configure_window_checked) -static bool OpenXcb(Error* error); -static void CloseXcb(); -static void CloseAll(); - -static DynamicLibrary s_xcb_library; -static DynamicLibrary s_x11xcb_library; -static bool s_close_registered = false; - -#define ADD_FUNC(F) static decltype(&::F) F; -XCB_FUNCTIONS(ADD_FUNC); -#undef ADD_FUNC - -} // namespace dyn_libs +namespace { template struct XCBPointerDeleter @@ -62,52 +47,45 @@ struct XCBPointerDeleter template using XCBPointer = std::unique_ptr>; -} // namespace - -bool dyn_libs::OpenXcb(Error* error) +struct Locals { - if (s_xcb_library.IsOpen()) - return true; - - const std::string libname = DynamicLibrary::GetVersionedFilename("xcb", 1); - if (!s_xcb_library.Open(libname.c_str(), error)) - { - Error::AddPrefix(error, "Failed to load xcb: "); - return false; - } + DynamicLibrary xcb_library; + std::once_flag xcb_once_flag; -#define LOAD_FUNC(F) \ - if (!s_xcb_library.GetSymbol(#F, &F)) \ - { \ - Error::SetStringFmt(error, "Failed to find function {}", #F); \ - CloseXcb(); \ - return false; \ - } +#define ADD_FUNC(F) decltype(&::F) F; + XCB_FUNCTIONS(ADD_FUNC) +#undef ADD_FUNC +}; - XCB_FUNCTIONS(LOAD_FUNC) -#undef LOAD_FUNC +} // namespace - if (!s_close_registered) - { - s_close_registered = true; - std::atexit(&dyn_libs::CloseAll); - } +static bool OpenXcb(Error* error); - return true; -} +static Locals s_locals; -void dyn_libs::CloseXcb() +bool OpenXcb(Error* error) { -#define UNLOAD_FUNC(F) F = nullptr; - XCB_FUNCTIONS(UNLOAD_FUNC) -#undef UNLOAD_FUNC + if (s_locals.xcb_library.IsOpen()) + return true; - s_xcb_library.Close(); -} + std::call_once(s_locals.xcb_once_flag, [error]() { + if (!s_locals.xcb_library.Open(DynamicLibrary::GetVersionedFilename("xcb", 1).c_str(), error)) + { + Error::AddPrefix(error, "Failed to load xcb: "); + return; + } -void dyn_libs::CloseAll() -{ - CloseXcb(); + static const DynamicLibrary::SymbolTable symbols[] = { +#define SYMBOL_ENTRY(F) {#F, reinterpret_cast(&s_locals.F)}, + XCB_FUNCTIONS(SYMBOL_ENTRY) +#undef SYMBOL_ENTRY + }; + + if (!s_locals.xcb_library.ResolveSymbols(symbols, error)) + DynamicLibrary::ClearSymbols(symbols); + }); + + return s_locals.xcb_library.IsOpen(); } X11Window::X11Window() = default; @@ -163,14 +141,14 @@ bool X11Window::Create(xcb_connection_t* connection, xcb_window_t parent_window, { xcb_generic_error_t* xerror; - if (!dyn_libs::OpenXcb(error)) + if (!OpenXcb(error)) return false; m_connection = connection; m_parent_window = parent_window; XCBPointer gwa( - dyn_libs::xcb_get_geometry_reply(connection, dyn_libs::xcb_get_geometry(connection, parent_window), &xerror)); + s_locals.xcb_get_geometry_reply(connection, s_locals.xcb_get_geometry(connection, parent_window), &xerror)); if (!gwa) { SetErrorObject(error, "xcb_get_geometry_reply() failed: ", xerror); @@ -182,16 +160,16 @@ bool X11Window::Create(xcb_connection_t* connection, xcb_window_t parent_window, // Need to find the root window to get an appropriate depth. Needed for NVIDIA+XWayland. int visual_depth = XCB_COPY_FROM_PARENT; - for (xcb_screen_iterator_t it = dyn_libs::xcb_setup_roots_iterator(dyn_libs::xcb_get_setup(connection)); it.rem != 0; - dyn_libs::xcb_screen_next(&it)) + for (xcb_screen_iterator_t it = s_locals.xcb_setup_roots_iterator(s_locals.xcb_get_setup(connection)); it.rem != 0; + s_locals.xcb_screen_next(&it)) { if (it.data->root == gwa->root) { - for (xcb_depth_iterator_t dit = dyn_libs::xcb_screen_allowed_depths_iterator(it.data); dit.rem != 0; - dyn_libs::xcb_depth_next(&dit)) + for (xcb_depth_iterator_t dit = s_locals.xcb_screen_allowed_depths_iterator(it.data); dit.rem != 0; + s_locals.xcb_depth_next(&dit)) { - const int len = dyn_libs::xcb_depth_visuals_length(dit.data); - const xcb_visualtype_t* visuals = dyn_libs::xcb_depth_visuals(dit.data); + const int len = s_locals.xcb_depth_visuals_length(dit.data); + const xcb_visualtype_t* visuals = s_locals.xcb_depth_visuals(dit.data); int idx = 0; for (; idx < len; idx++) { @@ -210,24 +188,24 @@ bool X11Window::Create(xcb_connection_t* connection, xcb_window_t parent_window, WARNING_LOG("Could not find visual's depth."); // ID isn't "used" until the call succeeds. - m_colormap = dyn_libs::xcb_generate_id(connection); - if ((xerror = dyn_libs::xcb_request_check( + m_colormap = s_locals.xcb_generate_id(connection); + if ((xerror = s_locals.xcb_request_check( connection, - dyn_libs::xcb_create_colormap_checked(connection, XCB_COLORMAP_ALLOC_NONE, m_colormap, parent_window, vi)))) + s_locals.xcb_create_colormap_checked(connection, XCB_COLORMAP_ALLOC_NONE, m_colormap, parent_window, vi)))) { SetErrorObject(error, "xcb_create_colormap_checked() failed: ", xerror); m_colormap = {}; return false; } - m_window = dyn_libs::xcb_generate_id(connection); + m_window = s_locals.xcb_generate_id(connection); const u32 window_values[] = {XCB_PIXMAP_NONE, 0u, m_colormap}; - xerror = dyn_libs::xcb_request_check( + xerror = s_locals.xcb_request_check( connection, - dyn_libs::xcb_create_window_checked(connection, visual_depth, m_window, parent_window, 0, 0, m_width, m_height, 0, - XCB_WINDOW_CLASS_INPUT_OUTPUT, vi, - XCB_CW_BACK_PIXMAP | XCB_CW_BORDER_PIXEL | XCB_CW_COLORMAP, window_values)); + s_locals.xcb_create_window_checked(connection, visual_depth, m_window, parent_window, 0, 0, m_width, m_height, 0, + XCB_WINDOW_CLASS_INPUT_OUTPUT, vi, + XCB_CW_BACK_PIXMAP | XCB_CW_BORDER_PIXEL | XCB_CW_COLORMAP, window_values)); if (xerror) { SetErrorObject(error, "xcb_create_window_checked() failed: ", xerror); @@ -235,7 +213,7 @@ bool X11Window::Create(xcb_connection_t* connection, xcb_window_t parent_window, return false; } - xerror = dyn_libs::xcb_request_check(connection, dyn_libs::xcb_map_window_checked(connection, m_window)); + xerror = s_locals.xcb_request_check(connection, s_locals.xcb_map_window_checked(connection, m_window)); if (xerror) { SetErrorObject(error, "xcb_map_window_checked() failed: ", xerror); @@ -252,15 +230,14 @@ void X11Window::Destroy() if (m_window) { - if ((xerror = - dyn_libs::xcb_request_check(m_connection, dyn_libs::xcb_unmap_window_checked(m_connection, m_window)))) + if ((xerror = s_locals.xcb_request_check(m_connection, s_locals.xcb_unmap_window_checked(m_connection, m_window)))) { SetErrorObject(&error, "xcb_unmap_window_checked() failed: ", xerror); ERROR_LOG(error.GetDescription()); } if ((xerror = - dyn_libs::xcb_request_check(m_connection, dyn_libs::xcb_destroy_window_checked(m_connection, m_window)))) + s_locals.xcb_request_check(m_connection, s_locals.xcb_destroy_window_checked(m_connection, m_window)))) { SetErrorObject(&error, "xcb_destroy_window_checked() failed: ", xerror); ERROR_LOG(error.GetDescription()); @@ -273,7 +250,7 @@ void X11Window::Destroy() if (m_colormap) { if ((xerror = - dyn_libs::xcb_request_check(m_connection, dyn_libs::xcb_free_colormap_checked(m_connection, m_colormap)))) + s_locals.xcb_request_check(m_connection, s_locals.xcb_free_colormap_checked(m_connection, m_colormap)))) { SetErrorObject(&error, "xcb_free_colormap_checked() failed: ", xerror); ERROR_LOG(error.GetDescription()); @@ -295,8 +272,8 @@ void X11Window::Resize(u16 width, u16 height) } else { - XCBPointer gwa(dyn_libs::xcb_get_geometry_reply( - m_connection, dyn_libs::xcb_get_geometry(m_connection, m_parent_window), &xerror)); + XCBPointer gwa( + s_locals.xcb_get_geometry_reply(m_connection, s_locals.xcb_get_geometry(m_connection, m_parent_window), &xerror)); if (!gwa) { SetErrorObject(&error, "xcb_get_geometry() failed: ", xerror); @@ -309,8 +286,8 @@ void X11Window::Resize(u16 width, u16 height) } u32 values[] = {width, height}; - if ((xerror = dyn_libs::xcb_request_check( - m_connection, dyn_libs::xcb_configure_window_checked( + if ((xerror = s_locals.xcb_request_check( + m_connection, s_locals.xcb_configure_window_checked( m_connection, m_window, XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT, values)))) { SetErrorObject(&error, "xcb_configure_window_checked() failed: ", xerror);