mirror of
https://github.com/WinampDesktop/winamp.git
synced 2025-06-18 14:55:46 -04:00
HostDisplay: Move most backend logic to FrontendCommon
This commit is contained in:
@ -41,10 +41,6 @@ add_executable(duckstation-qt
|
||||
mainwindow.ui
|
||||
memorycardsettingswidget.cpp
|
||||
memorycardsettingswidget.h
|
||||
openglhostdisplay.cpp
|
||||
openglhostdisplay.h
|
||||
qthostdisplay.cpp
|
||||
qthostdisplay.h
|
||||
qtdisplaywidget.cpp
|
||||
qtdisplaywidget.h
|
||||
qthostinterface.cpp
|
||||
@ -58,19 +54,12 @@ add_executable(duckstation-qt
|
||||
settingsdialog.cpp
|
||||
settingsdialog.h
|
||||
settingsdialog.ui
|
||||
vulkanhostdisplay.cpp
|
||||
vulkanhostdisplay.h
|
||||
)
|
||||
|
||||
target_include_directories(duckstation-qt PRIVATE "${Qt5Gui_PRIVATE_INCLUDE_DIRS}")
|
||||
target_link_libraries(duckstation-qt PRIVATE frontend-common core common imgui glad minizip scmversion vulkan-loader Qt5::Core Qt5::Gui Qt5::Widgets Qt5::Network)
|
||||
|
||||
if(WIN32)
|
||||
target_sources(duckstation-qt PRIVATE
|
||||
d3d11hostdisplay.cpp
|
||||
d3d11hostdisplay.h
|
||||
)
|
||||
|
||||
# We want a Windows subsystem application not console.
|
||||
set_target_properties(duckstation-qt PROPERTIES
|
||||
WIN32_EXECUTABLE TRUE
|
||||
|
@ -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();
|
||||
}
|
@ -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;
|
||||
};
|
@ -39,7 +39,6 @@
|
||||
<ClCompile Include="advancedsettingswidget.cpp" />
|
||||
<ClCompile Include="audiosettingswidget.cpp" />
|
||||
<ClCompile Include="consolesettingswidget.cpp" />
|
||||
<ClCompile Include="d3d11hostdisplay.cpp" />
|
||||
<ClCompile Include="generalsettingswidget.cpp" />
|
||||
<ClCompile Include="gpusettingswidget.cpp" />
|
||||
<ClCompile Include="hotkeysettingswidget.cpp" />
|
||||
@ -50,19 +49,15 @@
|
||||
<ClCompile Include="gamepropertiesdialog.cpp" />
|
||||
<ClCompile Include="main.cpp" />
|
||||
<ClCompile Include="mainwindow.cpp" />
|
||||
<ClCompile Include="openglhostdisplay.cpp" />
|
||||
<ClCompile Include="controllersettingswidget.cpp" />
|
||||
<ClCompile Include="memorycardsettingswidget.cpp" />
|
||||
<ClCompile Include="qthostdisplay.cpp" />
|
||||
<ClCompile Include="qthostinterface.cpp" />
|
||||
<ClCompile Include="qtprogresscallback.cpp" />
|
||||
<ClCompile Include="qtsettingsinterface.cpp" />
|
||||
<ClCompile Include="qtutils.cpp" />
|
||||
<ClCompile Include="settingsdialog.cpp" />
|
||||
<ClCompile Include="vulkanhostdisplay.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="vulkanhostdisplay.h" />
|
||||
<QtMoc Include="aboutdialog.h" />
|
||||
<QtMoc Include="audiosettingswidget.h" />
|
||||
<QtMoc Include="controllersettingswidget.h" />
|
||||
@ -73,9 +68,7 @@
|
||||
<QtMoc Include="hotkeysettingswidget.h" />
|
||||
<QtMoc Include="inputbindingwidgets.h" />
|
||||
<QtMoc Include="advancedsettingswidget.h" />
|
||||
<ClInclude Include="d3d11hostdisplay.h" />
|
||||
<QtMoc Include="qtprogresscallback.h" />
|
||||
<ClInclude Include="qthostdisplay.h" />
|
||||
<ClInclude Include="resource.h" />
|
||||
<ClInclude Include="settingwidgetbinder.h" />
|
||||
<QtMoc Include="consolesettingswidget.h" />
|
||||
@ -83,25 +76,18 @@
|
||||
<QtMoc Include="gamelistwidget.h" />
|
||||
<QtMoc Include="gamepropertiesdialog.h" />
|
||||
<QtMoc Include="mainwindow.h" />
|
||||
<ClInclude Include="openglhostdisplay.h" />
|
||||
<QtMoc Include="qthostinterface.h" />
|
||||
<ClInclude Include="qtsettingsinterface.h" />
|
||||
<ClInclude Include="qtutils.h" />
|
||||
<QtMoc Include="settingsdialog.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\dep\glad\glad.vcxproj">
|
||||
<Project>{43540154-9e1e-409c-834f-b84be5621388}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\..\dep\imgui\imgui.vcxproj">
|
||||
<Project>{bb08260f-6fbc-46af-8924-090ee71360c6}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\..\dep\minizip\minizip.vcxproj">
|
||||
<Project>{8bda439c-6358-45fb-9994-2ff083babe06}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\..\dep\vulkan-loader\vulkan-loader.vcxproj">
|
||||
<Project>{9c8ddeb0-2b8f-4f5f-ba86-127cdf27f035}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\common\common.vcxproj">
|
||||
<Project>{ee054e08-3799-4a59-a422-18259c105ffd}</Project>
|
||||
</ProjectReference>
|
||||
|
@ -30,9 +30,6 @@
|
||||
<ClCompile Include="$(IntDir)moc_qtprogresscallback.cpp" />
|
||||
<ClCompile Include="generalsettingswidget.cpp" />
|
||||
<ClCompile Include="$(IntDir)moc_generalsettingswidget.cpp" />
|
||||
<ClCompile Include="qthostdisplay.cpp" />
|
||||
<ClCompile Include="openglhostdisplay.cpp" />
|
||||
<ClCompile Include="d3d11hostdisplay.cpp" />
|
||||
<ClCompile Include="advancedsettingswidget.cpp" />
|
||||
<ClCompile Include="$(IntDir)moc_advancedsettingswidget.cpp" />
|
||||
<ClCompile Include="gamepropertiesdialog.cpp" />
|
||||
@ -43,18 +40,13 @@
|
||||
<ClCompile Include="$(IntDir)moc_aboutdialog.cpp" />
|
||||
<ClCompile Include="$(IntDir)moc_controllersettingswidget.cpp" />
|
||||
<ClCompile Include="$(IntDir)moc_memorycardsettingswidget.cpp" />
|
||||
<ClCompile Include="$(IntDir)qrc_icons.cpp" />
|
||||
<ClCompile Include="vulkanhostdisplay.cpp" />
|
||||
<ClCompile Include="$(IntDir)qrc_resources.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="qtsettingsinterface.h" />
|
||||
<ClInclude Include="qtutils.h" />
|
||||
<ClInclude Include="settingwidgetbinder.h" />
|
||||
<ClInclude Include="qthostdisplay.h" />
|
||||
<ClInclude Include="d3d11hostdisplay.h" />
|
||||
<ClInclude Include="openglhostdisplay.h" />
|
||||
<ClInclude Include="resource.h" />
|
||||
<ClInclude Include="vulkanhostdisplay.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Filter Include="resources">
|
||||
@ -62,7 +54,7 @@
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<QtResource Include="resources\icons.qrc" />
|
||||
<QtResource Include="resources\resources.qrc" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<QtMoc Include="consolesettingswidget.h" />
|
||||
|
@ -2,13 +2,13 @@
|
||||
#include "aboutdialog.h"
|
||||
#include "common/assert.h"
|
||||
#include "core/game_list.h"
|
||||
#include "core/host_display.h"
|
||||
#include "core/settings.h"
|
||||
#include "core/system.h"
|
||||
#include "gamelistsettingswidget.h"
|
||||
#include "gamelistwidget.h"
|
||||
#include "gamepropertiesdialog.h"
|
||||
#include "qtdisplaywidget.h"
|
||||
#include "qthostdisplay.h"
|
||||
#include "qthostinterface.h"
|
||||
#include "qtsettingsinterface.h"
|
||||
#include "qtutils.h"
|
||||
@ -22,8 +22,8 @@
|
||||
#include <QtGui/QCursor>
|
||||
#include <QtGui/QWindowStateChangeEvent>
|
||||
#include <QtWidgets/QFileDialog>
|
||||
#include <QtWidgets/QStyleFactory>
|
||||
#include <QtWidgets/QMessageBox>
|
||||
#include <QtWidgets/QStyleFactory>
|
||||
#include <cmath>
|
||||
|
||||
static constexpr char DISC_IMAGE_FILTER[] =
|
||||
@ -72,14 +72,13 @@ bool MainWindow::confirmMessage(const QString& message)
|
||||
return (result == QMessageBox::Yes);
|
||||
}
|
||||
|
||||
void MainWindow::createDisplay(QThread* worker_thread, const QString& adapter_name, bool use_debug_device,
|
||||
bool fullscreen, bool render_to_main)
|
||||
QtDisplayWidget* MainWindow::createDisplay(QThread* worker_thread, const QString& adapter_name, bool use_debug_device,
|
||||
bool fullscreen, bool render_to_main)
|
||||
{
|
||||
Assert(!m_host_display && !m_display_widget);
|
||||
Assert(!fullscreen || !render_to_main);
|
||||
|
||||
m_host_display = m_host_interface->createHostDisplay();
|
||||
m_display_widget = m_host_display->createWidget((!fullscreen && render_to_main) ? m_ui.mainContainer : nullptr);
|
||||
m_display_widget = new QtDisplayWidget((!fullscreen && render_to_main) ? m_ui.mainContainer : nullptr);
|
||||
m_display_widget->setWindowTitle(windowTitle());
|
||||
m_display_widget->setWindowIcon(windowIcon());
|
||||
|
||||
@ -101,32 +100,37 @@ void MainWindow::createDisplay(QThread* worker_thread, const QString& adapter_na
|
||||
// we need the surface visible.. this might be able to be replaced with something else
|
||||
QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
|
||||
|
||||
if (!m_host_display->createDeviceContext(adapter_name, use_debug_device))
|
||||
std::optional<WindowInfo> wi = m_display_widget->getWindowInfo();
|
||||
if (!wi.has_value())
|
||||
{
|
||||
reportError(tr("Failed to create host display device context."));
|
||||
return;
|
||||
reportError(tr("Failed to get window info from widget"));
|
||||
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_rendering_to_main = (!is_fullscreen && m_display_widget->parent());
|
||||
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)
|
||||
{
|
||||
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);
|
||||
destroyDisplayWidget();
|
||||
m_display_widget = new QtDisplayWidget((!fullscreen && render_to_main) ? m_ui.mainContainer : nullptr);
|
||||
m_display_widget->setWindowTitle(windowTitle());
|
||||
m_display_widget->setWindowIcon(windowIcon());
|
||||
|
||||
@ -148,30 +152,44 @@ void MainWindow::updateDisplay(QThread* worker_thread, bool fullscreen, bool ren
|
||||
// we need the surface visible.. this might be able to be replaced with something else
|
||||
QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
|
||||
|
||||
if (!m_host_display->recreateSurface())
|
||||
std::optional<WindowInfo> wi = m_display_widget->getWindowInfo();
|
||||
if (!wi.has_value())
|
||||
{
|
||||
reportError(tr("Failed to get new window info from widget"));
|
||||
destroyDisplayWidget();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!m_host_display->ChangeRenderWindow(wi.value()))
|
||||
Panic("Failed to recreate surface on new widget.");
|
||||
|
||||
m_display_widget->setFocus();
|
||||
|
||||
QSignalBlocker blocker(m_ui.actionFullscreen);
|
||||
m_ui.actionFullscreen->setChecked(fullscreen);
|
||||
return m_display_widget;
|
||||
}
|
||||
|
||||
void MainWindow::destroyDisplay()
|
||||
{
|
||||
DebugAssert(m_host_display && m_display_widget);
|
||||
m_host_display = nullptr;
|
||||
destroyDisplayWidget();
|
||||
}
|
||||
|
||||
void MainWindow::destroyDisplayWidget()
|
||||
{
|
||||
if (!m_display_widget)
|
||||
return;
|
||||
|
||||
if (m_display_widget->parent())
|
||||
{
|
||||
m_ui.mainContainer->removeWidget(m_display_widget);
|
||||
switchToGameListView();
|
||||
m_ui.mainContainer->removeWidget(m_display_widget);
|
||||
}
|
||||
|
||||
m_host_display->destroyWidget();
|
||||
delete m_display_widget;
|
||||
m_display_widget = nullptr;
|
||||
|
||||
delete m_host_display;
|
||||
m_host_display = nullptr;
|
||||
}
|
||||
|
||||
void MainWindow::focusDisplayWidget()
|
||||
|
@ -12,9 +12,9 @@ class QThread;
|
||||
|
||||
class GameListWidget;
|
||||
class QtHostInterface;
|
||||
class QtHostDisplay;
|
||||
class QtDisplayWidget;
|
||||
|
||||
class HostDisplay;
|
||||
struct GameListEntry;
|
||||
|
||||
class MainWindow final : public QMainWindow
|
||||
@ -29,9 +29,9 @@ private Q_SLOTS:
|
||||
void reportError(const QString& message);
|
||||
void reportMessage(const QString& message);
|
||||
bool confirmMessage(const QString& message);
|
||||
void createDisplay(QThread* worker_thread, const QString& adapter_name, bool use_debug_device, bool fullscreen,
|
||||
bool render_to_main);
|
||||
void updateDisplay(QThread* worker_thread, bool fullscreen, bool render_to_main);
|
||||
QtDisplayWidget* createDisplay(QThread* worker_thread, const QString& adapter_name, bool use_debug_device,
|
||||
bool fullscreen, bool render_to_main);
|
||||
QtDisplayWidget* updateDisplay(QThread* worker_thread, bool fullscreen, bool render_to_main);
|
||||
void destroyDisplay();
|
||||
void focusDisplayWidget();
|
||||
|
||||
@ -72,6 +72,7 @@ private:
|
||||
void updateEmulationActions(bool starting, bool running);
|
||||
void switchToGameListView();
|
||||
void switchToEmulationView();
|
||||
void destroyDisplayWidget();
|
||||
SettingsDialog* getSettingsDialog();
|
||||
void doSettings(SettingsDialog::Category category = SettingsDialog::Category::Count);
|
||||
void updateDebugMenuCPUExecutionMode();
|
||||
@ -83,7 +84,7 @@ private:
|
||||
|
||||
GameListWidget* m_game_list_widget = nullptr;
|
||||
|
||||
QtHostDisplay* m_host_display = nullptr;
|
||||
HostDisplay* m_host_display = nullptr;
|
||||
QtDisplayWidget* m_display_widget = nullptr;
|
||||
|
||||
QLabel* m_status_speed_widget = nullptr;
|
||||
|
@ -1,407 +0,0 @@
|
||||
#include "openglhostdisplay.h"
|
||||
#include "common/assert.h"
|
||||
#include "common/log.h"
|
||||
#include "imgui.h"
|
||||
#include "qtdisplaywidget.h"
|
||||
#include "qthostinterface.h"
|
||||
#include <QtGui/QKeyEvent>
|
||||
#include <QtGui/QWindow>
|
||||
#include <array>
|
||||
#include <imgui_impl_opengl3.h>
|
||||
#include <tuple>
|
||||
Log_SetChannel(OpenGLHostDisplay);
|
||||
|
||||
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(QtHostInterface* host_interface) : QtHostDisplay(host_interface) {}
|
||||
|
||||
OpenGLHostDisplay::~OpenGLHostDisplay() = default;
|
||||
|
||||
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)
|
||||
{
|
||||
QtHostDisplay::WindowResized(new_window_width, new_window_height);
|
||||
m_gl_context->ResizeSurface(static_cast<u32>(new_window_width), static_cast<u32>(new_window_height));
|
||||
}
|
||||
|
||||
std::unique_ptr<HostDisplayTexture> OpenGLHostDisplay::CreateTexture(u32 width, u32 height, const void* initial_data,
|
||||
u32 initial_data_stride, bool dynamic)
|
||||
{
|
||||
return OpenGLDisplayWidgetTexture::Create(width, height, initial_data, initial_data_stride);
|
||||
}
|
||||
|
||||
void OpenGLHostDisplay::UpdateTexture(HostDisplayTexture* texture, u32 x, u32 y, u32 width, u32 height,
|
||||
const void* texture_data, u32 texture_data_stride)
|
||||
{
|
||||
OpenGLDisplayWidgetTexture* tex = static_cast<OpenGLDisplayWidgetTexture*>(texture);
|
||||
Assert((texture_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, texture_data_stride / sizeof(u32));
|
||||
|
||||
glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, width, height, GL_RGBA, GL_UNSIGNED_BYTE, texture_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, ¤t_fbo);
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
|
||||
m_gl_context->SetSwapInterval(enabled ? 1 : 0);
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, current_fbo);
|
||||
}
|
||||
|
||||
const char* OpenGLHostDisplay::GetGLSLVersionString() const
|
||||
{
|
||||
if (m_gl_context->IsGLES())
|
||||
{
|
||||
if (GLAD_GL_ES_VERSION_3_0)
|
||||
return "#version 300 es";
|
||||
else
|
||||
return "#version 100";
|
||||
}
|
||||
else
|
||||
{
|
||||
if (GLAD_GL_VERSION_3_3)
|
||||
return "#version 330";
|
||||
else
|
||||
return "#version 130";
|
||||
}
|
||||
}
|
||||
|
||||
std::string OpenGLHostDisplay::GetGLSLVersionHeader() const
|
||||
{
|
||||
std::string header = GetGLSLVersionString();
|
||||
header += "\n\n";
|
||||
if (m_gl_context->IsGLES())
|
||||
{
|
||||
header += "precision highp float;\n";
|
||||
header += "precision highp int;\n\n";
|
||||
}
|
||||
|
||||
return header;
|
||||
}
|
||||
|
||||
static void APIENTRY GLDebugCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length,
|
||||
const GLchar* message, const void* userParam)
|
||||
{
|
||||
switch (severity)
|
||||
{
|
||||
case GL_DEBUG_SEVERITY_HIGH_KHR:
|
||||
Log_ErrorPrintf(message);
|
||||
break;
|
||||
case GL_DEBUG_SEVERITY_MEDIUM_KHR:
|
||||
Log_WarningPrint(message);
|
||||
break;
|
||||
case GL_DEBUG_SEVERITY_LOW_KHR:
|
||||
Log_InfoPrintf(message);
|
||||
break;
|
||||
case GL_DEBUG_SEVERITY_NOTIFICATION:
|
||||
// Log_DebugPrint(message);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool OpenGLHostDisplay::hasDeviceContext() const
|
||||
{
|
||||
return static_cast<bool>(m_gl_context);
|
||||
}
|
||||
|
||||
bool OpenGLHostDisplay::createDeviceContext(const QString& adapter_name, bool debug_device)
|
||||
{
|
||||
m_window_width = m_widget->scaledWindowWidth();
|
||||
m_window_height = m_widget->scaledWindowHeight();
|
||||
|
||||
std::optional<WindowInfo> wi = getWindowInfo();
|
||||
if (!wi.has_value())
|
||||
return false;
|
||||
|
||||
m_gl_context = GL::Context::Create(wi.value());
|
||||
if (!m_gl_context)
|
||||
{
|
||||
Log_ErrorPrintf("Failed to create any GL context");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OpenGLHostDisplay::initializeDeviceContext(std::string_view shader_cache_directory, bool debug_device)
|
||||
{
|
||||
if (debug_device && GLAD_GL_KHR_debug)
|
||||
{
|
||||
glad_glDebugMessageCallbackKHR(GLDebugCallback, nullptr);
|
||||
glEnable(GL_DEBUG_OUTPUT);
|
||||
glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
|
||||
}
|
||||
|
||||
if (!QtHostDisplay::initializeDeviceContext(shader_cache_directory, debug_device))
|
||||
{
|
||||
m_gl_context->DoneCurrent();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OpenGLHostDisplay::activateDeviceContext()
|
||||
{
|
||||
if (!m_gl_context->MakeCurrent())
|
||||
{
|
||||
Log_ErrorPrintf("Failed to make GL context current");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void OpenGLHostDisplay::deactivateDeviceContext()
|
||||
{
|
||||
m_gl_context->DoneCurrent();
|
||||
}
|
||||
|
||||
void OpenGLHostDisplay::destroyDeviceContext()
|
||||
{
|
||||
QtHostDisplay::destroyDeviceContext();
|
||||
m_gl_context->DoneCurrent();
|
||||
m_gl_context.reset();
|
||||
}
|
||||
|
||||
bool OpenGLHostDisplay::recreateSurface()
|
||||
{
|
||||
m_window_width = m_widget->scaledWindowWidth();
|
||||
m_window_height = m_widget->scaledWindowHeight();
|
||||
|
||||
if (m_gl_context)
|
||||
{
|
||||
std::optional<WindowInfo> wi = getWindowInfo();
|
||||
if (!wi.has_value() || !m_gl_context->ChangeSurface(wi.value()))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void OpenGLHostDisplay::destroySurface() {}
|
||||
|
||||
bool OpenGLHostDisplay::createImGuiContext()
|
||||
{
|
||||
if (!QtHostDisplay::createImGuiContext())
|
||||
return false;
|
||||
|
||||
if (!ImGui_ImplOpenGL3_Init(GetGLSLVersionString()))
|
||||
return false;
|
||||
|
||||
ImGui_ImplOpenGL3_NewFrame();
|
||||
ImGui::NewFrame();
|
||||
return true;
|
||||
}
|
||||
|
||||
void OpenGLHostDisplay::destroyImGuiContext()
|
||||
{
|
||||
ImGui_ImplOpenGL3_Shutdown();
|
||||
|
||||
QtHostDisplay::destroyImGuiContext();
|
||||
}
|
||||
|
||||
bool OpenGLHostDisplay::createDeviceResources()
|
||||
{
|
||||
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 = vec4(texture(samp0, v_tex0).rgb, 1.0);
|
||||
}
|
||||
)";
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
void OpenGLHostDisplay::destroyDeviceResources()
|
||||
{
|
||||
QtHostDisplay::destroyDeviceResources();
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
void OpenGLHostDisplay::Render()
|
||||
{
|
||||
glDisable(GL_SCISSOR_TEST);
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
|
||||
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
|
||||
renderDisplay();
|
||||
|
||||
ImGui::Render();
|
||||
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
|
||||
|
||||
m_gl_context->SwapBuffers();
|
||||
|
||||
ImGui::NewFrame();
|
||||
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 - (m_display_top_margin + 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);
|
||||
}
|
@ -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;
|
||||
};
|
@ -1,8 +1,8 @@
|
||||
#include "qtdisplaywidget.h"
|
||||
#include "common/bitutils.h"
|
||||
#include "qthostdisplay.h"
|
||||
#include "qthostinterface.h"
|
||||
#include "qtutils.h"
|
||||
#include <QtCore/QDebug>
|
||||
#include <QtGui/QGuiApplication>
|
||||
#include <QtGui/QKeyEvent>
|
||||
#include <QtGui/QScreen>
|
||||
@ -10,6 +10,10 @@
|
||||
#include <QtGui/QWindowStateChangeEvent>
|
||||
#include <cmath>
|
||||
|
||||
#if !defined(WIN32) && !defined(APPLE)
|
||||
#include <qpa/qplatformnativeinterface.h>
|
||||
#endif
|
||||
|
||||
QtDisplayWidget::QtDisplayWidget(QWidget* parent) : QWidget(parent)
|
||||
{
|
||||
// We want a native window for both D3D and OpenGL.
|
||||
@ -47,6 +51,46 @@ int QtDisplayWidget::scaledWindowHeight() const
|
||||
return static_cast<int>(std::ceil(static_cast<qreal>(height()) * devicePixelRatioFromScreen()));
|
||||
}
|
||||
|
||||
std::optional<WindowInfo> QtDisplayWidget::getWindowInfo() const
|
||||
{
|
||||
WindowInfo wi;
|
||||
|
||||
// Windows and Apple are easy here since there's no display connection.
|
||||
#if defined(WIN32)
|
||||
wi.type = WindowInfo::Type::Win32;
|
||||
wi.window_handle = reinterpret_cast<void*>(winId());
|
||||
#elif defined(__APPLE__)
|
||||
wi.type = WindowInfo::Type::MacOS;
|
||||
wi.window_handle = reinterpret_cast<void*>(winId());
|
||||
#else
|
||||
QPlatformNativeInterface* pni = QGuiApplication::platformNativeInterface();
|
||||
const QString platform_name = QGuiApplication::platformName();
|
||||
if (platform_name == QStringLiteral("xcb"))
|
||||
{
|
||||
wi.type = WindowInfo::Type::X11;
|
||||
wi.display_connection = pni->nativeResourceForWindow("display", windowHandle());
|
||||
wi.window_handle = reinterpret_cast<void*>(winId());
|
||||
}
|
||||
else if (platform_name == QStringLiteral("wayland"))
|
||||
{
|
||||
wi.type = WindowInfo::Type::Wayland;
|
||||
wi.display_connection = pni->nativeResourceForWindow("display", windowHandle());
|
||||
wi.window_handle = pni->nativeResourceForWindow("surface", windowHandle());
|
||||
}
|
||||
else
|
||||
{
|
||||
qCritical() << "Unknown PNI platform " << platform_name;
|
||||
return std::nullopt;
|
||||
}
|
||||
#endif
|
||||
|
||||
wi.surface_width = scaledWindowWidth();
|
||||
wi.surface_height = scaledWindowHeight();
|
||||
wi.surface_format = WindowInfo::SurfaceFormat::RGB8;
|
||||
|
||||
return wi;
|
||||
}
|
||||
|
||||
QPaintEngine* QtDisplayWidget::paintEngine() const
|
||||
{
|
||||
return nullptr;
|
||||
|
@ -1,6 +1,8 @@
|
||||
#pragma once
|
||||
#include "common/types.h"
|
||||
#include "common/window_info.h"
|
||||
#include <QtWidgets/QWidget>
|
||||
#include <optional>
|
||||
|
||||
class QtDisplayWidget final : public QWidget
|
||||
{
|
||||
@ -16,6 +18,8 @@ public:
|
||||
int scaledWindowHeight() const;
|
||||
qreal devicePixelRatioFromScreen() const;
|
||||
|
||||
std::optional<WindowInfo> getWindowInfo() const;
|
||||
|
||||
Q_SIGNALS:
|
||||
void windowResizedEvent(int width, int height);
|
||||
void windowRestoredEvent();
|
||||
|
@ -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;
|
||||
}
|
@ -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;
|
||||
};
|
@ -8,14 +8,17 @@
|
||||
#include "core/game_list.h"
|
||||
#include "core/gpu.h"
|
||||
#include "core/system.h"
|
||||
#include "frontend-common/imgui_styles.h"
|
||||
#include "frontend-common/opengl_host_display.h"
|
||||
#include "frontend-common/sdl_audio_stream.h"
|
||||
#include "frontend-common/sdl_controller_interface.h"
|
||||
#include "frontend-common/vulkan_host_display.h"
|
||||
#include "imgui.h"
|
||||
#include "mainwindow.h"
|
||||
#include "openglhostdisplay.h"
|
||||
#include "qtdisplaywidget.h"
|
||||
#include "qtprogresscallback.h"
|
||||
#include "qtsettingsinterface.h"
|
||||
#include "qtutils.h"
|
||||
#include "vulkanhostdisplay.h"
|
||||
#include <QtCore/QCoreApplication>
|
||||
#include <QtCore/QDateTime>
|
||||
#include <QtCore/QDebug>
|
||||
@ -27,7 +30,7 @@
|
||||
Log_SetChannel(QtHostInterface);
|
||||
|
||||
#ifdef WIN32
|
||||
#include "d3d11hostdisplay.h"
|
||||
#include "frontend-common/d3d11_host_display.h"
|
||||
#endif
|
||||
|
||||
QtHostInterface::QtHostInterface(QObject* parent) : QObject(parent), CommonHostInterface()
|
||||
@ -37,7 +40,7 @@ QtHostInterface::QtHostInterface(QObject* parent) : QObject(parent), CommonHostI
|
||||
|
||||
QtHostInterface::~QtHostInterface()
|
||||
{
|
||||
Assert(!getHostDisplay());
|
||||
Assert(!m_display);
|
||||
}
|
||||
|
||||
const char* QtHostInterface::GetFrontendName() const
|
||||
@ -177,7 +180,7 @@ void QtHostInterface::applySettings()
|
||||
if (m_system)
|
||||
{
|
||||
const bool render_to_main = getSettingValue("Main/RenderToMainWindow", true).toBool();
|
||||
if (getHostDisplay() && !m_is_fullscreen && render_to_main != m_is_rendering_to_main)
|
||||
if (m_display && !m_is_fullscreen && render_to_main != m_is_rendering_to_main)
|
||||
{
|
||||
m_is_rendering_to_main = render_to_main;
|
||||
updateDisplayState();
|
||||
@ -271,10 +274,10 @@ void QtHostInterface::onDisplayWindowMouseButtonEvent(int button, bool pressed)
|
||||
void QtHostInterface::onHostDisplayWindowResized(int width, int height)
|
||||
{
|
||||
// this can be null if it was destroyed and the main thread is late catching up
|
||||
if (!getHostDisplay())
|
||||
if (!m_display)
|
||||
return;
|
||||
|
||||
getHostDisplay()->WindowResized(width, height);
|
||||
m_display->ResizeRenderWindow(width, height);
|
||||
|
||||
// re-render the display, since otherwise it will be out of date and stretched if paused
|
||||
if (m_system)
|
||||
@ -289,7 +292,7 @@ void QtHostInterface::redrawDisplayWindow()
|
||||
return;
|
||||
}
|
||||
|
||||
if (!getHostDisplay() || !m_system)
|
||||
if (!m_display || !m_system)
|
||||
return;
|
||||
|
||||
renderDisplay();
|
||||
@ -306,71 +309,69 @@ void QtHostInterface::toggleFullscreen()
|
||||
SetFullscreen(!m_is_fullscreen);
|
||||
}
|
||||
|
||||
QtHostDisplay* QtHostInterface::getHostDisplay()
|
||||
{
|
||||
return static_cast<QtHostDisplay*>(m_display);
|
||||
}
|
||||
|
||||
bool QtHostInterface::AcquireHostDisplay()
|
||||
{
|
||||
Assert(!m_display);
|
||||
|
||||
m_is_rendering_to_main = getSettingValue("Main/RenderToMainWindow", true).toBool();
|
||||
emit createDisplayRequested(m_worker_thread, QString::fromStdString(m_settings.gpu_adapter),
|
||||
m_settings.gpu_use_debug_device, m_is_fullscreen, m_is_rendering_to_main);
|
||||
Assert(m_display);
|
||||
|
||||
if (!getHostDisplay()->hasDeviceContext())
|
||||
QtDisplayWidget* display_widget =
|
||||
createDisplayRequested(m_worker_thread, QString::fromStdString(m_settings.gpu_adapter),
|
||||
m_settings.gpu_use_debug_device, m_is_fullscreen, m_is_rendering_to_main);
|
||||
if (!display_widget || !m_display->HasRenderDevice())
|
||||
{
|
||||
emit destroyDisplayRequested();
|
||||
m_display = nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!getHostDisplay()->activateDeviceContext() ||
|
||||
!getHostDisplay()->initializeDeviceContext(GetShaderCacheDirectory(), m_settings.gpu_use_debug_device))
|
||||
createImGuiContext(display_widget->devicePixelRatioFromScreen());
|
||||
|
||||
if (!m_display->MakeRenderContextCurrent() ||
|
||||
!m_display->InitializeRenderDevice(GetShaderCacheDirectory(), m_settings.gpu_use_debug_device))
|
||||
{
|
||||
getHostDisplay()->destroyDeviceContext();
|
||||
destroyImGuiContext();
|
||||
m_display->DestroyRenderDevice();
|
||||
emit destroyDisplayRequested();
|
||||
m_display = nullptr;
|
||||
m_display.reset();
|
||||
return false;
|
||||
}
|
||||
|
||||
connectDisplaySignals();
|
||||
connectDisplaySignals(display_widget);
|
||||
ImGui::NewFrame();
|
||||
return true;
|
||||
}
|
||||
|
||||
QtHostDisplay* QtHostInterface::createHostDisplay()
|
||||
HostDisplay* QtHostInterface::createHostDisplay()
|
||||
{
|
||||
Assert(!getHostDisplay());
|
||||
|
||||
switch (m_settings.gpu_renderer)
|
||||
{
|
||||
case GPURenderer::HardwareVulkan:
|
||||
m_display = new VulkanHostDisplay(this);
|
||||
m_display = std::make_unique<FrontendCommon::VulkanHostDisplay>();
|
||||
break;
|
||||
|
||||
case GPURenderer::HardwareOpenGL:
|
||||
#ifndef WIN32
|
||||
default:
|
||||
#endif
|
||||
m_display = new OpenGLHostDisplay(this);
|
||||
m_display = std::make_unique<FrontendCommon::OpenGLHostDisplay>();
|
||||
break;
|
||||
|
||||
#ifdef WIN32
|
||||
case GPURenderer::HardwareD3D11:
|
||||
default:
|
||||
m_display = new D3D11HostDisplay(this);
|
||||
m_display = std::make_unique<FrontendCommon::D3D11HostDisplay>();
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
|
||||
return getHostDisplay();
|
||||
return m_display.get();
|
||||
}
|
||||
|
||||
void QtHostInterface::connectDisplaySignals()
|
||||
void QtHostInterface::connectDisplaySignals(QtDisplayWidget* widget)
|
||||
{
|
||||
QtDisplayWidget* widget = getHostDisplay()->getWidget();
|
||||
widget->disconnect(this);
|
||||
|
||||
connect(widget, &QtDisplayWidget::windowResizedEvent, this, &QtHostInterface::onHostDisplayWindowResized);
|
||||
connect(widget, &QtDisplayWidget::windowRestoredEvent, this, &QtHostInterface::redrawDisplayWindow);
|
||||
connect(widget, &QtDisplayWidget::windowClosedEvent, this, &QtHostInterface::powerOffSystem,
|
||||
@ -380,21 +381,16 @@ void QtHostInterface::connectDisplaySignals()
|
||||
connect(widget, &QtDisplayWidget::windowMouseButtonEvent, this, &QtHostInterface::onDisplayWindowMouseButtonEvent);
|
||||
}
|
||||
|
||||
void QtHostInterface::disconnectDisplaySignals()
|
||||
{
|
||||
getHostDisplay()->getWidget()->disconnect(this);
|
||||
}
|
||||
|
||||
void QtHostInterface::updateDisplayState()
|
||||
{
|
||||
// this expects the context to get moved back to us afterwards
|
||||
getHostDisplay()->deactivateDeviceContext();
|
||||
emit updateDisplayRequested(m_worker_thread, m_is_fullscreen, m_is_rendering_to_main);
|
||||
if (!getHostDisplay()->activateDeviceContext())
|
||||
m_display->DoneRenderContextCurrent();
|
||||
|
||||
QtDisplayWidget* display_widget = updateDisplayRequested(m_worker_thread, m_is_fullscreen, m_is_rendering_to_main);
|
||||
if (!display_widget || !m_display->MakeRenderContextCurrent())
|
||||
Panic("Failed to make device context current after updating");
|
||||
|
||||
getHostDisplay()->updateImGuiDisplaySize();
|
||||
connectDisplaySignals();
|
||||
connectDisplaySignals(display_widget);
|
||||
redrawDisplayWindow();
|
||||
UpdateSpeedLimiterState();
|
||||
}
|
||||
@ -403,9 +399,10 @@ void QtHostInterface::ReleaseHostDisplay()
|
||||
{
|
||||
Assert(m_display);
|
||||
|
||||
getHostDisplay()->destroyDeviceContext();
|
||||
m_display->DestroyRenderDevice();
|
||||
destroyImGuiContext();
|
||||
emit destroyDisplayRequested();
|
||||
m_display = nullptr;
|
||||
m_display.reset();
|
||||
m_is_fullscreen = false;
|
||||
}
|
||||
|
||||
@ -938,6 +935,7 @@ void QtHostInterface::renderDisplay()
|
||||
DrawImGuiWindows();
|
||||
|
||||
m_display->Render();
|
||||
ImGui::NewFrame();
|
||||
|
||||
m_system->GetGPU()->RestoreGraphicsAPIState();
|
||||
}
|
||||
@ -950,6 +948,25 @@ void QtHostInterface::wakeThread()
|
||||
QMetaObject::invokeMethod(m_worker_thread_event_loop, "quit", Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
void QtHostInterface::createImGuiContext(float framebuffer_scale)
|
||||
{
|
||||
ImGui::CreateContext();
|
||||
|
||||
auto& io = ImGui::GetIO();
|
||||
io.IniFilename = nullptr;
|
||||
io.DisplayFramebufferScale.x = framebuffer_scale;
|
||||
io.DisplayFramebufferScale.y = framebuffer_scale;
|
||||
ImGui::GetStyle().ScaleAllSizes(framebuffer_scale);
|
||||
|
||||
ImGui::StyleColorsDarker();
|
||||
ImGui::AddRobotoRegularFont(15.0f * framebuffer_scale);
|
||||
}
|
||||
|
||||
void QtHostInterface::destroyImGuiContext()
|
||||
{
|
||||
ImGui::DestroyContext();
|
||||
}
|
||||
|
||||
QtHostInterface::Thread::Thread(QtHostInterface* parent) : QThread(parent), m_parent(parent) {}
|
||||
|
||||
QtHostInterface::Thread::~Thread() = default;
|
||||
|
@ -5,6 +5,7 @@
|
||||
#include "frontend-common/common_host_interface.h"
|
||||
#include <QtCore/QByteArray>
|
||||
#include <QtCore/QObject>
|
||||
#include <QtCore/QString>
|
||||
#include <QtCore/QSettings>
|
||||
#include <QtCore/QThread>
|
||||
#include <atomic>
|
||||
@ -25,8 +26,7 @@ class QTimer;
|
||||
class GameList;
|
||||
|
||||
class MainWindow;
|
||||
|
||||
class QtHostDisplay;
|
||||
class QtDisplayWidget;
|
||||
|
||||
Q_DECLARE_METATYPE(SystemBootParameters);
|
||||
|
||||
@ -66,7 +66,7 @@ public:
|
||||
|
||||
ALWAYS_INLINE MainWindow* getMainWindow() const { return m_main_window; }
|
||||
void setMainWindow(MainWindow* window);
|
||||
QtHostDisplay* createHostDisplay();
|
||||
HostDisplay* createHostDisplay();
|
||||
|
||||
void populateSaveStateMenus(const char* game_code, QMenu* load_menu, QMenu* save_menu);
|
||||
|
||||
@ -96,9 +96,9 @@ Q_SIGNALS:
|
||||
void emulationPaused(bool paused);
|
||||
void stateSaved(const QString& game_code, bool global, qint32 slot);
|
||||
void gameListRefreshed();
|
||||
void createDisplayRequested(QThread* worker_thread, const QString& adapter_name, bool use_debug_device,
|
||||
bool fullscreen, bool render_to_main);
|
||||
void updateDisplayRequested(QThread* worker_thread, bool fullscreen, bool render_to_main);
|
||||
QtDisplayWidget* createDisplayRequested(QThread* worker_thread, const QString& adapter_name, bool use_debug_device,
|
||||
bool fullscreen, bool render_to_main);
|
||||
QtDisplayWidget* updateDisplayRequested(QThread* worker_thread, bool fullscreen, bool render_to_main);
|
||||
void focusDisplayWidgetRequested();
|
||||
void destroyDisplayRequested();
|
||||
void systemPerformanceCountersUpdated(float speed, float fps, float vps, float avg_frame_time,
|
||||
@ -186,21 +186,21 @@ private:
|
||||
Common::Event m_init_event;
|
||||
};
|
||||
|
||||
QtHostDisplay* getHostDisplay();
|
||||
|
||||
void createBackgroundControllerPollTimer();
|
||||
void destroyBackgroundControllerPollTimer();
|
||||
void startBackgroundControllerPollTimer();
|
||||
void stopBackgroundControllerPollTimer();
|
||||
|
||||
void createImGuiContext(float framebuffer_scale);
|
||||
void destroyImGuiContext();
|
||||
|
||||
void createThread();
|
||||
void stopThread();
|
||||
void threadEntryPoint();
|
||||
bool initializeOnThread();
|
||||
void shutdownOnThread();
|
||||
void renderDisplay();
|
||||
void connectDisplaySignals();
|
||||
void disconnectDisplaySignals();
|
||||
void connectDisplaySignals(QtDisplayWidget* widget);
|
||||
void updateDisplayState();
|
||||
void wakeThread();
|
||||
|
||||
|
@ -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();
|
||||
}
|
@ -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;
|
||||
};
|
Reference in New Issue
Block a user