Rewrite host GPU abstraction

- Don't have to repeat the same thing for 4 renderers.
 - Add native Metal renderer.
This commit is contained in:
Stenzek
2023-08-13 13:42:02 +10:00
parent bfa792ddbf
commit e3d9ba4c99
249 changed files with 28851 additions and 32222 deletions

View File

@ -198,7 +198,7 @@ if(WIN32)
)
endif()
if(APPLE)
if(APPLE AND NOT CMAKE_GENERATOR MATCHES "Xcode")
set(BUNDLE_PATH ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/DuckStation.app)
# Ask for an application bundle.

View File

@ -333,6 +333,8 @@ void AdvancedSettingsWidget::addTweakOptions()
Settings::DEFAULT_GPU_MAX_RUN_AHEAD);
addBooleanTweakOption(m_dialog, m_ui.tweakOptionTable, tr("Use Debug Host GPU Device"), "GPU", "UseDebugDevice",
false);
addBooleanTweakOption(m_dialog, m_ui.tweakOptionTable, tr("Disable Shader Cache"), "GPU", "DisableShaderCache",
false);
addBooleanTweakOption(m_dialog, m_ui.tweakOptionTable, tr("Stretch Display Vertically"), "Display",
"StretchVertically", false);
@ -388,6 +390,7 @@ void AdvancedSettingsWidget::onResetToDefaultClicked()
setIntRangeTweakOption(m_ui.tweakOptionTable, i++,
static_cast<int>(Settings::DEFAULT_GPU_MAX_RUN_AHEAD)); // GPU max run-ahead
setBooleanTweakOption(m_ui.tweakOptionTable, i++, false); // Use debug host GPU device
setBooleanTweakOption(m_ui.tweakOptionTable, i++, false); // Disable Shader Cache
setBooleanTweakOption(m_ui.tweakOptionTable, i++, false); // Stretch Display Vertically
setBooleanTweakOption(m_ui.tweakOptionTable, i++, true); // Increase Timer Resolution
setBooleanTweakOption(m_ui.tweakOptionTable, i++, false); // Allow booting without SBI file

View File

@ -12,11 +12,11 @@
// For enumerating adapters.
#ifdef _WIN32
#include "util/d3d11_host_display.h"
#include "util/d3d12_host_display.h"
#include "util/d3d11_device.h"
#include "util/d3d12_device.h"
#endif
#ifdef WITH_VULKAN
#include "util/vulkan_host_display.h"
#include "util/vulkan_device.h"
#endif
DisplaySettingsWidget::DisplaySettingsWidget(SettingsDialog* dialog, QWidget* parent)
@ -187,23 +187,28 @@ void DisplaySettingsWidget::setupAdditionalUi()
void DisplaySettingsWidget::populateGPUAdaptersAndResolutions()
{
HostDisplay::AdapterAndModeList aml;
GPUDevice::AdapterAndModeList aml;
bool thread_supported = false;
bool threaded_presentation_supported = false;
switch (static_cast<GPURenderer>(m_ui.renderer->currentIndex()))
{
#ifdef _WIN32
case GPURenderer::HardwareD3D11:
aml = D3D11HostDisplay::StaticGetAdapterAndModeList();
aml = D3D11Device::StaticGetAdapterAndModeList();
break;
case GPURenderer::HardwareD3D12:
aml = D3D12HostDisplay::StaticGetAdapterAndModeList();
aml = D3D12Device::StaticGetAdapterAndModeList();
break;
#endif
#ifdef __APPLE__
case GPURenderer::HardwareMetal:
aml = GPUDevice::WrapGetMetalAdapterAndModeList();
break;
#endif
#ifdef WITH_VULKAN
case GPURenderer::HardwareVulkan:
aml = VulkanHostDisplay::StaticGetAdapterAndModeList(nullptr);
aml = VulkanDevice::StaticGetAdapterAndModeList();
threaded_presentation_supported = true;
break;
#endif

View File

@ -2,8 +2,11 @@
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
#pragma once
#include "util/window_info.h"
#include "common/types.h"
#include "common/window_info.h"
#include <QtWidgets/QStackedWidget>
#include <QtWidgets/QWidget>
#include <optional>

View File

@ -24,7 +24,7 @@
#include "core/system.h"
#include "util/cd_image.h"
#include "util/host_display.h"
#include "util/gpu_device.h"
#include "util/platform_misc.h"
#include "common/assert.h"
@ -218,77 +218,33 @@ bool MainWindow::nativeEvent(const QByteArray& eventType, void* message, qintptr
#endif
bool MainWindow::createDisplay(bool fullscreen, bool render_to_main)
std::optional<WindowInfo> MainWindow::acquireRenderWindow(bool recreate_window, bool fullscreen, bool render_to_main,
bool surfaceless, bool use_main_window_pos)
{
Log_DevPrintf("createDisplay(%u, %u)", static_cast<u32>(fullscreen), static_cast<u32>(render_to_main));
const std::string fullscreen_mode(Host::GetBaseStringSettingValue("GPU", "FullscreenMode", ""));
const bool is_exclusive_fullscreen = (fullscreen && !fullscreen_mode.empty() && g_host_display->SupportsFullscreen());
createDisplayWidget(fullscreen, render_to_main, is_exclusive_fullscreen);
std::optional<WindowInfo> wi = m_display_widget->getWindowInfo();
if (!wi.has_value())
{
QMessageBox::critical(this, tr("Error"), tr("Failed to get window info from widget"));
destroyDisplayWidget(true);
return false;
}
g_emu_thread->connectDisplaySignals(m_display_widget);
if (!g_host_display->CreateDevice(wi.value(), System::ShouldUseVSync()))
{
QMessageBox::critical(this, tr("Error"), tr("Failed to create host display device context."));
destroyDisplayWidget(true);
return false;
}
m_display_created = true;
if (is_exclusive_fullscreen)
setDisplayFullscreen(fullscreen_mode);
updateWindowTitle();
updateWindowState();
m_ui.actionStartFullscreenUI->setEnabled(false);
m_ui.actionStartFullscreenUI2->setEnabled(false);
updateDisplayWidgetCursor();
updateDisplayRelatedActions(true, render_to_main, fullscreen);
m_display_widget->setFocus();
g_host_display->DoneCurrent();
return true;
}
bool MainWindow::updateDisplay(bool fullscreen, bool render_to_main, bool surfaceless)
{
Log_DevPrintf("updateDisplay() fullscreen=%s render_to_main=%s surfaceless=%s", fullscreen ? "true" : "false",
render_to_main ? "true" : "false", surfaceless ? "true" : "false");
Log_DevPrintf(
"acquireRenderWindow() recreate=%s fullscreen=%s render_to_main=%s surfaceless=%s use_main_window_pos=%s",
recreate_window ? "true" : "false", fullscreen ? "true" : "false", render_to_main ? "true" : "false",
surfaceless ? "true" : "false", use_main_window_pos ? "true" : "false");
QWidget* container =
m_display_container ? static_cast<QWidget*>(m_display_container) : static_cast<QWidget*>(m_display_widget);
const bool is_fullscreen = isRenderingFullscreen();
const bool is_rendering_to_main = isRenderingToMain();
const std::string fullscreen_mode(Host::GetBaseStringSettingValue("GPU", "FullscreenMode", ""));
const bool is_exclusive_fullscreen = (fullscreen && !fullscreen_mode.empty() && g_host_display->SupportsFullscreen());
const bool changing_surfaceless = (!m_display_widget != surfaceless);
if (fullscreen == is_fullscreen && is_rendering_to_main == render_to_main && !changing_surfaceless)
return true;
if (m_display_created && !recreate_window && fullscreen == is_fullscreen && is_rendering_to_main == render_to_main &&
!changing_surfaceless)
{
return m_display_widget ? m_display_widget->getWindowInfo() : WindowInfo();
}
// Skip recreating the surface if we're just transitioning between fullscreen and windowed with render-to-main off.
// .. except on Wayland, where everything tends to break if you don't recreate.
const bool has_container = (m_display_container != nullptr);
const bool needs_container = DisplayContainer::isNeeded(fullscreen, render_to_main);
if (!is_rendering_to_main && !render_to_main && !is_exclusive_fullscreen && has_container == needs_container &&
!needs_container && !changing_surfaceless)
if (m_display_created && !recreate_window && !is_rendering_to_main && !render_to_main &&
has_container == needs_container && !needs_container && !changing_surfaceless)
{
Log_DevPrintf("Toggling to %s without recreating surface", (fullscreen ? "fullscreen" : "windowed"));
if (g_host_display->IsFullscreen())
g_host_display->SetFullscreen(false, 0, 0, 0.0f);
// since we don't destroy the display widget, we need to save it here
if (!is_fullscreen && !is_rendering_to_main)
@ -306,53 +262,48 @@ bool MainWindow::updateDisplay(bool fullscreen, bool render_to_main, bool surfac
updateDisplayWidgetCursor();
m_display_widget->setFocus();
updateWindowState();
QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
return true;
return m_display_widget->getWindowInfo();
}
g_host_display->DestroySurface();
destroyDisplayWidget(surfaceless || fullscreen);
destroyDisplayWidget(surfaceless);
m_display_created = true;
// if we're going to surfaceless, we're done here
if (surfaceless)
{
updateWindowState();
updateDisplayRelatedActions(false, render_to_main, fullscreen);
return true;
}
return WindowInfo();
createDisplayWidget(fullscreen, render_to_main, is_exclusive_fullscreen);
createDisplayWidget(fullscreen, render_to_main, use_main_window_pos);
// we need the surface visible.. this might be able to be replaced with something else
QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
std::optional<WindowInfo> wi = m_display_widget->getWindowInfo();
if (!wi.has_value())
{
QMessageBox::critical(this, tr("Error"), tr("Failed to get new window info from widget"));
QMessageBox::critical(this, tr("Error"), tr("Failed to get window info from widget"));
destroyDisplayWidget(true);
return false;
return std::nullopt;
}
g_emu_thread->connectDisplaySignals(m_display_widget);
if (!g_host_display->ChangeWindow(wi.value()))
Panic("Failed to recreate surface on new widget.");
if (is_exclusive_fullscreen)
setDisplayFullscreen(fullscreen_mode);
updateWindowTitle();
updateWindowState();
m_ui.actionStartFullscreenUI->setEnabled(false);
m_ui.actionStartFullscreenUI2->setEnabled(false);
updateDisplayWidgetCursor();
updateDisplayRelatedActions(true, render_to_main, fullscreen);
m_display_widget->setFocus();
QSignalBlocker blocker(m_ui.actionFullscreen);
m_ui.actionFullscreen->setChecked(fullscreen);
return true;
return wi;
}
void MainWindow::createDisplayWidget(bool fullscreen, bool render_to_main, bool is_exclusive_fullscreen)
void MainWindow::createDisplayWidget(bool fullscreen, bool render_to_main, bool use_main_window_pos)
{
// If we're rendering to main and were hidden (e.g. coming back from fullscreen),
// make sure we're visible before trying to add ourselves. Otherwise Wayland breaks.
@ -388,20 +339,21 @@ void MainWindow::createDisplayWidget(bool fullscreen, bool render_to_main, bool
// and positioning has no effect anyway.
if (!s_use_central_widget)
{
if (isVisible())
if (isVisible() && g_emu_thread->shouldRenderToMain())
container->move(pos());
else
restoreDisplayWindowGeometryFromConfig();
}
if (!is_exclusive_fullscreen)
container->showFullScreen();
else
container->showNormal();
container->showFullScreen();
}
else if (!render_to_main)
{
restoreDisplayWindowGeometryFromConfig();
// See lameland comment above.
if (use_main_window_pos && !s_use_central_widget)
container->move(pos());
else
restoreDisplayWindowGeometryFromConfig();
container->showNormal();
}
else if (s_use_central_widget)
@ -420,27 +372,13 @@ void MainWindow::createDisplayWidget(bool fullscreen, bool render_to_main, bool
m_ui.mainContainer->setCurrentIndex(1);
}
updateDisplayRelatedActions(true, render_to_main, fullscreen);
// We need the surface visible.
QGuiApplication::sync();
}
void MainWindow::setDisplayFullscreen(const std::string& fullscreen_mode)
{
u32 width, height;
float refresh_rate;
bool result = false;
if (HostDisplay::ParseFullscreenMode(fullscreen_mode, &width, &height, &refresh_rate))
{
result = g_host_display->SetFullscreen(true, width, height, refresh_rate);
if (result)
Host::AddOSDMessage(TRANSLATE_STR("OSDMessage", "Acquired exclusive fullscreen."), 10.0f);
else
Host::AddOSDMessage(TRANSLATE_STR("OSDMessage", "Failed to acquire exclusive fullscreen."), 10.0f);
}
}
void MainWindow::displaySizeRequested(qint32 width, qint32 height)
void MainWindow::displayResizeRequested(qint32 width, qint32 height)
{
if (!m_display_widget)
return;
@ -462,7 +400,7 @@ void MainWindow::displaySizeRequested(qint32 width, qint32 height)
QtUtils::ResizePotentiallyFixedSizeWindow(this, width, height + extra_height);
}
void MainWindow::destroyDisplay()
void MainWindow::releaseRenderWindow()
{
// Now we can safely destroy the display window.
destroyDisplayWidget(true);
@ -470,6 +408,8 @@ void MainWindow::destroyDisplay()
updateDisplayRelatedActions(false, false, false);
m_ui.actionViewSystemDisplay->setEnabled(false);
m_ui.actionFullscreen->setEnabled(false);
m_ui.actionStartFullscreenUI->setEnabled(true);
m_ui.actionStartFullscreenUI2->setEnabled(true);
}
@ -532,7 +472,7 @@ void MainWindow::updateDisplayWidgetCursor()
void MainWindow::updateDisplayRelatedActions(bool has_surface, bool render_to_main, bool fullscreen)
{
// rendering to main, or switched to gamelist/grid
m_ui.actionViewSystemDisplay->setEnabled((has_surface && render_to_main) || (!has_surface && g_host_display));
m_ui.actionViewSystemDisplay->setEnabled((has_surface && render_to_main) || (!has_surface && g_gpu_device));
m_ui.menuWindowSize->setEnabled(has_surface && !fullscreen);
m_ui.actionFullscreen->setEnabled(has_surface);
@ -1842,10 +1782,10 @@ bool MainWindow::isShowingGameList() const
bool MainWindow::isRenderingFullscreen() const
{
if (!g_host_display || !m_display_widget)
if (!g_gpu_device || !m_display_widget)
return false;
return getDisplayContainer()->isFullScreen() || g_host_display->IsFullscreen();
return getDisplayContainer()->isFullScreen();
}
bool MainWindow::isRenderingToMain() const
@ -1999,12 +1939,11 @@ void MainWindow::connectSignals()
Qt::QueuedConnection);
connect(g_emu_thread, &EmuThread::errorReported, this, &MainWindow::reportError, Qt::BlockingQueuedConnection);
connect(g_emu_thread, &EmuThread::messageConfirmed, this, &MainWindow::confirmMessage, Qt::BlockingQueuedConnection);
connect(g_emu_thread, &EmuThread::createDisplayRequested, this, &MainWindow::createDisplay,
connect(g_emu_thread, &EmuThread::onAcquireRenderWindowRequested, this, &MainWindow::acquireRenderWindow,
Qt::BlockingQueuedConnection);
connect(g_emu_thread, &EmuThread::destroyDisplayRequested, this, &MainWindow::destroyDisplay);
connect(g_emu_thread, &EmuThread::updateDisplayRequested, this, &MainWindow::updateDisplay,
connect(g_emu_thread, &EmuThread::onReleaseRenderWindowRequested, this, &MainWindow::releaseRenderWindow);
connect(g_emu_thread, &EmuThread::onResizeRenderWindowRequested, this, &MainWindow::displayResizeRequested,
Qt::BlockingQueuedConnection);
connect(g_emu_thread, &EmuThread::displaySizeRequested, this, &MainWindow::displaySizeRequested);
connect(g_emu_thread, &EmuThread::focusDisplayWidgetRequested, this, &MainWindow::focusDisplayWidget);
connect(g_emu_thread, &EmuThread::systemStarting, this, &MainWindow::onSystemStarting);
connect(g_emu_thread, &EmuThread::systemStarted, this, &MainWindow::onSystemStarted);
@ -2415,6 +2354,7 @@ void MainWindow::closeEvent(QCloseEvent* event)
if (!s_system_valid)
{
saveGeometryToConfig();
g_emu_thread->stopFullscreenUI();
QMainWindow::closeEvent(event);
return;
}

View File

@ -2,6 +2,16 @@
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
#pragma once
#include "controllersettingsdialog.h"
#include "displaywidget.h"
#include "settingsdialog.h"
#include "ui_mainwindow.h"
#include "core/types.h"
#include "util/window_info.h"
#include <QtCore/QThread>
#include <QtWidgets/QLabel>
#include <QtWidgets/QMainWindow>
@ -10,13 +20,6 @@
#include <memory>
#include <optional>
#include "controllersettingsdialog.h"
#include "common/window_info.h"
#include "core/types.h"
#include "displaywidget.h"
#include "settingsdialog.h"
#include "ui_mainwindow.h"
class QLabel;
class QThread;
class QProgressBar;
@ -29,7 +32,7 @@ class CheatManagerDialog;
class DebuggerWindow;
class MainWindow;
class HostDisplay;
class GPUDevice;
namespace GameList {
struct Entry;
}
@ -110,10 +113,11 @@ public Q_SLOTS:
private Q_SLOTS:
void reportError(const QString& title, const QString& message);
bool confirmMessage(const QString& title, const QString& message);
bool createDisplay(bool fullscreen, bool render_to_main);
bool updateDisplay(bool fullscreen, bool render_to_main, bool surfaceless);
void displaySizeRequested(qint32 width, qint32 height);
void destroyDisplay();
std::optional<WindowInfo> acquireRenderWindow(bool recreate_window, bool fullscreen, bool render_to_main,
bool surfaceless, bool use_main_window_pos);
void displayResizeRequested(qint32 width, qint32 height);
void releaseRenderWindow();
void focusDisplayWidget();
void onMouseModeRequested(bool relative_mode, bool hide_cursor);
@ -208,11 +212,10 @@ private:
void restoreGeometryFromConfig();
void saveDisplayWindowGeometryToConfig();
void restoreDisplayWindowGeometryFromConfig();
void createDisplayWidget(bool fullscreen, bool render_to_main, bool is_exclusive_fullscreen);
void createDisplayWidget(bool fullscreen, bool render_to_main, bool use_main_window_pos);
void destroyDisplayWidget(bool show_game_list);
void updateDisplayWidgetCursor();
void updateDisplayRelatedActions(bool has_surface, bool render_to_main, bool fullscreen);
void setDisplayFullscreen(const std::string& fullscreen_mode);
SettingsDialog* getSettingsDialog();
void doSettings(const char* category = nullptr);

View File

@ -4,7 +4,9 @@
#include "postprocessingchainconfigwidget.h"
#include "postprocessingshaderconfigwidget.h"
#include "qthost.h"
#include "util/postprocessing_chain.h"
#include <QtGui/QCursor>
#include <QtWidgets/QMenu>
#include <QtWidgets/QMessageBox>
@ -71,9 +73,9 @@ void PostProcessingChainConfigWidget::updateList()
for (u32 i = 0; i < m_chain.GetStageCount(); i++)
{
const FrontendCommon::PostProcessingShader& shader = m_chain.GetShaderStage(i);
const PostProcessingShader* shader = m_chain.GetShaderStage(i);
QListWidgetItem* item = new QListWidgetItem(QString::fromStdString(shader.GetName()), m_ui.shaders);
QListWidgetItem* item = new QListWidgetItem(QString::fromStdString(shader->GetName()), m_ui.shaders);
item->setData(Qt::UserRole, QVariant(i));
}
@ -94,7 +96,7 @@ void PostProcessingChainConfigWidget::updateButtonStates(std::optional<u32> inde
m_ui.clear->setEnabled(!m_chain.IsEmpty());
// m_ui.reload->setEnabled(!m_chain.IsEmpty());
m_ui.shaderSettings->setEnabled(index.has_value() && (index.value() < m_chain.GetStageCount()) &&
m_chain.GetShaderStage(index.value()).HasOptions());
m_chain.GetShaderStage(index.value())->HasOptions());
if (index.has_value())
{
@ -112,7 +114,7 @@ void PostProcessingChainConfigWidget::onAddButtonClicked()
{
QMenu menu;
const std::vector<std::string> shaders(FrontendCommon::PostProcessingChain::GetAvailableShaderNames());
const std::vector<std::string> shaders(PostProcessingChain::GetAvailableShaderNames());
if (shaders.empty())
{
menu.addAction(tr("No Shaders Available"))->setEnabled(false);
@ -198,7 +200,7 @@ void PostProcessingChainConfigWidget::onShaderConfigButtonClicked()
std::optional<u32> index = getSelectedIndex();
if (index.has_value() && index.value() < m_chain.GetStageCount())
{
PostProcessingShaderConfigDialog shader_config(this, &m_chain.GetShaderStage(index.value()));
PostProcessingShaderConfigDialog shader_config(this, m_chain.GetShaderStage(index.value()));
connect(&shader_config, &PostProcessingShaderConfigDialog::configChanged, [this]() { configChanged(); });
shader_config.exec();
}

View File

@ -2,12 +2,15 @@
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
#pragma once
#include "common/types.h"
#include "ui_postprocessingchainconfigwidget.h"
#include "util/postprocessing_chain.h"
#include "common/types.h"
#include <QtWidgets/QWidget>
#include <optional>
#include <memory>
#include <optional>
#include <string_view>
namespace FrontendCommon {
@ -22,7 +25,7 @@ public:
PostProcessingChainConfigWidget(QWidget* parent);
~PostProcessingChainConfigWidget();
ALWAYS_INLINE FrontendCommon::PostProcessingChain& getChain() { return m_chain; }
ALWAYS_INLINE PostProcessingChain& getChain() { return m_chain; }
bool setConfigString(const std::string_view& config_string);
void setOptionsButtonVisible(bool visible);
@ -51,5 +54,5 @@ private:
Ui::PostProcessingChainConfigWidget m_ui;
FrontendCommon::PostProcessingChain m_chain;
PostProcessingChain m_chain;
};

View File

@ -71,11 +71,11 @@ void PostProcessingSettingsWidget::updateShaderConfigPanel(s32 index)
if (index < 0)
return;
FrontendCommon::PostProcessingShader& shader = m_ui.widget->getChain().GetShaderStage(static_cast<u32>(index));
if (!shader.HasOptions())
PostProcessingShader* shader = m_ui.widget->getChain().GetShaderStage(static_cast<u32>(index));
if (!shader->HasOptions())
return;
m_shader_config = new PostProcessingShaderConfigWidget(m_ui.scrollArea, &shader);
m_shader_config = new PostProcessingShaderConfigWidget(m_ui.scrollArea, shader);
connect(m_shader_config, &PostProcessingShaderConfigWidget::configChanged,
[this]() { onConfigChanged(m_ui.widget->getChain().GetConfigString()); });
m_ui.scrollArea->setWidget(m_shader_config);

View File

@ -8,10 +8,8 @@
#include <QtWidgets/QLabel>
#include <QtWidgets/QSlider>
using FrontendCommon::PostProcessingShader;
PostProcessingShaderConfigWidget::PostProcessingShaderConfigWidget(QWidget* parent,
FrontendCommon::PostProcessingShader* shader)
PostProcessingShader* shader)
: QWidget(parent), m_shader(shader)
{
createUi();
@ -146,7 +144,7 @@ void PostProcessingShaderConfigWidget::onResetToDefaultsClicked()
}
PostProcessingShaderConfigDialog::PostProcessingShaderConfigDialog(QWidget* parent,
FrontendCommon::PostProcessingShader* shader)
PostProcessingShader* shader)
: QDialog(parent)
{
setWindowTitle(tr("%1 Shader Options").arg(QString::fromStdString(shader->GetName())));

View File

@ -2,7 +2,9 @@
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
#pragma once
#include "util/postprocessing_shader.h"
#include <QtWidgets/QDialog>
#include <QtWidgets/QWidget>
@ -13,7 +15,7 @@ class PostProcessingShaderConfigWidget : public QWidget
Q_OBJECT
public:
PostProcessingShaderConfigWidget(QWidget* parent, FrontendCommon::PostProcessingShader* shader);
PostProcessingShaderConfigWidget(QWidget* parent, PostProcessingShader* shader);
~PostProcessingShaderConfigWidget();
QGridLayout* getLayout() { return m_layout; }
@ -28,7 +30,7 @@ private Q_SLOTS:
protected:
void createUi();
FrontendCommon::PostProcessingShader* m_shader;
PostProcessingShader* m_shader;
QGridLayout* m_layout;
};
@ -37,7 +39,7 @@ class PostProcessingShaderConfigDialog : public QDialog
Q_OBJECT
public:
PostProcessingShaderConfigDialog(QWidget* parent, FrontendCommon::PostProcessingShader* shader);
PostProcessingShaderConfigDialog(QWidget* parent, PostProcessingShader* shader);
~PostProcessingShaderConfigDialog();
Q_SIGNALS:

View File

@ -27,7 +27,6 @@
#include "common/log.h"
#include "common/path.h"
#include "common/string_util.h"
#include "common/window_info.h"
#include "util/audio_stream.h"
#include "util/imgui_manager.h"
@ -107,7 +106,9 @@ static bool s_start_fullscreen_ui_fullscreen = false;
EmuThread* g_emu_thread;
GDBServer* g_gdb_server;
EmuThread::EmuThread(QThread* ui_thread) : QThread(), m_ui_thread(ui_thread) {}
EmuThread::EmuThread(QThread* ui_thread) : QThread(), m_ui_thread(ui_thread)
{
}
EmuThread::~EmuThread() = default;
@ -336,24 +337,31 @@ void EmuThread::setInitialState(std::optional<bool> override_fullscreen)
m_is_surfaceless = false;
}
void EmuThread::checkForSettingsChanges(const Settings& old_settings)
{
if (g_main_window)
{
QMetaObject::invokeMethod(g_main_window, &MainWindow::checkForSettingChanges, Qt::QueuedConnection);
updatePerformanceCounters();
}
if (g_gpu_device)
{
const bool render_to_main = shouldRenderToMain();
if (m_is_rendering_to_main != render_to_main)
{
m_is_rendering_to_main = render_to_main;
g_gpu_device->UpdateWindow();
}
}
}
void Host::CheckForSettingsChanges(const Settings& old_settings)
{
CommonHost::CheckForSettingsChanges(old_settings);
g_emu_thread->checkForSettingsChanges(old_settings);
}
void EmuThread::checkForSettingsChanges(const Settings& old_settings)
{
const bool render_to_main = shouldRenderToMain();
if (m_is_rendering_to_main != render_to_main)
{
m_is_rendering_to_main = render_to_main;
updateDisplayState();
}
QMetaObject::invokeMethod(g_main_window, &MainWindow::checkForSettingChanges, Qt::QueuedConnection);
}
void EmuThread::setDefaultSettings(bool system /* = true */, bool controller /* = true */)
{
if (isOnThread())
@ -399,7 +407,7 @@ void Host::RequestResizeHostDisplay(s32 new_window_width, s32 new_window_height)
if (g_emu_thread->isFullscreen())
return;
emit g_emu_thread->displaySizeRequested(new_window_width, new_window_height);
emit g_emu_thread->onResizeRenderWindowRequested(new_window_width, new_window_height);
}
void EmuThread::applySettings(bool display_osd_messages /* = false */)
@ -456,8 +464,10 @@ void EmuThread::startFullscreenUI()
setInitialState(s_start_fullscreen_ui_fullscreen ? std::optional<bool>(true) : std::optional<bool>());
m_run_fullscreen_ui = true;
if (!acquireHostDisplay(Settings::GetRenderAPIForRenderer(g_settings.gpu_renderer)))
if (!Host::CreateGPUDevice(Settings::GetRenderAPIForRenderer(g_settings.gpu_renderer)) || !FullscreenUI::Initialize())
{
Host::ReleaseGPUDevice();
Host::ReleaseRenderWindow();
m_run_fullscreen_ui = false;
return;
}
@ -475,7 +485,7 @@ void EmuThread::stopFullscreenUI()
QMetaObject::invokeMethod(this, &EmuThread::stopFullscreenUI, Qt::QueuedConnection);
// wait until the host display is gone
while (g_host_display)
while (g_gpu_device)
QApplication::processEvents(QEventLoop::ExcludeUserInputEvents, 1);
return;
@ -484,11 +494,12 @@ void EmuThread::stopFullscreenUI()
if (System::IsValid())
shutdownSystem();
if (!g_host_display)
if (!g_gpu_device)
return;
m_run_fullscreen_ui = false;
releaseHostDisplay();
Host::ReleaseGPUDevice();
Host::ReleaseRenderWindow();
}
void EmuThread::bootSystem(std::shared_ptr<SystemBootParameters> params)
@ -506,7 +517,7 @@ void EmuThread::bootSystem(std::shared_ptr<SystemBootParameters> params)
return;
// force a frame to be drawn to repaint the window
renderDisplay(false);
Host::InvalidateDisplay();
}
void EmuThread::bootOrLoadState(std::string path)
@ -568,8 +579,8 @@ void EmuThread::onDisplayWindowMouseMoveEvent(bool relative, float x, float y)
DebugAssert(isOnThread());
if (!relative)
{
if (g_host_display)
g_host_display->SetMousePosition(static_cast<s32>(x), static_cast<s32>(y));
if (g_gpu_device)
g_gpu_device->SetMousePosition(static_cast<s32>(x), static_cast<s32>(y));
InputManager::UpdatePointerAbsolutePosition(0, x, y);
ImGuiManager::UpdateMousePosition(x, y);
@ -581,11 +592,11 @@ void EmuThread::onDisplayWindowMouseMoveEvent(bool relative, float x, float y)
if (y != 0.0f)
InputManager::UpdatePointerRelativeDelta(0, InputPointerAxis::Y, y);
if (g_host_display)
if (g_gpu_device)
{
const float abs_x = static_cast<float>(g_host_display->GetMousePositionX()) + x;
const float abs_y = static_cast<float>(g_host_display->GetMousePositionY()) + y;
g_host_display->SetMousePosition(static_cast<s32>(abs_x), static_cast<s32>(abs_y));
const float abs_x = static_cast<float>(g_gpu_device->GetMousePositionX()) + x;
const float abs_y = static_cast<float>(g_gpu_device->GetMousePositionY()) + y;
g_gpu_device->SetMousePosition(static_cast<s32>(abs_x), static_cast<s32>(abs_y));
ImGuiManager::UpdateMousePosition(abs_x, abs_y);
}
}
@ -612,33 +623,9 @@ void EmuThread::onDisplayWindowMouseWheelEvent(const QPoint& delta_angle)
InputManager::UpdatePointerRelativeDelta(0, InputPointerAxis::WheelY, dy);
}
void EmuThread::onDisplayWindowResized(int width, int height)
void EmuThread::onDisplayWindowResized(int width, int height, float scale)
{
// this can be null if it was destroyed and the main thread is late catching up
if (!g_host_display)
return;
Log_DevPrintf("Display window resized to %dx%d", width, height);
g_host_display->ResizeWindow(width, height);
ImGuiManager::WindowResized();
System::HostDisplayResized();
// re-render the display, since otherwise it will be out of date and stretched if paused
if (System::IsValid())
{
if (m_is_exclusive_fullscreen && !g_host_display->IsFullscreen())
{
// we lost exclusive fullscreen, switch to borderless
Host::AddOSDMessage(TRANSLATE_STR("OSDMessage", "Lost exclusive fullscreen."), 10.0f);
m_is_exclusive_fullscreen = false;
m_is_fullscreen = false;
m_lost_exclusive_fullscreen = true;
}
// force redraw if we're paused
if (!System::IsRunning() && !FullscreenUI::HasActiveWindow())
renderDisplay(false);
}
Host::ResizeDisplayWindow(width, height, scale);
}
void EmuThread::redrawDisplayWindow()
@ -649,10 +636,10 @@ void EmuThread::redrawDisplayWindow()
return;
}
if (!g_host_display || System::IsShutdown())
if (!g_gpu_device || System::IsShutdown())
return;
renderDisplay(false);
Host::RenderDisplay(false);
}
void EmuThread::toggleFullscreen()
@ -663,22 +650,24 @@ void EmuThread::toggleFullscreen()
return;
}
setFullscreen(!m_is_fullscreen);
setFullscreen(!m_is_fullscreen, true);
}
void EmuThread::setFullscreen(bool fullscreen)
void EmuThread::setFullscreen(bool fullscreen, bool allow_render_to_main)
{
if (!isOnThread())
{
QMetaObject::invokeMethod(this, "setFullscreen", Qt::QueuedConnection, Q_ARG(bool, fullscreen));
QMetaObject::invokeMethod(this, "setFullscreen", Qt::QueuedConnection, Q_ARG(bool, fullscreen),
Q_ARG(bool, allow_render_to_main));
return;
}
if (!g_host_display || m_is_fullscreen == fullscreen)
if (!g_gpu_device || m_is_fullscreen == fullscreen)
return;
m_is_fullscreen = fullscreen;
updateDisplayState();
m_is_rendering_to_main = allow_render_to_main && shouldRenderToMain();
Host::UpdateDisplayWindow();
}
bool Host::IsFullscreen()
@ -688,7 +677,7 @@ bool Host::IsFullscreen()
void Host::SetFullscreen(bool enabled)
{
g_emu_thread->setFullscreen(enabled);
g_emu_thread->setFullscreen(enabled, true);
}
void EmuThread::setSurfaceless(bool surfaceless)
@ -699,11 +688,11 @@ void EmuThread::setSurfaceless(bool surfaceless)
return;
}
if (!g_host_display || m_is_surfaceless == surfaceless)
if (!g_gpu_device || m_is_surfaceless == surfaceless)
return;
m_is_surfaceless = surfaceless;
updateDisplayState();
Host::UpdateDisplayWindow();
}
void EmuThread::requestDisplaySize(float scale)
@ -720,52 +709,25 @@ void EmuThread::requestDisplaySize(float scale)
System::RequestDisplaySize(scale);
}
bool EmuThread::acquireHostDisplay(RenderAPI api)
std::optional<WindowInfo> EmuThread::acquireRenderWindow(bool recreate_window)
{
if (g_host_display)
{
if (g_host_display->GetRenderAPI() == api)
{
// current is fine
return true;
}
DebugAssert(g_gpu_device);
u32 fs_width, fs_height;
float fs_refresh_rate;
m_is_exclusive_fullscreen = (m_is_fullscreen && g_gpu_device->SupportsExclusiveFullscreen() &&
GPUDevice::GetRequestedExclusiveFullscreenMode(&fs_width, &fs_height, &fs_refresh_rate));
// otherwise we need to switch
releaseHostDisplay();
}
const bool window_fullscreen = m_is_fullscreen && !m_is_exclusive_fullscreen;
const bool render_to_main = !m_is_exclusive_fullscreen && !window_fullscreen && m_is_rendering_to_main;
const bool use_main_window_pos = m_is_exclusive_fullscreen && shouldRenderToMain();
g_host_display = Host::CreateDisplayForAPI(api);
if (!g_host_display)
return false;
return emit onAcquireRenderWindowRequested(recreate_window, window_fullscreen, render_to_main, m_is_surfaceless,
use_main_window_pos);
}
if (!createDisplayRequested(m_is_fullscreen, m_is_rendering_to_main))
{
emit destroyDisplayRequested();
g_host_display.reset();
return false;
}
if (!g_host_display->MakeCurrent() || !g_host_display->SetupDevice() || !ImGuiManager::Initialize() ||
!CommonHost::CreateHostDisplayResources())
{
ImGuiManager::Shutdown();
CommonHost::ReleaseHostDisplayResources();
g_host_display.reset();
emit destroyDisplayRequested();
return false;
}
m_is_exclusive_fullscreen = g_host_display->IsFullscreen();
if (m_run_fullscreen_ui && !FullscreenUI::Initialize())
{
Log_ErrorPrint("Failed to initialize fullscreen UI");
releaseHostDisplay();
m_run_fullscreen_ui = false;
return false;
}
return true;
void EmuThread::releaseRenderWindow()
{
emit onReleaseRenderWindowRequested();
}
void EmuThread::connectDisplaySignals(DisplayWidget* widget)
@ -781,46 +743,6 @@ void EmuThread::connectDisplaySignals(DisplayWidget* widget)
connect(widget, &DisplayWidget::windowMouseWheelEvent, this, &EmuThread::onDisplayWindowMouseWheelEvent);
}
void EmuThread::updateDisplayState()
{
if (!g_host_display)
return;
// this expects the context to get moved back to us afterwards
g_host_display->DoneCurrent();
updateDisplayRequested(m_is_fullscreen, m_is_rendering_to_main && !m_is_fullscreen, m_is_surfaceless);
if (!g_host_display->MakeCurrent())
Panic("Failed to make device context current after updating");
m_is_exclusive_fullscreen = g_host_display->IsFullscreen();
ImGuiManager::WindowResized();
System::HostDisplayResized();
if (!System::IsShutdown())
{
System::UpdateSoftwareCursor();
if (!FullscreenUI::IsInitialized() || System::IsPaused())
redrawDisplayWindow();
}
System::UpdateSpeedLimiterState();
}
void EmuThread::releaseHostDisplay()
{
if (!g_host_display)
return;
CommonHost::ReleaseHostDisplayResources();
FullscreenUI::Shutdown();
ImGuiManager::Shutdown();
g_host_display.reset();
emit destroyDisplayRequested();
m_is_fullscreen = false;
}
void Host::OnSystemStarting()
{
CommonHost::OnSystemStarting();
@ -844,7 +766,7 @@ void Host::OnSystemPaused()
emit g_emu_thread->systemPaused();
g_emu_thread->startBackgroundControllerPollTimer();
g_emu_thread->renderDisplay(false);
Host::InvalidateDisplay();
}
void Host::OnSystemResumed()
@ -1246,7 +1168,7 @@ void EmuThread::singleStepCPU()
return;
System::SingleStepCPU();
renderDisplay(false);
Host::InvalidateDisplay();
}
void EmuThread::dumpRAM(const QString& filename)
@ -1449,11 +1371,11 @@ void EmuThread::run()
m_event_loop->processEvents(QEventLoop::AllEvents);
CommonHost::PumpMessagesOnCPUThread();
if (g_host_display)
if (g_gpu_device)
{
renderDisplay(false);
if (!g_host_display->IsVsyncEnabled())
g_host_display->ThrottlePresentation();
Host::RenderDisplay(false);
if (!g_gpu_device->IsVsyncEnabled())
g_gpu_device->ThrottlePresentation();
}
}
}
@ -1468,35 +1390,8 @@ void EmuThread::run()
moveToThread(m_ui_thread);
}
void EmuThread::renderDisplay(bool skip_present)
void Host::BeginPresentFrame()
{
// acquire for IO.MousePos.
std::atomic_thread_fence(std::memory_order_acquire);
if (!skip_present)
{
FullscreenUI::Render();
ImGuiManager::RenderTextOverlays();
ImGuiManager::RenderOSDMessages();
}
// Debug windows are always rendered, otherwise mouse input breaks on skip.
ImGuiManager::RenderOverlayWindows();
ImGuiManager::RenderDebugWindows();
g_host_display->Render(skip_present);
ImGuiManager::NewFrame();
}
void Host::InvalidateDisplay()
{
g_emu_thread->renderDisplay(false);
}
void Host::RenderDisplay(bool skip_present)
{
g_emu_thread->renderDisplay(skip_present);
}
void EmuThread::wakeThread()
@ -1605,39 +1500,34 @@ void Host::CommitBaseSettingChanges()
QtHost::QueueSettingsSave();
}
bool Host::AcquireHostDisplay(RenderAPI api)
std::optional<WindowInfo> Host::AcquireRenderWindow(bool recreate_window)
{
return g_emu_thread->acquireHostDisplay(api);
return g_emu_thread->acquireRenderWindow(recreate_window);
}
void Host::ReleaseHostDisplay()
void Host::ReleaseRenderWindow()
{
if (g_emu_thread->isRunningFullscreenUI())
{
// keep display alive when running fsui
return;
}
g_emu_thread->releaseHostDisplay();
g_emu_thread->releaseRenderWindow();
}
void EmuThread::updatePerformanceCounters()
{
GPURenderer renderer = GPURenderer::Count;
const RenderAPI render_api = g_gpu_device ? g_gpu_device->GetRenderAPI() : RenderAPI::None;
const bool hardware_renderer = g_gpu && g_gpu->IsHardwareRenderer();
u32 render_width = 0;
u32 render_height = 0;
if (g_gpu)
{
renderer = g_gpu->GetRendererType();
std::tie(render_width, render_height) = g_gpu->GetEffectiveDisplayResolution();
}
if (renderer != m_last_renderer)
if (render_api != m_last_render_api || hardware_renderer != m_last_hardware_renderer)
{
const QString renderer_str = hardware_renderer ? QString::fromUtf8(GPUDevice::RenderAPIToString(render_api)) :
qApp->translate("GPURenderer", "Software");
QMetaObject::invokeMethod(g_main_window->getStatusRendererWidget(), "setText", Qt::QueuedConnection,
Q_ARG(const QString&, QString::fromUtf8(Settings::GetRendererName(renderer))));
m_last_renderer = renderer;
Q_ARG(const QString&, renderer_str));
m_last_render_api = render_api;
m_last_hardware_renderer = hardware_renderer;
}
if (render_width != m_last_render_width || render_height != m_last_render_height)
{
@ -1674,7 +1564,8 @@ void EmuThread::resetPerformanceCounters()
m_last_video_fps = std::numeric_limits<float>::infinity();
m_last_render_width = std::numeric_limits<u32>::max();
m_last_render_height = std::numeric_limits<u32>::max();
m_last_renderer = GPURenderer::Count;
m_last_render_api = RenderAPI::None;
m_last_hardware_renderer = false;
QString blank;
QMetaObject::invokeMethod(g_main_window->getStatusRendererWidget(), "setText", Qt::QueuedConnection,

View File

@ -2,16 +2,20 @@
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
#pragma once
#include "gdbserver.h"
#include "qtutils.h"
#include "core/host.h"
#include "util/host_display.h"
#include "core/host_settings.h"
#include "core/system.h"
#include "core/types.h"
#include "core/common_host.h"
#include "core/game_list.h"
#include "util/gpu_device.h"
#include "util/input_manager.h"
#include "gdbserver.h"
#include "qtutils.h"
#include <QtCore/QByteArray>
#include <QtCore/QMetaType>
#include <QtCore/QObject>
@ -39,7 +43,7 @@ class QTranslator;
class INISettingsInterface;
class HostDisplay;
class GPUDevice;
class MainWindow;
class DisplayWidget;
@ -95,10 +99,9 @@ public:
ALWAYS_INLINE bool isSurfaceless() const { return m_is_surfaceless; }
ALWAYS_INLINE bool isRunningFullscreenUI() const { return m_run_fullscreen_ui; }
bool acquireHostDisplay(RenderAPI api);
std::optional<WindowInfo> acquireRenderWindow(bool recreate_window);
void connectDisplaySignals(DisplayWidget* widget);
void releaseHostDisplay();
void renderDisplay(bool skip_present);
void releaseRenderWindow();
void startBackgroundControllerPollTimer();
void stopBackgroundControllerPollTimer();
@ -132,11 +135,11 @@ Q_SIGNALS:
void systemPaused();
void systemResumed();
void gameListRefreshed();
bool createDisplayRequested(bool fullscreen, bool render_to_main);
bool updateDisplayRequested(bool fullscreen, bool render_to_main, bool surfaceless);
void displaySizeRequested(qint32 width, qint32 height);
std::optional<WindowInfo> onAcquireRenderWindowRequested(bool recreate_window, bool fullscreen, bool render_to_main,
bool surfaceless, bool use_main_window_pos);
void onResizeRenderWindowRequested(qint32 width, qint32 height);
void onReleaseRenderWindowRequested();
void focusDisplayWidgetRequested();
void destroyDisplayRequested();
void runningGameChanged(const QString& filename, const QString& game_serial, const QString& game_title);
void inputProfileLoaded();
void mouseModeRequested(bool relative, bool hide_cursor);
@ -180,7 +183,7 @@ public Q_SLOTS:
void saveScreenshot();
void redrawDisplayWindow();
void toggleFullscreen();
void setFullscreen(bool fullscreen);
void setFullscreen(bool fullscreen, bool allow_render_to_main);
void setSurfaceless(bool surfaceless);
void requestDisplaySize(float scale);
void loadCheatList(const QString& filename);
@ -194,7 +197,7 @@ private Q_SLOTS:
void onDisplayWindowMouseMoveEvent(bool relative, float x, float y);
void onDisplayWindowMouseButtonEvent(int button, bool pressed);
void onDisplayWindowMouseWheelEvent(const QPoint& delta_angle);
void onDisplayWindowResized(int width, int height);
void onDisplayWindowResized(int width, int height, float scale);
void onDisplayWindowKeyEvent(int key, bool pressed);
void onDisplayWindowTextEntered(const QString& text);
void doBackgroundControllerPoll();
@ -210,7 +213,6 @@ private:
void createBackgroundControllerPollTimer();
void destroyBackgroundControllerPollTimer();
void setInitialState(std::optional<bool> override_fullscreen);
void updateDisplayState();
QThread* m_ui_thread;
QSemaphore m_started_semaphore;
@ -233,7 +235,8 @@ private:
float m_last_video_fps = std::numeric_limits<float>::infinity();
u32 m_last_render_width = std::numeric_limits<u32>::max();
u32 m_last_render_height = std::numeric_limits<u32>::max();
GPURenderer m_last_renderer = GPURenderer::Count;
RenderAPI m_last_render_api = RenderAPI::None;
bool m_last_hardware_renderer = false;
};
extern EmuThread* g_emu_thread;

View File

@ -2,8 +2,11 @@
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
#pragma once
#include "common/window_info.h"
#include "util/window_info.h"
#include "common/types.h"
#include <QtCore/QByteArray>
#include <QtCore/QMetaType>
#include <QtCore/QString>