From 7353cae70726e152495d4de885499cf5b5b6db94 Mon Sep 17 00:00:00 2001 From: Stenzek Date: Wed, 5 Oct 2016 20:37:11 +1000 Subject: [PATCH] GLInterface: Implement core and shared context creation for WGL --- Source/Core/Common/GL/GLInterface/WGL.cpp | 464 +++++++++++++++++++--- Source/Core/Common/GL/GLInterface/WGL.h | 10 + 2 files changed, 409 insertions(+), 65 deletions(-) diff --git a/Source/Core/Common/GL/GLInterface/WGL.cpp b/Source/Core/Common/GL/GLInterface/WGL.cpp index ea13b60e36..ab51344964 100644 --- a/Source/Core/Common/GL/GLInterface/WGL.cpp +++ b/Source/Core/Common/GL/GLInterface/WGL.cpp @@ -2,6 +2,7 @@ // Licensed under GPLv2+ // Refer to the license.txt file included. +#include #include #include @@ -9,13 +10,152 @@ #include "Common/Logging/Log.h" #include "Common/MsgHandler.h" -static HDC hDC = nullptr; // Private GDI Device Context -static HGLRC hRC = nullptr; // Permanent Rendering Context -static HINSTANCE dllHandle = nullptr; // Handle to OpenGL32.dll +// from wglext.h +#ifndef WGL_ARB_pbuffer +#define WGL_ARB_pbuffer 1 +DECLARE_HANDLE(HPBUFFERARB); +#define WGL_DRAW_TO_PBUFFER_ARB 0x202D +#define WGL_MAX_PBUFFER_PIXELS_ARB 0x202E +#define WGL_MAX_PBUFFER_WIDTH_ARB 0x202F +#define WGL_MAX_PBUFFER_HEIGHT_ARB 0x2030 +#define WGL_PBUFFER_LARGEST_ARB 0x2033 +#define WGL_PBUFFER_WIDTH_ARB 0x2034 +#define WGL_PBUFFER_HEIGHT_ARB 0x2035 +#define WGL_PBUFFER_LOST_ARB 0x2036 +typedef HPBUFFERARB(WINAPI* PFNWGLCREATEPBUFFERARBPROC)(HDC hDC, int iPixelFormat, int iWidth, + int iHeight, const int* piAttribList); +typedef HDC(WINAPI* PFNWGLGETPBUFFERDCARBPROC)(HPBUFFERARB hPbuffer); +typedef int(WINAPI* PFNWGLRELEASEPBUFFERDCARBPROC)(HPBUFFERARB hPbuffer, HDC hDC); +typedef BOOL(WINAPI* PFNWGLDESTROYPBUFFERARBPROC)(HPBUFFERARB hPbuffer); +typedef BOOL(WINAPI* PFNWGLQUERYPBUFFERARBPROC)(HPBUFFERARB hPbuffer, int iAttribute, int* piValue); +#endif /* WGL_ARB_pbuffer */ -// typedef from wglext.h +#ifndef WGL_ARB_pixel_format +#define WGL_ARB_pixel_format 1 +#define WGL_NUMBER_PIXEL_FORMATS_ARB 0x2000 +#define WGL_DRAW_TO_WINDOW_ARB 0x2001 +#define WGL_DRAW_TO_BITMAP_ARB 0x2002 +#define WGL_ACCELERATION_ARB 0x2003 +#define WGL_NEED_PALETTE_ARB 0x2004 +#define WGL_NEED_SYSTEM_PALETTE_ARB 0x2005 +#define WGL_SWAP_LAYER_BUFFERS_ARB 0x2006 +#define WGL_SWAP_METHOD_ARB 0x2007 +#define WGL_NUMBER_OVERLAYS_ARB 0x2008 +#define WGL_NUMBER_UNDERLAYS_ARB 0x2009 +#define WGL_TRANSPARENT_ARB 0x200A +#define WGL_TRANSPARENT_RED_VALUE_ARB 0x2037 +#define WGL_TRANSPARENT_GREEN_VALUE_ARB 0x2038 +#define WGL_TRANSPARENT_BLUE_VALUE_ARB 0x2039 +#define WGL_TRANSPARENT_ALPHA_VALUE_ARB 0x203A +#define WGL_TRANSPARENT_INDEX_VALUE_ARB 0x203B +#define WGL_SHARE_DEPTH_ARB 0x200C +#define WGL_SHARE_STENCIL_ARB 0x200D +#define WGL_SHARE_ACCUM_ARB 0x200E +#define WGL_SUPPORT_GDI_ARB 0x200F +#define WGL_SUPPORT_OPENGL_ARB 0x2010 +#define WGL_DOUBLE_BUFFER_ARB 0x2011 +#define WGL_STEREO_ARB 0x2012 +#define WGL_PIXEL_TYPE_ARB 0x2013 +#define WGL_COLOR_BITS_ARB 0x2014 +#define WGL_RED_BITS_ARB 0x2015 +#define WGL_RED_SHIFT_ARB 0x2016 +#define WGL_GREEN_BITS_ARB 0x2017 +#define WGL_GREEN_SHIFT_ARB 0x2018 +#define WGL_BLUE_BITS_ARB 0x2019 +#define WGL_BLUE_SHIFT_ARB 0x201A +#define WGL_ALPHA_BITS_ARB 0x201B +#define WGL_ALPHA_SHIFT_ARB 0x201C +#define WGL_ACCUM_BITS_ARB 0x201D +#define WGL_ACCUM_RED_BITS_ARB 0x201E +#define WGL_ACCUM_GREEN_BITS_ARB 0x201F +#define WGL_ACCUM_BLUE_BITS_ARB 0x2020 +#define WGL_ACCUM_ALPHA_BITS_ARB 0x2021 +#define WGL_DEPTH_BITS_ARB 0x2022 +#define WGL_STENCIL_BITS_ARB 0x2023 +#define WGL_AUX_BUFFERS_ARB 0x2024 +#define WGL_NO_ACCELERATION_ARB 0x2025 +#define WGL_GENERIC_ACCELERATION_ARB 0x2026 +#define WGL_FULL_ACCELERATION_ARB 0x2027 +#define WGL_SWAP_EXCHANGE_ARB 0x2028 +#define WGL_SWAP_COPY_ARB 0x2029 +#define WGL_SWAP_UNDEFINED_ARB 0x202A +#define WGL_TYPE_RGBA_ARB 0x202B +#define WGL_TYPE_COLORINDEX_ARB 0x202C +typedef BOOL(WINAPI* PFNWGLGETPIXELFORMATATTRIBIVARBPROC)(HDC hdc, int iPixelFormat, + int iLayerPlane, UINT nAttributes, + const int* piAttributes, int* piValues); +typedef BOOL(WINAPI* PFNWGLGETPIXELFORMATATTRIBFVARBPROC)(HDC hdc, int iPixelFormat, + int iLayerPlane, UINT nAttributes, + const int* piAttributes, FLOAT* pfValues); +typedef BOOL(WINAPI* PFNWGLCHOOSEPIXELFORMATARBPROC)(HDC hdc, const int* piAttribIList, + const FLOAT* pfAttribFList, UINT nMaxFormats, + int* piFormats, UINT* nNumFormats); +#endif /* WGL_ARB_pixel_format */ + +#ifndef WGL_ARB_create_context +#define WGL_ARB_create_context 1 +#define WGL_CONTEXT_DEBUG_BIT_ARB 0x00000001 +#define WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB 0x00000002 +#define WGL_CONTEXT_MAJOR_VERSION_ARB 0x2091 +#define WGL_CONTEXT_MINOR_VERSION_ARB 0x2092 +#define WGL_CONTEXT_LAYER_PLANE_ARB 0x2093 +#define WGL_CONTEXT_FLAGS_ARB 0x2094 +#define ERROR_INVALID_VERSION_ARB 0x2095 +typedef HGLRC(WINAPI* PFNWGLCREATECONTEXTATTRIBSARBPROC)(HDC hDC, HGLRC hShareContext, + const int* attribList); +#endif /* WGL_ARB_create_context */ + +#ifndef WGL_ARB_create_context_profile +#define WGL_ARB_create_context_profile 1 +#define WGL_CONTEXT_PROFILE_MASK_ARB 0x9126 +#define WGL_CONTEXT_CORE_PROFILE_BIT_ARB 0x00000001 +#define WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB 0x00000002 +#define ERROR_INVALID_PROFILE_ARB 0x2096 +#endif /* WGL_ARB_create_context_profile */ + +#ifndef WGL_EXT_swap_control +#define WGL_EXT_swap_control 1 typedef BOOL(WINAPI* PFNWGLSWAPINTERVALEXTPROC)(int interval); +typedef int(WINAPI* PFNWGLGETSWAPINTERVALEXTPROC)(void); +#endif /* WGL_EXT_swap_control */ + +// Persistent pointers static PFNWGLSWAPINTERVALEXTPROC wglSwapIntervalEXT = nullptr; +static PFNWGLCREATECONTEXTATTRIBSARBPROC wglCreateContextAttribsARB = nullptr; +static PFNWGLCHOOSEPIXELFORMATARBPROC wglChoosePixelFormatARB = nullptr; +static PFNWGLCREATEPBUFFERARBPROC wglCreatePbufferARB = nullptr; +static PFNWGLGETPBUFFERDCARBPROC wglGetPbufferDCARB = nullptr; +static PFNWGLRELEASEPBUFFERDCARBPROC wglReleasePbufferDCARB = nullptr; +static PFNWGLDESTROYPBUFFERARBPROC wglDestroyPbufferARB = nullptr; + +static void LoadWGLExtensions() +{ + wglSwapIntervalEXT = reinterpret_cast( + GLInterface->GetFuncAddress("wglSwapIntervalEXT")); + wglCreateContextAttribsARB = reinterpret_cast( + wglGetProcAddress("wglCreateContextAttribsARB")); + wglChoosePixelFormatARB = reinterpret_cast( + wglGetProcAddress("wglChoosePixelFormatARB")); + wglCreatePbufferARB = + reinterpret_cast(wglGetProcAddress("wglCreatePbufferARB")); + wglGetPbufferDCARB = + reinterpret_cast(wglGetProcAddress("wglGetPbufferDCARB")); + wglReleasePbufferDCARB = + reinterpret_cast(wglGetProcAddress("wglReleasePbufferDCARB")); + wglDestroyPbufferARB = + reinterpret_cast(wglGetProcAddress("wglGetPbufferDCARB")); +} + +static void ClearWGLExtensionPointers() +{ + wglSwapIntervalEXT = nullptr; + wglCreateContextAttribsARB = nullptr; + wglChoosePixelFormatARB = nullptr; + wglCreatePbufferARB = nullptr; + wglGetPbufferDCARB = nullptr; + wglReleasePbufferDCARB = nullptr; + wglDestroyPbufferARB = nullptr; +} void cInterfaceWGL::SwapInterval(int Interval) { @@ -26,14 +166,20 @@ void cInterfaceWGL::SwapInterval(int Interval) } void cInterfaceWGL::Swap() { - SwapBuffers(hDC); + SwapBuffers(m_dc); } void* cInterfaceWGL::GetFuncAddress(const std::string& name) { - void* func = (void*)wglGetProcAddress((LPCSTR)name.c_str()); + FARPROC func = wglGetProcAddress(name.c_str()); if (func == nullptr) - func = (void*)GetProcAddress(dllHandle, (LPCSTR)name.c_str()); + { + // Using GetModuleHandle here is okay, since we import functions from opengl32.dll, it's + // guaranteed to be loaded. + HMODULE opengl_module = GetModuleHandle(TEXT("opengl32.dll")); + func = GetProcAddress(opengl_module, name.c_str()); + } + return func; } @@ -56,92 +202,268 @@ bool cInterfaceWGL::PeekMessages() // Call browser: Core.cpp:EmuThread() > main.cpp:Video_Initialize() bool cInterfaceWGL::Create(void* window_handle, bool core) { - if (window_handle == nullptr) + if (!window_handle) return false; - HWND window_handle_reified = reinterpret_cast(window_handle); - RECT window_rect = {0}; - - if (!GetClientRect(window_handle_reified, &window_rect)) + RECT window_rect = {}; + m_window_handle = reinterpret_cast(window_handle); + if (!GetClientRect(m_window_handle, &window_rect)) return false; + // Clear extension function pointers before creating the first context. + ClearWGLExtensionPointers(); + // Control window size and picture scaling - int twidth = (window_rect.right - window_rect.left); - int theight = (window_rect.bottom - window_rect.top); + int twidth = window_rect.right - window_rect.left; + int theight = window_rect.bottom - window_rect.top; s_backbuffer_width = twidth; s_backbuffer_height = theight; - m_window_handle = window_handle_reified; + static constexpr PIXELFORMATDESCRIPTOR pfd = { + sizeof(PIXELFORMATDESCRIPTOR), // Size Of This Pixel Format Descriptor + 1, // Version Number + PFD_DRAW_TO_WINDOW | // Format Must Support Window + PFD_SUPPORT_OPENGL | // Format Must Support OpenGL + PFD_DOUBLEBUFFER, // Must Support Double Buffering + PFD_TYPE_RGBA, // Request An RGBA Format + 32, // Select Our Color Depth + 0, + 0, 0, 0, 0, 0, // Color Bits Ignored + 0, // 8bit Alpha Buffer + 0, // Shift Bit Ignored + 0, // No Accumulation Buffer + 0, 0, 0, 0, // Accumulation Bits Ignored + 0, // 0Bit Z-Buffer (Depth Buffer) + 0, // 0bit Stencil Buffer + 0, // No Auxiliary Buffer + PFD_MAIN_PLANE, // Main Drawing Layer + 0, // Reserved + 0, 0, 0 // Layer Masks Ignored + }; - dllHandle = LoadLibrary(TEXT("OpenGL32.dll")); - - PIXELFORMATDESCRIPTOR pfd = // pfd Tells Windows How We Want Things To Be - { - sizeof(PIXELFORMATDESCRIPTOR), // Size Of This Pixel Format Descriptor - 1, // Version Number - PFD_DRAW_TO_WINDOW | // Format Must Support Window - PFD_SUPPORT_OPENGL | // Format Must Support OpenGL - PFD_DOUBLEBUFFER, // Must Support Double Buffering - PFD_TYPE_RGBA, // Request An RGBA Format - 32, // Select Our Color Depth - 0, - 0, 0, 0, 0, 0, // Color Bits Ignored - 0, // 8bit Alpha Buffer - 0, // Shift Bit Ignored - 0, // No Accumulation Buffer - 0, 0, 0, 0, // Accumulation Bits Ignored - 0, // 0Bit Z-Buffer (Depth Buffer) - 0, // 0bit Stencil Buffer - 0, // No Auxiliary Buffer - PFD_MAIN_PLANE, // Main Drawing Layer - 0, // Reserved - 0, 0, 0 // Layer Masks Ignored - }; - - int PixelFormat; // Holds The Results After Searching For A Match - - if (!(hDC = GetDC(window_handle_reified))) + m_dc = GetDC(m_window_handle); + if (!m_dc) { PanicAlert("(1) Can't create an OpenGL Device context. Fail."); return false; } - if (!(PixelFormat = ChoosePixelFormat(hDC, &pfd))) + int pixel_format; + if (!(pixel_format = ChoosePixelFormat(m_dc, &pfd))) { PanicAlert("(2) Can't find a suitable PixelFormat."); return false; } - if (!SetPixelFormat(hDC, PixelFormat, &pfd)) + if (!SetPixelFormat(m_dc, pixel_format, &pfd)) { PanicAlert("(3) Can't set the PixelFormat."); return false; } - if (!(hRC = wglCreateContext(hDC))) + if (!(m_rc = wglCreateContext(m_dc))) { PanicAlert("(4) Can't create an OpenGL rendering context."); return false; } + // WGL only supports desktop GL, for now. + if (s_opengl_mode == GLInterfaceMode::MODE_DETECT) + s_opengl_mode = GLInterfaceMode::MODE_OPENGL; + + if (core) + { + // Make the fallback context current, temporarily. + // This is because we need an active context to use wglCreateContextAttribsARB. + if (!wglMakeCurrent(m_dc, m_rc)) + { + PanicAlert("(5) Can't make dummy WGL context current."); + return false; + } + + // Load WGL extension function pointers. + LoadWGLExtensions(); + + // Attempt creating a core context. + HGLRC core_context = CreateCoreContext(m_dc, nullptr); + + // Switch out the temporary context before continuing, regardless of whether we got a core + // context. If we didn't get a core context, the caller expects that the context is not current. + if (!wglMakeCurrent(m_dc, nullptr)) + { + PanicAlert("(6) Failed to switch out temporary context"); + return false; + } + + // If we got a core context, destroy and replace the temporary context. + if (core_context) + { + wglDeleteContext(m_rc); + m_rc = core_context; + m_core = true; + } + else + { + WARN_LOG(VIDEO, "Failed to create a core OpenGL context. Using fallback context."); + } + } + + return true; +} + +bool cInterfaceWGL::Create(cInterfaceBase* main_context) +{ + cInterfaceWGL* wgl_main_context = static_cast(main_context); + + // WGL does not support surfaceless contexts, so we use a 1x1 pbuffer instead. + if (!CreatePBuffer(wgl_main_context->m_dc, 1, 1, &m_pbuffer_handle, &m_dc)) + return false; + + m_rc = CreateCoreContext(m_dc, wgl_main_context->m_rc); + if (!m_rc) + return false; + + m_core = true; + return true; +} + +std::unique_ptr cInterfaceWGL::CreateSharedContext() +{ + std::unique_ptr context = std::make_unique(); + if (!context->Create(this)) + { + context->Shutdown(); + return nullptr; + } + + return std::move(context); +} + +HGLRC cInterfaceWGL::CreateCoreContext(HDC dc, HGLRC share_context) +{ + if (!wglCreateContextAttribsARB) + { + WARN_LOG(VIDEO, "Missing WGL_ARB_create_context extension"); + return nullptr; + } + + // List of versions to attempt context creation for. (4.5-3.2, geometry shaders is a minimum + // requirement since we're using core profile) + static constexpr std::array, 8> try_versions = { + {{4, 5}, {4, 4}, {4, 3}, {4, 2}, {4, 1}, {4, 0}, {3, 3}, {3, 2}}}; + + for (const auto& version : try_versions) + { + // Construct list of attributes. Prefer a forward-compatible, core context. + std::array attribs = { + WGL_CONTEXT_PROFILE_MASK_ARB, + WGL_CONTEXT_CORE_PROFILE_BIT_ARB, +#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 + WGL_CONTEXT_MAJOR_VERSION_ARB, + version.first, + WGL_CONTEXT_MINOR_VERSION_ARB, + version.second, + 0, + 0}; + + // Attempt creating this context. + HGLRC core_context = wglCreateContextAttribsARB(dc, nullptr, attribs.data()); + if (core_context) + { + // If we're creating a shared context, share the resources before the context is used. + if (share_context) + { + if (!wglShareLists(share_context, core_context)) + { + ERROR_LOG(VIDEO, "wglShareLists failed"); + wglDeleteContext(core_context); + return nullptr; + } + } + + INFO_LOG(VIDEO, "WGL: Created a GL %d.%d core context", version.first, version.second); + return core_context; + } + } + + WARN_LOG(VIDEO, "Unable to create a core OpenGL context of an acceptable version"); + return nullptr; +} + +bool cInterfaceWGL::CreatePBuffer(HDC onscreen_dc, int width, int height, HANDLE* pbuffer_handle, + HDC* pbuffer_dc) +{ + if (!wglChoosePixelFormatARB || !wglCreatePbufferARB || !wglGetPbufferDCARB || + !wglReleasePbufferDCARB || !wglDestroyPbufferARB) + { + ERROR_LOG(VIDEO, "Missing WGL_ARB_pbuffer extension"); + return false; + } + + static constexpr std::array pf_iattribs = { + WGL_DRAW_TO_PBUFFER_ARB, + 1, // Pixel format compatible with pbuffer rendering + WGL_RED_BITS_ARB, + 0, + WGL_GREEN_BITS_ARB, + 0, + WGL_BLUE_BITS_ARB, + 0, + WGL_DEPTH_BITS_ARB, + 0, + WGL_STENCIL_BITS_ARB, + 0, + 0, + 0}; + + static constexpr std::array pf_fattribs = {}; + + int pixel_format; + UINT num_pixel_formats; + if (!wglChoosePixelFormatARB(onscreen_dc, pf_iattribs.data(), pf_fattribs.data(), 1, + &pixel_format, &num_pixel_formats)) + { + ERROR_LOG(VIDEO, "Failed to obtain a compatible pbuffer pixel format"); + return false; + } + + static constexpr std::array pb_attribs = {}; + + HPBUFFERARB pbuffer = + wglCreatePbufferARB(onscreen_dc, pixel_format, width, height, pb_attribs.data()); + if (!pbuffer) + { + ERROR_LOG(VIDEO, "Failed to create pbuffer"); + return false; + } + + HDC dc = wglGetPbufferDCARB(pbuffer); + if (!dc) + { + ERROR_LOG(VIDEO, "Failed to get drawing context from pbuffer"); + wglDestroyPbufferARB(pbuffer); + return false; + } + + *pbuffer_handle = pbuffer; + *pbuffer_dc = dc; return true; } bool cInterfaceWGL::MakeCurrent() { - bool success = wglMakeCurrent(hDC, hRC) ? true : false; - if (success) - { - // Grab the swap interval function pointer - wglSwapIntervalEXT = - (PFNWGLSWAPINTERVALEXTPROC)GLInterface->GetFuncAddress("wglSwapIntervalEXT"); - } - return success; + return wglMakeCurrent(m_dc, m_rc) == TRUE; } bool cInterfaceWGL::ClearCurrent() { - return wglMakeCurrent(hDC, nullptr) ? true : false; + return wglMakeCurrent(m_dc, nullptr) == TRUE; } // Update window width, size and etc. Called from Render.cpp @@ -151,28 +473,40 @@ void cInterfaceWGL::Update() GetClientRect(m_window_handle, &rcWindow); // Get the new window width and height - s_backbuffer_width = (rcWindow.right - rcWindow.left); - s_backbuffer_height = (rcWindow.bottom - rcWindow.top); + s_backbuffer_width = rcWindow.right - rcWindow.left; + s_backbuffer_height = rcWindow.bottom - rcWindow.top; } // Close backend void cInterfaceWGL::Shutdown() { - if (hRC) + if (m_rc) { - if (!wglMakeCurrent(nullptr, nullptr)) + if (!wglMakeCurrent(m_dc, nullptr)) NOTICE_LOG(VIDEO, "Could not release drawing context."); - if (!wglDeleteContext(hRC)) + if (!wglDeleteContext(m_rc)) ERROR_LOG(VIDEO, "Attempt to release rendering context failed."); - hRC = nullptr; + m_rc = nullptr; } - if (hDC && !ReleaseDC(m_window_handle, hDC)) + if (m_dc) { - ERROR_LOG(VIDEO, "Attempt to release device context failed."); - hDC = nullptr; + if (m_pbuffer_handle) + { + wglReleasePbufferDCARB(static_cast(m_pbuffer_handle), m_dc); + m_dc = nullptr; + + wglDestroyPbufferARB(static_cast(m_pbuffer_handle)); + m_pbuffer_handle = nullptr; + } + else + { + if (!ReleaseDC(m_window_handle, m_dc)) + ERROR_LOG(VIDEO, "Attempt to release device context failed."); + + m_dc = nullptr; + } } - FreeLibrary(dllHandle); } diff --git a/Source/Core/Common/GL/GLInterface/WGL.h b/Source/Core/Common/GL/GLInterface/WGL.h index c71e403884..24e2b942ca 100644 --- a/Source/Core/Common/GL/GLInterface/WGL.h +++ b/Source/Core/Common/GL/GLInterface/WGL.h @@ -14,6 +14,7 @@ public: void Swap() override; void* GetFuncAddress(const std::string& name) override; bool Create(void* window_handle, bool core) override; + bool Create(cInterfaceBase* main_context) override; bool MakeCurrent() override; bool ClearCurrent() override; void Shutdown() override; @@ -21,6 +22,15 @@ public: void Update() override; bool PeekMessages() override; + std::unique_ptr CreateSharedContext() override; + private: + static HGLRC CreateCoreContext(HDC dc, HGLRC share_context); + static bool CreatePBuffer(HDC onscreen_dc, int width, int height, HANDLE* pbuffer_handle, + HDC* pbuffer_dc); + HWND m_window_handle = nullptr; + HANDLE m_pbuffer_handle = nullptr; + HDC m_dc = nullptr; + HGLRC m_rc = nullptr; };