HostDisplay: Move most backend logic to FrontendCommon

This commit is contained in:
Connor McLaughlin 2020-06-30 02:46:57 +10:00
parent 84a52a3911
commit 2a38090e7a
43 changed files with 865 additions and 1904 deletions

View file

@ -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

View file

@ -11,7 +11,8 @@ struct WindowInfo
X11,
Wayland,
MacOS,
Android
Android,
Libretro,
};
enum class SurfaceFormat

View file

@ -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<HostDisplayTexture> 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<float>(m_display_width) / static_cast<float>(m_display_height)) / m_display_pixel_aspect_ratio;
(static_cast<float>(m_display_width) / static_cast<float>(m_display_height)) / m_display_aspect_ratio;
const float display_width = static_cast<float>(m_display_width);
const float display_height = static_cast<float>(m_display_height) * y_scale;
const float active_left = static_cast<float>(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<s32>(static_cast<float>(resize_width) / m_display_pixel_aspect_ratio);
resize_height = static_cast<s32>(static_cast<float>(resize_width) / m_display_aspect_ratio);
}
else
{
resize_height = std::abs(m_display_texture_view_height);
resize_width = static_cast<s32>(static_cast<float>(resize_height) * m_display_pixel_aspect_ratio);
resize_width = static_cast<s32>(static_cast<float>(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<float>(m_display_texture_view_width) / static_cast<float>(std::abs(m_display_texture_view_height));
if (ratio > 1.0f)

View file

@ -1,7 +1,9 @@
#pragma once
#include "common/rectangle.h"
#include "common/window_info.h"
#include "types.h"
#include <memory>
#include <string_view>
#include <tuple>
#include <vector>
@ -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<s32>(m_window_info.surface_width); }
ALWAYS_INLINE s32 GetWindowHeight() const { return static_cast<s32>(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<HostDisplayTexture> 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<s32, s32, s32, s32> 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;

View file

@ -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<std::vector<u8>> GetBIOSImage(ConsoleRegion region);
@ -153,7 +153,7 @@ protected:
bool SaveState(const char* filename);
void CreateAudioStream();
HostDisplay* m_display = nullptr;
std::unique_ptr<HostDisplay> m_display;
std::unique_ptr<AudioStream> m_audio_stream;
std::unique_ptr<System> m_system;
Settings m_settings;

View file

@ -463,7 +463,7 @@ const char* Settings::GetControllerTypeDisplayName(ControllerType type)
static std::array<const char*, 4> s_memory_card_type_names = {{"None", "Shared", "PerGame", "PerGameTitle"}};
static std::array<const char*, 4> 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<MemoryCardType> Settings::ParseMemoryCardTypeName(const char* str)
{

View file

@ -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();

View file

@ -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

View file

@ -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<HostDisplayTexture> 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<WindowInfo> wi = getWindowInfo();
if (!wi || !m_interface.CreateContextAndSwapChain(wi.value(), adapter_name.toStdString(),
shouldUseFlipModelSwapChain(), debug_device))
{
return false;
}
m_window_width = static_cast<s32>(m_interface.GetSwapChainWidth());
m_window_height = static_cast<s32>(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<WindowInfo> wi = getWindowInfo();
if (!wi.has_value())
return false;
if (!m_interface.RecreateSwapChain(wi.value(), shouldUseFlipModelSwapChain()))
return false;
m_window_width = static_cast<s32>(m_interface.GetSwapChainWidth());
m_window_height = static_cast<s32>(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<u32>(new_window_width), static_cast<u32>(new_window_height));
m_window_width = static_cast<s32>(m_interface.GetSwapChainWidth());
m_window_height = static_cast<s32>(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();
}

View file

@ -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 <memory>
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<HostDisplayTexture> 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;
};

View file

@ -39,7 +39,6 @@
<ClCompile Include="advancedsettingswidget.cpp" />
<ClCompile Include="audiosettingswidget.cpp" />
<ClCompile Include="consolesettingswidget.cpp" />
<ClCompile Include="d3d11hostdisplay.cpp" />
<ClCompile Include="generalsettingswidget.cpp" />
<ClCompile Include="gpusettingswidget.cpp" />
<ClCompile Include="hotkeysettingswidget.cpp" />
@ -50,19 +49,15 @@
<ClCompile Include="gamepropertiesdialog.cpp" />
<ClCompile Include="main.cpp" />
<ClCompile Include="mainwindow.cpp" />
<ClCompile Include="openglhostdisplay.cpp" />
<ClCompile Include="controllersettingswidget.cpp" />
<ClCompile Include="memorycardsettingswidget.cpp" />
<ClCompile Include="qthostdisplay.cpp" />
<ClCompile Include="qthostinterface.cpp" />
<ClCompile Include="qtprogresscallback.cpp" />
<ClCompile Include="qtsettingsinterface.cpp" />
<ClCompile Include="qtutils.cpp" />
<ClCompile Include="settingsdialog.cpp" />
<ClCompile Include="vulkanhostdisplay.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="vulkanhostdisplay.h" />
<QtMoc Include="aboutdialog.h" />
<QtMoc Include="audiosettingswidget.h" />
<QtMoc Include="controllersettingswidget.h" />
@ -73,9 +68,7 @@
<QtMoc Include="hotkeysettingswidget.h" />
<QtMoc Include="inputbindingwidgets.h" />
<QtMoc Include="advancedsettingswidget.h" />
<ClInclude Include="d3d11hostdisplay.h" />
<QtMoc Include="qtprogresscallback.h" />
<ClInclude Include="qthostdisplay.h" />
<ClInclude Include="resource.h" />
<ClInclude Include="settingwidgetbinder.h" />
<QtMoc Include="consolesettingswidget.h" />
@ -83,25 +76,18 @@
<QtMoc Include="gamelistwidget.h" />
<QtMoc Include="gamepropertiesdialog.h" />
<QtMoc Include="mainwindow.h" />
<ClInclude Include="openglhostdisplay.h" />
<QtMoc Include="qthostinterface.h" />
<ClInclude Include="qtsettingsinterface.h" />
<ClInclude Include="qtutils.h" />
<QtMoc Include="settingsdialog.h" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\dep\glad\glad.vcxproj">
<Project>{43540154-9e1e-409c-834f-b84be5621388}</Project>
</ProjectReference>
<ProjectReference Include="..\..\dep\imgui\imgui.vcxproj">
<Project>{bb08260f-6fbc-46af-8924-090ee71360c6}</Project>
</ProjectReference>
<ProjectReference Include="..\..\dep\minizip\minizip.vcxproj">
<Project>{8bda439c-6358-45fb-9994-2ff083babe06}</Project>
</ProjectReference>
<ProjectReference Include="..\..\dep\vulkan-loader\vulkan-loader.vcxproj">
<Project>{9c8ddeb0-2b8f-4f5f-ba86-127cdf27f035}</Project>
</ProjectReference>
<ProjectReference Include="..\common\common.vcxproj">
<Project>{ee054e08-3799-4a59-a422-18259c105ffd}</Project>
</ProjectReference>

View file

@ -30,9 +30,6 @@
<ClCompile Include="$(IntDir)moc_qtprogresscallback.cpp" />
<ClCompile Include="generalsettingswidget.cpp" />
<ClCompile Include="$(IntDir)moc_generalsettingswidget.cpp" />
<ClCompile Include="qthostdisplay.cpp" />
<ClCompile Include="openglhostdisplay.cpp" />
<ClCompile Include="d3d11hostdisplay.cpp" />
<ClCompile Include="advancedsettingswidget.cpp" />
<ClCompile Include="$(IntDir)moc_advancedsettingswidget.cpp" />
<ClCompile Include="gamepropertiesdialog.cpp" />
@ -43,18 +40,13 @@
<ClCompile Include="$(IntDir)moc_aboutdialog.cpp" />
<ClCompile Include="$(IntDir)moc_controllersettingswidget.cpp" />
<ClCompile Include="$(IntDir)moc_memorycardsettingswidget.cpp" />
<ClCompile Include="$(IntDir)qrc_icons.cpp" />
<ClCompile Include="vulkanhostdisplay.cpp" />
<ClCompile Include="$(IntDir)qrc_resources.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="qtsettingsinterface.h" />
<ClInclude Include="qtutils.h" />
<ClInclude Include="settingwidgetbinder.h" />
<ClInclude Include="qthostdisplay.h" />
<ClInclude Include="d3d11hostdisplay.h" />
<ClInclude Include="openglhostdisplay.h" />
<ClInclude Include="resource.h" />
<ClInclude Include="vulkanhostdisplay.h" />
</ItemGroup>
<ItemGroup>
<Filter Include="resources">
@ -62,7 +54,7 @@
</Filter>
</ItemGroup>
<ItemGroup>
<QtResource Include="resources\icons.qrc" />
<QtResource Include="resources\resources.qrc" />
</ItemGroup>
<ItemGroup>
<QtMoc Include="consolesettingswidget.h" />

View file

@ -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 <QtGui/QCursor>
#include <QtGui/QWindowStateChangeEvent>
#include <QtWidgets/QFileDialog>
#include <QtWidgets/QStyleFactory>
#include <QtWidgets/QMessageBox>
#include <QtWidgets/QStyleFactory>
#include <cmath>
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<WindowInfo> 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<WindowInfo> 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()

View file

@ -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;

View file

@ -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 <glad.h>
// 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 <memory>
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<HostDisplayTexture> 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<GL::Context> 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;
};

View file

@ -1,8 +1,8 @@
#include "qtdisplaywidget.h"
#include "common/bitutils.h"
#include "qthostdisplay.h"
#include "qthostinterface.h"
#include "qtutils.h"
#include <QtCore/QDebug>
#include <QtGui/QGuiApplication>
#include <QtGui/QKeyEvent>
#include <QtGui/QScreen>
@ -10,6 +10,10 @@
#include <QtGui/QWindowStateChangeEvent>
#include <cmath>
#if !defined(WIN32) && !defined(APPLE)
#include <qpa/qplatformnativeinterface.h>
#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<int>(std::ceil(static_cast<qreal>(height()) * devicePixelRatioFromScreen()));
}
std::optional<WindowInfo> 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<void*>(winId());
#elif defined(__APPLE__)
wi.type = WindowInfo::Type::MacOS;
wi.window_handle = reinterpret_cast<void*>(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<void*>(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;

View file

@ -1,6 +1,8 @@
#pragma once
#include "common/types.h"
#include "common/window_info.h"
#include <QtWidgets/QWidget>
#include <optional>
class QtDisplayWidget final : public QWidget
{
@ -16,6 +18,8 @@ public:
int scaledWindowHeight() const;
qreal devicePixelRatioFromScreen() const;
std::optional<WindowInfo> getWindowInfo() const;
Q_SIGNALS:
void windowResizedEvent(int width, int height);
void windowRestoredEvent();

View file

@ -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 <QtGui/QGuiApplication>
#include <QtCore/QDebug>
#include <cmath>
#if !defined(WIN32) && !defined(APPLE)
#include <qpa/qplatformnativeinterface.h>
#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<float>(m_window_width);
io.DisplaySize.y = static_cast<float>(m_window_height);
const float framebuffer_scale = static_cast<float>(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<float>(m_window_width);
io.DisplaySize.y = static_cast<float>(m_window_height);
}
std::optional<WindowInfo> 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<void*>(m_widget->winId());
#elif defined(__APPLE__)
wi.type = WindowInfo::Type::MacOS;
wi.window_handle = reinterpret_cast<void*>(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<void*>(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;
}

View file

@ -1,50 +0,0 @@
#pragma once
#include "common/types.h"
#include "common/window_info.h"
#include "core/host_display.h"
#include <optional>
#include <string_view>
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<WindowInfo> getWindowInfo() const;
QtHostInterface* m_host_interface;
QtDisplayWidget* m_widget = nullptr;
};

View file

@ -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 <QtCore/QCoreApplication>
#include <QtCore/QDateTime>
#include <QtCore/QDebug>
@ -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<QtHostDisplay*>(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<FrontendCommon::VulkanHostDisplay>();
break;
case GPURenderer::HardwareOpenGL:
#ifndef WIN32
default:
#endif
m_display = new OpenGLHostDisplay(this);
m_display = std::make_unique<FrontendCommon::OpenGLHostDisplay>();
break;
#ifdef WIN32
case GPURenderer::HardwareD3D11:
default:
m_display = new D3D11HostDisplay(this);
m_display = std::make_unique<FrontendCommon::D3D11HostDisplay>();
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;

View file

@ -5,6 +5,7 @@
#include "frontend-common/common_host_interface.h"
#include <QtCore/QByteArray>
#include <QtCore/QObject>
#include <QtCore/QString>
#include <QtCore/QSettings>
#include <QtCore/QThread>
#include <atomic>
@ -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();

View file

@ -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<HostDisplayTexture> 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<WindowInfo> wi = getWindowInfo();
if (!wi || !m_vulkan_display.CreateContextAndSwapChain(wi.value(), adapter_name.toStdString(), debug_device))
return false;
m_window_width = static_cast<s32>(m_vulkan_display.GetSwapChainWidth());
m_window_height = static_cast<s32>(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<WindowInfo> wi = getWindowInfo();
if (!wi.has_value())
return false;
if (!m_vulkan_display.RecreateSwapChain(wi.value()))
return false;
m_window_width = static_cast<s32>(m_vulkan_display.GetSwapChainWidth());
m_window_height = static_cast<s32>(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<u32>(new_window_width), static_cast<u32>(new_window_height));
m_window_width = static_cast<s32>(m_vulkan_display.GetSwapChainWidth());
m_window_height = static_cast<s32>(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();
}

View file

@ -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 <memory>
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<HostDisplayTexture> 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;
};

View file

@ -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
)

View file

@ -55,23 +55,17 @@
</ProjectReference>
</ItemGroup>
<ItemGroup>
<ClCompile Include="sdl_d3d11_host_display.cpp" />
<ClCompile Include="imgui_impl_sdl.cpp" />
<ClCompile Include="opengl_host_display.cpp" />
<ClCompile Include="sdl_host_interface.cpp" />
<ClCompile Include="main.cpp" />
<ClCompile Include="sdl_util.cpp" />
<ClCompile Include="sdl_vulkan_host_display.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="sdl_d3d11_host_display.h" />
<ClInclude Include="imgui_impl_sdl.h" />
<ClInclude Include="opengl_host_display.h" />
<ClInclude Include="resource.h" />
<ClInclude Include="sdl_host_interface.h" />
<ClInclude Include="sdl_key_names.h" />
<ClInclude Include="sdl_util.h" />
<ClInclude Include="sdl_vulkan_host_display.h" />
</ItemGroup>
<ItemGroup>
<Manifest Include="duckstation-sdl.manifest" />

View file

@ -2,22 +2,16 @@
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<ClCompile Include="main.cpp" />
<ClCompile Include="opengl_host_display.cpp" />
<ClCompile Include="sdl_host_interface.cpp" />
<ClCompile Include="imgui_impl_sdl.cpp" />
<ClCompile Include="sdl_util.cpp" />
<ClCompile Include="sdl_vulkan_host_display.cpp" />
<ClCompile Include="sdl_d3d11_host_display.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="opengl_host_display.h" />
<ClInclude Include="sdl_host_interface.h" />
<ClInclude Include="imgui_impl_sdl.h" />
<ClInclude Include="sdl_key_names.h" />
<ClInclude Include="resource.h" />
<ClInclude Include="sdl_util.h" />
<ClInclude Include="sdl_vulkan_host_display.h" />
<ClInclude Include="sdl_d3d11_host_display.h" />
</ItemGroup>
<ItemGroup>
<Manifest Include="duckstation-sdl.manifest" />

View file

@ -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 <SDL_syswm.h>
#include <array>
#include <imgui.h>
#include <imgui_impl_opengl3.h>
#include <tuple>
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<void*>(static_cast<uintptr_t>(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<OpenGLDisplayWidgetTexture> 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<OpenGLDisplayWidgetTexture>(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<u32>(new_window_width), static_cast<u32>(new_window_height));
m_window_width = static_cast<s32>(m_gl_context->GetSurfaceWidth());
m_window_height = static_cast<s32>(m_gl_context->GetSurfaceHeight());
ImGui::GetIO().DisplaySize.x = static_cast<float>(m_window_width);
ImGui::GetIO().DisplaySize.y = static_cast<float>(m_window_height);
}
std::unique_ptr<HostDisplayTexture> 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<OpenGLDisplayWidgetTexture*>(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<GLuint>(reinterpret_cast<uintptr_t>(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, &current_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<WindowInfo> 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<s32>(m_gl_context->GetSurfaceWidth());
m_window_height = static_cast<s32>(m_gl_context->GetSurfaceHeight());
// start with vsync on
m_gl_context->SetSwapInterval(1);
return true;
}
bool OpenGLHostDisplay::CreateImGuiContext()
{
ImGui::GetIO().DisplaySize.x = static_cast<float>(m_window_width);
ImGui::GetIO().DisplaySize.y = static_cast<float>(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<HostDisplay> OpenGLHostDisplay::Create(SDL_Window* window, bool debug_device)
{
std::unique_ptr<OpenGLHostDisplay> display = std::make_unique<OpenGLHostDisplay>(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<float>(m_display_texture_view_x) / static_cast<float>(m_display_texture_width),
static_cast<float>(m_display_texture_view_y) / static_cast<float>(m_display_texture_height),
(static_cast<float>(m_display_texture_view_width) - 0.5f) / static_cast<float>(m_display_texture_width),
(static_cast<float>(m_display_texture_view_height) + 0.5f) / static_cast<float>(m_display_texture_height));
glBindTexture(GL_TEXTURE_2D, static_cast<GLuint>(reinterpret_cast<uintptr_t>(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);
}

View file

@ -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 <SDL.h>
#include <memory>
#include <string>
class OpenGLHostDisplay final : public HostDisplay
{
public:
OpenGLHostDisplay(SDL_Window* window);
~OpenGLHostDisplay();
static std::unique_ptr<HostDisplay> 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<HostDisplayTexture> 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<GL::Context> 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;
};

View file

@ -1,121 +0,0 @@
#include "sdl_d3d11_host_display.h"
#include "imgui_impl_sdl.h"
#include "sdl_util.h"
#include <SDL_syswm.h>
#include <array>
#include <dxgi1_5.h>
#include <imgui.h>
#include <imgui_impl_dx11.h>
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<HostDisplay> SDLD3D11HostDisplay::Create(SDL_Window* window, std::string_view adapter_name, bool debug_device)
{
std::unique_ptr<SDLD3D11HostDisplay> display = std::make_unique<SDLD3D11HostDisplay>(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<u32>(new_window_width), static_cast<u32>(new_window_height));
HostDisplay::WindowResized(static_cast<s32>(m_interface.GetSwapChainWidth()),
static_cast<s32>(m_interface.GetSwapChainHeight()));
ImGui::GetIO().DisplaySize.x = static_cast<float>(m_interface.GetSwapChainWidth());
ImGui::GetIO().DisplaySize.y = static_cast<float>(m_interface.GetSwapChainHeight());
}
std::unique_ptr<HostDisplayTexture> 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<WindowInfo> 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);
}

View file

@ -1,39 +0,0 @@
#pragma once
#include "core/host_display.h"
#include "frontend-common/d3d11_host_display.h"
#include <SDL.h>
class SDLD3D11HostDisplay final : public HostDisplay
{
public:
template<typename T>
using ComPtr = Microsoft::WRL::ComPtr<T>;
SDLD3D11HostDisplay(SDL_Window* window);
~SDLD3D11HostDisplay();
static std::unique_ptr<HostDisplay> 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<HostDisplayTexture> 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);
};

View file

@ -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 <cinttypes>
#include <cmath>
#include <imgui.h>
@ -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<HostDisplay> display;
std::optional<WindowInfo> wi = SDLUtil::GetWindowInfoForSDLWindow(m_window);
if (!wi.has_value())
{
ReportError("Failed to get window info from SDL window");
return false;
}
std::unique_ptr<HostDisplay> 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<FrontendCommon::VulkanHostDisplay>();
break;
case GPURenderer::HardwareOpenGL:
#ifndef WIN32
default:
#endif
display = OpenGLHostDisplay::Create(m_window, m_settings.gpu_use_debug_device);
display = std::make_unique<FrontendCommon::OpenGLHostDisplay>();
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<FrontendCommon::D3D11HostDisplay>();
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<int>(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)
{

View file

@ -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 <SDL_syswm.h>
#include <array>
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<HostDisplay> SDLVulkanHostDisplay::Create(SDL_Window* window, std::string_view adapter_name,
std::string_view shader_cache_directory, bool debug_device)
{
std::unique_ptr<SDLVulkanHostDisplay> display = std::make_unique<SDLVulkanHostDisplay>(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<u32>(new_window_width), static_cast<u32>(new_window_height));
HostDisplay::WindowResized(static_cast<s32>(m_display.GetSwapChainWidth()),
static_cast<s32>(m_display.GetSwapChainHeight()));
ImGui::GetIO().DisplaySize.x = static_cast<float>(m_display.GetSwapChainWidth());
ImGui::GetIO().DisplaySize.y = static_cast<float>(m_display.GetSwapChainHeight());
}
std::unique_ptr<HostDisplayTexture> 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<WindowInfo> 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);
}

View file

@ -1,40 +0,0 @@
#pragma once
#include "core/host_display.h"
#include "frontend-common/vulkan_host_display.h"
#include <SDL.h>
#include <string_view>
class SDLVulkanHostDisplay final : public HostDisplay
{
public:
SDLVulkanHostDisplay(SDL_Window* window);
~SDLVulkanHostDisplay();
static std::unique_ptr<HostDisplay> 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<HostDisplayTexture> 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;
};

View file

@ -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()

View file

@ -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)

View file

@ -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<bool>(m_device);
}
bool D3D11HostDisplay::HasRenderSurface() const
{
return static_cast<bool>(m_swap_chain);
}
std::unique_ptr<HostDisplayTexture> 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<bool>(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<HWND>(new_wi.window_handle);
const HWND window_hwnd = reinterpret_cast<HWND>(m_window_info.window_handle);
RECT client_rc{};
GetClientRect(window_hwnd, &client_rc);
const u32 width = static_cast<u32>(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<float>(backbuffer_desc.Width);
ImGui::GetIO().DisplaySize.y = static_cast<float>(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<float>(m_swap_chain_width);
ImGui::GetIO().DisplaySize.y = static_cast<float>(m_swap_chain_height);
ImGui::GetIO().DisplaySize.x = static_cast<float>(m_window_info.surface_width);
ImGui::GetIO().DisplaySize.y = static_cast<float>(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<float, 4> 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<std::string> D3D11HostDisplay::EnumerateAdapterNames(IDXGIFactory* d
return adapter_names;
}
bool D3D11HostDisplay::UseFlipModelSwapChain() const
{
return true;
}
} // namespace FrontendCommon

View file

@ -15,7 +15,7 @@
namespace FrontendCommon {
class D3D11HostDisplay final
class D3D11HostDisplay : public HostDisplay
{
public:
template<typename T>
@ -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<bool>(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<HostDisplayTexture> 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<std::string> EnumerateAdapterNames();
private:
protected:
static constexpr u32 DISPLAY_UNIFORM_BUFFER_SIZE = 16;
virtual bool UseFlipModelSwapChain() const;
static std::vector<std::string> 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<IDXGIFactory> m_dxgi_factory;
ComPtr<ID3D11Device> 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;

View file

@ -44,6 +44,9 @@
<ProjectReference Include="..\..\dep\glad\glad.vcxproj">
<Project>{43540154-9e1e-409c-834f-b84be5621388}</Project>
</ProjectReference>
<ProjectReference Include="..\..\dep\imgui\imgui.vcxproj">
<Project>{bb08260f-6fbc-46af-8924-090ee71360c6}</Project>
</ProjectReference>
<ProjectReference Include="..\..\dep\libcue\libcue.vcxproj">
<Project>{6a4208ed-e3dc-41e1-81cd-f61025fc285a}</Project>
</ProjectReference>
@ -67,6 +70,7 @@
<ClCompile Include="icon.cpp" />
<ClCompile Include="imgui_styles.cpp" />
<ClCompile Include="ini_settings_interface.cpp" />
<ClCompile Include="opengl_host_display.cpp" />
<ClCompile Include="save_state_selector_ui.cpp" />
<ClCompile Include="sdl_audio_stream.cpp" />
<ClCompile Include="sdl_controller_interface.cpp" />
@ -80,6 +84,7 @@
<ClInclude Include="icon.h" />
<ClInclude Include="imgui_styles.h" />
<ClInclude Include="ini_settings_interface.h" />
<ClInclude Include="opengl_host_display.h" />
<ClInclude Include="save_state_selector_ui.h" />
<ClInclude Include="sdl_audio_stream.h" />
<ClInclude Include="sdl_controller_interface.h" />
@ -234,7 +239,7 @@
<PreprocessorDefinitions>WITH_SDL2=1;WITH_DISCORD_PRESENCE=1;WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<AdditionalIncludeDirectories>$(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)</AdditionalIncludeDirectories>
<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)</AdditionalIncludeDirectories>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
<LanguageStandard>stdcpp17</LanguageStandard>
<ConformanceMode>true</ConformanceMode>
@ -261,7 +266,7 @@
<PreprocessorDefinitions>WITH_SDL2=1;WITH_DISCORD_PRESENCE=1;_ITERATOR_DEBUG_LEVEL=1;WIN32;_DEBUGFAST;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<AdditionalIncludeDirectories>$(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)</AdditionalIncludeDirectories>
<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)</AdditionalIncludeDirectories>
<BasicRuntimeChecks>Default</BasicRuntimeChecks>
<SupportJustMyCode>false</SupportJustMyCode>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
@ -291,7 +296,7 @@
<PreprocessorDefinitions>WITH_SDL2=1;WITH_DISCORD_PRESENCE=1;WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<AdditionalIncludeDirectories>$(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)</AdditionalIncludeDirectories>
<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)</AdditionalIncludeDirectories>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
<LanguageStandard>stdcpp17</LanguageStandard>
<ConformanceMode>true</ConformanceMode>
@ -318,7 +323,7 @@
<PreprocessorDefinitions>WITH_SDL2=1;WITH_DISCORD_PRESENCE=1;_ITERATOR_DEBUG_LEVEL=1;WIN32;_DEBUGFAST;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<AdditionalIncludeDirectories>$(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)</AdditionalIncludeDirectories>
<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)</AdditionalIncludeDirectories>
<BasicRuntimeChecks>Default</BasicRuntimeChecks>
<SupportJustMyCode>false</SupportJustMyCode>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
@ -349,7 +354,7 @@
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>WITH_SDL2=1;WITH_DISCORD_PRESENCE=1;WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
<AdditionalIncludeDirectories>$(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)</AdditionalIncludeDirectories>
<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)</AdditionalIncludeDirectories>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
<LanguageStandard>stdcpp17</LanguageStandard>
<WholeProgramOptimization>false</WholeProgramOptimization>
@ -378,7 +383,7 @@
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>WITH_SDL2=1;WITH_DISCORD_PRESENCE=1;WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
<AdditionalIncludeDirectories>$(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)</AdditionalIncludeDirectories>
<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)</AdditionalIncludeDirectories>
<OmitFramePointers>true</OmitFramePointers>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
<LanguageStandard>stdcpp17</LanguageStandard>
@ -409,7 +414,7 @@
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>WITH_SDL2=1;WITH_DISCORD_PRESENCE=1;WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
<AdditionalIncludeDirectories>$(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)</AdditionalIncludeDirectories>
<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)</AdditionalIncludeDirectories>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
<LanguageStandard>stdcpp17</LanguageStandard>
<WholeProgramOptimization>false</WholeProgramOptimization>
@ -438,7 +443,7 @@
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>WITH_SDL2=1;WITH_DISCORD_PRESENCE=1;WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
<AdditionalIncludeDirectories>$(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)</AdditionalIncludeDirectories>
<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)</AdditionalIncludeDirectories>
<OmitFramePointers>true</OmitFramePointers>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
<LanguageStandard>stdcpp17</LanguageStandard>

View file

@ -12,6 +12,7 @@
<ClCompile Include="save_state_selector_ui.cpp" />
<ClCompile Include="vulkan_host_display.cpp" />
<ClCompile Include="d3d11_host_display.cpp" />
<ClCompile Include="opengl_host_display.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="icon.h" />
@ -25,6 +26,7 @@
<ClInclude Include="save_state_selector_ui.h" />
<ClInclude Include="vulkan_host_display.h" />
<ClInclude Include="d3d11_host_display.h" />
<ClInclude Include="opengl_host_display.h" />
</ItemGroup>
<ItemGroup>
<None Include="font_roboto_regular.inl" />

View file

@ -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 <QtGui/QKeyEvent>
#include <QtGui/QWindow>
#include <array>
#include <imgui_impl_opengl3.h>
#include <tuple>
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<void*>(static_cast<uintptr_t>(m_id)); }
u32 GetWidth() const override { return m_width; }
@ -23,8 +21,8 @@ public:
GLuint GetGLID() const { return m_id; }
static std::unique_ptr<OpenGLDisplayWidgetTexture> Create(u32 width, u32 height, const void* initial_data,
u32 initial_data_stride)
static std::unique_ptr<OpenGLHostDisplayTexture> 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<OpenGLDisplayWidgetTexture>(id, width, height);
return std::make_unique<OpenGLHostDisplayTexture>(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<u32>(new_window_width), static_cast<u32>(new_window_height));
}
std::unique_ptr<HostDisplayTexture> 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<OpenGLDisplayWidgetTexture*>(texture);
OpenGLHostDisplayTexture* tex = static_cast<OpenGLHostDisplayTexture*>(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<bool>(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<WindowInfo> 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<WindowInfo> wi = getWindowInfo();
if (!wi.has_value() || !m_gl_context->ChangeSurface(wi.value()))
return false;
ImGui::GetIO().DisplaySize.x = static_cast<float>(m_window_info.surface_width);
ImGui::GetIO().DisplaySize.y = static_cast<float>(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<u32>(new_window_width), static_cast<u32>(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<float>(m_window_info.surface_width);
ImGui::GetIO().DisplaySize.y = static_cast<float>(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<float>(m_window_info.surface_width);
ImGui::GetIO().DisplaySize.y = static_cast<float>(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<float>(m_display_texture_view_x) / static_cast<float>(m_display_texture_width),
static_cast<float>(m_display_texture_view_y) / static_cast<float>(m_display_texture_height),
(static_cast<float>(m_display_texture_view_width) - 0.5f) / static_cast<float>(m_display_texture_width),
(static_cast<float>(m_display_texture_view_height) + 0.5f) / static_cast<float>(m_display_texture_height));
glBindTexture(GL_TEXTURE_2D, static_cast<GLuint>(reinterpret_cast<uintptr_t>(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<float>(texture_view_x) / static_cast<float>(texture_width),
static_cast<float>(texture_view_y) / static_cast<float>(texture_height),
(static_cast<float>(texture_view_width) - 0.5f) / static_cast<float>(texture_width),
(static_cast<float>(texture_view_height) + 0.5f) / static_cast<float>(texture_height));
glBindTexture(GL_TEXTURE_2D, static_cast<GLuint>(reinterpret_cast<uintptr_t>(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<OpenGLHostDisplayTexture*>(texture_handle)->GetGLID());
glBindSampler(0, m_display_linear_sampler);
glBindVertexArray(m_display_vao);
glDrawArrays(GL_TRIANGLES, 0, 3);
glBindSampler(0, 0);
}
} // namespace FrontendCommon

View file

@ -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 <glad.h>
// 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 <memory>
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<HostDisplayTexture> 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<GL::Context> 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

View file

@ -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<float>(m_window_info.surface_width);
ImGui::GetIO().DisplaySize.y = static_cast<float>(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<float>(m_swap_chain->GetWidth());
ImGui::GetIO().DisplaySize.y = static_cast<float>(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<float>(m_window_info.surface_width);
ImGui::GetIO().DisplaySize.y = static_cast<float>(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<bool>(g_vulkan_context);
}
bool VulkanHostDisplay::HasRenderSurface() const
{
return static_cast<bool>(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<float>(m_swap_chain->GetWidth());
ImGui::GetIO().DisplaySize.y = static_cast<float>(m_swap_chain->GetHeight());
ImGui::GetIO().DisplaySize.x = static_cast<float>(m_window_info.surface_width);
ImGui::GetIO().DisplaySize.y = static_cast<float>(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();

View file

@ -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<HostDisplayTexture> 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<HostDisplayTexture> 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<bool>(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<std::string> 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<Vulkan::SwapChain> m_swap_chain;
VkDescriptorSetLayout m_descriptor_set_layout = VK_NULL_HANDLE;