diff --git a/src/common/gl/context_wgl.cpp b/src/common/gl/context_wgl.cpp index 9fc8599cd..c640b2930 100644 --- a/src/common/gl/context_wgl.cpp +++ b/src/common/gl/context_wgl.cpp @@ -1,11 +1,11 @@ #include "context_wgl.h" #include "../assert.h" #include "../log.h" +#include "../scope_guard.h" #include "loader.h" Log_SetChannel(GL::ContextWGL); // TODO: get rid of this -#include "glad_wgl.h" #pragma comment(lib, "opengl32.lib") static void* GetProcAddressCallback(const char* name) @@ -29,8 +29,7 @@ ContextWGL::~ContextWGL() if (m_rc) wglDeleteContext(m_rc); - if (m_dc) - ReleaseDC(GetHWND(), m_dc); + ReleaseDC(); } std::unique_ptr ContextWGL::Create(const WindowInfo& wi, const Version* versions_to_try, @@ -52,7 +51,8 @@ bool ContextWGL::Initialize(const Version* versions_to_try, size_t num_versions_ } else { - Panic("Create pbuffer"); + Log_ErrorPrint("ContextWGL must always start with a valid surface."); + return false; } // Everything including core/ES requires a dummy profile to load the WGL extensions. @@ -87,11 +87,7 @@ bool ContextWGL::ChangeSurface(const WindowInfo& new_wi) { const bool was_current = (wglGetCurrentContext() == m_rc); - if (m_dc) - { - ReleaseDC(GetHWND(), m_dc); - m_dc = {}; - } + ReleaseDC(); m_wi = new_wi; if (!InitializeDC()) @@ -153,7 +149,8 @@ std::unique_ptr ContextWGL::CreateSharedContext(const WindowInfo& wi) } else { - Panic("Create pbuffer"); + Log_ErrorPrint("PBuffer not implemented"); + return nullptr; } if (m_version.profile == Profile::NoProfile) @@ -171,7 +168,7 @@ std::unique_ptr ContextWGL::CreateSharedContext(const WindowInfo& wi) return context; } -bool ContextWGL::InitializeDC() +HDC ContextWGL::GetDCAndSetPixelFormat(HWND hwnd) { PIXELFORMATDESCRIPTOR pfd = {}; pfd.nSize = sizeof(pfd); @@ -179,59 +176,159 @@ bool ContextWGL::InitializeDC() pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER; pfd.iPixelType = PFD_TYPE_RGBA; pfd.dwLayerMask = PFD_MAIN_PLANE; + pfd.cRedBits = 8; + pfd.cGreenBits = 8; + pfd.cBlueBits = 8; + pfd.cColorBits = 24; - switch (m_wi.surface_format) - { - case WindowInfo::SurfaceFormat::RGB8: - pfd.cColorBits = 32; - pfd.cRedBits = 8; - pfd.cGreenBits = 8; - pfd.cBlueBits = 8; - break; - - case WindowInfo::SurfaceFormat::RGBA8: - pfd.cColorBits = 32; - pfd.cRedBits = 8; - pfd.cGreenBits = 8; - pfd.cBlueBits = 8; - pfd.cAlphaBits = 8; - break; - - case WindowInfo::SurfaceFormat::RGB565: - pfd.cColorBits = 16; - pfd.cRedBits = 5; - pfd.cGreenBits = 6; - pfd.cBlueBits = 5; - break; - - case WindowInfo::SurfaceFormat::Auto: - break; - - default: - UnreachableCode(); - break; - } - - m_dc = GetDC(GetHWND()); - if (!m_dc) + HDC hDC = ::GetDC(hwnd); + if (!hDC) { Log_ErrorPrintf("GetDC() failed: 0x%08X", GetLastError()); - return false; + return {}; } - const int pf = ChoosePixelFormat(m_dc, &pfd); - if (pf == 0) + if (!m_pixel_format.has_value()) { - Log_ErrorPrintf("ChoosePixelFormat() failed: 0x%08X", GetLastError()); - return false; + const int pf = ChoosePixelFormat(hDC, &pfd); + if (pf == 0) + { + Log_ErrorPrintf("ChoosePixelFormat() failed: 0x%08X", GetLastError()); + ::ReleaseDC(hwnd, hDC); + return {}; + } + + m_pixel_format = pf; } - if (!SetPixelFormat(m_dc, pf, &pfd)) + if (!SetPixelFormat(hDC, m_pixel_format.value(), &pfd)) { Log_ErrorPrintf("SetPixelFormat() failed: 0x%08X", GetLastError()); + ::ReleaseDC(hwnd, hDC); + return {}; + } + + return hDC; +} + +bool ContextWGL::InitializeDC() +{ + if (m_wi.type == WindowInfo::Type::Win32) + { + m_dc = GetDCAndSetPixelFormat(GetHWND()); + if (!m_dc) + { + Log_ErrorPrint("Failed to get DC for window"); + return false; + } + + return true; + } + else if (m_wi.type == WindowInfo::Type::Surfaceless) + { + return CreatePBuffer(); + } + else + { + Log_ErrorPrintf("Unknown window info type %u", static_cast(m_wi.type)); + return false; + } +} + +void ContextWGL::ReleaseDC() +{ + if (m_pbuffer) + { + wglReleasePbufferDCARB(m_pbuffer, m_dc); + m_dc = {}; + + wglDestroyPbufferARB(m_pbuffer); + m_pbuffer = {}; + + ::ReleaseDC(m_dummy_window, m_dummy_dc); + m_dummy_dc = {}; + + DestroyWindow(m_dummy_window); + m_dummy_window = {}; + } + else if (m_dc) + { + ::ReleaseDC(GetHWND(), m_dc); + m_dc = {}; + } +} + +bool ContextWGL::CreatePBuffer() +{ + static bool window_class_registered = false; + static const wchar_t* window_class_name = L"ContextWGLPBuffer"; + + if (!window_class_registered) + { + WNDCLASSEXW wc = {}; + wc.cbSize = sizeof(WNDCLASSEXW); + wc.style = 0; + wc.lpfnWndProc = DefWindowProcW; + wc.cbClsExtra = 0; + wc.cbWndExtra = 0; + wc.hInstance = GetModuleHandle(nullptr); + wc.hIcon = NULL; + wc.hCursor = LoadCursor(NULL, IDC_ARROW); + wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); + wc.lpszMenuName = NULL; + wc.lpszClassName = window_class_name; + wc.hIconSm = NULL; + + if (!RegisterClassExW(&wc)) + { + Log_ErrorPrint("(ContextWGL::CreatePBuffer) RegisterClassExW() failed"); + return false; + } + + window_class_registered = true; + } + + HWND hwnd = CreateWindowExW(0, window_class_name, window_class_name, 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL); + if (!hwnd) + { + Log_ErrorPrint("(ContextWGL::CreatePBuffer) CreateWindowEx() failed"); return false; } + Common::ScopeGuard hwnd_guard([hwnd]() { DestroyWindow(hwnd); }); + + HDC hdc = GetDCAndSetPixelFormat(hwnd); + if (!hdc) + return false; + + Common::ScopeGuard hdc_guard([hdc, hwnd]() { ::ReleaseDC(hwnd, hdc); }); + + static constexpr const int pb_attribs[] = {0, 0}; + + AssertMsg(m_pixel_format.has_value(), "Has pixel format for pbuffer"); + HPBUFFERARB pbuffer = wglCreatePbufferARB(hdc, m_pixel_format.value(), 1, 1, pb_attribs); + if (!pbuffer) + { + Log_ErrorPrint("(ContextWGL::CreatePBuffer) wglCreatePbufferARB() failed"); + return false; + } + + Common::ScopeGuard pbuffer_guard([pbuffer]() { wglDestroyPbufferARB(pbuffer); }); + + m_dc = wglGetPbufferDCARB(pbuffer); + if (!m_dc) + { + Log_ErrorPrint("(ContextWGL::CreatePbuffer) wglGetPbufferDCARB() failed"); + return false; + } + + m_dummy_window = hwnd; + m_dummy_dc = hdc; + m_pbuffer = pbuffer; + + pbuffer_guard.Dismiss(); + hdc_guard.Dismiss(); + hwnd_guard.Dismiss(); return true; } @@ -255,7 +352,7 @@ bool ContextWGL::CreateAnyContext(HGLRC share_context, bool make_current) // re-init glad-wgl if (!gladLoadWGLLoader([](const char* name) -> void* { return wglGetProcAddress(name); }, m_dc)) { - Log_ErrorPrintf("Loading GLAD WGL functions failed"); + Log_ErrorPrint("Loading GLAD WGL functions failed"); return false; } } @@ -322,7 +419,7 @@ bool ContextWGL::CreateVersionContext(const Version& version, HGLRC share_contex } else { - Log_ErrorPrintf("Unknown profile"); + Log_ErrorPrint("Unknown profile"); return false; } @@ -342,7 +439,7 @@ bool ContextWGL::CreateVersionContext(const Version& version, HGLRC share_contex // re-init glad-wgl if (make_current && !gladLoadWGLLoader([](const char* name) -> void* { return wglGetProcAddress(name); }, m_dc)) { - Log_ErrorPrintf("Loading GLAD WGL functions failed"); + Log_ErrorPrint("Loading GLAD WGL functions failed"); return false; } diff --git a/src/common/gl/context_wgl.h b/src/common/gl/context_wgl.h index bc8a83ae3..a48b99af1 100644 --- a/src/common/gl/context_wgl.h +++ b/src/common/gl/context_wgl.h @@ -1,7 +1,10 @@ #pragma once #include "../windows_headers.h" + #include "context.h" +#include "glad_wgl.h" #include "loader.h" +#include namespace GL { @@ -26,13 +29,25 @@ public: private: ALWAYS_INLINE HWND GetHWND() const { return static_cast(m_wi.window_handle); } + HDC GetDCAndSetPixelFormat(HWND hwnd); + bool Initialize(const Version* versions_to_try, size_t num_versions_to_try); bool InitializeDC(); + void ReleaseDC(); + bool CreatePBuffer(); bool CreateAnyContext(HGLRC share_context, bool make_current); bool CreateVersionContext(const Version& version, HGLRC share_context, bool make_current); HDC m_dc = {}; HGLRC m_rc = {}; + + // Can't change pixel format once it's set for a RC. + std::optional m_pixel_format; + + // Dummy window for creating a PBuffer off when we're surfaceless. + HWND m_dummy_window = {}; + HDC m_dummy_dc = {}; + HPBUFFERARB m_pbuffer = {}; }; } // namespace GL \ No newline at end of file