diff --git a/src/duckstation-qt/CMakeLists.txt b/src/duckstation-qt/CMakeLists.txt
index 6dc18d695..dfa086cd5 100644
--- a/src/duckstation-qt/CMakeLists.txt
+++ b/src/duckstation-qt/CMakeLists.txt
@@ -58,10 +58,12 @@ add_executable(duckstation-qt
settingsdialog.cpp
settingsdialog.h
settingsdialog.ui
+ vulkanhostdisplay.cpp
+ vulkanhostdisplay.h
)
target_include_directories(duckstation-qt PRIVATE "${Qt5Gui_PRIVATE_INCLUDE_DIRS}")
-target_link_libraries(duckstation-qt PRIVATE frontend-common core common imgui glad minizip scmversion Qt5::Core Qt5::Gui Qt5::Widgets Qt5::Network)
+target_link_libraries(duckstation-qt PRIVATE frontend-common core common imgui glad minizip scmversion vulkan-loader Qt5::Core Qt5::Gui Qt5::Widgets Qt5::Network)
if(WIN32)
target_sources(duckstation-qt PRIVATE
diff --git a/src/duckstation-qt/d3d11hostdisplay.cpp b/src/duckstation-qt/d3d11hostdisplay.cpp
index a3781b478..1e5ad79be 100644
--- a/src/duckstation-qt/d3d11hostdisplay.cpp
+++ b/src/duckstation-qt/d3d11hostdisplay.cpp
@@ -214,7 +214,7 @@ bool D3D11HostDisplay::createDeviceContext(bool debug_device)
m_allow_tearing_supported = (allow_tearing_supported == TRUE);
}
- return true;
+ return createSwapChain();
}
void D3D11HostDisplay::destroyDeviceContext()
@@ -232,7 +232,7 @@ bool D3D11HostDisplay::shouldUseFlipModelSwapChain() const
return m_widget->parent() == nullptr;
}
-bool D3D11HostDisplay::createSurface()
+bool D3D11HostDisplay::createSwapChain()
{
m_using_flip_model_swap_chain = shouldUseFlipModelSwapChain();
@@ -282,11 +282,7 @@ bool D3D11HostDisplay::createSurface()
if (FAILED(hr))
Log_WarningPrintf("MakeWindowAssociation() to disable ALT+ENTER failed");
- if (!createSwapChainRTV())
- return false;
-
- emit m_widget->windowResizedEvent(m_window_width, m_window_height);
- return true;
+ return createSwapChainRTV();
}
bool D3D11HostDisplay::createSwapChainRTV()
@@ -314,6 +310,11 @@ bool D3D11HostDisplay::createSwapChainRTV()
return true;
}
+bool D3D11HostDisplay::recreateSurface()
+{
+ return createSwapChain();
+}
+
void D3D11HostDisplay::destroySurface()
{
m_swap_chain_rtv.Reset();
diff --git a/src/duckstation-qt/d3d11hostdisplay.h b/src/duckstation-qt/d3d11hostdisplay.h
index 4659e92c3..fa5bf1c65 100644
--- a/src/duckstation-qt/d3d11hostdisplay.h
+++ b/src/duckstation-qt/d3d11hostdisplay.h
@@ -22,7 +22,7 @@ public:
bool hasDeviceContext() const override;
bool createDeviceContext(bool debug_device) override;
void destroyDeviceContext() override;
- bool createSurface() override;
+ bool recreateSurface() override;
void destroySurface() override;
RenderAPI GetRenderAPI() const override;
@@ -50,6 +50,7 @@ private:
void destroyDeviceResources() override;
bool shouldUseFlipModelSwapChain() const;
+ bool createSwapChain();
bool createSwapChainRTV();
void renderDisplay();
diff --git a/src/duckstation-qt/duckstation-qt.vcxproj b/src/duckstation-qt/duckstation-qt.vcxproj
index 111ace081..1b55b2df5 100644
--- a/src/duckstation-qt/duckstation-qt.vcxproj
+++ b/src/duckstation-qt/duckstation-qt.vcxproj
@@ -59,8 +59,10 @@
+
+
@@ -97,6 +99,9 @@
{8bda439c-6358-45fb-9994-2ff083babe06}
+
+ {9c8ddeb0-2b8f-4f5f-ba86-127cdf27f035}
+
{ee054e08-3799-4a59-a422-18259c105ffd}
@@ -335,7 +340,7 @@
WITH_DISCORD_PRESENCE=1;_CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)
true
ProgramDatabase
- $(SolutionDir)dep\glad\Include;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\zlib\include;$(SolutionDir)dep\minizip\include;$(SolutionDir)src;$(SolutionDir)dep\msvc\qt5-x86\include;%(AdditionalIncludeDirectories)
+ $(SolutionDir)dep\glad\Include;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\zlib\include;$(SolutionDir)dep\minizip\include;$(SolutionDir)dep\vulkan-loader\include;$(SolutionDir)src;$(SolutionDir)dep\msvc\qt5-x86\include;%(AdditionalIncludeDirectories)
true
false
stdcpp17
@@ -356,7 +361,7 @@
WITH_DISCORD_PRESENCE=1;_CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)
true
ProgramDatabase
- $(SolutionDir)dep\glad\Include;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\zlib\include;$(SolutionDir)dep\minizip\include;$(SolutionDir)src;$(SolutionDir)dep\msvc\qt5-x64\include;%(AdditionalIncludeDirectories)
+ $(SolutionDir)dep\glad\Include;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\zlib\include;$(SolutionDir)dep\minizip\include;$(SolutionDir)dep\vulkan-loader\include;$(SolutionDir)src;$(SolutionDir)dep\msvc\qt5-x64\include;%(AdditionalIncludeDirectories)
true
false
stdcpp17
@@ -377,7 +382,7 @@
WITH_DISCORD_PRESENCE=1;_ITERATOR_DEBUG_LEVEL=1;_CRT_SECURE_NO_WARNINGS;WIN32;_DEBUGFAST;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)
true
ProgramDatabase
- $(SolutionDir)dep\glad\Include;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\zlib\include;$(SolutionDir)dep\minizip\include;$(SolutionDir)src;$(SolutionDir)dep\msvc\qt5-x86\include;%(AdditionalIncludeDirectories)
+ $(SolutionDir)dep\glad\Include;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\zlib\include;$(SolutionDir)dep\minizip\include;$(SolutionDir)dep\vulkan-loader\include;$(SolutionDir)src;$(SolutionDir)dep\msvc\qt5-x86\include;%(AdditionalIncludeDirectories)
Default
true
false
@@ -400,7 +405,7 @@
WITH_DISCORD_PRESENCE=1;_ITERATOR_DEBUG_LEVEL=1;_CRT_SECURE_NO_WARNINGS;WIN32;_DEBUGFAST;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)
true
ProgramDatabase
- $(SolutionDir)dep\glad\Include;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\zlib\include;$(SolutionDir)dep\minizip\include;$(SolutionDir)src;$(SolutionDir)dep\msvc\qt5-x64\include;%(AdditionalIncludeDirectories)
+ $(SolutionDir)dep\glad\Include;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\zlib\include;$(SolutionDir)dep\minizip\include;$(SolutionDir)dep\vulkan-loader\include;$(SolutionDir)src;$(SolutionDir)dep\msvc\qt5-x64\include;%(AdditionalIncludeDirectories)
Default
true
false
@@ -422,7 +427,7 @@
MaxSpeed
true
WITH_DISCORD_PRESENCE=1;_CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)
- $(SolutionDir)dep\glad\Include;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\zlib\include;$(SolutionDir)dep\minizip\include;$(SolutionDir)src;$(SolutionDir)dep\msvc\qt5-x86\include;%(AdditionalIncludeDirectories)
+ $(SolutionDir)dep\glad\Include;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\zlib\include;$(SolutionDir)dep\minizip\include;$(SolutionDir)dep\vulkan-loader\include;$(SolutionDir)src;$(SolutionDir)dep\msvc\qt5-x86\include;%(AdditionalIncludeDirectories)
true
false
stdcpp17
@@ -445,7 +450,7 @@
MaxSpeed
true
WITH_DISCORD_PRESENCE=1;_CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)
- $(SolutionDir)dep\glad\Include;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\zlib\include;$(SolutionDir)dep\minizip\include;$(SolutionDir)src;$(SolutionDir)dep\msvc\qt5-x86\include;%(AdditionalIncludeDirectories)
+ $(SolutionDir)dep\glad\Include;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\zlib\include;$(SolutionDir)dep\minizip\include;$(SolutionDir)dep\vulkan-loader\include;$(SolutionDir)src;$(SolutionDir)dep\msvc\qt5-x86\include;%(AdditionalIncludeDirectories)
true
true
stdcpp17
@@ -469,7 +474,7 @@
MaxSpeed
true
WITH_DISCORD_PRESENCE=1;_CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)
- $(SolutionDir)dep\glad\Include;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\zlib\include;$(SolutionDir)dep\minizip\include;$(SolutionDir)src;$(SolutionDir)dep\msvc\qt5-x64\include;%(AdditionalIncludeDirectories)
+ $(SolutionDir)dep\glad\Include;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\zlib\include;$(SolutionDir)dep\minizip\include;$(SolutionDir)dep\vulkan-loader\include;$(SolutionDir)src;$(SolutionDir)dep\msvc\qt5-x64\include;%(AdditionalIncludeDirectories)
true
false
stdcpp17
@@ -492,7 +497,7 @@
MaxSpeed
true
WITH_DISCORD_PRESENCE=1;_CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)
- $(SolutionDir)dep\glad\Include;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\zlib\include;$(SolutionDir)dep\minizip\include;$(SolutionDir)src;$(SolutionDir)dep\msvc\qt5-x64\include;%(AdditionalIncludeDirectories)
+ $(SolutionDir)dep\glad\Include;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\zlib\include;$(SolutionDir)dep\minizip\include;$(SolutionDir)dep\vulkan-loader\include;$(SolutionDir)src;$(SolutionDir)dep\msvc\qt5-x64\include;%(AdditionalIncludeDirectories)
true
true
stdcpp17
@@ -512,4 +517,4 @@
-
+
\ No newline at end of file
diff --git a/src/duckstation-qt/duckstation-qt.vcxproj.filters b/src/duckstation-qt/duckstation-qt.vcxproj.filters
index 5cf95dab4..1b30ee96e 100644
--- a/src/duckstation-qt/duckstation-qt.vcxproj.filters
+++ b/src/duckstation-qt/duckstation-qt.vcxproj.filters
@@ -16,7 +16,6 @@
-
@@ -39,6 +38,13 @@
+
+
+
+
+
+
+
@@ -48,6 +54,7 @@
+
@@ -74,6 +81,8 @@
+
+
@@ -85,6 +94,7 @@
+
@@ -95,4 +105,4 @@
-
+
\ No newline at end of file
diff --git a/src/duckstation-qt/mainwindow.cpp b/src/duckstation-qt/mainwindow.cpp
index 8b6bceea3..4d208127a 100644
--- a/src/duckstation-qt/mainwindow.cpp
+++ b/src/duckstation-qt/mainwindow.cpp
@@ -103,13 +103,6 @@ void MainWindow::createDisplay(QThread* worker_thread, bool use_debug_device, bo
return;
}
- if (!m_host_display->createSurface())
- {
- reportError(tr("Failed to create host display surface."));
- m_host_display->destroyDeviceContext();
- return;
- }
-
m_host_display->deactivateDeviceContext();
}
@@ -151,7 +144,7 @@ void MainWindow::updateDisplay(QThread* worker_thread, bool fullscreen, bool ren
// we need the surface visible.. this might be able to be replaced with something else
QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
- if (!m_host_display->createSurface())
+ if (!m_host_display->recreateSurface())
Panic("Failed to recreate surface on new widget.");
m_display_widget->setFocus();
diff --git a/src/duckstation-qt/openglhostdisplay.cpp b/src/duckstation-qt/openglhostdisplay.cpp
index 4feaca1fc..c6234e7c6 100644
--- a/src/duckstation-qt/openglhostdisplay.cpp
+++ b/src/duckstation-qt/openglhostdisplay.cpp
@@ -4,15 +4,10 @@
#include "imgui.h"
#include "qtdisplaywidget.h"
#include "qthostinterface.h"
-#include
-#include
#include
#include
#include
#include
-#if !defined(WIN32) && !defined(APPLE)
-#include
-#endif
#include
Log_SetChannel(OpenGLHostDisplay);
@@ -193,49 +188,16 @@ bool OpenGLHostDisplay::hasDeviceContext() const
return static_cast(m_gl_context);
}
-WindowInfo OpenGLHostDisplay::getWindowInfo() const
-{
- WindowInfo wi;
-
- // Windows and Apple are easy here since there's no display connection.
-#if defined(WIN32)
- wi.type = WindowInfo::Type::Win32;
- wi.window_handle = reinterpret_cast(m_widget->winId());
-#elif defined(__APPLE__)
- wi.type = WindowInfo::Type::MacOS;
- wi.window_handle = reinterpret_cast(m_widget->winId());
-#else
- QPlatformNativeInterface* pni = QGuiApplication::platformNativeInterface();
- const QString platform_name = QGuiApplication::platformName();
- if (platform_name == QStringLiteral("xcb"))
- {
- wi.type = WindowInfo::Type::X11;
- wi.display_connection = pni->nativeResourceForWindow("display", m_widget->windowHandle());
- wi.window_handle = reinterpret_cast(m_widget->winId());
- }
- else if (platform_name == QStringLiteral("wayland"))
- {
- wi.type = WindowInfo::Type::Wayland;
- wi.display_connection = pni->nativeResourceForWindow("display", m_widget->windowHandle());
- wi.window_handle = pni->nativeResourceForWindow("surface", m_widget->windowHandle());
- }
- else
- {
- qCritical() << "Unknown PNI platform " << platform_name;
- return wi;
- }
-#endif
-
- wi.surface_width = m_widget->width();
- wi.surface_height = m_widget->height();
- wi.surface_format = WindowInfo::SurfaceFormat::RGB8;
-
- return wi;
-}
-
bool OpenGLHostDisplay::createDeviceContext(bool debug_device)
{
- m_gl_context = GL::Context::Create(getWindowInfo());
+ m_window_width = m_widget->scaledWindowWidth();
+ m_window_height = m_widget->scaledWindowHeight();
+
+ std::optional wi = getWindowInfo();
+ if (!wi.has_value())
+ return false;
+
+ m_gl_context = GL::Context::Create(wi.value());
if (!m_gl_context)
{
Log_ErrorPrintf("Failed to create any GL context");
@@ -245,7 +207,7 @@ bool OpenGLHostDisplay::createDeviceContext(bool debug_device)
return true;
}
-bool OpenGLHostDisplay::initializeDeviceContext(bool debug_device)
+bool OpenGLHostDisplay::initializeDeviceContext(std::string_view shader_cache_directory, bool debug_device)
{
if (debug_device && GLAD_GL_KHR_debug)
{
@@ -254,7 +216,7 @@ bool OpenGLHostDisplay::initializeDeviceContext(bool debug_device)
glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
}
- if (!QtHostDisplay::initializeDeviceContext(debug_device))
+ if (!QtHostDisplay::initializeDeviceContext(shader_cache_directory, debug_device))
{
m_gl_context->DoneCurrent();
return false;
@@ -286,14 +248,17 @@ void OpenGLHostDisplay::destroyDeviceContext()
m_gl_context.reset();
}
-bool OpenGLHostDisplay::createSurface()
+bool OpenGLHostDisplay::recreateSurface()
{
m_window_width = m_widget->scaledWindowWidth();
m_window_height = m_widget->scaledWindowHeight();
- emit m_widget->windowResizedEvent(m_window_width, m_window_height);
if (m_gl_context)
- m_gl_context->ChangeSurface(getWindowInfo());
+ {
+ std::optional wi = getWindowInfo();
+ if (!wi.has_value() || !m_gl_context->ChangeSurface(wi.value()))
+ return false;
+ }
return true;
}
diff --git a/src/duckstation-qt/openglhostdisplay.h b/src/duckstation-qt/openglhostdisplay.h
index 6ec94678f..9342ae9f4 100644
--- a/src/duckstation-qt/openglhostdisplay.h
+++ b/src/duckstation-qt/openglhostdisplay.h
@@ -27,11 +27,11 @@ public:
bool hasDeviceContext() const override;
bool createDeviceContext(bool debug_device) override;
- bool initializeDeviceContext(bool debug_device) override;
+ bool initializeDeviceContext(std::string_view shader_cache_directory, bool debug_device) override;
bool activateDeviceContext() override;
void deactivateDeviceContext() override;
void destroyDeviceContext() override;
- bool createSurface() override;
+ bool recreateSurface() override;
void destroySurface();
RenderAPI GetRenderAPI() const override;
@@ -54,8 +54,6 @@ private:
const char* GetGLSLVersionString() const;
std::string GetGLSLVersionHeader() const;
- WindowInfo getWindowInfo() const;
-
bool createImGuiContext() override;
void destroyImGuiContext() override;
bool createDeviceResources() override;
diff --git a/src/duckstation-qt/qthostdisplay.cpp b/src/duckstation-qt/qthostdisplay.cpp
index c343611df..d7d0df492 100644
--- a/src/duckstation-qt/qthostdisplay.cpp
+++ b/src/duckstation-qt/qthostdisplay.cpp
@@ -4,7 +4,12 @@
#include "imgui.h"
#include "qtdisplaywidget.h"
#include "qthostinterface.h"
+#include
+#include
#include
+#if !defined(WIN32) && !defined(APPLE)
+#include
+#endif
QtHostDisplay::QtHostDisplay(QtHostInterface* host_interface) : m_host_interface(host_interface) {}
@@ -42,7 +47,7 @@ bool QtHostDisplay::createDeviceContext(bool debug_device)
return false;
}
-bool QtHostDisplay::initializeDeviceContext(bool debug_device)
+bool QtHostDisplay::initializeDeviceContext(std::string_view shader_cache_directory, bool debug_device)
{
if (!createImGuiContext() || !createDeviceResources())
return false;
@@ -63,7 +68,7 @@ void QtHostDisplay::destroyDeviceContext()
destroyDeviceResources();
}
-bool QtHostDisplay::createSurface()
+bool QtHostDisplay::recreateSurface()
{
return false;
}
@@ -118,3 +123,43 @@ void QtHostDisplay::updateImGuiDisplaySize()
io.DisplaySize.x = static_cast(m_window_width);
io.DisplaySize.y = static_cast(m_window_height);
}
+
+std::optional QtHostDisplay::getWindowInfo() const
+{
+ WindowInfo wi;
+
+ // Windows and Apple are easy here since there's no display connection.
+#if defined(WIN32)
+ wi.type = WindowInfo::Type::Win32;
+ wi.window_handle = reinterpret_cast(m_widget->winId());
+#elif defined(__APPLE__)
+ wi.type = WindowInfo::Type::MacOS;
+ wi.window_handle = reinterpret_cast(m_widget->winId());
+#else
+ QPlatformNativeInterface* pni = QGuiApplication::platformNativeInterface();
+ const QString platform_name = QGuiApplication::platformName();
+ if (platform_name == QStringLiteral("xcb"))
+ {
+ wi.type = WindowInfo::Type::X11;
+ wi.display_connection = pni->nativeResourceForWindow("display", m_widget->windowHandle());
+ wi.window_handle = reinterpret_cast(m_widget->winId());
+ }
+ else if (platform_name == QStringLiteral("wayland"))
+ {
+ wi.type = WindowInfo::Type::Wayland;
+ wi.display_connection = pni->nativeResourceForWindow("display", m_widget->windowHandle());
+ wi.window_handle = pni->nativeResourceForWindow("surface", m_widget->windowHandle());
+ }
+ else
+ {
+ qCritical() << "Unknown PNI platform " << platform_name;
+ return std::nullopt;
+ }
+#endif
+
+ wi.surface_width = m_widget->width();
+ wi.surface_height = m_widget->height();
+ wi.surface_format = WindowInfo::SurfaceFormat::RGB8;
+
+ return wi;
+}
diff --git a/src/duckstation-qt/qthostdisplay.h b/src/duckstation-qt/qthostdisplay.h
index 285291ad9..dbf337406 100644
--- a/src/duckstation-qt/qthostdisplay.h
+++ b/src/duckstation-qt/qthostdisplay.h
@@ -1,6 +1,9 @@
#pragma once
#include "common/types.h"
+#include "common/window_info.h"
#include "core/host_display.h"
+#include
+#include
class QThread;
class QWidget;
@@ -22,11 +25,11 @@ public:
virtual bool hasDeviceContext() const;
virtual bool createDeviceContext(bool debug_device);
- virtual bool initializeDeviceContext(bool debug_device);
+ virtual bool initializeDeviceContext(std::string_view shader_cache_directory, bool debug_device);
virtual bool activateDeviceContext();
virtual void deactivateDeviceContext();
virtual void destroyDeviceContext();
- virtual bool createSurface();
+ virtual bool recreateSurface();
virtual void destroySurface();
virtual void WindowResized(s32 new_window_width, s32 new_window_height) override;
@@ -39,6 +42,8 @@ protected:
virtual bool createDeviceResources();
virtual void destroyDeviceResources();
+ std::optional getWindowInfo() const;
+
QtHostInterface* m_host_interface;
QtDisplayWidget* m_widget = nullptr;
};
diff --git a/src/duckstation-qt/qthostinterface.cpp b/src/duckstation-qt/qthostinterface.cpp
index 18233d813..5f56492ae 100644
--- a/src/duckstation-qt/qthostinterface.cpp
+++ b/src/duckstation-qt/qthostinterface.cpp
@@ -15,6 +15,7 @@
#include "qtprogresscallback.h"
#include "qtsettingsinterface.h"
#include "qtutils.h"
+#include "vulkanhostdisplay.h"
#include
#include
#include
@@ -327,7 +328,7 @@ bool QtHostInterface::AcquireHostDisplay()
}
if (!getHostDisplay()->activateDeviceContext() ||
- !getHostDisplay()->initializeDeviceContext(m_settings.gpu_use_debug_device))
+ !getHostDisplay()->initializeDeviceContext(GetShaderCacheDirectory(), m_settings.gpu_use_debug_device))
{
getHostDisplay()->destroyDeviceContext();
emit destroyDisplayRequested();
@@ -343,14 +344,26 @@ QtHostDisplay* QtHostInterface::createHostDisplay()
{
Assert(!getHostDisplay());
-#ifdef WIN32
- if (m_settings.gpu_renderer == GPURenderer::HardwareOpenGL)
- m_display = new OpenGLHostDisplay(this);
- else
- m_display = new D3D11HostDisplay(this);
-#else
- m_display = new OpenGLHostDisplay(this);
+ switch (m_settings.gpu_renderer)
+ {
+ case GPURenderer::HardwareVulkan:
+ m_display = new VulkanHostDisplay(this);
+ break;
+
+ case GPURenderer::HardwareOpenGL:
+#ifndef WIN32
+ default:
#endif
+ m_display = new OpenGLHostDisplay(this);
+ break;
+
+#ifdef WIN32
+ case GPURenderer::HardwareD3D11:
+ default:
+ m_display = new D3D11HostDisplay(this);
+ break;
+#endif
+ }
return getHostDisplay();
}
diff --git a/src/duckstation-qt/vulkanhostdisplay.cpp b/src/duckstation-qt/vulkanhostdisplay.cpp
new file mode 100644
index 000000000..ba86e0a1f
--- /dev/null
+++ b/src/duckstation-qt/vulkanhostdisplay.cpp
@@ -0,0 +1,168 @@
+#include "vulkanhostdisplay.h"
+#include "common/assert.h"
+#include "common/log.h"
+#include "imgui.h"
+#include "qtdisplaywidget.h"
+Log_SetChannel(VulkanHostDisplay);
+
+VulkanHostDisplay::VulkanHostDisplay(QtHostInterface* host_interface) : QtHostDisplay(host_interface) {}
+
+VulkanHostDisplay::~VulkanHostDisplay() = default;
+
+HostDisplay::RenderAPI VulkanHostDisplay::GetRenderAPI() const
+{
+ return m_vulkan_display.GetRenderAPI();
+}
+
+void* VulkanHostDisplay::GetRenderDevice() const
+{
+ return m_vulkan_display.GetRenderDevice();
+}
+
+void* VulkanHostDisplay::GetRenderContext() const
+{
+ return m_vulkan_display.GetRenderContext();
+}
+
+std::unique_ptr VulkanHostDisplay::CreateTexture(u32 width, u32 height, const void* initial_data,
+ u32 initial_data_stride, bool dynamic)
+{
+ return m_vulkan_display.CreateTexture(width, height, initial_data, initial_data_stride, dynamic);
+}
+
+void VulkanHostDisplay::UpdateTexture(HostDisplayTexture* texture, u32 x, u32 y, u32 width, u32 height,
+ const void* texture_data, u32 texture_data_stride)
+{
+ m_vulkan_display.UpdateTexture(texture, x, y, width, height, texture_data, texture_data_stride);
+}
+
+bool VulkanHostDisplay::DownloadTexture(const void* texture_handle, u32 x, u32 y, u32 width, u32 height, void* out_data,
+ u32 out_data_stride)
+{
+ return m_vulkan_display.DownloadTexture(texture_handle, x, y, width, height, out_data, out_data_stride);
+}
+
+void VulkanHostDisplay::SetVSync(bool enabled)
+{
+ m_vulkan_display.SetVSync(enabled);
+}
+
+bool VulkanHostDisplay::hasDeviceContext() const
+{
+ return m_vulkan_display.HasContext();
+}
+
+bool VulkanHostDisplay::createDeviceContext(bool debug_device)
+{
+ std::optional wi = getWindowInfo();
+ if (!wi || !m_vulkan_display.CreateContextAndSwapChain(wi.value(), debug_device))
+ return false;
+
+ m_window_width = static_cast(m_vulkan_display.GetSwapChainWidth());
+ m_window_height = static_cast(m_vulkan_display.GetSwapChainHeight());
+ return true;
+}
+
+bool VulkanHostDisplay::initializeDeviceContext(std::string_view shader_cache_directory, bool debug_device)
+{
+ m_vulkan_display.CreateShaderCache(shader_cache_directory, debug_device);
+
+ return QtHostDisplay::initializeDeviceContext(shader_cache_directory, debug_device);
+}
+
+bool VulkanHostDisplay::activateDeviceContext()
+{
+ return true;
+}
+
+void VulkanHostDisplay::deactivateDeviceContext() {}
+
+void VulkanHostDisplay::destroyDeviceContext()
+{
+ QtHostDisplay::destroyDeviceContext();
+ m_vulkan_display.DestroyShaderCache();
+ m_vulkan_display.DestroySwapChain();
+ m_vulkan_display.DestroyContext();
+}
+
+bool VulkanHostDisplay::recreateSurface()
+{
+ std::optional wi = getWindowInfo();
+ if (!wi.has_value())
+ return false;
+
+ if (!m_vulkan_display.RecreateSwapChain(wi.value()))
+ return false;
+
+ m_window_width = static_cast(m_vulkan_display.GetSwapChainWidth());
+ m_window_height = static_cast(m_vulkan_display.GetSwapChainHeight());
+ return true;
+}
+
+void VulkanHostDisplay::destroySurface()
+{
+ m_vulkan_display.DestroySwapChain();
+}
+
+void VulkanHostDisplay::WindowResized(s32 new_window_width, s32 new_window_height)
+{
+ QtHostDisplay::WindowResized(new_window_width, new_window_height);
+
+ m_vulkan_display.ResizeSwapChain(static_cast(new_window_width), static_cast(new_window_height));
+ m_window_width = static_cast(m_vulkan_display.GetSwapChainWidth());
+ m_window_height = static_cast(m_vulkan_display.GetSwapChainHeight());
+}
+
+bool VulkanHostDisplay::createDeviceResources()
+{
+ if (!QtHostDisplay::createDeviceResources())
+ return false;
+
+ return m_vulkan_display.CreateResources();
+}
+
+void VulkanHostDisplay::destroyDeviceResources()
+{
+ QtHostDisplay::destroyDeviceResources();
+ m_vulkan_display.DestroyResources();
+}
+
+bool VulkanHostDisplay::createImGuiContext()
+{
+ if (!QtHostDisplay::createImGuiContext() || !m_vulkan_display.CreateImGuiContext())
+ return false;
+
+ ImGui::NewFrame();
+ return true;
+}
+
+void VulkanHostDisplay::destroyImGuiContext()
+{
+ m_vulkan_display.DestroyImGuiContext();
+ QtHostDisplay::destroyImGuiContext();
+}
+
+void VulkanHostDisplay::Render()
+{
+ if (!m_vulkan_display.HasSwapChain() || !m_vulkan_display.BeginRender())
+ return;
+
+ if (HasDisplayTexture())
+ {
+ const auto [left, top, width, height] = CalculateDrawRect(m_window_width, m_window_height, m_display_top_margin);
+ m_vulkan_display.RenderDisplay(left, top, width, height, m_display_texture_handle, m_display_texture_width,
+ m_display_texture_height, m_display_texture_view_x, m_display_texture_view_y,
+ m_display_texture_view_width, m_display_texture_view_height,
+ m_display_linear_filtering);
+ }
+
+ m_vulkan_display.RenderImGui();
+
+ if (HasSoftwareCursor())
+ {
+ const auto [left, top, width, height] = CalculateSoftwareCursorDrawRect();
+ m_vulkan_display.RenderSoftwareCursor(left, top, width, height, m_cursor_texture.get());
+ }
+
+ m_vulkan_display.EndRenderAndPresent();
+}
diff --git a/src/duckstation-qt/vulkanhostdisplay.h b/src/duckstation-qt/vulkanhostdisplay.h
new file mode 100644
index 000000000..dcd496cab
--- /dev/null
+++ b/src/duckstation-qt/vulkanhostdisplay.h
@@ -0,0 +1,49 @@
+#pragma once
+#include "common/window_info.h"
+#include "core/host_display.h"
+#include "qtdisplaywidget.h"
+#include "qthostdisplay.h"
+#include "frontend-common/vulkan_host_display.h"
+#include
+
+class QtHostInterface;
+
+class VulkanHostDisplay final : public QtHostDisplay
+{
+public:
+ VulkanHostDisplay(QtHostInterface* host_interface);
+ ~VulkanHostDisplay();
+
+ bool hasDeviceContext() const override;
+ bool createDeviceContext(bool debug_device) override;
+ bool initializeDeviceContext(std::string_view shader_cache_directory, bool debug_device) override;
+ bool activateDeviceContext() override;
+ void deactivateDeviceContext() override;
+ void destroyDeviceContext() override;
+ bool recreateSurface() override;
+ void destroySurface();
+
+ RenderAPI GetRenderAPI() const override;
+ void* GetRenderDevice() const override;
+ void* GetRenderContext() const override;
+ void WindowResized(s32 new_window_width, s32 new_window_height) override;
+
+ std::unique_ptr CreateTexture(u32 width, u32 height, const void* initial_data,
+ u32 initial_data_stride, bool dynamic) override;
+ void UpdateTexture(HostDisplayTexture* texture, u32 x, u32 y, u32 width, u32 height, const void* texture_data,
+ u32 texture_data_stride) override;
+ bool DownloadTexture(const void* texture_handle, u32 x, u32 y, u32 width, u32 height, void* out_data,
+ u32 out_data_stride) override;
+
+ void SetVSync(bool enabled) override;
+
+ void Render() override;
+
+private:
+ bool createImGuiContext() override;
+ void destroyImGuiContext() override;
+ bool createDeviceResources() override;
+ void destroyDeviceResources() override;
+
+ FrontendCommon::VulkanHostDisplay m_vulkan_display;
+};