aboutsummaryrefslogtreecommitdiff
path: root/src/frontend/duckstation/gl/context_wgl.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/frontend/duckstation/gl/context_wgl.cpp')
-rw-r--r--src/frontend/duckstation/gl/context_wgl.cpp452
1 files changed, 452 insertions, 0 deletions
diff --git a/src/frontend/duckstation/gl/context_wgl.cpp b/src/frontend/duckstation/gl/context_wgl.cpp
new file mode 100644
index 0000000..03c18e8
--- /dev/null
+++ b/src/frontend/duckstation/gl/context_wgl.cpp
@@ -0,0 +1,452 @@
+#include "context_wgl.h"
+#include "../duckstation_compat.h"
+#include "../log.h"
+#include "../scoped_guard.h"
+#include "loader.h"
+Log_SetChannel(GL::ContextWGL);
+
+// TODO: get rid of this
+#pragma comment(lib, "opengl32.lib")
+
+static void* GetProcAddressCallback(const char* name)
+{
+ void* addr = reinterpret_cast<void*>(wglGetProcAddress(name));
+ if (addr)
+ return addr;
+
+ // try opengl32.dll
+ return reinterpret_cast<void*>(::GetProcAddress(GetModuleHandleA("opengl32.dll"), name));
+}
+
+namespace GL {
+ContextWGL::ContextWGL(const WindowInfo& wi) : Context(wi) {}
+
+ContextWGL::~ContextWGL()
+{
+ if (wglGetCurrentContext() == m_rc)
+ wglMakeCurrent(m_dc, nullptr);
+
+ if (m_rc)
+ wglDeleteContext(m_rc);
+
+ ReleaseDC();
+}
+
+std::unique_ptr<Context> ContextWGL::Create(const WindowInfo& wi, const Version* versions_to_try,
+ size_t num_versions_to_try)
+{
+ std::unique_ptr<ContextWGL> context = std::make_unique<ContextWGL>(wi);
+ if (!context->Initialize(versions_to_try, num_versions_to_try))
+ return nullptr;
+
+ return context;
+}
+
+bool ContextWGL::Initialize(const Version* versions_to_try, size_t num_versions_to_try)
+{
+ if (m_wi.type == WindowInfo::Type::Win32)
+ {
+ if (!InitializeDC())
+ return false;
+ }
+ else
+ {
+ 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.
+ if (!CreateAnyContext(nullptr, true))
+ return false;
+
+ for (size_t i = 0; i < num_versions_to_try; i++)
+ {
+ const Version& cv = versions_to_try[i];
+ if (cv.profile == Profile::NoProfile)
+ {
+ // we already have the dummy context, so just use that
+ m_version = cv;
+ return true;
+ }
+ else if (CreateVersionContext(cv, nullptr, true))
+ {
+ m_version = cv;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void* ContextWGL::GetProcAddress(const char* name)
+{
+ return GetProcAddressCallback(name);
+}
+
+bool ContextWGL::ChangeSurface(const WindowInfo& new_wi)
+{
+ const bool was_current = (wglGetCurrentContext() == m_rc);
+
+ ReleaseDC();
+
+ m_wi = new_wi;
+ if (!InitializeDC())
+ return false;
+
+ if (was_current && !wglMakeCurrent(m_dc, m_rc))
+ {
+ Log_ErrorPrintf("Failed to make context current again after surface change: 0x%08X", GetLastError());
+ return false;
+ }
+
+ return true;
+}
+
+void ContextWGL::ResizeSurface(u32 new_surface_width /*= 0*/, u32 new_surface_height /*= 0*/)
+{
+ RECT client_rc = {};
+ GetClientRect(GetHWND(), &client_rc);
+ m_wi.surface_width = static_cast<u32>(client_rc.right - client_rc.left);
+ m_wi.surface_height = static_cast<u32>(client_rc.bottom - client_rc.top);
+}
+
+bool ContextWGL::SwapBuffers()
+{
+ return ::SwapBuffers(m_dc);
+}
+
+bool ContextWGL::MakeCurrent()
+{
+ if (!wglMakeCurrent(m_dc, m_rc))
+ {
+ Log_ErrorPrintf("wglMakeCurrent() failed: 0x%08X", GetLastError());
+ return false;
+ }
+
+ return true;
+}
+
+bool ContextWGL::DoneCurrent()
+{
+ return wglMakeCurrent(m_dc, nullptr);
+}
+
+bool ContextWGL::SetSwapInterval(s32 interval)
+{
+ if (!GLAD_WGL_EXT_swap_control)
+ return false;
+
+ return wglSwapIntervalEXT(interval);
+}
+
+std::unique_ptr<Context> ContextWGL::CreateSharedContext(const WindowInfo& wi)
+{
+ std::unique_ptr<ContextWGL> context = std::make_unique<ContextWGL>(wi);
+ if (wi.type == WindowInfo::Type::Win32)
+ {
+ if (!context->InitializeDC())
+ return nullptr;
+ }
+ else
+ {
+ Log_ErrorPrint("PBuffer not implemented");
+ return nullptr;
+ }
+
+ if (m_version.profile == Profile::NoProfile)
+ {
+ if (!context->CreateAnyContext(m_rc, false))
+ return nullptr;
+ }
+ else
+ {
+ if (!context->CreateVersionContext(m_version, m_rc, false))
+ return nullptr;
+ }
+
+ context->m_version = m_version;
+ return context;
+}
+
+HDC ContextWGL::GetDCAndSetPixelFormat(HWND hwnd)
+{
+ PIXELFORMATDESCRIPTOR pfd = {};
+ pfd.nSize = sizeof(pfd);
+ pfd.nVersion = 1;
+ 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;
+
+ HDC hDC = ::GetDC(hwnd);
+ if (!hDC)
+ {
+ Log_ErrorPrintf("GetDC() failed: 0x%08X", GetLastError());
+ return {};
+ }
+
+ if (!m_pixel_format.has_value())
+ {
+ 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(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<unsigned>(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;
+ }
+
+ ScopedGuard hwnd_guard([hwnd]() { DestroyWindow(hwnd); });
+
+ HDC hdc = GetDCAndSetPixelFormat(hwnd);
+ if (!hdc)
+ return false;
+
+ ScopedGuard 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;
+ }
+
+ ScopedGuard 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.Cancel();
+ hdc_guard.Cancel();
+ hwnd_guard.Cancel();
+ return true;
+}
+
+bool ContextWGL::CreateAnyContext(HGLRC share_context, bool make_current)
+{
+ m_rc = wglCreateContext(m_dc);
+ if (!m_rc)
+ {
+ Log_ErrorPrintf("wglCreateContext() failed: 0x%08X", GetLastError());
+ return false;
+ }
+
+ if (make_current)
+ {
+ if (!wglMakeCurrent(m_dc, m_rc))
+ {
+ Log_ErrorPrintf("wglMakeCurrent() failed: 0x%08X", GetLastError());
+ return false;
+ }
+
+ // re-init glad-wgl
+ if (!gladLoadWGLLoader([](const char* name) -> void* { return reinterpret_cast<void*>(wglGetProcAddress(name)); }, m_dc))
+ {
+ Log_ErrorPrint("Loading GLAD WGL functions failed");
+ return false;
+ }
+ }
+
+ if (share_context && !wglShareLists(share_context, m_rc))
+ {
+ Log_ErrorPrintf("wglShareLists() failed: 0x%08X", GetLastError());
+ return false;
+ }
+
+ return true;
+}
+
+bool ContextWGL::CreateVersionContext(const Version& version, HGLRC share_context, bool make_current)
+{
+ // we need create context attribs
+ if (!GLAD_WGL_ARB_create_context)
+ {
+ Log_ErrorPrint("Missing GLAD_WGL_ARB_create_context.");
+ return false;
+ }
+
+ HGLRC new_rc;
+ if (version.profile == Profile::Core)
+ {
+ const int attribs[] = {WGL_CONTEXT_PROFILE_MASK_ARB,
+ WGL_CONTEXT_CORE_PROFILE_BIT_ARB,
+ WGL_CONTEXT_MAJOR_VERSION_ARB,
+ version.major_version,
+ WGL_CONTEXT_MINOR_VERSION_ARB,
+ version.minor_version,
+#ifdef _DEBUG
+ WGL_CONTEXT_FLAGS_ARB,
+ WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB | WGL_CONTEXT_DEBUG_BIT_ARB,
+#else
+ WGL_CONTEXT_FLAGS_ARB,
+ WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB,
+#endif
+ 0,
+ 0};
+
+ new_rc = wglCreateContextAttribsARB(m_dc, share_context, attribs);
+ }
+ else if (version.profile == Profile::ES)
+ {
+ if ((version.major_version >= 2 && !GLAD_WGL_EXT_create_context_es2_profile) ||
+ (version.major_version < 2 && !GLAD_WGL_EXT_create_context_es_profile))
+ {
+ Log_ErrorPrint("WGL_EXT_create_context_es_profile not supported");
+ return false;
+ }
+
+ const int attribs[] = {
+ WGL_CONTEXT_PROFILE_MASK_ARB,
+ ((version.major_version >= 2) ? WGL_CONTEXT_ES2_PROFILE_BIT_EXT : WGL_CONTEXT_ES_PROFILE_BIT_EXT),
+ WGL_CONTEXT_MAJOR_VERSION_ARB,
+ version.major_version,
+ WGL_CONTEXT_MINOR_VERSION_ARB,
+ version.minor_version,
+ 0,
+ 0};
+
+ new_rc = wglCreateContextAttribsARB(m_dc, share_context, attribs);
+ }
+ else
+ {
+ Log_ErrorPrint("Unknown profile");
+ return false;
+ }
+
+ if (!new_rc)
+ return false;
+
+ // destroy and swap contexts
+ if (m_rc)
+ {
+ if (!wglMakeCurrent(m_dc, make_current ? new_rc : nullptr))
+ {
+ Log_ErrorPrintf("wglMakeCurrent() failed: 0x%08X", GetLastError());
+ wglDeleteContext(new_rc);
+ return false;
+ }
+
+ // re-init glad-wgl
+ if (make_current && !gladLoadWGLLoader([](const char* name) -> void* { return reinterpret_cast<void*>(wglGetProcAddress(name)); }, m_dc))
+ {
+ Log_ErrorPrint("Loading GLAD WGL functions failed");
+ return false;
+ }
+
+ wglDeleteContext(m_rc);
+ }
+
+ m_rc = new_rc;
+ return true;
+}
+} // namespace GL