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 870 additions and 1909 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.ActiveCfg = ReleaseLTCG|Win32
{4266505B-DBAF-484B-AB31-B53B9C8235B3}.ReleaseLTCG|x86.Build.0 = 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.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.ActiveCfg = Debug|Win32
{9D206548-DE8F-4D9D-A561-C7E5CD7A20DF}.Debug|x86.Build.0 = 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.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.ActiveCfg = DebugFast|Win32
{9D206548-DE8F-4D9D-A561-C7E5CD7A20DF}.DebugFast|x86.Build.0 = 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.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.ActiveCfg = Release|Win32
{9D206548-DE8F-4D9D-A561-C7E5CD7A20DF}.Release|x86.Build.0 = 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.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.ActiveCfg = ReleaseLTCG|Win32
{9D206548-DE8F-4D9D-A561-C7E5CD7A20DF}.ReleaseLTCG|x86.Build.0 = ReleaseLTCG|Win32 {9D206548-DE8F-4D9D-A561-C7E5CD7A20DF}.ReleaseLTCG|x86.Build.0 = ReleaseLTCG|Win32
{7F909E29-4808-4BD9-A60C-56C51A3AAEC2}.Debug|x64.ActiveCfg = Debug|x64 {7F909E29-4808-4BD9-A60C-56C51A3AAEC2}.Debug|x64.ActiveCfg = Debug|x64

View File

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

View File

@ -13,12 +13,6 @@ HostDisplayTexture::~HostDisplayTexture() = default;
HostDisplay::~HostDisplay() = 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*/) void HostDisplay::SetSoftwareCursor(std::unique_ptr<HostDisplayTexture> texture, float scale /*= 1.0f*/)
{ {
m_cursor_texture = std::move(texture); 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 float* out_y_scale) const
{ {
const float y_scale = 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_width = static_cast<float>(m_display_width);
const float display_height = static_cast<float>(m_display_height) * y_scale; const float display_height = static_cast<float>(m_display_height) * y_scale;
const float active_left = static_cast<float>(m_display_active_left); 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; s32 resize_height = 0;
if (apply_aspect_ratio && full_resolution) 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_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 else
{ {
resize_height = std::abs(m_display_texture_view_height); 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) 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_width = right - left;
resize_height = bottom - top; resize_height = bottom - top;
} }
else if (!full_resolution) 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 = const float ratio =
static_cast<float>(m_display_texture_view_width) / static_cast<float>(std::abs(m_display_texture_view_height)); static_cast<float>(m_display_texture_view_width) / static_cast<float>(std::abs(m_display_texture_view_height));
if (ratio > 1.0f) if (ratio > 1.0f)

View File

@ -1,7 +1,9 @@
#pragma once #pragma once
#include "common/rectangle.h" #include "common/rectangle.h"
#include "common/window_info.h"
#include "types.h" #include "types.h"
#include <memory> #include <memory>
#include <string_view>
#include <tuple> #include <tuple>
#include <vector> #include <vector>
@ -31,8 +33,8 @@ public:
virtual ~HostDisplay(); virtual ~HostDisplay();
ALWAYS_INLINE s32 GetWindowWidth() const { return m_window_width; } ALWAYS_INLINE s32 GetWindowWidth() const { return static_cast<s32>(m_window_info.surface_width); }
ALWAYS_INLINE s32 GetWindowHeight() const { return m_window_height; } 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. // Position is relative to the top-left corner of the window.
ALWAYS_INLINE s32 GetMousePositionX() const { return m_mouse_position_x; } ALWAYS_INLINE s32 GetMousePositionX() const { return m_mouse_position_x; }
@ -47,8 +49,19 @@ public:
virtual void* GetRenderDevice() const = 0; virtual void* GetRenderDevice() const = 0;
virtual void* GetRenderContext() 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. /// 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. /// 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, 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, virtual bool DownloadTexture(const void* texture_handle, u32 x, u32 y, u32 width, u32 height, void* out_data,
u32 out_data_stride) = 0; 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; 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, 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_width = display_width;
m_display_height = display_height; m_display_height = display_height;
@ -99,7 +113,7 @@ public:
m_display_active_top = active_top; m_display_active_top = active_top;
m_display_active_width = active_width; m_display_active_width = active_width;
m_display_active_height = active_height; 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; m_display_changed = true;
} }
@ -147,8 +161,7 @@ protected:
std::tuple<s32, s32, s32, s32> CalculateSoftwareCursorDrawRect() const; std::tuple<s32, s32, s32, s32> CalculateSoftwareCursorDrawRect() const;
s32 m_window_width = 0; WindowInfo m_window_info;
s32 m_window_height = 0;
s32 m_mouse_position_x = 0; s32 m_mouse_position_x = 0;
s32 m_mouse_position_y = 0; s32 m_mouse_position_y = 0;
@ -159,7 +172,7 @@ protected:
s32 m_display_active_top = 0; s32 m_display_active_top = 0;
s32 m_display_active_width = 0; s32 m_display_active_width = 0;
s32 m_display_active_height = 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; void* m_display_texture_handle = nullptr;
s32 m_display_texture_width = 0; s32 m_display_texture_width = 0;

View File

@ -39,7 +39,7 @@ public:
virtual ~HostInterface(); virtual ~HostInterface();
/// Access to host display. /// 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. /// Access to host audio stream.
ALWAYS_INLINE AudioStream* GetAudioStream() const { return m_audio_stream.get(); } ALWAYS_INLINE AudioStream* GetAudioStream() const { return m_audio_stream.get(); }
@ -96,6 +96,12 @@ public:
/// Retrieves information about specified game from game list. /// Retrieves information about specified game from game list.
virtual void GetGameInfo(const char* path, CDImage* image, std::string* code, std::string* title); 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(). /// Enables the software cursor. Can be called multiple times, but must be matched by a call to DisableSoftwareCursor().
void EnableSoftwareCursor(); void EnableSoftwareCursor();
@ -135,12 +141,6 @@ protected:
/// Sets the user directory to the program directory, i.e. "portable mode". /// Sets the user directory to the program directory, i.e. "portable mode".
void SetUserDirectoryToProgramDirectory(); 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. /// Loads the BIOS image for the specified region.
std::optional<std::vector<u8>> GetBIOSImage(ConsoleRegion region); std::optional<std::vector<u8>> GetBIOSImage(ConsoleRegion region);
@ -153,7 +153,7 @@ protected:
bool SaveState(const char* filename); bool SaveState(const char* filename);
void CreateAudioStream(); void CreateAudioStream();
HostDisplay* m_display = nullptr; std::unique_ptr<HostDisplay> m_display;
std::unique_ptr<AudioStream> m_audio_stream; std::unique_ptr<AudioStream> m_audio_stream;
std::unique_ptr<System> m_system; std::unique_ptr<System> m_system;
Settings m_settings; 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_names = {{"None", "Shared", "PerGame", "PerGameTitle"}};
static std::array<const char*, 4> s_memory_card_type_display_names = {{"No Memory Card", "Shared Between All Games", 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)", "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) std::optional<MemoryCardType> Settings::ParseMemoryCardTypeName(const char* str)
{ {

View File

@ -45,6 +45,12 @@ struct SystemBootParameters
class System class System
{ {
public: 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; friend TimingEvent;
~System(); ~System();

View File

@ -41,10 +41,6 @@ add_executable(duckstation-qt
mainwindow.ui mainwindow.ui
memorycardsettingswidget.cpp memorycardsettingswidget.cpp
memorycardsettingswidget.h memorycardsettingswidget.h
openglhostdisplay.cpp
openglhostdisplay.h
qthostdisplay.cpp
qthostdisplay.h
qtdisplaywidget.cpp qtdisplaywidget.cpp
qtdisplaywidget.h qtdisplaywidget.h
qthostinterface.cpp qthostinterface.cpp
@ -58,19 +54,12 @@ add_executable(duckstation-qt
settingsdialog.cpp settingsdialog.cpp
settingsdialog.h settingsdialog.h
settingsdialog.ui settingsdialog.ui
vulkanhostdisplay.cpp
vulkanhostdisplay.h
) )
target_include_directories(duckstation-qt PRIVATE "${Qt5Gui_PRIVATE_INCLUDE_DIRS}") 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) 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) if(WIN32)
target_sources(duckstation-qt PRIVATE
d3d11hostdisplay.cpp
d3d11hostdisplay.h
)
# We want a Windows subsystem application not console. # We want a Windows subsystem application not console.
set_target_properties(duckstation-qt PROPERTIES set_target_properties(duckstation-qt PROPERTIES
WIN32_EXECUTABLE TRUE 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="advancedsettingswidget.cpp" />
<ClCompile Include="audiosettingswidget.cpp" /> <ClCompile Include="audiosettingswidget.cpp" />
<ClCompile Include="consolesettingswidget.cpp" /> <ClCompile Include="consolesettingswidget.cpp" />
<ClCompile Include="d3d11hostdisplay.cpp" />
<ClCompile Include="generalsettingswidget.cpp" /> <ClCompile Include="generalsettingswidget.cpp" />
<ClCompile Include="gpusettingswidget.cpp" /> <ClCompile Include="gpusettingswidget.cpp" />
<ClCompile Include="hotkeysettingswidget.cpp" /> <ClCompile Include="hotkeysettingswidget.cpp" />
@ -50,19 +49,15 @@
<ClCompile Include="gamepropertiesdialog.cpp" /> <ClCompile Include="gamepropertiesdialog.cpp" />
<ClCompile Include="main.cpp" /> <ClCompile Include="main.cpp" />
<ClCompile Include="mainwindow.cpp" /> <ClCompile Include="mainwindow.cpp" />
<ClCompile Include="openglhostdisplay.cpp" />
<ClCompile Include="controllersettingswidget.cpp" /> <ClCompile Include="controllersettingswidget.cpp" />
<ClCompile Include="memorycardsettingswidget.cpp" /> <ClCompile Include="memorycardsettingswidget.cpp" />
<ClCompile Include="qthostdisplay.cpp" />
<ClCompile Include="qthostinterface.cpp" /> <ClCompile Include="qthostinterface.cpp" />
<ClCompile Include="qtprogresscallback.cpp" /> <ClCompile Include="qtprogresscallback.cpp" />
<ClCompile Include="qtsettingsinterface.cpp" /> <ClCompile Include="qtsettingsinterface.cpp" />
<ClCompile Include="qtutils.cpp" /> <ClCompile Include="qtutils.cpp" />
<ClCompile Include="settingsdialog.cpp" /> <ClCompile Include="settingsdialog.cpp" />
<ClCompile Include="vulkanhostdisplay.cpp" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="vulkanhostdisplay.h" />
<QtMoc Include="aboutdialog.h" /> <QtMoc Include="aboutdialog.h" />
<QtMoc Include="audiosettingswidget.h" /> <QtMoc Include="audiosettingswidget.h" />
<QtMoc Include="controllersettingswidget.h" /> <QtMoc Include="controllersettingswidget.h" />
@ -73,9 +68,7 @@
<QtMoc Include="hotkeysettingswidget.h" /> <QtMoc Include="hotkeysettingswidget.h" />
<QtMoc Include="inputbindingwidgets.h" /> <QtMoc Include="inputbindingwidgets.h" />
<QtMoc Include="advancedsettingswidget.h" /> <QtMoc Include="advancedsettingswidget.h" />
<ClInclude Include="d3d11hostdisplay.h" />
<QtMoc Include="qtprogresscallback.h" /> <QtMoc Include="qtprogresscallback.h" />
<ClInclude Include="qthostdisplay.h" />
<ClInclude Include="resource.h" /> <ClInclude Include="resource.h" />
<ClInclude Include="settingwidgetbinder.h" /> <ClInclude Include="settingwidgetbinder.h" />
<QtMoc Include="consolesettingswidget.h" /> <QtMoc Include="consolesettingswidget.h" />
@ -83,25 +76,18 @@
<QtMoc Include="gamelistwidget.h" /> <QtMoc Include="gamelistwidget.h" />
<QtMoc Include="gamepropertiesdialog.h" /> <QtMoc Include="gamepropertiesdialog.h" />
<QtMoc Include="mainwindow.h" /> <QtMoc Include="mainwindow.h" />
<ClInclude Include="openglhostdisplay.h" />
<QtMoc Include="qthostinterface.h" /> <QtMoc Include="qthostinterface.h" />
<ClInclude Include="qtsettingsinterface.h" /> <ClInclude Include="qtsettingsinterface.h" />
<ClInclude Include="qtutils.h" /> <ClInclude Include="qtutils.h" />
<QtMoc Include="settingsdialog.h" /> <QtMoc Include="settingsdialog.h" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\..\dep\glad\glad.vcxproj">
<Project>{43540154-9e1e-409c-834f-b84be5621388}</Project>
</ProjectReference>
<ProjectReference Include="..\..\dep\imgui\imgui.vcxproj"> <ProjectReference Include="..\..\dep\imgui\imgui.vcxproj">
<Project>{bb08260f-6fbc-46af-8924-090ee71360c6}</Project> <Project>{bb08260f-6fbc-46af-8924-090ee71360c6}</Project>
</ProjectReference> </ProjectReference>
<ProjectReference Include="..\..\dep\minizip\minizip.vcxproj"> <ProjectReference Include="..\..\dep\minizip\minizip.vcxproj">
<Project>{8bda439c-6358-45fb-9994-2ff083babe06}</Project> <Project>{8bda439c-6358-45fb-9994-2ff083babe06}</Project>
</ProjectReference> </ProjectReference>
<ProjectReference Include="..\..\dep\vulkan-loader\vulkan-loader.vcxproj">
<Project>{9c8ddeb0-2b8f-4f5f-ba86-127cdf27f035}</Project>
</ProjectReference>
<ProjectReference Include="..\common\common.vcxproj"> <ProjectReference Include="..\common\common.vcxproj">
<Project>{ee054e08-3799-4a59-a422-18259c105ffd}</Project> <Project>{ee054e08-3799-4a59-a422-18259c105ffd}</Project>
</ProjectReference> </ProjectReference>

View File

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

View File

@ -2,13 +2,13 @@
#include "aboutdialog.h" #include "aboutdialog.h"
#include "common/assert.h" #include "common/assert.h"
#include "core/game_list.h" #include "core/game_list.h"
#include "core/host_display.h"
#include "core/settings.h" #include "core/settings.h"
#include "core/system.h" #include "core/system.h"
#include "gamelistsettingswidget.h" #include "gamelistsettingswidget.h"
#include "gamelistwidget.h" #include "gamelistwidget.h"
#include "gamepropertiesdialog.h" #include "gamepropertiesdialog.h"
#include "qtdisplaywidget.h" #include "qtdisplaywidget.h"
#include "qthostdisplay.h"
#include "qthostinterface.h" #include "qthostinterface.h"
#include "qtsettingsinterface.h" #include "qtsettingsinterface.h"
#include "qtutils.h" #include "qtutils.h"
@ -22,8 +22,8 @@
#include <QtGui/QCursor> #include <QtGui/QCursor>
#include <QtGui/QWindowStateChangeEvent> #include <QtGui/QWindowStateChangeEvent>
#include <QtWidgets/QFileDialog> #include <QtWidgets/QFileDialog>
#include <QtWidgets/QStyleFactory>
#include <QtWidgets/QMessageBox> #include <QtWidgets/QMessageBox>
#include <QtWidgets/QStyleFactory>
#include <cmath> #include <cmath>
static constexpr char DISC_IMAGE_FILTER[] = static constexpr char DISC_IMAGE_FILTER[] =
@ -72,14 +72,13 @@ bool MainWindow::confirmMessage(const QString& message)
return (result == QMessageBox::Yes); return (result == QMessageBox::Yes);
} }
void MainWindow::createDisplay(QThread* worker_thread, const QString& adapter_name, bool use_debug_device, QtDisplayWidget* MainWindow::createDisplay(QThread* worker_thread, const QString& adapter_name, bool use_debug_device,
bool fullscreen, bool render_to_main) bool fullscreen, bool render_to_main)
{ {
Assert(!m_host_display && !m_display_widget); Assert(!m_host_display && !m_display_widget);
Assert(!fullscreen || !render_to_main); Assert(!fullscreen || !render_to_main);
m_host_display = m_host_interface->createHostDisplay(); m_display_widget = new QtDisplayWidget((!fullscreen && render_to_main) ? m_ui.mainContainer : nullptr);
m_display_widget = m_host_display->createWidget((!fullscreen && render_to_main) ? m_ui.mainContainer : nullptr);
m_display_widget->setWindowTitle(windowTitle()); m_display_widget->setWindowTitle(windowTitle());
m_display_widget->setWindowIcon(windowIcon()); 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 // we need the surface visible.. this might be able to be replaced with something else
QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents); 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 create host display device context.")); reportError(tr("Failed to get window info from widget"));
return; destroyDisplayWidget();
return nullptr;
} }
m_host_display->deactivateDeviceContext(); 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."));
destroyDisplayWidget();
return nullptr;
}
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_fullscreen = m_display_widget->isFullScreen();
const bool is_rendering_to_main = (!is_fullscreen && m_display_widget->parent()); const bool is_rendering_to_main = (!is_fullscreen && m_display_widget->parent());
if (fullscreen == is_fullscreen && is_rendering_to_main == render_to_main) if (fullscreen == is_fullscreen && is_rendering_to_main == render_to_main)
return; return m_display_widget;
m_host_display->destroySurface(); m_host_display->DestroyRenderSurface();
if (is_rendering_to_main) destroyDisplayWidget();
{ m_display_widget = new QtDisplayWidget((!fullscreen && render_to_main) ? m_ui.mainContainer : nullptr);
switchToGameListView();
m_ui.mainContainer->removeWidget(m_display_widget);
}
m_host_display->destroyWidget();
m_display_widget = m_host_display->createWidget((!fullscreen && render_to_main) ? m_ui.mainContainer : nullptr);
m_display_widget->setWindowTitle(windowTitle()); m_display_widget->setWindowTitle(windowTitle());
m_display_widget->setWindowIcon(windowIcon()); 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 // we need the surface visible.. this might be able to be replaced with something else
QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents); 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."); Panic("Failed to recreate surface on new widget.");
m_display_widget->setFocus(); m_display_widget->setFocus();
QSignalBlocker blocker(m_ui.actionFullscreen); QSignalBlocker blocker(m_ui.actionFullscreen);
m_ui.actionFullscreen->setChecked(fullscreen); m_ui.actionFullscreen->setChecked(fullscreen);
return m_display_widget;
} }
void MainWindow::destroyDisplay() void MainWindow::destroyDisplay()
{ {
DebugAssert(m_host_display && m_display_widget); 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()) if (m_display_widget->parent())
{ {
m_ui.mainContainer->removeWidget(m_display_widget);
switchToGameListView(); switchToGameListView();
m_ui.mainContainer->removeWidget(m_display_widget);
} }
m_host_display->destroyWidget(); delete m_display_widget;
m_display_widget = nullptr; m_display_widget = nullptr;
delete m_host_display;
m_host_display = nullptr;
} }
void MainWindow::focusDisplayWidget() void MainWindow::focusDisplayWidget()

View File

@ -12,9 +12,9 @@ class QThread;
class GameListWidget; class GameListWidget;
class QtHostInterface; class QtHostInterface;
class QtHostDisplay;
class QtDisplayWidget; class QtDisplayWidget;
class HostDisplay;
struct GameListEntry; struct GameListEntry;
class MainWindow final : public QMainWindow class MainWindow final : public QMainWindow
@ -29,9 +29,9 @@ private Q_SLOTS:
void reportError(const QString& message); void reportError(const QString& message);
void reportMessage(const QString& message); void reportMessage(const QString& message);
bool confirmMessage(const QString& message); bool confirmMessage(const QString& message);
void createDisplay(QThread* worker_thread, const QString& adapter_name, bool use_debug_device, bool fullscreen, QtDisplayWidget* createDisplay(QThread* worker_thread, const QString& adapter_name, bool use_debug_device,
bool render_to_main); bool fullscreen, bool render_to_main);
void updateDisplay(QThread* worker_thread, bool fullscreen, bool render_to_main); QtDisplayWidget* updateDisplay(QThread* worker_thread, bool fullscreen, bool render_to_main);
void destroyDisplay(); void destroyDisplay();
void focusDisplayWidget(); void focusDisplayWidget();
@ -72,6 +72,7 @@ private:
void updateEmulationActions(bool starting, bool running); void updateEmulationActions(bool starting, bool running);
void switchToGameListView(); void switchToGameListView();
void switchToEmulationView(); void switchToEmulationView();
void destroyDisplayWidget();
SettingsDialog* getSettingsDialog(); SettingsDialog* getSettingsDialog();
void doSettings(SettingsDialog::Category category = SettingsDialog::Category::Count); void doSettings(SettingsDialog::Category category = SettingsDialog::Category::Count);
void updateDebugMenuCPUExecutionMode(); void updateDebugMenuCPUExecutionMode();
@ -83,7 +84,7 @@ private:
GameListWidget* m_game_list_widget = nullptr; GameListWidget* m_game_list_widget = nullptr;
QtHostDisplay* m_host_display = nullptr; HostDisplay* m_host_display = nullptr;
QtDisplayWidget* m_display_widget = nullptr; QtDisplayWidget* m_display_widget = nullptr;
QLabel* m_status_speed_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 "qtdisplaywidget.h"
#include "common/bitutils.h" #include "common/bitutils.h"
#include "qthostdisplay.h"
#include "qthostinterface.h" #include "qthostinterface.h"
#include "qtutils.h" #include "qtutils.h"
#include <QtCore/QDebug>
#include <QtGui/QGuiApplication> #include <QtGui/QGuiApplication>
#include <QtGui/QKeyEvent> #include <QtGui/QKeyEvent>
#include <QtGui/QScreen> #include <QtGui/QScreen>
@ -10,6 +10,10 @@
#include <QtGui/QWindowStateChangeEvent> #include <QtGui/QWindowStateChangeEvent>
#include <cmath> #include <cmath>
#if !defined(WIN32) && !defined(APPLE)
#include <qpa/qplatformnativeinterface.h>
#endif
QtDisplayWidget::QtDisplayWidget(QWidget* parent) : QWidget(parent) QtDisplayWidget::QtDisplayWidget(QWidget* parent) : QWidget(parent)
{ {
// We want a native window for both D3D and OpenGL. // 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())); 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 QPaintEngine* QtDisplayWidget::paintEngine() const
{ {
return nullptr; return nullptr;

View File

@ -1,6 +1,8 @@
#pragma once #pragma once
#include "common/types.h" #include "common/types.h"
#include "common/window_info.h"
#include <QtWidgets/QWidget> #include <QtWidgets/QWidget>
#include <optional>
class QtDisplayWidget final : public QWidget class QtDisplayWidget final : public QWidget
{ {
@ -16,6 +18,8 @@ public:
int scaledWindowHeight() const; int scaledWindowHeight() const;
qreal devicePixelRatioFromScreen() const; qreal devicePixelRatioFromScreen() const;
std::optional<WindowInfo> getWindowInfo() const;
Q_SIGNALS: Q_SIGNALS:
void windowResizedEvent(int width, int height); void windowResizedEvent(int width, int height);
void windowRestoredEvent(); 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/game_list.h"
#include "core/gpu.h" #include "core/gpu.h"
#include "core/system.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_audio_stream.h"
#include "frontend-common/sdl_controller_interface.h" #include "frontend-common/sdl_controller_interface.h"
#include "frontend-common/vulkan_host_display.h"
#include "imgui.h"
#include "mainwindow.h" #include "mainwindow.h"
#include "openglhostdisplay.h" #include "qtdisplaywidget.h"
#include "qtprogresscallback.h" #include "qtprogresscallback.h"
#include "qtsettingsinterface.h" #include "qtsettingsinterface.h"
#include "qtutils.h" #include "qtutils.h"
#include "vulkanhostdisplay.h"
#include <QtCore/QCoreApplication> #include <QtCore/QCoreApplication>
#include <QtCore/QDateTime> #include <QtCore/QDateTime>
#include <QtCore/QDebug> #include <QtCore/QDebug>
@ -27,7 +30,7 @@
Log_SetChannel(QtHostInterface); Log_SetChannel(QtHostInterface);
#ifdef WIN32 #ifdef WIN32
#include "d3d11hostdisplay.h" #include "frontend-common/d3d11_host_display.h"
#endif #endif
QtHostInterface::QtHostInterface(QObject* parent) : QObject(parent), CommonHostInterface() QtHostInterface::QtHostInterface(QObject* parent) : QObject(parent), CommonHostInterface()
@ -37,7 +40,7 @@ QtHostInterface::QtHostInterface(QObject* parent) : QObject(parent), CommonHostI
QtHostInterface::~QtHostInterface() QtHostInterface::~QtHostInterface()
{ {
Assert(!getHostDisplay()); Assert(!m_display);
} }
const char* QtHostInterface::GetFrontendName() const const char* QtHostInterface::GetFrontendName() const
@ -177,7 +180,7 @@ void QtHostInterface::applySettings()
if (m_system) if (m_system)
{ {
const bool render_to_main = getSettingValue("Main/RenderToMainWindow", true).toBool(); 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; m_is_rendering_to_main = render_to_main;
updateDisplayState(); updateDisplayState();
@ -271,10 +274,10 @@ void QtHostInterface::onDisplayWindowMouseButtonEvent(int button, bool pressed)
void QtHostInterface::onHostDisplayWindowResized(int width, int height) void QtHostInterface::onHostDisplayWindowResized(int width, int height)
{ {
// this can be null if it was destroyed and the main thread is late catching up // this can be null if it was destroyed and the main thread is late catching up
if (!getHostDisplay()) if (!m_display)
return; 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 // re-render the display, since otherwise it will be out of date and stretched if paused
if (m_system) if (m_system)
@ -289,7 +292,7 @@ void QtHostInterface::redrawDisplayWindow()
return; return;
} }
if (!getHostDisplay() || !m_system) if (!m_display || !m_system)
return; return;
renderDisplay(); renderDisplay();
@ -306,71 +309,69 @@ void QtHostInterface::toggleFullscreen()
SetFullscreen(!m_is_fullscreen); SetFullscreen(!m_is_fullscreen);
} }
QtHostDisplay* QtHostInterface::getHostDisplay()
{
return static_cast<QtHostDisplay*>(m_display);
}
bool QtHostInterface::AcquireHostDisplay() bool QtHostInterface::AcquireHostDisplay()
{ {
Assert(!m_display); Assert(!m_display);
m_is_rendering_to_main = getSettingValue("Main/RenderToMainWindow", true).toBool(); 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(); emit destroyDisplayRequested();
m_display = nullptr; m_display = nullptr;
return false; return false;
} }
if (!getHostDisplay()->activateDeviceContext() || createImGuiContext(display_widget->devicePixelRatioFromScreen());
!getHostDisplay()->initializeDeviceContext(GetShaderCacheDirectory(), m_settings.gpu_use_debug_device))
if (!m_display->MakeRenderContextCurrent() ||
!m_display->InitializeRenderDevice(GetShaderCacheDirectory(), m_settings.gpu_use_debug_device))
{ {
getHostDisplay()->destroyDeviceContext(); destroyImGuiContext();
m_display->DestroyRenderDevice();
emit destroyDisplayRequested(); emit destroyDisplayRequested();
m_display = nullptr; m_display.reset();
return false; return false;
} }
connectDisplaySignals(); connectDisplaySignals(display_widget);
ImGui::NewFrame();
return true; return true;
} }
QtHostDisplay* QtHostInterface::createHostDisplay() HostDisplay* QtHostInterface::createHostDisplay()
{ {
Assert(!getHostDisplay());
switch (m_settings.gpu_renderer) switch (m_settings.gpu_renderer)
{ {
case GPURenderer::HardwareVulkan: case GPURenderer::HardwareVulkan:
m_display = new VulkanHostDisplay(this); m_display = std::make_unique<FrontendCommon::VulkanHostDisplay>();
break; break;
case GPURenderer::HardwareOpenGL: case GPURenderer::HardwareOpenGL:
#ifndef WIN32 #ifndef WIN32
default: default:
#endif #endif
m_display = new OpenGLHostDisplay(this); m_display = std::make_unique<FrontendCommon::OpenGLHostDisplay>();
break; break;
#ifdef WIN32 #ifdef WIN32
case GPURenderer::HardwareD3D11: case GPURenderer::HardwareD3D11:
default: default:
m_display = new D3D11HostDisplay(this); m_display = std::make_unique<FrontendCommon::D3D11HostDisplay>();
break; break;
#endif #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::windowResizedEvent, this, &QtHostInterface::onHostDisplayWindowResized);
connect(widget, &QtDisplayWidget::windowRestoredEvent, this, &QtHostInterface::redrawDisplayWindow); connect(widget, &QtDisplayWidget::windowRestoredEvent, this, &QtHostInterface::redrawDisplayWindow);
connect(widget, &QtDisplayWidget::windowClosedEvent, this, &QtHostInterface::powerOffSystem, connect(widget, &QtDisplayWidget::windowClosedEvent, this, &QtHostInterface::powerOffSystem,
@ -380,21 +381,16 @@ void QtHostInterface::connectDisplaySignals()
connect(widget, &QtDisplayWidget::windowMouseButtonEvent, this, &QtHostInterface::onDisplayWindowMouseButtonEvent); connect(widget, &QtDisplayWidget::windowMouseButtonEvent, this, &QtHostInterface::onDisplayWindowMouseButtonEvent);
} }
void QtHostInterface::disconnectDisplaySignals()
{
getHostDisplay()->getWidget()->disconnect(this);
}
void QtHostInterface::updateDisplayState() void QtHostInterface::updateDisplayState()
{ {
// this expects the context to get moved back to us afterwards // this expects the context to get moved back to us afterwards
getHostDisplay()->deactivateDeviceContext(); m_display->DoneRenderContextCurrent();
emit updateDisplayRequested(m_worker_thread, m_is_fullscreen, m_is_rendering_to_main);
if (!getHostDisplay()->activateDeviceContext()) 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"); Panic("Failed to make device context current after updating");
getHostDisplay()->updateImGuiDisplaySize(); connectDisplaySignals(display_widget);
connectDisplaySignals();
redrawDisplayWindow(); redrawDisplayWindow();
UpdateSpeedLimiterState(); UpdateSpeedLimiterState();
} }
@ -403,9 +399,10 @@ void QtHostInterface::ReleaseHostDisplay()
{ {
Assert(m_display); Assert(m_display);
getHostDisplay()->destroyDeviceContext(); m_display->DestroyRenderDevice();
destroyImGuiContext();
emit destroyDisplayRequested(); emit destroyDisplayRequested();
m_display = nullptr; m_display.reset();
m_is_fullscreen = false; m_is_fullscreen = false;
} }
@ -938,6 +935,7 @@ void QtHostInterface::renderDisplay()
DrawImGuiWindows(); DrawImGuiWindows();
m_display->Render(); m_display->Render();
ImGui::NewFrame();
m_system->GetGPU()->RestoreGraphicsAPIState(); m_system->GetGPU()->RestoreGraphicsAPIState();
} }
@ -950,6 +948,25 @@ void QtHostInterface::wakeThread()
QMetaObject::invokeMethod(m_worker_thread_event_loop, "quit", Qt::QueuedConnection); 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(QtHostInterface* parent) : QThread(parent), m_parent(parent) {}
QtHostInterface::Thread::~Thread() = default; QtHostInterface::Thread::~Thread() = default;

View File

@ -5,6 +5,7 @@
#include "frontend-common/common_host_interface.h" #include "frontend-common/common_host_interface.h"
#include <QtCore/QByteArray> #include <QtCore/QByteArray>
#include <QtCore/QObject> #include <QtCore/QObject>
#include <QtCore/QString>
#include <QtCore/QSettings> #include <QtCore/QSettings>
#include <QtCore/QThread> #include <QtCore/QThread>
#include <atomic> #include <atomic>
@ -25,8 +26,7 @@ class QTimer;
class GameList; class GameList;
class MainWindow; class MainWindow;
class QtDisplayWidget;
class QtHostDisplay;
Q_DECLARE_METATYPE(SystemBootParameters); Q_DECLARE_METATYPE(SystemBootParameters);
@ -66,7 +66,7 @@ public:
ALWAYS_INLINE MainWindow* getMainWindow() const { return m_main_window; } ALWAYS_INLINE MainWindow* getMainWindow() const { return m_main_window; }
void setMainWindow(MainWindow* window); void setMainWindow(MainWindow* window);
QtHostDisplay* createHostDisplay(); HostDisplay* createHostDisplay();
void populateSaveStateMenus(const char* game_code, QMenu* load_menu, QMenu* save_menu); void populateSaveStateMenus(const char* game_code, QMenu* load_menu, QMenu* save_menu);
@ -96,9 +96,9 @@ Q_SIGNALS:
void emulationPaused(bool paused); void emulationPaused(bool paused);
void stateSaved(const QString& game_code, bool global, qint32 slot); void stateSaved(const QString& game_code, bool global, qint32 slot);
void gameListRefreshed(); void gameListRefreshed();
void createDisplayRequested(QThread* worker_thread, const QString& adapter_name, bool use_debug_device, QtDisplayWidget* createDisplayRequested(QThread* worker_thread, const QString& adapter_name, bool use_debug_device,
bool fullscreen, bool render_to_main); bool fullscreen, bool render_to_main);
void updateDisplayRequested(QThread* worker_thread, bool fullscreen, bool render_to_main); QtDisplayWidget* updateDisplayRequested(QThread* worker_thread, bool fullscreen, bool render_to_main);
void focusDisplayWidgetRequested(); void focusDisplayWidgetRequested();
void destroyDisplayRequested(); void destroyDisplayRequested();
void systemPerformanceCountersUpdated(float speed, float fps, float vps, float avg_frame_time, void systemPerformanceCountersUpdated(float speed, float fps, float vps, float avg_frame_time,
@ -186,21 +186,21 @@ private:
Common::Event m_init_event; Common::Event m_init_event;
}; };
QtHostDisplay* getHostDisplay();
void createBackgroundControllerPollTimer(); void createBackgroundControllerPollTimer();
void destroyBackgroundControllerPollTimer(); void destroyBackgroundControllerPollTimer();
void startBackgroundControllerPollTimer(); void startBackgroundControllerPollTimer();
void stopBackgroundControllerPollTimer(); void stopBackgroundControllerPollTimer();
void createImGuiContext(float framebuffer_scale);
void destroyImGuiContext();
void createThread(); void createThread();
void stopThread(); void stopThread();
void threadEntryPoint(); void threadEntryPoint();
bool initializeOnThread(); bool initializeOnThread();
void shutdownOnThread(); void shutdownOnThread();
void renderDisplay(); void renderDisplay();
void connectDisplaySignals(); void connectDisplaySignals(QtDisplayWidget* widget);
void disconnectDisplaySignals();
void updateDisplayState(); void updateDisplayState();
void wakeThread(); 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.cpp
imgui_impl_sdl.h imgui_impl_sdl.h
main.cpp main.cpp
opengl_host_display.cpp
opengl_host_display.h
sdl_host_interface.cpp sdl_host_interface.cpp
sdl_host_interface.h sdl_host_interface.h
sdl_key_names.h sdl_key_names.h
sdl_util.cpp sdl_util.cpp
sdl_util.h sdl_util.h
sdl_vulkan_host_display.cpp
sdl_vulkan_host_display.h
) )
target_include_directories(duckstation-sdl PRIVATE ${SDL2_INCLUDE_DIRS}) 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) if(WIN32)
target_sources(duckstation-sdl PRIVATE target_sources(duckstation-sdl PRIVATE
sdl_d3d11_host_display.cpp
sdl_d3d11_host_display.h
duckstation-sdl.manifest duckstation-sdl.manifest
) )

View File

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

View File

@ -2,22 +2,16 @@
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup> <ItemGroup>
<ClCompile Include="main.cpp" /> <ClCompile Include="main.cpp" />
<ClCompile Include="opengl_host_display.cpp" />
<ClCompile Include="sdl_host_interface.cpp" /> <ClCompile Include="sdl_host_interface.cpp" />
<ClCompile Include="imgui_impl_sdl.cpp" /> <ClCompile Include="imgui_impl_sdl.cpp" />
<ClCompile Include="sdl_util.cpp" /> <ClCompile Include="sdl_util.cpp" />
<ClCompile Include="sdl_vulkan_host_display.cpp" />
<ClCompile Include="sdl_d3d11_host_display.cpp" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="opengl_host_display.h" />
<ClInclude Include="sdl_host_interface.h" /> <ClInclude Include="sdl_host_interface.h" />
<ClInclude Include="imgui_impl_sdl.h" /> <ClInclude Include="imgui_impl_sdl.h" />
<ClInclude Include="sdl_key_names.h" /> <ClInclude Include="sdl_key_names.h" />
<ClInclude Include="resource.h" /> <ClInclude Include="resource.h" />
<ClInclude Include="sdl_util.h" /> <ClInclude Include="sdl_util.h" />
<ClInclude Include="sdl_vulkan_host_display.h" />
<ClInclude Include="sdl_d3d11_host_display.h" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Manifest Include="duckstation-sdl.manifest" /> <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/icon.h"
#include "frontend-common/imgui_styles.h" #include "frontend-common/imgui_styles.h"
#include "frontend-common/ini_settings_interface.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_audio_stream.h"
#include "frontend-common/sdl_controller_interface.h" #include "frontend-common/sdl_controller_interface.h"
#include "frontend-common/vulkan_host_display.h"
#include "imgui_impl_sdl.h" #include "imgui_impl_sdl.h"
#include "opengl_host_display.h"
#include "scmversion/scmversion.h" #include "scmversion/scmversion.h"
#include "sdl_key_names.h" #include "sdl_key_names.h"
#include "sdl_vulkan_host_display.h" #include "sdl_util.h"
#include <cinttypes> #include <cinttypes>
#include <cmath> #include <cmath>
#include <imgui.h> #include <imgui.h>
@ -26,7 +27,7 @@
Log_SetChannel(SDLHostInterface); Log_SetChannel(SDLHostInterface);
#ifdef WIN32 #ifdef WIN32
#include "sdl_d3d11_host_display.h" #include "frontend-common/d3d11_host_display.h"
#endif #endif
SDLHostInterface::SDLHostInterface() SDLHostInterface::SDLHostInterface()
@ -129,49 +130,86 @@ void SDLHostInterface::DestroySDLWindow()
bool SDLHostInterface::CreateDisplay() bool SDLHostInterface::CreateDisplay()
{ {
const std::string shader_cache_directory(GetShaderCacheDirectory()); std::optional<WindowInfo> wi = SDLUtil::GetWindowInfoForSDLWindow(m_window);
std::unique_ptr<HostDisplay> display; 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) switch (m_settings.gpu_renderer)
{ {
case GPURenderer::HardwareVulkan: case GPURenderer::HardwareVulkan:
display = SDLVulkanHostDisplay::Create(m_window, m_settings.gpu_adapter, shader_cache_directory, display = std::make_unique<FrontendCommon::VulkanHostDisplay>();
m_settings.gpu_use_debug_device);
break; break;
case GPURenderer::HardwareOpenGL: case GPURenderer::HardwareOpenGL:
#ifndef WIN32 #ifndef WIN32
default: default:
#endif #endif
display = OpenGLHostDisplay::Create(m_window, m_settings.gpu_use_debug_device); display = std::make_unique<FrontendCommon::OpenGLHostDisplay>();
break; break;
#ifdef WIN32 #ifdef WIN32
case GPURenderer::HardwareD3D11: case GPURenderer::HardwareD3D11:
default: default:
display = SDLD3D11HostDisplay::Create(m_window, m_settings.gpu_adapter, m_settings.gpu_use_debug_device); display = std::make_unique<FrontendCommon::D3D11HostDisplay>();
break; break;
#endif #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; 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 = m_app_icon_texture =
display->CreateTexture(APP_ICON_WIDTH, APP_ICON_HEIGHT, APP_ICON_DATA, APP_ICON_WIDTH * sizeof(u32)); 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; return false;
display->SetDisplayTopMargin(m_fullscreen ? 0 : static_cast<int>(20.0f * ImGui::GetIO().DisplayFramebufferScale.x)); 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; return true;
} }
void SDLHostInterface::DestroyDisplay() void SDLHostInterface::DestroyDisplay()
{ {
m_app_icon_texture.reset(); m_app_icon_texture.reset();
delete m_display; m_display->DestroyRenderDevice();
m_display = nullptr; m_display.reset();
} }
void SDLHostInterface::CreateImGuiContext() void SDLHostInterface::CreateImGuiContext()
@ -227,13 +265,16 @@ bool SDLHostInterface::AcquireHostDisplay()
{ {
ImGui::EndFrame(); ImGui::EndFrame();
DestroyDisplay(); DestroyDisplay();
DestroySDLWindow();
// We need to recreate the window, otherwise bad things happen...
DestroySDLWindow();
if (!CreateSDLWindow()) if (!CreateSDLWindow())
Panic("Failed to recreate SDL window on GPU renderer switch"); Panic("Failed to recreate SDL window on GPU renderer switch");
if (!CreateDisplay()) if (!CreateDisplay())
Panic("Failed to recreate display on GPU renderer switch"); Panic("Failed to recreate display on GPU renderer switch");
ImGui::NewFrame();
} }
return true; return true;
@ -357,7 +398,7 @@ bool SDLHostInterface::SetFullscreen(bool enabled)
int window_width, window_height; int window_width, window_height;
SDL_GetWindowSize(m_window, &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; m_fullscreen = enabled;
return true; return true;
} }
@ -389,6 +430,8 @@ bool SDLHostInterface::Initialize()
return false; return false;
} }
ImGui::NewFrame();
RegisterHotkeys(); RegisterHotkeys();
// process events to pick up controllers before updating input map // 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) 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(); UpdateFramebufferScale();
} }
else if (event->window.event == SDL_WINDOWEVENT_MOVED) else if (event->window.event == SDL_WINDOWEVENT_MOVED)
@ -1492,6 +1535,8 @@ void SDLHostInterface::Run()
m_system->GetGPU()->ResetGraphicsAPIState(); m_system->GetGPU()->ResetGraphicsAPIState();
m_display->Render(); m_display->Render();
ImGui_ImplSDL2_NewFrame(m_window);
ImGui::NewFrame();
if (m_system) 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 imgui_styles.h
ini_settings_interface.cpp ini_settings_interface.cpp
ini_settings_interface.h ini_settings_interface.h
opengl_host_display.cpp
opengl_host_display.h
save_state_selector_ui.cpp save_state_selector_ui.cpp
save_state_selector_ui.h save_state_selector_ui.h
vulkan_host_display.cpp vulkan_host_display.cpp
vulkan_host_display.h 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) if(WIN32)
target_sources(frontend-common PRIVATE target_sources(frontend-common PRIVATE
@ -25,7 +27,7 @@ if(WIN32)
target_link_libraries(frontend-common PRIVATE d3d11.lib dxgi.lib) target_link_libraries(frontend-common PRIVATE d3d11.lib dxgi.lib)
endif() endif()
if(SDL2_FOUND) if(SDL2_FOUND AND NOT BUILD_LIBRETRO_CORE)
target_sources(frontend-common PRIVATE target_sources(frontend-common PRIVATE
sdl_audio_stream.cpp sdl_audio_stream.cpp
sdl_audio_stream.h sdl_audio_stream.h
@ -45,7 +47,7 @@ if(SDL2_FOUND)
endif() endif()
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_compile_definitions(frontend-common PUBLIC -DWITH_DISCORD_PRESENCE=1)
target_link_libraries(frontend-common PRIVATE discord-rpc) target_link_libraries(frontend-common PRIVATE discord-rpc)
endif() endif()

View File

@ -1814,8 +1814,8 @@ void CommonHostInterface::DisplayLoadingScreen(const char* message, int progress
const bool has_progress = (progress_min < progress_max); const bool has_progress = (progress_min < progress_max);
// eat the last imgui frame, it might've been partially rendered by the caller. // eat the last imgui frame, it might've been partially rendered by the caller.
ImGui::EndFrame(); //ImGui::EndFrame();
ImGui::NewFrame(); //ImGui::NewFrame();
ImGui::SetNextWindowSize(ImVec2(width, (has_progress ? 50.0f : 30.0f) * scale), ImGuiCond_Always); 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, 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(); ImGui::End();
m_display->Render(); m_display->Render();
ImGui::NewFrame();
} }
void CommonHostInterface::GetGameInfo(const char* path, CDImage* image, std::string* code, std::string* title) 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() = 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, std::unique_ptr<HostDisplayTexture> D3D11HostDisplay::CreateTexture(u32 width, u32 height, const void* initial_data,
u32 initial_data_stride, bool dynamic) u32 initial_data_stride, bool dynamic)
@ -135,13 +164,7 @@ void D3D11HostDisplay::SetVSync(bool enabled)
m_vsync = enabled; m_vsync = enabled;
} }
bool D3D11HostDisplay::HasContext() const bool D3D11HostDisplay::CreateRenderDevice(const WindowInfo& wi, std::string_view adapter_name, bool debug_device)
{
return static_cast<bool>(m_device);
}
bool D3D11HostDisplay::CreateContextAndSwapChain(const WindowInfo& wi, std::string_view adapter_name,
bool use_flip_model, bool debug_device)
{ {
UINT create_flags = 0; UINT create_flags = 0;
if (debug_device) if (debug_device)
@ -242,24 +265,53 @@ bool D3D11HostDisplay::CreateContextAndSwapChain(const WindowInfo& wi, std::stri
m_allow_tearing_supported = (allow_tearing_supported == TRUE); m_allow_tearing_supported = (allow_tearing_supported == TRUE);
} }
return CreateSwapChain(wi, use_flip_model); m_window_info = wi;
return true;
} }
void D3D11HostDisplay::DestroyContext() bool D3D11HostDisplay::InitializeRenderDevice(std::string_view shader_cache_directory, bool debug_device)
{ {
Assert(!m_swap_chain); if (!CreateSwapChain())
return false;
if (!CreateResources())
return false;
if (ImGui::GetCurrentContext() && !CreateImGuiContext())
return false;
return true;
}
void D3D11HostDisplay::DestroyRenderDevice()
{
if (ImGui::GetCurrentContext())
DestroyImGuiContext();
DestroyResources();
DestroyRenderSurface();
m_context.Reset(); m_context.Reset();
m_device.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; 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{}; RECT client_rc{};
GetClientRect(window_hwnd, &client_rc); GetClientRect(window_hwnd, &client_rc);
const u32 width = static_cast<u32>(client_rc.right - client_rc.left); const u32 width = static_cast<u32>(client_rc.right - client_rc.left);
@ -330,23 +382,33 @@ bool D3D11HostDisplay::CreateSwapChainRTV()
return false; return false;
} }
m_swap_chain_width = backbuffer_desc.Width; m_window_info.surface_width = backbuffer_desc.Width;
m_swap_chain_height = backbuffer_desc.Height; 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; 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_rtv.Reset();
m_swap_chain.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) if (!m_swap_chain)
return; return;
@ -423,8 +485,8 @@ void D3D11HostDisplay::DestroyResources()
bool D3D11HostDisplay::CreateImGuiContext() bool D3D11HostDisplay::CreateImGuiContext()
{ {
ImGui::GetIO().DisplaySize.x = static_cast<float>(m_swap_chain_width); ImGui::GetIO().DisplaySize.x = static_cast<float>(m_window_info.surface_width);
ImGui::GetIO().DisplaySize.y = static_cast<float>(m_swap_chain_height); ImGui::GetIO().DisplaySize.y = static_cast<float>(m_window_info.surface_height);
if (!ImGui_ImplDX11_Init(m_device.Get(), m_context.Get())) if (!ImGui_ImplDX11_Init(m_device.Get(), m_context.Get()))
return false; return false;
@ -438,12 +500,27 @@ void D3D11HostDisplay::DestroyImGuiContext()
ImGui_ImplDX11_Shutdown(); ImGui_ImplDX11_Shutdown();
} }
bool D3D11HostDisplay::BeginRender() bool D3D11HostDisplay::Render()
{ {
static constexpr std::array<float, 4> clear_color = {}; static constexpr std::array<float, 4> clear_color = {};
m_context->ClearRenderTargetView(m_swap_chain_rtv.Get(), clear_color.data()); m_context->ClearRenderTargetView(m_swap_chain_rtv.Get(), clear_color.data());
m_context->OMSetRenderTargets(1, m_swap_chain_rtv.GetAddressOf(), nullptr); 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; return true;
} }
@ -453,20 +530,20 @@ void D3D11HostDisplay::RenderImGui()
ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData()); ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData());
} }
void D3D11HostDisplay::EndRenderAndPresent() void D3D11HostDisplay::RenderDisplay()
{ {
if (!m_vsync && m_using_allow_tearing) if (!HasDisplayTexture())
m_swap_chain->Present(0, DXGI_PRESENT_ALLOW_TEARING); return;
else
m_swap_chain->Present(BoolToUInt32(m_vsync), 0);
ImGui::NewFrame(); const auto [left, top, width, height] = CalculateDrawRect(GetWindowWidth(), GetWindowHeight(), m_display_top_margin);
ImGui_ImplDX11_NewFrame(); 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, 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, s32 texture_height, s32 texture_view_x, s32 texture_view_y, s32 texture_view_width,
u32 texture_view_height, bool linear_filter) s32 texture_view_height, bool linear_filter)
{ {
m_context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST); m_context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
m_context->VSSetShader(m_display_vertex_shader.Get(), nullptr, 0); 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); 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, void D3D11HostDisplay::RenderSoftwareCursor(s32 left, s32 top, s32 width, s32 height,
HostDisplayTexture* texture_handle) HostDisplayTexture* texture_handle)
{ {
@ -574,4 +660,9 @@ std::vector<std::string> D3D11HostDisplay::EnumerateAdapterNames(IDXGIFactory* d
return adapter_names; return adapter_names;
} }
bool D3D11HostDisplay::UseFlipModelSwapChain() const
{
return true;
}
} // namespace FrontendCommon } // namespace FrontendCommon

View File

@ -15,7 +15,7 @@
namespace FrontendCommon { namespace FrontendCommon {
class D3D11HostDisplay final class D3D11HostDisplay : public HostDisplay
{ {
public: public:
template<typename T> template<typename T>
@ -24,56 +24,62 @@ public:
D3D11HostDisplay(); D3D11HostDisplay();
~D3D11HostDisplay(); ~D3D11HostDisplay();
ALWAYS_INLINE HostDisplay::RenderAPI GetRenderAPI() const { return HostDisplay::RenderAPI::D3D11; } virtual RenderAPI GetRenderAPI() const override;
ALWAYS_INLINE void* GetRenderDevice() const { return m_device.Get(); } virtual void* GetRenderDevice() const override;
ALWAYS_INLINE void* GetRenderContext() const { return m_context.Get(); } virtual void* GetRenderContext() const override;
bool CreateContextAndSwapChain(const WindowInfo& wi, std::string_view adapter_name, bool use_flip_model, virtual bool HasRenderDevice() const override;
bool debug_device); virtual bool HasRenderSurface() const override;
bool HasContext() const;
void DestroyContext();
bool CreateResources(); virtual bool CreateRenderDevice(const WindowInfo& wi, std::string_view adapter_name, bool debug_device) override;
void DestroyResources(); virtual bool InitializeRenderDevice(std::string_view shader_cache_directory, bool debug_device) override;
virtual void DestroyRenderDevice() override;
bool CreateImGuiContext(); virtual bool MakeRenderContextCurrent() override;
void DestroyImGuiContext(); virtual bool DoneRenderContextCurrent() override;
ALWAYS_INLINE u32 GetSwapChainWidth() const { return m_swap_chain_width; } virtual bool ChangeRenderWindow(const WindowInfo& new_wi) override;
ALWAYS_INLINE u32 GetSwapChainHeight() const { return m_swap_chain_height; } virtual void ResizeRenderWindow(s32 new_window_width, s32 new_window_height) override;
ALWAYS_INLINE bool HasSwapChain() const { return static_cast<bool>(m_swap_chain); } virtual void DestroyRenderSurface() override;
bool RecreateSwapChain(const WindowInfo& new_wi, bool use_flip_model);
void ResizeSwapChain(u32 new_width, u32 new_height);
void DestroySwapChain();
std::unique_ptr<HostDisplayTexture> CreateTexture(u32 width, u32 height, const void* initial_data, 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, 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, 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(); virtual bool Render() override;
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();
static std::vector<std::string> EnumerateAdapterNames(); static std::vector<std::string> EnumerateAdapterNames();
private: protected:
static constexpr u32 DISPLAY_UNIFORM_BUFFER_SIZE = 16; static constexpr u32 DISPLAY_UNIFORM_BUFFER_SIZE = 16;
virtual bool UseFlipModelSwapChain() const;
static std::vector<std::string> EnumerateAdapterNames(IDXGIFactory* dxgi_factory); 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(); 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<IDXGIFactory> m_dxgi_factory;
ComPtr<ID3D11Device> m_device; ComPtr<ID3D11Device> m_device;
@ -94,9 +100,6 @@ private:
D3D11::StreamBuffer m_display_uniform_buffer; D3D11::StreamBuffer m_display_uniform_buffer;
D3D11::AutoStagingTexture m_readback_staging_texture; 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_allow_tearing_supported = false;
bool m_using_flip_model_swap_chain = true; bool m_using_flip_model_swap_chain = true;
bool m_using_allow_tearing = false; bool m_using_allow_tearing = false;

View File

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

View File

@ -12,6 +12,7 @@
<ClCompile Include="save_state_selector_ui.cpp" /> <ClCompile Include="save_state_selector_ui.cpp" />
<ClCompile Include="vulkan_host_display.cpp" /> <ClCompile Include="vulkan_host_display.cpp" />
<ClCompile Include="d3d11_host_display.cpp" /> <ClCompile Include="d3d11_host_display.cpp" />
<ClCompile Include="opengl_host_display.cpp" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="icon.h" /> <ClInclude Include="icon.h" />
@ -25,6 +26,7 @@
<ClInclude Include="save_state_selector_ui.h" /> <ClInclude Include="save_state_selector_ui.h" />
<ClInclude Include="vulkan_host_display.h" /> <ClInclude Include="vulkan_host_display.h" />
<ClInclude Include="d3d11_host_display.h" /> <ClInclude Include="d3d11_host_display.h" />
<ClInclude Include="opengl_host_display.h" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="font_roboto_regular.inl" /> <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/assert.h"
#include "common/log.h" #include "common/log.h"
#include "imgui.h" #include "imgui.h"
#include "qtdisplaywidget.h"
#include "qthostinterface.h"
#include <QtGui/QKeyEvent>
#include <QtGui/QWindow>
#include <array> #include <array>
#include <imgui_impl_opengl3.h> #include <imgui_impl_opengl3.h>
#include <tuple> #include <tuple>
Log_SetChannel(OpenGLHostDisplay); Log_SetChannel(LibretroOpenGLHostDisplay);
class OpenGLDisplayWidgetTexture : public HostDisplayTexture namespace FrontendCommon {
class OpenGLHostDisplayTexture : public HostDisplayTexture
{ {
public: public:
OpenGLDisplayWidgetTexture(GLuint id, u32 width, u32 height) : m_id(id), m_width(width), m_height(height) {} OpenGLHostDisplayTexture(GLuint id, u32 width, u32 height) : m_id(id), m_width(width), m_height(height) {}
~OpenGLDisplayWidgetTexture() override { glDeleteTextures(1, &m_id); } ~OpenGLHostDisplayTexture() override { glDeleteTextures(1, &m_id); }
void* GetHandle() const override { return reinterpret_cast<void*>(static_cast<uintptr_t>(m_id)); } void* GetHandle() const override { return reinterpret_cast<void*>(static_cast<uintptr_t>(m_id)); }
u32 GetWidth() const override { return m_width; } u32 GetWidth() const override { return m_width; }
@ -23,8 +21,8 @@ public:
GLuint GetGLID() const { return m_id; } GLuint GetGLID() const { return m_id; }
static std::unique_ptr<OpenGLDisplayWidgetTexture> Create(u32 width, u32 height, const void* initial_data, static std::unique_ptr<OpenGLHostDisplayTexture> Create(u32 width, u32 height, const void* initial_data,
u32 initial_data_stride) u32 initial_data_stride)
{ {
GLuint id; GLuint id;
glGenTextures(1, &id); glGenTextures(1, &id);
@ -42,7 +40,7 @@ public:
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glBindTexture(GL_TEXTURE_2D, id); glBindTexture(GL_TEXTURE_2D, id);
return std::make_unique<OpenGLDisplayWidgetTexture>(id, width, height); return std::make_unique<OpenGLHostDisplayTexture>(id, width, height);
} }
private: private:
@ -51,13 +49,16 @@ private:
u32 m_height; 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 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 void* OpenGLHostDisplay::GetRenderDevice() const
@ -70,22 +71,16 @@ void* OpenGLHostDisplay::GetRenderContext() const
return m_gl_context.get(); 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, std::unique_ptr<HostDisplayTexture> OpenGLHostDisplay::CreateTexture(u32 width, u32 height, const void* initial_data,
u32 initial_data_stride, bool dynamic) 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, void OpenGLHostDisplay::UpdateTexture(HostDisplayTexture* texture, u32 x, u32 y, u32 width, u32 height,
const void* texture_data, u32 texture_data_stride) 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); Assert((texture_data_stride % sizeof(u32)) == 0);
GLint old_texture_binding = 0, old_alignment = 0, old_row_length = 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 const char* OpenGLHostDisplay::GetGLSLVersionString() const
{ {
if (m_gl_context->IsGLES()) if (GetRenderAPI() == RenderAPI::OpenGLES)
{ {
if (GLAD_GL_ES_VERSION_3_0) if (GLAD_GL_ES_VERSION_3_0)
return "#version 300 es"; return "#version 300 es";
@ -154,7 +149,7 @@ std::string OpenGLHostDisplay::GetGLSLVersionHeader() const
{ {
std::string header = GetGLSLVersionString(); std::string header = GetGLSLVersionString();
header += "\n\n"; header += "\n\n";
if (m_gl_context->IsGLES()) if (GetRenderAPI() == RenderAPI::OpenGLES)
{ {
header += "precision highp float;\n"; header += "precision highp float;\n";
header += "precision highp int;\n\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); 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(); return m_window_info.type != WindowInfo::Type::Surfaceless;
m_window_height = m_widget->scaledWindowHeight(); }
std::optional<WindowInfo> wi = getWindowInfo(); bool OpenGLHostDisplay::CreateRenderDevice(const WindowInfo& wi, std::string_view adapter_name, bool debug_device)
if (!wi.has_value()) {
return false; m_gl_context = GL::Context::Create(wi);
m_gl_context = GL::Context::Create(wi.value());
if (!m_gl_context) if (!m_gl_context)
{ {
Log_ErrorPrintf("Failed to create any GL context"); Log_ErrorPrintf("Failed to create any GL context");
return false; 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; 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) 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);
glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS); // glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
} }
if (!QtHostDisplay::initializeDeviceContext(shader_cache_directory, debug_device)) if (!CreateResources())
{ return false;
m_gl_context->DoneCurrent();
if (ImGui::GetCurrentContext() && !CreateImGuiContext())
return false; return false;
}
return true; return true;
} }
bool OpenGLHostDisplay::activateDeviceContext() bool OpenGLHostDisplay::MakeRenderContextCurrent()
{ {
if (!m_gl_context->MakeCurrent()) if (!m_gl_context->MakeCurrent())
{ {
@ -236,56 +236,92 @@ bool OpenGLHostDisplay::activateDeviceContext()
return true; 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->DoneCurrent();
m_gl_context.reset(); m_gl_context.reset();
} }
bool OpenGLHostDisplay::recreateSurface() bool OpenGLHostDisplay::ChangeRenderWindow(const WindowInfo& new_wi)
{ {
m_window_width = m_widget->scaledWindowWidth(); Assert(m_gl_context);
m_window_height = m_widget->scaledWindowHeight();
if (m_gl_context) if (!m_gl_context->ChangeSurface(new_wi))
{ {
std::optional<WindowInfo> wi = getWindowInfo(); Log_ErrorPrintf("Failed to change surface");
if (!wi.has_value() || !m_gl_context->ChangeSurface(wi.value())) return false;
return false; }
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())
{
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; return true;
} }
void OpenGLHostDisplay::destroySurface() {} void OpenGLHostDisplay::ResizeRenderWindow(s32 new_window_width, s32 new_window_height)
bool OpenGLHostDisplay::createImGuiContext()
{ {
if (!QtHostDisplay::createImGuiContext()) if (!m_gl_context)
return false; return;
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 (!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())) if (!ImGui_ImplOpenGL3_Init(GetGLSLVersionString()))
return false; return false;
ImGui_ImplOpenGL3_NewFrame(); ImGui_ImplOpenGL3_NewFrame();
ImGui::NewFrame();
return true; return true;
} }
void OpenGLHostDisplay::destroyImGuiContext() void OpenGLHostDisplay::DestroyImGuiContext()
{ {
ImGui_ImplOpenGL3_Shutdown(); ImGui_ImplOpenGL3_Shutdown();
QtHostDisplay::destroyImGuiContext();
} }
bool OpenGLHostDisplay::createDeviceResources() bool OpenGLHostDisplay::CreateResources()
{ {
static constexpr char fullscreen_quad_vertex_shader[] = R"( static constexpr char fullscreen_quad_vertex_shader[] = R"(
uniform vec4 u_src_rect; uniform vec4 u_src_rect;
@ -318,7 +354,7 @@ void main()
return false; return false;
} }
if (!m_gl_context->IsGLES()) if (GetRenderAPI() != RenderAPI::OpenGLES)
m_display_program.BindFragData(0, "o_col0"); m_display_program.BindFragData(0, "o_col0");
if (!m_display_program.Link()) if (!m_display_program.Link())
@ -345,10 +381,8 @@ void main()
return true; return true;
} }
void OpenGLHostDisplay::destroyDeviceResources() void OpenGLHostDisplay::DestroyResources()
{ {
QtHostDisplay::destroyDeviceResources();
if (m_display_vao != 0) if (m_display_vao != 0)
glDeleteVertexArrays(1, &m_display_vao); glDeleteVertexArrays(1, &m_display_vao);
if (m_display_linear_sampler != 0) if (m_display_linear_sampler != 0)
@ -359,49 +393,95 @@ void OpenGLHostDisplay::destroyDeviceResources()
m_display_program.Destroy(); m_display_program.Destroy();
} }
void OpenGLHostDisplay::Render() bool OpenGLHostDisplay::Render()
{ {
glDisable(GL_SCISSOR_TEST); glDisable(GL_SCISSOR_TEST);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
glClearColor(0.0f, 0.0f, 0.0f, 1.0f); glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT); glClear(GL_COLOR_BUFFER_BIT);
renderDisplay(); RenderDisplay();
ImGui::Render(); if (ImGui::GetCurrentContext())
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); RenderImGui();
RenderSoftwareCursor();
m_gl_context->SwapBuffers(); m_gl_context->SwapBuffers();
ImGui::NewFrame(); if (ImGui::GetCurrentContext())
ImGui_ImplOpenGL3_NewFrame(); ImGui_ImplOpenGL3_NewFrame();
return true;
}
void OpenGLHostDisplay::RenderImGui()
{
ImGui::Render();
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
GL::Program::ResetLastProgram(); GL::Program::ResetLastProgram();
} }
void OpenGLHostDisplay::renderDisplay() void OpenGLHostDisplay::RenderDisplay()
{ {
if (!m_display_texture_handle) if (!HasDisplayTexture())
return; return;
const auto [vp_left, vp_top, vp_width, vp_height] = const auto [left, top, width, height] = CalculateDrawRect(GetWindowWidth(), GetWindowHeight(), m_display_top_margin);
CalculateDrawRect(m_window_width, m_window_height, 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_BLEND);
glDisable(GL_CULL_FACE); glDisable(GL_CULL_FACE);
glDisable(GL_DEPTH_TEST); glDisable(GL_DEPTH_TEST);
glDisable(GL_SCISSOR_TEST); glDisable(GL_SCISSOR_TEST);
glDepthMask(GL_FALSE); glDepthMask(GL_FALSE);
m_display_program.Bind(); m_display_program.Bind();
m_display_program.Uniform4f( m_display_program.Uniform4f(0, static_cast<float>(texture_view_x) / static_cast<float>(texture_width),
0, static_cast<float>(m_display_texture_view_x) / static_cast<float>(m_display_texture_width), static_cast<float>(texture_view_y) / static_cast<float>(texture_height),
static_cast<float>(m_display_texture_view_y) / static_cast<float>(m_display_texture_height), (static_cast<float>(texture_view_width) - 0.5f) / static_cast<float>(texture_width),
(static_cast<float>(m_display_texture_view_width) - 0.5f) / static_cast<float>(m_display_texture_width), (static_cast<float>(texture_view_height) + 0.5f) / static_cast<float>(texture_height));
(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>(texture_handle)));
glBindTexture(GL_TEXTURE_2D, static_cast<GLuint>(reinterpret_cast<uintptr_t>(m_display_texture_handle))); glBindSampler(0, linear_filter ? m_display_linear_sampler : m_display_nearest_sampler);
glBindSampler(0, m_display_linear_filtering ? m_display_linear_sampler : m_display_nearest_sampler);
glBindVertexArray(m_display_vao); glBindVertexArray(m_display_vao);
glDrawArrays(GL_TRIANGLES, 0, 3); glDrawArrays(GL_TRIANGLES, 0, 3);
glBindSampler(0, 0); 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"); 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); Assert(!m_swap_chain);
@ -115,22 +130,39 @@ bool VulkanHostDisplay::RecreateSwapChain(const WindowInfo& new_wi)
return false; 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; 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(); 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"); Panic("Failed to resize swap chain");
ImGui::GetIO().DisplaySize.x = static_cast<float>(m_swap_chain->GetWidth()); m_window_info.surface_width = m_swap_chain->GetWidth();
ImGui::GetIO().DisplaySize.y = static_cast<float>(m_swap_chain->GetHeight()); 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(); m_swap_chain.reset();
} }
@ -193,27 +225,47 @@ void VulkanHostDisplay::SetVSync(bool enabled)
m_swap_chain->SetVSync(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"); Log_ErrorPrintf("Failed to create Vulkan context");
return false; 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; 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); return static_cast<bool>(g_vulkan_context);
} }
bool VulkanHostDisplay::HasRenderSurface() const
{
return static_cast<bool>(m_swap_chain);
}
bool VulkanHostDisplay::CreateResources() bool VulkanHostDisplay::CreateResources()
{ {
static constexpr char fullscreen_quad_vertex_shader[] = R"( static constexpr char fullscreen_quad_vertex_shader[] = R"(
@ -329,24 +381,37 @@ void VulkanHostDisplay::DestroyImGuiContext()
ImGui_ImplVulkan_Shutdown(); ImGui_ImplVulkan_Shutdown();
} }
void VulkanHostDisplay::DestroyContext() void VulkanHostDisplay::DestroyRenderDevice()
{ {
if (!g_vulkan_context) if (!g_vulkan_context)
return; return;
g_vulkan_context->WaitForGPUIdle(); g_vulkan_context->WaitForGPUIdle();
if (ImGui::GetCurrentContext())
DestroyImGuiContext();
DestroyResources();
Vulkan::ShaderCache::Destroy();
DestroyRenderSurface();
Vulkan::Context::Destroy(); Vulkan::Context::Destroy();
} }
void VulkanHostDisplay::DestroyShaderCache() bool VulkanHostDisplay::MakeRenderContextCurrent()
{ {
Vulkan::ShaderCache::Destroy(); return true;
}
bool VulkanHostDisplay::DoneRenderContextCurrent()
{
return true;
} }
bool VulkanHostDisplay::CreateImGuiContext() bool VulkanHostDisplay::CreateImGuiContext()
{ {
ImGui::GetIO().DisplaySize.x = static_cast<float>(m_swap_chain->GetWidth()); ImGui::GetIO().DisplaySize.x = static_cast<float>(m_window_info.surface_width);
ImGui::GetIO().DisplaySize.y = static_cast<float>(m_swap_chain->GetHeight()); ImGui::GetIO().DisplaySize.y = static_cast<float>(m_window_info.surface_height);
ImGui_ImplVulkan_InitInfo vii = {}; ImGui_ImplVulkan_InitInfo vii = {};
vii.Instance = g_vulkan_context->GetVulkanInstance(); vii.Instance = g_vulkan_context->GetVulkanInstance();
@ -370,14 +435,14 @@ bool VulkanHostDisplay::CreateImGuiContext()
return true; return true;
} }
bool VulkanHostDisplay::BeginRender() bool VulkanHostDisplay::Render()
{ {
VkResult res = m_swap_chain->AcquireNextImage(); VkResult res = m_swap_chain->AcquireNextImage();
if (res != VK_SUCCESS) if (res != VK_SUCCESS)
{ {
if (res == VK_SUBOPTIMAL_KHR || res == VK_ERROR_OUT_OF_DATE_KHR) if (res == VK_SUBOPTIMAL_KHR || res == VK_ERROR_OUT_OF_DATE_KHR)
{ {
ResizeSwapChain(0, 0); ResizeRenderWindow(0, 0);
res = m_swap_chain->AcquireNextImage(); res = m_swap_chain->AcquireNextImage();
} }
@ -409,13 +474,13 @@ bool VulkanHostDisplay::BeginRender()
1u, 1u,
&clear_value}; &clear_value};
vkCmdBeginRenderPass(cmdbuffer, &rp, VK_SUBPASS_CONTENTS_INLINE); vkCmdBeginRenderPass(cmdbuffer, &rp, VK_SUBPASS_CONTENTS_INLINE);
return true;
}
void VulkanHostDisplay::EndRenderAndPresent() RenderDisplay();
{
VkCommandBuffer cmdbuffer = g_vulkan_context->GetCurrentCommandBuffer(); if (ImGui::GetCurrentContext())
Vulkan::Texture& swap_chain_texture = m_swap_chain->GetCurrentTexture(); RenderImGui();
RenderSoftwareCursor();
vkCmdEndRenderPass(cmdbuffer); vkCmdEndRenderPass(cmdbuffer);
@ -426,13 +491,26 @@ void VulkanHostDisplay::EndRenderAndPresent()
m_swap_chain->GetCurrentImageIndex()); m_swap_chain->GetCurrentImageIndex());
g_vulkan_context->MoveToNextCommandBuffer(); g_vulkan_context->MoveToNextCommandBuffer();
ImGui::NewFrame(); if (ImGui::GetCurrentContext())
ImGui_ImplVulkan_NewFrame(); 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, 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, s32 texture_height, s32 texture_view_x, s32 texture_view_y,
u32 texture_view_width, u32 texture_view_height, bool linear_filter) s32 texture_view_width, s32 texture_view_height, bool linear_filter)
{ {
VkCommandBuffer cmdbuffer = g_vulkan_context->GetCurrentCommandBuffer(); VkCommandBuffer cmdbuffer = g_vulkan_context->GetCurrentCommandBuffer();
@ -469,6 +547,15 @@ void VulkanHostDisplay::RenderImGui()
ImGui_ImplVulkan_RenderDrawData(ImGui::GetDrawData(), g_vulkan_context->GetCurrentCommandBuffer()); 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) void VulkanHostDisplay::RenderSoftwareCursor(s32 left, s32 top, s32 width, s32 height, HostDisplayTexture* texture)
{ {
VkCommandBuffer cmdbuffer = g_vulkan_context->GetCurrentCommandBuffer(); VkCommandBuffer cmdbuffer = g_vulkan_context->GetCurrentCommandBuffer();

View File

@ -14,57 +14,44 @@ class SwapChain;
namespace FrontendCommon { namespace FrontendCommon {
class VulkanHostDisplay class VulkanHostDisplay : public HostDisplay
{ {
public: public:
VulkanHostDisplay(); VulkanHostDisplay();
~VulkanHostDisplay(); virtual ~VulkanHostDisplay();
ALWAYS_INLINE HostDisplay::RenderAPI GetRenderAPI() const { return HostDisplay::RenderAPI::Vulkan; } virtual RenderAPI GetRenderAPI() const override;
ALWAYS_INLINE void* GetRenderDevice() const { return nullptr; } virtual void* GetRenderDevice() const override;
ALWAYS_INLINE void* GetRenderContext() const { return nullptr; } virtual void* GetRenderContext() const override;
std::unique_ptr<HostDisplayTexture> CreateTexture(u32 width, u32 height, const void* data, u32 data_stride, virtual bool HasRenderDevice() const override;
bool dynamic); virtual bool HasRenderSurface() const override;
void UpdateTexture(HostDisplayTexture* texture, u32 x, u32 y, u32 width, u32 height, const void* data,
u32 data_stride); 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, 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(); virtual bool Render() override;
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();
bool CreateContextAndSwapChain(const WindowInfo& wi, std::string_view gpu_name, bool debug_device);
bool HasContext() const;
void DestroyContext();
void CreateShaderCache(std::string_view shader_cache_directory, bool debug_shaders);
void DestroyShaderCache();
bool CreateResources();
void DestroyResources();
bool CreateImGuiContext();
void DestroyImGuiContext();
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); }
bool RecreateSwapChain(const WindowInfo& new_wi);
void ResizeSwapChain(u32 new_width, u32 new_height);
void DestroySwapChain();
static std::vector<std::string> EnumerateAdapterNames(); static std::vector<std::string> EnumerateAdapterNames();
private: protected:
struct PushConstants struct PushConstants
{ {
float src_rect_left; float src_rect_left;
@ -73,6 +60,21 @@ private:
float src_rect_height; 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; std::unique_ptr<Vulkan::SwapChain> m_swap_chain;
VkDescriptorSetLayout m_descriptor_set_layout = VK_NULL_HANDLE; VkDescriptorSetLayout m_descriptor_set_layout = VK_NULL_HANDLE;