ContextWGL: Use pbuffers when we don't have a surface

Fixes context restore error tripping when confirming shutdown while
fullscreen in Qt.
This commit is contained in:
Connor McLaughlin 2022-06-20 19:56:25 +10:00 committed by refractionpcsx2
parent 771b1490d2
commit 5c88c585a0
4 changed files with 156 additions and 23 deletions

View File

@ -13,10 +13,10 @@
* If not, see <http://www.gnu.org/licenses/>. * If not, see <http://www.gnu.org/licenses/>.
*/ */
#include "common/PrecompiledHeader.h" #include "common/GL/ContextWGL.h"
#include "common/Assertions.h"
#include "common/Console.h" #include "common/Console.h"
#include "ContextWGL.h" #include "common/ScopedGuard.h"
static void* GetProcAddressCallback(const char* name) static void* GetProcAddressCallback(const char* name)
{ {
@ -43,8 +43,7 @@ namespace GL
if (m_rc) if (m_rc)
wglDeleteContext(m_rc); wglDeleteContext(m_rc);
if (m_dc) ReleaseDC();
ReleaseDC(GetHWND(), m_dc);
} }
std::unique_ptr<Context> ContextWGL::Create(const WindowInfo& wi, const Version* versions_to_try, std::unique_ptr<Context> ContextWGL::Create(const WindowInfo& wi, const Version* versions_to_try,
@ -66,7 +65,7 @@ namespace GL
} }
else else
{ {
Console.Error("PBuffer not implemented"); Console.Error("ContextWGL must always start with a valid surface.");
return false; return false;
} }
@ -102,11 +101,7 @@ namespace GL
{ {
const bool was_current = (wglGetCurrentContext() == m_rc); const bool was_current = (wglGetCurrentContext() == m_rc);
if (m_dc) ReleaseDC();
{
ReleaseDC(GetHWND(), m_dc);
m_dc = {};
}
m_wi = new_wi; m_wi = new_wi;
if (!InitializeDC()) if (!InitializeDC())
@ -187,7 +182,7 @@ namespace GL
return context; return context;
} }
bool ContextWGL::InitializeDC() HDC ContextWGL::GetDCAndSetPixelFormat(HWND hwnd)
{ {
PIXELFORMATDESCRIPTOR pfd = {}; PIXELFORMATDESCRIPTOR pfd = {};
pfd.nSize = sizeof(pfd); pfd.nSize = sizeof(pfd);
@ -200,28 +195,156 @@ namespace GL
pfd.cBlueBits = 8; pfd.cBlueBits = 8;
pfd.cColorBits = 24; pfd.cColorBits = 24;
m_dc = GetDC(GetHWND()); HDC hDC = ::GetDC(hwnd);
if (!m_dc) if (!hDC)
{ {
Console.Error("GetDC() failed: 0x%08X", GetLastError()); Console.Error("GetDC() failed: 0x%08X", GetLastError());
return false; return false;
} }
const int pf = ChoosePixelFormat(m_dc, &pfd); if (!m_pixel_format.has_value())
{
const int pf = ChoosePixelFormat(hDC, &pfd);
if (pf == 0) if (pf == 0)
{ {
Console.Error("ChoosePixelFormat() failed: 0x%08X", GetLastError()); Console.Error("ChoosePixelFormat() failed: 0x%08X", GetLastError());
::ReleaseDC(hwnd, hDC);
return false; return false;
} }
if (!SetPixelFormat(m_dc, pf, &pfd)) m_pixel_format = pf;
}
if (!SetPixelFormat(hDC, m_pixel_format.value(), &pfd))
{ {
Console.Error("SetPixelFormat() failed: 0x%08X", GetLastError()); Console.Error("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)
{
Console.Error("Failed to get DC for window");
return false; return false;
} }
return true; return true;
} }
else if (m_wi.type == WindowInfo::Type::Surfaceless)
{
return CreatePBuffer();
}
else
{
Console.Error("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))
{
Console.Error("(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)
{
Console.Error("(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};
pxAssertRel(m_pixel_format.has_value(), "Has pixel format for pbuffer");
HPBUFFERARB pbuffer = wglCreatePbufferARB(hdc, m_pixel_format.value(), 1, 1, pb_attribs);
if (!pbuffer)
{
Console.Error("(ContextWGL::CreatePBuffer) wglCreatePbufferARB() failed");
return false;
}
ScopedGuard pbuffer_guard([pbuffer]() { wglDestroyPbufferARB(pbuffer); });
m_dc = wglGetPbufferDCARB(pbuffer);
if (!m_dc)
{
Console.Error("(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) bool ContextWGL::CreateAnyContext(HGLRC share_context, bool make_current)
{ {

View File

@ -24,6 +24,7 @@
#define WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN
#endif #endif
#include <windows.h> #include <windows.h>
#include <optional>
namespace GL namespace GL
{ {
@ -48,12 +49,24 @@ namespace GL
private: private:
__fi HWND GetHWND() const { return static_cast<HWND>(m_wi.window_handle); } __fi HWND GetHWND() const { return static_cast<HWND>(m_wi.window_handle); }
HDC GetDCAndSetPixelFormat(HWND hwnd);
bool Initialize(const Version* versions_to_try, size_t num_versions_to_try); bool Initialize(const Version* versions_to_try, size_t num_versions_to_try);
bool InitializeDC(); bool InitializeDC();
void ReleaseDC();
bool CreatePBuffer();
bool CreateAnyContext(HGLRC share_context, bool make_current); bool CreateAnyContext(HGLRC share_context, bool make_current);
bool CreateVersionContext(const Version& version, HGLRC share_context, bool make_current); bool CreateVersionContext(const Version& version, HGLRC share_context, bool make_current);
HDC m_dc = {}; HDC m_dc = {};
HGLRC m_rc = {}; HGLRC m_rc = {};
// Can't change pixel format once it's set for a RC.
std::optional<int> 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 } // namespace GL

View File

@ -688,10 +688,7 @@ void EmuThread::releaseHostDisplay()
ImGuiManager::Shutdown(); ImGuiManager::Shutdown();
if (s_host_display) if (s_host_display)
{
s_host_display->DestroyRenderSurface();
s_host_display->DestroyRenderDevice(); s_host_display->DestroyRenderDevice();
}
emit onDestroyDisplayRequested(); emit onDestroyDisplayRequested();

View File

@ -326,9 +326,9 @@ void VulkanHostDisplay::DestroyRenderDevice()
return; return;
g_vulkan_context->WaitForGPUIdle(); g_vulkan_context->WaitForGPUIdle();
m_swap_chain.reset();
Vulkan::ShaderCache::Destroy(); Vulkan::ShaderCache::Destroy();
DestroyRenderSurface();
Vulkan::Context::Destroy(); Vulkan::Context::Destroy();
} }