diff --git a/src/common/adaptive_mutex.h b/src/common/adaptive_mutex.h new file mode 100644 index 000000000..f174f5996 --- /dev/null +++ b/src/common/adaptive_mutex.h @@ -0,0 +1,27 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#ifdef __linux__ +#include +#endif + +namespace Common { + +#ifdef PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP +class AdaptiveMutex { +public: + void lock() { + pthread_mutex_lock(&mutex); + } + void unlock() { + pthread_mutex_unlock(&mutex); + } + +private: + pthread_mutex_t mutex = PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP; +}; +#endif // PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP + +} // namespace Common diff --git a/src/core/libraries/kernel/kernel.cpp b/src/core/libraries/kernel/kernel.cpp index a9d04ca38..2b7735219 100644 --- a/src/core/libraries/kernel/kernel.cpp +++ b/src/core/libraries/kernel/kernel.cpp @@ -60,7 +60,7 @@ static void KernelServiceThread(std::stop_token stoken) { } io_context.run(); - io_context.reset(); + io_context.restart(); asio_requests = 0; } diff --git a/src/core/libraries/pad/pad.cpp b/src/core/libraries/pad/pad.cpp index 7eb628a90..9a44f91f0 100644 --- a/src/core/libraries/pad/pad.cpp +++ b/src/core/libraries/pad/pad.cpp @@ -11,6 +11,8 @@ namespace Libraries::Pad { +using Input::GameController; + int PS4_SYSV_ABI scePadClose(s32 handle) { LOG_ERROR(Lib_Pad, "(STUBBED) called"); return ORBIS_OK; @@ -290,7 +292,8 @@ int PS4_SYSV_ABI scePadRead(s32 handle, OrbisPadData* pData, s32 num) { int connected_count = 0; bool connected = false; Input::State states[64]; - auto* controller = Common::Singleton::Instance(); + auto* controller = Common::Singleton::Instance(); + const auto* engine = controller->GetEngine(); int ret_num = controller->ReadStates(states, num, &connected, &connected_count); if (!connected) { @@ -311,9 +314,14 @@ int PS4_SYSV_ABI scePadRead(s32 handle, OrbisPadData* pData, s32 num) { pData[i].angularVelocity.x = states[i].angularVelocity.x; pData[i].angularVelocity.y = states[i].angularVelocity.y; pData[i].angularVelocity.z = states[i].angularVelocity.z; - Input::GameController::CalculateOrientation(pData[i].acceleration, pData[i].angularVelocity, - 1.0f / controller->accel_poll_rate, - pData[i].orientation); + if (engine) { + const auto accel_poll_rate = engine->GetAccelPollRate(); + if (accel_poll_rate != 0.0f) { + GameController::CalculateOrientation(pData[i].acceleration, + pData[i].angularVelocity, + 1.0f / accel_poll_rate, pData[i].orientation); + } + } pData[i].touchData.touchNum = (states[i].touchpad[0].state ? 1 : 0) + (states[i].touchpad[1].state ? 1 : 0); pData[i].touchData.touch[0].x = states[i].touchpad[0].x; @@ -356,7 +364,8 @@ int PS4_SYSV_ABI scePadReadState(s32 handle, OrbisPadData* pData) { if (handle == ORBIS_PAD_ERROR_DEVICE_NO_HANDLE) { return ORBIS_PAD_ERROR_INVALID_HANDLE; } - auto* controller = Common::Singleton::Instance(); + auto* controller = Common::Singleton::Instance(); + const auto* engine = controller->GetEngine(); int connectedCount = 0; bool isConnected = false; Input::State state; @@ -374,9 +383,13 @@ int PS4_SYSV_ABI scePadReadState(s32 handle, OrbisPadData* pData) { pData->angularVelocity.x = state.angularVelocity.x; pData->angularVelocity.y = state.angularVelocity.y; pData->angularVelocity.z = state.angularVelocity.z; - Input::GameController::CalculateOrientation(pData->acceleration, pData->angularVelocity, - 1.0f / controller->accel_poll_rate, - pData->orientation); + if (engine) { + const auto accel_poll_rate = engine->GetAccelPollRate(); + if (accel_poll_rate != 0.0f) { + GameController::CalculateOrientation(pData->acceleration, pData->angularVelocity, + 1.0f / accel_poll_rate, pData->orientation); + } + } pData->touchData.touchNum = (state.touchpad[0].state ? 1 : 0) + (state.touchpad[1].state ? 1 : 0); pData->touchData.touch[0].x = state.touchpad[0].x; @@ -468,7 +481,7 @@ int PS4_SYSV_ABI scePadSetLightBar(s32 handle, const OrbisPadLightBarParam* pPar return ORBIS_PAD_ERROR_INVALID_LIGHTBAR_SETTING; } - auto* controller = Common::Singleton::Instance(); + auto* controller = Common::Singleton::Instance(); controller->SetLightBarRGB(pParam->r, pParam->g, pParam->b); return ORBIS_OK; } @@ -536,7 +549,7 @@ int PS4_SYSV_ABI scePadSetVibration(s32 handle, const OrbisPadVibrationParam* pP if (pParam != nullptr) { LOG_DEBUG(Lib_Pad, "scePadSetVibration called handle = {} data = {} , {}", handle, pParam->smallMotor, pParam->largeMotor); - auto* controller = Common::Singleton::Instance(); + auto* controller = Common::Singleton::Instance(); controller->SetVibration(pParam->smallMotor, pParam->largeMotor); return ORBIS_OK; } diff --git a/src/imgui/renderer/imgui_core.cpp b/src/imgui/renderer/imgui_core.cpp index 615fcd52b..dfd2037a7 100644 --- a/src/imgui/renderer/imgui_core.cpp +++ b/src/imgui/renderer/imgui_core.cpp @@ -186,7 +186,8 @@ ImGuiID NewFrame(bool is_reusing_frame) { Sdl::NewFrame(is_reusing_frame); ImGui::NewFrame(); - ImGuiWindowFlags flags = ImGuiDockNodeFlags_PassthruCentralNode; + ImGuiWindowFlags flags = + ImGuiDockNodeFlags_PassthruCentralNode | ImGuiDockNodeFlags_AutoHideTabBar; if (!DebugState.IsShowingDebugMenuBar()) { flags |= ImGuiDockNodeFlags_NoTabBar; } diff --git a/src/input/controller.cpp b/src/input/controller.cpp index eb43e6adf..71f0b0c09 100644 --- a/src/input/controller.cpp +++ b/src/input/controller.cpp @@ -10,6 +10,55 @@ namespace Input { +using Libraries::Pad::OrbisPadButtonDataOffset; + +void State::OnButton(OrbisPadButtonDataOffset button, bool isPressed) { + if (isPressed) { + buttonsState |= button; + } else { + buttonsState &= ~button; + } +} + +void State::OnAxis(Axis axis, int value) { + const auto toggle = [&](const auto button) { + if (value > 0) { + buttonsState |= button; + } else { + buttonsState &= ~button; + } + }; + switch (axis) { + case Axis::TriggerLeft: + toggle(OrbisPadButtonDataOffset::L2); + break; + case Axis::TriggerRight: + toggle(OrbisPadButtonDataOffset::R2); + break; + default: + break; + } + axes[static_cast(axis)] = value; +} + +void State::OnTouchpad(int touchIndex, bool isDown, float x, float y) { + touchpad[touchIndex].state = isDown; + touchpad[touchIndex].x = static_cast(x * 1920); + touchpad[touchIndex].y = static_cast(y * 941); +} + +void State::OnGyro(const float gyro[3]) { + angularVelocity.x = gyro[0]; + angularVelocity.y = gyro[1]; + angularVelocity.z = gyro[2]; +} + +void State::OnAccel(const float accel[3]) { + acceleration.x = accel[0]; + acceleration.y = accel[1]; + acceleration.z = accel[2]; +} + GameController::GameController() { m_states_num = 0; m_last_state = State(); @@ -20,7 +69,7 @@ void GameController::ReadState(State* state, bool* isConnected, int* connectedCo *isConnected = m_connected; *connectedCount = m_connected_count; - *state = GetLastState(); + *state = m_engine && m_connected ? m_engine->ReadState() : GetLastState(); } int GameController::ReadStates(State* states, int states_num, bool* isConnected, @@ -75,45 +124,22 @@ void GameController::AddState(const State& state) { m_states_num++; } -void GameController::CheckButton(int id, Libraries::Pad::OrbisPadButtonDataOffset button, - bool is_pressed) { +void GameController::CheckButton(int id, OrbisPadButtonDataOffset button, bool is_pressed) { std::scoped_lock lock{m_mutex}; auto state = GetLastState(); + state.time = Libraries::Kernel::sceKernelGetProcessTime(); - if (is_pressed) { - state.buttonsState |= button; - } else { - state.buttonsState &= ~button; - } + state.OnButton(button, is_pressed); AddState(state); } void GameController::Axis(int id, Input::Axis axis, int value) { - using Libraries::Pad::OrbisPadButtonDataOffset; - std::scoped_lock lock{m_mutex}; auto state = GetLastState(); state.time = Libraries::Kernel::sceKernelGetProcessTime(); - int axis_id = static_cast(axis); - state.axes[axis_id] = value; - - if (axis == Input::Axis::TriggerLeft) { - if (value > 0) { - state.buttonsState |= OrbisPadButtonDataOffset::L2; - } else { - state.buttonsState &= ~OrbisPadButtonDataOffset::L2; - } - } - - if (axis == Input::Axis::TriggerRight) { - if (value > 0) { - state.buttonsState |= OrbisPadButtonDataOffset::R2; - } else { - state.buttonsState &= ~OrbisPadButtonDataOffset::R2; - } - } + state.OnAxis(axis, value); AddState(state); } @@ -124,9 +150,7 @@ void GameController::Gyro(int id, const float gyro[3]) { state.time = Libraries::Kernel::sceKernelGetProcessTime(); // Update the angular velocity (gyro data) - state.angularVelocity.x = gyro[0]; // X-axis - state.angularVelocity.y = gyro[1]; // Y-axis - state.angularVelocity.z = gyro[2]; // Z-axis + state.OnGyro(gyro); AddState(state); } @@ -136,9 +160,7 @@ void GameController::Acceleration(int id, const float acceleration[3]) { state.time = Libraries::Kernel::sceKernelGetProcessTime(); // Update the acceleration values - state.acceleration.x = acceleration[0]; // X-axis - state.acceleration.y = acceleration[1]; // Y-axis - state.acceleration.z = acceleration[2]; // Z-axis + state.OnAccel(acceleration); AddState(state); } @@ -211,62 +233,48 @@ void GameController::CalculateOrientation(Libraries::Pad::OrbisFVector3& acceler } void GameController::SetLightBarRGB(u8 r, u8 g, u8 b) { - if (m_sdl_gamepad != nullptr) { - SDL_SetGamepadLED(m_sdl_gamepad, r, g, b); + if (!m_engine) { + return; } + std::scoped_lock _{m_mutex}; + m_engine->SetLightBarRGB(r, g, b); } -bool GameController::SetVibration(u8 smallMotor, u8 largeMotor) { - if (m_sdl_gamepad != nullptr) { - return SDL_RumbleGamepad(m_sdl_gamepad, (smallMotor / 255.0f) * 0xFFFF, - (largeMotor / 255.0f) * 0xFFFF, -1); +void GameController::SetVibration(u8 smallMotor, u8 largeMotor) { + if (!m_engine) { + return; } - return true; + std::scoped_lock _{m_mutex}; + m_engine->SetVibration(smallMotor, largeMotor); } void GameController::SetTouchpadState(int touchIndex, bool touchDown, float x, float y) { if (touchIndex < 2) { std::scoped_lock lock{m_mutex}; auto state = GetLastState(); - state.time = Libraries::Kernel::sceKernelGetProcessTime(); - state.touchpad[touchIndex].state = touchDown; - state.touchpad[touchIndex].x = static_cast(x * 1920); - state.touchpad[touchIndex].y = static_cast(y * 941); + state.time = Libraries::Kernel::sceKernelGetProcessTime(); + state.OnTouchpad(touchIndex, touchDown, x, y); AddState(state); } } -void GameController::TryOpenSDLController() { - if (m_sdl_gamepad == nullptr || !SDL_GamepadConnected(m_sdl_gamepad)) { - int gamepad_count; - SDL_JoystickID* gamepads = SDL_GetGamepads(&gamepad_count); - m_sdl_gamepad = gamepad_count > 0 ? SDL_OpenGamepad(gamepads[0]) : nullptr; - if (Config::getIsMotionControlsEnabled()) { - if (SDL_SetGamepadSensorEnabled(m_sdl_gamepad, SDL_SENSOR_GYRO, true)) { - gyro_poll_rate = SDL_GetGamepadSensorDataRate(m_sdl_gamepad, SDL_SENSOR_GYRO); - LOG_INFO(Input, "Gyro initialized, poll rate: {}", gyro_poll_rate); - } else { - LOG_ERROR(Input, "Failed to initialize gyro controls for gamepad"); - } - if (SDL_SetGamepadSensorEnabled(m_sdl_gamepad, SDL_SENSOR_ACCEL, true)) { - accel_poll_rate = SDL_GetGamepadSensorDataRate(m_sdl_gamepad, SDL_SENSOR_ACCEL); - LOG_INFO(Input, "Accel initialized, poll rate: {}", accel_poll_rate); - } else { - LOG_ERROR(Input, "Failed to initialize accel controls for gamepad"); - } - } - - SDL_free(gamepads); - - SetLightBarRGB(0, 0, 255); +void GameController::SetEngine(std::unique_ptr engine) { + std::scoped_lock _{m_mutex}; + m_engine = std::move(engine); + if (m_engine) { + m_engine->Init(); } } +Engine* GameController::GetEngine() { + return m_engine.get(); +} + u32 GameController::Poll() { - std::scoped_lock lock{m_mutex}; if (m_connected) { + std::scoped_lock lock{m_mutex}; auto time = Libraries::Kernel::sceKernelGetProcessTime(); if (m_states_num == 0) { auto diff = (time - m_last_state.time) / 1000; diff --git a/src/input/controller.h b/src/input/controller.h index c6fc02c24..a45e71d77 100644 --- a/src/input/controller.h +++ b/src/input/controller.h @@ -3,12 +3,12 @@ #pragma once +#include +#include #include #include "common/types.h" #include "core/libraries/pad/pad.h" -struct SDL_Gamepad; - namespace Input { enum class Axis { @@ -28,7 +28,14 @@ struct TouchpadEntry { u16 y{}; }; -struct State { +class State { +public: + void OnButton(Libraries::Pad::OrbisPadButtonDataOffset, bool); + void OnAxis(Axis, int); + void OnTouchpad(int touchIndex, bool isDown, float x, float y); + void OnGyro(const float[3]); + void OnAccel(const float[3]); + Libraries::Pad::OrbisPadButtonDataOffset buttonsState{}; u64 time = 0; int axes[static_cast(Axis::AxisMax)] = {128, 128, 128, 128, 0, 0}; @@ -38,9 +45,19 @@ struct State { Libraries::Pad::OrbisFQuaternion orientation = {0.0f, 0.0f, 0.0f, 1.0f}; }; +class Engine { +public: + virtual ~Engine() = default; + virtual void Init() = 0; + virtual void SetLightBarRGB(u8 r, u8 g, u8 b) = 0; + virtual void SetVibration(u8 smallMotor, u8 largeMotor) = 0; + virtual State ReadState() = 0; + virtual float GetAccelPollRate() const = 0; + virtual float GetGyroPollRate() const = 0; +}; + inline int GetAxis(int min, int max, int value) { - int v = (255 * (value - min)) / (max - min); - return (v < 0 ? 0 : (v > 255 ? 255 : v)); + return std::clamp((255 * (value - min)) / (max - min), 0, 255); } constexpr u32 MAX_STATES = 64; @@ -59,13 +76,12 @@ public: void Gyro(int id, const float gyro[3]); void Acceleration(int id, const float acceleration[3]); void SetLightBarRGB(u8 r, u8 g, u8 b); - bool SetVibration(u8 smallMotor, u8 largeMotor); + void SetVibration(u8 smallMotor, u8 largeMotor); void SetTouchpadState(int touchIndex, bool touchDown, float x, float y); - void TryOpenSDLController(); + void SetEngine(std::unique_ptr); + Engine* GetEngine(); u32 Poll(); - float gyro_poll_rate; - float accel_poll_rate; static void CalculateOrientation(Libraries::Pad::OrbisFVector3& acceleration, Libraries::Pad::OrbisFVector3& angularVelocity, float deltaTime, @@ -85,7 +101,7 @@ private: std::array m_states; std::array m_private; - SDL_Gamepad* m_sdl_gamepad = nullptr; + std::unique_ptr m_engine = nullptr; }; } // namespace Input diff --git a/src/qt_gui/cheats_patches.cpp b/src/qt_gui/cheats_patches.cpp index 2fea0b6ea..13157aa3a 100644 --- a/src/qt_gui/cheats_patches.cpp +++ b/src/qt_gui/cheats_patches.cpp @@ -188,8 +188,12 @@ void CheatsPatches::setupUI() { } }); + QPushButton* closeButton = new QPushButton(tr("Close")); + connect(closeButton, &QPushButton::clicked, [this]() { QWidget::close(); }); + controlLayout->addWidget(downloadButton); controlLayout->addWidget(deleteCheatButton); + controlLayout->addWidget(closeButton); cheatsLayout->addLayout(controlLayout); cheatsTab->setLayout(cheatsLayout); @@ -464,6 +468,8 @@ void CheatsPatches::onSaveButtonClicked() { } else { QMessageBox::information(this, tr("Success"), tr("Options saved successfully.")); } + + QWidget::close(); } QCheckBox* CheatsPatches::findCheckBoxByName(const QString& name) { diff --git a/src/qt_gui/main_window.cpp b/src/qt_gui/main_window.cpp index 0dd2a58a8..6893fafdd 100644 --- a/src/qt_gui/main_window.cpp +++ b/src/qt_gui/main_window.cpp @@ -251,6 +251,12 @@ void MainWindow::CreateConnects() { } }); + connect(ui->shadFolderAct, &QAction::triggered, this, [this]() { + QString userPath; + Common::FS::PathToQString(userPath, Common::FS::GetUserPath(Common::FS::PathType::UserDir)); + QDesktopServices::openUrl(QUrl::fromLocalFile(userPath)); + }); + connect(ui->playButton, &QPushButton::clicked, this, &MainWindow::StartGame); connect(m_game_grid_frame.get(), &QTableWidget::cellDoubleClicked, this, &MainWindow::StartGame); @@ -992,6 +998,7 @@ QIcon MainWindow::RecolorIcon(const QIcon& icon, bool isWhite) { void MainWindow::SetUiIcons(bool isWhite) { ui->bootInstallPkgAct->setIcon(RecolorIcon(ui->bootInstallPkgAct->icon(), isWhite)); ui->bootGameAct->setIcon(RecolorIcon(ui->bootGameAct->icon(), isWhite)); + ui->shadFolderAct->setIcon(RecolorIcon(ui->shadFolderAct->icon(), isWhite)); ui->exitAct->setIcon(RecolorIcon(ui->exitAct->icon(), isWhite)); #ifdef ENABLE_UPDATER ui->updaterAct->setIcon(RecolorIcon(ui->updaterAct->icon(), isWhite)); diff --git a/src/qt_gui/main_window_ui.h b/src/qt_gui/main_window_ui.h index 0d5038d7e..7de166121 100644 --- a/src/qt_gui/main_window_ui.h +++ b/src/qt_gui/main_window_ui.h @@ -12,6 +12,7 @@ public: QAction* bootInstallPkgAct; QAction* bootGameAct; QAction* addElfFolderAct; + QAction* shadFolderAct; QAction* exitAct; QAction* showGameListAct; QAction* refreshGameListAct; @@ -89,6 +90,9 @@ public: addElfFolderAct = new QAction(MainWindow); addElfFolderAct->setObjectName("addElfFolderAct"); addElfFolderAct->setIcon(QIcon(":images/folder_icon.png")); + shadFolderAct = new QAction(MainWindow); + shadFolderAct->setObjectName("shadFolderAct"); + shadFolderAct->setIcon(QIcon(":images/folder_icon.png")); exitAct = new QAction(MainWindow); exitAct->setObjectName("exitAct"); exitAct->setIcon(QIcon(":images/exit_icon.png")); @@ -274,7 +278,9 @@ public: menuBar->addAction(menuHelp->menuAction()); menuFile->addAction(bootInstallPkgAct); menuFile->addAction(bootGameAct); + menuFile->addSeparator(); menuFile->addAction(addElfFolderAct); + menuFile->addAction(shadFolderAct); menuFile->addSeparator(); menuFile->addAction(menuRecent->menuAction()); menuFile->addSeparator(); @@ -333,6 +339,8 @@ public: "MainWindow", "Install application from a .pkg file", nullptr)); #endif // QT_CONFIG(tooltip) menuRecent->setTitle(QCoreApplication::translate("MainWindow", "Recent Games", nullptr)); + shadFolderAct->setText( + QCoreApplication::translate("MainWindow", "Open shadPS4 Folder", nullptr)); exitAct->setText(QCoreApplication::translate("MainWindow", "Exit", nullptr)); #if QT_CONFIG(tooltip) exitAct->setToolTip(QCoreApplication::translate("MainWindow", "Exit shadPS4", nullptr)); diff --git a/src/qt_gui/settings_dialog.cpp b/src/qt_gui/settings_dialog.cpp index a4b584294..175c8c51d 100644 --- a/src/qt_gui/settings_dialog.cpp +++ b/src/qt_gui/settings_dialog.cpp @@ -522,22 +522,6 @@ bool SettingsDialog::eventFilter(QObject* obj, QEvent* event) { } else { ui->descriptionText->setText(defaultTextEdit); } - - // if the text exceeds the size of the box, it will increase the size - QRect currentGeometry = this->geometry(); - int newWidth = currentGeometry.width(); - - int documentHeight = ui->descriptionText->document()->size().height(); - int visibleHeight = ui->descriptionText->viewport()->height(); - if (documentHeight > visibleHeight) { - ui->descriptionText->setMaximumSize(16777215, 110); - this->setGeometry(currentGeometry.x(), currentGeometry.y(), newWidth, - currentGeometry.height() + 40); - } else { - ui->descriptionText->setMaximumSize(16777215, 70); - this->setGeometry(currentGeometry.x(), currentGeometry.y(), newWidth, - initialHeight); - } return true; } } diff --git a/src/qt_gui/settings_dialog.ui b/src/qt_gui/settings_dialog.ui index 8d68d1c90..c084d4849 100644 --- a/src/qt_gui/settings_dialog.ui +++ b/src/qt_gui/settings_dialog.ui @@ -12,7 +12,7 @@ 0 0 970 - 750 + 820 @@ -68,7 +68,7 @@ 0 0 946 - 586 + 611 @@ -77,43 +77,6 @@ 0 - - - - - - System - - - - - - Console Language - - - - - - - - - - - - Emulator Language - - - - - - - - - - - - - @@ -217,246 +180,6 @@ - - - - 6 - - - QLayout::SizeConstraint::SetDefaultConstraint - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 0 - 0 - - - - - 0 - 0 - - - - - 16777215 - 16777215 - - - - Update - - - - 10 - - - 1 - - - 11 - - - 11 - - - - - - 0 - 0 - - - - - 0 - 0 - - - - - 16777215 - 16777215 - - - - Update Channel - - - - 7 - - - 11 - - - 11 - - - 11 - - - 11 - - - - - - 0 - 0 - - - - - Release - - - - - Nightly - - - - - - - - - - - - 0 - 0 - - - - - 0 - 0 - - - - - 16777215 - 16777215 - - - - Check for Updates - - - - - - - - 0 - 0 - - - - - 11 - false - - - - Check for Updates at Startup - - - - - - - - - - - - - - - 0 - 0 - - - - - 0 - 0 - - - - Game Compatibility - - - - 10 - - - 1 - - - 11 - - - - - Display Compatibility Data - - - - - - - Update Compatibility Database On Startup - - - - - - - - 0 - 0 - - - - - 0 - 0 - - - - - 16777215 - 16777215 - - - - Update Compatibility Database - - - - - - - - @@ -627,6 +350,283 @@ + + + + + + System + + + + + + Console Language + + + + + + + + + + + + Emulator Language + + + + + + + + + + + + + + + + + 6 + + + QLayout::SizeConstraint::SetDefaultConstraint + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + + 0 + 0 + + + + + 16777215 + 16777215 + + + + Update + + + + 10 + + + 1 + + + 11 + + + 190 + + + + + + 0 + 0 + + + + + 0 + 0 + + + + + 16777215 + 16777215 + + + + Update Channel + + + + 7 + + + 11 + + + 11 + + + 11 + + + 11 + + + + + + 0 + 0 + + + + + Release + + + + + Nightly + + + + + + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + + 16777215 + 16777215 + + + + Check for Updates + + + + + + + + 0 + 0 + + + + + 11 + false + + + + Check for Updates at Startup + + + + + + + + + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + Game Compatibility + + + + 10 + + + 1 + + + 11 + + + + + Display Compatibility Data + + + + + + + Update Compatibility Database On Startup + + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + + 16777215 + 16777215 + + + + Update Compatibility Database + + + + + + + + @@ -645,12 +645,12 @@ 0 0 946 - 586 + 605 - + @@ -664,17 +664,14 @@ Cursor - - - 0 - + 11 11 - + true @@ -701,7 +698,7 @@ - + true @@ -836,7 +833,7 @@ true - + 0 0 @@ -872,6 +869,12 @@ true + + + 0 + 0 + + 0 @@ -885,23 +888,6 @@ - - - - - - Qt::Orientation::Horizontal - - - - 40 - 20 - - - - - - @@ -943,7 +929,7 @@ 0 0 946 - 586 + 605 @@ -1124,11 +1110,14 @@ + + true + Advanced - Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignVCenter + Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignTop @@ -1194,7 +1183,7 @@ 0 0 946 - 586 + 605 @@ -1233,22 +1222,6 @@ - - - - Qt::Orientation::Horizontal - - - QSizePolicy::Policy::Preferred - - - - 40 - 20 - - - - @@ -1445,10 +1418,16 @@ + + + 0 + 0 + + 16777215 - 70 + 120 diff --git a/src/qt_gui/translations/ar.ts b/src/qt_gui/translations/ar.ts index a4dadcb1a..47bd673b2 100644 --- a/src/qt_gui/translations/ar.ts +++ b/src/qt_gui/translations/ar.ts @@ -247,6 +247,10 @@ Recent Games الألعاب الأخيرة + + Open shadPS4 Folder + Open shadPS4 Folder + Exit خروج diff --git a/src/qt_gui/translations/da_DK.ts b/src/qt_gui/translations/da_DK.ts index 70b7d3ecc..91a98abd4 100644 --- a/src/qt_gui/translations/da_DK.ts +++ b/src/qt_gui/translations/da_DK.ts @@ -247,6 +247,10 @@ Recent Games Recent Games + + Open shadPS4 Folder + Open shadPS4 Folder + Exit Exit diff --git a/src/qt_gui/translations/de.ts b/src/qt_gui/translations/de.ts index 7f1de3afd..b1e1d2664 100644 --- a/src/qt_gui/translations/de.ts +++ b/src/qt_gui/translations/de.ts @@ -247,6 +247,10 @@ Recent Games Zuletzt gespielt + + Open shadPS4 Folder + Open shadPS4 Folder + Exit Beenden diff --git a/src/qt_gui/translations/el.ts b/src/qt_gui/translations/el.ts index 84165536e..ecda0ede0 100644 --- a/src/qt_gui/translations/el.ts +++ b/src/qt_gui/translations/el.ts @@ -247,6 +247,10 @@ Recent Games Recent Games + + Open shadPS4 Folder + Open shadPS4 Folder + Exit Exit diff --git a/src/qt_gui/translations/en.ts b/src/qt_gui/translations/en.ts index fad185d41..9127df7e3 100644 --- a/src/qt_gui/translations/en.ts +++ b/src/qt_gui/translations/en.ts @@ -247,6 +247,10 @@ Recent Games Recent Games + + Open shadPS4 Folder + Open shadPS4 Folder + Exit Exit diff --git a/src/qt_gui/translations/es_ES.ts b/src/qt_gui/translations/es_ES.ts index a97d3d3c8..a47f7c577 100644 --- a/src/qt_gui/translations/es_ES.ts +++ b/src/qt_gui/translations/es_ES.ts @@ -247,6 +247,10 @@ Recent Games Juegos recientes + + Open shadPS4 Folder + Open shadPS4 Folder + Exit Salir diff --git a/src/qt_gui/translations/fa_IR.ts b/src/qt_gui/translations/fa_IR.ts index 697e615fb..976e7614e 100644 --- a/src/qt_gui/translations/fa_IR.ts +++ b/src/qt_gui/translations/fa_IR.ts @@ -247,6 +247,10 @@ Recent Games بازی های اخیر + + Open shadPS4 Folder + Open shadPS4 Folder + Exit خروج diff --git a/src/qt_gui/translations/fi.ts b/src/qt_gui/translations/fi.ts index 51e85dfbb..abc091b7e 100644 --- a/src/qt_gui/translations/fi.ts +++ b/src/qt_gui/translations/fi.ts @@ -247,6 +247,10 @@ Recent Games Viimeisimmät Pelit + + Open shadPS4 Folder + Open shadPS4 Folder + Exit Sulje diff --git a/src/qt_gui/translations/fr.ts b/src/qt_gui/translations/fr.ts index 35f3eb55f..d2a1c5307 100644 --- a/src/qt_gui/translations/fr.ts +++ b/src/qt_gui/translations/fr.ts @@ -247,6 +247,10 @@ Recent Games Jeux récents + + Open shadPS4 Folder + Ouvrir le dossier de shadPS4 + Exit Fermer diff --git a/src/qt_gui/translations/hu_HU.ts b/src/qt_gui/translations/hu_HU.ts index a2bd9c1da..dff6a3a18 100644 --- a/src/qt_gui/translations/hu_HU.ts +++ b/src/qt_gui/translations/hu_HU.ts @@ -247,6 +247,10 @@ Recent Games Legutóbbi Játékok + + Open shadPS4 Folder + Open shadPS4 Folder + Exit Kilépés diff --git a/src/qt_gui/translations/id.ts b/src/qt_gui/translations/id.ts index b97914ca2..e6fb8b5aa 100644 --- a/src/qt_gui/translations/id.ts +++ b/src/qt_gui/translations/id.ts @@ -247,6 +247,10 @@ Recent Games Recent Games + + Open shadPS4 Folder + Open shadPS4 Folder + Exit Exit diff --git a/src/qt_gui/translations/it.ts b/src/qt_gui/translations/it.ts index d4ea1c7e6..73dbdc603 100644 --- a/src/qt_gui/translations/it.ts +++ b/src/qt_gui/translations/it.ts @@ -247,6 +247,10 @@ Recent Games Giochi Recenti + + Open shadPS4 Folder + Open shadPS4 Folder + Exit Uscita diff --git a/src/qt_gui/translations/ja_JP.ts b/src/qt_gui/translations/ja_JP.ts index 359955765..e07d4eb25 100644 --- a/src/qt_gui/translations/ja_JP.ts +++ b/src/qt_gui/translations/ja_JP.ts @@ -247,6 +247,10 @@ Recent Games 最近のゲーム + + Open shadPS4 Folder + Open shadPS4 Folder + Exit 終了 diff --git a/src/qt_gui/translations/ko_KR.ts b/src/qt_gui/translations/ko_KR.ts index 9cca0b656..560b58340 100644 --- a/src/qt_gui/translations/ko_KR.ts +++ b/src/qt_gui/translations/ko_KR.ts @@ -247,6 +247,10 @@ Recent Games Recent Games + + Open shadPS4 Folder + Open shadPS4 Folder + Exit Exit diff --git a/src/qt_gui/translations/lt_LT.ts b/src/qt_gui/translations/lt_LT.ts index 0594bcbd2..e2ec1e5c3 100644 --- a/src/qt_gui/translations/lt_LT.ts +++ b/src/qt_gui/translations/lt_LT.ts @@ -247,6 +247,10 @@ Recent Games Recent Games + + Open shadPS4 Folder + Open shadPS4 Folder + Exit Exit diff --git a/src/qt_gui/translations/nb.ts b/src/qt_gui/translations/nb.ts index 8ca8246ba..b94d29b23 100644 --- a/src/qt_gui/translations/nb.ts +++ b/src/qt_gui/translations/nb.ts @@ -247,6 +247,10 @@ Recent Games Nylige spill + + Open shadPS4 Folder + Open shadPS4 Folder + Exit Avslutt diff --git a/src/qt_gui/translations/nl.ts b/src/qt_gui/translations/nl.ts index 12d644458..add27500f 100644 --- a/src/qt_gui/translations/nl.ts +++ b/src/qt_gui/translations/nl.ts @@ -247,6 +247,10 @@ Recent Games Recent Games + + Open shadPS4 Folder + Open shadPS4 Folder + Exit Exit diff --git a/src/qt_gui/translations/pl_PL.ts b/src/qt_gui/translations/pl_PL.ts index 782db12e2..3280beea7 100644 --- a/src/qt_gui/translations/pl_PL.ts +++ b/src/qt_gui/translations/pl_PL.ts @@ -247,6 +247,10 @@ Recent Games Ostatnie gry + + Open shadPS4 Folder + Open shadPS4 Folder + Exit Wyjdź diff --git a/src/qt_gui/translations/pt_BR.ts b/src/qt_gui/translations/pt_BR.ts index 94bbf028a..b9d889519 100644 --- a/src/qt_gui/translations/pt_BR.ts +++ b/src/qt_gui/translations/pt_BR.ts @@ -247,6 +247,10 @@ Recent Games Jogos Recentes + + Open shadPS4 Folder + Abrir pasta shadPS4 + Exit Sair @@ -1341,4 +1345,4 @@ TB - \ No newline at end of file + diff --git a/src/qt_gui/translations/ro_RO.ts b/src/qt_gui/translations/ro_RO.ts index 3bd8e38b5..00a9eb179 100644 --- a/src/qt_gui/translations/ro_RO.ts +++ b/src/qt_gui/translations/ro_RO.ts @@ -247,6 +247,10 @@ Recent Games Recent Games + + Open shadPS4 Folder + Open shadPS4 Folder + Exit Exit diff --git a/src/qt_gui/translations/ru_RU.ts b/src/qt_gui/translations/ru_RU.ts index a38e2fd98..4c90450dd 100644 --- a/src/qt_gui/translations/ru_RU.ts +++ b/src/qt_gui/translations/ru_RU.ts @@ -247,6 +247,10 @@ Recent Games Недавние игры + + Open shadPS4 Folder + Open shadPS4 Folder + Exit Выход diff --git a/src/qt_gui/translations/sq.ts b/src/qt_gui/translations/sq.ts index a83dc9829..768db1e75 100644 --- a/src/qt_gui/translations/sq.ts +++ b/src/qt_gui/translations/sq.ts @@ -247,6 +247,10 @@ Recent Games Lojërat e fundit + + Open shadPS4 Folder + Open shadPS4 Folder + Exit Dil diff --git a/src/qt_gui/translations/sv.ts b/src/qt_gui/translations/sv.ts index 9a244a9df..3781ba45c 100644 --- a/src/qt_gui/translations/sv.ts +++ b/src/qt_gui/translations/sv.ts @@ -722,6 +722,10 @@ Recent Games Senaste spel + + Open shadPS4 Folder + Open shadPS4 Folder + Exit Avsluta diff --git a/src/qt_gui/translations/tr_TR.ts b/src/qt_gui/translations/tr_TR.ts index be50f935a..5e8499073 100644 --- a/src/qt_gui/translations/tr_TR.ts +++ b/src/qt_gui/translations/tr_TR.ts @@ -247,6 +247,10 @@ Recent Games Son Oyunlar + + Open shadPS4 Folder + Open shadPS4 Folder + Exit Çıkış diff --git a/src/qt_gui/translations/uk_UA.ts b/src/qt_gui/translations/uk_UA.ts index ff4e48e80..a1c7e97e0 100644 --- a/src/qt_gui/translations/uk_UA.ts +++ b/src/qt_gui/translations/uk_UA.ts @@ -247,6 +247,10 @@ Recent Games Нещодавні ігри + + Open shadPS4 Folder + Open shadPS4 Folder + Exit Вихід diff --git a/src/qt_gui/translations/vi_VN.ts b/src/qt_gui/translations/vi_VN.ts index e546d955c..a579a1983 100644 --- a/src/qt_gui/translations/vi_VN.ts +++ b/src/qt_gui/translations/vi_VN.ts @@ -247,6 +247,10 @@ Recent Games Recent Games + + Open shadPS4 Folder + Open shadPS4 Folder + Exit Exit diff --git a/src/qt_gui/translations/zh_CN.ts b/src/qt_gui/translations/zh_CN.ts index ece5f9490..5450f3dfd 100644 --- a/src/qt_gui/translations/zh_CN.ts +++ b/src/qt_gui/translations/zh_CN.ts @@ -247,6 +247,10 @@ Recent Games 最近启动的游戏 + + Open shadPS4 Folder + Open shadPS4 Folder + Exit 退出 diff --git a/src/qt_gui/translations/zh_TW.ts b/src/qt_gui/translations/zh_TW.ts index 11642d52b..0ce0b4d69 100644 --- a/src/qt_gui/translations/zh_TW.ts +++ b/src/qt_gui/translations/zh_TW.ts @@ -247,6 +247,10 @@ Recent Games Recent Games + + Open shadPS4 Folder + Open shadPS4 Folder + Exit Exit diff --git a/src/sdl_window.cpp b/src/sdl_window.cpp index e1241f9fe..aa3aaf29f 100644 --- a/src/sdl_window.cpp +++ b/src/sdl_window.cpp @@ -12,6 +12,7 @@ #include "common/config.h" #include "common/elf_info.h" #include "common/version.h" +#include "core/libraries/kernel/time.h" #include "core/libraries/pad/pad.h" #include "imgui/renderer/imgui_core.h" #include "input/controller.h" @@ -24,47 +25,200 @@ #include "SDL3/SDL_metal.h" #endif +namespace Input { + +using Libraries::Pad::OrbisPadButtonDataOffset; + +static OrbisPadButtonDataOffset SDLGamepadToOrbisButton(u8 button) { + using OPBDO = OrbisPadButtonDataOffset; + + switch (button) { + case SDL_GAMEPAD_BUTTON_DPAD_DOWN: + return OPBDO::Down; + case SDL_GAMEPAD_BUTTON_DPAD_UP: + return OPBDO::Up; + case SDL_GAMEPAD_BUTTON_DPAD_LEFT: + return OPBDO::Left; + case SDL_GAMEPAD_BUTTON_DPAD_RIGHT: + return OPBDO::Right; + case SDL_GAMEPAD_BUTTON_SOUTH: + return OPBDO::Cross; + case SDL_GAMEPAD_BUTTON_NORTH: + return OPBDO::Triangle; + case SDL_GAMEPAD_BUTTON_WEST: + return OPBDO::Square; + case SDL_GAMEPAD_BUTTON_EAST: + return OPBDO::Circle; + case SDL_GAMEPAD_BUTTON_START: + return OPBDO::Options; + case SDL_GAMEPAD_BUTTON_TOUCHPAD: + return OPBDO::TouchPad; + case SDL_GAMEPAD_BUTTON_BACK: + return OPBDO::TouchPad; + case SDL_GAMEPAD_BUTTON_LEFT_SHOULDER: + return OPBDO::L1; + case SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER: + return OPBDO::R1; + case SDL_GAMEPAD_BUTTON_LEFT_STICK: + return OPBDO::L3; + case SDL_GAMEPAD_BUTTON_RIGHT_STICK: + return OPBDO::R3; + default: + return OPBDO::None; + } +} + +static SDL_GamepadAxis InputAxisToSDL(Axis axis) { + switch (axis) { + case Axis::LeftX: + return SDL_GAMEPAD_AXIS_LEFTX; + case Axis::LeftY: + return SDL_GAMEPAD_AXIS_LEFTY; + case Axis::RightX: + return SDL_GAMEPAD_AXIS_RIGHTX; + case Axis::RightY: + return SDL_GAMEPAD_AXIS_RIGHTY; + case Axis::TriggerLeft: + return SDL_GAMEPAD_AXIS_LEFT_TRIGGER; + case Axis::TriggerRight: + return SDL_GAMEPAD_AXIS_RIGHT_TRIGGER; + default: + UNREACHABLE(); + } +} + +SDLInputEngine::~SDLInputEngine() { + if (m_gamepad) { + SDL_CloseGamepad(m_gamepad); + } +} + +void SDLInputEngine::Init() { + if (m_gamepad) { + SDL_CloseGamepad(m_gamepad); + m_gamepad = nullptr; + } + int gamepad_count; + SDL_JoystickID* gamepads = SDL_GetGamepads(&gamepad_count); + if (!gamepads) { + LOG_ERROR(Input, "Cannot get gamepad list: {}", SDL_GetError()); + return; + } + if (gamepad_count == 0) { + LOG_INFO(Input, "No gamepad found!"); + SDL_free(gamepads); + return; + } + LOG_INFO(Input, "Got {} gamepads. Opening the first one.", gamepad_count); + if (!(m_gamepad = SDL_OpenGamepad(gamepads[0]))) { + LOG_ERROR(Input, "Failed to open gamepad 0: {}", SDL_GetError()); + SDL_free(gamepads); + return; + } + if (Config::getIsMotionControlsEnabled()) { + if (SDL_SetGamepadSensorEnabled(m_gamepad, SDL_SENSOR_GYRO, true)) { + m_gyro_poll_rate = SDL_GetGamepadSensorDataRate(m_gamepad, SDL_SENSOR_GYRO); + LOG_INFO(Input, "Gyro initialized, poll rate: {}", m_gyro_poll_rate); + } else { + LOG_ERROR(Input, "Failed to initialize gyro controls for gamepad"); + } + if (SDL_SetGamepadSensorEnabled(m_gamepad, SDL_SENSOR_ACCEL, true)) { + m_accel_poll_rate = SDL_GetGamepadSensorDataRate(m_gamepad, SDL_SENSOR_ACCEL); + LOG_INFO(Input, "Accel initialized, poll rate: {}", m_accel_poll_rate); + } else { + LOG_ERROR(Input, "Failed to initialize accel controls for gamepad"); + }; + } + SDL_free(gamepads); + SetLightBarRGB(0, 0, 255); +} + +void SDLInputEngine::SetLightBarRGB(u8 r, u8 g, u8 b) { + if (m_gamepad) { + SDL_SetGamepadLED(m_gamepad, r, g, b); + } +} + +void SDLInputEngine::SetVibration(u8 smallMotor, u8 largeMotor) { + if (m_gamepad) { + const auto low_freq = (smallMotor / 255.0f) * 0xFFFF; + const auto high_freq = (largeMotor / 255.0f) * 0xFFFF; + SDL_RumbleGamepad(m_gamepad, low_freq, high_freq, -1); + } +} + +State SDLInputEngine::ReadState() { + State state{}; + state.time = Libraries::Kernel::sceKernelGetProcessTime(); + + // Buttons + for (u8 i = 0; i < SDL_GAMEPAD_BUTTON_COUNT; ++i) { + auto orbisButton = SDLGamepadToOrbisButton(i); + if (orbisButton == OrbisPadButtonDataOffset::None) { + continue; + } + state.OnButton(orbisButton, SDL_GetGamepadButton(m_gamepad, (SDL_GamepadButton)i)); + } + + // Axes + for (int i = 0; i < static_cast(Axis::AxisMax); ++i) { + const auto axis = static_cast(i); + const auto value = SDL_GetGamepadAxis(m_gamepad, InputAxisToSDL(axis)); + switch (axis) { + case Axis::TriggerLeft: + case Axis::TriggerRight: + state.OnAxis(axis, GetAxis(0, 0x8000, value)); + break; + default: + state.OnAxis(axis, GetAxis(-0x8000, 0x8000, value)); + break; + } + } + + // Touchpad + if (SDL_GetNumGamepadTouchpads(m_gamepad) > 0) { + for (int finger = 0; finger < 2; ++finger) { + bool down; + float x, y; + if (SDL_GetGamepadTouchpadFinger(m_gamepad, 0, finger, &down, &x, &y, NULL)) { + state.OnTouchpad(finger, down, x, y); + } + } + } + + // Gyro + if (SDL_GamepadHasSensor(m_gamepad, SDL_SENSOR_GYRO)) { + float gyro[3]; + if (SDL_GetGamepadSensorData(m_gamepad, SDL_SENSOR_GYRO, gyro, 3)) { + state.OnGyro(gyro); + } + } + + // Accel + if (SDL_GamepadHasSensor(m_gamepad, SDL_SENSOR_ACCEL)) { + float accel[3]; + if (SDL_GetGamepadSensorData(m_gamepad, SDL_SENSOR_ACCEL, accel, 3)) { + state.OnAccel(accel); + } + } + + return state; +} + +float SDLInputEngine::GetGyroPollRate() const { + return m_gyro_poll_rate; +} + +float SDLInputEngine::GetAccelPollRate() const { + return m_accel_poll_rate; +} + +} // namespace Input + namespace Frontend { using namespace Libraries::Pad; -static OrbisPadButtonDataOffset SDLGamepadToOrbisButton(u8 button) { - switch (button) { - case SDL_GAMEPAD_BUTTON_DPAD_DOWN: - return OrbisPadButtonDataOffset::Down; - case SDL_GAMEPAD_BUTTON_DPAD_UP: - return OrbisPadButtonDataOffset::Up; - case SDL_GAMEPAD_BUTTON_DPAD_LEFT: - return OrbisPadButtonDataOffset::Left; - case SDL_GAMEPAD_BUTTON_DPAD_RIGHT: - return OrbisPadButtonDataOffset::Right; - case SDL_GAMEPAD_BUTTON_SOUTH: - return OrbisPadButtonDataOffset::Cross; - case SDL_GAMEPAD_BUTTON_NORTH: - return OrbisPadButtonDataOffset::Triangle; - case SDL_GAMEPAD_BUTTON_WEST: - return OrbisPadButtonDataOffset::Square; - case SDL_GAMEPAD_BUTTON_EAST: - return OrbisPadButtonDataOffset::Circle; - case SDL_GAMEPAD_BUTTON_START: - return OrbisPadButtonDataOffset::Options; - case SDL_GAMEPAD_BUTTON_TOUCHPAD: - return OrbisPadButtonDataOffset::TouchPad; - case SDL_GAMEPAD_BUTTON_BACK: - return OrbisPadButtonDataOffset::TouchPad; - case SDL_GAMEPAD_BUTTON_LEFT_SHOULDER: - return OrbisPadButtonDataOffset::L1; - case SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER: - return OrbisPadButtonDataOffset::R1; - case SDL_GAMEPAD_BUTTON_LEFT_STICK: - return OrbisPadButtonDataOffset::L3; - case SDL_GAMEPAD_BUTTON_RIGHT_STICK: - return OrbisPadButtonDataOffset::R3; - default: - return OrbisPadButtonDataOffset::None; - } -} - static Uint32 SDLCALL PollController(void* userdata, SDL_TimerID timer_id, Uint32 interval) { auto* controller = reinterpret_cast(userdata); return controller->Poll(); @@ -116,7 +270,7 @@ WindowSDL::WindowSDL(s32 width_, s32 height_, Input::GameController* controller_ SDL_SetWindowFullscreen(window, Config::getIsFullscreen()); SDL_InitSubSystem(SDL_INIT_GAMEPAD); - controller->TryOpenSDLController(); + controller->SetEngine(std::make_unique()); #if defined(SDL_PLATFORM_WIN32) window_info.type = WindowSystemType::Windows; @@ -306,7 +460,7 @@ void WindowSDL::OnGamepadEvent(const SDL_Event* event) { switch (event->type) { case SDL_EVENT_GAMEPAD_ADDED: case SDL_EVENT_GAMEPAD_REMOVED: - controller->TryOpenSDLController(); + controller->SetEngine(std::make_unique()); break; case SDL_EVENT_GAMEPAD_TOUCHPAD_DOWN: case SDL_EVENT_GAMEPAD_TOUCHPAD_UP: @@ -317,7 +471,7 @@ void WindowSDL::OnGamepadEvent(const SDL_Event* event) { break; case SDL_EVENT_GAMEPAD_BUTTON_DOWN: case SDL_EVENT_GAMEPAD_BUTTON_UP: { - button = SDLGamepadToOrbisButton(event->gbutton.button); + button = Input::SDLGamepadToOrbisButton(event->gbutton.button); if (button == OrbisPadButtonDataOffset::None) { break; } diff --git a/src/sdl_window.h b/src/sdl_window.h index 9d2405468..65bdd192d 100644 --- a/src/sdl_window.h +++ b/src/sdl_window.h @@ -7,14 +7,32 @@ #include "common/types.h" #include "core/libraries/pad/pad.h" +#include "input/controller.h" struct SDL_Window; struct SDL_Gamepad; union SDL_Event; namespace Input { -class GameController; -} + +class SDLInputEngine : public Engine { +public: + ~SDLInputEngine() override; + void Init() override; + void SetLightBarRGB(u8 r, u8 g, u8 b) override; + void SetVibration(u8 smallMotor, u8 largeMotor) override; + float GetGyroPollRate() const override; + float GetAccelPollRate() const override; + State ReadState() override; + +private: + SDL_Gamepad* m_gamepad = nullptr; + + float m_gyro_poll_rate{}; + float m_accel_poll_rate{}; +}; + +} // namespace Input namespace Frontend { diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_special.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_special.cpp index 4a22ba09f..a0a3ed8ff 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_special.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_special.cpp @@ -24,10 +24,48 @@ void ConvertDepthMode(EmitContext& ctx) { ctx.OpStore(ctx.output_position, vector); } +void ConvertPositionToClipSpace(EmitContext& ctx) { + const Id type{ctx.F32[1]}; + Id position{ctx.OpLoad(ctx.F32[4], ctx.output_position)}; + const Id x{ctx.OpCompositeExtract(type, position, 0u)}; + const Id y{ctx.OpCompositeExtract(type, position, 1u)}; + const Id z{ctx.OpCompositeExtract(type, position, 2u)}; + const Id w{ctx.OpCompositeExtract(type, position, 3u)}; + const Id xoffset_ptr{ctx.OpAccessChain(ctx.TypePointer(spv::StorageClass::PushConstant, type), + ctx.push_data_block, + ctx.ConstU32(PushData::XOffsetIndex))}; + const Id xoffset{ctx.OpLoad(type, xoffset_ptr)}; + const Id yoffset_ptr{ctx.OpAccessChain(ctx.TypePointer(spv::StorageClass::PushConstant, type), + ctx.push_data_block, + ctx.ConstU32(PushData::YOffsetIndex))}; + const Id yoffset{ctx.OpLoad(type, yoffset_ptr)}; + const Id xscale_ptr{ctx.OpAccessChain(ctx.TypePointer(spv::StorageClass::PushConstant, type), + ctx.push_data_block, + ctx.ConstU32(PushData::XScaleIndex))}; + const Id xscale{ctx.OpLoad(type, xscale_ptr)}; + const Id yscale_ptr{ctx.OpAccessChain(ctx.TypePointer(spv::StorageClass::PushConstant, type), + ctx.push_data_block, + ctx.ConstU32(PushData::YScaleIndex))}; + const Id yscale{ctx.OpLoad(type, yscale_ptr)}; + const Id vport_w = + ctx.Constant(type, float(std::min(ctx.profile.max_viewport_width / 2, 8_KB))); + const Id wnd_x = ctx.OpFAdd(type, ctx.OpFMul(type, x, xscale), xoffset); + const Id ndc_x = ctx.OpFSub(type, ctx.OpFDiv(type, wnd_x, vport_w), ctx.Constant(type, 1.f)); + const Id vport_h = + ctx.Constant(type, float(std::min(ctx.profile.max_viewport_height / 2, 8_KB))); + const Id wnd_y = ctx.OpFAdd(type, ctx.OpFMul(type, y, yscale), yoffset); + const Id ndc_y = ctx.OpFSub(type, ctx.OpFDiv(type, wnd_y, vport_h), ctx.Constant(type, 1.f)); + const Id vector{ctx.OpCompositeConstruct(ctx.F32[4], std::array({ndc_x, ndc_y, z, w}))}; + ctx.OpStore(ctx.output_position, vector); +} + void EmitEpilogue(EmitContext& ctx) { if (ctx.stage == Stage::Vertex && ctx.runtime_info.vs_info.emulate_depth_negative_one_to_one) { ConvertDepthMode(ctx); } + if (ctx.stage == Stage::Vertex && ctx.runtime_info.vs_info.clip_disable) { + ConvertPositionToClipSpace(ctx); + } } void EmitDiscard(EmitContext& ctx) { diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp index 7d492a384..b0bf5aa0a 100644 --- a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp +++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp @@ -568,25 +568,34 @@ void EmitContext::DefineOutputs() { void EmitContext::DefinePushDataBlock() { // Create push constants block for instance steps rates - const Id struct_type{Name( - TypeStruct(U32[1], U32[1], U32[4], U32[4], U32[4], U32[4], U32[4], U32[4]), "AuxData")}; + const Id struct_type{Name(TypeStruct(U32[1], U32[1], U32[4], U32[4], U32[4], U32[4], U32[4], + U32[4], F32[1], F32[1], F32[1], F32[1]), + "AuxData")}; Decorate(struct_type, spv::Decoration::Block); MemberName(struct_type, 0, "sr0"); MemberName(struct_type, 1, "sr1"); - MemberName(struct_type, 2, "buf_offsets0"); - MemberName(struct_type, 3, "buf_offsets1"); - MemberName(struct_type, 4, "ud_regs0"); - MemberName(struct_type, 5, "ud_regs1"); - MemberName(struct_type, 6, "ud_regs2"); - MemberName(struct_type, 7, "ud_regs3"); + MemberName(struct_type, Shader::PushData::BufOffsetIndex + 0, "buf_offsets0"); + MemberName(struct_type, Shader::PushData::BufOffsetIndex + 1, "buf_offsets1"); + MemberName(struct_type, Shader::PushData::UdRegsIndex + 0, "ud_regs0"); + MemberName(struct_type, Shader::PushData::UdRegsIndex + 1, "ud_regs1"); + MemberName(struct_type, Shader::PushData::UdRegsIndex + 2, "ud_regs2"); + MemberName(struct_type, Shader::PushData::UdRegsIndex + 3, "ud_regs3"); + MemberName(struct_type, Shader::PushData::XOffsetIndex, "xoffset"); + MemberName(struct_type, Shader::PushData::YOffsetIndex, "yoffset"); + MemberName(struct_type, Shader::PushData::XScaleIndex, "xscale"); + MemberName(struct_type, Shader::PushData::YScaleIndex, "yscale"); MemberDecorate(struct_type, 0, spv::Decoration::Offset, 0U); MemberDecorate(struct_type, 1, spv::Decoration::Offset, 4U); - MemberDecorate(struct_type, 2, spv::Decoration::Offset, 8U); - MemberDecorate(struct_type, 3, spv::Decoration::Offset, 24U); - MemberDecorate(struct_type, 4, spv::Decoration::Offset, 40U); - MemberDecorate(struct_type, 5, spv::Decoration::Offset, 56U); - MemberDecorate(struct_type, 6, spv::Decoration::Offset, 72U); - MemberDecorate(struct_type, 7, spv::Decoration::Offset, 88U); + MemberDecorate(struct_type, Shader::PushData::BufOffsetIndex + 0, spv::Decoration::Offset, 8U); + MemberDecorate(struct_type, Shader::PushData::BufOffsetIndex + 1, spv::Decoration::Offset, 24U); + MemberDecorate(struct_type, Shader::PushData::UdRegsIndex + 0, spv::Decoration::Offset, 40U); + MemberDecorate(struct_type, Shader::PushData::UdRegsIndex + 1, spv::Decoration::Offset, 56U); + MemberDecorate(struct_type, Shader::PushData::UdRegsIndex + 2, spv::Decoration::Offset, 72U); + MemberDecorate(struct_type, Shader::PushData::UdRegsIndex + 3, spv::Decoration::Offset, 88U); + MemberDecorate(struct_type, Shader::PushData::XOffsetIndex, spv::Decoration::Offset, 104U); + MemberDecorate(struct_type, Shader::PushData::YOffsetIndex, spv::Decoration::Offset, 108U); + MemberDecorate(struct_type, Shader::PushData::XScaleIndex, spv::Decoration::Offset, 112U); + MemberDecorate(struct_type, Shader::PushData::YScaleIndex, spv::Decoration::Offset, 116U); push_data_block = DefineVar(struct_type, spv::StorageClass::PushConstant); Name(push_data_block, "push_data"); interfaces.push_back(push_data_block); @@ -847,6 +856,10 @@ void EmitContext::DefineSharedMemory() { if (shared_memory_size == 0) { shared_memory_size = DefaultSharedMemSize; } + + const u32 max_shared_memory_size = runtime_info.cs_info.max_shared_memory_size; + ASSERT(shared_memory_size <= max_shared_memory_size); + const u32 num_elements{Common::DivCeil(shared_memory_size, 4U)}; const Id type{TypeArray(U32[1], ConstU32(num_elements))}; shared_memory_u32_type = TypePointer(spv::StorageClass::Workgroup, type); diff --git a/src/shader_recompiler/info.h b/src/shader_recompiler/info.h index aeff346fa..2cde30629 100644 --- a/src/shader_recompiler/info.h +++ b/src/shader_recompiler/info.h @@ -96,11 +96,19 @@ using FMaskResourceList = boost::container::small_vector; struct PushData { static constexpr u32 BufOffsetIndex = 2; static constexpr u32 UdRegsIndex = 4; + static constexpr u32 XOffsetIndex = 8; + static constexpr u32 YOffsetIndex = 9; + static constexpr u32 XScaleIndex = 10; + static constexpr u32 YScaleIndex = 11; u32 step0; u32 step1; std::array buf_offsets; std::array ud_regs; + float xoffset; + float yoffset; + float xscale; + float yscale; void AddOffset(u32 binding, u32 offset) { ASSERT(offset < 256 && binding < buf_offsets.size()); diff --git a/src/shader_recompiler/profile.h b/src/shader_recompiler/profile.h index f8878d442..f8b91a283 100644 --- a/src/shader_recompiler/profile.h +++ b/src/shader_recompiler/profile.h @@ -30,6 +30,8 @@ struct Profile { bool needs_manual_interpolation{}; bool needs_lds_barriers{}; u64 min_ssbo_alignment{}; + u32 max_viewport_width{}; + u32 max_viewport_height{}; }; } // namespace Shader diff --git a/src/shader_recompiler/runtime_info.h b/src/shader_recompiler/runtime_info.h index cf49b0879..2bf5e3f0a 100644 --- a/src/shader_recompiler/runtime_info.h +++ b/src/shader_recompiler/runtime_info.h @@ -84,6 +84,7 @@ struct VertexRuntimeInfo { u32 num_outputs; std::array outputs; bool emulate_depth_negative_one_to_one{}; + bool clip_disable{}; // Domain AmdGpu::TessellationType tess_type; AmdGpu::TessellationTopology tess_topology; @@ -92,7 +93,8 @@ struct VertexRuntimeInfo { bool operator==(const VertexRuntimeInfo& other) const noexcept { return emulate_depth_negative_one_to_one == other.emulate_depth_negative_one_to_one && - tess_type == other.tess_type && tess_topology == other.tess_topology && + clip_disable == other.clip_disable && tess_type == other.tess_type && + tess_topology == other.tess_topology && tess_partitioning == other.tess_partitioning && hs_output_cp_stride == other.hs_output_cp_stride; } @@ -198,6 +200,7 @@ struct FragmentRuntimeInfo { struct ComputeRuntimeInfo { u32 shared_memory_size; + u32 max_shared_memory_size; std::array workgroup_size; std::array tgid_enable; diff --git a/src/video_core/buffer_cache/word_manager.h b/src/video_core/buffer_cache/word_manager.h index 7ad33d7a6..5ad724f96 100644 --- a/src/video_core/buffer_cache/word_manager.h +++ b/src/video_core/buffer_cache/word_manager.h @@ -8,6 +8,9 @@ #include #include +#ifdef __linux__ +#include "common/adaptive_mutex.h" +#endif #include "common/spin_lock.h" #include "common/types.h" #include "video_core/page_manager.h" @@ -272,7 +275,11 @@ private: } } +#ifdef PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP + Common::AdaptiveMutex lock; +#else Common::SpinLock lock; +#endif PageManager* tracker; VAddr cpu_addr = 0; WordsArray cpu; diff --git a/src/video_core/page_manager.h b/src/video_core/page_manager.h index f44307f92..f6bae9641 100644 --- a/src/video_core/page_manager.h +++ b/src/video_core/page_manager.h @@ -5,6 +5,9 @@ #include #include +#ifdef __linux__ +#include "common/adaptive_mutex.h" +#endif #include "common/spin_lock.h" #include "common/types.h" @@ -36,7 +39,11 @@ private: std::unique_ptr impl; Vulkan::Rasterizer* rasterizer; boost::icl::interval_map cached_pages; +#ifdef PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP + Common::AdaptiveMutex lock; +#else Common::SpinLock lock; +#endif }; } // namespace VideoCore diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.h b/src/video_core/renderer_vulkan/vk_graphics_pipeline.h index f696c1f72..9a20f94c1 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.h +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.h @@ -42,13 +42,14 @@ struct GraphicsPipelineKey { vk::Format stencil_format; struct { + bool clip_disable : 1; bool depth_test_enable : 1; bool depth_write_enable : 1; bool depth_bounds_test_enable : 1; bool depth_bias_enable : 1; bool stencil_test_enable : 1; // Must be named to be zero-initialized. - u8 _unused : 3; + u8 _unused : 2; }; vk::CompareOp depth_compare_op; @@ -94,6 +95,10 @@ public: return key.mrt_mask; } + auto IsClipDisabled() const { + return key.clip_disable; + } + [[nodiscard]] bool IsPrimitiveListTopology() const { return key.prim_type == AmdGpu::PrimitiveType::PointList || key.prim_type == AmdGpu::PrimitiveType::LineList || diff --git a/src/video_core/renderer_vulkan/vk_instance.cpp b/src/video_core/renderer_vulkan/vk_instance.cpp index 1600600b9..15bd573ef 100644 --- a/src/video_core/renderer_vulkan/vk_instance.cpp +++ b/src/video_core/renderer_vulkan/vk_instance.cpp @@ -208,6 +208,7 @@ std::string Instance::GetDriverVersionName() { bool Instance::CreateDevice() { const vk::StructureChain feature_chain = physical_device.getFeatures2< vk::PhysicalDeviceFeatures2, vk::PhysicalDeviceExtendedDynamicStateFeaturesEXT, + vk::PhysicalDevicePrimitiveTopologyListRestartFeaturesEXT, vk::PhysicalDeviceExtendedDynamicState2FeaturesEXT, vk::PhysicalDeviceExtendedDynamicState3FeaturesEXT, vk::PhysicalDeviceCustomBorderColorFeaturesEXT, @@ -270,7 +271,6 @@ bool Instance::CreateDevice() { legacy_vertex_attributes = add_extension(VK_EXT_LEGACY_VERTEX_ATTRIBUTES_EXTENSION_NAME); image_load_store_lod = add_extension(VK_AMD_SHADER_IMAGE_LOAD_STORE_LOD_EXTENSION_NAME); amd_gcn_shader = add_extension(VK_AMD_GCN_SHADER_EXTENSION_NAME); - add_extension(VK_KHR_SWAPCHAIN_MUTABLE_FORMAT_EXTENSION_NAME); // These extensions are promoted by Vulkan 1.3, but for greater compatibility we use Vulkan 1.2 // with extensions. @@ -317,6 +317,9 @@ bool Instance::CreateDevice() { .pQueuePriorities = queue_priorities.data(), }; + const auto topology_list_restart_features = + feature_chain.get(); + const auto vk12_features = feature_chain.get(); vk::StructureChain device_chain = { vk::DeviceCreateInfo{ @@ -406,6 +409,8 @@ bool Instance::CreateDevice() { }, vk::PhysicalDevicePrimitiveTopologyListRestartFeaturesEXT{ .primitiveTopologyListRestart = true, + .primitiveTopologyPatchListRestart = + topology_list_restart_features.primitiveTopologyPatchListRestart, }, vk::PhysicalDeviceFragmentShaderBarycentricFeaturesKHR{ .fragmentShaderBarycentric = true, diff --git a/src/video_core/renderer_vulkan/vk_instance.h b/src/video_core/renderer_vulkan/vk_instance.h index e0d4d0b4d..8c4752c3f 100644 --- a/src/video_core/renderer_vulkan/vk_instance.h +++ b/src/video_core/renderer_vulkan/vk_instance.h @@ -239,6 +239,11 @@ public: return subgroup_size; } + /// Returns the maximum size of compute shared memory. + u32 MaxComputeSharedMemorySize() const { + return properties.limits.maxComputeSharedMemorySize; + } + /// Returns the maximum supported elements in a texel buffer u32 MaxTexelBufferElements() const { return properties.limits.maxTexelBufferElements; @@ -274,6 +279,14 @@ public: return min_imported_host_pointer_alignment; } + u32 GetMaxViewportWidth() const { + return properties.limits.maxViewportDimensions[0]; + } + + u32 GetMaxViewportHeight() const { + return properties.limits.maxViewportDimensions[1]; + } + /// Returns the sample count flags supported by framebuffers. vk::SampleCountFlags GetFramebufferSampleCounts() const { return properties.limits.framebufferColorSampleCounts & diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index f93494389..c6a56745d 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -125,6 +125,7 @@ const Shader::RuntimeInfo& PipelineCache::BuildRuntimeInfo(Stage stage, LogicalS info.vs_info.emulate_depth_negative_one_to_one = !instance.IsDepthClipControlSupported() && regs.clipper_control.clip_space == Liverpool::ClipSpace::MinusWToW; + info.vs_info.clip_disable = graphics_key.clip_disable; if (l_stage == LogicalStage::TessellationEval) { info.vs_info.tess_type = regs.tess_config.type; info.vs_info.tess_topology = regs.tess_config.topology; @@ -183,6 +184,7 @@ const Shader::RuntimeInfo& PipelineCache::BuildRuntimeInfo(Stage stage, LogicalS info.cs_info.tgid_enable = {cs_pgm.IsTgidEnabled(0), cs_pgm.IsTgidEnabled(1), cs_pgm.IsTgidEnabled(2)}; info.cs_info.shared_memory_size = cs_pgm.SharedMemSize(); + info.cs_info.max_shared_memory_size = instance.MaxComputeSharedMemorySize(); break; } default: @@ -209,6 +211,8 @@ PipelineCache::PipelineCache(const Instance& instance_, Scheduler& scheduler_, instance.GetDriverID() == vk::DriverId::eNvidiaProprietary, .needs_lds_barriers = instance.GetDriverID() == vk::DriverId::eNvidiaProprietary || instance.GetDriverID() == vk::DriverId::eMoltenvk, + .max_viewport_width = instance.GetMaxViewportWidth(), + .max_viewport_height = instance.GetMaxViewportHeight(), }; auto [cache_result, cache] = instance.GetDevice().createPipelineCacheUnique({}); ASSERT_MSG(cache_result == vk::Result::eSuccess, "Failed to create pipeline cache: {}", @@ -261,6 +265,8 @@ bool PipelineCache::RefreshGraphicsKey() { auto& regs = liverpool->regs; auto& key = graphics_key; + key.clip_disable = + regs.clipper_control.clip_disable || regs.primitive_type == AmdGpu::PrimitiveType::RectList; key.depth_test_enable = regs.depth_control.depth_enable; key.depth_write_enable = regs.depth_control.depth_write_enable && !regs.depth_render_control.depth_clear_enable; diff --git a/src/video_core/renderer_vulkan/vk_presenter.cpp b/src/video_core/renderer_vulkan/vk_presenter.cpp index 0f45574bc..36d64a5d5 100644 --- a/src/video_core/renderer_vulkan/vk_presenter.cpp +++ b/src/video_core/renderer_vulkan/vk_presenter.cpp @@ -380,7 +380,7 @@ void Presenter::RecreateFrame(Frame* frame, u32 width, u32 height) { const vk::ImageViewCreateInfo view_info = { .image = frame->image, .viewType = vk::ImageViewType::e2D, - .format = swapchain.GetViewFormat(), + .format = format, .subresourceRange{ .aspectMask = vk::ImageAspectFlagBits::eColor, .baseMipLevel = 0, @@ -476,7 +476,7 @@ bool Presenter::ShowSplash(Frame* frame /*= nullptr*/) { if (!frame) { if (!splash_img.has_value()) { VideoCore::ImageInfo info{}; - info.pixel_format = vk::Format::eR8G8B8A8Srgb; + info.pixel_format = vk::Format::eR8G8B8A8Unorm; info.type = vk::ImageType::e2D; info.size = VideoCore::Extent3D{splash->GetImageInfo().width, splash->GetImageInfo().height, 1}; @@ -487,6 +487,7 @@ bool Presenter::ShowSplash(Frame* frame /*= nullptr*/) { splash->GetImageInfo().width, splash->GetImageInfo().height, 0); splash_img.emplace(instance, present_scheduler, info); + splash_img->flags &= ~VideoCore::GpuDirty; texture_cache.RefreshImage(*splash_img); splash_img->Transit(vk::ImageLayout::eTransferSrcOptimal, @@ -602,6 +603,23 @@ Frame* Presenter::PrepareFrameInternal(VideoCore::ImageId image_id, bool is_eop) .pImageMemoryBarriers = &pre_barrier, }); + const std::array attachments = {vk::RenderingAttachmentInfo{ + .imageView = frame->image_view, + .imageLayout = vk::ImageLayout::eColorAttachmentOptimal, + .loadOp = vk::AttachmentLoadOp::eClear, + .storeOp = vk::AttachmentStoreOp::eStore, + }}; + const vk::RenderingInfo rendering_info{ + .renderArea = + vk::Rect2D{ + .offset = {0, 0}, + .extent = {frame->width, frame->height}, + }, + .layerCount = 1, + .colorAttachmentCount = attachments.size(), + .pColorAttachments = attachments.data(), + }; + if (image_id != VideoCore::NULL_IMAGE_ID) { auto& image = texture_cache.GetImage(image_id); image.Transit(vk::ImageLayout::eShaderReadOnlyOptimal, vk::AccessFlagBits2::eShaderRead, {}, @@ -662,26 +680,13 @@ Frame* Presenter::PrepareFrameInternal(VideoCore::ImageId image_id, bool is_eop) cmdbuf.pushConstants(*pp_pipeline_layout, vk::ShaderStageFlagBits::eFragment, 0, sizeof(PostProcessSettings), &pp_settings); - const std::array attachments = {vk::RenderingAttachmentInfo{ - .imageView = frame->image_view, - .imageLayout = vk::ImageLayout::eColorAttachmentOptimal, - .loadOp = vk::AttachmentLoadOp::eClear, - .storeOp = vk::AttachmentStoreOp::eStore, - }}; - - vk::RenderingInfo rendering_info{ - .renderArea = - vk::Rect2D{ - .offset = {0, 0}, - .extent = {frame->width, frame->height}, - }, - .layerCount = 1, - .colorAttachmentCount = attachments.size(), - .pColorAttachments = attachments.data(), - }; cmdbuf.beginRendering(rendering_info); cmdbuf.draw(3, 1, 0, 0); cmdbuf.endRendering(); + } else { + // Fix display of garbage images on startup on some drivers + cmdbuf.beginRendering(rendering_info); + cmdbuf.endRendering(); } const auto post_barrier = diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index bac647125..88b510eca 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -504,6 +504,17 @@ bool Rasterizer::BindResources(const Pipeline* pipeline) { } push_data.step0 = regs.vgt_instance_step_rate_0; push_data.step1 = regs.vgt_instance_step_rate_1; + + // TODO(roamic): add support for multiple viewports and geometry shaders when ViewportIndex + // is encountered and implemented in the recompiler. + if (stage->stage == Shader::Stage::Vertex) { + push_data.xoffset = + regs.viewport_control.xoffset_enable ? regs.viewports[0].xoffset : 0.f; + push_data.xscale = regs.viewport_control.xscale_enable ? regs.viewports[0].xscale : 1.f; + push_data.yoffset = + regs.viewport_control.yoffset_enable ? regs.viewports[0].yoffset : 0.f; + push_data.yscale = regs.viewport_control.yscale_enable ? regs.viewports[0].yscale : 1.f; + } stage->PushUd(binding, push_data); BindBuffers(*stage, binding, push_data, set_writes, buffer_barriers); @@ -791,8 +802,6 @@ void Rasterizer::BeginRendering(const GraphicsPipeline& pipeline, RenderState& s const auto mip = view.info.range.base.level; state.width = std::min(state.width, std::max(image.info.size.width >> mip, 1u)); state.height = std::min(state.height, std::max(image.info.size.height >> mip, 1u)); - ASSERT(old_img.info.size.width == state.width); - ASSERT(old_img.info.size.height == state.height); } auto& image = texture_cache.GetImage(image_id); if (image.binding.force_general) { @@ -1032,7 +1041,7 @@ void Rasterizer::UnmapMemory(VAddr addr, u64 size) { } void Rasterizer::UpdateDynamicState(const GraphicsPipeline& pipeline) { - UpdateViewportScissorState(); + UpdateViewportScissorState(pipeline); auto& regs = liverpool->regs; const auto cmdbuf = scheduler.CommandBuffer(); @@ -1112,7 +1121,7 @@ void Rasterizer::UpdateDynamicState(const GraphicsPipeline& pipeline) { } } -void Rasterizer::UpdateViewportScissorState() { +void Rasterizer::UpdateViewportScissorState(const GraphicsPipeline& pipeline) { const auto& regs = liverpool->regs; const auto combined_scissor_value_tl = [](s16 scr, s16 win, s16 gen, s16 win_offset) { @@ -1151,26 +1160,46 @@ void Rasterizer::UpdateViewportScissorState() { ? 1.0f : 0.0f; + if (regs.polygon_control.enable_window_offset) { + LOG_ERROR(Render_Vulkan, + "PA_SU_SC_MODE_CNTL.VTX_WINDOW_OFFSET_ENABLE support is not yet implemented."); + } + for (u32 i = 0; i < Liverpool::NumViewports; i++) { const auto& vp = regs.viewports[i]; const auto& vp_d = regs.viewport_depths[i]; if (vp.xscale == 0) { continue; } - const auto xoffset = vp_ctl.xoffset_enable ? vp.xoffset : 0.f; - const auto xscale = vp_ctl.xscale_enable ? vp.xscale : 1.f; - const auto yoffset = vp_ctl.yoffset_enable ? vp.yoffset : 0.f; - const auto yscale = vp_ctl.yscale_enable ? vp.yscale : 1.f; - const auto zoffset = vp_ctl.zoffset_enable ? vp.zoffset : 0.f; - const auto zscale = vp_ctl.zscale_enable ? vp.zscale : 1.f; - viewports.push_back({ - .x = xoffset - xscale, - .y = yoffset - yscale, - .width = xscale * 2.0f, - .height = yscale * 2.0f, - .minDepth = zoffset - zscale * reduce_z, - .maxDepth = zscale + zoffset, - }); + + if (pipeline.IsClipDisabled()) { + // In case if clipping is disabled we patch the shader to convert vertex position + // from screen space coordinates to NDC by defining a render space as full hardware + // window range [0..16383, 0..16383] and setting the viewport to its size. + viewports.push_back({ + .x = 0.f, + .y = 0.f, + .width = float(std::min(instance.GetMaxViewportWidth(), 16_KB)), + .height = float(std::min(instance.GetMaxViewportHeight(), 16_KB)), + .minDepth = 0.0, + .maxDepth = 1.0, + }); + } else { + const auto xoffset = vp_ctl.xoffset_enable ? vp.xoffset : 0.f; + const auto xscale = vp_ctl.xscale_enable ? vp.xscale : 1.f; + const auto yoffset = vp_ctl.yoffset_enable ? vp.yoffset : 0.f; + const auto yscale = vp_ctl.yscale_enable ? vp.yscale : 1.f; + const auto zoffset = vp_ctl.zoffset_enable ? vp.zoffset : 0.f; + const auto zscale = vp_ctl.zscale_enable ? vp.zscale : 1.f; + viewports.push_back({ + .x = xoffset - xscale, + .y = yoffset - yscale, + .width = xscale * 2.0f, + .height = yscale * 2.0f, + .minDepth = zoffset - zscale * reduce_z, + .maxDepth = zscale + zoffset, + }); + } auto vp_scsr = scsr; if (regs.mode_control.vport_scissor_enable) { @@ -1192,8 +1221,8 @@ void Rasterizer::UpdateViewportScissorState() { if (viewports.empty()) { // Vulkan requires providing at least one viewport. constexpr vk::Viewport empty_viewport = { - .x = 0.0f, - .y = 0.0f, + .x = -1.0f, + .y = -1.0f, .width = 1.0f, .height = 1.0f, .minDepth = 0.0f, diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.h b/src/video_core/renderer_vulkan/vk_rasterizer.h index abf58e522..ed6cc7e71 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.h +++ b/src/video_core/renderer_vulkan/vk_rasterizer.h @@ -76,7 +76,7 @@ private: void EliminateFastClear(); void UpdateDynamicState(const GraphicsPipeline& pipeline); - void UpdateViewportScissorState(); + void UpdateViewportScissorState(const GraphicsPipeline& pipeline); bool FilterDraw(); diff --git a/src/video_core/renderer_vulkan/vk_swapchain.cpp b/src/video_core/renderer_vulkan/vk_swapchain.cpp index 438fe30ce..5467a5733 100644 --- a/src/video_core/renderer_vulkan/vk_swapchain.cpp +++ b/src/video_core/renderer_vulkan/vk_swapchain.cpp @@ -17,7 +17,7 @@ Swapchain::Swapchain(const Instance& instance_, const Frontend::WindowSDL& windo FindPresentFormat(); Create(window.GetWidth(), window.GetHeight()); - ImGui::Core::Initialize(instance, window, image_count, view_format); + ImGui::Core::Initialize(instance, window, image_count, surface_format.format); } Swapchain::~Swapchain() { @@ -57,17 +57,7 @@ void Swapchain::Create(u32 width_, u32 height_) { const u32 queue_family_indices_count = exclusive ? 1u : 2u; const vk::SharingMode sharing_mode = exclusive ? vk::SharingMode::eExclusive : vk::SharingMode::eConcurrent; - const vk::Format view_formats[2] = { - surface_format.format, - view_format, - }; - const vk::ImageFormatListCreateInfo format_list = { - .viewFormatCount = 2, - .pViewFormats = view_formats, - }; const vk::SwapchainCreateInfoKHR swapchain_info = { - .pNext = &format_list, - .flags = vk::SwapchainCreateFlagBitsKHR::eMutableFormat, .surface = surface, .minImageCount = image_count, .imageFormat = surface_format.format, @@ -157,22 +147,20 @@ void Swapchain::FindPresentFormat() { // If there is a single undefined surface format, the device doesn't care, so we'll just use // RGBA sRGB. if (formats[0].format == vk::Format::eUndefined) { - surface_format.format = vk::Format::eR8G8B8A8Srgb; + surface_format.format = vk::Format::eR8G8B8A8Unorm; surface_format.colorSpace = vk::ColorSpaceKHR::eSrgbNonlinear; - view_format = FormatToUnorm(surface_format.format); return; } // Try to find a suitable format. for (const vk::SurfaceFormatKHR& sformat : formats) { vk::Format format = sformat.format; - if (format != vk::Format::eR8G8B8A8Srgb && format != vk::Format::eB8G8R8A8Srgb) { + if (format != vk::Format::eR8G8B8A8Unorm && format != vk::Format::eB8G8R8A8Unorm) { continue; } surface_format.format = format; surface_format.colorSpace = sformat.colorSpace; - view_format = FormatToUnorm(surface_format.format); return; } @@ -274,7 +262,7 @@ void Swapchain::SetupImages() { auto [im_view_result, im_view] = device.createImageView(vk::ImageViewCreateInfo{ .image = images[i], .viewType = vk::ImageViewType::e2D, - .format = FormatToUnorm(surface_format.format), + .format = surface_format.format, .subresourceRange = { .aspectMask = vk::ImageAspectFlagBits::eColor, diff --git a/src/video_core/renderer_vulkan/vk_swapchain.h b/src/video_core/renderer_vulkan/vk_swapchain.h index 9da75c758..f5cf9f0d2 100644 --- a/src/video_core/renderer_vulkan/vk_swapchain.h +++ b/src/video_core/renderer_vulkan/vk_swapchain.h @@ -17,17 +17,6 @@ namespace Vulkan { class Instance; class Scheduler; -inline vk::Format FormatToUnorm(vk::Format fmt) { - switch (fmt) { - case vk::Format::eR8G8B8A8Srgb: - return vk::Format::eR8G8B8A8Unorm; - case vk::Format::eB8G8R8A8Srgb: - return vk::Format::eB8G8R8A8Unorm; - default: - UNREACHABLE(); - } -} - class Swapchain { public: explicit Swapchain(const Instance& instance, const Frontend::WindowSDL& window); @@ -61,10 +50,6 @@ public: return surface_format; } - vk::Format GetViewFormat() const { - return view_format; - } - vk::SwapchainKHR GetHandle() const { return swapchain; } diff --git a/src/video_core/texture_cache/image_info.cpp b/src/video_core/texture_cache/image_info.cpp index 07a0488f3..a9ed76960 100644 --- a/src/video_core/texture_cache/image_info.cpp +++ b/src/video_core/texture_cache/image_info.cpp @@ -219,7 +219,7 @@ int ImageInfo::IsMipOf(const ImageInfo& info) const { return -1; } - if (IsTilingCompatible(info.tiling_idx, tiling_idx)) { + if (!IsTilingCompatible(info.tiling_idx, tiling_idx)) { return -1; } diff --git a/src/video_core/texture_cache/image_view.cpp b/src/video_core/texture_cache/image_view.cpp index 6b1349386..7befb5259 100644 --- a/src/video_core/texture_cache/image_view.cpp +++ b/src/video_core/texture_cache/image_view.cpp @@ -114,9 +114,12 @@ ImageView::ImageView(const Vulkan::Instance& instance, const ImageViewInfo& info const auto view_aspect = aspect & vk::ImageAspectFlagBits::eDepth ? "Depth" : aspect & vk::ImageAspectFlagBits::eStencil ? "Stencil" : "Color"; - Vulkan::SetObjectName(instance.GetDevice(), *image_view, "ImageView {}x{}x{} {:#x}:{:#x} ({})", - image.info.size.width, image.info.size.height, image.info.size.depth, - image.info.guest_address, image.info.guest_size, view_aspect); + Vulkan::SetObjectName( + instance.GetDevice(), *image_view, "ImageView {}x{}x{} {:#x}:{:#x} {}:{} {}:{} ({})", + image.info.size.width, image.info.size.height, image.info.size.depth, + image.info.guest_address, image.info.guest_size, info.range.base.level, + info.range.base.level + info.range.extent.levels - 1, info.range.base.layer, + info.range.base.layer + info.range.extent.layers - 1, view_aspect); } ImageView::~ImageView() = default;