From 2a38090e7ae869f1cff888d71ee75c9ecc7f3f56 Mon Sep 17 00:00:00 2001 From: Connor McLaughlin Date: Tue, 30 Jun 2020 02:46:57 +1000 Subject: [PATCH] HostDisplay: Move most backend logic to FrontendCommon --- duckstation.sln | 4 - src/common/window_info.h | 3 +- src/core/host_display.cpp | 20 +- src/core/host_display.h | 31 +- src/core/host_interface.h | 16 +- src/core/settings.cpp | 2 +- src/core/system.h | 6 + src/duckstation-qt/CMakeLists.txt | 11 - src/duckstation-qt/d3d11hostdisplay.cpp | 174 --------- src/duckstation-qt/d3d11hostdisplay.h | 51 --- src/duckstation-qt/duckstation-qt.vcxproj | 14 - .../duckstation-qt.vcxproj.filters | 12 +- src/duckstation-qt/mainwindow.cpp | 70 ++-- src/duckstation-qt/mainwindow.h | 11 +- src/duckstation-qt/openglhostdisplay.h | 70 ---- src/duckstation-qt/qtdisplaywidget.cpp | 46 ++- src/duckstation-qt/qtdisplaywidget.h | 4 + src/duckstation-qt/qthostdisplay.cpp | 165 -------- src/duckstation-qt/qthostdisplay.h | 50 --- src/duckstation-qt/qthostinterface.cpp | 103 +++-- src/duckstation-qt/qthostinterface.h | 20 +- src/duckstation-qt/vulkanhostdisplay.cpp | 168 -------- src/duckstation-qt/vulkanhostdisplay.h | 49 --- src/duckstation-sdl/CMakeLists.txt | 6 - src/duckstation-sdl/duckstation-sdl.vcxproj | 6 - .../duckstation-sdl.vcxproj.filters | 6 - src/duckstation-sdl/opengl_host_display.cpp | 369 ------------------ src/duckstation-sdl/opengl_host_display.h | 51 --- .../sdl_d3d11_host_display.cpp | 121 ------ src/duckstation-sdl/sdl_d3d11_host_display.h | 39 -- src/duckstation-sdl/sdl_host_interface.cpp | 79 +++- .../sdl_vulkan_host_display.cpp | 137 ------- src/duckstation-sdl/sdl_vulkan_host_display.h | 40 -- src/frontend-common/CMakeLists.txt | 8 +- src/frontend-common/common_host_interface.cpp | 5 +- src/frontend-common/d3d11_host_display.cpp | 157 ++++++-- src/frontend-common/d3d11_host_display.h | 73 ++-- src/frontend-common/frontend-common.vcxproj | 21 +- .../frontend-common.vcxproj.filters | 2 + .../opengl_host_display.cpp} | 248 ++++++++---- src/frontend-common/opengl_host_display.h | 82 ++++ src/frontend-common/vulkan_host_display.cpp | 143 +++++-- src/frontend-common/vulkan_host_display.h | 76 ++-- 43 files changed, 865 insertions(+), 1904 deletions(-) delete mode 100644 src/duckstation-qt/d3d11hostdisplay.cpp delete mode 100644 src/duckstation-qt/d3d11hostdisplay.h delete mode 100644 src/duckstation-qt/openglhostdisplay.h delete mode 100644 src/duckstation-qt/qthostdisplay.cpp delete mode 100644 src/duckstation-qt/qthostdisplay.h delete mode 100644 src/duckstation-qt/vulkanhostdisplay.cpp delete mode 100644 src/duckstation-qt/vulkanhostdisplay.h delete mode 100644 src/duckstation-sdl/opengl_host_display.cpp delete mode 100644 src/duckstation-sdl/opengl_host_display.h delete mode 100644 src/duckstation-sdl/sdl_d3d11_host_display.cpp delete mode 100644 src/duckstation-sdl/sdl_d3d11_host_display.h delete mode 100644 src/duckstation-sdl/sdl_vulkan_host_display.cpp delete mode 100644 src/duckstation-sdl/sdl_vulkan_host_display.h rename src/{duckstation-qt/openglhostdisplay.cpp => frontend-common/opengl_host_display.cpp} (54%) create mode 100644 src/frontend-common/opengl_host_display.h diff --git a/duckstation.sln b/duckstation.sln index 8179f7aee..2380b0563 100644 --- a/duckstation.sln +++ b/duckstation.sln @@ -438,19 +438,15 @@ Global {4266505B-DBAF-484B-AB31-B53B9C8235B3}.ReleaseLTCG|x86.ActiveCfg = ReleaseLTCG|Win32 {4266505B-DBAF-484B-AB31-B53B9C8235B3}.ReleaseLTCG|x86.Build.0 = ReleaseLTCG|Win32 {9D206548-DE8F-4D9D-A561-C7E5CD7A20DF}.Debug|x64.ActiveCfg = Debug|x64 - {9D206548-DE8F-4D9D-A561-C7E5CD7A20DF}.Debug|x64.Build.0 = Debug|x64 {9D206548-DE8F-4D9D-A561-C7E5CD7A20DF}.Debug|x86.ActiveCfg = Debug|Win32 {9D206548-DE8F-4D9D-A561-C7E5CD7A20DF}.Debug|x86.Build.0 = Debug|Win32 {9D206548-DE8F-4D9D-A561-C7E5CD7A20DF}.DebugFast|x64.ActiveCfg = DebugFast|x64 - {9D206548-DE8F-4D9D-A561-C7E5CD7A20DF}.DebugFast|x64.Build.0 = DebugFast|x64 {9D206548-DE8F-4D9D-A561-C7E5CD7A20DF}.DebugFast|x86.ActiveCfg = DebugFast|Win32 {9D206548-DE8F-4D9D-A561-C7E5CD7A20DF}.DebugFast|x86.Build.0 = DebugFast|Win32 {9D206548-DE8F-4D9D-A561-C7E5CD7A20DF}.Release|x64.ActiveCfg = Release|x64 - {9D206548-DE8F-4D9D-A561-C7E5CD7A20DF}.Release|x64.Build.0 = Release|x64 {9D206548-DE8F-4D9D-A561-C7E5CD7A20DF}.Release|x86.ActiveCfg = Release|Win32 {9D206548-DE8F-4D9D-A561-C7E5CD7A20DF}.Release|x86.Build.0 = Release|Win32 {9D206548-DE8F-4D9D-A561-C7E5CD7A20DF}.ReleaseLTCG|x64.ActiveCfg = ReleaseLTCG|x64 - {9D206548-DE8F-4D9D-A561-C7E5CD7A20DF}.ReleaseLTCG|x64.Build.0 = ReleaseLTCG|x64 {9D206548-DE8F-4D9D-A561-C7E5CD7A20DF}.ReleaseLTCG|x86.ActiveCfg = ReleaseLTCG|Win32 {9D206548-DE8F-4D9D-A561-C7E5CD7A20DF}.ReleaseLTCG|x86.Build.0 = ReleaseLTCG|Win32 {7F909E29-4808-4BD9-A60C-56C51A3AAEC2}.Debug|x64.ActiveCfg = Debug|x64 diff --git a/src/common/window_info.h b/src/common/window_info.h index 95dadcb25..993e03782 100644 --- a/src/common/window_info.h +++ b/src/common/window_info.h @@ -11,7 +11,8 @@ struct WindowInfo X11, Wayland, MacOS, - Android + Android, + Libretro, }; enum class SurfaceFormat diff --git a/src/core/host_display.cpp b/src/core/host_display.cpp index 07dd1ce32..8fd31eaec 100644 --- a/src/core/host_display.cpp +++ b/src/core/host_display.cpp @@ -13,12 +13,6 @@ HostDisplayTexture::~HostDisplayTexture() = default; HostDisplay::~HostDisplay() = default; -void HostDisplay::WindowResized(s32 new_window_width, s32 new_window_height) -{ - m_window_width = new_window_width; - m_window_height = new_window_height; -} - void HostDisplay::SetSoftwareCursor(std::unique_ptr texture, float scale /*= 1.0f*/) { m_cursor_texture = std::move(texture); @@ -68,7 +62,7 @@ void HostDisplay::CalculateDrawRect(s32 window_width, s32 window_height, s32* ou float* out_y_scale) const { const float y_scale = - (static_cast(m_display_width) / static_cast(m_display_height)) / m_display_pixel_aspect_ratio; + (static_cast(m_display_width) / static_cast(m_display_height)) / m_display_aspect_ratio; const float display_width = static_cast(m_display_width); const float display_height = static_cast(m_display_height) * y_scale; const float active_left = static_cast(m_display_active_left); @@ -266,26 +260,28 @@ bool HostDisplay::WriteDisplayTextureToFile(const char* filename, bool full_reso s32 resize_height = 0; if (apply_aspect_ratio && full_resolution) { - if (m_display_pixel_aspect_ratio > 1.0f) + if (m_display_aspect_ratio > 1.0f) { resize_width = m_display_texture_view_width; - resize_height = static_cast(static_cast(resize_width) / m_display_pixel_aspect_ratio); + resize_height = static_cast(static_cast(resize_width) / m_display_aspect_ratio); } else { resize_height = std::abs(m_display_texture_view_height); - resize_width = static_cast(static_cast(resize_height) * m_display_pixel_aspect_ratio); + resize_width = static_cast(static_cast(resize_height) * m_display_aspect_ratio); } } else if (apply_aspect_ratio) { - const auto [left, top, right, bottom] = CalculateDrawRect(m_window_width, m_window_height, m_display_top_margin); + const auto [left, top, right, bottom] = + CalculateDrawRect(GetWindowWidth(), GetWindowHeight(), m_display_top_margin); resize_width = right - left; resize_height = bottom - top; } else if (!full_resolution) { - const auto [left, top, right, bottom] = CalculateDrawRect(m_window_width, m_window_height, m_display_top_margin); + const auto [left, top, right, bottom] = + CalculateDrawRect(GetWindowWidth(), GetWindowHeight(), m_display_top_margin); const float ratio = static_cast(m_display_texture_view_width) / static_cast(std::abs(m_display_texture_view_height)); if (ratio > 1.0f) diff --git a/src/core/host_display.h b/src/core/host_display.h index dd8141d33..40f9087d8 100644 --- a/src/core/host_display.h +++ b/src/core/host_display.h @@ -1,7 +1,9 @@ #pragma once #include "common/rectangle.h" +#include "common/window_info.h" #include "types.h" #include +#include #include #include @@ -31,8 +33,8 @@ public: virtual ~HostDisplay(); - ALWAYS_INLINE s32 GetWindowWidth() const { return m_window_width; } - ALWAYS_INLINE s32 GetWindowHeight() const { return m_window_height; } + ALWAYS_INLINE s32 GetWindowWidth() const { return static_cast(m_window_info.surface_width); } + ALWAYS_INLINE s32 GetWindowHeight() const { return static_cast(m_window_info.surface_height); } // Position is relative to the top-left corner of the window. ALWAYS_INLINE s32 GetMousePositionX() const { return m_mouse_position_x; } @@ -47,8 +49,19 @@ public: virtual void* GetRenderDevice() const = 0; virtual void* GetRenderContext() const = 0; + virtual bool HasRenderDevice() const = 0; + virtual bool HasRenderSurface() const = 0; + + virtual bool CreateRenderDevice(const WindowInfo& wi, std::string_view adapter_name, bool debug_device) = 0; + virtual bool InitializeRenderDevice(std::string_view shader_cache_directory, bool debug_device) = 0; + virtual bool MakeRenderContextCurrent() = 0; + virtual bool DoneRenderContextCurrent() = 0; + virtual void DestroyRenderDevice() = 0; + virtual void DestroyRenderSurface() = 0; + virtual bool ChangeRenderWindow(const WindowInfo& wi) = 0; + /// Call when the window size changes externally to recreate any resources. - virtual void WindowResized(s32 new_window_width, s32 new_window_height); + virtual void ResizeRenderWindow(s32 new_window_width, s32 new_window_height) = 0; /// Creates an abstracted RGBA8 texture. If dynamic, the texture can be updated with UpdateTexture() below. virtual std::unique_ptr CreateTexture(u32 width, u32 height, const void* data, u32 data_stride, @@ -59,7 +72,8 @@ public: virtual bool DownloadTexture(const void* texture_handle, u32 x, u32 y, u32 width, u32 height, void* out_data, u32 out_data_stride) = 0; - virtual void Render() = 0; + /// Returns false if the window was completely occluded. + virtual bool Render() = 0; virtual void SetVSync(bool enabled) = 0; @@ -91,7 +105,7 @@ public: } void SetDisplayParameters(s32 display_width, s32 display_height, s32 active_left, s32 active_top, s32 active_width, - s32 active_height, float pixel_aspect_ratio) + s32 active_height, float display_aspect_ratio) { m_display_width = display_width; m_display_height = display_height; @@ -99,7 +113,7 @@ public: m_display_active_top = active_top; m_display_active_width = active_width; m_display_active_height = active_height; - m_display_pixel_aspect_ratio = pixel_aspect_ratio; + m_display_aspect_ratio = display_aspect_ratio; m_display_changed = true; } @@ -147,8 +161,7 @@ protected: std::tuple CalculateSoftwareCursorDrawRect() const; - s32 m_window_width = 0; - s32 m_window_height = 0; + WindowInfo m_window_info; s32 m_mouse_position_x = 0; s32 m_mouse_position_y = 0; @@ -159,7 +172,7 @@ protected: s32 m_display_active_top = 0; s32 m_display_active_width = 0; s32 m_display_active_height = 0; - float m_display_pixel_aspect_ratio = 1.0f; + float m_display_aspect_ratio = 1.0f; void* m_display_texture_handle = nullptr; s32 m_display_texture_width = 0; diff --git a/src/core/host_interface.h b/src/core/host_interface.h index 21f5e00eb..ac328d0e2 100644 --- a/src/core/host_interface.h +++ b/src/core/host_interface.h @@ -39,7 +39,7 @@ public: virtual ~HostInterface(); /// Access to host display. - ALWAYS_INLINE HostDisplay* GetDisplay() const { return m_display; } + ALWAYS_INLINE HostDisplay* GetDisplay() const { return m_display.get(); } /// Access to host audio stream. ALWAYS_INLINE AudioStream* GetAudioStream() const { return m_audio_stream.get(); } @@ -96,6 +96,12 @@ public: /// Retrieves information about specified game from game list. virtual void GetGameInfo(const char* path, CDImage* image, std::string* code, std::string* title); + /// Returns the default path to a memory card. + virtual std::string GetSharedMemoryCardPath(u32 slot) const; + + /// Returns the default path to a memory card for a specific game. + virtual std::string GetGameMemoryCardPath(const char* game_code, u32 slot) const; + /// Enables the software cursor. Can be called multiple times, but must be matched by a call to DisableSoftwareCursor(). void EnableSoftwareCursor(); @@ -135,12 +141,6 @@ protected: /// Sets the user directory to the program directory, i.e. "portable mode". void SetUserDirectoryToProgramDirectory(); - /// Returns the default path to a memory card. - std::string GetSharedMemoryCardPath(u32 slot) const; - - /// Returns the default path to a memory card for a specific game. - std::string GetGameMemoryCardPath(const char* game_code, u32 slot) const; - /// Loads the BIOS image for the specified region. std::optional> GetBIOSImage(ConsoleRegion region); @@ -153,7 +153,7 @@ protected: bool SaveState(const char* filename); void CreateAudioStream(); - HostDisplay* m_display = nullptr; + std::unique_ptr m_display; std::unique_ptr m_audio_stream; std::unique_ptr m_system; Settings m_settings; diff --git a/src/core/settings.cpp b/src/core/settings.cpp index ace6d7092..f12f3aebe 100644 --- a/src/core/settings.cpp +++ b/src/core/settings.cpp @@ -463,7 +463,7 @@ const char* Settings::GetControllerTypeDisplayName(ControllerType type) static std::array s_memory_card_type_names = {{"None", "Shared", "PerGame", "PerGameTitle"}}; static std::array s_memory_card_type_display_names = {{"No Memory Card", "Shared Between All Games", "Separate Card Per Game (Game Code)", - "Seperate Card Per Game (Game Title)"}}; + "Separate Card Per Game (Game Title)"}}; std::optional Settings::ParseMemoryCardTypeName(const char* str) { diff --git a/src/core/system.h b/src/core/system.h index 45dba7c5c..e9865f9eb 100644 --- a/src/core/system.h +++ b/src/core/system.h @@ -45,6 +45,12 @@ struct SystemBootParameters class System { public: + enum : u32 + { + // 5 megabytes is sufficient for now, at the moment they're around 4.2MB. + MAX_SAVE_STATE_SIZE = 5 * 1024 * 1024 + }; + friend TimingEvent; ~System(); diff --git a/src/duckstation-qt/CMakeLists.txt b/src/duckstation-qt/CMakeLists.txt index 03f30745a..81d32ba66 100644 --- a/src/duckstation-qt/CMakeLists.txt +++ b/src/duckstation-qt/CMakeLists.txt @@ -41,10 +41,6 @@ add_executable(duckstation-qt mainwindow.ui memorycardsettingswidget.cpp memorycardsettingswidget.h - openglhostdisplay.cpp - openglhostdisplay.h - qthostdisplay.cpp - qthostdisplay.h qtdisplaywidget.cpp qtdisplaywidget.h qthostinterface.cpp @@ -58,19 +54,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 vulkan-loader Qt5::Core Qt5::Gui Qt5::Widgets Qt5::Network) if(WIN32) - target_sources(duckstation-qt PRIVATE - d3d11hostdisplay.cpp - d3d11hostdisplay.h - ) - # We want a Windows subsystem application not console. set_target_properties(duckstation-qt PROPERTIES WIN32_EXECUTABLE TRUE diff --git a/src/duckstation-qt/d3d11hostdisplay.cpp b/src/duckstation-qt/d3d11hostdisplay.cpp deleted file mode 100644 index fc7c324fa..000000000 --- a/src/duckstation-qt/d3d11hostdisplay.cpp +++ /dev/null @@ -1,174 +0,0 @@ -#include "d3d11hostdisplay.h" -#include "common/assert.h" -#include "common/log.h" -#include "imgui.h" -#include "qtdisplaywidget.h" -Log_SetChannel(D3D11HostDisplay); - -D3D11HostDisplay::D3D11HostDisplay(QtHostInterface* host_interface) : QtHostDisplay(host_interface) {} - -D3D11HostDisplay::~D3D11HostDisplay() = default; - -HostDisplay::RenderAPI D3D11HostDisplay::GetRenderAPI() const -{ - return m_interface.GetRenderAPI(); -} - -void* D3D11HostDisplay::GetRenderDevice() const -{ - return m_interface.GetRenderDevice(); -} - -void* D3D11HostDisplay::GetRenderContext() const -{ - return m_interface.GetRenderContext(); -} - -std::unique_ptr D3D11HostDisplay::CreateTexture(u32 width, u32 height, const void* initial_data, - u32 initial_data_stride, bool dynamic) -{ - return m_interface.CreateTexture(width, height, initial_data, initial_data_stride, dynamic); -} - -void D3D11HostDisplay::UpdateTexture(HostDisplayTexture* texture, u32 x, u32 y, u32 width, u32 height, - const void* texture_data, u32 texture_data_stride) -{ - m_interface.UpdateTexture(texture, x, y, width, height, texture_data, texture_data_stride); -} - -bool D3D11HostDisplay::DownloadTexture(const void* texture_handle, u32 x, u32 y, u32 width, u32 height, void* out_data, - u32 out_data_stride) -{ - return m_interface.DownloadTexture(texture_handle, x, y, width, height, out_data, out_data_stride); -} - -void D3D11HostDisplay::SetVSync(bool enabled) -{ - m_interface.SetVSync(enabled); -} - -bool D3D11HostDisplay::shouldUseFlipModelSwapChain() const -{ - // For some reason DXGI gets stuck waiting for some kernel object when the Qt window has a parent (render-to-main) on - // some computers, unless the window is completely occluded. The legacy swap chain mode does not have this problem. - return m_widget->parent() == nullptr; -} - -bool D3D11HostDisplay::hasDeviceContext() const -{ - return m_interface.HasContext(); -} - -bool D3D11HostDisplay::createDeviceContext(const QString& adapter_name, bool debug_device) -{ - std::optional wi = getWindowInfo(); - if (!wi || !m_interface.CreateContextAndSwapChain(wi.value(), adapter_name.toStdString(), - shouldUseFlipModelSwapChain(), debug_device)) - { - return false; - } - - m_window_width = static_cast(m_interface.GetSwapChainWidth()); - m_window_height = static_cast(m_interface.GetSwapChainHeight()); - return true; -} - -bool D3D11HostDisplay::initializeDeviceContext(std::string_view shader_cache_directory, bool debug_device) -{ - return QtHostDisplay::initializeDeviceContext(shader_cache_directory, debug_device); -} - -bool D3D11HostDisplay::activateDeviceContext() -{ - return true; -} - -void D3D11HostDisplay::deactivateDeviceContext() {} - -void D3D11HostDisplay::destroyDeviceContext() -{ - QtHostDisplay::destroyDeviceContext(); - m_interface.DestroySwapChain(); - m_interface.DestroyContext(); -} - -bool D3D11HostDisplay::recreateSurface() -{ - std::optional wi = getWindowInfo(); - if (!wi.has_value()) - return false; - - if (!m_interface.RecreateSwapChain(wi.value(), shouldUseFlipModelSwapChain())) - return false; - - m_window_width = static_cast(m_interface.GetSwapChainWidth()); - m_window_height = static_cast(m_interface.GetSwapChainHeight()); - return true; -} - -void D3D11HostDisplay::destroySurface() -{ - m_interface.DestroySwapChain(); -} - -void D3D11HostDisplay::WindowResized(s32 new_window_width, s32 new_window_height) -{ - QtHostDisplay::WindowResized(new_window_width, new_window_height); - - m_interface.ResizeSwapChain(static_cast(new_window_width), static_cast(new_window_height)); - m_window_width = static_cast(m_interface.GetSwapChainWidth()); - m_window_height = static_cast(m_interface.GetSwapChainHeight()); -} - -bool D3D11HostDisplay::createDeviceResources() -{ - if (!QtHostDisplay::createDeviceResources()) - return false; - - return m_interface.CreateResources(); -} - -void D3D11HostDisplay::destroyDeviceResources() -{ - QtHostDisplay::destroyDeviceResources(); - m_interface.DestroyResources(); -} - -bool D3D11HostDisplay::createImGuiContext() -{ - if (!QtHostDisplay::createImGuiContext() || !m_interface.CreateImGuiContext()) - return false; - - ImGui::NewFrame(); - return true; -} - -void D3D11HostDisplay::destroyImGuiContext() -{ - m_interface.DestroyImGuiContext(); - QtHostDisplay::destroyImGuiContext(); -} - -void D3D11HostDisplay::Render() -{ - if (!m_interface.HasSwapChain() || !m_interface.BeginRender()) - return; - - if (HasDisplayTexture()) - { - const auto [left, top, width, height] = CalculateDrawRect(m_window_width, m_window_height, m_display_top_margin); - m_interface.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_interface.RenderImGui(); - - if (HasSoftwareCursor()) - { - const auto [left, top, width, height] = CalculateSoftwareCursorDrawRect(); - m_interface.RenderSoftwareCursor(left, top, width, height, m_cursor_texture.get()); - } - - m_interface.EndRenderAndPresent(); -} diff --git a/src/duckstation-qt/d3d11hostdisplay.h b/src/duckstation-qt/d3d11hostdisplay.h deleted file mode 100644 index 22abf6e7f..000000000 --- a/src/duckstation-qt/d3d11hostdisplay.h +++ /dev/null @@ -1,51 +0,0 @@ -#pragma once -#include "common/window_info.h" -#include "core/host_display.h" -#include "frontend-common/d3d11_host_display.h" -#include "qtdisplaywidget.h" -#include "qthostdisplay.h" -#include - -class QtHostInterface; - -class D3D11HostDisplay final : public QtHostDisplay -{ -public: - D3D11HostDisplay(QtHostInterface* host_interface); - ~D3D11HostDisplay(); - - bool hasDeviceContext() const override; - bool createDeviceContext(const QString& adapter_name, 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 shouldUseFlipModelSwapChain() const; - - bool createImGuiContext() override; - void destroyImGuiContext() override; - bool createDeviceResources() override; - void destroyDeviceResources() override; - - FrontendCommon::D3D11HostDisplay m_interface; -}; diff --git a/src/duckstation-qt/duckstation-qt.vcxproj b/src/duckstation-qt/duckstation-qt.vcxproj index bbbb62936..3ca75c7b2 100644 --- a/src/duckstation-qt/duckstation-qt.vcxproj +++ b/src/duckstation-qt/duckstation-qt.vcxproj @@ -39,7 +39,6 @@ - @@ -50,19 +49,15 @@ - - - - @@ -73,9 +68,7 @@ - - @@ -83,25 +76,18 @@ - - - {43540154-9e1e-409c-834f-b84be5621388} - {bb08260f-6fbc-46af-8924-090ee71360c6} {8bda439c-6358-45fb-9994-2ff083babe06} - - {9c8ddeb0-2b8f-4f5f-ba86-127cdf27f035} - {ee054e08-3799-4a59-a422-18259c105ffd} diff --git a/src/duckstation-qt/duckstation-qt.vcxproj.filters b/src/duckstation-qt/duckstation-qt.vcxproj.filters index 1b30ee96e..ecfa80a20 100644 --- a/src/duckstation-qt/duckstation-qt.vcxproj.filters +++ b/src/duckstation-qt/duckstation-qt.vcxproj.filters @@ -30,9 +30,6 @@ - - - @@ -43,18 +40,13 @@ - - + - - - - @@ -62,7 +54,7 @@ - + diff --git a/src/duckstation-qt/mainwindow.cpp b/src/duckstation-qt/mainwindow.cpp index db81cd70e..4bd9de1b8 100644 --- a/src/duckstation-qt/mainwindow.cpp +++ b/src/duckstation-qt/mainwindow.cpp @@ -2,13 +2,13 @@ #include "aboutdialog.h" #include "common/assert.h" #include "core/game_list.h" +#include "core/host_display.h" #include "core/settings.h" #include "core/system.h" #include "gamelistsettingswidget.h" #include "gamelistwidget.h" #include "gamepropertiesdialog.h" #include "qtdisplaywidget.h" -#include "qthostdisplay.h" #include "qthostinterface.h" #include "qtsettingsinterface.h" #include "qtutils.h" @@ -22,8 +22,8 @@ #include #include #include -#include #include +#include #include static constexpr char DISC_IMAGE_FILTER[] = @@ -72,14 +72,13 @@ bool MainWindow::confirmMessage(const QString& message) return (result == QMessageBox::Yes); } -void MainWindow::createDisplay(QThread* worker_thread, const QString& adapter_name, bool use_debug_device, - bool fullscreen, bool render_to_main) +QtDisplayWidget* MainWindow::createDisplay(QThread* worker_thread, const QString& adapter_name, bool use_debug_device, + bool fullscreen, bool render_to_main) { Assert(!m_host_display && !m_display_widget); Assert(!fullscreen || !render_to_main); - m_host_display = m_host_interface->createHostDisplay(); - m_display_widget = m_host_display->createWidget((!fullscreen && render_to_main) ? m_ui.mainContainer : nullptr); + m_display_widget = new QtDisplayWidget((!fullscreen && render_to_main) ? m_ui.mainContainer : nullptr); m_display_widget->setWindowTitle(windowTitle()); m_display_widget->setWindowIcon(windowIcon()); @@ -101,32 +100,37 @@ void MainWindow::createDisplay(QThread* worker_thread, const QString& adapter_na // we need the surface visible.. this might be able to be replaced with something else QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents); - if (!m_host_display->createDeviceContext(adapter_name, use_debug_device)) + std::optional wi = m_display_widget->getWindowInfo(); + if (!wi.has_value()) + { + reportError(tr("Failed to get window info from widget")); + destroyDisplayWidget(); + return nullptr; + } + + m_host_display = m_host_interface->createHostDisplay(); + if (!m_host_display || !m_host_display->CreateRenderDevice(wi.value(), adapter_name.toStdString(), use_debug_device)) { reportError(tr("Failed to create host display device context.")); - return; + destroyDisplayWidget(); + return nullptr; } - m_host_display->deactivateDeviceContext(); + m_host_display->DoneRenderContextCurrent(); + return m_display_widget; } -void MainWindow::updateDisplay(QThread* worker_thread, bool fullscreen, bool render_to_main) +QtDisplayWidget* MainWindow::updateDisplay(QThread* worker_thread, bool fullscreen, bool render_to_main) { const bool is_fullscreen = m_display_widget->isFullScreen(); const bool is_rendering_to_main = (!is_fullscreen && m_display_widget->parent()); if (fullscreen == is_fullscreen && is_rendering_to_main == render_to_main) - return; - - m_host_display->destroySurface(); + return m_display_widget; - if (is_rendering_to_main) - { - switchToGameListView(); - m_ui.mainContainer->removeWidget(m_display_widget); - } + m_host_display->DestroyRenderSurface(); - m_host_display->destroyWidget(); - m_display_widget = m_host_display->createWidget((!fullscreen && render_to_main) ? m_ui.mainContainer : nullptr); + destroyDisplayWidget(); + m_display_widget = new QtDisplayWidget((!fullscreen && render_to_main) ? m_ui.mainContainer : nullptr); m_display_widget->setWindowTitle(windowTitle()); m_display_widget->setWindowIcon(windowIcon()); @@ -148,30 +152,44 @@ 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->recreateSurface()) + std::optional wi = m_display_widget->getWindowInfo(); + if (!wi.has_value()) + { + reportError(tr("Failed to get new window info from widget")); + destroyDisplayWidget(); + return nullptr; + } + + if (!m_host_display->ChangeRenderWindow(wi.value())) Panic("Failed to recreate surface on new widget."); m_display_widget->setFocus(); QSignalBlocker blocker(m_ui.actionFullscreen); m_ui.actionFullscreen->setChecked(fullscreen); + return m_display_widget; } void MainWindow::destroyDisplay() { DebugAssert(m_host_display && m_display_widget); + m_host_display = nullptr; + destroyDisplayWidget(); +} + +void MainWindow::destroyDisplayWidget() +{ + if (!m_display_widget) + return; if (m_display_widget->parent()) { - m_ui.mainContainer->removeWidget(m_display_widget); switchToGameListView(); + m_ui.mainContainer->removeWidget(m_display_widget); } - m_host_display->destroyWidget(); + delete m_display_widget; m_display_widget = nullptr; - - delete m_host_display; - m_host_display = nullptr; } void MainWindow::focusDisplayWidget() diff --git a/src/duckstation-qt/mainwindow.h b/src/duckstation-qt/mainwindow.h index 3d6440c61..05c04f96b 100644 --- a/src/duckstation-qt/mainwindow.h +++ b/src/duckstation-qt/mainwindow.h @@ -12,9 +12,9 @@ class QThread; class GameListWidget; class QtHostInterface; -class QtHostDisplay; class QtDisplayWidget; +class HostDisplay; struct GameListEntry; class MainWindow final : public QMainWindow @@ -29,9 +29,9 @@ private Q_SLOTS: void reportError(const QString& message); void reportMessage(const QString& message); bool confirmMessage(const QString& message); - void createDisplay(QThread* worker_thread, const QString& adapter_name, bool use_debug_device, bool fullscreen, - bool render_to_main); - void updateDisplay(QThread* worker_thread, bool fullscreen, bool render_to_main); + QtDisplayWidget* createDisplay(QThread* worker_thread, const QString& adapter_name, bool use_debug_device, + bool fullscreen, bool render_to_main); + QtDisplayWidget* updateDisplay(QThread* worker_thread, bool fullscreen, bool render_to_main); void destroyDisplay(); void focusDisplayWidget(); @@ -72,6 +72,7 @@ private: void updateEmulationActions(bool starting, bool running); void switchToGameListView(); void switchToEmulationView(); + void destroyDisplayWidget(); SettingsDialog* getSettingsDialog(); void doSettings(SettingsDialog::Category category = SettingsDialog::Category::Count); void updateDebugMenuCPUExecutionMode(); @@ -83,7 +84,7 @@ private: GameListWidget* m_game_list_widget = nullptr; - QtHostDisplay* m_host_display = nullptr; + HostDisplay* m_host_display = nullptr; QtDisplayWidget* m_display_widget = nullptr; QLabel* m_status_speed_widget = nullptr; diff --git a/src/duckstation-qt/openglhostdisplay.h b/src/duckstation-qt/openglhostdisplay.h deleted file mode 100644 index 120701bb3..000000000 --- a/src/duckstation-qt/openglhostdisplay.h +++ /dev/null @@ -1,70 +0,0 @@ -#pragma once - -// GLAD has to come first so that Qt doesn't pull in the system GL headers, which are incompatible with glad. -#include - -// Hack to prevent Apple's glext.h headers from getting included via qopengl.h, since we still want to use glad. -#ifdef __APPLE__ -#define __glext_h_ -#endif - -#include "common/gl/context.h" -#include "common/gl/program.h" -#include "common/gl/texture.h" -#include "common/window_info.h" -#include "core/host_display.h" -#include "qtdisplaywidget.h" -#include "qthostdisplay.h" -#include - -class QtHostInterface; - -class OpenGLHostDisplay final : public QtHostDisplay -{ -public: - OpenGLHostDisplay(QtHostInterface* host_interface); - ~OpenGLHostDisplay(); - - bool hasDeviceContext() const override; - bool createDeviceContext(const QString& adapter_name, 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: - const char* GetGLSLVersionString() const; - std::string GetGLSLVersionHeader() const; - - bool createImGuiContext() override; - void destroyImGuiContext() override; - bool createDeviceResources() override; - void destroyDeviceResources() override; - - void renderDisplay(); - - std::unique_ptr m_gl_context = nullptr; - - GL::Program m_display_program; - GLuint m_display_vao = 0; - GLuint m_display_nearest_sampler = 0; - GLuint m_display_linear_sampler = 0; -}; diff --git a/src/duckstation-qt/qtdisplaywidget.cpp b/src/duckstation-qt/qtdisplaywidget.cpp index e41387153..82a6eb0f3 100644 --- a/src/duckstation-qt/qtdisplaywidget.cpp +++ b/src/duckstation-qt/qtdisplaywidget.cpp @@ -1,8 +1,8 @@ #include "qtdisplaywidget.h" #include "common/bitutils.h" -#include "qthostdisplay.h" #include "qthostinterface.h" #include "qtutils.h" +#include #include #include #include @@ -10,6 +10,10 @@ #include #include +#if !defined(WIN32) && !defined(APPLE) +#include +#endif + QtDisplayWidget::QtDisplayWidget(QWidget* parent) : QWidget(parent) { // We want a native window for both D3D and OpenGL. @@ -47,6 +51,46 @@ int QtDisplayWidget::scaledWindowHeight() const return static_cast(std::ceil(static_cast(height()) * devicePixelRatioFromScreen())); } +std::optional QtDisplayWidget::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(winId()); +#elif defined(__APPLE__) + wi.type = WindowInfo::Type::MacOS; + wi.window_handle = reinterpret_cast(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", windowHandle()); + wi.window_handle = reinterpret_cast(winId()); + } + else if (platform_name == QStringLiteral("wayland")) + { + wi.type = WindowInfo::Type::Wayland; + wi.display_connection = pni->nativeResourceForWindow("display", windowHandle()); + wi.window_handle = pni->nativeResourceForWindow("surface", windowHandle()); + } + else + { + qCritical() << "Unknown PNI platform " << platform_name; + return std::nullopt; + } +#endif + + wi.surface_width = scaledWindowWidth(); + wi.surface_height = scaledWindowHeight(); + wi.surface_format = WindowInfo::SurfaceFormat::RGB8; + + return wi; +} + QPaintEngine* QtDisplayWidget::paintEngine() const { return nullptr; diff --git a/src/duckstation-qt/qtdisplaywidget.h b/src/duckstation-qt/qtdisplaywidget.h index 9be4289a7..7d15c8cc5 100644 --- a/src/duckstation-qt/qtdisplaywidget.h +++ b/src/duckstation-qt/qtdisplaywidget.h @@ -1,6 +1,8 @@ #pragma once #include "common/types.h" +#include "common/window_info.h" #include +#include class QtDisplayWidget final : public QWidget { @@ -16,6 +18,8 @@ public: int scaledWindowHeight() const; qreal devicePixelRatioFromScreen() const; + std::optional getWindowInfo() const; + Q_SIGNALS: void windowResizedEvent(int width, int height); void windowRestoredEvent(); diff --git a/src/duckstation-qt/qthostdisplay.cpp b/src/duckstation-qt/qthostdisplay.cpp deleted file mode 100644 index 056aa48ef..000000000 --- a/src/duckstation-qt/qthostdisplay.cpp +++ /dev/null @@ -1,165 +0,0 @@ -#include "qthostdisplay.h" -#include "common/assert.h" -#include "frontend-common/imgui_styles.h" -#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) {} - -QtHostDisplay::~QtHostDisplay() = default; - -QtDisplayWidget* QtHostDisplay::createWidget(QWidget* parent) -{ - Assert(!m_widget); - m_widget = new QtDisplayWidget(parent); - - // We want a native window for both D3D and OpenGL. - m_widget->setAutoFillBackground(false); - m_widget->setAttribute(Qt::WA_NativeWindow, true); - m_widget->setAttribute(Qt::WA_NoSystemBackground, true); - m_widget->setAttribute(Qt::WA_PaintOnScreen, true); - - return m_widget; -} - -void QtHostDisplay::destroyWidget() -{ - Assert(m_widget); - - delete m_widget; - m_widget = nullptr; -} - -bool QtHostDisplay::hasDeviceContext() const -{ - return false; -} - -bool QtHostDisplay::createDeviceContext(const QString& adapter_name, bool debug_device) -{ - return false; -} - -bool QtHostDisplay::initializeDeviceContext(std::string_view shader_cache_directory, bool debug_device) -{ - if (!createImGuiContext() || !createDeviceResources()) - return false; - - return true; -} - -bool QtHostDisplay::activateDeviceContext() -{ - return true; -} - -void QtHostDisplay::deactivateDeviceContext() {} - -void QtHostDisplay::destroyDeviceContext() -{ - destroyImGuiContext(); - destroyDeviceResources(); -} - -bool QtHostDisplay::recreateSurface() -{ - return false; -} - -void QtHostDisplay::destroySurface() {} - -bool QtHostDisplay::createImGuiContext() -{ - ImGui::CreateContext(); - - auto& io = ImGui::GetIO(); - io.IniFilename = nullptr; - io.DisplaySize.x = static_cast(m_window_width); - io.DisplaySize.y = static_cast(m_window_height); - - const float framebuffer_scale = static_cast(m_widget->devicePixelRatioFromScreen()); - io.DisplayFramebufferScale.x = framebuffer_scale; - io.DisplayFramebufferScale.y = framebuffer_scale; - ImGui::GetStyle().ScaleAllSizes(framebuffer_scale); - - ImGui::StyleColorsDarker(); - ImGui::AddRobotoRegularFont(15.0f * framebuffer_scale); - - return true; -} - -void QtHostDisplay::destroyImGuiContext() -{ - ImGui::DestroyContext(); -} - -bool QtHostDisplay::createDeviceResources() -{ - return true; -} - -void QtHostDisplay::destroyDeviceResources() {} - -void QtHostDisplay::WindowResized(s32 new_window_width, s32 new_window_height) -{ - HostDisplay::WindowResized(new_window_width, new_window_height); - updateImGuiDisplaySize(); -} - -void QtHostDisplay::updateImGuiDisplaySize() -{ - // imgui may not have been initialized yet - if (!ImGui::GetCurrentContext()) - return; - - auto& io = ImGui::GetIO(); - 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 deleted file mode 100644 index 7ea7b29cf..000000000 --- a/src/duckstation-qt/qthostdisplay.h +++ /dev/null @@ -1,50 +0,0 @@ -#pragma once -#include "common/types.h" -#include "common/window_info.h" -#include "core/host_display.h" -#include -#include - -class QString; -class QThread; -class QWidget; - -class QtHostInterface; -class QtDisplayWidget; - -class QtHostDisplay : public HostDisplay -{ -public: - QtHostDisplay(QtHostInterface* host_interface); - virtual ~QtHostDisplay(); - - ALWAYS_INLINE bool hasWidget() const { return (m_widget != nullptr); } - ALWAYS_INLINE QtDisplayWidget* getWidget() const { return m_widget; } - - virtual QtDisplayWidget* createWidget(QWidget* parent); - virtual void destroyWidget(); - - virtual bool hasDeviceContext() const; - virtual bool createDeviceContext(const QString& adapter_name, 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 recreateSurface(); - virtual void destroySurface(); - - virtual void WindowResized(s32 new_window_width, s32 new_window_height) override; - - void updateImGuiDisplaySize(); - -protected: - virtual bool createImGuiContext(); - virtual void destroyImGuiContext(); - 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 0afaa5269..d4944a5b2 100644 --- a/src/duckstation-qt/qthostinterface.cpp +++ b/src/duckstation-qt/qthostinterface.cpp @@ -8,14 +8,17 @@ #include "core/game_list.h" #include "core/gpu.h" #include "core/system.h" +#include "frontend-common/imgui_styles.h" +#include "frontend-common/opengl_host_display.h" #include "frontend-common/sdl_audio_stream.h" #include "frontend-common/sdl_controller_interface.h" +#include "frontend-common/vulkan_host_display.h" +#include "imgui.h" #include "mainwindow.h" -#include "openglhostdisplay.h" +#include "qtdisplaywidget.h" #include "qtprogresscallback.h" #include "qtsettingsinterface.h" #include "qtutils.h" -#include "vulkanhostdisplay.h" #include #include #include @@ -27,7 +30,7 @@ Log_SetChannel(QtHostInterface); #ifdef WIN32 -#include "d3d11hostdisplay.h" +#include "frontend-common/d3d11_host_display.h" #endif QtHostInterface::QtHostInterface(QObject* parent) : QObject(parent), CommonHostInterface() @@ -37,7 +40,7 @@ QtHostInterface::QtHostInterface(QObject* parent) : QObject(parent), CommonHostI QtHostInterface::~QtHostInterface() { - Assert(!getHostDisplay()); + Assert(!m_display); } const char* QtHostInterface::GetFrontendName() const @@ -177,7 +180,7 @@ void QtHostInterface::applySettings() if (m_system) { const bool render_to_main = getSettingValue("Main/RenderToMainWindow", true).toBool(); - if (getHostDisplay() && !m_is_fullscreen && render_to_main != m_is_rendering_to_main) + if (m_display && !m_is_fullscreen && render_to_main != m_is_rendering_to_main) { m_is_rendering_to_main = render_to_main; updateDisplayState(); @@ -271,10 +274,10 @@ void QtHostInterface::onDisplayWindowMouseButtonEvent(int button, bool pressed) void QtHostInterface::onHostDisplayWindowResized(int width, int height) { // this can be null if it was destroyed and the main thread is late catching up - if (!getHostDisplay()) + if (!m_display) return; - getHostDisplay()->WindowResized(width, height); + m_display->ResizeRenderWindow(width, height); // re-render the display, since otherwise it will be out of date and stretched if paused if (m_system) @@ -289,7 +292,7 @@ void QtHostInterface::redrawDisplayWindow() return; } - if (!getHostDisplay() || !m_system) + if (!m_display || !m_system) return; renderDisplay(); @@ -306,71 +309,69 @@ void QtHostInterface::toggleFullscreen() SetFullscreen(!m_is_fullscreen); } -QtHostDisplay* QtHostInterface::getHostDisplay() -{ - return static_cast(m_display); -} - bool QtHostInterface::AcquireHostDisplay() { Assert(!m_display); m_is_rendering_to_main = getSettingValue("Main/RenderToMainWindow", true).toBool(); - emit createDisplayRequested(m_worker_thread, QString::fromStdString(m_settings.gpu_adapter), - m_settings.gpu_use_debug_device, m_is_fullscreen, m_is_rendering_to_main); - Assert(m_display); - if (!getHostDisplay()->hasDeviceContext()) + QtDisplayWidget* display_widget = + createDisplayRequested(m_worker_thread, QString::fromStdString(m_settings.gpu_adapter), + m_settings.gpu_use_debug_device, m_is_fullscreen, m_is_rendering_to_main); + if (!display_widget || !m_display->HasRenderDevice()) { emit destroyDisplayRequested(); m_display = nullptr; return false; } - if (!getHostDisplay()->activateDeviceContext() || - !getHostDisplay()->initializeDeviceContext(GetShaderCacheDirectory(), m_settings.gpu_use_debug_device)) + createImGuiContext(display_widget->devicePixelRatioFromScreen()); + + if (!m_display->MakeRenderContextCurrent() || + !m_display->InitializeRenderDevice(GetShaderCacheDirectory(), m_settings.gpu_use_debug_device)) { - getHostDisplay()->destroyDeviceContext(); + destroyImGuiContext(); + m_display->DestroyRenderDevice(); emit destroyDisplayRequested(); - m_display = nullptr; + m_display.reset(); return false; } - connectDisplaySignals(); + connectDisplaySignals(display_widget); + ImGui::NewFrame(); return true; } -QtHostDisplay* QtHostInterface::createHostDisplay() +HostDisplay* QtHostInterface::createHostDisplay() { - Assert(!getHostDisplay()); - switch (m_settings.gpu_renderer) { case GPURenderer::HardwareVulkan: - m_display = new VulkanHostDisplay(this); + m_display = std::make_unique(); break; case GPURenderer::HardwareOpenGL: #ifndef WIN32 default: #endif - m_display = new OpenGLHostDisplay(this); + m_display = std::make_unique(); break; #ifdef WIN32 case GPURenderer::HardwareD3D11: default: - m_display = new D3D11HostDisplay(this); + m_display = std::make_unique(); break; #endif } - return getHostDisplay(); + return m_display.get(); } -void QtHostInterface::connectDisplaySignals() +void QtHostInterface::connectDisplaySignals(QtDisplayWidget* widget) { - QtDisplayWidget* widget = getHostDisplay()->getWidget(); + widget->disconnect(this); + connect(widget, &QtDisplayWidget::windowResizedEvent, this, &QtHostInterface::onHostDisplayWindowResized); connect(widget, &QtDisplayWidget::windowRestoredEvent, this, &QtHostInterface::redrawDisplayWindow); connect(widget, &QtDisplayWidget::windowClosedEvent, this, &QtHostInterface::powerOffSystem, @@ -380,21 +381,16 @@ void QtHostInterface::connectDisplaySignals() connect(widget, &QtDisplayWidget::windowMouseButtonEvent, this, &QtHostInterface::onDisplayWindowMouseButtonEvent); } -void QtHostInterface::disconnectDisplaySignals() -{ - getHostDisplay()->getWidget()->disconnect(this); -} - void QtHostInterface::updateDisplayState() { // this expects the context to get moved back to us afterwards - getHostDisplay()->deactivateDeviceContext(); - emit updateDisplayRequested(m_worker_thread, m_is_fullscreen, m_is_rendering_to_main); - if (!getHostDisplay()->activateDeviceContext()) + m_display->DoneRenderContextCurrent(); + + QtDisplayWidget* display_widget = updateDisplayRequested(m_worker_thread, m_is_fullscreen, m_is_rendering_to_main); + if (!display_widget || !m_display->MakeRenderContextCurrent()) Panic("Failed to make device context current after updating"); - getHostDisplay()->updateImGuiDisplaySize(); - connectDisplaySignals(); + connectDisplaySignals(display_widget); redrawDisplayWindow(); UpdateSpeedLimiterState(); } @@ -403,9 +399,10 @@ void QtHostInterface::ReleaseHostDisplay() { Assert(m_display); - getHostDisplay()->destroyDeviceContext(); + m_display->DestroyRenderDevice(); + destroyImGuiContext(); emit destroyDisplayRequested(); - m_display = nullptr; + m_display.reset(); m_is_fullscreen = false; } @@ -938,6 +935,7 @@ void QtHostInterface::renderDisplay() DrawImGuiWindows(); m_display->Render(); + ImGui::NewFrame(); m_system->GetGPU()->RestoreGraphicsAPIState(); } @@ -950,6 +948,25 @@ void QtHostInterface::wakeThread() QMetaObject::invokeMethod(m_worker_thread_event_loop, "quit", Qt::QueuedConnection); } +void QtHostInterface::createImGuiContext(float framebuffer_scale) +{ + ImGui::CreateContext(); + + auto& io = ImGui::GetIO(); + io.IniFilename = nullptr; + io.DisplayFramebufferScale.x = framebuffer_scale; + io.DisplayFramebufferScale.y = framebuffer_scale; + ImGui::GetStyle().ScaleAllSizes(framebuffer_scale); + + ImGui::StyleColorsDarker(); + ImGui::AddRobotoRegularFont(15.0f * framebuffer_scale); +} + +void QtHostInterface::destroyImGuiContext() +{ + ImGui::DestroyContext(); +} + QtHostInterface::Thread::Thread(QtHostInterface* parent) : QThread(parent), m_parent(parent) {} QtHostInterface::Thread::~Thread() = default; diff --git a/src/duckstation-qt/qthostinterface.h b/src/duckstation-qt/qthostinterface.h index 783f2fe7d..187e057e6 100644 --- a/src/duckstation-qt/qthostinterface.h +++ b/src/duckstation-qt/qthostinterface.h @@ -5,6 +5,7 @@ #include "frontend-common/common_host_interface.h" #include #include +#include #include #include #include @@ -25,8 +26,7 @@ class QTimer; class GameList; class MainWindow; - -class QtHostDisplay; +class QtDisplayWidget; Q_DECLARE_METATYPE(SystemBootParameters); @@ -66,7 +66,7 @@ public: ALWAYS_INLINE MainWindow* getMainWindow() const { return m_main_window; } void setMainWindow(MainWindow* window); - QtHostDisplay* createHostDisplay(); + HostDisplay* createHostDisplay(); void populateSaveStateMenus(const char* game_code, QMenu* load_menu, QMenu* save_menu); @@ -96,9 +96,9 @@ Q_SIGNALS: void emulationPaused(bool paused); void stateSaved(const QString& game_code, bool global, qint32 slot); void gameListRefreshed(); - void createDisplayRequested(QThread* worker_thread, const QString& adapter_name, bool use_debug_device, - bool fullscreen, bool render_to_main); - void updateDisplayRequested(QThread* worker_thread, bool fullscreen, bool render_to_main); + QtDisplayWidget* createDisplayRequested(QThread* worker_thread, const QString& adapter_name, bool use_debug_device, + bool fullscreen, bool render_to_main); + QtDisplayWidget* updateDisplayRequested(QThread* worker_thread, bool fullscreen, bool render_to_main); void focusDisplayWidgetRequested(); void destroyDisplayRequested(); void systemPerformanceCountersUpdated(float speed, float fps, float vps, float avg_frame_time, @@ -186,21 +186,21 @@ private: Common::Event m_init_event; }; - QtHostDisplay* getHostDisplay(); - void createBackgroundControllerPollTimer(); void destroyBackgroundControllerPollTimer(); void startBackgroundControllerPollTimer(); void stopBackgroundControllerPollTimer(); + void createImGuiContext(float framebuffer_scale); + void destroyImGuiContext(); + void createThread(); void stopThread(); void threadEntryPoint(); bool initializeOnThread(); void shutdownOnThread(); void renderDisplay(); - void connectDisplaySignals(); - void disconnectDisplaySignals(); + void connectDisplaySignals(QtDisplayWidget* widget); void updateDisplayState(); void wakeThread(); diff --git a/src/duckstation-qt/vulkanhostdisplay.cpp b/src/duckstation-qt/vulkanhostdisplay.cpp deleted file mode 100644 index 9cb6a5453..000000000 --- a/src/duckstation-qt/vulkanhostdisplay.cpp +++ /dev/null @@ -1,168 +0,0 @@ -#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(const QString& adapter_name, bool debug_device) -{ - std::optional wi = getWindowInfo(); - if (!wi || !m_vulkan_display.CreateContextAndSwapChain(wi.value(), adapter_name.toStdString(), 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 deleted file mode 100644 index aee7cf99d..000000000 --- a/src/duckstation-qt/vulkanhostdisplay.h +++ /dev/null @@ -1,49 +0,0 @@ -#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(const QString& adapter_name, 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; -}; diff --git a/src/duckstation-sdl/CMakeLists.txt b/src/duckstation-sdl/CMakeLists.txt index 7bc9b2f3d..d96c6da08 100644 --- a/src/duckstation-sdl/CMakeLists.txt +++ b/src/duckstation-sdl/CMakeLists.txt @@ -2,15 +2,11 @@ add_executable(duckstation-sdl imgui_impl_sdl.cpp imgui_impl_sdl.h main.cpp - opengl_host_display.cpp - opengl_host_display.h sdl_host_interface.cpp sdl_host_interface.h sdl_key_names.h sdl_util.cpp sdl_util.h - sdl_vulkan_host_display.cpp - sdl_vulkan_host_display.h ) target_include_directories(duckstation-sdl PRIVATE ${SDL2_INCLUDE_DIRS}) @@ -18,8 +14,6 @@ target_link_libraries(duckstation-sdl PRIVATE core common imgui nativefiledialog if(WIN32) target_sources(duckstation-sdl PRIVATE - sdl_d3d11_host_display.cpp - sdl_d3d11_host_display.h duckstation-sdl.manifest ) diff --git a/src/duckstation-sdl/duckstation-sdl.vcxproj b/src/duckstation-sdl/duckstation-sdl.vcxproj index d09f036da..bb6742bdd 100644 --- a/src/duckstation-sdl/duckstation-sdl.vcxproj +++ b/src/duckstation-sdl/duckstation-sdl.vcxproj @@ -55,23 +55,17 @@ - - - - - - diff --git a/src/duckstation-sdl/duckstation-sdl.vcxproj.filters b/src/duckstation-sdl/duckstation-sdl.vcxproj.filters index b8d47c50a..a54cb633f 100644 --- a/src/duckstation-sdl/duckstation-sdl.vcxproj.filters +++ b/src/duckstation-sdl/duckstation-sdl.vcxproj.filters @@ -2,22 +2,16 @@ - - - - - - diff --git a/src/duckstation-sdl/opengl_host_display.cpp b/src/duckstation-sdl/opengl_host_display.cpp deleted file mode 100644 index 9ea69e2ed..000000000 --- a/src/duckstation-sdl/opengl_host_display.cpp +++ /dev/null @@ -1,369 +0,0 @@ -#include "opengl_host_display.h" -#include "common/assert.h" -#include "common/log.h" -#include "imgui_impl_sdl.h" -#include "sdl_util.h" -#include -#include -#include -#include -#include -Log_SetChannel(OpenGLHostDisplay); - -class OpenGLDisplayWidgetTexture : public HostDisplayTexture -{ -public: - OpenGLDisplayWidgetTexture(GLuint id, u32 width, u32 height) : m_id(id), m_width(width), m_height(height) {} - ~OpenGLDisplayWidgetTexture() override { glDeleteTextures(1, &m_id); } - - void* GetHandle() const override { return reinterpret_cast(static_cast(m_id)); } - u32 GetWidth() const override { return m_width; } - u32 GetHeight() const override { return m_height; } - - GLuint GetGLID() const { return m_id; } - - static std::unique_ptr Create(u32 width, u32 height, const void* initial_data, - u32 initial_data_stride) - { - GLuint id; - glGenTextures(1, &id); - - GLint old_texture_binding = 0; - glGetIntegerv(GL_TEXTURE_BINDING_2D, &old_texture_binding); - - // TODO: Set pack width - Assert(!initial_data || initial_data_stride == (width * sizeof(u32))); - - glBindTexture(GL_TEXTURE_2D, id); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, initial_data); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - - glBindTexture(GL_TEXTURE_2D, id); - return std::make_unique(id, width, height); - } - -private: - GLuint m_id; - u32 m_width; - u32 m_height; -}; - -OpenGLHostDisplay::OpenGLHostDisplay(SDL_Window* window) : m_window(window) -{ - SDL_GetWindowSize(window, &m_window_width, &m_window_height); -} - -OpenGLHostDisplay::~OpenGLHostDisplay() -{ - if (m_gl_context) - { - if (m_display_vao != 0) - glDeleteVertexArrays(1, &m_display_vao); - if (m_display_linear_sampler != 0) - glDeleteSamplers(1, &m_display_linear_sampler); - if (m_display_nearest_sampler != 0) - glDeleteSamplers(1, &m_display_nearest_sampler); - - m_display_program.Destroy(); - ImGui_ImplOpenGL3_Shutdown(); - ImGui_ImplSDL2_Shutdown(); - m_gl_context.reset(); - } - - if (m_window) - SDL_DestroyWindow(m_window); -} - -HostDisplay::RenderAPI OpenGLHostDisplay::GetRenderAPI() const -{ - return m_gl_context->IsGLES() ? HostDisplay::RenderAPI::OpenGLES : HostDisplay::RenderAPI::OpenGL; -} - -void* OpenGLHostDisplay::GetRenderDevice() const -{ - return nullptr; -} - -void* OpenGLHostDisplay::GetRenderContext() const -{ - return m_gl_context.get(); -} - -void OpenGLHostDisplay::WindowResized(s32 new_window_width, s32 new_window_height) -{ - HostDisplay::WindowResized(new_window_width, new_window_height); - m_gl_context->ResizeSurface(static_cast(new_window_width), static_cast(new_window_height)); - m_window_width = static_cast(m_gl_context->GetSurfaceWidth()); - m_window_height = static_cast(m_gl_context->GetSurfaceHeight()); - ImGui::GetIO().DisplaySize.x = static_cast(m_window_width); - ImGui::GetIO().DisplaySize.y = static_cast(m_window_height); -} - -std::unique_ptr OpenGLHostDisplay::CreateTexture(u32 width, u32 height, const void* data, - u32 data_stride, bool dynamic) -{ - return OpenGLDisplayWidgetTexture::Create(width, height, data, data_stride); -} - -void OpenGLHostDisplay::UpdateTexture(HostDisplayTexture* texture, u32 x, u32 y, u32 width, u32 height, - const void* data, u32 data_stride) -{ - OpenGLDisplayWidgetTexture* tex = static_cast(texture); - Assert((data_stride % sizeof(u32)) == 0); - - GLint old_texture_binding = 0, old_alignment = 0, old_row_length = 0; - glGetIntegerv(GL_TEXTURE_BINDING_2D, &old_texture_binding); - glGetIntegerv(GL_UNPACK_ALIGNMENT, &old_alignment); - glGetIntegerv(GL_UNPACK_ROW_LENGTH, &old_row_length); - - glBindTexture(GL_TEXTURE_2D, tex->GetGLID()); - glPixelStorei(GL_UNPACK_ALIGNMENT, 4); - glPixelStorei(GL_UNPACK_ROW_LENGTH, data_stride / sizeof(u32)); - - glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, width, height, GL_RGBA, GL_UNSIGNED_BYTE, data); - - glPixelStorei(GL_UNPACK_ALIGNMENT, old_alignment); - glPixelStorei(GL_UNPACK_ROW_LENGTH, old_row_length); - glBindTexture(GL_TEXTURE_2D, old_texture_binding); -} - -bool OpenGLHostDisplay::DownloadTexture(const void* texture_handle, u32 x, u32 y, u32 width, u32 height, void* out_data, - u32 out_data_stride) -{ - GLint old_alignment = 0, old_row_length = 0; - glGetIntegerv(GL_PACK_ALIGNMENT, &old_alignment); - glGetIntegerv(GL_PACK_ROW_LENGTH, &old_row_length); - glPixelStorei(GL_PACK_ALIGNMENT, sizeof(u32)); - glPixelStorei(GL_PACK_ROW_LENGTH, out_data_stride / sizeof(u32)); - - const GLuint texture = static_cast(reinterpret_cast(texture_handle)); - GL::Texture::GetTextureSubImage(texture, 0, x, y, 0, width, height, 1, GL_RGBA, GL_UNSIGNED_BYTE, - height * out_data_stride, out_data); - - glPixelStorei(GL_PACK_ALIGNMENT, old_alignment); - glPixelStorei(GL_PACK_ROW_LENGTH, old_row_length); - return true; -} - -void OpenGLHostDisplay::SetVSync(bool enabled) -{ - // Window framebuffer has to be bound to call SetSwapInterval. - GLint current_fbo = 0; - glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, ¤t_fbo); - glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); - m_gl_context->SetSwapInterval(enabled ? 1 : 0); - glBindFramebuffer(GL_DRAW_FRAMEBUFFER, current_fbo); -} - -const char* OpenGLHostDisplay::GetGLSLVersionString() const -{ - if (m_gl_context->IsGLES()) - { - if (GLAD_GL_ES_VERSION_3_0) - return "#version 300 es"; - else - return "#version 100"; - } - else - { - if (GLAD_GL_VERSION_3_3) - return "#version 330"; - else - return "#version 130"; - } -} - -std::string OpenGLHostDisplay::GetGLSLVersionHeader() const -{ - std::string header = GetGLSLVersionString(); - header += "\n\n"; - if (m_gl_context->IsGLES()) - { - header += "precision highp float;\n"; - header += "precision highp int;\n\n"; - } - - return header; -} - -static void APIENTRY GLDebugCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, - const GLchar* message, const void* userParam) -{ - switch (severity) - { - case GL_DEBUG_SEVERITY_HIGH_KHR: - Log_ErrorPrintf(message); - break; - case GL_DEBUG_SEVERITY_MEDIUM_KHR: - Log_WarningPrint(message); - break; - case GL_DEBUG_SEVERITY_LOW_KHR: - Log_InfoPrintf(message); - break; - case GL_DEBUG_SEVERITY_NOTIFICATION: - // Log_DebugPrint(message); - break; - } -} - -bool OpenGLHostDisplay::CreateGLContext(bool debug_device) -{ - std::optional wi = SDLUtil::GetWindowInfoForSDLWindow(m_window); - if (!wi) - return false; - - m_gl_context = GL::Context::Create(wi.value()); - if (!m_gl_context) - { - Log_ErrorPrintf("Failed to create a GL context of any kind."); - return false; - } - - if (debug_device && GLAD_GL_KHR_debug) - { - glad_glDebugMessageCallbackKHR(GLDebugCallback, nullptr); - glEnable(GL_DEBUG_OUTPUT); - glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS); - } - - // this can change due to retina scaling on macos? - m_window_width = static_cast(m_gl_context->GetSurfaceWidth()); - m_window_height = static_cast(m_gl_context->GetSurfaceHeight()); - - // start with vsync on - m_gl_context->SetSwapInterval(1); - return true; -} - -bool OpenGLHostDisplay::CreateImGuiContext() -{ - ImGui::GetIO().DisplaySize.x = static_cast(m_window_width); - ImGui::GetIO().DisplaySize.y = static_cast(m_window_height); - - if (!ImGui_ImplSDL2_InitForOpenGL(m_window, nullptr) || !ImGui_ImplOpenGL3_Init(GetGLSLVersionString())) - return false; - - ImGui_ImplOpenGL3_NewFrame(); - ImGui_ImplSDL2_NewFrame(m_window); - ImGui::NewFrame(); - return true; -} - -bool OpenGLHostDisplay::CreateGLResources() -{ - static constexpr char fullscreen_quad_vertex_shader[] = R"( -uniform vec4 u_src_rect; -out vec2 v_tex0; - -void main() -{ - vec2 pos = vec2(float((gl_VertexID << 1) & 2), float(gl_VertexID & 2)); - v_tex0 = u_src_rect.xy + pos * u_src_rect.zw; - gl_Position = vec4(pos * vec2(2.0f, -2.0f) + vec2(-1.0f, 1.0f), 0.0f, 1.0f); -} -)"; - - static constexpr char display_fragment_shader[] = R"( -uniform sampler2D samp0; - -in vec2 v_tex0; -out vec4 o_col0; - -void main() -{ - o_col0 = texture(samp0, v_tex0); -} -)"; - - if (!m_display_program.Compile(GetGLSLVersionHeader() + fullscreen_quad_vertex_shader, {}, - GetGLSLVersionHeader() + display_fragment_shader)) - { - Log_ErrorPrintf("Failed to compile display shaders"); - return false; - } - - if (!m_gl_context->IsGLES()) - m_display_program.BindFragData(0, "o_col0"); - - if (!m_display_program.Link()) - { - Log_ErrorPrintf("Failed to link display program"); - return false; - } - - m_display_program.Bind(); - m_display_program.RegisterUniform("u_src_rect"); - m_display_program.RegisterUniform("samp0"); - m_display_program.Uniform1i(1, 0); - - glGenVertexArrays(1, &m_display_vao); - - // samplers - glGenSamplers(1, &m_display_nearest_sampler); - glSamplerParameteri(m_display_nearest_sampler, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glSamplerParameteri(m_display_nearest_sampler, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glGenSamplers(1, &m_display_linear_sampler); - glSamplerParameteri(m_display_linear_sampler, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glSamplerParameteri(m_display_linear_sampler, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - - return true; -} - -std::unique_ptr OpenGLHostDisplay::Create(SDL_Window* window, bool debug_device) -{ - std::unique_ptr display = std::make_unique(window); - if (!display->CreateGLContext(debug_device) || !display->CreateImGuiContext() || !display->CreateGLResources()) - return nullptr; - - return display; -} - -void OpenGLHostDisplay::Render() -{ - glDisable(GL_SCISSOR_TEST); - glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); - glClearColor(0.0f, 0.0f, 0.0f, 0.0f); - glClear(GL_COLOR_BUFFER_BIT); - - RenderDisplay(); - - ImGui::Render(); - ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); - - m_gl_context->SwapBuffers(); - - ImGui::NewFrame(); - ImGui_ImplSDL2_NewFrame(m_window); - ImGui_ImplOpenGL3_NewFrame(); - - GL::Program::ResetLastProgram(); -} - -void OpenGLHostDisplay::RenderDisplay() -{ - if (!m_display_texture_handle) - return; - - const auto [vp_left, vp_top, vp_width, vp_height] = - CalculateDrawRect(m_window_width, m_window_height, m_display_top_margin); - - glViewport(vp_left, m_window_height - vp_top - vp_height, vp_width, vp_height); - glDisable(GL_BLEND); - glDisable(GL_CULL_FACE); - glDisable(GL_DEPTH_TEST); - glDisable(GL_SCISSOR_TEST); - glDepthMask(GL_FALSE); - m_display_program.Bind(); - m_display_program.Uniform4f( - 0, static_cast(m_display_texture_view_x) / static_cast(m_display_texture_width), - static_cast(m_display_texture_view_y) / static_cast(m_display_texture_height), - (static_cast(m_display_texture_view_width) - 0.5f) / static_cast(m_display_texture_width), - (static_cast(m_display_texture_view_height) + 0.5f) / static_cast(m_display_texture_height)); - glBindTexture(GL_TEXTURE_2D, static_cast(reinterpret_cast(m_display_texture_handle))); - glBindSampler(0, m_display_linear_filtering ? m_display_linear_sampler : m_display_nearest_sampler); - glBindVertexArray(m_display_vao); - glDrawArrays(GL_TRIANGLES, 0, 3); - glBindSampler(0, 0); -} diff --git a/src/duckstation-sdl/opengl_host_display.h b/src/duckstation-sdl/opengl_host_display.h deleted file mode 100644 index 954989192..000000000 --- a/src/duckstation-sdl/opengl_host_display.h +++ /dev/null @@ -1,51 +0,0 @@ -#pragma once -#include "common/gl/context.h" -#include "common/gl/program.h" -#include "common/gl/texture.h" -#include "core/host_display.h" -#include -#include -#include - -class OpenGLHostDisplay final : public HostDisplay -{ -public: - OpenGLHostDisplay(SDL_Window* window); - ~OpenGLHostDisplay(); - - static std::unique_ptr Create(SDL_Window* window, bool debug_device); - - 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* data, u32 data_stride, - bool dynamic) override; - void UpdateTexture(HostDisplayTexture* texture, u32 x, u32 y, u32 width, u32 height, const void* data, - u32 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: - const char* GetGLSLVersionString() const; - std::string GetGLSLVersionHeader() const; - - bool CreateGLContext(bool debug_device); - bool CreateImGuiContext(); - bool CreateGLResources(); - - void RenderDisplay(); - - SDL_Window* m_window = nullptr; - std::unique_ptr m_gl_context; - - GL::Program m_display_program; - GLuint m_display_vao = 0; - GLuint m_display_nearest_sampler = 0; - GLuint m_display_linear_sampler = 0; -}; diff --git a/src/duckstation-sdl/sdl_d3d11_host_display.cpp b/src/duckstation-sdl/sdl_d3d11_host_display.cpp deleted file mode 100644 index e1e7cdafb..000000000 --- a/src/duckstation-sdl/sdl_d3d11_host_display.cpp +++ /dev/null @@ -1,121 +0,0 @@ -#include "sdl_d3d11_host_display.h" -#include "imgui_impl_sdl.h" -#include "sdl_util.h" -#include -#include -#include -#include -#include - -SDLD3D11HostDisplay::SDLD3D11HostDisplay(SDL_Window* window) : m_window(window) -{ - SDL_GetWindowSize(window, &m_window_width, &m_window_height); -} - -SDLD3D11HostDisplay::~SDLD3D11HostDisplay() -{ - if (m_window) - SDL_DestroyWindow(m_window); -} - -std::unique_ptr SDLD3D11HostDisplay::Create(SDL_Window* window, std::string_view adapter_name, bool debug_device) -{ - std::unique_ptr display = std::make_unique(window); - if (!display->Initialize(adapter_name, debug_device)) - return {}; - - return display; -} - -HostDisplay::RenderAPI SDLD3D11HostDisplay::GetRenderAPI() const -{ - return m_interface.GetRenderAPI(); -} - -void* SDLD3D11HostDisplay::GetRenderDevice() const -{ - return m_interface.GetRenderDevice(); -} - -void* SDLD3D11HostDisplay::GetRenderContext() const -{ - return m_interface.GetRenderContext(); -} - -void SDLD3D11HostDisplay::WindowResized(s32 new_window_width, s32 new_window_height) -{ - m_interface.ResizeSwapChain(static_cast(new_window_width), static_cast(new_window_height)); - HostDisplay::WindowResized(static_cast(m_interface.GetSwapChainWidth()), - static_cast(m_interface.GetSwapChainHeight())); - ImGui::GetIO().DisplaySize.x = static_cast(m_interface.GetSwapChainWidth()); - ImGui::GetIO().DisplaySize.y = static_cast(m_interface.GetSwapChainHeight()); -} - -std::unique_ptr SDLD3D11HostDisplay::CreateTexture(u32 width, u32 height, const void* data, - u32 data_stride, bool dynamic) -{ - return m_interface.CreateTexture(width, height, data, data_stride, dynamic); -} - -void SDLD3D11HostDisplay::UpdateTexture(HostDisplayTexture* texture, u32 x, u32 y, u32 width, u32 height, - const void* data, u32 data_stride) -{ - m_interface.UpdateTexture(texture, x, y, width, height, data, data_stride); -} - -bool SDLD3D11HostDisplay::DownloadTexture(const void* texture_handle, u32 x, u32 y, u32 width, u32 height, - void* out_data, u32 out_data_stride) -{ - return m_interface.DownloadTexture(texture_handle, x, y, width, height, out_data, out_data_stride); -} - -void SDLD3D11HostDisplay::SetVSync(bool enabled) -{ - m_interface.SetVSync(enabled); -} - -bool SDLD3D11HostDisplay::Initialize(std::string_view adapter_name, bool debug_device) -{ - std::optional wi = SDLUtil::GetWindowInfoForSDLWindow(m_window); - if (!wi.has_value()) - return false; - - if (!m_interface.CreateContextAndSwapChain(wi.value(), adapter_name, true, debug_device)) - return false; - - if (!m_interface.CreateResources()) - return false; - - if (!m_interface.CreateImGuiContext() || !ImGui_ImplSDL2_InitForVulkan(m_window)) - return false; - - ImGui_ImplSDL2_NewFrame(m_window); - ImGui::NewFrame(); - return true; -} - -void SDLD3D11HostDisplay::Render() -{ - if (!m_interface.BeginRender()) - return; - - if (HasDisplayTexture()) - { - const auto [left, top, width, height] = CalculateDrawRect(m_window_width, m_window_height, m_display_top_margin); - m_interface.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_interface.RenderImGui(); - - if (HasSoftwareCursor()) - { - const auto [left, top, width, height] = CalculateSoftwareCursorDrawRect(); - m_interface.RenderSoftwareCursor(left, top, width, height, m_cursor_texture.get()); - } - - m_interface.EndRenderAndPresent(); - - ImGui_ImplSDL2_NewFrame(m_window); -} diff --git a/src/duckstation-sdl/sdl_d3d11_host_display.h b/src/duckstation-sdl/sdl_d3d11_host_display.h deleted file mode 100644 index b172b2da7..000000000 --- a/src/duckstation-sdl/sdl_d3d11_host_display.h +++ /dev/null @@ -1,39 +0,0 @@ -#pragma once -#include "core/host_display.h" -#include "frontend-common/d3d11_host_display.h" -#include - -class SDLD3D11HostDisplay final : public HostDisplay -{ -public: - template - using ComPtr = Microsoft::WRL::ComPtr; - - SDLD3D11HostDisplay(SDL_Window* window); - ~SDLD3D11HostDisplay(); - - static std::unique_ptr Create(SDL_Window* window, std::string_view adapter_name, bool debug_device); - - 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* data, u32 data_stride, - bool dynamic) override; - void UpdateTexture(HostDisplayTexture* texture, u32 x, u32 y, u32 width, u32 height, const void* data, - u32 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: - SDL_Window* m_window = nullptr; - - FrontendCommon::D3D11HostDisplay m_interface; - - bool Initialize(std::string_view adapter_name, bool debug_device); -}; diff --git a/src/duckstation-sdl/sdl_host_interface.cpp b/src/duckstation-sdl/sdl_host_interface.cpp index 3bf16d93b..610a5c24a 100644 --- a/src/duckstation-sdl/sdl_host_interface.cpp +++ b/src/duckstation-sdl/sdl_host_interface.cpp @@ -11,13 +11,14 @@ #include "frontend-common/icon.h" #include "frontend-common/imgui_styles.h" #include "frontend-common/ini_settings_interface.h" +#include "frontend-common/opengl_host_display.h" #include "frontend-common/sdl_audio_stream.h" #include "frontend-common/sdl_controller_interface.h" +#include "frontend-common/vulkan_host_display.h" #include "imgui_impl_sdl.h" -#include "opengl_host_display.h" #include "scmversion/scmversion.h" #include "sdl_key_names.h" -#include "sdl_vulkan_host_display.h" +#include "sdl_util.h" #include #include #include @@ -26,7 +27,7 @@ Log_SetChannel(SDLHostInterface); #ifdef WIN32 -#include "sdl_d3d11_host_display.h" +#include "frontend-common/d3d11_host_display.h" #endif SDLHostInterface::SDLHostInterface() @@ -129,49 +130,86 @@ void SDLHostInterface::DestroySDLWindow() bool SDLHostInterface::CreateDisplay() { - const std::string shader_cache_directory(GetShaderCacheDirectory()); - std::unique_ptr display; + std::optional wi = SDLUtil::GetWindowInfoForSDLWindow(m_window); + if (!wi.has_value()) + { + ReportError("Failed to get window info from SDL window"); + return false; + } + std::unique_ptr display; switch (m_settings.gpu_renderer) { case GPURenderer::HardwareVulkan: - display = SDLVulkanHostDisplay::Create(m_window, m_settings.gpu_adapter, shader_cache_directory, - m_settings.gpu_use_debug_device); + display = std::make_unique(); break; case GPURenderer::HardwareOpenGL: #ifndef WIN32 default: #endif - display = OpenGLHostDisplay::Create(m_window, m_settings.gpu_use_debug_device); + display = std::make_unique(); break; #ifdef WIN32 case GPURenderer::HardwareD3D11: default: - display = SDLD3D11HostDisplay::Create(m_window, m_settings.gpu_adapter, m_settings.gpu_use_debug_device); + display = std::make_unique(); break; #endif } - if (!display) + Assert(display); + if (!display->CreateRenderDevice(wi.value(), m_settings.gpu_adapter, m_settings.gpu_use_debug_device) || + !display->InitializeRenderDevice(GetShaderCacheDirectory(), m_settings.gpu_use_debug_device)) + { + ReportError("Failed to create/initialize display render device"); return false; + } + + bool imgui_result; + switch (display->GetRenderAPI()) + { +#ifdef WIN32 + case HostDisplay::RenderAPI::D3D11: + imgui_result = ImGui_ImplSDL2_InitForD3D(m_window); + break; +#endif + + case HostDisplay::RenderAPI::Vulkan: + imgui_result = ImGui_ImplSDL2_InitForVulkan(m_window); + break; + + case HostDisplay::RenderAPI::OpenGL: + case HostDisplay::RenderAPI::OpenGLES: + imgui_result = ImGui_ImplSDL2_InitForOpenGL(m_window, nullptr); + break; + + default: + imgui_result = true; + break; + } + if (!imgui_result) + { + ReportError("Failed to initialize ImGui SDL2 wrapper"); + return false; + } m_app_icon_texture = display->CreateTexture(APP_ICON_WIDTH, APP_ICON_HEIGHT, APP_ICON_DATA, APP_ICON_WIDTH * sizeof(u32)); - if (!display) + if (!m_app_icon_texture) return false; display->SetDisplayTopMargin(m_fullscreen ? 0 : static_cast(20.0f * ImGui::GetIO().DisplayFramebufferScale.x)); - m_display = display.release(); + m_display = std::move(display); return true; } void SDLHostInterface::DestroyDisplay() { m_app_icon_texture.reset(); - delete m_display; - m_display = nullptr; + m_display->DestroyRenderDevice(); + m_display.reset(); } void SDLHostInterface::CreateImGuiContext() @@ -227,13 +265,16 @@ bool SDLHostInterface::AcquireHostDisplay() { ImGui::EndFrame(); DestroyDisplay(); - DestroySDLWindow(); + // We need to recreate the window, otherwise bad things happen... + DestroySDLWindow(); if (!CreateSDLWindow()) Panic("Failed to recreate SDL window on GPU renderer switch"); if (!CreateDisplay()) Panic("Failed to recreate display on GPU renderer switch"); + + ImGui::NewFrame(); } return true; @@ -357,7 +398,7 @@ bool SDLHostInterface::SetFullscreen(bool enabled) int window_width, window_height; SDL_GetWindowSize(m_window, &window_width, &window_height); - m_display->WindowResized(window_width, window_height); + m_display->ResizeRenderWindow(window_width, window_height); m_fullscreen = enabled; return true; } @@ -389,6 +430,8 @@ bool SDLHostInterface::Initialize() return false; } + ImGui::NewFrame(); + RegisterHotkeys(); // process events to pick up controllers before updating input map @@ -488,7 +531,7 @@ void SDLHostInterface::HandleSDLEvent(const SDL_Event* event) { if (event->window.event == SDL_WINDOWEVENT_RESIZED) { - m_display->WindowResized(event->window.data1, event->window.data2); + m_display->ResizeRenderWindow(event->window.data1, event->window.data2); UpdateFramebufferScale(); } else if (event->window.event == SDL_WINDOWEVENT_MOVED) @@ -1492,6 +1535,8 @@ void SDLHostInterface::Run() m_system->GetGPU()->ResetGraphicsAPIState(); m_display->Render(); + ImGui_ImplSDL2_NewFrame(m_window); + ImGui::NewFrame(); if (m_system) { diff --git a/src/duckstation-sdl/sdl_vulkan_host_display.cpp b/src/duckstation-sdl/sdl_vulkan_host_display.cpp deleted file mode 100644 index c7bfc67e3..000000000 --- a/src/duckstation-sdl/sdl_vulkan_host_display.cpp +++ /dev/null @@ -1,137 +0,0 @@ -#include "sdl_vulkan_host_display.h" -#include "common/assert.h" -#include "common/log.h" -#include "imgui.h" -#include "imgui_impl_sdl.h" -#include "imgui_impl_vulkan.h" -#include "sdl_util.h" -#include -#include -Log_SetChannel(VulkanHostDisplay); - -SDLVulkanHostDisplay::SDLVulkanHostDisplay(SDL_Window* window) : m_window(window) -{ - SDL_GetWindowSize(window, &m_window_width, &m_window_height); -} - -SDLVulkanHostDisplay::~SDLVulkanHostDisplay() -{ - ImGui_ImplSDL2_Shutdown(); - m_display.DestroyImGuiContext(); - m_display.DestroyResources(); - m_display.DestroyShaderCache(); - m_display.DestroySwapChain(); - m_display.DestroyContext(); - - if (m_window) - SDL_DestroyWindow(m_window); -} - -std::unique_ptr SDLVulkanHostDisplay::Create(SDL_Window* window, std::string_view adapter_name, - std::string_view shader_cache_directory, bool debug_device) -{ - std::unique_ptr display = std::make_unique(window); - if (!display->Initialize(adapter_name, shader_cache_directory, debug_device)) - return nullptr; - - return display; -} - -HostDisplay::RenderAPI SDLVulkanHostDisplay::GetRenderAPI() const -{ - return m_display.GetRenderAPI(); -} - -void* SDLVulkanHostDisplay::GetRenderDevice() const -{ - return m_display.GetRenderDevice(); -} - -void* SDLVulkanHostDisplay::GetRenderContext() const -{ - return m_display.GetRenderContext(); -} - -void SDLVulkanHostDisplay::WindowResized(s32 new_window_width, s32 new_window_height) -{ - m_display.ResizeSwapChain(static_cast(new_window_width), static_cast(new_window_height)); - HostDisplay::WindowResized(static_cast(m_display.GetSwapChainWidth()), - static_cast(m_display.GetSwapChainHeight())); - ImGui::GetIO().DisplaySize.x = static_cast(m_display.GetSwapChainWidth()); - ImGui::GetIO().DisplaySize.y = static_cast(m_display.GetSwapChainHeight()); -} - -std::unique_ptr SDLVulkanHostDisplay::CreateTexture(u32 width, u32 height, const void* data, - u32 data_stride, bool dynamic /*= false*/) -{ - return m_display.CreateTexture(width, height, data, data_stride, dynamic); -} - -void SDLVulkanHostDisplay::UpdateTexture(HostDisplayTexture* texture, u32 x, u32 y, u32 width, u32 height, - const void* data, u32 data_stride) -{ - m_display.UpdateTexture(texture, x, y, width, height, data, data_stride); -} - -bool SDLVulkanHostDisplay::DownloadTexture(const void* texture_handle, u32 x, u32 y, u32 width, u32 height, - void* out_data, u32 out_data_stride) -{ - return m_display.DownloadTexture(texture_handle, x, y, width, height, out_data, out_data_stride); -} - -void SDLVulkanHostDisplay::SetVSync(bool enabled) -{ - m_display.SetVSync(enabled); -} - -bool SDLVulkanHostDisplay::Initialize(std::string_view adapter_name, std::string_view shader_cache_directory, - bool debug_device) -{ - std::optional wi = SDLUtil::GetWindowInfoForSDLWindow(m_window); - if (!wi.has_value()) - { - Log_ErrorPrintf("Failed to get window info for SDL window"); - return false; - } - - if (!m_display.CreateContextAndSwapChain(wi.value(), adapter_name, debug_device)) - return false; - - m_display.CreateShaderCache(shader_cache_directory, debug_device); - - if (!m_display.CreateResources()) - return false; - - if (!m_display.CreateImGuiContext() || !ImGui_ImplSDL2_InitForVulkan(m_window)) - return false; - - ImGui_ImplSDL2_NewFrame(m_window); - ImGui::NewFrame(); - return true; -} - -void SDLVulkanHostDisplay::Render() -{ - if (!m_display.BeginRender()) - return; - - if (HasDisplayTexture()) - { - const auto [left, top, width, height] = CalculateDrawRect(m_window_width, m_window_height, m_display_top_margin); - m_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_display.RenderImGui(); - - if (HasSoftwareCursor()) - { - const auto [left, top, width, height] = CalculateSoftwareCursorDrawRect(); - m_display.RenderSoftwareCursor(left, top, width, height, m_cursor_texture.get()); - } - - m_display.EndRenderAndPresent(); - - ImGui_ImplSDL2_NewFrame(m_window); -} diff --git a/src/duckstation-sdl/sdl_vulkan_host_display.h b/src/duckstation-sdl/sdl_vulkan_host_display.h deleted file mode 100644 index e9287c8d2..000000000 --- a/src/duckstation-sdl/sdl_vulkan_host_display.h +++ /dev/null @@ -1,40 +0,0 @@ -#pragma once -#include "core/host_display.h" -#include "frontend-common/vulkan_host_display.h" -#include -#include - -class SDLVulkanHostDisplay final : public HostDisplay -{ -public: - SDLVulkanHostDisplay(SDL_Window* window); - ~SDLVulkanHostDisplay(); - - static std::unique_ptr Create(SDL_Window* window, std::string_view adapter_name, - std::string_view shader_cache_directory, bool debug_device); - - 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* data, u32 data_stride, - bool dynamic = false) override; - - void UpdateTexture(HostDisplayTexture* texture, u32 x, u32 y, u32 width, u32 height, const void* data, - u32 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 Initialize(std::string_view adapter_name, std::string_view shader_cache_directory, bool debug_device); - - SDL_Window* m_window = nullptr; - FrontendCommon::VulkanHostDisplay m_display; -}; diff --git a/src/frontend-common/CMakeLists.txt b/src/frontend-common/CMakeLists.txt index ca3e649a8..81f74a4b6 100644 --- a/src/frontend-common/CMakeLists.txt +++ b/src/frontend-common/CMakeLists.txt @@ -9,13 +9,15 @@ add_library(frontend-common imgui_styles.h ini_settings_interface.cpp ini_settings_interface.h + opengl_host_display.cpp + opengl_host_display.h save_state_selector_ui.cpp save_state_selector_ui.h vulkan_host_display.cpp vulkan_host_display.h ) -target_link_libraries(frontend-common PUBLIC core common imgui simpleini scmversion vulkan-loader) +target_link_libraries(frontend-common PUBLIC core common imgui simpleini scmversion glad vulkan-loader) if(WIN32) target_sources(frontend-common PRIVATE @@ -25,7 +27,7 @@ if(WIN32) target_link_libraries(frontend-common PRIVATE d3d11.lib dxgi.lib) endif() -if(SDL2_FOUND) +if(SDL2_FOUND AND NOT BUILD_LIBRETRO_CORE) target_sources(frontend-common PRIVATE sdl_audio_stream.cpp sdl_audio_stream.h @@ -45,7 +47,7 @@ if(SDL2_FOUND) endif() endif() -if(ENABLE_DISCORD_PRESENCE) +if(ENABLE_DISCORD_PRESENCE AND NOT BUILD_LIBRETRO_CORE) target_compile_definitions(frontend-common PUBLIC -DWITH_DISCORD_PRESENCE=1) target_link_libraries(frontend-common PRIVATE discord-rpc) endif() diff --git a/src/frontend-common/common_host_interface.cpp b/src/frontend-common/common_host_interface.cpp index 0204ec975..5dcdff0f9 100644 --- a/src/frontend-common/common_host_interface.cpp +++ b/src/frontend-common/common_host_interface.cpp @@ -1814,8 +1814,8 @@ void CommonHostInterface::DisplayLoadingScreen(const char* message, int progress const bool has_progress = (progress_min < progress_max); // eat the last imgui frame, it might've been partially rendered by the caller. - ImGui::EndFrame(); - ImGui::NewFrame(); + //ImGui::EndFrame(); + //ImGui::NewFrame(); ImGui::SetNextWindowSize(ImVec2(width, (has_progress ? 50.0f : 30.0f) * scale), ImGuiCond_Always); ImGui::SetNextWindowPos(ImVec2(io.DisplaySize.x * 0.5f, io.DisplaySize.y * 0.5f), ImGuiCond_Always, @@ -1843,6 +1843,7 @@ void CommonHostInterface::DisplayLoadingScreen(const char* message, int progress ImGui::End(); m_display->Render(); + ImGui::NewFrame(); } void CommonHostInterface::GetGameInfo(const char* path, CDImage* image, std::string* code, std::string* title) diff --git a/src/frontend-common/d3d11_host_display.cpp b/src/frontend-common/d3d11_host_display.cpp index 72b1f2267..146d60ab9 100644 --- a/src/frontend-common/d3d11_host_display.cpp +++ b/src/frontend-common/d3d11_host_display.cpp @@ -67,7 +67,36 @@ private: D3D11HostDisplay::D3D11HostDisplay() = default; -D3D11HostDisplay::~D3D11HostDisplay() = default; +D3D11HostDisplay::~D3D11HostDisplay() +{ + AssertMsg(!m_context, "Context should have been destroyed by now"); + AssertMsg(!m_swap_chain, "Swap chain should have been destroyed by now"); +} + +HostDisplay::RenderAPI D3D11HostDisplay::GetRenderAPI() const +{ + return HostDisplay::RenderAPI::D3D11; +} + +void* D3D11HostDisplay::GetRenderDevice() const +{ + return m_device.Get(); +} + +void* D3D11HostDisplay::GetRenderContext() const +{ + return m_context.Get(); +} + +bool D3D11HostDisplay::HasRenderDevice() const +{ + return static_cast(m_device); +} + +bool D3D11HostDisplay::HasRenderSurface() const +{ + return static_cast(m_swap_chain); +} std::unique_ptr D3D11HostDisplay::CreateTexture(u32 width, u32 height, const void* initial_data, u32 initial_data_stride, bool dynamic) @@ -135,13 +164,7 @@ void D3D11HostDisplay::SetVSync(bool enabled) m_vsync = enabled; } -bool D3D11HostDisplay::HasContext() const -{ - return static_cast(m_device); -} - -bool D3D11HostDisplay::CreateContextAndSwapChain(const WindowInfo& wi, std::string_view adapter_name, - bool use_flip_model, bool debug_device) +bool D3D11HostDisplay::CreateRenderDevice(const WindowInfo& wi, std::string_view adapter_name, bool debug_device) { UINT create_flags = 0; if (debug_device) @@ -242,24 +265,53 @@ bool D3D11HostDisplay::CreateContextAndSwapChain(const WindowInfo& wi, std::stri m_allow_tearing_supported = (allow_tearing_supported == TRUE); } - return CreateSwapChain(wi, use_flip_model); + m_window_info = wi; + return true; +} + +bool D3D11HostDisplay::InitializeRenderDevice(std::string_view shader_cache_directory, bool debug_device) +{ + if (!CreateSwapChain()) + return false; + + if (!CreateResources()) + return false; + + if (ImGui::GetCurrentContext() && !CreateImGuiContext()) + return false; + + return true; } -void D3D11HostDisplay::DestroyContext() +void D3D11HostDisplay::DestroyRenderDevice() { - Assert(!m_swap_chain); + if (ImGui::GetCurrentContext()) + DestroyImGuiContext(); + + DestroyResources(); + DestroyRenderSurface(); m_context.Reset(); m_device.Reset(); } -bool D3D11HostDisplay::CreateSwapChain(const WindowInfo& new_wi, bool use_flip_model) +bool D3D11HostDisplay::MakeRenderContextCurrent() { - if (new_wi.type != WindowInfo::Type::Win32) + return true; +} + +bool D3D11HostDisplay::DoneRenderContextCurrent() +{ + return true; +} + +bool D3D11HostDisplay::CreateSwapChain() +{ + if (m_window_info.type != WindowInfo::Type::Win32) return false; - m_using_flip_model_swap_chain = use_flip_model; + m_using_flip_model_swap_chain = UseFlipModelSwapChain(); - const HWND window_hwnd = reinterpret_cast(new_wi.window_handle); + const HWND window_hwnd = reinterpret_cast(m_window_info.window_handle); RECT client_rc{}; GetClientRect(window_hwnd, &client_rc); const u32 width = static_cast(client_rc.right - client_rc.left); @@ -330,23 +382,33 @@ bool D3D11HostDisplay::CreateSwapChainRTV() return false; } - m_swap_chain_width = backbuffer_desc.Width; - m_swap_chain_height = backbuffer_desc.Height; + m_window_info.surface_width = backbuffer_desc.Width; + m_window_info.surface_height = backbuffer_desc.Height; + + if (ImGui::GetCurrentContext()) + { + ImGui::GetIO().DisplaySize.x = static_cast(backbuffer_desc.Width); + ImGui::GetIO().DisplaySize.y = static_cast(backbuffer_desc.Height); + } + return true; } -bool D3D11HostDisplay::RecreateSwapChain(const WindowInfo& new_wi, bool use_flip_model) +bool D3D11HostDisplay::ChangeRenderWindow(const WindowInfo& new_wi) { - return CreateSwapChain(new_wi, use_flip_model); + DestroyRenderSurface(); + + m_window_info = new_wi; + return CreateSwapChain(); } -void D3D11HostDisplay::DestroySwapChain() +void D3D11HostDisplay::DestroyRenderSurface() { m_swap_chain_rtv.Reset(); m_swap_chain.Reset(); } -void D3D11HostDisplay::ResizeSwapChain(u32 new_width, u32 new_height) +void D3D11HostDisplay::ResizeRenderWindow(s32 new_window_width, s32 new_window_height) { if (!m_swap_chain) return; @@ -423,8 +485,8 @@ void D3D11HostDisplay::DestroyResources() bool D3D11HostDisplay::CreateImGuiContext() { - ImGui::GetIO().DisplaySize.x = static_cast(m_swap_chain_width); - ImGui::GetIO().DisplaySize.y = static_cast(m_swap_chain_height); + ImGui::GetIO().DisplaySize.x = static_cast(m_window_info.surface_width); + ImGui::GetIO().DisplaySize.y = static_cast(m_window_info.surface_height); if (!ImGui_ImplDX11_Init(m_device.Get(), m_context.Get())) return false; @@ -438,12 +500,27 @@ void D3D11HostDisplay::DestroyImGuiContext() ImGui_ImplDX11_Shutdown(); } -bool D3D11HostDisplay::BeginRender() +bool D3D11HostDisplay::Render() { static constexpr std::array clear_color = {}; m_context->ClearRenderTargetView(m_swap_chain_rtv.Get(), clear_color.data()); m_context->OMSetRenderTargets(1, m_swap_chain_rtv.GetAddressOf(), nullptr); + RenderDisplay(); + + if (ImGui::GetCurrentContext()) + RenderImGui(); + + RenderSoftwareCursor(); + + if (!m_vsync && m_using_allow_tearing) + m_swap_chain->Present(0, DXGI_PRESENT_ALLOW_TEARING); + else + m_swap_chain->Present(BoolToUInt32(m_vsync), 0); + + if (ImGui::GetCurrentContext()) + ImGui_ImplDX11_NewFrame(); + return true; } @@ -453,20 +530,20 @@ void D3D11HostDisplay::RenderImGui() ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData()); } -void D3D11HostDisplay::EndRenderAndPresent() +void D3D11HostDisplay::RenderDisplay() { - if (!m_vsync && m_using_allow_tearing) - m_swap_chain->Present(0, DXGI_PRESENT_ALLOW_TEARING); - else - m_swap_chain->Present(BoolToUInt32(m_vsync), 0); + if (!HasDisplayTexture()) + return; - ImGui::NewFrame(); - ImGui_ImplDX11_NewFrame(); + const auto [left, top, width, height] = CalculateDrawRect(GetWindowWidth(), GetWindowHeight(), m_display_top_margin); + 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); } void D3D11HostDisplay::RenderDisplay(s32 left, s32 top, s32 width, s32 height, void* texture_handle, u32 texture_width, - u32 texture_height, u32 texture_view_x, u32 texture_view_y, u32 texture_view_width, - u32 texture_view_height, bool linear_filter) + s32 texture_height, s32 texture_view_x, s32 texture_view_y, s32 texture_view_width, + s32 texture_view_height, bool linear_filter) { m_context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST); m_context->VSSetShader(m_display_vertex_shader.Get(), nullptr, 0); @@ -493,6 +570,15 @@ void D3D11HostDisplay::RenderDisplay(s32 left, s32 top, s32 width, s32 height, v m_context->Draw(3, 0); } +void D3D11HostDisplay::RenderSoftwareCursor() +{ + if (!HasSoftwareCursor()) + return; + + const auto [left, top, width, height] = CalculateSoftwareCursorDrawRect(); + RenderSoftwareCursor(left, top, width, height, m_cursor_texture.get()); +} + void D3D11HostDisplay::RenderSoftwareCursor(s32 left, s32 top, s32 width, s32 height, HostDisplayTexture* texture_handle) { @@ -574,4 +660,9 @@ std::vector D3D11HostDisplay::EnumerateAdapterNames(IDXGIFactory* d return adapter_names; } +bool D3D11HostDisplay::UseFlipModelSwapChain() const +{ + return true; +} + } // namespace FrontendCommon diff --git a/src/frontend-common/d3d11_host_display.h b/src/frontend-common/d3d11_host_display.h index 4617a6f2b..b882b8dac 100644 --- a/src/frontend-common/d3d11_host_display.h +++ b/src/frontend-common/d3d11_host_display.h @@ -15,7 +15,7 @@ namespace FrontendCommon { -class D3D11HostDisplay final +class D3D11HostDisplay : public HostDisplay { public: template @@ -24,56 +24,62 @@ public: D3D11HostDisplay(); ~D3D11HostDisplay(); - ALWAYS_INLINE HostDisplay::RenderAPI GetRenderAPI() const { return HostDisplay::RenderAPI::D3D11; } - ALWAYS_INLINE void* GetRenderDevice() const { return m_device.Get(); } - ALWAYS_INLINE void* GetRenderContext() const { return m_context.Get(); } + virtual RenderAPI GetRenderAPI() const override; + virtual void* GetRenderDevice() const override; + virtual void* GetRenderContext() const override; - bool CreateContextAndSwapChain(const WindowInfo& wi, std::string_view adapter_name, bool use_flip_model, - bool debug_device); - bool HasContext() const; - void DestroyContext(); + virtual bool HasRenderDevice() const override; + virtual bool HasRenderSurface() const override; - bool CreateResources(); - void DestroyResources(); + virtual bool CreateRenderDevice(const WindowInfo& wi, std::string_view adapter_name, bool debug_device) override; + virtual bool InitializeRenderDevice(std::string_view shader_cache_directory, bool debug_device) override; + virtual void DestroyRenderDevice() override; - bool CreateImGuiContext(); - void DestroyImGuiContext(); + virtual bool MakeRenderContextCurrent() override; + virtual bool DoneRenderContextCurrent() override; - ALWAYS_INLINE u32 GetSwapChainWidth() const { return m_swap_chain_width; } - ALWAYS_INLINE u32 GetSwapChainHeight() const { return m_swap_chain_height; } - ALWAYS_INLINE bool HasSwapChain() const { return static_cast(m_swap_chain); } - - bool RecreateSwapChain(const WindowInfo& new_wi, bool use_flip_model); - void ResizeSwapChain(u32 new_width, u32 new_height); - void DestroySwapChain(); + virtual bool ChangeRenderWindow(const WindowInfo& new_wi) override; + virtual void ResizeRenderWindow(s32 new_window_width, s32 new_window_height) override; + virtual void DestroyRenderSurface() override; std::unique_ptr CreateTexture(u32 width, u32 height, const void* initial_data, - u32 initial_data_stride, bool dynamic); + 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); + 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); + u32 out_data_stride) override; - void SetVSync(bool enabled); + virtual void SetVSync(bool enabled) override; - bool BeginRender(); - void RenderDisplay(s32 left, s32 top, s32 width, s32 height, void* texture_handle, u32 texture_width, - u32 texture_height, u32 texture_view_x, u32 texture_view_y, u32 texture_view_width, - u32 texture_view_height, bool linear_filter); - void RenderImGui(); - void RenderSoftwareCursor(s32 left, s32 top, s32 width, s32 height, HostDisplayTexture* texture_handle); - void EndRenderAndPresent(); + virtual bool Render() override; static std::vector EnumerateAdapterNames(); -private: +protected: static constexpr u32 DISPLAY_UNIFORM_BUFFER_SIZE = 16; + virtual bool UseFlipModelSwapChain() const; + static std::vector EnumerateAdapterNames(IDXGIFactory* dxgi_factory); - bool CreateSwapChain(const WindowInfo& new_wi, bool use_flip_model); + virtual bool CreateResources(); + virtual void DestroyResources(); + + virtual bool CreateImGuiContext(); + virtual void DestroyImGuiContext(); + + bool CreateSwapChain(); bool CreateSwapChainRTV(); + void RenderDisplay(); + void RenderImGui(); + void RenderSoftwareCursor(); + + void RenderDisplay(s32 left, s32 top, s32 width, s32 height, void* texture_handle, u32 texture_width, + s32 texture_height, s32 texture_view_x, s32 texture_view_y, s32 texture_view_width, + s32 texture_view_height, bool linear_filter); + void RenderSoftwareCursor(s32 left, s32 top, s32 width, s32 height, HostDisplayTexture* texture_handle); + ComPtr m_dxgi_factory; ComPtr m_device; @@ -94,9 +100,6 @@ private: D3D11::StreamBuffer m_display_uniform_buffer; D3D11::AutoStagingTexture m_readback_staging_texture; - u32 m_swap_chain_width = 0; - u32 m_swap_chain_height = 0; - bool m_allow_tearing_supported = false; bool m_using_flip_model_swap_chain = true; bool m_using_allow_tearing = false; diff --git a/src/frontend-common/frontend-common.vcxproj b/src/frontend-common/frontend-common.vcxproj index 00be65a23..953c71a98 100644 --- a/src/frontend-common/frontend-common.vcxproj +++ b/src/frontend-common/frontend-common.vcxproj @@ -44,6 +44,9 @@ {43540154-9e1e-409c-834f-b84be5621388} + + {bb08260f-6fbc-46af-8924-090ee71360c6} + {6a4208ed-e3dc-41e1-81cd-f61025fc285a} @@ -67,6 +70,7 @@ + @@ -80,6 +84,7 @@ + @@ -234,7 +239,7 @@ WITH_SDL2=1;WITH_DISCORD_PRESENCE=1;WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions) true ProgramDatabase - $(SolutionDir)dep\msvc\include\SDL;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\simpleini\include;$(SolutionDir)dep\discord-rpc\include;$(SolutionDir)dep\vulkan-loader\include;$(SolutionDir)src;%(AdditionalIncludeDirectories) + $(SolutionDir)dep\msvc\include\SDL;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\simpleini\include;$(SolutionDir)dep\discord-rpc\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\vulkan-loader\include;$(SolutionDir)src;%(AdditionalIncludeDirectories) true stdcpp17 true @@ -261,7 +266,7 @@ WITH_SDL2=1;WITH_DISCORD_PRESENCE=1;_ITERATOR_DEBUG_LEVEL=1;WIN32;_DEBUGFAST;_DEBUG;_LIB;%(PreprocessorDefinitions) true ProgramDatabase - $(SolutionDir)dep\msvc\include\SDL;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\simpleini\include;$(SolutionDir)dep\discord-rpc\include;$(SolutionDir)dep\vulkan-loader\include;$(SolutionDir)src;%(AdditionalIncludeDirectories) + $(SolutionDir)dep\msvc\include\SDL;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\simpleini\include;$(SolutionDir)dep\discord-rpc\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\vulkan-loader\include;$(SolutionDir)src;%(AdditionalIncludeDirectories) Default false true @@ -291,7 +296,7 @@ WITH_SDL2=1;WITH_DISCORD_PRESENCE=1;WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions) true ProgramDatabase - $(SolutionDir)dep\msvc\include\SDL;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\simpleini\include;$(SolutionDir)dep\discord-rpc\include;$(SolutionDir)dep\vulkan-loader\include;$(SolutionDir)src;%(AdditionalIncludeDirectories) + $(SolutionDir)dep\msvc\include\SDL;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\simpleini\include;$(SolutionDir)dep\discord-rpc\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\vulkan-loader\include;$(SolutionDir)src;%(AdditionalIncludeDirectories) true stdcpp17 true @@ -318,7 +323,7 @@ WITH_SDL2=1;WITH_DISCORD_PRESENCE=1;_ITERATOR_DEBUG_LEVEL=1;WIN32;_DEBUGFAST;_DEBUG;_LIB;%(PreprocessorDefinitions) true ProgramDatabase - $(SolutionDir)dep\msvc\include\SDL;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\simpleini\include;$(SolutionDir)dep\discord-rpc\include;$(SolutionDir)dep\vulkan-loader\include;$(SolutionDir)src;%(AdditionalIncludeDirectories) + $(SolutionDir)dep\msvc\include\SDL;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\simpleini\include;$(SolutionDir)dep\discord-rpc\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\vulkan-loader\include;$(SolutionDir)src;%(AdditionalIncludeDirectories) Default false true @@ -349,7 +354,7 @@ true WITH_SDL2=1;WITH_DISCORD_PRESENCE=1;WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions) true - $(SolutionDir)dep\msvc\include\SDL;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\simpleini\include;$(SolutionDir)dep\discord-rpc\include;$(SolutionDir)dep\vulkan-loader\include;$(SolutionDir)src;%(AdditionalIncludeDirectories) + $(SolutionDir)dep\msvc\include\SDL;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\simpleini\include;$(SolutionDir)dep\discord-rpc\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\vulkan-loader\include;$(SolutionDir)src;%(AdditionalIncludeDirectories) true stdcpp17 false @@ -378,7 +383,7 @@ true WITH_SDL2=1;WITH_DISCORD_PRESENCE=1;WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions) true - $(SolutionDir)dep\msvc\include\SDL;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\simpleini\include;$(SolutionDir)dep\discord-rpc\include;$(SolutionDir)dep\vulkan-loader\include;$(SolutionDir)src;%(AdditionalIncludeDirectories) + $(SolutionDir)dep\msvc\include\SDL;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\simpleini\include;$(SolutionDir)dep\discord-rpc\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\vulkan-loader\include;$(SolutionDir)src;%(AdditionalIncludeDirectories) true true stdcpp17 @@ -409,7 +414,7 @@ true WITH_SDL2=1;WITH_DISCORD_PRESENCE=1;WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions) true - $(SolutionDir)dep\msvc\include\SDL;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\simpleini\include;$(SolutionDir)dep\discord-rpc\include;$(SolutionDir)dep\vulkan-loader\include;$(SolutionDir)src;%(AdditionalIncludeDirectories) + $(SolutionDir)dep\msvc\include\SDL;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\simpleini\include;$(SolutionDir)dep\discord-rpc\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\vulkan-loader\include;$(SolutionDir)src;%(AdditionalIncludeDirectories) true stdcpp17 false @@ -438,7 +443,7 @@ true WITH_SDL2=1;WITH_DISCORD_PRESENCE=1;WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions) true - $(SolutionDir)dep\msvc\include\SDL;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\simpleini\include;$(SolutionDir)dep\discord-rpc\include;$(SolutionDir)dep\vulkan-loader\include;$(SolutionDir)src;%(AdditionalIncludeDirectories) + $(SolutionDir)dep\msvc\include\SDL;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\simpleini\include;$(SolutionDir)dep\discord-rpc\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\vulkan-loader\include;$(SolutionDir)src;%(AdditionalIncludeDirectories) true true stdcpp17 diff --git a/src/frontend-common/frontend-common.vcxproj.filters b/src/frontend-common/frontend-common.vcxproj.filters index d16067b62..bf40abb3d 100644 --- a/src/frontend-common/frontend-common.vcxproj.filters +++ b/src/frontend-common/frontend-common.vcxproj.filters @@ -12,6 +12,7 @@ + @@ -25,6 +26,7 @@ + diff --git a/src/duckstation-qt/openglhostdisplay.cpp b/src/frontend-common/opengl_host_display.cpp similarity index 54% rename from src/duckstation-qt/openglhostdisplay.cpp rename to src/frontend-common/opengl_host_display.cpp index 849a2632d..3d81ba9bb 100644 --- a/src/duckstation-qt/openglhostdisplay.cpp +++ b/src/frontend-common/opengl_host_display.cpp @@ -1,21 +1,19 @@ -#include "openglhostdisplay.h" +#include "opengl_host_display.h" #include "common/assert.h" #include "common/log.h" #include "imgui.h" -#include "qtdisplaywidget.h" -#include "qthostinterface.h" -#include -#include #include #include #include -Log_SetChannel(OpenGLHostDisplay); +Log_SetChannel(LibretroOpenGLHostDisplay); -class OpenGLDisplayWidgetTexture : public HostDisplayTexture +namespace FrontendCommon { + +class OpenGLHostDisplayTexture : public HostDisplayTexture { public: - OpenGLDisplayWidgetTexture(GLuint id, u32 width, u32 height) : m_id(id), m_width(width), m_height(height) {} - ~OpenGLDisplayWidgetTexture() override { glDeleteTextures(1, &m_id); } + OpenGLHostDisplayTexture(GLuint id, u32 width, u32 height) : m_id(id), m_width(width), m_height(height) {} + ~OpenGLHostDisplayTexture() override { glDeleteTextures(1, &m_id); } void* GetHandle() const override { return reinterpret_cast(static_cast(m_id)); } u32 GetWidth() const override { return m_width; } @@ -23,8 +21,8 @@ public: GLuint GetGLID() const { return m_id; } - static std::unique_ptr Create(u32 width, u32 height, const void* initial_data, - u32 initial_data_stride) + static std::unique_ptr Create(u32 width, u32 height, const void* initial_data, + u32 initial_data_stride) { GLuint id; glGenTextures(1, &id); @@ -42,7 +40,7 @@ public: glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glBindTexture(GL_TEXTURE_2D, id); - return std::make_unique(id, width, height); + return std::make_unique(id, width, height); } private: @@ -51,13 +49,16 @@ private: u32 m_height; }; -OpenGLHostDisplay::OpenGLHostDisplay(QtHostInterface* host_interface) : QtHostDisplay(host_interface) {} +OpenGLHostDisplay::OpenGLHostDisplay() = default; -OpenGLHostDisplay::~OpenGLHostDisplay() = default; +OpenGLHostDisplay::~OpenGLHostDisplay() +{ + AssertMsg(!m_gl_context, "Context should have been destroyed by now"); +} HostDisplay::RenderAPI OpenGLHostDisplay::GetRenderAPI() const { - return m_gl_context->IsGLES() ? HostDisplay::RenderAPI::OpenGLES : HostDisplay::RenderAPI::OpenGL; + return m_gl_context->IsGLES() ? RenderAPI::OpenGLES : RenderAPI::OpenGL; } void* OpenGLHostDisplay::GetRenderDevice() const @@ -70,22 +71,16 @@ void* OpenGLHostDisplay::GetRenderContext() const return m_gl_context.get(); } -void OpenGLHostDisplay::WindowResized(s32 new_window_width, s32 new_window_height) -{ - QtHostDisplay::WindowResized(new_window_width, new_window_height); - m_gl_context->ResizeSurface(static_cast(new_window_width), static_cast(new_window_height)); -} - std::unique_ptr OpenGLHostDisplay::CreateTexture(u32 width, u32 height, const void* initial_data, u32 initial_data_stride, bool dynamic) { - return OpenGLDisplayWidgetTexture::Create(width, height, initial_data, initial_data_stride); + return OpenGLHostDisplayTexture::Create(width, height, initial_data, initial_data_stride); } void OpenGLHostDisplay::UpdateTexture(HostDisplayTexture* texture, u32 x, u32 y, u32 width, u32 height, const void* texture_data, u32 texture_data_stride) { - OpenGLDisplayWidgetTexture* tex = static_cast(texture); + OpenGLHostDisplayTexture* tex = static_cast(texture); Assert((texture_data_stride % sizeof(u32)) == 0); GLint old_texture_binding = 0, old_alignment = 0, old_row_length = 0; @@ -134,7 +129,7 @@ void OpenGLHostDisplay::SetVSync(bool enabled) const char* OpenGLHostDisplay::GetGLSLVersionString() const { - if (m_gl_context->IsGLES()) + if (GetRenderAPI() == RenderAPI::OpenGLES) { if (GLAD_GL_ES_VERSION_3_0) return "#version 300 es"; @@ -154,7 +149,7 @@ std::string OpenGLHostDisplay::GetGLSLVersionHeader() const { std::string header = GetGLSLVersionString(); header += "\n\n"; - if (m_gl_context->IsGLES()) + if (GetRenderAPI() == RenderAPI::OpenGLES) { header += "precision highp float;\n"; header += "precision highp int;\n\n"; @@ -183,49 +178,54 @@ static void APIENTRY GLDebugCallback(GLenum source, GLenum type, GLuint id, GLen } } -bool OpenGLHostDisplay::hasDeviceContext() const +bool OpenGLHostDisplay::HasRenderDevice() const { return static_cast(m_gl_context); } -bool OpenGLHostDisplay::createDeviceContext(const QString& adapter_name, bool debug_device) +bool OpenGLHostDisplay::HasRenderSurface() const { - m_window_width = m_widget->scaledWindowWidth(); - m_window_height = m_widget->scaledWindowHeight(); - - std::optional wi = getWindowInfo(); - if (!wi.has_value()) - return false; + return m_window_info.type != WindowInfo::Type::Surfaceless; +} - m_gl_context = GL::Context::Create(wi.value()); +bool OpenGLHostDisplay::CreateRenderDevice(const WindowInfo& wi, std::string_view adapter_name, bool debug_device) +{ + m_gl_context = GL::Context::Create(wi); if (!m_gl_context) { Log_ErrorPrintf("Failed to create any GL context"); return false; } + m_window_info = wi; + m_window_info.surface_width = m_gl_context->GetSurfaceWidth(); + m_window_info.surface_height = m_gl_context->GetSurfaceHeight(); return true; } -bool OpenGLHostDisplay::initializeDeviceContext(std::string_view shader_cache_directory, bool debug_device) +bool OpenGLHostDisplay::InitializeRenderDevice(std::string_view shader_cache_directory, bool debug_device) { if (debug_device && GLAD_GL_KHR_debug) { - glad_glDebugMessageCallbackKHR(GLDebugCallback, nullptr); + if (GetRenderAPI() == RenderAPI::OpenGLES) + glDebugMessageCallbackKHR(GLDebugCallback, nullptr); + else + glDebugMessageCallback(GLDebugCallback, nullptr); + glEnable(GL_DEBUG_OUTPUT); - glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS); + // glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS); } - if (!QtHostDisplay::initializeDeviceContext(shader_cache_directory, debug_device)) - { - m_gl_context->DoneCurrent(); + if (!CreateResources()) + return false; + + if (ImGui::GetCurrentContext() && !CreateImGuiContext()) return false; - } return true; } -bool OpenGLHostDisplay::activateDeviceContext() +bool OpenGLHostDisplay::MakeRenderContextCurrent() { if (!m_gl_context->MakeCurrent()) { @@ -236,56 +236,92 @@ bool OpenGLHostDisplay::activateDeviceContext() return true; } -void OpenGLHostDisplay::deactivateDeviceContext() +bool OpenGLHostDisplay::DoneRenderContextCurrent() { - m_gl_context->DoneCurrent(); + return m_gl_context->DoneCurrent(); } -void OpenGLHostDisplay::destroyDeviceContext() +void OpenGLHostDisplay::DestroyRenderDevice() { - QtHostDisplay::destroyDeviceContext(); + if (!m_gl_context) + return; + + if (ImGui::GetCurrentContext()) + DestroyImGuiContext(); + + DestroyResources(); + m_gl_context->DoneCurrent(); m_gl_context.reset(); } -bool OpenGLHostDisplay::recreateSurface() +bool OpenGLHostDisplay::ChangeRenderWindow(const WindowInfo& new_wi) { - m_window_width = m_widget->scaledWindowWidth(); - m_window_height = m_widget->scaledWindowHeight(); + Assert(m_gl_context); + + if (!m_gl_context->ChangeSurface(new_wi)) + { + Log_ErrorPrintf("Failed to change surface"); + return false; + } - if (m_gl_context) + m_window_info = new_wi; + m_window_info.surface_width = m_gl_context->GetSurfaceWidth(); + m_window_info.surface_height = m_gl_context->GetSurfaceHeight(); + + if (ImGui::GetCurrentContext()) { - std::optional wi = getWindowInfo(); - if (!wi.has_value() || !m_gl_context->ChangeSurface(wi.value())) - return false; + ImGui::GetIO().DisplaySize.x = static_cast(m_window_info.surface_width); + ImGui::GetIO().DisplaySize.y = static_cast(m_window_info.surface_height); } return true; } -void OpenGLHostDisplay::destroySurface() {} +void OpenGLHostDisplay::ResizeRenderWindow(s32 new_window_width, s32 new_window_height) +{ + if (!m_gl_context) + return; -bool OpenGLHostDisplay::createImGuiContext() + m_gl_context->ResizeSurface(static_cast(new_window_width), static_cast(new_window_height)); + m_window_info.surface_width = m_gl_context->GetSurfaceWidth(); + m_window_info.surface_height = m_gl_context->GetSurfaceHeight(); + + if (ImGui::GetCurrentContext()) + { + ImGui::GetIO().DisplaySize.x = static_cast(m_window_info.surface_width); + ImGui::GetIO().DisplaySize.y = static_cast(m_window_info.surface_height); + } +} + +void OpenGLHostDisplay::DestroyRenderSurface() { - if (!QtHostDisplay::createImGuiContext()) - return false; + if (!m_gl_context) + return; + + m_window_info = {}; + if (!m_gl_context->ChangeSurface(m_window_info)) + Log_ErrorPrintf("Failed to switch to surfaceless"); +} + +bool OpenGLHostDisplay::CreateImGuiContext() +{ + ImGui::GetIO().DisplaySize.x = static_cast(m_window_info.surface_width); + ImGui::GetIO().DisplaySize.y = static_cast(m_window_info.surface_height); if (!ImGui_ImplOpenGL3_Init(GetGLSLVersionString())) return false; ImGui_ImplOpenGL3_NewFrame(); - ImGui::NewFrame(); return true; } -void OpenGLHostDisplay::destroyImGuiContext() +void OpenGLHostDisplay::DestroyImGuiContext() { ImGui_ImplOpenGL3_Shutdown(); - - QtHostDisplay::destroyImGuiContext(); } -bool OpenGLHostDisplay::createDeviceResources() +bool OpenGLHostDisplay::CreateResources() { static constexpr char fullscreen_quad_vertex_shader[] = R"( uniform vec4 u_src_rect; @@ -318,7 +354,7 @@ void main() return false; } - if (!m_gl_context->IsGLES()) + if (GetRenderAPI() != RenderAPI::OpenGLES) m_display_program.BindFragData(0, "o_col0"); if (!m_display_program.Link()) @@ -345,10 +381,8 @@ void main() return true; } -void OpenGLHostDisplay::destroyDeviceResources() +void OpenGLHostDisplay::DestroyResources() { - QtHostDisplay::destroyDeviceResources(); - if (m_display_vao != 0) glDeleteVertexArrays(1, &m_display_vao); if (m_display_linear_sampler != 0) @@ -359,49 +393,95 @@ void OpenGLHostDisplay::destroyDeviceResources() m_display_program.Destroy(); } -void OpenGLHostDisplay::Render() +bool OpenGLHostDisplay::Render() { glDisable(GL_SCISSOR_TEST); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); glClearColor(0.0f, 0.0f, 0.0f, 1.0f); glClear(GL_COLOR_BUFFER_BIT); - renderDisplay(); + RenderDisplay(); - ImGui::Render(); - ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); + if (ImGui::GetCurrentContext()) + RenderImGui(); + + RenderSoftwareCursor(); m_gl_context->SwapBuffers(); - ImGui::NewFrame(); - ImGui_ImplOpenGL3_NewFrame(); + if (ImGui::GetCurrentContext()) + ImGui_ImplOpenGL3_NewFrame(); + + return true; +} +void OpenGLHostDisplay::RenderImGui() +{ + ImGui::Render(); + ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); GL::Program::ResetLastProgram(); } -void OpenGLHostDisplay::renderDisplay() +void OpenGLHostDisplay::RenderDisplay() { - if (!m_display_texture_handle) + if (!HasDisplayTexture()) return; - const auto [vp_left, vp_top, vp_width, vp_height] = - CalculateDrawRect(m_window_width, m_window_height, m_display_top_margin); + const auto [left, top, width, height] = CalculateDrawRect(GetWindowWidth(), GetWindowHeight(), m_display_top_margin); + RenderDisplay(left, GetWindowHeight() - top - height, 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); +} - glViewport(vp_left, m_window_height - (m_display_top_margin + vp_top) - vp_height, vp_width, vp_height); +void OpenGLHostDisplay::RenderDisplay(s32 left, s32 bottom, s32 width, s32 height, void* texture_handle, + u32 texture_width, s32 texture_height, s32 texture_view_x, s32 texture_view_y, + s32 texture_view_width, s32 texture_view_height, bool linear_filter) +{ + glViewport(left, bottom, width, height); glDisable(GL_BLEND); glDisable(GL_CULL_FACE); glDisable(GL_DEPTH_TEST); glDisable(GL_SCISSOR_TEST); glDepthMask(GL_FALSE); m_display_program.Bind(); - m_display_program.Uniform4f( - 0, static_cast(m_display_texture_view_x) / static_cast(m_display_texture_width), - static_cast(m_display_texture_view_y) / static_cast(m_display_texture_height), - (static_cast(m_display_texture_view_width) - 0.5f) / static_cast(m_display_texture_width), - (static_cast(m_display_texture_view_height) + 0.5f) / static_cast(m_display_texture_height)); - glBindTexture(GL_TEXTURE_2D, static_cast(reinterpret_cast(m_display_texture_handle))); - glBindSampler(0, m_display_linear_filtering ? m_display_linear_sampler : m_display_nearest_sampler); + m_display_program.Uniform4f(0, static_cast(texture_view_x) / static_cast(texture_width), + static_cast(texture_view_y) / static_cast(texture_height), + (static_cast(texture_view_width) - 0.5f) / static_cast(texture_width), + (static_cast(texture_view_height) + 0.5f) / static_cast(texture_height)); + glBindTexture(GL_TEXTURE_2D, static_cast(reinterpret_cast(texture_handle))); + glBindSampler(0, linear_filter ? m_display_linear_sampler : m_display_nearest_sampler); + glBindVertexArray(m_display_vao); + glDrawArrays(GL_TRIANGLES, 0, 3); + glBindSampler(0, 0); +} + +void OpenGLHostDisplay::RenderSoftwareCursor() +{ + if (!HasSoftwareCursor()) + return; + + const auto [left, top, width, height] = CalculateSoftwareCursorDrawRect(); + RenderSoftwareCursor(left, top, width, height, m_cursor_texture.get()); +} + +void OpenGLHostDisplay::RenderSoftwareCursor(s32 left, s32 top, s32 width, s32 height, + HostDisplayTexture* texture_handle) +{ + glViewport(left, GetWindowHeight() - top - height, width, height); + glEnable(GL_BLEND); + glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ZERO); + glBlendEquationSeparate(GL_FUNC_ADD, GL_FUNC_ADD); + glDisable(GL_CULL_FACE); + glDisable(GL_DEPTH_TEST); + glDisable(GL_SCISSOR_TEST); + glDepthMask(GL_FALSE); + m_display_program.Bind(); + m_display_program.Uniform4f(0, 0.0f, 0.0f, 1.0f, 1.0f); + glBindTexture(GL_TEXTURE_2D, static_cast(texture_handle)->GetGLID()); + glBindSampler(0, m_display_linear_sampler); glBindVertexArray(m_display_vao); glDrawArrays(GL_TRIANGLES, 0, 3); glBindSampler(0, 0); } + +} // namespace FrontendCommon diff --git a/src/frontend-common/opengl_host_display.h b/src/frontend-common/opengl_host_display.h new file mode 100644 index 000000000..9bd51624d --- /dev/null +++ b/src/frontend-common/opengl_host_display.h @@ -0,0 +1,82 @@ +#pragma once + +// GLAD has to come first so that Qt doesn't pull in the system GL headers, which are incompatible with glad. +#include + +// Hack to prevent Apple's glext.h headers from getting included via qopengl.h, since we still want to use glad. +#ifdef __APPLE__ +#define __glext_h_ +#endif + +#include "common/gl/context.h" +#include "common/gl/program.h" +#include "common/gl/texture.h" +#include "common/window_info.h" +#include "core/host_display.h" +#include + +namespace FrontendCommon { + +class OpenGLHostDisplay : public HostDisplay +{ +public: + OpenGLHostDisplay(); + virtual ~OpenGLHostDisplay(); + + virtual RenderAPI GetRenderAPI() const override; + virtual void* GetRenderDevice() const override; + virtual void* GetRenderContext() const override; + + virtual bool HasRenderDevice() const override; + virtual bool HasRenderSurface() const override; + + virtual bool CreateRenderDevice(const WindowInfo& wi, std::string_view adapter_name, bool debug_device) override; + virtual bool InitializeRenderDevice(std::string_view shader_cache_directory, bool debug_device) override; + virtual void DestroyRenderDevice() override; + + virtual bool MakeRenderContextCurrent() override; + virtual bool DoneRenderContextCurrent() override; + + virtual bool ChangeRenderWindow(const WindowInfo& new_wi) override; + virtual void ResizeRenderWindow(s32 new_window_width, s32 new_window_height) override; + virtual void DestroyRenderSurface() 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; + + virtual void SetVSync(bool enabled) override; + + virtual bool Render() override; + +protected: + const char* GetGLSLVersionString() const; + std::string GetGLSLVersionHeader() const; + + virtual bool CreateResources(); + virtual void DestroyResources(); + + virtual bool CreateImGuiContext(); + virtual void DestroyImGuiContext(); + + void RenderDisplay(); + void RenderImGui(); + void RenderSoftwareCursor(); + + void RenderDisplay(s32 left, s32 bottom, s32 width, s32 height, void* texture_handle, u32 texture_width, + s32 texture_height, s32 texture_view_x, s32 texture_view_y, s32 texture_view_width, + s32 texture_view_height, bool linear_filter); + void RenderSoftwareCursor(s32 left, s32 top, s32 width, s32 height, HostDisplayTexture* texture_handle); + + std::unique_ptr m_gl_context; + + GL::Program m_display_program; + GLuint m_display_vao = 0; + GLuint m_display_nearest_sampler = 0; + GLuint m_display_linear_sampler = 0; +}; + +} // namespace FrontendCommon \ No newline at end of file diff --git a/src/frontend-common/vulkan_host_display.cpp b/src/frontend-common/vulkan_host_display.cpp index 2450a3fcd..bf7c50c44 100644 --- a/src/frontend-common/vulkan_host_display.cpp +++ b/src/frontend-common/vulkan_host_display.cpp @@ -96,7 +96,22 @@ VulkanHostDisplay::~VulkanHostDisplay() AssertMsg(!m_swap_chain, "Swap chain should have been destroyed by now"); } -bool VulkanHostDisplay::RecreateSwapChain(const WindowInfo& new_wi) +HostDisplay::RenderAPI VulkanHostDisplay::GetRenderAPI() const +{ + return HostDisplay::RenderAPI::Vulkan; +} + +void* VulkanHostDisplay::GetRenderDevice() const +{ + return nullptr; +} + +void* VulkanHostDisplay::GetRenderContext() const +{ + return nullptr; +} + +bool VulkanHostDisplay::ChangeRenderWindow(const WindowInfo& new_wi) { Assert(!m_swap_chain); @@ -115,22 +130,39 @@ bool VulkanHostDisplay::RecreateSwapChain(const WindowInfo& new_wi) return false; } + m_window_info = wi_copy; + m_window_info.surface_width = m_swap_chain->GetWidth(); + m_window_info.surface_height = m_swap_chain->GetHeight(); + + if (ImGui::GetCurrentContext()) + { + ImGui::GetIO().DisplaySize.x = static_cast(m_window_info.surface_width); + ImGui::GetIO().DisplaySize.y = static_cast(m_window_info.surface_height); + } + return true; } -void VulkanHostDisplay::ResizeSwapChain(u32 new_width, u32 new_height) +void VulkanHostDisplay::ResizeRenderWindow(s32 new_window_width, s32 new_window_height) { g_vulkan_context->WaitForGPUIdle(); - if (!m_swap_chain->ResizeSwapChain(new_width, new_height)) + if (!m_swap_chain->ResizeSwapChain(new_window_width, new_window_height)) Panic("Failed to resize swap chain"); - ImGui::GetIO().DisplaySize.x = static_cast(m_swap_chain->GetWidth()); - ImGui::GetIO().DisplaySize.y = static_cast(m_swap_chain->GetHeight()); + m_window_info.surface_width = m_swap_chain->GetWidth(); + m_window_info.surface_height = m_swap_chain->GetHeight(); + + if (ImGui::GetCurrentContext()) + { + ImGui::GetIO().DisplaySize.x = static_cast(m_window_info.surface_width); + ImGui::GetIO().DisplaySize.y = static_cast(m_window_info.surface_height); + } } -void VulkanHostDisplay::DestroySwapChain() +void VulkanHostDisplay::DestroyRenderSurface() { + m_window_info = {}; m_swap_chain.reset(); } @@ -193,27 +225,47 @@ void VulkanHostDisplay::SetVSync(bool enabled) m_swap_chain->SetVSync(enabled); } -bool VulkanHostDisplay::CreateContextAndSwapChain(const WindowInfo& wi, std::string_view gpu_name, bool debug_device) +bool VulkanHostDisplay::CreateRenderDevice(const WindowInfo& wi, std::string_view adapter_name, bool debug_device) { - if (!Vulkan::Context::Create(gpu_name, &wi, &m_swap_chain, debug_device, false)) + if (!Vulkan::Context::Create(adapter_name, &wi, &m_swap_chain, debug_device, false)) { Log_ErrorPrintf("Failed to create Vulkan context"); return false; } + m_window_info = wi; + if (m_swap_chain) + { + m_window_info.surface_width = m_swap_chain->GetWidth(); + m_window_info.surface_height = m_swap_chain->GetHeight(); + } + return true; } -void VulkanHostDisplay::CreateShaderCache(std::string_view shader_cache_directory, bool debug_shaders) +bool VulkanHostDisplay::InitializeRenderDevice(std::string_view shader_cache_directory, bool debug_device) { - Vulkan::ShaderCache::Create(shader_cache_directory, debug_shaders); + Vulkan::ShaderCache::Create(shader_cache_directory, debug_device); + + if (!CreateResources()) + return false; + + if (ImGui::GetCurrentContext() && !CreateImGuiContext()) + return false; + + return true; } -bool VulkanHostDisplay::HasContext() const +bool VulkanHostDisplay::HasRenderDevice() const { return static_cast(g_vulkan_context); } +bool VulkanHostDisplay::HasRenderSurface() const +{ + return static_cast(m_swap_chain); +} + bool VulkanHostDisplay::CreateResources() { static constexpr char fullscreen_quad_vertex_shader[] = R"( @@ -329,24 +381,37 @@ void VulkanHostDisplay::DestroyImGuiContext() ImGui_ImplVulkan_Shutdown(); } -void VulkanHostDisplay::DestroyContext() +void VulkanHostDisplay::DestroyRenderDevice() { if (!g_vulkan_context) return; g_vulkan_context->WaitForGPUIdle(); + + if (ImGui::GetCurrentContext()) + DestroyImGuiContext(); + + DestroyResources(); + + Vulkan::ShaderCache::Destroy(); + DestroyRenderSurface(); Vulkan::Context::Destroy(); } -void VulkanHostDisplay::DestroyShaderCache() +bool VulkanHostDisplay::MakeRenderContextCurrent() { - Vulkan::ShaderCache::Destroy(); + return true; +} + +bool VulkanHostDisplay::DoneRenderContextCurrent() +{ + return true; } bool VulkanHostDisplay::CreateImGuiContext() { - ImGui::GetIO().DisplaySize.x = static_cast(m_swap_chain->GetWidth()); - ImGui::GetIO().DisplaySize.y = static_cast(m_swap_chain->GetHeight()); + ImGui::GetIO().DisplaySize.x = static_cast(m_window_info.surface_width); + ImGui::GetIO().DisplaySize.y = static_cast(m_window_info.surface_height); ImGui_ImplVulkan_InitInfo vii = {}; vii.Instance = g_vulkan_context->GetVulkanInstance(); @@ -370,14 +435,14 @@ bool VulkanHostDisplay::CreateImGuiContext() return true; } -bool VulkanHostDisplay::BeginRender() +bool VulkanHostDisplay::Render() { VkResult res = m_swap_chain->AcquireNextImage(); if (res != VK_SUCCESS) { if (res == VK_SUBOPTIMAL_KHR || res == VK_ERROR_OUT_OF_DATE_KHR) { - ResizeSwapChain(0, 0); + ResizeRenderWindow(0, 0); res = m_swap_chain->AcquireNextImage(); } @@ -409,13 +474,13 @@ bool VulkanHostDisplay::BeginRender() 1u, &clear_value}; vkCmdBeginRenderPass(cmdbuffer, &rp, VK_SUBPASS_CONTENTS_INLINE); - return true; -} -void VulkanHostDisplay::EndRenderAndPresent() -{ - VkCommandBuffer cmdbuffer = g_vulkan_context->GetCurrentCommandBuffer(); - Vulkan::Texture& swap_chain_texture = m_swap_chain->GetCurrentTexture(); + RenderDisplay(); + + if (ImGui::GetCurrentContext()) + RenderImGui(); + + RenderSoftwareCursor(); vkCmdEndRenderPass(cmdbuffer); @@ -426,13 +491,26 @@ void VulkanHostDisplay::EndRenderAndPresent() m_swap_chain->GetCurrentImageIndex()); g_vulkan_context->MoveToNextCommandBuffer(); - ImGui::NewFrame(); - ImGui_ImplVulkan_NewFrame(); + if (ImGui::GetCurrentContext()) + ImGui_ImplVulkan_NewFrame(); + + return true; +} + +void VulkanHostDisplay::RenderDisplay() +{ + if (!HasDisplayTexture()) + return; + + const auto [left, top, width, height] = CalculateDrawRect(GetWindowWidth(), GetWindowHeight(), m_display_top_margin); + 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); } void VulkanHostDisplay::RenderDisplay(s32 left, s32 top, s32 width, s32 height, void* texture_handle, u32 texture_width, - u32 texture_height, u32 texture_view_x, u32 texture_view_y, - u32 texture_view_width, u32 texture_view_height, bool linear_filter) + s32 texture_height, s32 texture_view_x, s32 texture_view_y, + s32 texture_view_width, s32 texture_view_height, bool linear_filter) { VkCommandBuffer cmdbuffer = g_vulkan_context->GetCurrentCommandBuffer(); @@ -469,6 +547,15 @@ void VulkanHostDisplay::RenderImGui() ImGui_ImplVulkan_RenderDrawData(ImGui::GetDrawData(), g_vulkan_context->GetCurrentCommandBuffer()); } +void VulkanHostDisplay::RenderSoftwareCursor() +{ + if (!HasSoftwareCursor()) + return; + + const auto [left, top, width, height] = CalculateSoftwareCursorDrawRect(); + RenderSoftwareCursor(left, top, width, height, m_cursor_texture.get()); +} + void VulkanHostDisplay::RenderSoftwareCursor(s32 left, s32 top, s32 width, s32 height, HostDisplayTexture* texture) { VkCommandBuffer cmdbuffer = g_vulkan_context->GetCurrentCommandBuffer(); diff --git a/src/frontend-common/vulkan_host_display.h b/src/frontend-common/vulkan_host_display.h index 2f1613d70..6df0dfa0b 100644 --- a/src/frontend-common/vulkan_host_display.h +++ b/src/frontend-common/vulkan_host_display.h @@ -14,57 +14,44 @@ class SwapChain; namespace FrontendCommon { -class VulkanHostDisplay +class VulkanHostDisplay : public HostDisplay { public: VulkanHostDisplay(); - ~VulkanHostDisplay(); + virtual ~VulkanHostDisplay(); - ALWAYS_INLINE HostDisplay::RenderAPI GetRenderAPI() const { return HostDisplay::RenderAPI::Vulkan; } - ALWAYS_INLINE void* GetRenderDevice() const { return nullptr; } - ALWAYS_INLINE void* GetRenderContext() const { return nullptr; } + virtual RenderAPI GetRenderAPI() const override; + virtual void* GetRenderDevice() const override; + virtual void* GetRenderContext() const override; - std::unique_ptr CreateTexture(u32 width, u32 height, const void* data, u32 data_stride, - bool dynamic); - void UpdateTexture(HostDisplayTexture* texture, u32 x, u32 y, u32 width, u32 height, const void* data, - u32 data_stride); - bool DownloadTexture(const void* texture_handle, u32 x, u32 y, u32 width, u32 height, void* out_data, - u32 out_data_stride); + virtual bool HasRenderDevice() const override; + virtual bool HasRenderSurface() const override; - void SetVSync(bool enabled); + virtual bool CreateRenderDevice(const WindowInfo& wi, std::string_view adapter_name, bool debug_device) override; + virtual bool InitializeRenderDevice(std::string_view shader_cache_directory, bool debug_device) override; + virtual void DestroyRenderDevice() override; - bool BeginRender(); - void RenderDisplay(s32 left, s32 top, s32 width, s32 height, void* texture_handle, u32 texture_width, - u32 texture_height, u32 texture_view_x, u32 texture_view_y, u32 texture_view_width, - u32 texture_view_height, bool linear_filter); - void RenderImGui(); - void RenderSoftwareCursor(s32 left, s32 top, s32 width, s32 height, HostDisplayTexture* texture_handle); - void EndRenderAndPresent(); + virtual bool MakeRenderContextCurrent() override; + virtual bool DoneRenderContextCurrent() override; - bool CreateContextAndSwapChain(const WindowInfo& wi, std::string_view gpu_name, bool debug_device); - bool HasContext() const; - void DestroyContext(); + virtual bool ChangeRenderWindow(const WindowInfo& new_wi) override; + virtual void ResizeRenderWindow(s32 new_window_width, s32 new_window_height) override; + virtual void DestroyRenderSurface() override; - void CreateShaderCache(std::string_view shader_cache_directory, bool debug_shaders); - void DestroyShaderCache(); - - bool CreateResources(); - void DestroyResources(); - - bool CreateImGuiContext(); - void DestroyImGuiContext(); + 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; - ALWAYS_INLINE u32 GetSwapChainWidth() const { return m_swap_chain->GetWidth(); } - ALWAYS_INLINE u32 GetSwapChainHeight() const { return m_swap_chain->GetHeight(); } - ALWAYS_INLINE bool HasSwapChain() const { return static_cast(m_swap_chain); } + virtual void SetVSync(bool enabled) override; - bool RecreateSwapChain(const WindowInfo& new_wi); - void ResizeSwapChain(u32 new_width, u32 new_height); - void DestroySwapChain(); + virtual bool Render() override; static std::vector EnumerateAdapterNames(); -private: +protected: struct PushConstants { float src_rect_left; @@ -73,6 +60,21 @@ private: float src_rect_height; }; + virtual bool CreateResources(); + virtual void DestroyResources(); + + virtual bool CreateImGuiContext(); + virtual void DestroyImGuiContext(); + + void RenderDisplay(); + void RenderImGui(); + void RenderSoftwareCursor(); + + void RenderDisplay(s32 left, s32 top, s32 width, s32 height, void* texture_handle, u32 texture_width, + s32 texture_height, s32 texture_view_x, s32 texture_view_y, s32 texture_view_width, + s32 texture_view_height, bool linear_filter); + void RenderSoftwareCursor(s32 left, s32 top, s32 width, s32 height, HostDisplayTexture* texture_handle); + std::unique_ptr m_swap_chain; VkDescriptorSetLayout m_descriptor_set_layout = VK_NULL_HANDLE;