Merge branch 'main' into m4aac

This commit is contained in:
georgemoralis 2025-06-15 19:08:30 +03:00 committed by GitHub
commit 591ff5925d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
77 changed files with 5599 additions and 860 deletions

View File

@ -872,6 +872,7 @@ set(SHADER_RECOMPILER src/shader_recompiler/exception.h
src/shader_recompiler/ir/passes/ring_access_elimination.cpp src/shader_recompiler/ir/passes/ring_access_elimination.cpp
src/shader_recompiler/ir/passes/shader_info_collection_pass.cpp src/shader_recompiler/ir/passes/shader_info_collection_pass.cpp
src/shader_recompiler/ir/passes/shared_memory_barrier_pass.cpp src/shader_recompiler/ir/passes/shared_memory_barrier_pass.cpp
src/shader_recompiler/ir/passes/shared_memory_simplify_pass.cpp
src/shader_recompiler/ir/passes/shared_memory_to_storage_pass.cpp src/shader_recompiler/ir/passes/shared_memory_to_storage_pass.cpp
src/shader_recompiler/ir/passes/ssa_rewrite_pass.cpp src/shader_recompiler/ir/passes/ssa_rewrite_pass.cpp
src/shader_recompiler/ir/abstract_syntax_list.cpp src/shader_recompiler/ir/abstract_syntax_list.cpp
@ -1056,6 +1057,10 @@ set(QT_GUI src/qt_gui/about_dialog.cpp
src/qt_gui/settings_dialog.h src/qt_gui/settings_dialog.h
src/qt_gui/settings_dialog.ui src/qt_gui/settings_dialog.ui
src/qt_gui/main.cpp src/qt_gui/main.cpp
src/qt_gui/gui_settings.cpp
src/qt_gui/gui_settings.h
src/qt_gui/settings.cpp
src/qt_gui/settings.h
${EMULATOR} ${EMULATOR}
${RESOURCE_FILES} ${RESOURCE_FILES}
${TRANSLATIONS} ${TRANSLATIONS}
@ -1121,6 +1126,10 @@ if (APPLE)
set(MVK_BUNDLE_PATH "Resources/vulkan/icd.d") set(MVK_BUNDLE_PATH "Resources/vulkan/icd.d")
set_property(TARGET shadps4 APPEND PROPERTY BUILD_RPATH "@executable_path/../${MVK_BUNDLE_PATH}") set_property(TARGET shadps4 APPEND PROPERTY BUILD_RPATH "@executable_path/../${MVK_BUNDLE_PATH}")
set(MVK_DST ${CMAKE_CURRENT_BINARY_DIR}/shadps4.app/Contents/${MVK_BUNDLE_PATH}) set(MVK_DST ${CMAKE_CURRENT_BINARY_DIR}/shadps4.app/Contents/${MVK_BUNDLE_PATH})
add_custom_command(
OUTPUT ${MVK_DST}
COMMAND ${CMAKE_COMMAND} -E make_directory ${MVK_DST})
else() else()
set_property(TARGET shadps4 APPEND PROPERTY BUILD_RPATH "@executable_path") set_property(TARGET shadps4 APPEND PROPERTY BUILD_RPATH "@executable_path")
set(MVK_DST ${CMAKE_CURRENT_BINARY_DIR}) set(MVK_DST ${CMAKE_CURRENT_BINARY_DIR})
@ -1131,9 +1140,6 @@ if (APPLE)
set(MVK_ICD_SRC ${CMAKE_CURRENT_SOURCE_DIR}/externals/MoltenVK/MoltenVK/MoltenVK/icd/MoltenVK_icd.json) set(MVK_ICD_SRC ${CMAKE_CURRENT_SOURCE_DIR}/externals/MoltenVK/MoltenVK/MoltenVK/icd/MoltenVK_icd.json)
set(MVK_ICD_DST ${MVK_DST}/MoltenVK_icd.json) set(MVK_ICD_DST ${MVK_DST}/MoltenVK_icd.json)
add_custom_command(
OUTPUT ${MVK_DST}
COMMAND ${CMAKE_COMMAND} -E make_directory ${MVK_DST})
add_custom_command( add_custom_command(
OUTPUT ${MVK_ICD_DST} OUTPUT ${MVK_ICD_DST}
DEPENDS ${MVK_ICD_SRC} ${MVK_DST} DEPENDS ${MVK_ICD_SRC} ${MVK_DST}
@ -1148,17 +1154,13 @@ if (APPLE)
if (ARCHITECTURE STREQUAL "x86_64") if (ARCHITECTURE STREQUAL "x86_64")
# Reserve system-managed memory space. # Reserve system-managed memory space.
target_link_options(shadps4 PRIVATE -Wl,-no_pie,-no_fixup_chains,-no_huge,-pagezero_size,0x4000,-segaddr,TCB_SPACE,0x4000,-segaddr,SYSTEM_MANAGED,0x400000,-segaddr,SYSTEM_RESERVED,0x7FFFFC000,-image_base,0x20000000000) target_link_options(shadps4 PRIVATE -Wl,-ld_classic,-no_pie,-no_fixup_chains,-no_huge,-pagezero_size,0x4000,-segaddr,TCB_SPACE,0x4000,-segaddr,SYSTEM_MANAGED,0x400000,-segaddr,SYSTEM_RESERVED,0x7FFFFC000,-image_base,0x20000000000)
endif() endif()
# Replacement for std::chrono::time_zone # Replacement for std::chrono::time_zone
target_link_libraries(shadps4 PRIVATE date::date-tz) target_link_libraries(shadps4 PRIVATE date::date-tz)
endif() endif()
if (NOT ENABLE_QT_GUI)
target_link_libraries(shadps4 PRIVATE SDL3::SDL3)
endif()
if (ENABLE_QT_GUI) if (ENABLE_QT_GUI)
target_link_libraries(shadps4 PRIVATE Qt6::Widgets Qt6::Concurrent Qt6::Network Qt6::Multimedia) target_link_libraries(shadps4 PRIVATE Qt6::Widgets Qt6::Concurrent Qt6::Network Qt6::Multimedia)
add_definitions(-DENABLE_QT_GUI) add_definitions(-DENABLE_QT_GUI)

View File

@ -36,7 +36,7 @@ SPDX-License-Identifier: GPL-2.0-or-later
**shadPS4** is an early **PlayStation 4** emulator for **Windows**, **Linux** and **macOS** written in C++. **shadPS4** is an early **PlayStation 4** emulator for **Windows**, **Linux** and **macOS** written in C++.
If you encounter problems or have doubts, do not hesitate to look at the [**Quickstart**](https://github.com/shadps4-emu/shadPS4/blob/main/documents/Quickstart/Quickstart.md).\ If you encounter problems or have doubts, do not hesitate to look at the [**Quickstart**](https://github.com/shadps4-emu/shadPS4/wiki/I.-Quick-start-%5BUsers%5D).\
To verify that a game works, you can look at [**shadPS4 Game Compatibility**](https://github.com/shadps4-emu/shadps4-game-compatibility).\ To verify that a game works, you can look at [**shadPS4 Game Compatibility**](https://github.com/shadps4-emu/shadps4-game-compatibility).\
To discuss shadPS4 development, suggest ideas or to ask for help, join our [**Discord server**](https://discord.gg/bFJxfftGW6).\ To discuss shadPS4 development, suggest ideas or to ask for help, join our [**Discord server**](https://discord.gg/bFJxfftGW6).\
To get the latest news, go to our [**X (Twitter)**](https://x.com/shadps4) or our [**website**](https://shadps4.net/).\ To get the latest news, go to our [**X (Twitter)**](https://x.com/shadps4) or our [**website**](https://shadps4.net/).\
@ -124,8 +124,8 @@ Keyboard and mouse inputs can be customized in the settings menu by clicking the
# Firmware files # Firmware files
shadPS4 can load some PlayStation 4 firmware files, these must be dumped from your legally owned PlayStation 4 console.\ shadPS4 can load some PlayStation 4 firmware files, these must be dumped from your legally owned PlayStation 4 console.
The following firmware modules are supported and must be placed in shadPS4's `user/sys_modules` folder. The following firmware modules are supported and must be placed in shadPS4's `sys_modules` folder.
<div align="center"> <div align="center">
@ -138,8 +138,7 @@ The following firmware modules are supported and must be placed in shadPS4's `us
</div> </div>
> [!Caution] > [!Caution]
> The above modules are required to run the games properly and must be extracted from your PlayStation 4.\ > The above modules are required to run the games properly and must be extracted from your PlayStation 4.
> **We do not provide any information or support on how to do this**.
@ -148,7 +147,7 @@ The following firmware modules are supported and must be placed in shadPS4's `us
- [**georgemoralis**](https://github.com/georgemoralis) - [**georgemoralis**](https://github.com/georgemoralis)
- [**psucien**](https://github.com/psucien) - [**psucien**](https://github.com/psucien)
- [**viniciuslrangel**](https://github.com/viniciuslrangel) - [**viniciuslrangel**](https://github.com/viniciuslrangel)
- [**roamic**](https://github.com/vladmikhalin) - [**roamic**](https://github.com/roamic)
- [**squidbus**](https://github.com/squidbus) - [**squidbus**](https://github.com/squidbus)
- [**frodo**](https://github.com/baggins183) - [**frodo**](https://github.com/baggins183)
- [**Stephen Miller**](https://github.com/StevenMiller123) - [**Stephen Miller**](https://github.com/StevenMiller123)
@ -158,7 +157,7 @@ Logo is done by [**Xphalnos**](https://github.com/Xphalnos)
# Contributing # Contributing
If you want to contribute, please look the [**CONTRIBUTING.md**](https://github.com/shadps4-emu/shadPS4/blob/main/CONTRIBUTING.md) file.\ If you want to contribute, please read the [**CONTRIBUTING.md**](https://github.com/shadps4-emu/shadPS4/blob/main/CONTRIBUTING.md) file.\
Open a PR and we'll check it :) Open a PR and we'll check it :)
# Translations # Translations

View File

@ -25,7 +25,7 @@ sudo apt install build-essential clang git cmake libasound2-dev \
```bash ```bash
sudo dnf install clang git cmake libatomic alsa-lib-devel \ sudo dnf install clang git cmake libatomic alsa-lib-devel \
pipewire-jack-audio-connection-kit-devel openal-devel \ pipewire-jack-audio-connection-kit-devel openal-soft-devel \
openssl-devel libevdev-devel libudev-devel libXext-devel \ openssl-devel libevdev-devel libudev-devel libXext-devel \
qt6-qtbase-devel qt6-qtbase-private-devel \ qt6-qtbase-devel qt6-qtbase-private-devel \
qt6-qtmultimedia-devel qt6-qtsvg-devel qt6-qttools-devel \ qt6-qtmultimedia-devel qt6-qtsvg-devel qt6-qttools-devel \

@ -1 +1 @@
Subproject commit 3a0b07a24a4a681ffe70b461b1f4333b2729e2ef Subproject commit 00abd384ce01cbd439045905d2fa6cf799dfa2f6

@ -1 +1 @@
Subproject commit 969e75f7cc0718774231d029f9d52fa87d4ae1b2 Subproject commit 1a69a919fa302e92b337594bd0a8aaea61037d91

View File

@ -33,9 +33,7 @@ namespace Config {
static bool isNeo = false; static bool isNeo = false;
static bool isDevKit = false; static bool isDevKit = false;
static bool playBGM = false;
static bool isTrophyPopupDisabled = false; static bool isTrophyPopupDisabled = false;
static int BGMvolume = 50;
static bool enableDiscordRPC = false; static bool enableDiscordRPC = false;
static u32 screenWidth = 1280; static u32 screenWidth = 1280;
static u32 screenHeight = 720; static u32 screenHeight = 720;
@ -43,7 +41,6 @@ static s32 gpuId = -1; // Vulkan physical device index. Set to negative for auto
static std::string logFilter; static std::string logFilter;
static std::string logType = "sync"; static std::string logType = "sync";
static std::string userName = "shadPS4"; static std::string userName = "shadPS4";
static std::string updateChannel;
static std::string chooseHomeTab; static std::string chooseHomeTab;
static std::string backButtonBehavior = "left"; static std::string backButtonBehavior = "left";
static bool useSpecialPad = false; static bool useSpecialPad = false;
@ -52,8 +49,6 @@ static bool isMotionControlsEnabled = true;
static bool isDebugDump = false; static bool isDebugDump = false;
static bool isShaderDebug = false; static bool isShaderDebug = false;
static bool isShowSplash = false; static bool isShowSplash = false;
static bool isAutoUpdate = false;
static bool isAlwaysShowChangelog = false;
static std::string isSideTrophy = "right"; static std::string isSideTrophy = "right";
static bool isNullGpu = false; static bool isNullGpu = false;
static bool shouldCopyGPUBuffers = false; static bool shouldCopyGPUBuffers = false;
@ -86,27 +81,13 @@ static std::vector<GameInstallDir> settings_install_dirs = {};
std::vector<bool> install_dirs_enabled = {}; std::vector<bool> install_dirs_enabled = {};
std::filesystem::path settings_addon_install_dir = {}; std::filesystem::path settings_addon_install_dir = {};
std::filesystem::path save_data_path = {}; std::filesystem::path save_data_path = {};
u32 main_window_geometry_x = 400;
u32 main_window_geometry_y = 400;
u32 main_window_geometry_w = 1280;
u32 main_window_geometry_h = 720;
u32 mw_themes = 0; u32 mw_themes = 0;
u32 m_icon_size = 36;
u32 m_icon_size_grid = 69;
u32 m_slider_pos = 0;
u32 m_slider_pos_grid = 0;
u32 m_table_mode = 0;
u32 m_window_size_W = 1280;
u32 m_window_size_H = 720;
std::vector<std::string> m_elf_viewer; std::vector<std::string> m_elf_viewer;
std::vector<std::string> m_recent_files; std::vector<std::string> m_recent_files;
std::string emulator_language = "en_US"; std::string emulator_language = "en_US";
static int backgroundImageOpacity = 50;
static bool showBackgroundImage = true;
static bool isFullscreen = false; static bool isFullscreen = false;
static std::string fullscreenMode = "Windowed"; static std::string fullscreenMode = "Windowed";
static bool isHDRAllowed = false; static bool isHDRAllowed = false;
static bool showLabelsUnderIcons = true;
// Language // Language
u32 m_language = 1; // english u32 m_language = 1; // english
@ -176,14 +157,6 @@ bool getIsFullscreen() {
return isFullscreen; return isFullscreen;
} }
bool getShowLabelsUnderIcons() {
return showLabelsUnderIcons;
}
bool setShowLabelsUnderIcons() {
return false;
}
std::string getFullscreenMode() { std::string getFullscreenMode() {
return fullscreenMode; return fullscreenMode;
} }
@ -192,14 +165,6 @@ bool getisTrophyPopupDisabled() {
return isTrophyPopupDisabled; return isTrophyPopupDisabled;
} }
bool getPlayBGM() {
return playBGM;
}
int getBGMvolume() {
return BGMvolume;
}
bool getEnableDiscordRPC() { bool getEnableDiscordRPC() {
return enableDiscordRPC; return enableDiscordRPC;
} }
@ -240,10 +205,6 @@ std::string getUserName() {
return userName; return userName;
} }
std::string getUpdateChannel() {
return updateChannel;
}
std::string getChooseHomeTab() { std::string getChooseHomeTab() {
return chooseHomeTab; return chooseHomeTab;
} }
@ -276,14 +237,6 @@ bool showSplash() {
return isShowSplash; return isShowSplash;
} }
bool autoUpdate() {
return isAutoUpdate;
}
bool alwaysShowChangelog() {
return isAlwaysShowChangelog;
}
std::string sideTrophy() { std::string sideTrophy() {
return isSideTrophy; return isSideTrophy;
} }
@ -384,14 +337,6 @@ void setShowSplash(bool enable) {
isShowSplash = enable; isShowSplash = enable;
} }
void setAutoUpdate(bool enable) {
isAutoUpdate = enable;
}
void setAlwaysShowChangelog(bool enable) {
isAlwaysShowChangelog = enable;
}
void setSideTrophy(std::string side) { void setSideTrophy(std::string side) {
isSideTrophy = side; isSideTrophy = side;
} }
@ -431,9 +376,6 @@ void setVblankDiv(u32 value) {
void setIsFullscreen(bool enable) { void setIsFullscreen(bool enable) {
isFullscreen = enable; isFullscreen = enable;
} }
static void setShowLabelsUnderIcons(bool enable) {
showLabelsUnderIcons = enable;
}
void setFullscreenMode(std::string mode) { void setFullscreenMode(std::string mode) {
fullscreenMode = mode; fullscreenMode = mode;
@ -443,14 +385,6 @@ void setisTrophyPopupDisabled(bool disable) {
isTrophyPopupDisabled = disable; isTrophyPopupDisabled = disable;
} }
void setPlayBGM(bool enable) {
playBGM = enable;
}
void setBGMvolume(int volume) {
BGMvolume = volume;
}
void setEnableDiscordRPC(bool enable) { void setEnableDiscordRPC(bool enable) {
enableDiscordRPC = enable; enableDiscordRPC = enable;
} }
@ -490,9 +424,6 @@ void setUserName(const std::string& type) {
userName = type; userName = type;
} }
void setUpdateChannel(const std::string& type) {
updateChannel = type;
}
void setChooseHomeTab(const std::string& type) { void setChooseHomeTab(const std::string& type) {
chooseHomeTab = type; chooseHomeTab = type;
} }
@ -521,13 +452,6 @@ void setCheckCompatibilityOnStartup(bool use) {
checkCompatibilityOnStartup = use; checkCompatibilityOnStartup = use;
} }
void setMainWindowGeometry(u32 x, u32 y, u32 w, u32 h) {
main_window_geometry_x = x;
main_window_geometry_y = y;
main_window_geometry_w = w;
main_window_geometry_h = h;
}
bool addGameInstallDir(const std::filesystem::path& dir, bool enabled) { bool addGameInstallDir(const std::filesystem::path& dir, bool enabled) {
for (const auto& install_dir : settings_install_dirs) { for (const auto& install_dir : settings_install_dirs) {
if (install_dir.path == dir) { if (install_dir.path == dir) {
@ -564,34 +488,6 @@ void setMainWindowTheme(u32 theme) {
mw_themes = theme; mw_themes = theme;
} }
void setIconSize(u32 size) {
m_icon_size = size;
}
void setIconSizeGrid(u32 size) {
m_icon_size_grid = size;
}
void setSliderPosition(u32 pos) {
m_slider_pos = pos;
}
void setSliderPositionGrid(u32 pos) {
m_slider_pos_grid = pos;
}
void setTableMode(u32 mode) {
m_table_mode = mode;
}
void setMainWindowWidth(u32 width) {
m_window_size_W = width;
}
void setMainWindowHeight(u32 height) {
m_window_size_H = height;
}
void setElfViewer(const std::vector<std::string>& elfList) { void setElfViewer(const std::vector<std::string>& elfList) {
m_elf_viewer.resize(elfList.size()); m_elf_viewer.resize(elfList.size());
m_elf_viewer = elfList; m_elf_viewer = elfList;
@ -621,22 +517,6 @@ void setSaveDataPath(const std::filesystem::path& path) {
save_data_path = path; save_data_path = path;
} }
u32 getMainWindowGeometryX() {
return main_window_geometry_x;
}
u32 getMainWindowGeometryY() {
return main_window_geometry_y;
}
u32 getMainWindowGeometryW() {
return main_window_geometry_w;
}
u32 getMainWindowGeometryH() {
return main_window_geometry_h;
}
const std::vector<std::filesystem::path> getGameInstallDirs() { const std::vector<std::filesystem::path> getGameInstallDirs() {
std::vector<std::filesystem::path> enabled_dirs; std::vector<std::filesystem::path> enabled_dirs;
for (const auto& dir : settings_install_dirs) { for (const auto& dir : settings_install_dirs) {
@ -667,34 +547,6 @@ u32 getMainWindowTheme() {
return mw_themes; return mw_themes;
} }
u32 getIconSize() {
return m_icon_size;
}
u32 getIconSizeGrid() {
return m_icon_size_grid;
}
u32 getSliderPosition() {
return m_slider_pos;
}
u32 getSliderPositionGrid() {
return m_slider_pos_grid;
}
u32 getTableMode() {
return m_table_mode;
}
u32 getMainWindowWidth() {
return m_window_size_W;
}
u32 getMainWindowHeight() {
return m_window_size_H;
}
std::vector<std::string> getElfViewer() { std::vector<std::string> getElfViewer() {
return m_elf_viewer; return m_elf_viewer;
} }
@ -715,22 +567,6 @@ bool getSeparateLogFilesEnabled() {
return isSeparateLogFilesEnabled; return isSeparateLogFilesEnabled;
} }
int getBackgroundImageOpacity() {
return backgroundImageOpacity;
}
void setBackgroundImageOpacity(int opacity) {
backgroundImageOpacity = std::clamp(opacity, 0, 100);
}
bool getShowBackgroundImage() {
return showBackgroundImage;
}
void setShowBackgroundImage(bool show) {
showBackgroundImage = show;
}
bool getPSNSignedIn() { bool getPSNSignedIn() {
return isPSNSignedIn; return isPSNSignedIn;
} }
@ -764,23 +600,14 @@ void load(const std::filesystem::path& path) {
isNeo = toml::find_or<bool>(general, "isPS4Pro", false); isNeo = toml::find_or<bool>(general, "isPS4Pro", false);
isDevKit = toml::find_or<bool>(general, "isDevKit", false); isDevKit = toml::find_or<bool>(general, "isDevKit", false);
isPSNSignedIn = toml::find_or<bool>(general, "isPSNSignedIn", false); isPSNSignedIn = toml::find_or<bool>(general, "isPSNSignedIn", false);
playBGM = toml::find_or<bool>(general, "playBGM", false);
isTrophyPopupDisabled = toml::find_or<bool>(general, "isTrophyPopupDisabled", false); isTrophyPopupDisabled = toml::find_or<bool>(general, "isTrophyPopupDisabled", false);
trophyNotificationDuration = trophyNotificationDuration =
toml::find_or<double>(general, "trophyNotificationDuration", 5.0); toml::find_or<double>(general, "trophyNotificationDuration", 5.0);
BGMvolume = toml::find_or<int>(general, "BGMvolume", 50);
enableDiscordRPC = toml::find_or<bool>(general, "enableDiscordRPC", true); enableDiscordRPC = toml::find_or<bool>(general, "enableDiscordRPC", true);
logFilter = toml::find_or<std::string>(general, "logFilter", ""); logFilter = toml::find_or<std::string>(general, "logFilter", "");
logType = toml::find_or<std::string>(general, "logType", "sync"); logType = toml::find_or<std::string>(general, "logType", "sync");
userName = toml::find_or<std::string>(general, "userName", "shadPS4"); userName = toml::find_or<std::string>(general, "userName", "shadPS4");
if (Common::g_is_release) {
updateChannel = toml::find_or<std::string>(general, "updateChannel", "Release");
} else {
updateChannel = toml::find_or<std::string>(general, "updateChannel", "Nightly");
}
isShowSplash = toml::find_or<bool>(general, "showSplash", true); isShowSplash = toml::find_or<bool>(general, "showSplash", true);
isAutoUpdate = toml::find_or<bool>(general, "autoUpdate", false);
isAlwaysShowChangelog = toml::find_or<bool>(general, "alwaysShowChangelog", false);
isSideTrophy = toml::find_or<std::string>(general, "sideTrophy", "right"); isSideTrophy = toml::find_or<std::string>(general, "sideTrophy", "right");
compatibilityData = toml::find_or<bool>(general, "compatibilityEnabled", false); compatibilityData = toml::find_or<bool>(general, "compatibilityEnabled", false);
checkCompatibilityOnStartup = checkCompatibilityOnStartup =
@ -841,13 +668,7 @@ void load(const std::filesystem::path& path) {
const toml::value& gui = data.at("GUI"); const toml::value& gui = data.at("GUI");
load_game_size = toml::find_or<bool>(gui, "loadGameSizeEnabled", true); load_game_size = toml::find_or<bool>(gui, "loadGameSizeEnabled", true);
m_icon_size = toml::find_or<int>(gui, "iconSize", 0);
m_icon_size_grid = toml::find_or<int>(gui, "iconSizeGrid", 0);
m_slider_pos = toml::find_or<int>(gui, "sliderPos", 0);
m_slider_pos_grid = toml::find_or<int>(gui, "sliderPosGrid", 0);
mw_themes = toml::find_or<int>(gui, "theme", 0); mw_themes = toml::find_or<int>(gui, "theme", 0);
m_window_size_W = toml::find_or<int>(gui, "mw_width", 0);
m_window_size_H = toml::find_or<int>(gui, "mw_height", 0);
const auto install_dir_array = const auto install_dir_array =
toml::find_or<std::vector<std::u8string>>(gui, "installDirs", {}); toml::find_or<std::vector<std::u8string>>(gui, "installDirs", {});
@ -872,16 +693,9 @@ void load(const std::filesystem::path& path) {
save_data_path = toml::find_fs_path_or(gui, "saveDataPath", {}); save_data_path = toml::find_fs_path_or(gui, "saveDataPath", {});
settings_addon_install_dir = toml::find_fs_path_or(gui, "addonInstallDir", {}); settings_addon_install_dir = toml::find_fs_path_or(gui, "addonInstallDir", {});
main_window_geometry_x = toml::find_or<int>(gui, "geometry_x", 0);
main_window_geometry_y = toml::find_or<int>(gui, "geometry_y", 0);
main_window_geometry_w = toml::find_or<int>(gui, "geometry_w", 0);
main_window_geometry_h = toml::find_or<int>(gui, "geometry_h", 0);
m_elf_viewer = toml::find_or<std::vector<std::string>>(gui, "elfDirs", {}); m_elf_viewer = toml::find_or<std::vector<std::string>>(gui, "elfDirs", {});
m_recent_files = toml::find_or<std::vector<std::string>>(gui, "recentFiles", {}); m_recent_files = toml::find_or<std::vector<std::string>>(gui, "recentFiles", {});
m_table_mode = toml::find_or<int>(gui, "gameTableMode", 0);
emulator_language = toml::find_or<std::string>(gui, "emulatorLanguage", "en_US"); emulator_language = toml::find_or<std::string>(gui, "emulatorLanguage", "en_US");
backgroundImageOpacity = toml::find_or<int>(gui, "backgroundImageOpacity", 50);
showBackgroundImage = toml::find_or<bool>(gui, "showBackgroundImage", true);
} }
if (data.contains("Settings")) { if (data.contains("Settings")) {
@ -897,9 +711,10 @@ void load(const std::filesystem::path& path) {
// Check if the loaded language is in the allowed list // Check if the loaded language is in the allowed list
const std::vector<std::string> allowed_languages = { const std::vector<std::string> allowed_languages = {
"ar_SA", "da_DK", "de_DE", "el_GR", "en_US", "es_ES", "fa_IR", "fi_FI", "fr_FR", "hu_HU", "ar_SA", "da_DK", "de_DE", "el_GR", "en_US", "es_ES", "fa_IR", "fi_FI",
"id_ID", "it_IT", "ja_JP", "ko_KR", "lt_LT", "nb_NO", "nl_NL", "pl_PL", "pt_BR", "pt_PT", "fr_FR", "hu_HU", "id_ID", "it_IT", "ja_JP", "ko_KR", "lt_LT", "nb_NO",
"ro_RO", "ru_RU", "sq_AL", "sv_SE", "tr_TR", "uk_UA", "vi_VN", "zh_CN", "zh_TW"}; "nl_NL", "pl_PL", "pt_BR", "pt_PT", "ro_RO", "ru_RU", "sq_AL", "sv_SE",
"tr_TR", "uk_UA", "vi_VN", "zh_CN", "zh_TW", "ca_ES", "sr_CS"};
if (std::find(allowed_languages.begin(), allowed_languages.end(), emulator_language) == if (std::find(allowed_languages.begin(), allowed_languages.end(), emulator_language) ==
allowed_languages.end()) { allowed_languages.end()) {
@ -966,17 +781,12 @@ void save(const std::filesystem::path& path) {
data["General"]["isPSNSignedIn"] = isPSNSignedIn; data["General"]["isPSNSignedIn"] = isPSNSignedIn;
data["General"]["isTrophyPopupDisabled"] = isTrophyPopupDisabled; data["General"]["isTrophyPopupDisabled"] = isTrophyPopupDisabled;
data["General"]["trophyNotificationDuration"] = trophyNotificationDuration; data["General"]["trophyNotificationDuration"] = trophyNotificationDuration;
data["General"]["playBGM"] = playBGM;
data["General"]["BGMvolume"] = BGMvolume;
data["General"]["enableDiscordRPC"] = enableDiscordRPC; data["General"]["enableDiscordRPC"] = enableDiscordRPC;
data["General"]["logFilter"] = logFilter; data["General"]["logFilter"] = logFilter;
data["General"]["logType"] = logType; data["General"]["logType"] = logType;
data["General"]["userName"] = userName; data["General"]["userName"] = userName;
data["General"]["updateChannel"] = updateChannel;
data["General"]["chooseHomeTab"] = chooseHomeTab; data["General"]["chooseHomeTab"] = chooseHomeTab;
data["General"]["showSplash"] = isShowSplash; data["General"]["showSplash"] = isShowSplash;
data["General"]["autoUpdate"] = isAutoUpdate;
data["General"]["alwaysShowChangelog"] = isAlwaysShowChangelog;
data["General"]["sideTrophy"] = isSideTrophy; data["General"]["sideTrophy"] = isSideTrophy;
data["General"]["compatibilityEnabled"] = compatibilityData; data["General"]["compatibilityEnabled"] = compatibilityData;
data["General"]["checkCompatibilityOnStartup"] = checkCompatibilityOnStartup; data["General"]["checkCompatibilityOnStartup"] = checkCompatibilityOnStartup;
@ -1046,8 +856,6 @@ void save(const std::filesystem::path& path) {
data["GUI"]["addonInstallDir"] = data["GUI"]["addonInstallDir"] =
std::string{fmt::UTF(settings_addon_install_dir.u8string()).data}; std::string{fmt::UTF(settings_addon_install_dir.u8string()).data};
data["GUI"]["emulatorLanguage"] = emulator_language; data["GUI"]["emulatorLanguage"] = emulator_language;
data["GUI"]["backgroundImageOpacity"] = backgroundImageOpacity;
data["GUI"]["showBackgroundImage"] = showBackgroundImage;
data["Settings"]["consoleLanguage"] = m_language; data["Settings"]["consoleLanguage"] = m_language;
// Sorting of TOML sections // Sorting of TOML sections
@ -1082,18 +890,7 @@ void saveMainWindow(const std::filesystem::path& path) {
fmt::print("Saving new configuration file {}\n", fmt::UTF(path.u8string())); fmt::print("Saving new configuration file {}\n", fmt::UTF(path.u8string()));
} }
data["GUI"]["mw_width"] = m_window_size_W;
data["GUI"]["mw_height"] = m_window_size_H;
data["GUI"]["theme"] = mw_themes; data["GUI"]["theme"] = mw_themes;
data["GUI"]["iconSize"] = m_icon_size;
data["GUI"]["sliderPos"] = m_slider_pos;
data["GUI"]["iconSizeGrid"] = m_icon_size_grid;
data["GUI"]["sliderPosGrid"] = m_slider_pos_grid;
data["GUI"]["gameTableMode"] = m_table_mode;
data["GUI"]["geometry_x"] = main_window_geometry_x;
data["GUI"]["geometry_y"] = main_window_geometry_y;
data["GUI"]["geometry_w"] = main_window_geometry_w;
data["GUI"]["geometry_h"] = main_window_geometry_h;
data["GUI"]["elfDirs"] = m_elf_viewer; data["GUI"]["elfDirs"] = m_elf_viewer;
data["GUI"]["recentFiles"] = m_recent_files; data["GUI"]["recentFiles"] = m_recent_files;
@ -1112,19 +909,13 @@ void setDefaultValues() {
isPSNSignedIn = false; isPSNSignedIn = false;
isFullscreen = false; isFullscreen = false;
isTrophyPopupDisabled = false; isTrophyPopupDisabled = false;
playBGM = false;
BGMvolume = 50;
enableDiscordRPC = true; enableDiscordRPC = true;
screenWidth = 1280; screenWidth = 1280;
screenHeight = 720; screenHeight = 720;
logFilter = ""; logFilter = "";
logType = "sync"; logType = "sync";
userName = "shadPS4"; userName = "shadPS4";
if (Common::g_is_release) {
updateChannel = "Release";
} else {
updateChannel = "Nightly";
}
chooseHomeTab = "General"; chooseHomeTab = "General";
cursorState = HideCursorState::Idle; cursorState = HideCursorState::Idle;
cursorHideTimeout = 5; cursorHideTimeout = 5;
@ -1135,8 +926,6 @@ void setDefaultValues() {
isDebugDump = false; isDebugDump = false;
isShaderDebug = false; isShaderDebug = false;
isShowSplash = false; isShowSplash = false;
isAutoUpdate = false;
isAlwaysShowChangelog = false;
isSideTrophy = "right"; isSideTrophy = "right";
isNullGpu = false; isNullGpu = false;
shouldDumpShaders = false; shouldDumpShaders = false;
@ -1153,8 +942,6 @@ void setDefaultValues() {
gpuId = -1; gpuId = -1;
compatibilityData = false; compatibilityData = false;
checkCompatibilityOnStartup = false; checkCompatibilityOnStartup = false;
backgroundImageOpacity = 50;
showBackgroundImage = true;
} }
constexpr std::string_view GetDefaultKeyboardConfig() { constexpr std::string_view GetDefaultKeyboardConfig() {

View File

@ -26,25 +26,18 @@ bool GetLoadGameSizeEnabled();
std::filesystem::path GetSaveDataPath(); std::filesystem::path GetSaveDataPath();
void setLoadGameSizeEnabled(bool enable); void setLoadGameSizeEnabled(bool enable);
bool getIsFullscreen(); bool getIsFullscreen();
bool getShowLabelsUnderIcons();
bool setShowLabelsUnderIcons();
std::string getFullscreenMode(); std::string getFullscreenMode();
bool isNeoModeConsole(); bool isNeoModeConsole();
bool isDevKitConsole(); bool isDevKitConsole();
bool getPlayBGM();
int getBGMvolume();
bool getisTrophyPopupDisabled(); bool getisTrophyPopupDisabled();
bool getEnableDiscordRPC(); bool getEnableDiscordRPC();
bool getCompatibilityEnabled(); bool getCompatibilityEnabled();
bool getCheckCompatibilityOnStartup(); bool getCheckCompatibilityOnStartup();
int getBackgroundImageOpacity();
bool getShowBackgroundImage();
bool getPSNSignedIn(); bool getPSNSignedIn();
std::string getLogFilter(); std::string getLogFilter();
std::string getLogType(); std::string getLogType();
std::string getUserName(); std::string getUserName();
std::string getUpdateChannel();
std::string getChooseHomeTab(); std::string getChooseHomeTab();
s16 getCursorState(); s16 getCursorState();
@ -69,8 +62,6 @@ bool allowHDR();
bool debugDump(); bool debugDump();
bool collectShadersForDebug(); bool collectShadersForDebug();
bool showSplash(); bool showSplash();
bool autoUpdate();
bool alwaysShowChangelog();
std::string sideTrophy(); std::string sideTrophy();
bool nullGpu(); bool nullGpu();
bool copyGPUCmdBuffers(); bool copyGPUCmdBuffers();
@ -83,8 +74,6 @@ u32 vblankDiv();
void setDebugDump(bool enable); void setDebugDump(bool enable);
void setCollectShaderForDebug(bool enable); void setCollectShaderForDebug(bool enable);
void setShowSplash(bool enable); void setShowSplash(bool enable);
void setAutoUpdate(bool enable);
void setAlwaysShowChangelog(bool enable);
void setSideTrophy(std::string side); void setSideTrophy(std::string side);
void setNullGpu(bool enable); void setNullGpu(bool enable);
void setAllowHDR(bool enable); void setAllowHDR(bool enable);
@ -97,21 +86,16 @@ void setScreenHeight(u32 height);
void setIsFullscreen(bool enable); void setIsFullscreen(bool enable);
void setFullscreenMode(std::string mode); void setFullscreenMode(std::string mode);
void setisTrophyPopupDisabled(bool disable); void setisTrophyPopupDisabled(bool disable);
void setPlayBGM(bool enable);
void setBGMvolume(int volume);
void setEnableDiscordRPC(bool enable); void setEnableDiscordRPC(bool enable);
void setLanguage(u32 language); void setLanguage(u32 language);
void setNeoMode(bool enable); void setNeoMode(bool enable);
void setUserName(const std::string& type); void setUserName(const std::string& type);
void setUpdateChannel(const std::string& type);
void setChooseHomeTab(const std::string& type); void setChooseHomeTab(const std::string& type);
void setGameInstallDirs(const std::vector<std::filesystem::path>& dirs_config); void setGameInstallDirs(const std::vector<std::filesystem::path>& dirs_config);
void setAllGameInstallDirs(const std::vector<GameInstallDir>& dirs_config); void setAllGameInstallDirs(const std::vector<GameInstallDir>& dirs_config);
void setSaveDataPath(const std::filesystem::path& path); void setSaveDataPath(const std::filesystem::path& path);
void setCompatibilityEnabled(bool use); void setCompatibilityEnabled(bool use);
void setCheckCompatibilityOnStartup(bool use); void setCheckCompatibilityOnStartup(bool use);
void setBackgroundImageOpacity(int opacity);
void setShowBackgroundImage(bool show);
void setPSNSignedIn(bool sign); void setPSNSignedIn(bool sign);
void setCursorState(s16 cursorState); void setCursorState(s16 cursorState);
@ -141,38 +125,19 @@ void setVkHostMarkersEnabled(bool enable);
void setVkGuestMarkersEnabled(bool enable); void setVkGuestMarkersEnabled(bool enable);
// Gui // Gui
void setMainWindowGeometry(u32 x, u32 y, u32 w, u32 h);
bool addGameInstallDir(const std::filesystem::path& dir, bool enabled = true); bool addGameInstallDir(const std::filesystem::path& dir, bool enabled = true);
void removeGameInstallDir(const std::filesystem::path& dir); void removeGameInstallDir(const std::filesystem::path& dir);
void setGameInstallDirEnabled(const std::filesystem::path& dir, bool enabled); void setGameInstallDirEnabled(const std::filesystem::path& dir, bool enabled);
void setAddonInstallDir(const std::filesystem::path& dir); void setAddonInstallDir(const std::filesystem::path& dir);
void setMainWindowTheme(u32 theme); void setMainWindowTheme(u32 theme);
void setIconSize(u32 size);
void setIconSizeGrid(u32 size);
void setSliderPosition(u32 pos);
void setSliderPositionGrid(u32 pos);
void setTableMode(u32 mode);
void setMainWindowWidth(u32 width);
void setMainWindowHeight(u32 height);
void setElfViewer(const std::vector<std::string>& elfList); void setElfViewer(const std::vector<std::string>& elfList);
void setRecentFiles(const std::vector<std::string>& recentFiles); void setRecentFiles(const std::vector<std::string>& recentFiles);
void setEmulatorLanguage(std::string language); void setEmulatorLanguage(std::string language);
u32 getMainWindowGeometryX();
u32 getMainWindowGeometryY();
u32 getMainWindowGeometryW();
u32 getMainWindowGeometryH();
const std::vector<std::filesystem::path> getGameInstallDirs(); const std::vector<std::filesystem::path> getGameInstallDirs();
const std::vector<bool> getGameInstallDirsEnabled(); const std::vector<bool> getGameInstallDirsEnabled();
std::filesystem::path getAddonInstallDir(); std::filesystem::path getAddonInstallDir();
u32 getMainWindowTheme(); u32 getMainWindowTheme();
u32 getIconSize();
u32 getIconSizeGrid();
u32 getSliderPosition();
u32 getSliderPositionGrid();
u32 getTableMode();
u32 getMainWindowWidth();
u32 getMainWindowHeight();
std::vector<std::string> getElfViewer(); std::vector<std::string> getElfViewer();
std::vector<std::string> getRecentFiles(); std::vector<std::string> getRecentFiles();
std::string getEmulatorLanguage(); std::string getEmulatorLanguage();

View File

@ -10,6 +10,8 @@
namespace Core::FileSys { namespace Core::FileSys {
bool MntPoints::ignore_game_patches = false;
std::string RemoveTrailingSlashes(const std::string& path) { std::string RemoveTrailingSlashes(const std::string& path) {
// Remove trailing slashes to make comparisons simpler. // Remove trailing slashes to make comparisons simpler.
std::string path_sanitized = path; std::string path_sanitized = path;
@ -77,7 +79,7 @@ std::filesystem::path MntPoints::GetHostPath(std::string_view path, bool* is_rea
patch_path /= rel_path; patch_path /= rel_path;
if ((corrected_path.starts_with("/app0") || corrected_path.starts_with("/hostapp")) && if ((corrected_path.starts_with("/app0") || corrected_path.starts_with("/hostapp")) &&
!force_base_path && std::filesystem::exists(patch_path)) { !force_base_path && !ignore_game_patches && std::filesystem::exists(patch_path)) {
return patch_path; return patch_path;
} }
@ -137,7 +139,7 @@ std::filesystem::path MntPoints::GetHostPath(std::string_view path, bool* is_rea
return std::optional<std::filesystem::path>(current_path); return std::optional<std::filesystem::path>(current_path);
}; };
if (!force_base_path) { if (!force_base_path && !ignore_game_patches) {
if (const auto path = search(patch_path)) { if (const auto path = search(patch_path)) {
return *path; return *path;
} }

View File

@ -21,6 +21,7 @@ class MntPoints {
static constexpr bool NeedsCaseInsensitiveSearch = true; static constexpr bool NeedsCaseInsensitiveSearch = true;
#endif #endif
public: public:
static bool ignore_game_patches;
struct MntPair { struct MntPair {
std::filesystem::path host_path; std::filesystem::path host_path;
std::string mount; // e.g /app0 std::string mount; // e.g /app0

View File

@ -125,7 +125,6 @@ int EqueueInternal::WaitForEvents(SceKernelEvent* ev, int num, u32 micros) {
.count(); .count();
count = WaitForSmallTimer(ev, num, std::max(0l, long(micros - time_waited))); count = WaitForSmallTimer(ev, num, std::max(0l, long(micros - time_waited)));
} }
small_timer_event.event.data = 0;
} }
if (ev->flags & SceKernelEvent::Flags::OneShot) { if (ev->flags & SceKernelEvent::Flags::OneShot) {
@ -179,39 +178,46 @@ int EqueueInternal::GetTriggeredEvents(SceKernelEvent* ev, int num) {
} }
bool EqueueInternal::AddSmallTimer(EqueueEvent& ev) { bool EqueueInternal::AddSmallTimer(EqueueEvent& ev) {
// We assume that only one timer event (with the same ident across calls) SmallTimer st;
// can be posted to the queue, based on observations so far. In the opposite case, st.event = ev.event;
// the small timer storage and wait logic should be reworked. st.added = std::chrono::steady_clock::now();
ASSERT(!HasSmallTimer() || small_timer_event.event.ident == ev.event.ident); st.interval = std::chrono::microseconds{ev.event.data};
ev.time_added = std::chrono::steady_clock::now(); {
small_timer_event = std::move(ev); std::scoped_lock lock{m_mutex};
m_small_timers[st.event.ident] = std::move(st);
}
return true; return true;
} }
int EqueueInternal::WaitForSmallTimer(SceKernelEvent* ev, int num, u32 micros) { int EqueueInternal::WaitForSmallTimer(SceKernelEvent* ev, int num, u32 micros) {
int count{}; ASSERT(num >= 1);
ASSERT(num == 1);
auto curr_clock = std::chrono::steady_clock::now(); auto curr_clock = std::chrono::steady_clock::now();
const auto wait_end_us = (micros == 0) ? std::chrono::steady_clock::time_point::max() const auto wait_end_us = (micros == 0) ? std::chrono::steady_clock::time_point::max()
: curr_clock + std::chrono::microseconds{micros}; : curr_clock + std::chrono::microseconds{micros};
int count = 0;
do { do {
curr_clock = std::chrono::steady_clock::now(); curr_clock = std::chrono::steady_clock::now();
{ {
std::scoped_lock lock{m_mutex}; std::scoped_lock lock{m_mutex};
if ((curr_clock - small_timer_event.time_added) > for (auto it = m_small_timers.begin(); it != m_small_timers.end() && count < num;) {
std::chrono::microseconds{small_timer_event.event.data}) { const SmallTimer& st = it->second;
ev[count++] = small_timer_event.event;
small_timer_event.event.data = 0; if (curr_clock - st.added >= st.interval) {
break; ev[count++] = st.event;
it = m_small_timers.erase(it);
} else {
++it;
} }
} }
if (count > 0)
return count;
}
std::this_thread::yield(); std::this_thread::yield();
} while (curr_clock < wait_end_us); } while (curr_clock < wait_end_us);
return count; return 0;
} }
bool EqueueInternal::EventExists(u64 id, s16 filter) { bool EqueueInternal::EventExists(u64 id, s16 filter) {
@ -326,6 +332,11 @@ s32 PS4_SYSV_ABI sceKernelAddHRTimerEvent(SceKernelEqueue eq, int id, timespec*
// `HrTimerSpinlockThresholdUs`) and fall back to boost asio timers if the time to tick is // `HrTimerSpinlockThresholdUs`) and fall back to boost asio timers if the time to tick is
// large. Even for large delays, we truncate a small portion to complete the wait // large. Even for large delays, we truncate a small portion to complete the wait
// using the spinlock, prioritizing precision. // using the spinlock, prioritizing precision.
if (eq->EventExists(event.event.ident, event.event.filter)) {
eq->RemoveEvent(id, SceKernelEvent::Filter::HrTimer);
}
if (total_us < HrTimerSpinlockThresholdUs) { if (total_us < HrTimerSpinlockThresholdUs) {
return eq->AddSmallTimer(event) ? ORBIS_OK : ORBIS_KERNEL_ERROR_ENOMEM; return eq->AddSmallTimer(event) ? ORBIS_OK : ORBIS_KERNEL_ERROR_ENOMEM;
} }

View File

@ -9,6 +9,7 @@
#include <vector> #include <vector>
#include <boost/asio/steady_timer.hpp> #include <boost/asio/steady_timer.hpp>
#include <unordered_map>
#include "common/rdtsc.h" #include "common/rdtsc.h"
#include "common/types.h" #include "common/types.h"
@ -135,6 +136,12 @@ private:
}; };
class EqueueInternal { class EqueueInternal {
struct SmallTimer {
SceKernelEvent event;
std::chrono::steady_clock::time_point added;
std::chrono::microseconds interval;
};
public: public:
explicit EqueueInternal(std::string_view name) : m_name(name) {} explicit EqueueInternal(std::string_view name) : m_name(name) {}
@ -151,13 +158,14 @@ public:
int GetTriggeredEvents(SceKernelEvent* ev, int num); int GetTriggeredEvents(SceKernelEvent* ev, int num);
bool AddSmallTimer(EqueueEvent& event); bool AddSmallTimer(EqueueEvent& event);
bool HasSmallTimer() const { bool HasSmallTimer() {
return small_timer_event.event.data != 0; std::scoped_lock lock{m_mutex};
return !m_small_timers.empty();
} }
bool RemoveSmallTimer(u64 id) { bool RemoveSmallTimer(u64 id) {
if (HasSmallTimer() && small_timer_event.event.ident == id) { if (HasSmallTimer()) {
small_timer_event = {}; std::scoped_lock lock{m_mutex};
return true; return m_small_timers.erase(id) > 0;
} }
return false; return false;
} }
@ -170,8 +178,8 @@ private:
std::string m_name; std::string m_name;
std::mutex m_mutex; std::mutex m_mutex;
std::vector<EqueueEvent> m_events; std::vector<EqueueEvent> m_events;
EqueueEvent small_timer_event{};
std::condition_variable m_cond; std::condition_variable m_cond;
std::unordered_map<u64, SmallTimer> m_small_timers;
}; };
u64 PS4_SYSV_ABI sceKernelGetEventData(const SceKernelEvent* ev); u64 PS4_SYSV_ABI sceKernelGetEventData(const SceKernelEvent* ev);

View File

@ -1050,6 +1050,7 @@ void RegisterFileSystem(Core::Loader::SymbolsResolver* sym) {
LIB_FUNCTION("4wSze92BhLI", "libkernel", 1, "libkernel", 1, 1, sceKernelWrite); LIB_FUNCTION("4wSze92BhLI", "libkernel", 1, "libkernel", 1, 1, sceKernelWrite);
LIB_FUNCTION("+WRlkKjZvag", "libkernel", 1, "libkernel", 1, 1, readv); LIB_FUNCTION("+WRlkKjZvag", "libkernel", 1, "libkernel", 1, 1, readv);
LIB_FUNCTION("YSHRBRLn2pI", "libkernel", 1, "libkernel", 1, 1, writev); LIB_FUNCTION("YSHRBRLn2pI", "libkernel", 1, "libkernel", 1, 1, writev);
LIB_FUNCTION("kAt6VDbHmro", "libkernel", 1, "libkernel", 1, 1, sceKernelWritev);
LIB_FUNCTION("Oy6IpwgtYOk", "libScePosix", 1, "libkernel", 1, 1, posix_lseek); LIB_FUNCTION("Oy6IpwgtYOk", "libScePosix", 1, "libkernel", 1, 1, posix_lseek);
LIB_FUNCTION("Oy6IpwgtYOk", "libkernel", 1, "libkernel", 1, 1, posix_lseek); LIB_FUNCTION("Oy6IpwgtYOk", "libkernel", 1, "libkernel", 1, 1, posix_lseek);
LIB_FUNCTION("oib76F-12fk", "libkernel", 1, "libkernel", 1, 1, sceKernelLseek); LIB_FUNCTION("oib76F-12fk", "libkernel", 1, "libkernel", 1, 1, sceKernelLseek);

View File

@ -99,7 +99,7 @@ s32 PS4_SYSV_ABI sceKernelReleaseDirectMemory(u64 start, size_t len) {
s32 PS4_SYSV_ABI sceKernelAvailableDirectMemorySize(u64 searchStart, u64 searchEnd, s32 PS4_SYSV_ABI sceKernelAvailableDirectMemorySize(u64 searchStart, u64 searchEnd,
size_t alignment, u64* physAddrOut, size_t alignment, u64* physAddrOut,
size_t* sizeOut) { size_t* sizeOut) {
LOG_WARNING(Kernel_Vmm, "called searchStart = {:#x}, searchEnd = {:#x}, alignment = {:#x}", LOG_INFO(Kernel_Vmm, "called searchStart = {:#x}, searchEnd = {:#x}, alignment = {:#x}",
searchStart, searchEnd, alignment); searchStart, searchEnd, alignment);
if (physAddrOut == nullptr || sizeOut == nullptr) { if (physAddrOut == nullptr || sizeOut == nullptr) {
@ -287,7 +287,7 @@ s32 PS4_SYSV_ABI sceKernelMtypeprotect(const void* addr, u64 size, s32 mtype, s3
int PS4_SYSV_ABI sceKernelDirectMemoryQuery(u64 offset, int flags, OrbisQueryInfo* query_info, int PS4_SYSV_ABI sceKernelDirectMemoryQuery(u64 offset, int flags, OrbisQueryInfo* query_info,
size_t infoSize) { size_t infoSize) {
LOG_WARNING(Kernel_Vmm, "called offset = {:#x}, flags = {:#x}", offset, flags); LOG_INFO(Kernel_Vmm, "called offset = {:#x}, flags = {:#x}", offset, flags);
auto* memory = Core::Memory::Instance(); auto* memory = Core::Memory::Instance();
return memory->DirectMemoryQuery(offset, flags == 1, query_info); return memory->DirectMemoryQuery(offset, flags == 1, query_info);
} }

View File

@ -164,10 +164,12 @@ s32 PS4_SYSV_ABI sceNpTrophyCreateContext(OrbisNpTrophyContext* context, int32_t
} }
const auto ctx_id = trophy_contexts.insert(user_id, service_label); const auto ctx_id = trophy_contexts.insert(user_id, service_label);
contexts_internal[key].context_id = ctx_id.index;
LOG_INFO(Lib_NpTrophy, "New context = {}, user_id = {} service label = {}", ctx_id.index, *context = ctx_id.index + 1;
user_id, service_label); contexts_internal[key].context_id = *context;
*context = ctx_id.index; LOG_INFO(Lib_NpTrophy, "New context = {}, user_id = {} service label = {}", *context, user_id,
service_label);
return ORBIS_OK; return ORBIS_OK;
} }
@ -179,21 +181,23 @@ s32 PS4_SYSV_ABI sceNpTrophyCreateHandle(OrbisNpTrophyHandle* handle) {
if (trophy_handles.size() >= MaxTrophyHandles) { if (trophy_handles.size() >= MaxTrophyHandles) {
return ORBIS_NP_TROPHY_ERROR_HANDLE_EXCEEDS_MAX; return ORBIS_NP_TROPHY_ERROR_HANDLE_EXCEEDS_MAX;
} }
const auto handle_id = trophy_handles.insert();
LOG_INFO(Lib_NpTrophy, "New handle = {}", handle_id.index);
*handle = handle_id.index; const auto handle_id = trophy_handles.insert();
*handle = handle_id.index + 1;
LOG_INFO(Lib_NpTrophy, "New handle = {}", *handle);
return ORBIS_OK; return ORBIS_OK;
} }
int PS4_SYSV_ABI sceNpTrophyDestroyContext(OrbisNpTrophyContext context) { int PS4_SYSV_ABI sceNpTrophyDestroyContext(OrbisNpTrophyContext context) {
LOG_INFO(Lib_NpTrophy, "Destroyed Context {}", context); LOG_INFO(Lib_NpTrophy, "Destroyed Context {}", context);
if (context == ORBIS_NP_TROPHY_INVALID_CONTEXT) if (context == ORBIS_NP_TROPHY_INVALID_CONTEXT) {
return ORBIS_NP_TROPHY_ERROR_INVALID_CONTEXT; return ORBIS_NP_TROPHY_ERROR_INVALID_CONTEXT;
}
Common::SlotId contextId; Common::SlotId contextId;
contextId.index = context; contextId.index = context - 1;
ContextKey contextkey = trophy_contexts[contextId]; ContextKey contextkey = trophy_contexts[contextId];
trophy_contexts.erase(contextId); trophy_contexts.erase(contextId);
@ -206,15 +210,17 @@ s32 PS4_SYSV_ABI sceNpTrophyDestroyHandle(OrbisNpTrophyHandle handle) {
if (handle == ORBIS_NP_TROPHY_INVALID_HANDLE) if (handle == ORBIS_NP_TROPHY_INVALID_HANDLE)
return ORBIS_NP_TROPHY_ERROR_INVALID_HANDLE; return ORBIS_NP_TROPHY_ERROR_INVALID_HANDLE;
if (handle >= trophy_handles.size()) { s32 handle_index = handle - 1;
if (handle_index >= trophy_handles.size()) {
LOG_ERROR(Lib_NpTrophy, "Invalid handle {}", handle); LOG_ERROR(Lib_NpTrophy, "Invalid handle {}", handle);
return ORBIS_NP_TROPHY_ERROR_INVALID_HANDLE; return ORBIS_NP_TROPHY_ERROR_INVALID_HANDLE;
} }
if (!trophy_handles.is_allocated({static_cast<u32>(handle)})) {
if (!trophy_handles.is_allocated({static_cast<u32>(handle_index)})) {
return ORBIS_NP_TROPHY_ERROR_INVALID_HANDLE; return ORBIS_NP_TROPHY_ERROR_INVALID_HANDLE;
} }
trophy_handles.erase({static_cast<u32>(handle)}); trophy_handles.erase({static_cast<u32>(handle_index)});
LOG_INFO(Lib_NpTrophy, "Handle {} destroyed", handle); LOG_INFO(Lib_NpTrophy, "Handle {} destroyed", handle);
return ORBIS_OK; return ORBIS_OK;
} }

View File

@ -140,7 +140,7 @@ s32 PS4_SYSV_ABI sceVideodec2Flush(OrbisVideodec2Decoder decoder,
return ORBIS_VIDEODEC2_ERROR_ARGUMENT_POINTER; return ORBIS_VIDEODEC2_ERROR_ARGUMENT_POINTER;
} }
if (frameBuffer->thisSize != sizeof(OrbisVideodec2FrameBuffer) || if (frameBuffer->thisSize != sizeof(OrbisVideodec2FrameBuffer) ||
outputInfo->thisSize != sizeof(OrbisVideodec2OutputInfo)) { (outputInfo->thisSize | 8) != sizeof(OrbisVideodec2OutputInfo)) {
LOG_ERROR(Lib_Vdec2, "Invalid struct size"); LOG_ERROR(Lib_Vdec2, "Invalid struct size");
return ORBIS_VIDEODEC2_ERROR_STRUCT_SIZE; return ORBIS_VIDEODEC2_ERROR_STRUCT_SIZE;
} }
@ -167,7 +167,7 @@ s32 PS4_SYSV_ABI sceVideodec2GetPictureInfo(const OrbisVideodec2OutputInfo* outp
LOG_ERROR(Lib_Vdec2, "Invalid arguments"); LOG_ERROR(Lib_Vdec2, "Invalid arguments");
return ORBIS_VIDEODEC2_ERROR_ARGUMENT_POINTER; return ORBIS_VIDEODEC2_ERROR_ARGUMENT_POINTER;
} }
if (outputInfo->thisSize != sizeof(OrbisVideodec2OutputInfo)) { if ((outputInfo->thisSize | 8) != sizeof(OrbisVideodec2OutputInfo)) {
LOG_ERROR(Lib_Vdec2, "Invalid struct size"); LOG_ERROR(Lib_Vdec2, "Invalid struct size");
return ORBIS_VIDEODEC2_ERROR_STRUCT_SIZE; return ORBIS_VIDEODEC2_ERROR_STRUCT_SIZE;
} }
@ -179,7 +179,7 @@ s32 PS4_SYSV_ABI sceVideodec2GetPictureInfo(const OrbisVideodec2OutputInfo* outp
if (p1stPictureInfoOut) { if (p1stPictureInfoOut) {
OrbisVideodec2AvcPictureInfo* picInfo = OrbisVideodec2AvcPictureInfo* picInfo =
static_cast<OrbisVideodec2AvcPictureInfo*>(p1stPictureInfoOut); static_cast<OrbisVideodec2AvcPictureInfo*>(p1stPictureInfoOut);
if (picInfo->thisSize != sizeof(OrbisVideodec2AvcPictureInfo)) { if ((picInfo->thisSize | 16) != sizeof(OrbisVideodec2AvcPictureInfo)) {
LOG_ERROR(Lib_Vdec2, "Invalid struct size"); LOG_ERROR(Lib_Vdec2, "Invalid struct size");
return ORBIS_VIDEODEC2_ERROR_STRUCT_SIZE; return ORBIS_VIDEODEC2_ERROR_STRUCT_SIZE;
} }

View File

@ -73,8 +73,10 @@ struct OrbisVideodec2OutputInfo {
u32 frameHeight; u32 frameHeight;
void* frameBuffer; void* frameBuffer;
u64 frameBufferSize; u64 frameBufferSize;
u32 frameFormat;
u32 framePitchInBytes;
}; };
static_assert(sizeof(OrbisVideodec2OutputInfo) == 0x30); static_assert(sizeof(OrbisVideodec2OutputInfo) == 0x38);
struct OrbisVideodec2FrameBuffer { struct OrbisVideodec2FrameBuffer {
u64 thisSize; u64 thisSize;

View File

@ -55,6 +55,23 @@ struct OrbisVideodec2AvcPictureInfo {
u8 pic_struct; u8 pic_struct;
u8 field_pic_flag; u8 field_pic_flag;
u8 bottom_field_flag; u8 bottom_field_flag;
u8 sequenceParameterSetPresentFlag;
u8 pictureParameterSetPresentFlag;
u8 auDelimiterPresentFlag;
u8 endOfSequencePresentFlag;
u8 endOfStreamPresentFlag;
u8 fillerDataPresentFlag;
u8 pictureTimingSeiPresentFlag;
u8 bufferingPeriodSeiPresentFlag;
u8 constraint_set0_flag;
u8 constraint_set1_flag;
u8 constraint_set2_flag;
u8 constraint_set3_flag;
u8 constraint_set4_flag;
u8 constraint_set5_flag;
}; };
static_assert(sizeof(OrbisVideodec2AvcPictureInfo) == 0x78);
} // namespace Libraries::Vdec2 } // namespace Libraries::Vdec2

View File

@ -44,11 +44,15 @@ s32 VdecDecoder::Decode(const OrbisVideodec2InputData& inputData,
OrbisVideodec2FrameBuffer& frameBuffer, OrbisVideodec2FrameBuffer& frameBuffer,
OrbisVideodec2OutputInfo& outputInfo) { OrbisVideodec2OutputInfo& outputInfo) {
frameBuffer.isAccepted = false; frameBuffer.isAccepted = false;
outputInfo.thisSize = sizeof(OrbisVideodec2OutputInfo);
outputInfo.isValid = false; outputInfo.isValid = false;
outputInfo.isErrorFrame = true; outputInfo.isErrorFrame = true;
outputInfo.pictureCount = 0; outputInfo.pictureCount = 0;
// Only set frameFormat if the game uses the newer struct version.
if (outputInfo.thisSize == sizeof(OrbisVideodec2OutputInfo)) {
outputInfo.frameFormat = 0;
}
if (!inputData.auData) { if (!inputData.auData) {
return ORBIS_VIDEODEC2_ERROR_ACCESS_UNIT_POINTER; return ORBIS_VIDEODEC2_ERROR_ACCESS_UNIT_POINTER;
} }
@ -113,6 +117,11 @@ s32 VdecDecoder::Decode(const OrbisVideodec2InputData& inputData,
outputInfo.isErrorFrame = false; outputInfo.isErrorFrame = false;
outputInfo.pictureCount = 1; // TODO: 2 pictures for interlaced video outputInfo.pictureCount = 1; // TODO: 2 pictures for interlaced video
// Only set framePitchInBytes if the game uses the newer struct version.
if (outputInfo.thisSize == sizeof(OrbisVideodec2OutputInfo)) {
outputInfo.framePitchInBytes = frame->linesize[0];
}
if (outputInfo.isValid) { if (outputInfo.isValid) {
OrbisVideodec2AvcPictureInfo pictureInfo = {}; OrbisVideodec2AvcPictureInfo pictureInfo = {};
@ -140,11 +149,15 @@ s32 VdecDecoder::Decode(const OrbisVideodec2InputData& inputData,
s32 VdecDecoder::Flush(OrbisVideodec2FrameBuffer& frameBuffer, s32 VdecDecoder::Flush(OrbisVideodec2FrameBuffer& frameBuffer,
OrbisVideodec2OutputInfo& outputInfo) { OrbisVideodec2OutputInfo& outputInfo) {
frameBuffer.isAccepted = false; frameBuffer.isAccepted = false;
outputInfo.thisSize = sizeof(OrbisVideodec2OutputInfo);
outputInfo.isValid = false; outputInfo.isValid = false;
outputInfo.isErrorFrame = true; outputInfo.isErrorFrame = true;
outputInfo.pictureCount = 0; outputInfo.pictureCount = 0;
// Only set frameFormat if the game uses the newer struct version.
if (outputInfo.thisSize == sizeof(OrbisVideodec2OutputInfo)) {
outputInfo.frameFormat = 0;
}
AVFrame* frame = av_frame_alloc(); AVFrame* frame = av_frame_alloc();
if (!frame) { if (!frame) {
LOG_ERROR(Lib_Vdec2, "Failed to allocate frame"); LOG_ERROR(Lib_Vdec2, "Failed to allocate frame");
@ -182,6 +195,11 @@ s32 VdecDecoder::Flush(OrbisVideodec2FrameBuffer& frameBuffer,
outputInfo.isErrorFrame = false; outputInfo.isErrorFrame = false;
outputInfo.pictureCount = 1; // TODO: 2 pictures for interlaced video outputInfo.pictureCount = 1; // TODO: 2 pictures for interlaced video
// Only set framePitchInBytes if the game uses the newer struct version.
if (outputInfo.thisSize == sizeof(OrbisVideodec2OutputInfo)) {
outputInfo.framePitchInBytes = frame->linesize[0];
}
// FIXME: Should we add picture info here too? // FIXME: Should we add picture info here too?
} }

View File

@ -222,6 +222,7 @@ PAddr MemoryManager::Allocate(PAddr search_start, PAddr search_end, size_t size,
auto& area = CarveDmemArea(mapping_start, size)->second; auto& area = CarveDmemArea(mapping_start, size)->second;
area.memory_type = memory_type; area.memory_type = memory_type;
area.is_free = false; area.is_free = false;
MergeAdjacent(dmem_map, dmem_area);
return mapping_start; return mapping_start;
} }

View File

@ -75,6 +75,9 @@ struct DirectMemoryArea {
if (base + size != next.base) { if (base + size != next.base) {
return false; return false;
} }
if (memory_type != next.memory_type) {
return false;
}
if (is_free != next.is_free) { if (is_free != next.is_free) {
return false; return false;
} }

View File

@ -11,6 +11,7 @@
#include <windows.h> #include <windows.h>
#else #else
#include <csignal> #include <csignal>
#include <pthread.h>
#ifdef ARCH_X86_64 #ifdef ARCH_X86_64
#include <Zydis/Formatter.h> #include <Zydis/Formatter.h>
#endif #endif

View File

@ -51,7 +51,7 @@ Tcb* GetTcbBase() {
// Apple x86_64 // Apple x86_64
// Reserve space in the 32-bit address range for allocating TCB pages. // Reserve space in the 32-bit address range for allocating TCB pages.
asm(".zerofill TCB_SPACE,TCB_SPACE,__guest_system,0x3FC000"); asm(".zerofill TCB_SPACE,TCB_SPACE,__tcb_space,0x3FC000");
struct LdtPage { struct LdtPage {
void* tcb; void* tcb;

View File

@ -75,7 +75,7 @@ void Emulator::Run(std::filesystem::path file, const std::vector<std::string> ar
game_folder_name.ends_with("-UPDATE") || game_folder_name.ends_with("-patch")) { game_folder_name.ends_with("-UPDATE") || game_folder_name.ends_with("-patch")) {
// If an executable was launched from a separate update directory, // If an executable was launched from a separate update directory,
// use the base game directory as the game folder. // use the base game directory as the game folder.
const auto base_name = game_folder_name.substr(0, game_folder_name.size() - 7); const std::string base_name = game_folder_name.substr(0, game_folder_name.rfind('-'));
const auto base_path = game_folder.parent_path() / base_name; const auto base_path = game_folder.parent_path() / base_name;
if (std::filesystem::is_directory(base_path)) { if (std::filesystem::is_directory(base_path)) {
game_folder = base_path; game_folder = base_path;

View File

@ -35,16 +35,19 @@ int main(int argc, char* argv[]) {
std::unordered_map<std::string, std::function<void(int&)>> arg_map = { std::unordered_map<std::string, std::function<void(int&)>> arg_map = {
{"-h", {"-h",
[&](int&) { [&](int&) {
std::cout << "Usage: shadps4 [options] <elf or eboot.bin path>\n" std::cout
<< "Usage: shadps4 [options] <elf or eboot.bin path>\n"
"Options:\n" "Options:\n"
" -g, --game <path|ID> Specify game path to launch\n" " -g, --game <path|ID> Specify game path to launch\n"
" -- ... Parameters passed to the game ELF. " " -- ... Parameters passed to the game ELF. "
"Needs to be at the end of the line, and everything after \"--\" is a " "Needs to be at the end of the line, and everything after \"--\" is a "
"game argument.\n" "game argument.\n"
" -p, --patch <patch_file> Apply specified patch file\n" " -p, --patch <patch_file> Apply specified patch file\n"
" -i, --ignore-game-patch Disable automatic loading of game patch\n"
" -f, --fullscreen <true|false> Specify window initial fullscreen " " -f, --fullscreen <true|false> Specify window initial fullscreen "
"state. Does not overwrite the config file.\n" "state. Does not overwrite the config file.\n"
" --add-game-folder <folder> Adds a new game folder to the config.\n" " --add-game-folder <folder> Adds a new game folder to the config.\n"
" --set-addon-folder <folder> Sets the addon folder to the config.\n"
" -h, --help Display this help message\n"; " -h, --help Display this help message\n";
exit(0); exit(0);
}}, }},
@ -72,6 +75,8 @@ int main(int argc, char* argv[]) {
} }
}}, }},
{"--patch", [&](int& i) { arg_map["-p"](i); }}, {"--patch", [&](int& i) { arg_map["-p"](i); }},
{"-i", [&](int&) { Core::FileSys::MntPoints::ignore_game_patches = true; }},
{"--ignore-game-patch", [&](int& i) { arg_map["-i"](i); }},
{"-f", {"-f",
[&](int& i) { [&](int& i) {
if (++i >= argc) { if (++i >= argc) {
@ -112,7 +117,24 @@ int main(int argc, char* argv[]) {
std::cout << "Game folder successfully saved.\n"; std::cout << "Game folder successfully saved.\n";
exit(0); exit(0);
}}, }},
}; {"--set-addon-folder", [&](int& i) {
if (++i >= argc) {
std::cerr << "Error: Missing argument for --add-addon-folder\n";
exit(1);
}
std::string config_dir(argv[i]);
std::filesystem::path config_path = std::filesystem::path(config_dir);
std::error_code discard;
if (!std::filesystem::exists(config_path, discard)) {
std::cerr << "Error: File does not exist: " << config_path << "\n";
exit(1);
}
Config::setAddonInstallDir(config_path);
Config::save(Common::FS::GetUserPath(Common::FS::PathType::UserDir) / "config.toml");
std::cout << "Addon folder successfully saved.\n";
exit(0);
}}};
if (argc == 1) { if (argc == 1) {
int dummy = 0; // one does not simply pass 0 directly int dummy = 0; // one does not simply pass 0 directly

View File

@ -28,8 +28,10 @@
using namespace Common::FS; using namespace Common::FS;
CheckUpdate::CheckUpdate(const bool showMessage, QWidget* parent) CheckUpdate::CheckUpdate(std::shared_ptr<gui_settings> gui_settings, const bool showMessage,
: QDialog(parent), networkManager(new QNetworkAccessManager(this)) { QWidget* parent)
: QDialog(parent), m_gui_settings(std::move(gui_settings)),
networkManager(new QNetworkAccessManager(this)) {
setWindowTitle(tr("Auto Updater")); setWindowTitle(tr("Auto Updater"));
setFixedSize(0, 0); setFixedSize(0, 0);
CheckForUpdates(showMessage); CheckForUpdates(showMessage);
@ -43,7 +45,7 @@ void CheckUpdate::CheckForUpdates(const bool showMessage) {
bool checkName = true; bool checkName = true;
while (checkName) { while (checkName) {
updateChannel = QString::fromStdString(Config::getUpdateChannel()); updateChannel = m_gui_settings->GetValue(gui::gen_updateChannel).toString();
if (updateChannel == "Nightly") { if (updateChannel == "Nightly") {
url = QUrl("https://api.github.com/repos/shadps4-emu/shadPS4/releases"); url = QUrl("https://api.github.com/repos/shadps4-emu/shadPS4/releases");
checkName = false; checkName = false;
@ -52,12 +54,10 @@ void CheckUpdate::CheckForUpdates(const bool showMessage) {
checkName = false; checkName = false;
} else { } else {
if (Common::g_is_release) { if (Common::g_is_release) {
Config::setUpdateChannel("Release"); m_gui_settings->SetValue(gui::gen_updateChannel, "Release");
} else { } else {
Config::setUpdateChannel("Nightly"); m_gui_settings->SetValue(gui::gen_updateChannel, "Nightly");
} }
const auto config_dir = Common::FS::GetUserPath(Common::FS::PathType::UserDir);
Config::save(config_dir / "config.toml");
} }
} }
@ -198,7 +198,7 @@ void CheckUpdate::setupUI(const QString& downloadUrl, const QString& latestDate,
titleLayout->addWidget(titleLabel); titleLayout->addWidget(titleLabel);
layout->addLayout(titleLayout); layout->addLayout(titleLayout);
QString updateChannel = QString::fromStdString(Config::getUpdateChannel()); QString updateChannel = m_gui_settings->GetValue(gui::gen_updateChannel).toString();
QString updateText = QString("<p><b>" + tr("Update Channel") + ": </b>" + updateChannel + QString updateText = QString("<p><b>" + tr("Update Channel") + ": </b>" + updateChannel +
"<br>" "<br>"
@ -273,7 +273,7 @@ void CheckUpdate::setupUI(const QString& downloadUrl, const QString& latestDate,
} }
}); });
if (Config::alwaysShowChangelog()) { if (m_gui_settings->GetValue(gui::gen_showChangeLog).toBool()) {
requestChangelog(currentRev, latestRev, downloadUrl, latestDate, currentDate); requestChangelog(currentRev, latestRev, downloadUrl, latestDate, currentDate);
textField->setVisible(true); textField->setVisible(true);
toggleButton->setText(tr("Hide Changelog")); toggleButton->setText(tr("Hide Changelog"));
@ -290,14 +290,14 @@ void CheckUpdate::setupUI(const QString& downloadUrl, const QString& latestDate,
connect(noButton, &QPushButton::clicked, this, [this]() { close(); }); connect(noButton, &QPushButton::clicked, this, [this]() { close(); });
autoUpdateCheckBox->setChecked(Config::autoUpdate()); autoUpdateCheckBox->setChecked(m_gui_settings->GetValue(gui::gen_checkForUpdates).toBool());
#if (QT_VERSION < QT_VERSION_CHECK(6, 7, 0)) #if (QT_VERSION < QT_VERSION_CHECK(6, 7, 0))
connect(autoUpdateCheckBox, &QCheckBox::stateChanged, this, [](int state) { connect(autoUpdateCheckBox, &QCheckBox::stateChanged, this, [this](int state) {
#else #else
connect(autoUpdateCheckBox, &QCheckBox::checkStateChanged, this, [](Qt::CheckState state) { connect(autoUpdateCheckBox, &QCheckBox::checkStateChanged, this, [this](Qt::CheckState state) {
#endif #endif
const auto user_dir = Common::FS::GetUserPath(Common::FS::PathType::UserDir); const auto user_dir = Common::FS::GetUserPath(Common::FS::PathType::UserDir);
Config::setAutoUpdate(state == Qt::Checked); m_gui_settings->SetValue(gui::gen_checkForUpdates, (state == Qt::Checked));
Config::save(user_dir / "config.toml"); Config::save(user_dir / "config.toml");
}); });

View File

@ -8,12 +8,14 @@
#include <QDialog> #include <QDialog>
#include <QNetworkAccessManager> #include <QNetworkAccessManager>
#include <QPushButton> #include <QPushButton>
#include "gui_settings.h"
class CheckUpdate : public QDialog { class CheckUpdate : public QDialog {
Q_OBJECT Q_OBJECT
public: public:
explicit CheckUpdate(const bool showMessage, QWidget* parent = nullptr); explicit CheckUpdate(std::shared_ptr<gui_settings> gui_settings, const bool showMessage,
QWidget* parent = nullptr);
~CheckUpdate(); ~CheckUpdate();
private slots: private slots:
@ -35,6 +37,7 @@ private:
QString updateDownloadUrl; QString updateDownloadUrl;
QNetworkAccessManager* networkManager; QNetworkAccessManager* networkManager;
std::shared_ptr<gui_settings> m_gui_settings;
}; };
#endif // CHECKUPDATE_H #endif // CHECKUPDATE_H

View File

@ -5,11 +5,13 @@
#include "game_grid_frame.h" #include "game_grid_frame.h"
#include "qt_gui/compatibility_info.h" #include "qt_gui/compatibility_info.h"
GameGridFrame::GameGridFrame(std::shared_ptr<GameInfoClass> game_info_get, GameGridFrame::GameGridFrame(std::shared_ptr<gui_settings> gui_settings,
std::shared_ptr<GameInfoClass> game_info_get,
std::shared_ptr<CompatibilityInfoClass> compat_info_get, std::shared_ptr<CompatibilityInfoClass> compat_info_get,
QWidget* parent) QWidget* parent)
: QTableWidget(parent), m_game_info(game_info_get), m_compat_info(compat_info_get) { : QTableWidget(parent), m_gui_settings(std::move(gui_settings)), m_game_info(game_info_get),
icon_size = Config::getIconSizeGrid(); m_compat_info(compat_info_get) {
icon_size = m_gui_settings->GetValue(gui::gg_icon_size).toInt();
windowWidth = parent->width(); windowWidth = parent->width();
this->setShowGrid(false); this->setShowGrid(false);
this->setEditTriggers(QAbstractItemView::NoEditTriggers); this->setEditTriggers(QAbstractItemView::NoEditTriggers);
@ -74,7 +76,7 @@ void GameGridFrame::onCurrentCellChanged(int currentRow, int currentColumn, int
} }
void GameGridFrame::PlayBackgroundMusic(QString path) { void GameGridFrame::PlayBackgroundMusic(QString path) {
if (path.isEmpty() || !Config::getPlayBGM()) { if (path.isEmpty() || !m_gui_settings->GetValue(gui::gl_playBackgroundMusic).toBool()) {
BackgroundMusicPlayer::getInstance().stopMusic(); BackgroundMusicPlayer::getInstance().stopMusic();
return; return;
} }
@ -91,7 +93,8 @@ void GameGridFrame::PopulateGameGrid(QVector<GameInfo> m_games_search, bool from
else else
m_games_ = m_game_info->m_games; m_games_ = m_game_info->m_games;
m_games_shared = std::make_shared<QVector<GameInfo>>(m_games_); m_games_shared = std::make_shared<QVector<GameInfo>>(m_games_);
icon_size = Config::getIconSizeGrid(); // update icon size for resize event. icon_size =
m_gui_settings->GetValue(gui::gg_icon_size).toInt(); // update icon size for resize event.
int gamesPerRow = windowWidth / (icon_size + 20); // 2 x cell widget border size. int gamesPerRow = windowWidth / (icon_size + 20); // 2 x cell widget border size.
int row = 0; int row = 0;
@ -118,7 +121,7 @@ void GameGridFrame::PopulateGameGrid(QVector<GameInfo> m_games_search, bool from
layout->addWidget(name_label); layout->addWidget(name_label);
// Resizing of font-size. // Resizing of font-size.
float fontSize = (Config::getIconSizeGrid() / 5.5f); float fontSize = (m_gui_settings->GetValue(gui::gg_icon_size).toInt() / 5.5f);
QString styleSheet = QString styleSheet =
QString("color: white; font-weight: bold; font-size: %1px;").arg(fontSize); QString("color: white; font-weight: bold; font-size: %1px;").arg(fontSize);
name_label->setStyleSheet(styleSheet); name_label->setStyleSheet(styleSheet);
@ -168,7 +171,7 @@ void GameGridFrame::SetGridBackgroundImage(int row, int column) {
} }
// If background images are hidden, clear the background image // If background images are hidden, clear the background image
if (!Config::getShowBackgroundImage()) { if (!m_gui_settings->GetValue(gui::gl_showBackgroundImage).toBool()) {
backgroundImage = QImage(); backgroundImage = QImage();
m_last_opacity = -1; // Reset opacity tracking when disabled m_last_opacity = -1; // Reset opacity tracking when disabled
m_current_game_path.clear(); // Reset current game path m_current_game_path.clear(); // Reset current game path
@ -177,7 +180,7 @@ void GameGridFrame::SetGridBackgroundImage(int row, int column) {
} }
const auto& game = (*m_games_shared)[itemID]; const auto& game = (*m_games_shared)[itemID];
const int opacity = Config::getBackgroundImageOpacity(); const int opacity = m_gui_settings->GetValue(gui::gl_backgroundImageOpacity).toInt();
// Recompute if opacity changed or we switched to a different game // Recompute if opacity changed or we switched to a different game
if (opacity != m_last_opacity || game.pic_path != m_current_game_path) { if (opacity != m_last_opacity || game.pic_path != m_current_game_path) {
@ -195,7 +198,8 @@ void GameGridFrame::SetGridBackgroundImage(int row, int column) {
void GameGridFrame::RefreshGridBackgroundImage() { void GameGridFrame::RefreshGridBackgroundImage() {
QPalette palette; QPalette palette;
if (!backgroundImage.isNull() && Config::getShowBackgroundImage()) { if (!backgroundImage.isNull() &&
m_gui_settings->GetValue(gui::gl_showBackgroundImage).toBool()) {
QSize widgetSize = size(); QSize widgetSize = size();
QPixmap scaledPixmap = QPixmap scaledPixmap =
QPixmap::fromImage(backgroundImage) QPixmap::fromImage(backgroundImage)

View File

@ -11,6 +11,7 @@
#include "game_info.h" #include "game_info.h"
#include "game_list_utils.h" #include "game_list_utils.h"
#include "gui_context_menus.h" #include "gui_context_menus.h"
#include "gui_settings.h"
#include "qt_gui/compatibility_info.h" #include "qt_gui/compatibility_info.h"
class GameGridFrame : public QTableWidget { class GameGridFrame : public QTableWidget {
@ -37,9 +38,11 @@ private:
bool validCellSelected = false; bool validCellSelected = false;
int m_last_opacity = -1; // Track last opacity to avoid unnecessary recomputation int m_last_opacity = -1; // Track last opacity to avoid unnecessary recomputation
std::filesystem::path m_current_game_path; // Track current game path to detect changes std::filesystem::path m_current_game_path; // Track current game path to detect changes
std::shared_ptr<gui_settings> m_gui_settings;
public: public:
explicit GameGridFrame(std::shared_ptr<GameInfoClass> game_info_get, explicit GameGridFrame(std::shared_ptr<gui_settings> gui_settings,
std::shared_ptr<GameInfoClass> game_info_get,
std::shared_ptr<CompatibilityInfoClass> compat_info_get, std::shared_ptr<CompatibilityInfoClass> compat_info_get,
QWidget* parent = nullptr); QWidget* parent = nullptr);
void PopulateGameGrid(QVector<GameInfo> m_games, bool fromSearch); void PopulateGameGrid(QVector<GameInfo> m_games, bool fromSearch);

View File

@ -9,11 +9,13 @@
#include "game_list_frame.h" #include "game_list_frame.h"
#include "game_list_utils.h" #include "game_list_utils.h"
GameListFrame::GameListFrame(std::shared_ptr<GameInfoClass> game_info_get, GameListFrame::GameListFrame(std::shared_ptr<gui_settings> gui_settings,
std::shared_ptr<GameInfoClass> game_info_get,
std::shared_ptr<CompatibilityInfoClass> compat_info_get, std::shared_ptr<CompatibilityInfoClass> compat_info_get,
QWidget* parent) QWidget* parent)
: QTableWidget(parent), m_game_info(game_info_get), m_compat_info(compat_info_get) { : QTableWidget(parent), m_gui_settings(std::move(gui_settings)), m_game_info(game_info_get),
icon_size = Config::getIconSize(); m_compat_info(compat_info_get) {
icon_size = m_gui_settings->GetValue(gui::gl_icon_size).toInt();
this->setShowGrid(false); this->setShowGrid(false);
this->setEditTriggers(QAbstractItemView::NoEditTriggers); this->setEditTriggers(QAbstractItemView::NoEditTriggers);
this->setSelectionBehavior(QAbstractItemView::SelectRows); this->setSelectionBehavior(QAbstractItemView::SelectRows);
@ -97,7 +99,7 @@ void GameListFrame::onCurrentCellChanged(int currentRow, int currentColumn, int
} }
void GameListFrame::PlayBackgroundMusic(QTableWidgetItem* item) { void GameListFrame::PlayBackgroundMusic(QTableWidgetItem* item) {
if (!item || !Config::getPlayBGM()) { if (!item || !m_gui_settings->GetValue(gui::gl_playBackgroundMusic).toBool()) {
BackgroundMusicPlayer::getInstance().stopMusic(); BackgroundMusicPlayer::getInstance().stopMusic();
return; return;
} }
@ -172,7 +174,7 @@ void GameListFrame::SetListBackgroundImage(QTableWidgetItem* item) {
} }
// If background images are hidden, clear the background image // If background images are hidden, clear the background image
if (!Config::getShowBackgroundImage()) { if (!m_gui_settings->GetValue(gui::gl_showBackgroundImage).toBool()) {
backgroundImage = QImage(); backgroundImage = QImage();
m_last_opacity = -1; // Reset opacity tracking when disabled m_last_opacity = -1; // Reset opacity tracking when disabled
m_current_game_path.clear(); // Reset current game path m_current_game_path.clear(); // Reset current game path
@ -181,7 +183,7 @@ void GameListFrame::SetListBackgroundImage(QTableWidgetItem* item) {
} }
const auto& game = m_game_info->m_games[item->row()]; const auto& game = m_game_info->m_games[item->row()];
const int opacity = Config::getBackgroundImageOpacity(); const int opacity = m_gui_settings->GetValue(gui::gl_backgroundImageOpacity).toInt();
// Recompute if opacity changed or we switched to a different game // Recompute if opacity changed or we switched to a different game
if (opacity != m_last_opacity || game.pic_path != m_current_game_path) { if (opacity != m_last_opacity || game.pic_path != m_current_game_path) {
@ -200,7 +202,8 @@ void GameListFrame::SetListBackgroundImage(QTableWidgetItem* item) {
void GameListFrame::RefreshListBackgroundImage() { void GameListFrame::RefreshListBackgroundImage() {
QPalette palette; QPalette palette;
if (!backgroundImage.isNull() && Config::getShowBackgroundImage()) { if (!backgroundImage.isNull() &&
m_gui_settings->GetValue(gui::gl_showBackgroundImage).toBool()) {
QSize widgetSize = size(); QSize widgetSize = size();
QPixmap scaledPixmap = QPixmap scaledPixmap =
QPixmap::fromImage(backgroundImage) QPixmap::fromImage(backgroundImage)

View File

@ -17,11 +17,13 @@
#include "game_info.h" #include "game_info.h"
#include "game_list_utils.h" #include "game_list_utils.h"
#include "gui_context_menus.h" #include "gui_context_menus.h"
#include "gui_settings.h"
class GameListFrame : public QTableWidget { class GameListFrame : public QTableWidget {
Q_OBJECT Q_OBJECT
public: public:
explicit GameListFrame(std::shared_ptr<GameInfoClass> game_info_get, explicit GameListFrame(std::shared_ptr<gui_settings> gui_settings,
std::shared_ptr<GameInfoClass> game_info_get,
std::shared_ptr<CompatibilityInfoClass> compat_info_get, std::shared_ptr<CompatibilityInfoClass> compat_info_get,
QWidget* parent = nullptr); QWidget* parent = nullptr);
Q_SIGNALS: Q_SIGNALS:
@ -48,6 +50,7 @@ private:
QTableWidgetItem* m_current_item = nullptr; QTableWidgetItem* m_current_item = nullptr;
int m_last_opacity = -1; // Track last opacity to avoid unnecessary recomputation int m_last_opacity = -1; // Track last opacity to avoid unnecessary recomputation
std::filesystem::path m_current_game_path; // Track current game path to detect changes std::filesystem::path m_current_game_path; // Track current game path to detect changes
std::shared_ptr<gui_settings> m_gui_settings;
public: public:
void PopulateGameList(bool isInitialPopulation = true); void PopulateGameList(bool isInitialPopulation = true);

View File

@ -0,0 +1,9 @@
// SPDX-FileCopyrightText: Copyright 2025 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "gui_settings.h"
gui_settings::gui_settings(QObject* parent) : settings(parent) {
m_settings = std::make_unique<QSettings>(ComputeSettingsDir() + "qt_ui.ini",
QSettings::Format::IniFormat, parent);
}

46
src/qt_gui/gui_settings.h Normal file
View File

@ -0,0 +1,46 @@
// SPDX-FileCopyrightText: Copyright 2025 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <QWindow>
#include "settings.h"
namespace gui {
// categories
const QString general_settings = "general_settings";
const QString main_window = "main_window";
const QString game_list = "game_list";
const QString game_grid = "game_grid";
// general
const gui_value gen_checkForUpdates = gui_value(general_settings, "checkForUpdates", false);
const gui_value gen_showChangeLog = gui_value(general_settings, "showChangeLog", false);
const gui_value gen_updateChannel = gui_value(general_settings, "updateChannel", "Release");
// main window settings
const gui_value mw_geometry = gui_value(main_window, "geometry", QByteArray());
const gui_value mw_showLabelsUnderIcons = gui_value(main_window, "showLabelsUnderIcons", true);
// game list settings
const gui_value gl_mode = gui_value(game_list, "tableMode", 0);
const gui_value gl_icon_size = gui_value(game_list, "icon_size", 36);
const gui_value gl_slider_pos = gui_value(game_list, "slider_pos", 0);
const gui_value gl_showBackgroundImage = gui_value(game_list, "showBackgroundImage", true);
const gui_value gl_backgroundImageOpacity = gui_value(game_list, "backgroundImageOpacity", 50);
const gui_value gl_playBackgroundMusic = gui_value(game_list, "playBackgroundMusic", true);
const gui_value gl_backgroundMusicVolume = gui_value(game_list, "backgroundMusicVolume", 50);
// game grid settings
const gui_value gg_icon_size = gui_value(game_grid, "icon_size", 69);
const gui_value gg_slider_pos = gui_value(game_grid, "slider_pos", 0);
} // namespace gui
class gui_settings : public settings {
Q_OBJECT
public:
explicit gui_settings(QObject* parent = nullptr);
~gui_settings() override = default;
};

View File

@ -35,11 +35,11 @@ KBMSettings::KBMSettings(std::shared_ptr<GameInfoClass> game_info_get, QWidget*
ButtonsList = { ButtonsList = {
ui->CrossButton, ui->CircleButton, ui->TriangleButton, ui->SquareButton, ui->CrossButton, ui->CircleButton, ui->TriangleButton, ui->SquareButton,
ui->L1Button, ui->R1Button, ui->L2Button, ui->R2Button, ui->L1Button, ui->R1Button, ui->L2Button, ui->R2Button,
ui->L3Button, ui->R3Button, ui->TouchpadButton, ui->OptionsButton, ui->L3Button, ui->R3Button, ui->OptionsButton, ui->TouchpadButton,
ui->TouchpadButton, ui->DpadUpButton, ui->DpadDownButton, ui->DpadLeftButton, ui->DpadUpButton, ui->DpadDownButton, ui->DpadLeftButton, ui->DpadRightButton,
ui->DpadRightButton, ui->LStickUpButton, ui->LStickDownButton, ui->LStickLeftButton, ui->LStickUpButton, ui->LStickDownButton, ui->LStickLeftButton, ui->LStickRightButton,
ui->LStickRightButton, ui->RStickUpButton, ui->RStickDownButton, ui->RStickLeftButton, ui->RStickUpButton, ui->RStickDownButton, ui->RStickLeftButton, ui->RStickRightButton,
ui->RStickRightButton, ui->LHalfButton, ui->RHalfButton}; ui->LHalfButton, ui->RHalfButton};
ButtonConnects(); ButtonConnects();
SetUIValuestoMappings("default"); SetUIValuestoMappings("default");
@ -372,14 +372,31 @@ void KBMSettings::SaveKBMConfig(bool CloseOnSave) {
file.close(); file.close();
// Prevent duplicate inputs for KBM as this breaks the engine // Prevent duplicate inputs for KBM as this breaks the engine
bool duplicateFound = false;
QSet<QString> duplicateMappings;
for (auto it = inputs.begin(); it != inputs.end(); ++it) { for (auto it = inputs.begin(); it != inputs.end(); ++it) {
if (std::find(it + 1, inputs.end(), *it) != inputs.end()) { if (std::find(it + 1, inputs.end(), *it) != inputs.end()) {
QMessageBox::information(this, tr("Unable to Save"), duplicateFound = true;
tr("Cannot bind any unique input more than once")); duplicateMappings.insert(QString::fromStdString(*it));
return;
} }
} }
if (duplicateFound) {
QStringList duplicatesList;
for (const QString mapping : duplicateMappings) {
for (const auto& button : ButtonsList) {
if (button->text() == mapping)
duplicatesList.append(button->objectName() + " - " + mapping);
}
}
QMessageBox::information(
this, tr("Unable to Save"),
// clang-format off
QString(tr("Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons:\n\n%1").arg(duplicatesList.join("\n"))));
// clang-format on
return;
}
std::vector<std::string> save; std::vector<std::string> save;
bool CurrentLineEmpty = false, LastLineEmpty = false; bool CurrentLineEmpty = false, LastLineEmpty = false;
for (auto const& line : lines) { for (auto const& line : lines) {

View File

@ -41,7 +41,8 @@ int main(int argc, char* argv[]) {
std::unordered_map<std::string, std::function<void(int&)>> arg_map = { std::unordered_map<std::string, std::function<void(int&)>> arg_map = {
{"-h", {"-h",
[&](int&) { [&](int&) {
std::cout << "Usage: shadps4 [options]\n" std::cout
<< "Usage: shadps4 [options]\n"
"Options:\n" "Options:\n"
" No arguments: Opens the GUI.\n" " No arguments: Opens the GUI.\n"
" -g, --game <path|ID> Specify <eboot.bin or elf path> or " " -g, --game <path|ID> Specify <eboot.bin or elf path> or "
@ -50,6 +51,7 @@ int main(int argc, char* argv[]) {
"Needs to be at the end of the line, and everything after \"--\" is a " "Needs to be at the end of the line, and everything after \"--\" is a "
"game argument.\n" "game argument.\n"
" -p, --patch <patch_file> Apply specified patch file\n" " -p, --patch <patch_file> Apply specified patch file\n"
" -i, --ignore-game-patch Disable automatic loading of game patch\n"
" -s, --show-gui Show the GUI\n" " -s, --show-gui Show the GUI\n"
" -f, --fullscreen <true|false> Specify window initial fullscreen " " -f, --fullscreen <true|false> Specify window initial fullscreen "
"state. Does not overwrite the config file.\n" "state. Does not overwrite the config file.\n"
@ -84,6 +86,8 @@ int main(int argc, char* argv[]) {
} }
}}, }},
{"--patch", [&](int& i) { arg_map["-p"](i); }}, {"--patch", [&](int& i) { arg_map["-p"](i); }},
{"-i", [&](int&) { Core::FileSys::MntPoints::ignore_game_patches = true; }},
{"--ignore-game-patch", [&](int& i) { arg_map["-i"](i); }},
{"-f", {"-f",
[&](int& i) { [&](int& i) {
if (++i >= argc) { if (++i >= argc) {

View File

@ -32,6 +32,9 @@ MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent), ui(new Ui::MainWi
ui->setupUi(this); ui->setupUi(this);
installEventFilter(this); installEventFilter(this);
setAttribute(Qt::WA_DeleteOnClose); setAttribute(Qt::WA_DeleteOnClose);
m_gui_settings = std::make_shared<gui_settings>();
ui->toggleLabelsAct->setChecked(
m_gui_settings->GetValue(gui::mw_showLabelsUnderIcons).toBool());
} }
MainWindow::~MainWindow() { MainWindow::~MainWindow() {
@ -139,7 +142,7 @@ void MainWindow::PauseGame() {
void MainWindow::toggleLabelsUnderIcons() { void MainWindow::toggleLabelsUnderIcons() {
bool showLabels = ui->toggleLabelsAct->isChecked(); bool showLabels = ui->toggleLabelsAct->isChecked();
Config::setShowLabelsUnderIcons(); m_gui_settings->SetValue(gui::mw_showLabelsUnderIcons, showLabels);
UpdateToolbarLabels(); UpdateToolbarLabels();
if (isGameRunning) { if (isGameRunning) {
UpdateToolbarButtons(); UpdateToolbarButtons();
@ -290,21 +293,21 @@ void MainWindow::CreateDockWindows() {
setCentralWidget(phCentralWidget); setCentralWidget(phCentralWidget);
m_dock_widget.reset(new QDockWidget(tr("Game List"), this)); m_dock_widget.reset(new QDockWidget(tr("Game List"), this));
m_game_list_frame.reset(new GameListFrame(m_game_info, m_compat_info, this)); m_game_list_frame.reset(new GameListFrame(m_gui_settings, m_game_info, m_compat_info, this));
m_game_list_frame->setObjectName("gamelist"); m_game_list_frame->setObjectName("gamelist");
m_game_grid_frame.reset(new GameGridFrame(m_game_info, m_compat_info, this)); m_game_grid_frame.reset(new GameGridFrame(m_gui_settings, m_game_info, m_compat_info, this));
m_game_grid_frame->setObjectName("gamegridlist"); m_game_grid_frame->setObjectName("gamegridlist");
m_elf_viewer.reset(new ElfViewer(this)); m_elf_viewer.reset(new ElfViewer(this));
m_elf_viewer->setObjectName("elflist"); m_elf_viewer->setObjectName("elflist");
int table_mode = Config::getTableMode(); int table_mode = m_gui_settings->GetValue(gui::gl_mode).toInt();
int slider_pos = 0; int slider_pos = 0;
if (table_mode == 0) { // List if (table_mode == 0) { // List
m_game_grid_frame->hide(); m_game_grid_frame->hide();
m_elf_viewer->hide(); m_elf_viewer->hide();
m_game_list_frame->show(); m_game_list_frame->show();
m_dock_widget->setWidget(m_game_list_frame.data()); m_dock_widget->setWidget(m_game_list_frame.data());
slider_pos = Config::getSliderPosition(); slider_pos = m_gui_settings->GetValue(gui::gl_slider_pos).toInt();
ui->sizeSlider->setSliderPosition(slider_pos); // set slider pos at start; ui->sizeSlider->setSliderPosition(slider_pos); // set slider pos at start;
isTableList = true; isTableList = true;
} else if (table_mode == 1) { // Grid } else if (table_mode == 1) { // Grid
@ -312,7 +315,7 @@ void MainWindow::CreateDockWindows() {
m_elf_viewer->hide(); m_elf_viewer->hide();
m_game_grid_frame->show(); m_game_grid_frame->show();
m_dock_widget->setWidget(m_game_grid_frame.data()); m_dock_widget->setWidget(m_game_grid_frame.data());
slider_pos = Config::getSliderPositionGrid(); slider_pos = m_gui_settings->GetValue(gui::gg_slider_pos).toInt();
ui->sizeSlider->setSliderPosition(slider_pos); // set slider pos at start; ui->sizeSlider->setSliderPosition(slider_pos); // set slider pos at start;
isTableList = false; isTableList = false;
} else { } else {
@ -356,11 +359,11 @@ void MainWindow::LoadGameLists() {
#ifdef ENABLE_UPDATER #ifdef ENABLE_UPDATER
void MainWindow::CheckUpdateMain(bool checkSave) { void MainWindow::CheckUpdateMain(bool checkSave) {
if (checkSave) { if (checkSave) {
if (!Config::autoUpdate()) { if (!m_gui_settings->GetValue(gui::gen_checkForUpdates).toBool()) {
return; return;
} }
} }
auto checkUpdate = new CheckUpdate(false); auto checkUpdate = new CheckUpdate(m_gui_settings, false);
checkUpdate->exec(); checkUpdate->exec();
} }
#endif #endif
@ -380,13 +383,13 @@ void MainWindow::CreateConnects() {
m_game_list_frame->icon_size = m_game_list_frame->icon_size =
48 + value; // 48 is the minimum icon size to use due to text disappearing. 48 + value; // 48 is the minimum icon size to use due to text disappearing.
m_game_list_frame->ResizeIcons(48 + value); m_game_list_frame->ResizeIcons(48 + value);
Config::setIconSize(48 + value); m_gui_settings->SetValue(gui::gl_icon_size, 48 + value);
Config::setSliderPosition(value); m_gui_settings->SetValue(gui::gl_slider_pos, value);
} else { } else {
m_game_grid_frame->icon_size = 69 + value; m_game_grid_frame->icon_size = 69 + value;
m_game_grid_frame->PopulateGameGrid(m_game_info->m_games, false); m_game_grid_frame->PopulateGameGrid(m_game_info->m_games, false);
Config::setIconSizeGrid(69 + value); m_gui_settings->SetValue(gui::gg_icon_size, 69 + value);
Config::setSliderPositionGrid(value); m_gui_settings->SetValue(gui::gg_slider_pos, value);
} }
}); });
@ -404,7 +407,7 @@ void MainWindow::CreateConnects() {
&MainWindow::StartGame); &MainWindow::StartGame);
connect(ui->configureAct, &QAction::triggered, this, [this]() { connect(ui->configureAct, &QAction::triggered, this, [this]() {
auto settingsDialog = new SettingsDialog(m_compat_info, this); auto settingsDialog = new SettingsDialog(m_gui_settings, m_compat_info, this);
connect(settingsDialog, &SettingsDialog::LanguageChanged, this, connect(settingsDialog, &SettingsDialog::LanguageChanged, this,
&MainWindow::OnLanguageChanged); &MainWindow::OnLanguageChanged);
@ -418,7 +421,8 @@ void MainWindow::CreateConnects() {
connect(settingsDialog, &SettingsDialog::BackgroundOpacityChanged, this, connect(settingsDialog, &SettingsDialog::BackgroundOpacityChanged, this,
[this](int opacity) { [this](int opacity) {
Config::setBackgroundImageOpacity(opacity); m_gui_settings->SetValue(gui::gl_backgroundImageOpacity,
std::clamp(opacity, 0, 100));
if (m_game_list_frame) { if (m_game_list_frame) {
QTableWidgetItem* current = m_game_list_frame->GetCurrentItem(); QTableWidgetItem* current = m_game_list_frame->GetCurrentItem();
if (current) { if (current) {
@ -437,7 +441,7 @@ void MainWindow::CreateConnects() {
}); });
connect(ui->settingsButton, &QPushButton::clicked, this, [this]() { connect(ui->settingsButton, &QPushButton::clicked, this, [this]() {
auto settingsDialog = new SettingsDialog(m_compat_info, this); auto settingsDialog = new SettingsDialog(m_gui_settings, m_compat_info, this);
connect(settingsDialog, &SettingsDialog::LanguageChanged, this, connect(settingsDialog, &SettingsDialog::LanguageChanged, this,
&MainWindow::OnLanguageChanged); &MainWindow::OnLanguageChanged);
@ -451,7 +455,8 @@ void MainWindow::CreateConnects() {
connect(settingsDialog, &SettingsDialog::BackgroundOpacityChanged, this, connect(settingsDialog, &SettingsDialog::BackgroundOpacityChanged, this,
[this](int opacity) { [this](int opacity) {
Config::setBackgroundImageOpacity(opacity); m_gui_settings->SetValue(gui::gl_backgroundImageOpacity,
std::clamp(opacity, 0, 100));
if (m_game_list_frame) { if (m_game_list_frame) {
QTableWidgetItem* current = m_game_list_frame->GetCurrentItem(); QTableWidgetItem* current = m_game_list_frame->GetCurrentItem();
if (current) { if (current) {
@ -481,7 +486,7 @@ void MainWindow::CreateConnects() {
#ifdef ENABLE_UPDATER #ifdef ENABLE_UPDATER
connect(ui->updaterAct, &QAction::triggered, this, [this]() { connect(ui->updaterAct, &QAction::triggered, this, [this]() {
auto checkUpdate = new CheckUpdate(true); auto checkUpdate = new CheckUpdate(m_gui_settings, true);
checkUpdate->exec(); checkUpdate->exec();
}); });
#endif #endif
@ -496,13 +501,13 @@ void MainWindow::CreateConnects() {
m_game_list_frame->icon_size = m_game_list_frame->icon_size =
36; // 36 is the minimum icon size to use due to text disappearing. 36; // 36 is the minimum icon size to use due to text disappearing.
ui->sizeSlider->setValue(0); // icone_size - 36 ui->sizeSlider->setValue(0); // icone_size - 36
Config::setIconSize(36); m_gui_settings->SetValue(gui::gl_icon_size, 36);
Config::setSliderPosition(0); m_gui_settings->SetValue(gui::gl_slider_pos, 0);
} else { } else {
m_game_grid_frame->icon_size = 69; m_game_grid_frame->icon_size = 69;
ui->sizeSlider->setValue(0); // icone_size - 36 ui->sizeSlider->setValue(0); // icone_size - 36
Config::setIconSizeGrid(69); m_gui_settings->SetValue(gui::gg_icon_size, 69);
Config::setSliderPositionGrid(0); m_gui_settings->SetValue(gui::gg_slider_pos, 9);
m_game_grid_frame->PopulateGameGrid(m_game_info->m_games, false); m_game_grid_frame->PopulateGameGrid(m_game_info->m_games, false);
} }
}); });
@ -511,13 +516,13 @@ void MainWindow::CreateConnects() {
if (isTableList) { if (isTableList) {
m_game_list_frame->icon_size = 64; m_game_list_frame->icon_size = 64;
ui->sizeSlider->setValue(28); ui->sizeSlider->setValue(28);
Config::setIconSize(64); m_gui_settings->SetValue(gui::gl_icon_size, 64);
Config::setSliderPosition(28); m_gui_settings->SetValue(gui::gl_slider_pos, 28);
} else { } else {
m_game_grid_frame->icon_size = 97; m_game_grid_frame->icon_size = 97;
ui->sizeSlider->setValue(28); ui->sizeSlider->setValue(28);
Config::setIconSizeGrid(97); m_gui_settings->SetValue(gui::gg_icon_size, 97);
Config::setSliderPositionGrid(28); m_gui_settings->SetValue(gui::gg_slider_pos, 28);
m_game_grid_frame->PopulateGameGrid(m_game_info->m_games, false); m_game_grid_frame->PopulateGameGrid(m_game_info->m_games, false);
} }
}); });
@ -526,13 +531,13 @@ void MainWindow::CreateConnects() {
if (isTableList) { if (isTableList) {
m_game_list_frame->icon_size = 128; m_game_list_frame->icon_size = 128;
ui->sizeSlider->setValue(92); ui->sizeSlider->setValue(92);
Config::setIconSize(128); m_gui_settings->SetValue(gui::gl_icon_size, 128);
Config::setSliderPosition(92); m_gui_settings->SetValue(gui::gl_slider_pos, 92);
} else { } else {
m_game_grid_frame->icon_size = 161; m_game_grid_frame->icon_size = 161;
ui->sizeSlider->setValue(92); ui->sizeSlider->setValue(92);
Config::setIconSizeGrid(161); m_gui_settings->SetValue(gui::gg_icon_size, 161);
Config::setSliderPositionGrid(92); m_gui_settings->SetValue(gui::gg_slider_pos, 92);
m_game_grid_frame->PopulateGameGrid(m_game_info->m_games, false); m_game_grid_frame->PopulateGameGrid(m_game_info->m_games, false);
} }
}); });
@ -541,13 +546,13 @@ void MainWindow::CreateConnects() {
if (isTableList) { if (isTableList) {
m_game_list_frame->icon_size = 256; m_game_list_frame->icon_size = 256;
ui->sizeSlider->setValue(220); ui->sizeSlider->setValue(220);
Config::setIconSize(256); m_gui_settings->SetValue(gui::gl_icon_size, 256);
Config::setSliderPosition(220); m_gui_settings->SetValue(gui::gl_slider_pos, 220);
} else { } else {
m_game_grid_frame->icon_size = 256; m_game_grid_frame->icon_size = 256;
ui->sizeSlider->setValue(220); ui->sizeSlider->setValue(220);
Config::setIconSizeGrid(256); m_gui_settings->SetValue(gui::gg_icon_size, 256);
Config::setSliderPositionGrid(220); m_gui_settings->SetValue(gui::gg_slider_pos, 220);
m_game_grid_frame->PopulateGameGrid(m_game_info->m_games, false); m_game_grid_frame->PopulateGameGrid(m_game_info->m_games, false);
} }
}); });
@ -563,8 +568,8 @@ void MainWindow::CreateConnects() {
m_game_list_frame->PopulateGameList(); m_game_list_frame->PopulateGameList();
} }
isTableList = true; isTableList = true;
Config::setTableMode(0); m_gui_settings->SetValue(gui::gl_mode, 0);
int slider_pos = Config::getSliderPosition(); int slider_pos = m_gui_settings->GetValue(gui::gl_slider_pos).toInt();
ui->sizeSlider->setEnabled(true); ui->sizeSlider->setEnabled(true);
ui->sizeSlider->setSliderPosition(slider_pos); ui->sizeSlider->setSliderPosition(slider_pos);
ui->mw_searchbar->setText(""); ui->mw_searchbar->setText("");
@ -582,8 +587,8 @@ void MainWindow::CreateConnects() {
m_game_grid_frame->PopulateGameGrid(m_game_info->m_games, false); m_game_grid_frame->PopulateGameGrid(m_game_info->m_games, false);
} }
isTableList = false; isTableList = false;
Config::setTableMode(1); m_gui_settings->SetValue(gui::gl_mode, 1);
int slider_pos_grid = Config::getSliderPositionGrid(); int slider_pos_grid = m_gui_settings->GetValue(gui::gg_slider_pos).toInt();
ui->sizeSlider->setEnabled(true); ui->sizeSlider->setEnabled(true);
ui->sizeSlider->setSliderPosition(slider_pos_grid); ui->sizeSlider->setSliderPosition(slider_pos_grid);
ui->mw_searchbar->setText(""); ui->mw_searchbar->setText("");
@ -598,7 +603,7 @@ void MainWindow::CreateConnects() {
m_elf_viewer->show(); m_elf_viewer->show();
isTableList = false; isTableList = false;
ui->sizeSlider->setDisabled(true); ui->sizeSlider->setDisabled(true);
Config::setTableMode(2); m_gui_settings->SetValue(gui::gl_mode, 2);
SetLastIconSizeBullet(); SetLastIconSizeBullet();
}); });
@ -840,7 +845,7 @@ void MainWindow::CreateConnects() {
void MainWindow::StartGame() { void MainWindow::StartGame() {
BackgroundMusicPlayer::getInstance().stopMusic(); BackgroundMusicPlayer::getInstance().stopMusic();
QString gamePath = ""; QString gamePath = "";
int table_mode = Config::getTableMode(); int table_mode = m_gui_settings->GetValue(gui::gl_mode).toInt();
if (table_mode == 0) { if (table_mode == 0) {
if (m_game_list_frame->currentItem()) { if (m_game_list_frame->currentItem()) {
int itemID = m_game_list_frame->currentItem()->row(); int itemID = m_game_list_frame->currentItem()->row();
@ -925,25 +930,25 @@ void MainWindow::RefreshGameTable() {
} }
void MainWindow::ConfigureGuiFromSettings() { void MainWindow::ConfigureGuiFromSettings() {
setGeometry(Config::getMainWindowGeometryX(), Config::getMainWindowGeometryY(), if (!restoreGeometry(m_gui_settings->GetValue(gui::mw_geometry).toByteArray())) {
Config::getMainWindowGeometryW(), Config::getMainWindowGeometryH()); // By default, set the window to 70% of the screen
resize(QGuiApplication::primaryScreen()->availableSize() * 0.7);
}
ui->showGameListAct->setChecked(true); ui->showGameListAct->setChecked(true);
if (Config::getTableMode() == 0) { int table_mode = m_gui_settings->GetValue(gui::gl_mode).toInt();
if (table_mode == 0) {
ui->setlistModeListAct->setChecked(true); ui->setlistModeListAct->setChecked(true);
} else if (Config::getTableMode() == 1) { } else if (table_mode == 1) {
ui->setlistModeGridAct->setChecked(true); ui->setlistModeGridAct->setChecked(true);
} else if (Config::getTableMode() == 2) { } else if (table_mode == 2) {
ui->setlistElfAct->setChecked(true); ui->setlistElfAct->setChecked(true);
} }
BackgroundMusicPlayer::getInstance().setVolume(Config::getBGMvolume()); BackgroundMusicPlayer::getInstance().setVolume(
m_gui_settings->GetValue(gui::gl_backgroundMusicVolume).toInt());
} }
void MainWindow::SaveWindowState() const { void MainWindow::SaveWindowState() {
Config::setMainWindowWidth(this->width()); m_gui_settings->SetValue(gui::mw_geometry, saveGeometry(), false);
Config::setMainWindowHeight(this->height());
Config::setMainWindowGeometry(this->geometry().x(), this->geometry().y(),
this->geometry().width(), this->geometry().height());
} }
void MainWindow::BootGame() { void MainWindow::BootGame() {
@ -1024,8 +1029,8 @@ void MainWindow::SetLastUsedTheme() {
void MainWindow::SetLastIconSizeBullet() { void MainWindow::SetLastIconSizeBullet() {
// set QAction bullet point if applicable // set QAction bullet point if applicable
int lastSize = Config::getIconSize(); int lastSize = m_gui_settings->GetValue(gui::gl_icon_size).toInt();
int lastSizeGrid = Config::getIconSizeGrid(); int lastSizeGrid = m_gui_settings->GetValue(gui::gg_icon_size).toInt();
if (isTableList) { if (isTableList) {
switch (lastSize) { switch (lastSize) {
case 36: case 36:
@ -1195,7 +1200,7 @@ bool MainWindow::eventFilter(QObject* obj, QEvent* event) {
if (event->type() == QEvent::KeyPress) { if (event->type() == QEvent::KeyPress) {
QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event); QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event);
if (keyEvent->key() == Qt::Key_Enter || keyEvent->key() == Qt::Key_Return) { if (keyEvent->key() == Qt::Key_Enter || keyEvent->key() == Qt::Key_Return) {
auto tblMode = Config::getTableMode(); auto tblMode = m_gui_settings->GetValue(gui::gl_mode).toInt();
if (tblMode != 2 && (tblMode != 1 || m_game_grid_frame->IsValidCellSelected())) { if (tblMode != 2 && (tblMode != 1 || m_game_grid_frame->IsValidCellSelected())) {
StartGame(); StartGame();
return true; return true;

View File

@ -20,6 +20,7 @@
#include "game_info.h" #include "game_info.h"
#include "game_list_frame.h" #include "game_list_frame.h"
#include "game_list_utils.h" #include "game_list_utils.h"
#include "gui_settings.h"
#include "main_window_themes.h" #include "main_window_themes.h"
#include "main_window_ui.h" #include "main_window_ui.h"
@ -41,7 +42,7 @@ public:
private Q_SLOTS: private Q_SLOTS:
void ConfigureGuiFromSettings(); void ConfigureGuiFromSettings();
void SaveWindowState() const; void SaveWindowState();
void SearchGameTable(const QString& text); void SearchGameTable(const QString& text);
void ShowGameList(); void ShowGameList();
void RefreshGameTable(); void RefreshGameTable();
@ -102,6 +103,7 @@ private:
std::make_shared<CompatibilityInfoClass>(); std::make_shared<CompatibilityInfoClass>();
QTranslator* translator; QTranslator* translator;
std::shared_ptr<gui_settings> m_gui_settings;
protected: protected:
bool eventFilter(QObject* obj, QEvent* event) override; bool eventFilter(QObject* obj, QEvent* event) override;

View File

@ -107,7 +107,6 @@ public:
toggleLabelsAct = new QAction(MainWindow); toggleLabelsAct = new QAction(MainWindow);
toggleLabelsAct->setObjectName("toggleLabelsAct"); toggleLabelsAct->setObjectName("toggleLabelsAct");
toggleLabelsAct->setCheckable(true); toggleLabelsAct->setCheckable(true);
toggleLabelsAct->setChecked(Config::getShowLabelsUnderIcons());
setIconSizeTinyAct = new QAction(MainWindow); setIconSizeTinyAct = new QAction(MainWindow);
setIconSizeTinyAct->setObjectName("setIconSizeTinyAct"); setIconSizeTinyAct->setObjectName("setIconSizeTinyAct");

77
src/qt_gui/settings.cpp Normal file
View File

@ -0,0 +1,77 @@
// SPDX-FileCopyrightText: Copyright 2025 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <common/path_util.h>
#include "settings.h"
settings::settings(QObject* parent) : QObject(parent), m_settings_dir(ComputeSettingsDir()) {}
settings::~settings() {
sync();
}
void settings::sync() {
if (m_settings) {
m_settings->sync();
}
}
QString settings::GetSettingsDir() const {
return m_settings_dir.absolutePath();
}
QString settings::ComputeSettingsDir() {
const auto config_dir = Common::FS::GetUserPath(Common::FS::PathType::UserDir);
return QString::fromStdString(config_dir.string() + "/");
}
void settings::RemoveValue(const QString& key, const QString& name, bool sync) const {
if (m_settings) {
m_settings->beginGroup(key);
m_settings->remove(name);
m_settings->endGroup();
if (sync) {
m_settings->sync();
}
}
}
void settings::RemoveValue(const gui_value& entry, bool sync) const {
RemoveValue(entry.key, entry.name, sync);
}
QVariant settings::GetValue(const QString& key, const QString& name, const QVariant& def) const {
return m_settings ? m_settings->value(key + "/" + name, def) : def;
}
QVariant settings::GetValue(const gui_value& entry) const {
return GetValue(entry.key, entry.name, entry.def);
}
void settings::SetValue(const gui_value& entry, const QVariant& value, bool sync) const {
SetValue(entry.key, entry.name, value, sync);
}
void settings::SetValue(const QString& key, const QVariant& value, bool sync) const {
if (m_settings) {
m_settings->setValue(key, value);
if (sync) {
m_settings->sync();
}
}
}
void settings::SetValue(const QString& key, const QString& name, const QVariant& value,
bool sync) const {
if (m_settings) {
m_settings->beginGroup(key);
m_settings->setValue(name, value);
m_settings->endGroup();
if (sync) {
m_settings->sync();
}
}
}

55
src/qt_gui/settings.h Normal file
View File

@ -0,0 +1,55 @@
// SPDX-FileCopyrightText: Copyright 2025 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <QDir>
#include <QSettings>
#include <QString>
#include <QVariant>
struct gui_value {
QString key;
QString name;
QVariant def;
gui_value() {}
gui_value(const QString& k, const QString& n, const QVariant& d) : key(k), name(n), def(d) {}
bool operator==(const gui_value& rhs) const noexcept {
return key == rhs.key && name == rhs.name && def == rhs.def;
}
};
class settings : public QObject {
Q_OBJECT
public:
explicit settings(QObject* parent = nullptr);
~settings();
void sync();
QString GetSettingsDir() const;
QVariant GetValue(const QString& key, const QString& name, const QVariant& def) const;
QVariant GetValue(const gui_value& entry) const;
public Q_SLOTS:
/** Remove entry */
void RemoveValue(const QString& key, const QString& name, bool sync = true) const;
void RemoveValue(const gui_value& entry, bool sync = true) const;
/** Write value to entry */
void SetValue(const gui_value& entry, const QVariant& value, bool sync = true) const;
void SetValue(const QString& key, const QVariant& value, bool sync = true) const;
void SetValue(const QString& key, const QString& name, const QVariant& value,
bool sync = true) const;
protected:
static QString ComputeSettingsDir();
std::unique_ptr<QSettings> m_settings;
QDir m_settings_dir;
};

View File

@ -71,9 +71,10 @@ int bgm_volume_backup;
static std::vector<QString> m_physical_devices; static std::vector<QString> m_physical_devices;
SettingsDialog::SettingsDialog(std::shared_ptr<CompatibilityInfoClass> m_compat_info, SettingsDialog::SettingsDialog(std::shared_ptr<gui_settings> gui_settings,
std::shared_ptr<CompatibilityInfoClass> m_compat_info,
QWidget* parent) QWidget* parent)
: QDialog(parent), ui(new Ui::SettingsDialog) { : QDialog(parent), ui(new Ui::SettingsDialog), m_gui_settings(std::move(gui_settings)) {
ui->setupUi(this); ui->setupUi(this);
ui->tabWidgetSettings->setUsesScrollButtons(false); ui->tabWidgetSettings->setUsesScrollButtons(false);
@ -147,6 +148,7 @@ SettingsDialog::SettingsDialog(std::shared_ptr<CompatibilityInfoClass> m_compat_
Config::save(config_dir / "config.toml"); Config::save(config_dir / "config.toml");
} else if (button == ui->buttonBox->button(QDialogButtonBox::RestoreDefaults)) { } else if (button == ui->buttonBox->button(QDialogButtonBox::RestoreDefaults)) {
Config::setDefaultValues(); Config::setDefaultValues();
setDefaultValues();
Config::save(config_dir / "config.toml"); Config::save(config_dir / "config.toml");
LoadValuesFromConfig(); LoadValuesFromConfig();
} else if (button == ui->buttonBox->button(QDialogButtonBox::Close)) { } else if (button == ui->buttonBox->button(QDialogButtonBox::Close)) {
@ -175,28 +177,34 @@ SettingsDialog::SettingsDialog(std::shared_ptr<CompatibilityInfoClass> m_compat_
{ {
#ifdef ENABLE_UPDATER #ifdef ENABLE_UPDATER
#if (QT_VERSION < QT_VERSION_CHECK(6, 7, 0)) #if (QT_VERSION < QT_VERSION_CHECK(6, 7, 0))
connect(ui->updateCheckBox, &QCheckBox::stateChanged, this, connect(ui->updateCheckBox, &QCheckBox::stateChanged, this, [this](int state) {
[](int state) { Config::setAutoUpdate(state == Qt::Checked); }); m_gui_settings->SetValue(gui::gen_checkForUpdates, state == Qt::Checked);
});
connect(ui->changelogCheckBox, &QCheckBox::stateChanged, this, connect(ui->changelogCheckBox, &QCheckBox::stateChanged, this, [this](int state) {
[](int state) { Config::setAlwaysShowChangelog(state == Qt::Checked); }); m_gui_settings->SetValue(gui::gen_showChangeLog, state == Qt::Checked);
});
#else #else
connect(ui->updateCheckBox, &QCheckBox::checkStateChanged, this, connect(ui->updateCheckBox, &QCheckBox::checkStateChanged, this,
[](Qt::CheckState state) { Config::setAutoUpdate(state == Qt::Checked); }); [this](Qt::CheckState state) {
m_gui_settings->SetValue(gui::gen_checkForUpdates, state == Qt::Checked);
});
connect(ui->changelogCheckBox, &QCheckBox::checkStateChanged, this, connect(ui->changelogCheckBox, &QCheckBox::checkStateChanged, this,
[](Qt::CheckState state) { Config::setAlwaysShowChangelog(state == Qt::Checked); }); [this](Qt::CheckState state) {
m_gui_settings->SetValue(gui::gen_showChangeLog, state == Qt::Checked);
});
#endif #endif
connect(ui->updateComboBox, &QComboBox::currentTextChanged, this, connect(ui->updateComboBox, &QComboBox::currentTextChanged, this,
[this](const QString& channel) { [this](const QString& channel) {
if (channelMap.contains(channel)) { if (channelMap.contains(channel)) {
Config::setUpdateChannel(channelMap.value(channel).toStdString()); m_gui_settings->SetValue(gui::gen_updateChannel, channelMap.value(channel));
} }
}); });
connect(ui->checkUpdateButton, &QPushButton::clicked, this, []() { connect(ui->checkUpdateButton, &QPushButton::clicked, this, [this]() {
auto checkUpdate = new CheckUpdate(true); auto checkUpdate = new CheckUpdate(m_gui_settings, true);
checkUpdate->exec(); checkUpdate->exec();
}); });
#else #else
@ -235,12 +243,12 @@ SettingsDialog::SettingsDialog(std::shared_ptr<CompatibilityInfoClass> m_compat_
[](const QString& hometab) { Config::setChooseHomeTab(hometab.toStdString()); }); [](const QString& hometab) { Config::setChooseHomeTab(hometab.toStdString()); });
#if (QT_VERSION < QT_VERSION_CHECK(6, 7, 0)) #if (QT_VERSION < QT_VERSION_CHECK(6, 7, 0))
connect(ui->showBackgroundImageCheckBox, &QCheckBox::stateChanged, this, [](int state) { connect(ui->showBackgroundImageCheckBox, &QCheckBox::stateChanged, this, [this](int state) {
#else #else
connect(ui->showBackgroundImageCheckBox, &QCheckBox::checkStateChanged, this, connect(ui->showBackgroundImageCheckBox, &QCheckBox::checkStateChanged, this,
[](Qt::CheckState state) { [this](Qt::CheckState state) {
#endif #endif
Config::setShowBackgroundImage(state == Qt::Checked); m_gui_settings->SetValue(gui::gl_showBackgroundImage, state == Qt::Checked);
}); });
} }
@ -448,7 +456,7 @@ void SettingsDialog::LoadValuesFromConfig() {
ui->dumpShadersCheckBox->setChecked(toml::find_or<bool>(data, "GPU", "dumpShaders", false)); ui->dumpShadersCheckBox->setChecked(toml::find_or<bool>(data, "GPU", "dumpShaders", false));
ui->nullGpuCheckBox->setChecked(toml::find_or<bool>(data, "GPU", "nullGpu", false)); ui->nullGpuCheckBox->setChecked(toml::find_or<bool>(data, "GPU", "nullGpu", false));
ui->enableHDRCheckBox->setChecked(toml::find_or<bool>(data, "GPU", "allowHDR", false)); ui->enableHDRCheckBox->setChecked(toml::find_or<bool>(data, "GPU", "allowHDR", false));
ui->playBGMCheckBox->setChecked(toml::find_or<bool>(data, "General", "playBGM", false)); ui->playBGMCheckBox->setChecked(m_gui_settings->GetValue(gui::gl_playBackgroundMusic).toBool());
ui->disableTrophycheckBox->setChecked( ui->disableTrophycheckBox->setChecked(
toml::find_or<bool>(data, "General", "isTrophyPopupDisabled", false)); toml::find_or<bool>(data, "General", "isTrophyPopupDisabled", false));
ui->popUpDurationSpinBox->setValue(Config::getTrophyNotificationDuration()); ui->popUpDurationSpinBox->setValue(Config::getTrophyNotificationDuration());
@ -460,7 +468,7 @@ void SettingsDialog::LoadValuesFromConfig() {
ui->radioButton_Top->setChecked(side == "top"); ui->radioButton_Top->setChecked(side == "top");
ui->radioButton_Bottom->setChecked(side == "bottom"); ui->radioButton_Bottom->setChecked(side == "bottom");
ui->BGMVolumeSlider->setValue(toml::find_or<int>(data, "General", "BGMvolume", 50)); ui->BGMVolumeSlider->setValue(m_gui_settings->GetValue(gui::gl_backgroundMusicVolume).toInt());
ui->discordRPCCheckbox->setChecked( ui->discordRPCCheckbox->setChecked(
toml::find_or<bool>(data, "General", "enableDiscordRPC", true)); toml::find_or<bool>(data, "General", "enableDiscordRPC", true));
QString translatedText_FullscreenMode = QString translatedText_FullscreenMode =
@ -501,11 +509,10 @@ void SettingsDialog::LoadValuesFromConfig() {
toml::find_or<bool>(data, "General", "checkCompatibilityOnStartup", false)); toml::find_or<bool>(data, "General", "checkCompatibilityOnStartup", false));
#ifdef ENABLE_UPDATER #ifdef ENABLE_UPDATER
ui->updateCheckBox->setChecked(toml::find_or<bool>(data, "General", "autoUpdate", false)); ui->updateCheckBox->setChecked(m_gui_settings->GetValue(gui::gen_checkForUpdates).toBool());
ui->changelogCheckBox->setChecked( ui->changelogCheckBox->setChecked(m_gui_settings->GetValue(gui::gen_showChangeLog).toBool());
toml::find_or<bool>(data, "General", "alwaysShowChangelog", false));
QString updateChannel = QString::fromStdString(Config::getUpdateChannel()); QString updateChannel = m_gui_settings->GetValue(gui::gen_updateChannel).toString();
ui->updateComboBox->setCurrentText( ui->updateComboBox->setCurrentText(
channelMap.key(updateChannel != "Release" && updateChannel != "Nightly" channelMap.key(updateChannel != "Release" && updateChannel != "Nightly"
? (Common::g_is_release ? "Release" : "Nightly") ? (Common::g_is_release ? "Release" : "Nightly")
@ -536,11 +543,14 @@ void SettingsDialog::LoadValuesFromConfig() {
ui->removeFolderButton->setEnabled(!ui->gameFoldersListWidget->selectedItems().isEmpty()); ui->removeFolderButton->setEnabled(!ui->gameFoldersListWidget->selectedItems().isEmpty());
ResetInstallFolders(); ResetInstallFolders();
ui->backgroundImageOpacitySlider->setValue(Config::getBackgroundImageOpacity()); ui->backgroundImageOpacitySlider->setValue(
ui->showBackgroundImageCheckBox->setChecked(Config::getShowBackgroundImage()); m_gui_settings->GetValue(gui::gl_backgroundImageOpacity).toInt());
ui->showBackgroundImageCheckBox->setChecked(
m_gui_settings->GetValue(gui::gl_showBackgroundImage).toBool());
backgroundImageOpacitySlider_backup = Config::getBackgroundImageOpacity(); backgroundImageOpacitySlider_backup =
bgm_volume_backup = Config::getBGMvolume(); m_gui_settings->GetValue(gui::gl_backgroundImageOpacity).toInt();
bgm_volume_backup = m_gui_settings->GetValue(gui::gl_backgroundMusicVolume).toInt();
} }
void SettingsDialog::InitializeEmulatorLanguages() { void SettingsDialog::InitializeEmulatorLanguages() {
@ -754,8 +764,7 @@ void SettingsDialog::UpdateSettings() {
} else if (ui->radioButton_Bottom->isChecked()) { } else if (ui->radioButton_Bottom->isChecked()) {
Config::setSideTrophy("bottom"); Config::setSideTrophy("bottom");
} }
m_gui_settings->SetValue(gui::gl_playBackgroundMusic, ui->playBGMCheckBox->isChecked());
Config::setPlayBGM(ui->playBGMCheckBox->isChecked());
Config::setAllowHDR(ui->enableHDRCheckBox->isChecked()); Config::setAllowHDR(ui->enableHDRCheckBox->isChecked());
Config::setLogType(logTypeMap.value(ui->logTypeComboBox->currentText()).toStdString()); Config::setLogType(logTypeMap.value(ui->logTypeComboBox->currentText()).toStdString());
Config::setLogFilter(ui->logFilterLineEdit->text().toStdString()); Config::setLogFilter(ui->logFilterLineEdit->text().toStdString());
@ -764,7 +773,7 @@ void SettingsDialog::UpdateSettings() {
Config::setCursorState(ui->hideCursorComboBox->currentIndex()); Config::setCursorState(ui->hideCursorComboBox->currentIndex());
Config::setCursorHideTimeout(ui->idleTimeoutSpinBox->value()); Config::setCursorHideTimeout(ui->idleTimeoutSpinBox->value());
Config::setGpuId(ui->graphicsAdapterBox->currentIndex() - 1); Config::setGpuId(ui->graphicsAdapterBox->currentIndex() - 1);
Config::setBGMvolume(ui->BGMVolumeSlider->value()); m_gui_settings->SetValue(gui::gl_backgroundMusicVolume, ui->BGMVolumeSlider->value());
Config::setLanguage(languageIndexes[ui->consoleLanguageComboBox->currentIndex()]); Config::setLanguage(languageIndexes[ui->consoleLanguageComboBox->currentIndex()]);
Config::setEnableDiscordRPC(ui->discordRPCCheckbox->isChecked()); Config::setEnableDiscordRPC(ui->discordRPCCheckbox->isChecked());
Config::setScreenWidth(ui->widthSpinBox->value()); Config::setScreenWidth(ui->widthSpinBox->value());
@ -784,16 +793,19 @@ void SettingsDialog::UpdateSettings() {
Config::setVkCrashDiagnosticEnabled(ui->crashDiagnosticsCheckBox->isChecked()); Config::setVkCrashDiagnosticEnabled(ui->crashDiagnosticsCheckBox->isChecked());
Config::setCollectShaderForDebug(ui->collectShaderCheckBox->isChecked()); Config::setCollectShaderForDebug(ui->collectShaderCheckBox->isChecked());
Config::setCopyGPUCmdBuffers(ui->copyGPUBuffersCheckBox->isChecked()); Config::setCopyGPUCmdBuffers(ui->copyGPUBuffersCheckBox->isChecked());
Config::setAutoUpdate(ui->updateCheckBox->isChecked()); m_gui_settings->SetValue(gui::gen_checkForUpdates, ui->updateCheckBox->isChecked());
Config::setAlwaysShowChangelog(ui->changelogCheckBox->isChecked()); m_gui_settings->SetValue(gui::gen_showChangeLog, ui->changelogCheckBox->isChecked());
Config::setUpdateChannel(channelMap.value(ui->updateComboBox->currentText()).toStdString()); m_gui_settings->SetValue(gui::gen_updateChannel,
channelMap.value(ui->updateComboBox->currentText()));
Config::setChooseHomeTab( Config::setChooseHomeTab(
chooseHomeTabMap.value(ui->chooseHomeTabComboBox->currentText()).toStdString()); chooseHomeTabMap.value(ui->chooseHomeTabComboBox->currentText()).toStdString());
Config::setCompatibilityEnabled(ui->enableCompatibilityCheckBox->isChecked()); Config::setCompatibilityEnabled(ui->enableCompatibilityCheckBox->isChecked());
Config::setCheckCompatibilityOnStartup(ui->checkCompatibilityOnStartupCheckBox->isChecked()); Config::setCheckCompatibilityOnStartup(ui->checkCompatibilityOnStartupCheckBox->isChecked());
Config::setBackgroundImageOpacity(ui->backgroundImageOpacitySlider->value()); m_gui_settings->SetValue(gui::gl_backgroundImageOpacity,
std::clamp(ui->backgroundImageOpacitySlider->value(), 0, 100));
emit BackgroundOpacityChanged(ui->backgroundImageOpacitySlider->value()); emit BackgroundOpacityChanged(ui->backgroundImageOpacitySlider->value());
Config::setShowBackgroundImage(ui->showBackgroundImageCheckBox->isChecked()); m_gui_settings->SetValue(gui::gl_showBackgroundImage,
ui->showBackgroundImageCheckBox->isChecked());
std::vector<Config::GameInstallDir> dirs_with_states; std::vector<Config::GameInstallDir> dirs_with_states;
for (int i = 0; i < ui->gameFoldersListWidget->count(); i++) { for (int i = 0; i < ui->gameFoldersListWidget->count(); i++) {
@ -862,3 +874,16 @@ void SettingsDialog::ResetInstallFolders() {
Config::setAllGameInstallDirs(settings_install_dirs_config); Config::setAllGameInstallDirs(settings_install_dirs_config);
} }
} }
void SettingsDialog::setDefaultValues() {
m_gui_settings->SetValue(gui::gl_showBackgroundImage, true);
m_gui_settings->SetValue(gui::gl_backgroundImageOpacity, 50);
m_gui_settings->SetValue(gui::gl_playBackgroundMusic, false);
m_gui_settings->SetValue(gui::gl_backgroundMusicVolume, 50);
m_gui_settings->SetValue(gui::gen_checkForUpdates, false);
m_gui_settings->SetValue(gui::gen_showChangeLog, false);
if (Common::g_is_release) {
m_gui_settings->SetValue(gui::gen_updateChannel, "Release");
} else {
m_gui_settings->SetValue(gui::gen_updateChannel, "Nightly");
}
}

View File

@ -11,6 +11,7 @@
#include "common/config.h" #include "common/config.h"
#include "common/path_util.h" #include "common/path_util.h"
#include "gui_settings.h"
#include "qt_gui/compatibility_info.h" #include "qt_gui/compatibility_info.h"
namespace Ui { namespace Ui {
@ -20,7 +21,8 @@ class SettingsDialog;
class SettingsDialog : public QDialog { class SettingsDialog : public QDialog {
Q_OBJECT Q_OBJECT
public: public:
explicit SettingsDialog(std::shared_ptr<CompatibilityInfoClass> m_compat_info, explicit SettingsDialog(std::shared_ptr<gui_settings> gui_settings,
std::shared_ptr<CompatibilityInfoClass> m_compat_info,
QWidget* parent = nullptr); QWidget* parent = nullptr);
~SettingsDialog(); ~SettingsDialog();
@ -42,6 +44,7 @@ private:
void OnLanguageChanged(int index); void OnLanguageChanged(int index);
void OnCursorStateChanged(s16 index); void OnCursorStateChanged(s16 index);
void closeEvent(QCloseEvent* event) override; void closeEvent(QCloseEvent* event) override;
void setDefaultValues();
std::unique_ptr<Ui::SettingsDialog> ui; std::unique_ptr<Ui::SettingsDialog> ui;
@ -52,4 +55,5 @@ private:
int initialHeight; int initialHeight;
bool is_saving = false; bool is_saving = false;
std::shared_ptr<gui_settings> m_gui_settings;
}; };

View File

@ -2049,7 +2049,7 @@ Nightly: نُسخ تحتوي على أحدث الميزات، لكنها أقل
</message> </message>
<message> <message>
<source> * Unsupported Vulkan Version</source> <source> * Unsupported Vulkan Version</source>
<translation type="unfinished"> * Unsupported Vulkan Version</translation> <translation>نسخ Vulkan غير مدعومة</translation>
</message> </message>
</context> </context>
<context> <context>

File diff suppressed because it is too large Load Diff

View File

@ -26,7 +26,7 @@
</message> </message>
<message> <message>
<source>Cheats/Patches are experimental.\nUse with caution.\n\nDownload cheats individually by selecting the repository and clicking the download button.\nIn the Patches tab, you can download all patches at once, choose which ones you want to use, and save your selection.\n\nSince we do not develop the Cheats/Patches,\nplease report issues to the cheat author.\n\nCreated a new cheat? Visit:\n</source> <source>Cheats/Patches are experimental.\nUse with caution.\n\nDownload cheats individually by selecting the repository and clicking the download button.\nIn the Patches tab, you can download all patches at once, choose which ones you want to use, and save your selection.\n\nSince we do not develop the Cheats/Patches,\nplease report issues to the cheat author.\n\nCreated a new cheat? Visit:\n</source>
<translation type="unfinished">Cheats/Patches are experimental.\nUse with caution.\n\nDownload cheats individually by selecting the repository and clicking the download button.\nIn the Patches tab, you can download all patches at once, choose which ones you want to use, and save your selection.\n\nSince we do not develop the Cheats/Patches,\nplease report issues to the cheat author.\n\nCreated a new cheat? Visit:\n</translation> <translation>تقلبها/پچها آزمایشی هستند.\n با احتیاط استفاده کنید.\n\n با انتخاب مخزن و کلیک روی دکمه دانلود، تقلبها را بهصورت جداگانه دانلود کنید.\n در تب پچها، میتوانید همه پچها را بهطور همزمان دانلود کنید، انتخاب کنید که میخواهید از کدام استفاده کنید و انتخاب خود را ذخیره کنید.\n\n از آنجایی که ما تقلبها/پچها را توسعه نمیدهیم،\n لطفاً مشکلات را به نویسنده تقلب گزارش دهید.\n\n تقلب جدیدی ایجاد کردهاید؟ به این صفحه مراجعه کنید: \n</translation>
</message> </message>
<message> <message>
<source>No Image Available</source> <source>No Image Available</source>
@ -214,7 +214,7 @@
</message> </message>
<message> <message>
<source>XML ERROR:</source> <source>XML ERROR:</source>
<translation type="unfinished">XML ERROR:</translation> <translation>XML خطای :</translation>
</message> </message>
<message> <message>
<source>Failed to open files.json for writing</source> <source>Failed to open files.json for writing</source>
@ -407,43 +407,43 @@
<name>ControlSettings</name> <name>ControlSettings</name>
<message> <message>
<source>Configure Controls</source> <source>Configure Controls</source>
<translation type="unfinished">Configure Controls</translation> <translation>پیکربندی دسته ها</translation>
</message> </message>
<message> <message>
<source>D-Pad</source> <source>D-Pad</source>
<translation type="unfinished">D-Pad</translation> <translation>D-Pad</translation>
</message> </message>
<message> <message>
<source>Up</source> <source>Up</source>
<translation type="unfinished">Up</translation> <translation>بالا</translation>
</message> </message>
<message> <message>
<source>Left</source> <source>Left</source>
<translation type="unfinished">Left</translation> <translation>چپ</translation>
</message> </message>
<message> <message>
<source>Right</source> <source>Right</source>
<translation type="unfinished">Right</translation> <translation>راست</translation>
</message> </message>
<message> <message>
<source>Down</source> <source>Down</source>
<translation type="unfinished">Down</translation> <translation>پایین</translation>
</message> </message>
<message> <message>
<source>Left Stick Deadzone (def:2 max:127)</source> <source>Left Stick Deadzone (def:2 max:127)</source>
<translation type="unfinished">Left Stick Deadzone (def:2 max:127)</translation> <translation>منطقهی حساس به حرکت چپ (def:2 max:127)</translation>
</message> </message>
<message> <message>
<source>Left Deadzone</source> <source>Left Deadzone</source>
<translation type="unfinished">Left Deadzone</translation> <translation>منطقه مرده چپ</translation>
</message> </message>
<message> <message>
<source>Left Stick</source> <source>Left Stick</source>
<translation type="unfinished">Left Stick</translation> <translation>جواستیک چپ</translation>
</message> </message>
<message> <message>
<source>Config Selection</source> <source>Config Selection</source>
<translation type="unfinished">Config Selection</translation> <translation>انتخاب پیکربندی</translation>
</message> </message>
<message> <message>
<source>Common Config</source> <source>Common Config</source>
@ -451,7 +451,7 @@
</message> </message>
<message> <message>
<source>Use per-game configs</source> <source>Use per-game configs</source>
<translation type="unfinished">Use per-game configs</translation> <translation>از پیکربندیهای مخصوص هر بازی استفاده کنید</translation>
</message> </message>
<message> <message>
<source>L1 / LB</source> <source>L1 / LB</source>
@ -483,7 +483,7 @@
</message> </message>
<message> <message>
<source>R3</source> <source>R3</source>
<translation type="unfinished">R3</translation> <translation>R3</translation>
</message> </message>
<message> <message>
<source>Face Buttons</source> <source>Face Buttons</source>
@ -491,7 +491,7 @@
</message> </message>
<message> <message>
<source>Triangle / Y</source> <source>Triangle / Y</source>
<translation type="unfinished">Triangle / Y</translation> <translation>مثلث / Y</translation>
</message> </message>
<message> <message>
<source>Square / X</source> <source>Square / X</source>
@ -531,7 +531,7 @@
</message> </message>
<message> <message>
<source>B:</source> <source>B:</source>
<translation type="unfinished">B:</translation> <translation>B:</translation>
</message> </message>
<message> <message>
<source>Override Lightbar Color</source> <source>Override Lightbar Color</source>
@ -543,7 +543,7 @@
</message> </message>
<message> <message>
<source>Unable to Save</source> <source>Unable to Save</source>
<translation type="unfinished">Unable to Save</translation> <translation>ذخیره امکان پذیر نیست</translation>
</message> </message>
<message> <message>
<source>Cannot bind axis values more than once</source> <source>Cannot bind axis values more than once</source>
@ -570,7 +570,7 @@
<name>EditorDialog</name> <name>EditorDialog</name>
<message> <message>
<source>Edit Keyboard + Mouse and Controller input bindings</source> <source>Edit Keyboard + Mouse and Controller input bindings</source>
<translation type="unfinished">Edit Keyboard + Mouse and Controller input bindings</translation> <translation>تغییر دکمه های کیبرد + ماوس و دسته</translation>
</message> </message>
<message> <message>
<source>Use Per-Game configs</source> <source>Use Per-Game configs</source>
@ -582,7 +582,7 @@
</message> </message>
<message> <message>
<source>Could not open the file for reading</source> <source>Could not open the file for reading</source>
<translation type="unfinished">Could not open the file for reading</translation> <translation>نمی تواند فایل را برای خواندن باز کند</translation>
</message> </message>
<message> <message>
<source>Could not open the file for writing</source> <source>Could not open the file for writing</source>
@ -602,7 +602,7 @@
</message> </message>
<message> <message>
<source>Do you want to reset your custom default config to the original default config?</source> <source>Do you want to reset your custom default config to the original default config?</source>
<translation type="unfinished">Do you want to reset your custom default config to the original default config?</translation> <translation>آیا میخواهید پیکربندی سفارشی خود را به پیکربندی پیشفرض اصلی بازگردانید ؟</translation>
</message> </message>
<message> <message>
<source>Do you want to reset this config to your custom default config?</source> <source>Do you want to reset this config to your custom default config?</source>
@ -860,7 +860,7 @@
</message> </message>
<message> <message>
<source>View report</source> <source>View report</source>
<translation type="unfinished">View report</translation> <translation>مشاهده گزارش</translation>
</message> </message>
<message> <message>
<source>Submit a report</source> <source>Submit a report</source>
@ -916,11 +916,11 @@
</message> </message>
<message> <message>
<source>Delete Save Data</source> <source>Delete Save Data</source>
<translation type="unfinished">Delete Save Data</translation> <translation>پاک کردن داده های ذخیره شده</translation>
</message> </message>
<message> <message>
<source>This game has no update folder to open!</source> <source>This game has no update folder to open!</source>
<translation type="unfinished">This game has no update folder to open!</translation> <translation>این بازی هیچ پوشهی بهروزرسانی برای باز کردن ندارد!</translation>
</message> </message>
<message> <message>
<source>No log file found for this game!</source> <source>No log file found for this game!</source>
@ -948,7 +948,7 @@
</message> </message>
<message> <message>
<source>SFO Viewer for </source> <source>SFO Viewer for </source>
<translation type="unfinished">SFO Viewer for </translation> <translation>SFO مشاهده </translation>
</message> </message>
</context> </context>
<context> <context>
@ -986,7 +986,7 @@
</message> </message>
<message> <message>
<source>Up</source> <source>Up</source>
<translation type="unfinished">Up</translation> <translation/>
</message> </message>
<message> <message>
<source>unmapped</source> <source>unmapped</source>
@ -1058,7 +1058,7 @@
</message> </message>
<message> <message>
<source>Touchpad Click</source> <source>Touchpad Click</source>
<translation type="unfinished">Touchpad Click</translation> <translation>کلیک روی تاچپد</translation>
</message> </message>
<message> <message>
<source>Mouse to Joystick</source> <source>Mouse to Joystick</source>
@ -1078,7 +1078,7 @@
</message> </message>
<message> <message>
<source>Mouse Movement Parameters</source> <source>Mouse Movement Parameters</source>
<translation type="unfinished">Mouse Movement Parameters</translation> <translation/>
</message> </message>
<message> <message>
<source>note: click Help Button/Special Keybindings for more information</source> <source>note: click Help Button/Special Keybindings for more information</source>
@ -1102,7 +1102,7 @@
</message> </message>
<message> <message>
<source>Cross</source> <source>Cross</source>
<translation type="unfinished">Cross</translation> <translation>ضربدر</translation>
</message> </message>
<message> <message>
<source>Right Analog Halfmode</source> <source>Right Analog Halfmode</source>
@ -1122,7 +1122,7 @@
</message> </message>
<message> <message>
<source>Copy from Common Config</source> <source>Copy from Common Config</source>
<translation type="unfinished">Copy from Common Config</translation> <translation>کپی از پیکربندی مشترک</translation>
</message> </message>
<message> <message>
<source>Deadzone Offset (def 0.50):</source> <source>Deadzone Offset (def 0.50):</source>
@ -1130,23 +1130,23 @@
</message> </message>
<message> <message>
<source>Speed Multiplier (def 1.0):</source> <source>Speed Multiplier (def 1.0):</source>
<translation type="unfinished">Speed Multiplier (def 1.0):</translation> <translation>ضریب سرعت (def 1.0):</translation>
</message> </message>
<message> <message>
<source>Common Config Selected</source> <source>Common Config Selected</source>
<translation type="unfinished">Common Config Selected</translation> <translation>پیکربندی مشترک انتخاب شده</translation>
</message> </message>
<message> <message>
<source>This button copies mappings from the Common Config to the currently selected profile, and cannot be used when the currently selected profile is the Common Config.</source> <source>This button copies mappings from the Common Config to the currently selected profile, and cannot be used when the currently selected profile is the Common Config.</source>
<translation type="unfinished">This button copies mappings from the Common Config to the currently selected profile, and cannot be used when the currently selected profile is the Common Config.</translation> <translation>این دکمه نگاشتها را از پیکربندی مشترک به پروفایل انتخابشدهی فعلی کپی میکند و وقتی پروفایل انتخابشدهی فعلی پیکربندی مشترک باشد، نمیتوان از آن استفاده کرد.</translation>
</message> </message>
<message> <message>
<source>Copy values from Common Config</source> <source>Copy values from Common Config</source>
<translation type="unfinished">Copy values from Common Config</translation> <translation>کپی کردن مقادیر از پیکربندی مشترک</translation>
</message> </message>
<message> <message>
<source>Do you want to overwrite existing mappings with the mappings from the Common Config?</source> <source>Do you want to overwrite existing mappings with the mappings from the Common Config?</source>
<translation type="unfinished">Do you want to overwrite existing mappings with the mappings from the Common Config?</translation> <translation>آیا میخواهید نگاشتهای موجود را با نگاشتهای پیکربندی مشترک جایگزین کنید؟</translation>
</message> </message>
<message> <message>
<source>Unable to Save</source> <source>Unable to Save</source>
@ -1170,7 +1170,7 @@
</message> </message>
<message> <message>
<source>Save</source> <source>Save</source>
<translation type="unfinished">Save</translation> <translation>ذخیرهسازی</translation>
</message> </message>
<message> <message>
<source>Apply</source> <source>Apply</source>
@ -1213,7 +1213,7 @@
</message> </message>
<message> <message>
<source>Open shadPS4 Folder</source> <source>Open shadPS4 Folder</source>
<translation type="unfinished">Open shadPS4 Folder</translation> <translation>پوشه shadPS4 را باز کنید</translation>
</message> </message>
<message> <message>
<source>Exit</source> <source>Exit</source>
@ -1624,7 +1624,7 @@
</message> </message>
<message> <message>
<source>Collect Shaders</source> <source>Collect Shaders</source>
<translation type="unfinished">Collect Shaders</translation> <translation>جمع آوری شیدرها</translation>
</message> </message>
<message> <message>
<source>Copy GPU Buffers</source> <source>Copy GPU Buffers</source>
@ -1664,7 +1664,7 @@
</message> </message>
<message> <message>
<source>Title Music</source> <source>Title Music</source>
<translation type="unfinished">Title Music</translation> <translation/>
</message> </message>
<message> <message>
<source>Disable Trophy Notification</source> <source>Disable Trophy Notification</source>
@ -1728,7 +1728,7 @@
</message> </message>
<message> <message>
<source>Console Language:\nSets the language that the PS4 game uses.\nIt&apos;s recommended to set this to a language the game supports, which will vary by region.</source> <source>Console Language:\nSets the language that the PS4 game uses.\nIt&apos;s recommended to set this to a language the game supports, which will vary by region.</source>
<translation type="unfinished">Console Language:\nSets the language that the PS4 game uses.\nIt&apos;s recommended to set this to a language the game supports, which will vary by region.</translation> <translation>زبان کنسول:\nزبانی را که بازی PS4 استفاده میکند تنظیم میکند.\nتوصیه میشود این را روی زبانی که بازی پشتیبانی میکند تنظیم کنید، که بسته به منطقه متفاوت خواهد بود.</translation>
</message> </message>
<message> <message>
<source>Emulator Language:\nSets the language of the emulator&apos;s user interface.</source> <source>Emulator Language:\nSets the language of the emulator&apos;s user interface.</source>
@ -1748,7 +1748,7 @@
</message> </message>
<message> <message>
<source>Trophy Key:\nKey used to decrypt trophies. Must be obtained from your jailbroken console.\nMust contain only hex characters.</source> <source>Trophy Key:\nKey used to decrypt trophies. Must be obtained from your jailbroken console.\nMust contain only hex characters.</source>
<translation type="unfinished">Trophy Key:\nKey used to decrypt trophies. Must be obtained from your jailbroken console.\nMust contain only hex characters.</translation> <translation>کلید تروفی:\و کلیدی که برای رمزگشایی تروفیها استفاده میشود. باید از کنسول جیلبریک شده شما دریافت شود.\باید فقط شامل کاراکترهای هگز باشد.</translation>
</message> </message>
<message> <message>
<source>Log Type:\nSets whether to synchronize the output of the log window for performance. May have adverse effects on emulation.</source> <source>Log Type:\nSets whether to synchronize the output of the log window for performance. May have adverse effects on emulation.</source>
@ -1756,7 +1756,7 @@
</message> </message>
<message> <message>
<source>Log Filter:\nFilters the log to only print specific information.\nExamples: &quot;Core:Trace&quot; &quot;Lib.Pad:Debug Common.Filesystem:Error&quot; &quot;*:Critical&quot;\nLevels: Trace, Debug, Info, Warning, Error, Critical - in this order, a specific level silences all levels preceding it in the list and logs every level after it.</source> <source>Log Filter:\nFilters the log to only print specific information.\nExamples: &quot;Core:Trace&quot; &quot;Lib.Pad:Debug Common.Filesystem:Error&quot; &quot;*:Critical&quot;\nLevels: Trace, Debug, Info, Warning, Error, Critical - in this order, a specific level silences all levels preceding it in the list and logs every level after it.</source>
<translation type="unfinished">Log Filter:\nFilters the log to only print specific information.\nExamples: &quot;Core:Trace&quot; &quot;Lib.Pad:Debug Common.Filesystem:Error&quot; &quot;*:Critical&quot;\nLevels: Trace, Debug, Info, Warning, Error, Critical - in this order, a specific level silences all levels preceding it in the list and logs every level after it.</translation> <translation>فیلتر گزارش:\nگزارش را فیلتر میکند تا فقط اطلاعات خاصی چاپ شود.\nمثالها: &quot;هسته:ردیابی&quot; &quot;Lib.Pad:اشکال‌زدایی Common.Filesystem:خطا&quot; &quot;*:بحرانی&quot;\nسطوح: ردیابی، اشکالزدایی، اطلاعات، هشدار، خطا، بحرانی - به این ترتیب، یک سطح خاص تمام سطوح قبل از خود را در لیست بیصدا میکند و هر سطح بعد از خود را ثبت میکند.</translation>
</message> </message>
<message> <message>
<source>Update:\nRelease: Official versions released every month that may be very outdated, but are more reliable and tested.\nNightly: Development versions that have all the latest features and fixes, but may contain bugs and are less stable.</source> <source>Update:\nRelease: Official versions released every month that may be very outdated, but are more reliable and tested.\nNightly: Development versions that have all the latest features and fixes, but may contain bugs and are less stable.</source>
@ -1764,7 +1764,7 @@
</message> </message>
<message> <message>
<source>Background Image:\nControl the opacity of the game background image.</source> <source>Background Image:\nControl the opacity of the game background image.</source>
<translation type="unfinished">Background Image:\nControl the opacity of the game background image.</translation> <translation>تصویر پس‌زمینه: میزان شفافیت تصویر پسزمینه بازی را کنترل کنید.</translation>
</message> </message>
<message> <message>
<source>Play Title Music:\nIf a game supports it, enable playing special music when selecting the game in the GUI.</source> <source>Play Title Music:\nIf a game supports it, enable playing special music when selecting the game in the GUI.</source>
@ -1844,11 +1844,11 @@
</message> </message>
<message> <message>
<source>Enable Null GPU:\nFor the sake of technical debugging, disables game rendering as if there were no graphics card.</source> <source>Enable Null GPU:\nFor the sake of technical debugging, disables game rendering as if there were no graphics card.</source>
<translation type="unfinished">Enable Null GPU:\nFor the sake of technical debugging, disables game rendering as if there were no graphics card.</translation> <translation>فعال کردن پردازنده گرافیکی خالی:\برای رفع اشکال فنی، رندر بازی را طوری غیرفعال کنید که انگار هیچ کارت گرافیکی وجود ندارد.</translation>
</message> </message>
<message> <message>
<source>Enable HDR:\nEnables HDR in games that support it.\nYour monitor must have support for the BT2020 PQ color space and the RGB10A2 swapchain format.</source> <source>Enable HDR:\nEnables HDR in games that support it.\nYour monitor must have support for the BT2020 PQ color space and the RGB10A2 swapchain format.</source>
<translation type="unfinished">Enable HDR:\nEnables HDR in games that support it.\nYour monitor must have support for the BT2020 PQ color space and the RGB10A2 swapchain format.</translation> <translation>فعال کردن HDR و :\n این گزینه HDR را در بازیهایی که از آن پشتیبانی میکنند فعال میکند.\n مانیتور شما باید از فضای رنگی BT2020 PQ و فرمت swapchain RGB10A2 پشتیبانی کند.</translation>
</message> </message>
<message> <message>
<source>Game Folders:\nThe list of folders to check for installed games.</source> <source>Game Folders:\nThe list of folders to check for installed games.</source>
@ -1868,11 +1868,11 @@
</message> </message>
<message> <message>
<source>Enable Vulkan Validation Layers:\nEnables a system that validates the state of the Vulkan renderer and logs information about its internal state.\nThis will reduce performance and likely change the behavior of emulation.</source> <source>Enable Vulkan Validation Layers:\nEnables a system that validates the state of the Vulkan renderer and logs information about its internal state.\nThis will reduce performance and likely change the behavior of emulation.</source>
<translation type="unfinished">Enable Vulkan Validation Layers:\nEnables a system that validates the state of the Vulkan renderer and logs information about its internal state.\nThis will reduce performance and likely change the behavior of emulation.</translation> <translation>فعال کردن لایههای اعتبارسنجی Vulkan: \nسیستمی را فعال میکند که وضعیت رندرکننده Vulkan را اعتبارسنجی کرده و اطلاعات مربوط به وضعیت داخلی آن را ثبت میکند.\n این کار باعث کاهش عملکرد و احتمالاً تغییر رفتار شبیهسازی میشود.</translation>
</message> </message>
<message> <message>
<source>Enable Vulkan Synchronization Validation:\nEnables a system that validates the timing of Vulkan rendering tasks.\nThis will reduce performance and likely change the behavior of emulation.</source> <source>Enable Vulkan Synchronization Validation:\nEnables a system that validates the timing of Vulkan rendering tasks.\nThis will reduce performance and likely change the behavior of emulation.</source>
<translation type="unfinished">Enable Vulkan Synchronization Validation:\nEnables a system that validates the timing of Vulkan rendering tasks.\nThis will reduce performance and likely change the behavior of emulation.</translation> <translation>فعال کردن اعتبارسنجی همگامسازی Vulkan: \nسیستمی را فعال میکند که زمانبندی وظایف رندر Vulkan را اعتبارسنجی میکند.\n این کار باعث کاهش عملکرد و احتمالاً تغییر رفتار شبیهسازی میشود.</translation>
</message> </message>
<message> <message>
<source>Enable RenderDoc Debugging:\nIf enabled, the emulator will provide compatibility with Renderdoc to allow capture and analysis of the currently rendered frame.</source> <source>Enable RenderDoc Debugging:\nIf enabled, the emulator will provide compatibility with Renderdoc to allow capture and analysis of the currently rendered frame.</source>
@ -1880,7 +1880,7 @@
</message> </message>
<message> <message>
<source>Collect Shaders:\nYou need this enabled to edit shaders with the debug menu (Ctrl + F10).</source> <source>Collect Shaders:\nYou need this enabled to edit shaders with the debug menu (Ctrl + F10).</source>
<translation type="unfinished">Collect Shaders:\nYou need this enabled to edit shaders with the debug menu (Ctrl + F10).</translation> <translation>جمعآوری سایهزنها:\n برای ویرایش سایهزنها با منوی اشکالزدایی (Ctrl + F10) باید این گزینه فعال باشد.</translation>
</message> </message>
<message> <message>
<source>Crash Diagnostics:\nCreates a .yaml file with info about the Vulkan state at the time of crashing.\nUseful for debugging &apos;Device lost&apos; errors. If you have this enabled, you should enable Host AND Guest Debug Markers.\nDoes not work on Intel GPUs.\nYou need Vulkan Validation Layers enabled and the Vulkan SDK for this to work.</source> <source>Crash Diagnostics:\nCreates a .yaml file with info about the Vulkan state at the time of crashing.\nUseful for debugging &apos;Device lost&apos; errors. If you have this enabled, you should enable Host AND Guest Debug Markers.\nDoes not work on Intel GPUs.\nYou need Vulkan Validation Layers enabled and the Vulkan SDK for this to work.</source>
@ -1912,7 +1912,7 @@
</message> </message>
<message> <message>
<source>Nightly</source> <source>Nightly</source>
<translation type="unfinished">Nightly</translation> <translation>اخرین نسخه شبانه</translation>
</message> </message>
<message> <message>
<source>Set the volume of the background music.</source> <source>Set the volume of the background music.</source>
@ -1936,7 +1936,7 @@
</message> </message>
<message> <message>
<source>sync</source> <source>sync</source>
<translation type="unfinished">sync</translation> <translation>همزمان</translation>
</message> </message>
<message> <message>
<source>Auto Select</source> <source>Auto Select</source>
@ -2000,7 +2000,7 @@
</message> </message>
<message> <message>
<source>Right</source> <source>Right</source>
<translation type="unfinished">Right</translation> <translation>راست</translation>
</message> </message>
<message> <message>
<source>Top</source> <source>Top</source>
@ -2032,7 +2032,7 @@
</message> </message>
<message> <message>
<source>%1 already exists</source> <source>%1 already exists</source>
<translation type="unfinished">%1 already exists</translation> <translation>%1 از قبل وجود دارد</translation>
</message> </message>
<message> <message>
<source>Portable user folder created</source> <source>Portable user folder created</source>
@ -2044,7 +2044,7 @@
</message> </message>
<message> <message>
<source>Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\ntrophy.wav OR trophy.mp3, bronze.png, gold.png, platinum.png, silver.png\nNote: The sound will only work in QT versions.</source> <source>Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\ntrophy.wav OR trophy.mp3, bronze.png, gold.png, platinum.png, silver.png\nNote: The sound will only work in QT versions.</source>
<translation type="unfinished">Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\ntrophy.wav OR trophy.mp3, bronze.png, gold.png, platinum.png, silver.png\nNote: The sound will only work in QT versions.</translation> <translation>پوشه تصاویر/صداهای تروفی سفارشی را باز کنید:\n شما میتوانید تصاویر و صدای سفارشی به تروفیها اضافه کنید.\n فایلها را با نامهای زیر به custom_trophy اضافه کنید:\ntrophy.wav یا trophy.mp3، bronze.png، gold.png، platinum.png، silver.png \nتوجه: صدا فقط در نسخههای QT کار میکند.</translation>
</message> </message>
<message> <message>
<source> * Unsupported Vulkan Version</source> <source> * Unsupported Vulkan Version</source>
@ -2075,7 +2075,7 @@
</message> </message>
<message> <message>
<source>Show Hidden Trophies</source> <source>Show Hidden Trophies</source>
<translation type="unfinished">Show Hidden Trophies</translation> <translation>نمایش جوایز مخفی</translation>
</message> </message>
</context> </context>
</TS> </TS>

File diff suppressed because it is too large Load Diff

View File

@ -303,7 +303,8 @@ void SetupCapabilities(const Info& info, const Profile& profile, EmitContext& ct
ctx.AddCapability(spv::Capability::PhysicalStorageBufferAddresses); ctx.AddCapability(spv::Capability::PhysicalStorageBufferAddresses);
ctx.AddExtension("SPV_KHR_physical_storage_buffer"); ctx.AddExtension("SPV_KHR_physical_storage_buffer");
} }
if (info.uses_shared && profile.supports_workgroup_explicit_memory_layout) { const auto shared_type_count = std::popcount(static_cast<u32>(info.shared_types));
if (shared_type_count > 1 && profile.supports_workgroup_explicit_memory_layout) {
ctx.AddExtension("SPV_KHR_workgroup_memory_explicit_layout"); ctx.AddExtension("SPV_KHR_workgroup_memory_explicit_layout");
ctx.AddCapability(spv::Capability::WorkgroupMemoryExplicitLayoutKHR); ctx.AddCapability(spv::Capability::WorkgroupMemoryExplicitLayoutKHR);
ctx.AddCapability(spv::Capability::WorkgroupMemoryExplicitLayout16BitAccessKHR); ctx.AddCapability(spv::Capability::WorkgroupMemoryExplicitLayout16BitAccessKHR);

View File

@ -27,6 +27,19 @@ Id SharedAtomicU32(EmitContext& ctx, Id offset, Id value,
}); });
} }
Id SharedAtomicU32IncDec(EmitContext& ctx, Id offset,
Id (Sirit::Module::*atomic_func)(Id, Id, Id, Id)) {
const Id shift_id{ctx.ConstU32(2U)};
const Id index{ctx.OpShiftRightLogical(ctx.U32[1], offset, shift_id)};
const u32 num_elements{Common::DivCeil(ctx.runtime_info.cs_info.shared_memory_size, 4u)};
const Id pointer{
ctx.OpAccessChain(ctx.shared_u32, ctx.shared_memory_u32, ctx.u32_zero_value, index)};
const auto [scope, semantics]{AtomicArgs(ctx)};
return AccessBoundsCheck<32>(ctx, index, ctx.ConstU32(num_elements), [&] {
return (ctx.*atomic_func)(ctx.U32[1], pointer, scope, semantics);
});
}
Id SharedAtomicU64(EmitContext& ctx, Id offset, Id value, Id SharedAtomicU64(EmitContext& ctx, Id offset, Id value,
Id (Sirit::Module::*atomic_func)(Id, Id, Id, Id, Id)) { Id (Sirit::Module::*atomic_func)(Id, Id, Id, Id, Id)) {
const Id shift_id{ctx.ConstU32(3U)}; const Id shift_id{ctx.ConstU32(3U)};
@ -40,19 +53,6 @@ Id SharedAtomicU64(EmitContext& ctx, Id offset, Id value,
}); });
} }
Id SharedAtomicU32_IncDec(EmitContext& ctx, Id offset,
Id (Sirit::Module::*atomic_func)(Id, Id, Id, Id)) {
const Id shift_id{ctx.ConstU32(2U)};
const Id index{ctx.OpShiftRightLogical(ctx.U32[1], offset, shift_id)};
const u32 num_elements{Common::DivCeil(ctx.runtime_info.cs_info.shared_memory_size, 4u)};
const Id pointer{
ctx.OpAccessChain(ctx.shared_u32, ctx.shared_memory_u32, ctx.u32_zero_value, index)};
const auto [scope, semantics]{AtomicArgs(ctx)};
return AccessBoundsCheck<32>(ctx, index, ctx.ConstU32(num_elements), [&] {
return (ctx.*atomic_func)(ctx.U32[1], pointer, scope, semantics);
});
}
Id BufferAtomicU32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value, Id BufferAtomicU32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value,
Id (Sirit::Module::*atomic_func)(Id, Id, Id, Id, Id)) { Id (Sirit::Module::*atomic_func)(Id, Id, Id, Id, Id)) {
const auto& buffer = ctx.buffers[handle]; const auto& buffer = ctx.buffers[handle];
@ -68,6 +68,21 @@ Id BufferAtomicU32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id
}); });
} }
Id BufferAtomicU32IncDec(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address,
Id (Sirit::Module::*atomic_func)(Id, Id, Id, Id)) {
const auto& buffer = ctx.buffers[handle];
if (Sirit::ValidId(buffer.offset)) {
address = ctx.OpIAdd(ctx.U32[1], address, buffer.offset);
}
const Id index = ctx.OpShiftRightLogical(ctx.U32[1], address, ctx.ConstU32(2u));
const auto [id, pointer_type] = buffer[EmitContext::PointerType::U32];
const Id ptr = ctx.OpAccessChain(pointer_type, id, ctx.u32_zero_value, index);
const auto [scope, semantics]{AtomicArgs(ctx)};
return AccessBoundsCheck<32>(ctx, index, buffer.size_dwords, [&] {
return (ctx.*atomic_func)(ctx.U32[1], ptr, scope, semantics);
});
}
Id BufferAtomicU32CmpSwap(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value, Id BufferAtomicU32CmpSwap(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value,
Id cmp_value, Id cmp_value,
Id (Sirit::Module::*atomic_func)(Id, Id, Id, Id, Id, Id, Id)) { Id (Sirit::Module::*atomic_func)(Id, Id, Id, Id, Id, Id, Id)) {
@ -156,12 +171,12 @@ Id EmitSharedAtomicISub32(EmitContext& ctx, Id offset, Id value) {
return SharedAtomicU32(ctx, offset, value, &Sirit::Module::OpAtomicISub); return SharedAtomicU32(ctx, offset, value, &Sirit::Module::OpAtomicISub);
} }
Id EmitSharedAtomicIIncrement32(EmitContext& ctx, Id offset) { Id EmitSharedAtomicInc32(EmitContext& ctx, Id offset) {
return SharedAtomicU32_IncDec(ctx, offset, &Sirit::Module::OpAtomicIIncrement); return SharedAtomicU32IncDec(ctx, offset, &Sirit::Module::OpAtomicIIncrement);
} }
Id EmitSharedAtomicIDecrement32(EmitContext& ctx, Id offset) { Id EmitSharedAtomicDec32(EmitContext& ctx, Id offset) {
return SharedAtomicU32_IncDec(ctx, offset, &Sirit::Module::OpAtomicIDecrement); return SharedAtomicU32IncDec(ctx, offset, &Sirit::Module::OpAtomicIDecrement);
} }
Id EmitBufferAtomicIAdd32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value) { Id EmitBufferAtomicIAdd32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value) {
@ -172,6 +187,10 @@ Id EmitBufferAtomicIAdd64(EmitContext& ctx, IR::Inst* inst, u32 handle, Id addre
return BufferAtomicU64(ctx, inst, handle, address, value, &Sirit::Module::OpAtomicIAdd); return BufferAtomicU64(ctx, inst, handle, address, value, &Sirit::Module::OpAtomicIAdd);
} }
Id EmitBufferAtomicISub32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value) {
return BufferAtomicU32(ctx, inst, handle, address, value, &Sirit::Module::OpAtomicISub);
}
Id EmitBufferAtomicSMin32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value) { Id EmitBufferAtomicSMin32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value) {
return BufferAtomicU32(ctx, inst, handle, address, value, &Sirit::Module::OpAtomicSMin); return BufferAtomicU32(ctx, inst, handle, address, value, &Sirit::Module::OpAtomicSMin);
} }
@ -188,14 +207,12 @@ Id EmitBufferAtomicUMax32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id addre
return BufferAtomicU32(ctx, inst, handle, address, value, &Sirit::Module::OpAtomicUMax); return BufferAtomicU32(ctx, inst, handle, address, value, &Sirit::Module::OpAtomicUMax);
} }
Id EmitBufferAtomicInc32(EmitContext&, IR::Inst*, u32, Id, Id) { Id EmitBufferAtomicInc32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address) {
// TODO return BufferAtomicU32IncDec(ctx, inst, handle, address, &Sirit::Module::OpAtomicIIncrement);
UNREACHABLE_MSG("Unsupported BUFFER_ATOMIC opcode: ", IR::Opcode::BufferAtomicInc32);
} }
Id EmitBufferAtomicDec32(EmitContext&, IR::Inst*, u32, Id, Id) { Id EmitBufferAtomicDec32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address) {
// TODO return BufferAtomicU32IncDec(ctx, inst, handle, address, &Sirit::Module::OpAtomicIDecrement);
UNREACHABLE_MSG("Unsupported BUFFER_ATOMIC opcode: ", IR::Opcode::BufferAtomicDec32);
} }
Id EmitBufferAtomicAnd32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value) { Id EmitBufferAtomicAnd32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value) {

View File

@ -1,31 +1,54 @@
// SPDX-FileCopyrightText: Copyright 2025 shadPS4 Emulator Project // SPDX-FileCopyrightText: Copyright 2025 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h" #pragma once
#include "shader_recompiler/backend/spirv/spirv_emit_context.h" #include "shader_recompiler/backend/spirv/spirv_emit_context.h"
namespace Shader::Backend::SPIRV { namespace Shader::Backend::SPIRV {
template <u32 bit_size> template <u32 bit_size, u32 num_components = 1, bool is_float = false>
auto AccessBoundsCheck(EmitContext& ctx, Id index, Id buffer_size, auto emit_func) { std::tuple<Id, Id> ResolveTypeAndZero(EmitContext& ctx) {
Id zero_value{};
Id result_type{}; Id result_type{};
if constexpr (bit_size == 64) { Id zero_value{};
zero_value = ctx.u64_zero_value; if constexpr (bit_size == 64 && num_components == 1 && !is_float) {
result_type = ctx.U64; result_type = ctx.U64;
zero_value = ctx.u64_zero_value;
} else if constexpr (bit_size == 32) { } else if constexpr (bit_size == 32) {
zero_value = ctx.u32_zero_value; if (is_float) {
result_type = ctx.U32[1]; result_type = ctx.F32[num_components];
} else if constexpr (bit_size == 16) { zero_value = ctx.f32_zero_value;
zero_value = ctx.u16_zero_value;
result_type = ctx.U16;
} else { } else {
static_assert(false, "type not supported"); result_type = ctx.U32[num_components];
zero_value = ctx.u32_zero_value;
} }
} else if constexpr (bit_size == 16 && num_components == 1 && !is_float) {
result_type = ctx.U16;
zero_value = ctx.u16_zero_value;
} else if constexpr (bit_size == 8 && num_components == 1 && !is_float) {
result_type = ctx.U8;
zero_value = ctx.u8_zero_value;
} else {
static_assert(false, "Type not supported.");
}
if (num_components > 1) {
std::array<Id, num_components> zero_ids;
zero_ids.fill(zero_value);
zero_value = ctx.ConstantComposite(result_type, zero_ids);
}
return {result_type, zero_value};
}
template <u32 bit_size, u32 num_components = 1, bool is_float = false>
auto AccessBoundsCheck(EmitContext& ctx, Id index, Id buffer_size, auto emit_func) {
if (Sirit::ValidId(buffer_size)) { if (Sirit::ValidId(buffer_size)) {
// Bounds checking enabled, wrap in a conditional branch to make sure that // Bounds checking enabled, wrap in a conditional branch to make sure that
// the atomic is not mistakenly executed when the index is out of bounds. // the atomic is not mistakenly executed when the index is out of bounds.
const Id in_bounds = ctx.OpULessThan(ctx.U1[1], index, buffer_size); auto compare_index = index;
if (num_components > 1) {
compare_index = ctx.OpIAdd(ctx.U32[1], index, ctx.ConstU32(num_components - 1));
}
const Id in_bounds = ctx.OpULessThan(ctx.U1[1], compare_index, buffer_size);
const Id ib_label = ctx.OpLabel(); const Id ib_label = ctx.OpLabel();
const Id end_label = ctx.OpLabel(); const Id end_label = ctx.OpLabel();
ctx.OpSelectionMerge(end_label, spv::SelectionControlMask::MaskNone); ctx.OpSelectionMerge(end_label, spv::SelectionControlMask::MaskNone);
@ -36,6 +59,8 @@ auto AccessBoundsCheck(EmitContext& ctx, Id index, Id buffer_size, auto emit_fun
ctx.OpBranch(end_label); ctx.OpBranch(end_label);
ctx.AddLabel(end_label); ctx.AddLabel(end_label);
if (Sirit::ValidId(ib_result)) { if (Sirit::ValidId(ib_result)) {
const auto [result_type, zero_value] =
ResolveTypeAndZero<bit_size, num_components, is_float>(ctx);
return ctx.OpPhi(result_type, ib_result, ib_label, zero_value, last_label); return ctx.OpPhi(result_type, ib_result, ib_label, zero_value, last_label);
} else { } else {
return Id{0}; return Id{0};
@ -45,4 +70,21 @@ auto AccessBoundsCheck(EmitContext& ctx, Id index, Id buffer_size, auto emit_fun
return emit_func(); return emit_func();
} }
template <u32 bit_size, u32 num_components = 1, bool is_float = false>
static Id LoadAccessBoundsCheck(EmitContext& ctx, Id index, Id buffer_size, Id result) {
if (Sirit::ValidId(buffer_size)) {
// Bounds checking enabled, wrap in a select.
auto compare_index = index;
if (num_components > 1) {
compare_index = ctx.OpIAdd(ctx.U32[1], index, ctx.ConstU32(num_components - 1));
}
const Id in_bounds = ctx.OpULessThan(ctx.U1[1], compare_index, buffer_size);
const auto [result_type, zero_value] =
ResolveTypeAndZero<bit_size, num_components, is_float>(ctx);
return ctx.OpSelect(result_type, in_bounds, result, zero_value);
}
// Bounds checking not enabled, just return the plain value.
return result;
}
} // namespace Shader::Backend::SPIRV } // namespace Shader::Backend::SPIRV

View File

@ -11,6 +11,8 @@
#include <magic_enum/magic_enum.hpp> #include <magic_enum/magic_enum.hpp>
#include "emit_spirv_bounds.h"
namespace Shader::Backend::SPIRV { namespace Shader::Backend::SPIRV {
namespace { namespace {
@ -239,8 +241,8 @@ Id EmitGetAttribute(EmitContext& ctx, IR::Attribute attr, u32 comp, Id index) {
} }
if (IR::IsParam(attr)) { if (IR::IsParam(attr)) {
const u32 index{u32(attr) - u32(IR::Attribute::Param0)}; const u32 param_index{u32(attr) - u32(IR::Attribute::Param0)};
const auto& param{ctx.input_params.at(index)}; const auto& param{ctx.input_params.at(param_index)};
if (param.buffer_handle >= 0) { if (param.buffer_handle >= 0) {
const auto step_rate = EmitReadStepRate(ctx, param.id.value); const auto step_rate = EmitReadStepRate(ctx, param.id.value);
const auto offset = ctx.OpIAdd( const auto offset = ctx.OpIAdd(
@ -415,27 +417,6 @@ void EmitSetPatch(EmitContext& ctx, IR::Patch patch, Id value) {
ctx.OpStore(pointer, value); ctx.OpStore(pointer, value);
} }
template <u32 N>
static Id EmitLoadBufferBoundsCheck(EmitContext& ctx, Id index, Id buffer_size, Id result,
bool is_float) {
if (Sirit::ValidId(buffer_size)) {
// Bounds checking enabled, wrap in a select.
const auto result_type = is_float ? ctx.F32[N] : ctx.U32[N];
auto compare_index = index;
auto zero_value = is_float ? ctx.f32_zero_value : ctx.u32_zero_value;
if (N > 1) {
compare_index = ctx.OpIAdd(ctx.U32[1], index, ctx.ConstU32(N - 1));
std::array<Id, N> zero_ids;
zero_ids.fill(zero_value);
zero_value = ctx.ConstantComposite(result_type, zero_ids);
}
const Id in_bounds = ctx.OpULessThan(ctx.U1[1], compare_index, buffer_size);
return ctx.OpSelect(result_type, in_bounds, result, zero_value);
}
// Bounds checking not enabled, just return the plain value.
return result;
}
template <u32 N, PointerType alias> template <u32 N, PointerType alias>
static Id EmitLoadBufferB32xN(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address) { static Id EmitLoadBufferB32xN(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address) {
const auto flags = inst->Flags<IR::BufferInstInfo>(); const auto flags = inst->Flags<IR::BufferInstInfo>();
@ -454,8 +435,9 @@ static Id EmitLoadBufferB32xN(EmitContext& ctx, IR::Inst* inst, u32 handle, Id a
const Id result_i = ctx.OpLoad(data_types[1], ptr_i); const Id result_i = ctx.OpLoad(data_types[1], ptr_i);
if (!flags.typed) { if (!flags.typed) {
// Untyped loads have bounds checking per-component. // Untyped loads have bounds checking per-component.
ids.push_back(EmitLoadBufferBoundsCheck<1>(ctx, index_i, spv_buffer.size_dwords, ids.push_back(LoadAccessBoundsCheck < 32, 1,
result_i, alias == PointerType::F32)); alias ==
PointerType::F32 > (ctx, index_i, spv_buffer.size_dwords, result_i));
} else { } else {
ids.push_back(result_i); ids.push_back(result_i);
} }
@ -464,8 +446,8 @@ static Id EmitLoadBufferB32xN(EmitContext& ctx, IR::Inst* inst, u32 handle, Id a
const Id result = N == 1 ? ids[0] : ctx.OpCompositeConstruct(data_types[N], ids); const Id result = N == 1 ? ids[0] : ctx.OpCompositeConstruct(data_types[N], ids);
if (flags.typed) { if (flags.typed) {
// Typed loads have single bounds check for the whole load. // Typed loads have single bounds check for the whole load.
return EmitLoadBufferBoundsCheck<N>(ctx, index, spv_buffer.size_dwords, result, return LoadAccessBoundsCheck < 32, N,
alias == PointerType::F32); alias == PointerType::F32 > (ctx, index, spv_buffer.size_dwords, result);
} }
return result; return result;
} }
@ -477,8 +459,8 @@ Id EmitLoadBufferU8(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address) {
} }
const auto [id, pointer_type] = spv_buffer[PointerType::U8]; const auto [id, pointer_type] = spv_buffer[PointerType::U8];
const Id ptr{ctx.OpAccessChain(pointer_type, id, ctx.u32_zero_value, address)}; const Id ptr{ctx.OpAccessChain(pointer_type, id, ctx.u32_zero_value, address)};
const Id result{ctx.OpUConvert(ctx.U32[1], ctx.OpLoad(ctx.U8, ptr))}; const Id result{ctx.OpLoad(ctx.U8, ptr)};
return EmitLoadBufferBoundsCheck<1>(ctx, address, spv_buffer.size, result, false); return LoadAccessBoundsCheck<8>(ctx, address, spv_buffer.size, result);
} }
Id EmitLoadBufferU16(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address) { Id EmitLoadBufferU16(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address) {
@ -489,8 +471,8 @@ Id EmitLoadBufferU16(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address) {
const auto [id, pointer_type] = spv_buffer[PointerType::U16]; const auto [id, pointer_type] = spv_buffer[PointerType::U16];
const Id index = ctx.OpShiftRightLogical(ctx.U32[1], address, ctx.ConstU32(1u)); const Id index = ctx.OpShiftRightLogical(ctx.U32[1], address, ctx.ConstU32(1u));
const Id ptr{ctx.OpAccessChain(pointer_type, id, ctx.u32_zero_value, index)}; const Id ptr{ctx.OpAccessChain(pointer_type, id, ctx.u32_zero_value, index)};
const Id result{ctx.OpUConvert(ctx.U32[1], ctx.OpLoad(ctx.U16, ptr))}; const Id result{ctx.OpLoad(ctx.U16, ptr)};
return EmitLoadBufferBoundsCheck<1>(ctx, index, spv_buffer.size_shorts, result, false); return LoadAccessBoundsCheck<16>(ctx, index, spv_buffer.size_shorts, result);
} }
Id EmitLoadBufferU32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address) { Id EmitLoadBufferU32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address) {
@ -509,6 +491,18 @@ Id EmitLoadBufferU32x4(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address)
return EmitLoadBufferB32xN<4, PointerType::U32>(ctx, inst, handle, address); return EmitLoadBufferB32xN<4, PointerType::U32>(ctx, inst, handle, address);
} }
Id EmitLoadBufferU64(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address) {
const auto& spv_buffer = ctx.buffers[handle];
if (Sirit::ValidId(spv_buffer.offset)) {
address = ctx.OpIAdd(ctx.U32[1], address, spv_buffer.offset);
}
const auto [id, pointer_type] = spv_buffer[PointerType::U64];
const Id index = ctx.OpShiftRightLogical(ctx.U32[1], address, ctx.ConstU32(3u));
const Id ptr{ctx.OpAccessChain(pointer_type, id, ctx.u64_zero_value, index)};
const Id result{ctx.OpLoad(ctx.U64, ptr)};
return LoadAccessBoundsCheck<64>(ctx, index, spv_buffer.size_qwords, result);
}
Id EmitLoadBufferF32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address) { Id EmitLoadBufferF32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address) {
return EmitLoadBufferB32xN<1, PointerType::F32>(ctx, inst, handle, address); return EmitLoadBufferB32xN<1, PointerType::F32>(ctx, inst, handle, address);
} }
@ -529,29 +523,6 @@ Id EmitLoadBufferFormatF32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id addr
UNREACHABLE_MSG("SPIR-V instruction"); UNREACHABLE_MSG("SPIR-V instruction");
} }
template <u32 N>
void EmitStoreBufferBoundsCheck(EmitContext& ctx, Id index, Id buffer_size, auto emit_func) {
if (Sirit::ValidId(buffer_size)) {
// Bounds checking enabled, wrap in a conditional branch.
auto compare_index = index;
if (N > 1) {
compare_index = ctx.OpIAdd(ctx.U32[1], index, ctx.ConstU32(N - 1));
}
const Id in_bounds = ctx.OpULessThan(ctx.U1[1], compare_index, buffer_size);
const Id in_bounds_label = ctx.OpLabel();
const Id merge_label = ctx.OpLabel();
ctx.OpSelectionMerge(merge_label, spv::SelectionControlMask::MaskNone);
ctx.OpBranchConditional(in_bounds, in_bounds_label, merge_label);
ctx.AddLabel(in_bounds_label);
emit_func();
ctx.OpBranch(merge_label);
ctx.AddLabel(merge_label);
return;
}
// Bounds checking not enabled, just perform the store.
emit_func();
}
template <u32 N, PointerType alias> template <u32 N, PointerType alias>
static void EmitStoreBufferB32xN(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, static void EmitStoreBufferB32xN(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address,
Id value) { Id value) {
@ -569,19 +540,25 @@ static void EmitStoreBufferB32xN(EmitContext& ctx, IR::Inst* inst, u32 handle, I
const Id index_i = i == 0 ? index : ctx.OpIAdd(ctx.U32[1], index, ctx.ConstU32(i)); const Id index_i = i == 0 ? index : ctx.OpIAdd(ctx.U32[1], index, ctx.ConstU32(i));
const Id ptr_i = ctx.OpAccessChain(pointer_type, id, ctx.u32_zero_value, index_i); const Id ptr_i = ctx.OpAccessChain(pointer_type, id, ctx.u32_zero_value, index_i);
const Id value_i = N == 1 ? value : ctx.OpCompositeExtract(data_types[1], value, i); const Id value_i = N == 1 ? value : ctx.OpCompositeExtract(data_types[1], value, i);
auto store_i = [&]() { ctx.OpStore(ptr_i, value_i); }; auto store_i = [&] {
ctx.OpStore(ptr_i, value_i);
return Id{};
};
if (!flags.typed) { if (!flags.typed) {
// Untyped stores have bounds checking per-component. // Untyped stores have bounds checking per-component.
EmitStoreBufferBoundsCheck<1>(ctx, index_i, spv_buffer.size_dwords, store_i); AccessBoundsCheck<32, 1, alias == PointerType::F32>(
ctx, index_i, spv_buffer.size_dwords, store_i);
} else { } else {
store_i(); store_i();
} }
} }
return Id{};
}; };
if (flags.typed) { if (flags.typed) {
// Typed stores have single bounds check for the whole store. // Typed stores have single bounds check for the whole store.
EmitStoreBufferBoundsCheck<N>(ctx, index, spv_buffer.size_dwords, store); AccessBoundsCheck<32, N, alias == PointerType::F32>(ctx, index, spv_buffer.size_dwords,
store);
} else { } else {
store(); store();
} }
@ -594,8 +571,10 @@ void EmitStoreBufferU8(EmitContext& ctx, IR::Inst*, u32 handle, Id address, Id v
} }
const auto [id, pointer_type] = spv_buffer[PointerType::U8]; const auto [id, pointer_type] = spv_buffer[PointerType::U8];
const Id ptr{ctx.OpAccessChain(pointer_type, id, ctx.u32_zero_value, address)}; const Id ptr{ctx.OpAccessChain(pointer_type, id, ctx.u32_zero_value, address)};
const Id result{ctx.OpUConvert(ctx.U8, value)}; AccessBoundsCheck<8>(ctx, address, spv_buffer.size, [&] {
EmitStoreBufferBoundsCheck<1>(ctx, address, spv_buffer.size, [&] { ctx.OpStore(ptr, result); }); ctx.OpStore(ptr, value);
return Id{};
});
} }
void EmitStoreBufferU16(EmitContext& ctx, IR::Inst*, u32 handle, Id address, Id value) { void EmitStoreBufferU16(EmitContext& ctx, IR::Inst*, u32 handle, Id address, Id value) {
@ -606,9 +585,10 @@ void EmitStoreBufferU16(EmitContext& ctx, IR::Inst*, u32 handle, Id address, Id
const auto [id, pointer_type] = spv_buffer[PointerType::U16]; const auto [id, pointer_type] = spv_buffer[PointerType::U16];
const Id index = ctx.OpShiftRightLogical(ctx.U32[1], address, ctx.ConstU32(1u)); const Id index = ctx.OpShiftRightLogical(ctx.U32[1], address, ctx.ConstU32(1u));
const Id ptr{ctx.OpAccessChain(pointer_type, id, ctx.u32_zero_value, index)}; const Id ptr{ctx.OpAccessChain(pointer_type, id, ctx.u32_zero_value, index)};
const Id result{ctx.OpUConvert(ctx.U16, value)}; AccessBoundsCheck<16>(ctx, index, spv_buffer.size_shorts, [&] {
EmitStoreBufferBoundsCheck<1>(ctx, index, spv_buffer.size_shorts, ctx.OpStore(ptr, value);
[&] { ctx.OpStore(ptr, result); }); return Id{};
});
} }
void EmitStoreBufferU32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value) { void EmitStoreBufferU32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value) {
@ -627,6 +607,20 @@ void EmitStoreBufferU32x4(EmitContext& ctx, IR::Inst* inst, u32 handle, Id addre
EmitStoreBufferB32xN<4, PointerType::U32>(ctx, inst, handle, address, value); EmitStoreBufferB32xN<4, PointerType::U32>(ctx, inst, handle, address, value);
} }
void EmitStoreBufferU64(EmitContext& ctx, IR::Inst*, u32 handle, Id address, Id value) {
const auto& spv_buffer = ctx.buffers[handle];
if (Sirit::ValidId(spv_buffer.offset)) {
address = ctx.OpIAdd(ctx.U32[1], address, spv_buffer.offset);
}
const auto [id, pointer_type] = spv_buffer[PointerType::U64];
const Id index = ctx.OpShiftRightLogical(ctx.U32[1], address, ctx.ConstU32(3u));
const Id ptr{ctx.OpAccessChain(pointer_type, id, ctx.u64_zero_value, index)};
AccessBoundsCheck<64>(ctx, index, spv_buffer.size_qwords, [&] {
ctx.OpStore(ptr, value);
return Id{};
});
}
void EmitStoreBufferF32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value) { void EmitStoreBufferF32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value) {
EmitStoreBufferB32xN<1, PointerType::F32>(ctx, inst, handle, address, value); EmitStoreBufferB32xN<1, PointerType::F32>(ctx, inst, handle, address, value);
} }

View File

@ -263,4 +263,12 @@ Id EmitConvertU32U16(EmitContext& ctx, Id value) {
return ctx.OpUConvert(ctx.U32[1], value); return ctx.OpUConvert(ctx.U32[1], value);
} }
Id EmitConvertU8U32(EmitContext& ctx, Id value) {
return ctx.OpUConvert(ctx.U8, value);
}
Id EmitConvertU32U8(EmitContext& ctx, Id value) {
return ctx.OpUConvert(ctx.U32[1], value);
}
} // namespace Shader::Backend::SPIRV } // namespace Shader::Backend::SPIRV

View File

@ -69,6 +69,7 @@ Id EmitLoadBufferU32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address);
Id EmitLoadBufferU32x2(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address); Id EmitLoadBufferU32x2(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address);
Id EmitLoadBufferU32x3(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address); Id EmitLoadBufferU32x3(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address);
Id EmitLoadBufferU32x4(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address); Id EmitLoadBufferU32x4(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address);
Id EmitLoadBufferU64(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address);
Id EmitLoadBufferF32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address); Id EmitLoadBufferF32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address);
Id EmitLoadBufferF32x2(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address); Id EmitLoadBufferF32x2(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address);
Id EmitLoadBufferF32x3(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address); Id EmitLoadBufferF32x3(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address);
@ -80,6 +81,7 @@ void EmitStoreBufferU32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address
void EmitStoreBufferU32x2(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value); void EmitStoreBufferU32x2(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value);
void EmitStoreBufferU32x3(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value); void EmitStoreBufferU32x3(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value);
void EmitStoreBufferU32x4(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value); void EmitStoreBufferU32x4(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value);
void EmitStoreBufferU64(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value);
void EmitStoreBufferF32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value); void EmitStoreBufferF32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value);
void EmitStoreBufferF32x2(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value); void EmitStoreBufferF32x2(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value);
void EmitStoreBufferF32x3(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value); void EmitStoreBufferF32x3(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value);
@ -87,12 +89,13 @@ void EmitStoreBufferF32x4(EmitContext& ctx, IR::Inst* inst, u32 handle, Id addre
void EmitStoreBufferFormatF32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value); void EmitStoreBufferFormatF32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value);
Id EmitBufferAtomicIAdd32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value); Id EmitBufferAtomicIAdd32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value);
Id EmitBufferAtomicIAdd64(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value); Id EmitBufferAtomicIAdd64(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value);
Id EmitBufferAtomicISub32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value);
Id EmitBufferAtomicSMin32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value); Id EmitBufferAtomicSMin32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value);
Id EmitBufferAtomicUMin32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value); Id EmitBufferAtomicUMin32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value);
Id EmitBufferAtomicSMax32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value); Id EmitBufferAtomicSMax32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value);
Id EmitBufferAtomicUMax32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value); Id EmitBufferAtomicUMax32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value);
Id EmitBufferAtomicInc32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value); Id EmitBufferAtomicInc32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address);
Id EmitBufferAtomicDec32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value); Id EmitBufferAtomicDec32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address);
Id EmitBufferAtomicAnd32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value); Id EmitBufferAtomicAnd32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value);
Id EmitBufferAtomicOr32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value); Id EmitBufferAtomicOr32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value);
Id EmitBufferAtomicXor32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value); Id EmitBufferAtomicXor32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value);
@ -136,8 +139,8 @@ Id EmitSharedAtomicSMin32(EmitContext& ctx, Id offset, Id value);
Id EmitSharedAtomicAnd32(EmitContext& ctx, Id offset, Id value); Id EmitSharedAtomicAnd32(EmitContext& ctx, Id offset, Id value);
Id EmitSharedAtomicOr32(EmitContext& ctx, Id offset, Id value); Id EmitSharedAtomicOr32(EmitContext& ctx, Id offset, Id value);
Id EmitSharedAtomicXor32(EmitContext& ctx, Id offset, Id value); Id EmitSharedAtomicXor32(EmitContext& ctx, Id offset, Id value);
Id EmitSharedAtomicIIncrement32(EmitContext& ctx, Id offset); Id EmitSharedAtomicInc32(EmitContext& ctx, Id offset);
Id EmitSharedAtomicIDecrement32(EmitContext& ctx, Id offset); Id EmitSharedAtomicDec32(EmitContext& ctx, Id offset);
Id EmitSharedAtomicISub32(EmitContext& ctx, Id offset, Id value); Id EmitSharedAtomicISub32(EmitContext& ctx, Id offset, Id value);
Id EmitCompositeConstructU32x2(EmitContext& ctx, IR::Inst* inst, Id e1, Id e2); Id EmitCompositeConstructU32x2(EmitContext& ctx, IR::Inst* inst, Id e1, Id e2);
@ -461,6 +464,8 @@ Id EmitConvertF64U32(EmitContext& ctx, Id value);
Id EmitConvertF64U64(EmitContext& ctx, Id value); Id EmitConvertF64U64(EmitContext& ctx, Id value);
Id EmitConvertU16U32(EmitContext& ctx, Id value); Id EmitConvertU16U32(EmitContext& ctx, Id value);
Id EmitConvertU32U16(EmitContext& ctx, Id value); Id EmitConvertU32U16(EmitContext& ctx, Id value);
Id EmitConvertU8U32(EmitContext& ctx, Id value);
Id EmitConvertU32U8(EmitContext& ctx, Id value);
Id EmitImageSampleRaw(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address1, Id address2, Id EmitImageSampleRaw(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address1, Id address2,
Id address3, Id address4); Id address3, Id address4);

View File

@ -299,8 +299,7 @@ void EmitContext::DefineInterpolatedAttribs() {
// Iterate all input attributes, load them and manually interpolate. // Iterate all input attributes, load them and manually interpolate.
for (s32 i = 0; i < runtime_info.fs_info.num_inputs; i++) { for (s32 i = 0; i < runtime_info.fs_info.num_inputs; i++) {
const auto& input = runtime_info.fs_info.inputs[i]; const auto& input = runtime_info.fs_info.inputs[i];
const u32 semantic = input.param_index; auto& params = input_params[i];
auto& params = input_params[semantic];
if (input.is_flat || params.is_loaded) { if (input.is_flat || params.is_loaded) {
continue; continue;
} }
@ -318,7 +317,7 @@ void EmitContext::DefineInterpolatedAttribs() {
const Id p10_y{OpVectorTimesScalar(F32[4], p10, bary_coord_y)}; const Id p10_y{OpVectorTimesScalar(F32[4], p10, bary_coord_y)};
const Id p20_z{OpVectorTimesScalar(F32[4], p20, bary_coord_z)}; const Id p20_z{OpVectorTimesScalar(F32[4], p20, bary_coord_z)};
params.id = OpFAdd(F32[4], p0, OpFAdd(F32[4], p10_y, p20_z)); params.id = OpFAdd(F32[4], p0, OpFAdd(F32[4], p10_y, p20_z));
Name(params.id, fmt::format("fs_in_attr{}", semantic)); Name(params.id, fmt::format("fs_in_attr{}", i));
params.is_loaded = true; params.is_loaded = true;
} }
} }
@ -427,25 +426,28 @@ void EmitContext::DefineInputs() {
} }
for (s32 i = 0; i < runtime_info.fs_info.num_inputs; i++) { for (s32 i = 0; i < runtime_info.fs_info.num_inputs; i++) {
const auto& input = runtime_info.fs_info.inputs[i]; const auto& input = runtime_info.fs_info.inputs[i];
const u32 semantic = input.param_index;
ASSERT(semantic < IR::NumParams);
if (input.IsDefault()) { if (input.IsDefault()) {
input_params[semantic] = { input_params[i] = {
MakeDefaultValue(*this, input.default_value), input_f32, F32[1], 4, false, true, .id = MakeDefaultValue(*this, input.default_value),
.pointer_type = input_f32,
.component_type = F32[1],
.num_components = 4,
.is_integer = false,
.is_loaded = true,
}; };
continue; continue;
} }
const IR::Attribute param{IR::Attribute::Param0 + input.param_index}; const IR::Attribute param{IR::Attribute::Param0 + i};
const u32 num_components = info.loads.NumComponents(param); const u32 num_components = info.loads.NumComponents(param);
const Id type{F32[num_components]}; const Id type{F32[num_components]};
Id attr_id{}; Id attr_id{};
if (profile.needs_manual_interpolation && !input.is_flat) { if (profile.needs_manual_interpolation && !input.is_flat) {
attr_id = DefineInput(TypeArray(type, ConstU32(3U)), semantic); attr_id = DefineInput(TypeArray(type, ConstU32(3U)), input.param_index);
Decorate(attr_id, spv::Decoration::PerVertexKHR); Decorate(attr_id, spv::Decoration::PerVertexKHR);
Name(attr_id, fmt::format("fs_in_attr{}_p", semantic)); Name(attr_id, fmt::format("fs_in_attr{}_p", i));
} else { } else {
attr_id = DefineInput(type, semantic); attr_id = DefineInput(type, input.param_index);
Name(attr_id, fmt::format("fs_in_attr{}", semantic)); Name(attr_id, fmt::format("fs_in_attr{}", i));
if (input.is_flat) { if (input.is_flat) {
Decorate(attr_id, spv::Decoration::Flat); Decorate(attr_id, spv::Decoration::Flat);
@ -453,7 +455,7 @@ void EmitContext::DefineInputs() {
Decorate(attr_id, spv::Decoration::NoPerspective); Decorate(attr_id, spv::Decoration::NoPerspective);
} }
} }
input_params[semantic] = input_params[i] =
GetAttributeInfo(AmdGpu::NumberFormat::Float, attr_id, num_components, false); GetAttributeInfo(AmdGpu::NumberFormat::Float, attr_id, num_components, false);
} }
break; break;
@ -977,32 +979,46 @@ void EmitContext::DefineImagesAndSamplers() {
} }
void EmitContext::DefineSharedMemory() { void EmitContext::DefineSharedMemory() {
if (!info.uses_shared) { const auto num_types = std::popcount(static_cast<u32>(info.shared_types));
if (num_types == 0) {
return; return;
} }
ASSERT(info.stage == Stage::Compute); ASSERT(info.stage == Stage::Compute);
const u32 shared_memory_size = runtime_info.cs_info.shared_memory_size; const u32 shared_memory_size = runtime_info.cs_info.shared_memory_size;
const auto make_type = [&](Id element_type, u32 element_size) { const auto make_type = [&](IR::Type type, Id element_type, u32 element_size,
std::string_view name) {
if (False(info.shared_types & type)) {
// Skip unused shared memory types.
return std::make_tuple(Id{}, Id{}, Id{});
}
const u32 num_elements{Common::DivCeil(shared_memory_size, element_size)}; const u32 num_elements{Common::DivCeil(shared_memory_size, element_size)};
const Id array_type{TypeArray(element_type, ConstU32(num_elements))}; const Id array_type{TypeArray(element_type, ConstU32(num_elements))};
Decorate(array_type, spv::Decoration::ArrayStride, element_size); Decorate(array_type, spv::Decoration::ArrayStride, element_size);
const Id struct_type{TypeStruct(array_type)}; const Id struct_type{TypeStruct(array_type)};
MemberDecorate(struct_type, 0u, spv::Decoration::Offset, 0u); MemberDecorate(struct_type, 0u, spv::Decoration::Offset, 0u);
Decorate(struct_type, spv::Decoration::Block);
const Id pointer = TypePointer(spv::StorageClass::Workgroup, struct_type); const Id pointer = TypePointer(spv::StorageClass::Workgroup, struct_type);
const Id element_pointer = TypePointer(spv::StorageClass::Workgroup, element_type); const Id element_pointer = TypePointer(spv::StorageClass::Workgroup, element_type);
const Id variable = AddGlobalVariable(pointer, spv::StorageClass::Workgroup); const Id variable = AddGlobalVariable(pointer, spv::StorageClass::Workgroup);
Decorate(variable, spv::Decoration::Aliased); Name(variable, name);
interfaces.push_back(variable); interfaces.push_back(variable);
if (num_types > 1) {
Decorate(struct_type, spv::Decoration::Block);
Decorate(variable, spv::Decoration::Aliased);
}
return std::make_tuple(variable, element_pointer, pointer); return std::make_tuple(variable, element_pointer, pointer);
}; };
std::tie(shared_memory_u16, shared_u16, shared_memory_u16_type) = make_type(U16, 2u); std::tie(shared_memory_u16, shared_u16, shared_memory_u16_type) =
std::tie(shared_memory_u32, shared_u32, shared_memory_u32_type) = make_type(U32[1], 4u); make_type(IR::Type::U16, U16, 2u, "shared_mem_u16");
std::tie(shared_memory_u64, shared_u64, shared_memory_u64_type) = make_type(U64, 8u); std::tie(shared_memory_u32, shared_u32, shared_memory_u32_type) =
make_type(IR::Type::U32, U32[1], 4u, "shared_mem_u32");
std::tie(shared_memory_u64, shared_u64, shared_memory_u64_type) =
make_type(IR::Type::U64, U64, 8u, "shared_mem_u64");
} }
Id EmitContext::DefineFloat32ToUfloatM5(u32 mantissa_bits, const std::string_view name) { Id EmitContext::DefineFloat32ToUfloatM5(u32 mantissa_bits, const std::string_view name) {

View File

@ -216,34 +216,38 @@ void Translator::DS_WRITE(int bit_size, bool is_signed, bool is_pair, bool strid
if (is_pair) { if (is_pair) {
const u32 adj = (bit_size == 32 ? 4 : 8) * (stride64 ? 64 : 1); const u32 adj = (bit_size == 32 ? 4 : 8) * (stride64 ? 64 : 1);
const IR::U32 addr0 = ir.IAdd(addr, ir.Imm32(u32(inst.control.ds.offset0 * adj))); const IR::U32 addr0 = ir.IAdd(addr, ir.Imm32(u32(inst.control.ds.offset0 * adj)));
if (bit_size == 32) { if (bit_size == 64) {
ir.WriteShared(32, ir.GetVectorReg(data0), addr0);
} else {
ir.WriteShared(64, ir.WriteShared(64,
ir.PackUint2x32(ir.CompositeConstruct(ir.GetVectorReg(data0), ir.PackUint2x32(ir.CompositeConstruct(ir.GetVectorReg(data0),
ir.GetVectorReg(data0 + 1))), ir.GetVectorReg(data0 + 1))),
addr0); addr0);
} else if (bit_size == 32) {
ir.WriteShared(32, ir.GetVectorReg(data0), addr0);
} else if (bit_size == 16) {
ir.WriteShared(16, ir.UConvert(16, ir.GetVectorReg(data0)), addr0);
} }
const IR::U32 addr1 = ir.IAdd(addr, ir.Imm32(u32(inst.control.ds.offset1 * adj))); const IR::U32 addr1 = ir.IAdd(addr, ir.Imm32(u32(inst.control.ds.offset1 * adj)));
if (bit_size == 32) { if (bit_size == 64) {
ir.WriteShared(32, ir.GetVectorReg(data1), addr1);
} else {
ir.WriteShared(64, ir.WriteShared(64,
ir.PackUint2x32(ir.CompositeConstruct(ir.GetVectorReg(data1), ir.PackUint2x32(ir.CompositeConstruct(ir.GetVectorReg(data1),
ir.GetVectorReg(data1 + 1))), ir.GetVectorReg(data1 + 1))),
addr1); addr1);
} else if (bit_size == 32) {
ir.WriteShared(32, ir.GetVectorReg(data1), addr1);
} else if (bit_size == 16) {
ir.WriteShared(16, ir.UConvert(16, ir.GetVectorReg(data1)), addr1);
} }
} else if (bit_size == 64) { } else {
const IR::U32 addr0 = ir.IAdd(addr, ir.Imm32(offset)); const IR::U32 addr0 = ir.IAdd(addr, ir.Imm32(offset));
if (bit_size == 64) {
const IR::Value data = const IR::Value data =
ir.CompositeConstruct(ir.GetVectorReg(data0), ir.GetVectorReg(data0 + 1)); ir.CompositeConstruct(ir.GetVectorReg(data0), ir.GetVectorReg(data0 + 1));
ir.WriteShared(bit_size, ir.PackUint2x32(data), addr0); ir.WriteShared(bit_size, ir.PackUint2x32(data), addr0);
} else if (bit_size == 32) {
ir.WriteShared(bit_size, ir.GetVectorReg(data0), addr0);
} else if (bit_size == 16) { } else if (bit_size == 16) {
const IR::U32 addr0 = ir.IAdd(addr, ir.Imm32(offset)); ir.WriteShared(bit_size, ir.UConvert(16, ir.GetVectorReg(data0)), addr0);
ir.WriteShared(bit_size, ir.GetVectorReg(data0), addr0); }
} else {
const IR::U32 addr0 = ir.IAdd(addr, ir.Imm32(offset));
ir.WriteShared(bit_size, ir.GetVectorReg(data0), addr0);
} }
} }
@ -264,7 +268,7 @@ void Translator::DS_INC_U32(const GcnInst& inst, bool rtn) {
const IR::U32 offset = const IR::U32 offset =
ir.Imm32((u32(inst.control.ds.offset1) << 8u) + u32(inst.control.ds.offset0)); ir.Imm32((u32(inst.control.ds.offset1) << 8u) + u32(inst.control.ds.offset0));
const IR::U32 addr_offset = ir.IAdd(addr, offset); const IR::U32 addr_offset = ir.IAdd(addr, offset);
const IR::Value original_val = ir.SharedAtomicIIncrement(addr_offset); const IR::Value original_val = ir.SharedAtomicInc(addr_offset);
if (rtn) { if (rtn) {
SetDst(inst.dst[0], IR::U32{original_val}); SetDst(inst.dst[0], IR::U32{original_val});
} }
@ -275,7 +279,7 @@ void Translator::DS_DEC_U32(const GcnInst& inst, bool rtn) {
const IR::U32 offset = const IR::U32 offset =
ir.Imm32((u32(inst.control.ds.offset1) << 8u) + u32(inst.control.ds.offset0)); ir.Imm32((u32(inst.control.ds.offset1) << 8u) + u32(inst.control.ds.offset0));
const IR::U32 addr_offset = ir.IAdd(addr, offset); const IR::U32 addr_offset = ir.IAdd(addr, offset);
const IR::Value original_val = ir.SharedAtomicIDecrement(addr_offset); const IR::Value original_val = ir.SharedAtomicDec(addr_offset);
if (rtn) { if (rtn) {
SetDst(inst.dst[0], IR::U32{original_val}); SetDst(inst.dst[0], IR::U32{original_val});
} }
@ -309,36 +313,38 @@ void Translator::DS_READ(int bit_size, bool is_signed, bool is_pair, bool stride
const u32 adj = (bit_size == 32 ? 4 : 8) * (stride64 ? 64 : 1); const u32 adj = (bit_size == 32 ? 4 : 8) * (stride64 ? 64 : 1);
const IR::U32 addr0 = ir.IAdd(addr, ir.Imm32(u32(inst.control.ds.offset0 * adj))); const IR::U32 addr0 = ir.IAdd(addr, ir.Imm32(u32(inst.control.ds.offset0 * adj)));
const IR::Value data0 = ir.LoadShared(bit_size, is_signed, addr0); const IR::Value data0 = ir.LoadShared(bit_size, is_signed, addr0);
if (bit_size == 32) { if (bit_size == 64) {
ir.SetVectorReg(dst_reg++, IR::U32{data0});
} else {
const auto vector = ir.UnpackUint2x32(IR::U64{data0}); const auto vector = ir.UnpackUint2x32(IR::U64{data0});
ir.SetVectorReg(dst_reg++, IR::U32{ir.CompositeExtract(vector, 0)}); ir.SetVectorReg(dst_reg++, IR::U32{ir.CompositeExtract(vector, 0)});
ir.SetVectorReg(dst_reg++, IR::U32{ir.CompositeExtract(vector, 1)}); ir.SetVectorReg(dst_reg++, IR::U32{ir.CompositeExtract(vector, 1)});
} else if (bit_size == 32) {
ir.SetVectorReg(dst_reg++, IR::U32{data0});
} else if (bit_size == 16) {
ir.SetVectorReg(dst_reg++, IR::U32{ir.UConvert(32, IR::U16{data0})});
} }
const IR::U32 addr1 = ir.IAdd(addr, ir.Imm32(u32(inst.control.ds.offset1 * adj))); const IR::U32 addr1 = ir.IAdd(addr, ir.Imm32(u32(inst.control.ds.offset1 * adj)));
const IR::Value data1 = ir.LoadShared(bit_size, is_signed, addr1); const IR::Value data1 = ir.LoadShared(bit_size, is_signed, addr1);
if (bit_size == 32) { if (bit_size == 64) {
ir.SetVectorReg(dst_reg++, IR::U32{data1});
} else {
const auto vector = ir.UnpackUint2x32(IR::U64{data1}); const auto vector = ir.UnpackUint2x32(IR::U64{data1});
ir.SetVectorReg(dst_reg++, IR::U32{ir.CompositeExtract(vector, 0)}); ir.SetVectorReg(dst_reg++, IR::U32{ir.CompositeExtract(vector, 0)});
ir.SetVectorReg(dst_reg++, IR::U32{ir.CompositeExtract(vector, 1)}); ir.SetVectorReg(dst_reg++, IR::U32{ir.CompositeExtract(vector, 1)});
} else if (bit_size == 32) {
ir.SetVectorReg(dst_reg++, IR::U32{data1});
} else if (bit_size == 16) {
ir.SetVectorReg(dst_reg++, IR::U32{ir.UConvert(32, IR::U16{data1})});
} }
} else if (bit_size == 64) { } else {
const IR::U32 addr0 = ir.IAdd(addr, ir.Imm32(offset)); const IR::U32 addr0 = ir.IAdd(addr, ir.Imm32(offset));
const IR::Value data = ir.LoadShared(bit_size, is_signed, addr0); const IR::Value data = ir.LoadShared(bit_size, is_signed, addr0);
if (bit_size == 64) {
const auto vector = ir.UnpackUint2x32(IR::U64{data}); const auto vector = ir.UnpackUint2x32(IR::U64{data});
ir.SetVectorReg(dst_reg, IR::U32{ir.CompositeExtract(vector, 0)}); ir.SetVectorReg(dst_reg, IR::U32{ir.CompositeExtract(vector, 0)});
ir.SetVectorReg(dst_reg + 1, IR::U32{ir.CompositeExtract(vector, 1)}); ir.SetVectorReg(dst_reg + 1, IR::U32{ir.CompositeExtract(vector, 1)});
} else if (bit_size == 32) {
ir.SetVectorReg(dst_reg, IR::U32{data});
} else if (bit_size == 16) { } else if (bit_size == 16) {
const IR::U32 addr0 = ir.IAdd(addr, ir.Imm32(offset)); ir.SetVectorReg(dst_reg++, IR::U32{ir.UConvert(32, IR::U16{data})});
const IR::U16 data = IR::U16{ir.LoadShared(bit_size, is_signed, addr0)}; }
ir.SetVectorReg(dst_reg, ir.UConvert(32, data));
} else {
const IR::U32 addr0 = ir.IAdd(addr, ir.Imm32(offset));
const IR::U32 data = IR::U32{ir.LoadShared(bit_size, is_signed, addr0)};
ir.SetVectorReg(dst_reg, data);
} }
} }

View File

@ -22,15 +22,17 @@ void Translator::EmitVectorInterpolation(const GcnInst& inst) {
// VINTRP // VINTRP
void Translator::V_INTERP_P2_F32(const GcnInst& inst) { void Translator::V_INTERP_P2_F32(const GcnInst& inst) {
const auto& attr = runtime_info.fs_info.inputs.at(inst.control.vintrp.attr); const u32 attr_index = inst.control.vintrp.attr;
info.interp_qualifiers[attr.param_index] = vgpr_to_interp[inst.src[0].code]; const auto& attr = runtime_info.fs_info.inputs.at(attr_index);
const IR::Attribute attrib{IR::Attribute::Param0 + attr.param_index}; info.interp_qualifiers[attr_index] = vgpr_to_interp[inst.src[0].code];
const IR::Attribute attrib{IR::Attribute::Param0 + attr_index};
SetDst(inst.dst[0], ir.GetAttribute(attrib, inst.control.vintrp.chan)); SetDst(inst.dst[0], ir.GetAttribute(attrib, inst.control.vintrp.chan));
} }
void Translator::V_INTERP_MOV_F32(const GcnInst& inst) { void Translator::V_INTERP_MOV_F32(const GcnInst& inst) {
const auto& attr = runtime_info.fs_info.inputs.at(inst.control.vintrp.attr); const u32 attr_index = inst.control.vintrp.attr;
const IR::Attribute attrib{IR::Attribute::Param0 + attr.param_index}; const auto& attr = runtime_info.fs_info.inputs.at(attr_index);
const IR::Attribute attrib{IR::Attribute::Param0 + attr_index};
SetDst(inst.dst[0], ir.GetAttribute(attrib, inst.control.vintrp.chan)); SetDst(inst.dst[0], ir.GetAttribute(attrib, inst.control.vintrp.chan));
} }

View File

@ -354,9 +354,9 @@ void Translator::BUFFER_ATOMIC(AtomicOp op, const GcnInst& inst) {
case AtomicOp::Xor: case AtomicOp::Xor:
return ir.BufferAtomicXor(handle, address, vdata_val, buffer_info); return ir.BufferAtomicXor(handle, address, vdata_val, buffer_info);
case AtomicOp::Inc: case AtomicOp::Inc:
return ir.BufferAtomicInc(handle, address, vdata_val, buffer_info); return ir.BufferAtomicInc(handle, address, buffer_info);
case AtomicOp::Dec: case AtomicOp::Dec:
return ir.BufferAtomicDec(handle, address, vdata_val, buffer_info); return ir.BufferAtomicDec(handle, address, buffer_info);
default: default:
UNREACHABLE(); UNREACHABLE();
} }

View File

@ -214,7 +214,7 @@ struct Info {
bool uses_lane_id{}; bool uses_lane_id{};
bool uses_group_quad{}; bool uses_group_quad{};
bool uses_group_ballot{}; bool uses_group_ballot{};
bool uses_shared{}; IR::Type shared_types{};
bool uses_fp16{}; bool uses_fp16{};
bool uses_fp64{}; bool uses_fp64{};
bool uses_pack_10_11_11{}; bool uses_pack_10_11_11{};

View File

@ -353,12 +353,12 @@ U32 IREmitter::SharedAtomicXor(const U32& address, const U32& data) {
return Inst<U32>(Opcode::SharedAtomicXor32, address, data); return Inst<U32>(Opcode::SharedAtomicXor32, address, data);
} }
U32 IREmitter::SharedAtomicIIncrement(const U32& address) { U32 IREmitter::SharedAtomicInc(const U32& address) {
return Inst<U32>(Opcode::SharedAtomicIIncrement32, address); return Inst<U32>(Opcode::SharedAtomicInc32, address);
} }
U32 IREmitter::SharedAtomicIDecrement(const U32& address) { U32 IREmitter::SharedAtomicDec(const U32& address) {
return Inst<U32>(Opcode::SharedAtomicIDecrement32, address); return Inst<U32>(Opcode::SharedAtomicDec32, address);
} }
U32 IREmitter::SharedAtomicISub(const U32& address, const U32& data) { U32 IREmitter::SharedAtomicISub(const U32& address, const U32& data) {
@ -373,12 +373,12 @@ U32 IREmitter::ReadConstBuffer(const Value& handle, const U32& index) {
return Inst<U32>(Opcode::ReadConstBuffer, handle, index); return Inst<U32>(Opcode::ReadConstBuffer, handle, index);
} }
U32 IREmitter::LoadBufferU8(const Value& handle, const Value& address, BufferInstInfo info) { U8 IREmitter::LoadBufferU8(const Value& handle, const Value& address, BufferInstInfo info) {
return Inst<U32>(Opcode::LoadBufferU8, Flags{info}, handle, address); return Inst<U8>(Opcode::LoadBufferU8, Flags{info}, handle, address);
} }
U32 IREmitter::LoadBufferU16(const Value& handle, const Value& address, BufferInstInfo info) { U16 IREmitter::LoadBufferU16(const Value& handle, const Value& address, BufferInstInfo info) {
return Inst<U32>(Opcode::LoadBufferU16, Flags{info}, handle, address); return Inst<U16>(Opcode::LoadBufferU16, Flags{info}, handle, address);
} }
Value IREmitter::LoadBufferU32(int num_dwords, const Value& handle, const Value& address, Value IREmitter::LoadBufferU32(int num_dwords, const Value& handle, const Value& address,
@ -397,6 +397,10 @@ Value IREmitter::LoadBufferU32(int num_dwords, const Value& handle, const Value&
} }
} }
U64 IREmitter::LoadBufferU64(const Value& handle, const Value& address, BufferInstInfo info) {
return Inst<U64>(Opcode::LoadBufferU64, Flags{info}, handle, address);
}
Value IREmitter::LoadBufferF32(int num_dwords, const Value& handle, const Value& address, Value IREmitter::LoadBufferF32(int num_dwords, const Value& handle, const Value& address,
BufferInstInfo info) { BufferInstInfo info) {
switch (num_dwords) { switch (num_dwords) {
@ -417,12 +421,12 @@ Value IREmitter::LoadBufferFormat(const Value& handle, const Value& address, Buf
return Inst(Opcode::LoadBufferFormatF32, Flags{info}, handle, address); return Inst(Opcode::LoadBufferFormatF32, Flags{info}, handle, address);
} }
void IREmitter::StoreBufferU8(const Value& handle, const Value& address, const U32& data, void IREmitter::StoreBufferU8(const Value& handle, const Value& address, const U8& data,
BufferInstInfo info) { BufferInstInfo info) {
Inst(Opcode::StoreBufferU8, Flags{info}, handle, address, data); Inst(Opcode::StoreBufferU8, Flags{info}, handle, address, data);
} }
void IREmitter::StoreBufferU16(const Value& handle, const Value& address, const U32& data, void IREmitter::StoreBufferU16(const Value& handle, const Value& address, const U16& data,
BufferInstInfo info) { BufferInstInfo info) {
Inst(Opcode::StoreBufferU16, Flags{info}, handle, address, data); Inst(Opcode::StoreBufferU16, Flags{info}, handle, address, data);
} }
@ -447,6 +451,11 @@ void IREmitter::StoreBufferU32(int num_dwords, const Value& handle, const Value&
} }
} }
void IREmitter::StoreBufferU64(const Value& handle, const Value& address, const U64& data,
BufferInstInfo info) {
Inst(Opcode::StoreBufferU64, Flags{info}, handle, address, data);
}
void IREmitter::StoreBufferF32(int num_dwords, const Value& handle, const Value& address, void IREmitter::StoreBufferF32(int num_dwords, const Value& handle, const Value& address,
const Value& data, BufferInstInfo info) { const Value& data, BufferInstInfo info) {
switch (num_dwords) { switch (num_dwords) {
@ -474,7 +483,19 @@ void IREmitter::StoreBufferFormat(const Value& handle, const Value& address, con
Value IREmitter::BufferAtomicIAdd(const Value& handle, const Value& address, const Value& value, Value IREmitter::BufferAtomicIAdd(const Value& handle, const Value& address, const Value& value,
BufferInstInfo info) { BufferInstInfo info) {
switch (value.Type()) {
case Type::U32:
return Inst(Opcode::BufferAtomicIAdd32, Flags{info}, handle, address, value); return Inst(Opcode::BufferAtomicIAdd32, Flags{info}, handle, address, value);
case Type::U64:
return Inst(Opcode::BufferAtomicIAdd64, Flags{info}, handle, address, value);
default:
ThrowInvalidType(value.Type());
}
}
Value IREmitter::BufferAtomicISub(const Value& handle, const Value& address, const Value& value,
BufferInstInfo info) {
return Inst(Opcode::BufferAtomicISub32, Flags{info}, handle, address, value);
} }
Value IREmitter::BufferAtomicIMin(const Value& handle, const Value& address, const Value& value, Value IREmitter::BufferAtomicIMin(const Value& handle, const Value& address, const Value& value,
@ -489,14 +510,12 @@ Value IREmitter::BufferAtomicIMax(const Value& handle, const Value& address, con
: Inst(Opcode::BufferAtomicUMax32, Flags{info}, handle, address, value); : Inst(Opcode::BufferAtomicUMax32, Flags{info}, handle, address, value);
} }
Value IREmitter::BufferAtomicInc(const Value& handle, const Value& address, const Value& value, Value IREmitter::BufferAtomicInc(const Value& handle, const Value& address, BufferInstInfo info) {
BufferInstInfo info) { return Inst(Opcode::BufferAtomicInc32, Flags{info}, handle, address);
return Inst(Opcode::BufferAtomicInc32, Flags{info}, handle, address, value);
} }
Value IREmitter::BufferAtomicDec(const Value& handle, const Value& address, const Value& value, Value IREmitter::BufferAtomicDec(const Value& handle, const Value& address, BufferInstInfo info) {
BufferInstInfo info) { return Inst(Opcode::BufferAtomicDec32, Flags{info}, handle, address);
return Inst(Opcode::BufferAtomicDec32, Flags{info}, handle, address, value);
} }
Value IREmitter::BufferAtomicAnd(const Value& handle, const Value& address, const Value& value, Value IREmitter::BufferAtomicAnd(const Value& handle, const Value& address, const Value& value,
@ -1804,8 +1823,15 @@ F32F64 IREmitter::ConvertIToF(size_t dest_bitsize, size_t src_bitsize, bool is_s
: ConvertUToF(dest_bitsize, src_bitsize, value); : ConvertUToF(dest_bitsize, src_bitsize, value);
} }
U16U32U64 IREmitter::UConvert(size_t result_bitsize, const U16U32U64& value) { U8U16U32U64 IREmitter::UConvert(size_t result_bitsize, const U8U16U32U64& value) {
switch (result_bitsize) { switch (result_bitsize) {
case 8:
switch (value.Type()) {
case Type::U32:
return Inst<U8>(Opcode::ConvertU8U32, value);
default:
break;
}
case 16: case 16:
switch (value.Type()) { switch (value.Type()) {
case Type::U32: case Type::U32:
@ -1815,6 +1841,8 @@ U16U32U64 IREmitter::UConvert(size_t result_bitsize, const U16U32U64& value) {
} }
case 32: case 32:
switch (value.Type()) { switch (value.Type()) {
case Type::U8:
return Inst<U32>(Opcode::ConvertU32U8, value);
case Type::U16: case Type::U16:
return Inst<U32>(Opcode::ConvertU32U16, value); return Inst<U32>(Opcode::ConvertU32U16, value);
default: default:

View File

@ -100,33 +100,35 @@ public:
void WriteShared(int bit_size, const Value& value, const U32& offset); void WriteShared(int bit_size, const Value& value, const U32& offset);
[[nodiscard]] U32U64 SharedAtomicIAdd(const U32& address, const U32U64& data); [[nodiscard]] U32U64 SharedAtomicIAdd(const U32& address, const U32U64& data);
[[nodiscard]] U32 SharedAtomicISub(const U32& address, const U32& data);
[[nodiscard]] U32 SharedAtomicIMin(const U32& address, const U32& data, bool is_signed); [[nodiscard]] U32 SharedAtomicIMin(const U32& address, const U32& data, bool is_signed);
[[nodiscard]] U32 SharedAtomicIMax(const U32& address, const U32& data, bool is_signed); [[nodiscard]] U32 SharedAtomicIMax(const U32& address, const U32& data, bool is_signed);
[[nodiscard]] U32 SharedAtomicInc(const U32& address);
[[nodiscard]] U32 SharedAtomicDec(const U32& address);
[[nodiscard]] U32 SharedAtomicAnd(const U32& address, const U32& data); [[nodiscard]] U32 SharedAtomicAnd(const U32& address, const U32& data);
[[nodiscard]] U32 SharedAtomicOr(const U32& address, const U32& data); [[nodiscard]] U32 SharedAtomicOr(const U32& address, const U32& data);
[[nodiscard]] U32 SharedAtomicXor(const U32& address, const U32& data); [[nodiscard]] U32 SharedAtomicXor(const U32& address, const U32& data);
[[nodiscard]] U32 SharedAtomicIIncrement(const U32& address);
[[nodiscard]] U32 SharedAtomicIDecrement(const U32& address);
[[nodiscard]] U32 SharedAtomicISub(const U32& address, const U32& data);
[[nodiscard]] U32 ReadConst(const Value& base, const U32& offset); [[nodiscard]] U32 ReadConst(const Value& base, const U32& offset);
[[nodiscard]] U32 ReadConstBuffer(const Value& handle, const U32& index); [[nodiscard]] U32 ReadConstBuffer(const Value& handle, const U32& index);
[[nodiscard]] U32 LoadBufferU8(const Value& handle, const Value& address, BufferInstInfo info); [[nodiscard]] U8 LoadBufferU8(const Value& handle, const Value& address, BufferInstInfo info);
[[nodiscard]] U32 LoadBufferU16(const Value& handle, const Value& address, BufferInstInfo info); [[nodiscard]] U16 LoadBufferU16(const Value& handle, const Value& address, BufferInstInfo info);
[[nodiscard]] Value LoadBufferU32(int num_dwords, const Value& handle, const Value& address, [[nodiscard]] Value LoadBufferU32(int num_dwords, const Value& handle, const Value& address,
BufferInstInfo info); BufferInstInfo info);
[[nodiscard]] U64 LoadBufferU64(const Value& handle, const Value& address, BufferInstInfo info);
[[nodiscard]] Value LoadBufferF32(int num_dwords, const Value& handle, const Value& address, [[nodiscard]] Value LoadBufferF32(int num_dwords, const Value& handle, const Value& address,
BufferInstInfo info); BufferInstInfo info);
[[nodiscard]] Value LoadBufferFormat(const Value& handle, const Value& address, [[nodiscard]] Value LoadBufferFormat(const Value& handle, const Value& address,
BufferInstInfo info); BufferInstInfo info);
void StoreBufferU8(const Value& handle, const Value& address, const U32& data, void StoreBufferU8(const Value& handle, const Value& address, const U8& data,
BufferInstInfo info); BufferInstInfo info);
void StoreBufferU16(const Value& handle, const Value& address, const U32& data, void StoreBufferU16(const Value& handle, const Value& address, const U16& data,
BufferInstInfo info); BufferInstInfo info);
void StoreBufferU32(int num_dwords, const Value& handle, const Value& address, void StoreBufferU32(int num_dwords, const Value& handle, const Value& address,
const Value& data, BufferInstInfo info); const Value& data, BufferInstInfo info);
void StoreBufferU64(const Value& handle, const Value& address, const U64& data,
BufferInstInfo info);
void StoreBufferF32(int num_dwords, const Value& handle, const Value& address, void StoreBufferF32(int num_dwords, const Value& handle, const Value& address,
const Value& data, BufferInstInfo info); const Value& data, BufferInstInfo info);
void StoreBufferFormat(const Value& handle, const Value& address, const Value& data, void StoreBufferFormat(const Value& handle, const Value& address, const Value& data,
@ -134,14 +136,16 @@ public:
[[nodiscard]] Value BufferAtomicIAdd(const Value& handle, const Value& address, [[nodiscard]] Value BufferAtomicIAdd(const Value& handle, const Value& address,
const Value& value, BufferInstInfo info); const Value& value, BufferInstInfo info);
[[nodiscard]] Value BufferAtomicISub(const Value& handle, const Value& address,
const Value& value, BufferInstInfo info);
[[nodiscard]] Value BufferAtomicIMin(const Value& handle, const Value& address, [[nodiscard]] Value BufferAtomicIMin(const Value& handle, const Value& address,
const Value& value, bool is_signed, BufferInstInfo info); const Value& value, bool is_signed, BufferInstInfo info);
[[nodiscard]] Value BufferAtomicIMax(const Value& handle, const Value& address, [[nodiscard]] Value BufferAtomicIMax(const Value& handle, const Value& address,
const Value& value, bool is_signed, BufferInstInfo info); const Value& value, bool is_signed, BufferInstInfo info);
[[nodiscard]] Value BufferAtomicInc(const Value& handle, const Value& address, [[nodiscard]] Value BufferAtomicInc(const Value& handle, const Value& address,
const Value& value, BufferInstInfo info); BufferInstInfo info);
[[nodiscard]] Value BufferAtomicDec(const Value& handle, const Value& address, [[nodiscard]] Value BufferAtomicDec(const Value& handle, const Value& address,
const Value& value, BufferInstInfo info); BufferInstInfo info);
[[nodiscard]] Value BufferAtomicAnd(const Value& handle, const Value& address, [[nodiscard]] Value BufferAtomicAnd(const Value& handle, const Value& address,
const Value& value, BufferInstInfo info); const Value& value, BufferInstInfo info);
[[nodiscard]] Value BufferAtomicOr(const Value& handle, const Value& address, [[nodiscard]] Value BufferAtomicOr(const Value& handle, const Value& address,
@ -309,7 +313,7 @@ public:
[[nodiscard]] F32F64 ConvertIToF(size_t dest_bitsize, size_t src_bitsize, bool is_signed, [[nodiscard]] F32F64 ConvertIToF(size_t dest_bitsize, size_t src_bitsize, bool is_signed,
const Value& value); const Value& value);
[[nodiscard]] U16U32U64 UConvert(size_t result_bitsize, const U16U32U64& value); [[nodiscard]] U8U16U32U64 UConvert(size_t result_bitsize, const U8U16U32U64& value);
[[nodiscard]] F16F32F64 FPConvert(size_t result_bitsize, const F16F32F64& value); [[nodiscard]] F16F32F64 FPConvert(size_t result_bitsize, const F16F32F64& value);
[[nodiscard]] Value ImageAtomicIAdd(const Value& handle, const Value& coords, [[nodiscard]] Value ImageAtomicIAdd(const Value& handle, const Value& coords,

View File

@ -60,12 +60,15 @@ bool Inst::MayHaveSideEffects() const noexcept {
case Opcode::StoreBufferU32x2: case Opcode::StoreBufferU32x2:
case Opcode::StoreBufferU32x3: case Opcode::StoreBufferU32x3:
case Opcode::StoreBufferU32x4: case Opcode::StoreBufferU32x4:
case Opcode::StoreBufferU64:
case Opcode::StoreBufferF32: case Opcode::StoreBufferF32:
case Opcode::StoreBufferF32x2: case Opcode::StoreBufferF32x2:
case Opcode::StoreBufferF32x3: case Opcode::StoreBufferF32x3:
case Opcode::StoreBufferF32x4: case Opcode::StoreBufferF32x4:
case Opcode::StoreBufferFormatF32: case Opcode::StoreBufferFormatF32:
case Opcode::BufferAtomicIAdd32: case Opcode::BufferAtomicIAdd32:
case Opcode::BufferAtomicIAdd64:
case Opcode::BufferAtomicISub32:
case Opcode::BufferAtomicSMin32: case Opcode::BufferAtomicSMin32:
case Opcode::BufferAtomicUMin32: case Opcode::BufferAtomicUMin32:
case Opcode::BufferAtomicSMax32: case Opcode::BufferAtomicSMax32:
@ -76,15 +79,21 @@ bool Inst::MayHaveSideEffects() const noexcept {
case Opcode::BufferAtomicOr32: case Opcode::BufferAtomicOr32:
case Opcode::BufferAtomicXor32: case Opcode::BufferAtomicXor32:
case Opcode::BufferAtomicSwap32: case Opcode::BufferAtomicSwap32:
case Opcode::BufferAtomicCmpSwap32:
case Opcode::DataAppend: case Opcode::DataAppend:
case Opcode::DataConsume: case Opcode::DataConsume:
case Opcode::WriteSharedU64: case Opcode::WriteSharedU16:
case Opcode::WriteSharedU32: case Opcode::WriteSharedU32:
case Opcode::WriteSharedU64:
case Opcode::SharedAtomicIAdd32: case Opcode::SharedAtomicIAdd32:
case Opcode::SharedAtomicIAdd64:
case Opcode::SharedAtomicISub32:
case Opcode::SharedAtomicSMin32: case Opcode::SharedAtomicSMin32:
case Opcode::SharedAtomicUMin32: case Opcode::SharedAtomicUMin32:
case Opcode::SharedAtomicSMax32: case Opcode::SharedAtomicSMax32:
case Opcode::SharedAtomicUMax32: case Opcode::SharedAtomicUMax32:
case Opcode::SharedAtomicInc32:
case Opcode::SharedAtomicDec32:
case Opcode::SharedAtomicAnd32: case Opcode::SharedAtomicAnd32:
case Opcode::SharedAtomicOr32: case Opcode::SharedAtomicOr32:
case Opcode::SharedAtomicXor32: case Opcode::SharedAtomicXor32:

View File

@ -40,16 +40,16 @@ OPCODE(WriteSharedU64, Void, U32,
// Shared atomic operations // Shared atomic operations
OPCODE(SharedAtomicIAdd32, U32, U32, U32, ) OPCODE(SharedAtomicIAdd32, U32, U32, U32, )
OPCODE(SharedAtomicIAdd64, U64, U32, U64, ) OPCODE(SharedAtomicIAdd64, U64, U32, U64, )
OPCODE(SharedAtomicISub32, U32, U32, U32, )
OPCODE(SharedAtomicSMin32, U32, U32, U32, ) OPCODE(SharedAtomicSMin32, U32, U32, U32, )
OPCODE(SharedAtomicUMin32, U32, U32, U32, ) OPCODE(SharedAtomicUMin32, U32, U32, U32, )
OPCODE(SharedAtomicSMax32, U32, U32, U32, ) OPCODE(SharedAtomicSMax32, U32, U32, U32, )
OPCODE(SharedAtomicUMax32, U32, U32, U32, ) OPCODE(SharedAtomicUMax32, U32, U32, U32, )
OPCODE(SharedAtomicInc32, U32, U32, )
OPCODE(SharedAtomicDec32, U32, U32, )
OPCODE(SharedAtomicAnd32, U32, U32, U32, ) OPCODE(SharedAtomicAnd32, U32, U32, U32, )
OPCODE(SharedAtomicOr32, U32, U32, U32, ) OPCODE(SharedAtomicOr32, U32, U32, U32, )
OPCODE(SharedAtomicXor32, U32, U32, U32, ) OPCODE(SharedAtomicXor32, U32, U32, U32, )
OPCODE(SharedAtomicISub32, U32, U32, U32, )
OPCODE(SharedAtomicIIncrement32, U32, U32, )
OPCODE(SharedAtomicIDecrement32, U32, U32, )
// Context getters/setters // Context getters/setters
OPCODE(GetUserData, U32, ScalarReg, ) OPCODE(GetUserData, U32, ScalarReg, )
@ -94,23 +94,25 @@ OPCODE(UndefU32, U32,
OPCODE(UndefU64, U64, ) OPCODE(UndefU64, U64, )
// Buffer operations // Buffer operations
OPCODE(LoadBufferU8, U32, Opaque, Opaque, ) OPCODE(LoadBufferU8, U8, Opaque, Opaque, )
OPCODE(LoadBufferU16, U32, Opaque, Opaque, ) OPCODE(LoadBufferU16, U16, Opaque, Opaque, )
OPCODE(LoadBufferU32, U32, Opaque, Opaque, ) OPCODE(LoadBufferU32, U32, Opaque, Opaque, )
OPCODE(LoadBufferU32x2, U32x2, Opaque, Opaque, ) OPCODE(LoadBufferU32x2, U32x2, Opaque, Opaque, )
OPCODE(LoadBufferU32x3, U32x3, Opaque, Opaque, ) OPCODE(LoadBufferU32x3, U32x3, Opaque, Opaque, )
OPCODE(LoadBufferU32x4, U32x4, Opaque, Opaque, ) OPCODE(LoadBufferU32x4, U32x4, Opaque, Opaque, )
OPCODE(LoadBufferU64, U64, Opaque, Opaque, )
OPCODE(LoadBufferF32, F32, Opaque, Opaque, ) OPCODE(LoadBufferF32, F32, Opaque, Opaque, )
OPCODE(LoadBufferF32x2, F32x2, Opaque, Opaque, ) OPCODE(LoadBufferF32x2, F32x2, Opaque, Opaque, )
OPCODE(LoadBufferF32x3, F32x3, Opaque, Opaque, ) OPCODE(LoadBufferF32x3, F32x3, Opaque, Opaque, )
OPCODE(LoadBufferF32x4, F32x4, Opaque, Opaque, ) OPCODE(LoadBufferF32x4, F32x4, Opaque, Opaque, )
OPCODE(LoadBufferFormatF32, F32x4, Opaque, Opaque, ) OPCODE(LoadBufferFormatF32, F32x4, Opaque, Opaque, )
OPCODE(StoreBufferU8, Void, Opaque, Opaque, U32, ) OPCODE(StoreBufferU8, Void, Opaque, Opaque, U8, )
OPCODE(StoreBufferU16, Void, Opaque, Opaque, U32, ) OPCODE(StoreBufferU16, Void, Opaque, Opaque, U16, )
OPCODE(StoreBufferU32, Void, Opaque, Opaque, U32, ) OPCODE(StoreBufferU32, Void, Opaque, Opaque, U32, )
OPCODE(StoreBufferU32x2, Void, Opaque, Opaque, U32x2, ) OPCODE(StoreBufferU32x2, Void, Opaque, Opaque, U32x2, )
OPCODE(StoreBufferU32x3, Void, Opaque, Opaque, U32x3, ) OPCODE(StoreBufferU32x3, Void, Opaque, Opaque, U32x3, )
OPCODE(StoreBufferU32x4, Void, Opaque, Opaque, U32x4, ) OPCODE(StoreBufferU32x4, Void, Opaque, Opaque, U32x4, )
OPCODE(StoreBufferU64, Void, Opaque, Opaque, U64, )
OPCODE(StoreBufferF32, Void, Opaque, Opaque, F32, ) OPCODE(StoreBufferF32, Void, Opaque, Opaque, F32, )
OPCODE(StoreBufferF32x2, Void, Opaque, Opaque, F32x2, ) OPCODE(StoreBufferF32x2, Void, Opaque, Opaque, F32x2, )
OPCODE(StoreBufferF32x3, Void, Opaque, Opaque, F32x3, ) OPCODE(StoreBufferF32x3, Void, Opaque, Opaque, F32x3, )
@ -120,12 +122,13 @@ OPCODE(StoreBufferFormatF32, Void, Opaq
// Buffer atomic operations // Buffer atomic operations
OPCODE(BufferAtomicIAdd32, U32, Opaque, Opaque, U32 ) OPCODE(BufferAtomicIAdd32, U32, Opaque, Opaque, U32 )
OPCODE(BufferAtomicIAdd64, U64, Opaque, Opaque, U64 ) OPCODE(BufferAtomicIAdd64, U64, Opaque, Opaque, U64 )
OPCODE(BufferAtomicISub32, U32, Opaque, Opaque, U32 )
OPCODE(BufferAtomicSMin32, U32, Opaque, Opaque, U32 ) OPCODE(BufferAtomicSMin32, U32, Opaque, Opaque, U32 )
OPCODE(BufferAtomicUMin32, U32, Opaque, Opaque, U32 ) OPCODE(BufferAtomicUMin32, U32, Opaque, Opaque, U32 )
OPCODE(BufferAtomicSMax32, U32, Opaque, Opaque, U32 ) OPCODE(BufferAtomicSMax32, U32, Opaque, Opaque, U32 )
OPCODE(BufferAtomicUMax32, U32, Opaque, Opaque, U32 ) OPCODE(BufferAtomicUMax32, U32, Opaque, Opaque, U32 )
OPCODE(BufferAtomicInc32, U32, Opaque, Opaque, U32, ) OPCODE(BufferAtomicInc32, U32, Opaque, Opaque, )
OPCODE(BufferAtomicDec32, U32, Opaque, Opaque, U32, ) OPCODE(BufferAtomicDec32, U32, Opaque, Opaque, )
OPCODE(BufferAtomicAnd32, U32, Opaque, Opaque, U32, ) OPCODE(BufferAtomicAnd32, U32, Opaque, Opaque, U32, )
OPCODE(BufferAtomicOr32, U32, Opaque, Opaque, U32, ) OPCODE(BufferAtomicOr32, U32, Opaque, Opaque, U32, )
OPCODE(BufferAtomicXor32, U32, Opaque, Opaque, U32, ) OPCODE(BufferAtomicXor32, U32, Opaque, Opaque, U32, )
@ -405,6 +408,8 @@ OPCODE(ConvertF64U32, F64, U32,
OPCODE(ConvertF32U16, F32, U16, ) OPCODE(ConvertF32U16, F32, U16, )
OPCODE(ConvertU16U32, U16, U32, ) OPCODE(ConvertU16U32, U16, U32, )
OPCODE(ConvertU32U16, U32, U16, ) OPCODE(ConvertU32U16, U32, U16, )
OPCODE(ConvertU8U32, U8, U32, )
OPCODE(ConvertU32U8, U32, U8, )
// Image operations // Image operations
OPCODE(ImageSampleRaw, F32x4, Opaque, F32x4, F32x4, F32x4, F32, ) OPCODE(ImageSampleRaw, F32x4, Opaque, F32x4, F32x4, F32x4, F32, )

View File

@ -438,7 +438,9 @@ void HullShaderTransform(IR::Program& program, RuntimeInfo& runtime_info) {
IR::IREmitter ir{*block, IR::Block::InstructionList::s_iterator_to(inst)}; IR::IREmitter ir{*block, IR::Block::InstructionList::s_iterator_to(inst)};
const u32 num_dwords = opcode == IR::Opcode::WriteSharedU32 ? 1 : 2; const u32 num_dwords = opcode == IR::Opcode::WriteSharedU32 ? 1 : 2;
const IR::U32 addr{inst.Arg(0)}; const IR::U32 addr{inst.Arg(0)};
const IR::U32 data{inst.Arg(1).Resolve()}; const IR::Value data = num_dwords == 2
? ir.UnpackUint2x32(IR::U64{inst.Arg(1).Resolve()})
: inst.Arg(1).Resolve();
const auto SetOutput = [&](IR::U32 addr, IR::U32 value, AttributeRegion output_kind, const auto SetOutput = [&](IR::U32 addr, IR::U32 value, AttributeRegion output_kind,
u32 off_dw) { u32 off_dw) {
@ -466,10 +468,10 @@ void HullShaderTransform(IR::Program& program, RuntimeInfo& runtime_info) {
AttributeRegion region = GetAttributeRegionKind(&inst, info, runtime_info); AttributeRegion region = GetAttributeRegionKind(&inst, info, runtime_info);
if (num_dwords == 1) { if (num_dwords == 1) {
SetOutput(addr, data, region, 0); SetOutput(addr, IR::U32{data}, region, 0);
} else { } else {
for (auto i = 0; i < num_dwords; i++) { for (auto i = 0; i < num_dwords; i++) {
SetOutput(addr, IR::U32{data.Inst()->Arg(i)}, region, i); SetOutput(addr, IR::U32{ir.CompositeExtract(data, i)}, region, i);
} }
} }
inst.Invalidate(); inst.Invalidate();
@ -499,7 +501,7 @@ void HullShaderTransform(IR::Program& program, RuntimeInfo& runtime_info) {
ReadTessControlPointAttribute(addr, stride, ir, i, is_tcs_output_read); ReadTessControlPointAttribute(addr, stride, ir, i, is_tcs_output_read);
read_components.push_back(ir.BitCast<IR::U32>(component)); read_components.push_back(ir.BitCast<IR::U32>(component));
} }
attr_read = ir.CompositeConstruct(read_components); attr_read = ir.PackUint2x32(ir.CompositeConstruct(read_components));
} }
inst.ReplaceUsesWithAndRemove(attr_read); inst.ReplaceUsesWithAndRemove(attr_read);
break; break;
@ -578,7 +580,7 @@ void DomainShaderTransform(IR::Program& program, RuntimeInfo& runtime_info) {
const IR::F32 component = GetInput(addr, i); const IR::F32 component = GetInput(addr, i);
read_components.push_back(ir.BitCast<IR::U32>(component)); read_components.push_back(ir.BitCast<IR::U32>(component));
} }
attr_read = ir.CompositeConstruct(read_components); attr_read = ir.PackUint2x32(ir.CompositeConstruct(read_components));
} }
inst.ReplaceUsesWithAndRemove(attr_read); inst.ReplaceUsesWithAndRemove(attr_read);
break; break;

View File

@ -28,6 +28,7 @@ void HullShaderTransform(IR::Program& program, RuntimeInfo& runtime_info);
void DomainShaderTransform(IR::Program& program, RuntimeInfo& runtime_info); void DomainShaderTransform(IR::Program& program, RuntimeInfo& runtime_info);
void SharedMemoryBarrierPass(IR::Program& program, const RuntimeInfo& runtime_info, void SharedMemoryBarrierPass(IR::Program& program, const RuntimeInfo& runtime_info,
const Profile& profile); const Profile& profile);
void SharedMemorySimplifyPass(IR::Program& program, const Profile& profile);
void SharedMemoryToStoragePass(IR::Program& program, const RuntimeInfo& runtime_info, void SharedMemoryToStoragePass(IR::Program& program, const RuntimeInfo& runtime_info,
const Profile& profile); const Profile& profile);

View File

@ -34,13 +34,13 @@ static IR::Value LoadBufferFormat(IR::IREmitter& ir, const IR::Value handle, con
interpreted = ir.Imm32(0.f); interpreted = ir.Imm32(0.f);
break; break;
case AmdGpu::DataFormat::Format8: { case AmdGpu::DataFormat::Format8: {
const auto unpacked = const auto raw = ir.UConvert(32, ir.LoadBufferU8(handle, address, info));
ir.Unpack4x8(format_info.num_format, ir.LoadBufferU8(handle, address, info)); const auto unpacked = ir.Unpack4x8(format_info.num_format, raw);
interpreted = ir.CompositeExtract(unpacked, 0); interpreted = ir.CompositeExtract(unpacked, 0);
break; break;
} }
case AmdGpu::DataFormat::Format8_8: { case AmdGpu::DataFormat::Format8_8: {
const auto raw = ir.LoadBufferU16(handle, address, info); const auto raw = ir.UConvert(32, ir.LoadBufferU16(handle, address, info));
const auto unpacked = ir.Unpack4x8(format_info.num_format, raw); const auto unpacked = ir.Unpack4x8(format_info.num_format, raw);
interpreted = ir.CompositeConstruct(ir.CompositeExtract(unpacked, 0), interpreted = ir.CompositeConstruct(ir.CompositeExtract(unpacked, 0),
ir.CompositeExtract(unpacked, 1)); ir.CompositeExtract(unpacked, 1));
@ -51,8 +51,8 @@ static IR::Value LoadBufferFormat(IR::IREmitter& ir, const IR::Value handle, con
IR::U32{ir.LoadBufferU32(1, handle, address, info)}); IR::U32{ir.LoadBufferU32(1, handle, address, info)});
break; break;
case AmdGpu::DataFormat::Format16: { case AmdGpu::DataFormat::Format16: {
const auto unpacked = const auto raw = ir.UConvert(32, ir.LoadBufferU16(handle, address, info));
ir.Unpack2x16(format_info.num_format, ir.LoadBufferU16(handle, address, info)); const auto unpacked = ir.Unpack2x16(format_info.num_format, raw);
interpreted = ir.CompositeExtract(unpacked, 0); interpreted = ir.CompositeExtract(unpacked, 0);
break; break;
} }
@ -126,7 +126,7 @@ static void StoreBufferFormat(IR::IREmitter& ir, const IR::Value handle, const I
const auto packed = const auto packed =
ir.Pack4x8(format_info.num_format, ir.CompositeConstruct(real_value, ir.Imm32(0.f), ir.Pack4x8(format_info.num_format, ir.CompositeConstruct(real_value, ir.Imm32(0.f),
ir.Imm32(0.f), ir.Imm32(0.f))); ir.Imm32(0.f), ir.Imm32(0.f)));
ir.StoreBufferU8(handle, address, packed, info); ir.StoreBufferU8(handle, address, ir.UConvert(8, packed), info);
break; break;
} }
case AmdGpu::DataFormat::Format8_8: { case AmdGpu::DataFormat::Format8_8: {
@ -134,7 +134,7 @@ static void StoreBufferFormat(IR::IREmitter& ir, const IR::Value handle, const I
ir.CompositeConstruct(ir.CompositeExtract(real_value, 0), ir.CompositeConstruct(ir.CompositeExtract(real_value, 0),
ir.CompositeExtract(real_value, 1), ir.CompositeExtract(real_value, 1),
ir.Imm32(0.f), ir.Imm32(0.f))); ir.Imm32(0.f), ir.Imm32(0.f)));
ir.StoreBufferU16(handle, address, packed, info); ir.StoreBufferU16(handle, address, ir.UConvert(16, packed), info);
break; break;
} }
case AmdGpu::DataFormat::Format8_8_8_8: { case AmdGpu::DataFormat::Format8_8_8_8: {
@ -145,7 +145,7 @@ static void StoreBufferFormat(IR::IREmitter& ir, const IR::Value handle, const I
case AmdGpu::DataFormat::Format16: { case AmdGpu::DataFormat::Format16: {
const auto packed = const auto packed =
ir.Pack2x16(format_info.num_format, ir.CompositeConstruct(real_value, ir.Imm32(0.f))); ir.Pack2x16(format_info.num_format, ir.CompositeConstruct(real_value, ir.Imm32(0.f)));
ir.StoreBufferU16(handle, address, packed, info); ir.StoreBufferU16(handle, address, ir.UConvert(16, packed), info);
break; break;
} }
case AmdGpu::DataFormat::Format16_16: { case AmdGpu::DataFormat::Format16_16: {

View File

@ -17,6 +17,8 @@ using SharpLocation = u32;
bool IsBufferAtomic(const IR::Inst& inst) { bool IsBufferAtomic(const IR::Inst& inst) {
switch (inst.GetOpcode()) { switch (inst.GetOpcode()) {
case IR::Opcode::BufferAtomicIAdd32: case IR::Opcode::BufferAtomicIAdd32:
case IR::Opcode::BufferAtomicIAdd64:
case IR::Opcode::BufferAtomicISub32:
case IR::Opcode::BufferAtomicSMin32: case IR::Opcode::BufferAtomicSMin32:
case IR::Opcode::BufferAtomicUMin32: case IR::Opcode::BufferAtomicUMin32:
case IR::Opcode::BufferAtomicSMax32: case IR::Opcode::BufferAtomicSMax32:
@ -27,6 +29,7 @@ bool IsBufferAtomic(const IR::Inst& inst) {
case IR::Opcode::BufferAtomicOr32: case IR::Opcode::BufferAtomicOr32:
case IR::Opcode::BufferAtomicXor32: case IR::Opcode::BufferAtomicXor32:
case IR::Opcode::BufferAtomicSwap32: case IR::Opcode::BufferAtomicSwap32:
case IR::Opcode::BufferAtomicCmpSwap32:
return true; return true;
default: default:
return false; return false;
@ -41,6 +44,7 @@ bool IsBufferStore(const IR::Inst& inst) {
case IR::Opcode::StoreBufferU32x2: case IR::Opcode::StoreBufferU32x2:
case IR::Opcode::StoreBufferU32x3: case IR::Opcode::StoreBufferU32x3:
case IR::Opcode::StoreBufferU32x4: case IR::Opcode::StoreBufferU32x4:
case IR::Opcode::StoreBufferU64:
case IR::Opcode::StoreBufferF32: case IR::Opcode::StoreBufferF32:
case IR::Opcode::StoreBufferF32x2: case IR::Opcode::StoreBufferF32x2:
case IR::Opcode::StoreBufferF32x3: case IR::Opcode::StoreBufferF32x3:
@ -60,6 +64,7 @@ bool IsBufferInstruction(const IR::Inst& inst) {
case IR::Opcode::LoadBufferU32x2: case IR::Opcode::LoadBufferU32x2:
case IR::Opcode::LoadBufferU32x3: case IR::Opcode::LoadBufferU32x3:
case IR::Opcode::LoadBufferU32x4: case IR::Opcode::LoadBufferU32x4:
case IR::Opcode::LoadBufferU64:
case IR::Opcode::LoadBufferF32: case IR::Opcode::LoadBufferF32:
case IR::Opcode::LoadBufferF32x2: case IR::Opcode::LoadBufferF32x2:
case IR::Opcode::LoadBufferF32x3: case IR::Opcode::LoadBufferF32x3:
@ -85,6 +90,10 @@ IR::Type BufferDataType(const IR::Inst& inst, AmdGpu::NumberFormat num_format) {
case IR::Opcode::LoadBufferU16: case IR::Opcode::LoadBufferU16:
case IR::Opcode::StoreBufferU16: case IR::Opcode::StoreBufferU16:
return IR::Type::U16; return IR::Type::U16;
case IR::Opcode::LoadBufferU64:
case IR::Opcode::StoreBufferU64:
case IR::Opcode::BufferAtomicIAdd64:
return IR::Type::U64;
case IR::Opcode::LoadBufferFormatF32: case IR::Opcode::LoadBufferFormatF32:
case IR::Opcode::StoreBufferFormatF32: case IR::Opcode::StoreBufferFormatF32:
// Formatted buffer loads can use a variety of types. // Formatted buffer loads can use a variety of types.

View File

@ -35,12 +35,28 @@ void Visit(Info& info, const IR::Inst& inst) {
break; break;
} }
case IR::Opcode::LoadSharedU16: case IR::Opcode::LoadSharedU16:
case IR::Opcode::LoadSharedU32:
case IR::Opcode::LoadSharedU64:
case IR::Opcode::WriteSharedU16: case IR::Opcode::WriteSharedU16:
info.shared_types |= IR::Type::U16;
break;
case IR::Opcode::LoadSharedU32:
case IR::Opcode::WriteSharedU32: case IR::Opcode::WriteSharedU32:
case IR::Opcode::SharedAtomicIAdd32:
case IR::Opcode::SharedAtomicISub32:
case IR::Opcode::SharedAtomicSMin32:
case IR::Opcode::SharedAtomicUMin32:
case IR::Opcode::SharedAtomicSMax32:
case IR::Opcode::SharedAtomicUMax32:
case IR::Opcode::SharedAtomicInc32:
case IR::Opcode::SharedAtomicDec32:
case IR::Opcode::SharedAtomicAnd32:
case IR::Opcode::SharedAtomicOr32:
case IR::Opcode::SharedAtomicXor32:
info.shared_types |= IR::Type::U32;
break;
case IR::Opcode::LoadSharedU64:
case IR::Opcode::WriteSharedU64: case IR::Opcode::WriteSharedU64:
info.uses_shared = true; case IR::Opcode::SharedAtomicIAdd64:
info.shared_types |= IR::Type::U64;
break; break;
case IR::Opcode::ConvertF16F32: case IR::Opcode::ConvertF16F32:
case IR::Opcode::ConvertF32F16: case IR::Opcode::ConvertF32F16:

View File

@ -1,6 +1,7 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include <unordered_set>
#include "shader_recompiler/ir/breadth_first_search.h" #include "shader_recompiler/ir/breadth_first_search.h"
#include "shader_recompiler/ir/ir_emitter.h" #include "shader_recompiler/ir/ir_emitter.h"
#include "shader_recompiler/ir/program.h" #include "shader_recompiler/ir/program.h"
@ -9,12 +10,14 @@
namespace Shader::Optimization { namespace Shader::Optimization {
static bool IsLoadShared(const IR::Inst& inst) { static bool IsLoadShared(const IR::Inst& inst) {
return inst.GetOpcode() == IR::Opcode::LoadSharedU32 || return inst.GetOpcode() == IR::Opcode::LoadSharedU16 ||
inst.GetOpcode() == IR::Opcode::LoadSharedU32 ||
inst.GetOpcode() == IR::Opcode::LoadSharedU64; inst.GetOpcode() == IR::Opcode::LoadSharedU64;
} }
static bool IsWriteShared(const IR::Inst& inst) { static bool IsWriteShared(const IR::Inst& inst) {
return inst.GetOpcode() == IR::Opcode::WriteSharedU32 || return inst.GetOpcode() == IR::Opcode::WriteSharedU16 ||
inst.GetOpcode() == IR::Opcode::WriteSharedU32 ||
inst.GetOpcode() == IR::Opcode::WriteSharedU64; inst.GetOpcode() == IR::Opcode::WriteSharedU64;
} }
@ -49,11 +52,14 @@ static void EmitBarrierInBlock(IR::Block* block) {
} }
} }
using NodeSet = std::unordered_set<const IR::Block*>;
// Inserts a barrier after divergent conditional blocks to avoid undefined // Inserts a barrier after divergent conditional blocks to avoid undefined
// behavior when some threads write and others read from shared memory. // behavior when some threads write and others read from shared memory.
static void EmitBarrierInMergeBlock(const IR::AbstractSyntaxNode::Data& data) { static void EmitBarrierInMergeBlock(const IR::AbstractSyntaxNode::Data& data,
NodeSet& divergence_end, u32& divergence_depth) {
const IR::U1 cond = data.if_node.cond; const IR::U1 cond = data.if_node.cond;
const auto insert_barrier = const auto is_divergent_cond =
IR::BreadthFirstSearch(cond, [](IR::Inst* inst) -> std::optional<bool> { IR::BreadthFirstSearch(cond, [](IR::Inst* inst) -> std::optional<bool> {
if (inst->GetOpcode() == IR::Opcode::GetAttributeU32 && if (inst->GetOpcode() == IR::Opcode::GetAttributeU32 &&
inst->Arg(0).Attribute() == IR::Attribute::LocalInvocationId) { inst->Arg(0).Attribute() == IR::Attribute::LocalInvocationId) {
@ -61,12 +67,16 @@ static void EmitBarrierInMergeBlock(const IR::AbstractSyntaxNode::Data& data) {
} }
return std::nullopt; return std::nullopt;
}); });
if (insert_barrier) { if (is_divergent_cond) {
if (divergence_depth == 0) {
IR::Block* const merge = data.if_node.merge; IR::Block* const merge = data.if_node.merge;
auto insert_point = std::ranges::find_if_not(merge->Instructions(), IR::IsPhi); auto insert_point = std::ranges::find_if_not(merge->Instructions(), IR::IsPhi);
IR::IREmitter ir{*merge, insert_point}; IR::IREmitter ir{*merge, insert_point};
ir.Barrier(); ir.Barrier();
} }
++divergence_depth;
divergence_end.emplace(data.if_node.merge);
}
} }
static constexpr u32 GcnSubgroupSize = 64; static constexpr u32 GcnSubgroupSize = 64;
@ -87,19 +97,22 @@ void SharedMemoryBarrierPass(IR::Program& program, const RuntimeInfo& runtime_in
return; return;
} }
using Type = IR::AbstractSyntaxNode::Type; using Type = IR::AbstractSyntaxNode::Type;
u32 branch_depth{}; u32 divergence_depth{};
NodeSet divergence_end;
for (const IR::AbstractSyntaxNode& node : program.syntax_list) { for (const IR::AbstractSyntaxNode& node : program.syntax_list) {
if (node.type == Type::EndIf) { if (node.type == Type::EndIf) {
--branch_depth; if (divergence_end.contains(node.data.end_if.merge)) {
--divergence_depth;
}
continue; continue;
} }
// Check if branch depth is zero, we don't want to insert barrier in potentially divergent // Check if branch depth is zero, we don't want to insert barrier in potentially divergent
// code. // code.
if (node.type == Type::If && branch_depth++ == 0) { if (node.type == Type::If) {
EmitBarrierInMergeBlock(node.data); EmitBarrierInMergeBlock(node.data, divergence_end, divergence_depth);
continue; continue;
} }
if (node.type == Type::Block && branch_depth == 0) { if (node.type == Type::Block && divergence_depth == 0) {
EmitBarrierInBlock(node.data.block); EmitBarrierInBlock(node.data.block);
} }
} }

View File

@ -0,0 +1,127 @@
// SPDX-FileCopyrightText: Copyright 2025 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "shader_recompiler/ir/ir_emitter.h"
#include "shader_recompiler/ir/program.h"
#include "shader_recompiler/profile.h"
namespace Shader::Optimization {
static bool Requires16BitSharedAtomic(const IR::Inst& inst) {
// Nothing yet
return false;
}
static bool Requires64BitSharedAtomic(const IR::Inst& inst) {
switch (inst.GetOpcode()) {
case IR::Opcode::SharedAtomicIAdd64:
return true;
default:
return false;
}
}
static bool IsNon32BitSharedLoadStore(const IR::Inst& inst) {
switch (inst.GetOpcode()) {
case IR::Opcode::LoadSharedU16:
case IR::Opcode::LoadSharedU64:
case IR::Opcode::WriteSharedU16:
case IR::Opcode::WriteSharedU64:
return true;
default:
return false;
}
}
IR::Type CalculateSpecialSharedAtomicTypes(IR::Program& program) {
IR::Type extra_atomic_types{IR::Type::Void};
for (IR::Block* const block : program.blocks) {
for (IR::Inst& inst : block->Instructions()) {
if (Requires16BitSharedAtomic(inst)) {
extra_atomic_types |= IR::Type::U16;
}
if (Requires64BitSharedAtomic(inst)) {
extra_atomic_types |= IR::Type::U64;
}
}
}
return extra_atomic_types;
}
// Simplifies down U16 and U64 shared memory operations to U32 when aliasing is not supported and
// atomics of the same type are not used.
void SharedMemorySimplifyPass(IR::Program& program, const Profile& profile) {
if (program.info.stage != Stage::Compute || profile.supports_workgroup_explicit_memory_layout) {
return;
}
const auto atomic_types = CalculateSpecialSharedAtomicTypes(program);
if (True(atomic_types & IR::Type::U16) && True(atomic_types & IR::Type::U64)) {
// If both other atomic types are used, there is nothing to do.
return;
}
// Iterate through shared load/store U16/U64 instructions, replacing with
// equivalent U32 ops when the types are not needed for atomics.
for (IR::Block* const block : program.blocks) {
for (IR::Inst& inst : block->Instructions()) {
if (!IsNon32BitSharedLoadStore(inst)) {
continue;
}
IR::IREmitter ir{*block, IR::Block::InstructionList::s_iterator_to(inst)};
const IR::U32 offset{inst.Arg(0)};
if (False(atomic_types & IR::Type::U16)) {
switch (inst.GetOpcode()) {
case IR::Opcode::LoadSharedU16: {
const IR::U32 dword_offset{ir.BitwiseAnd(offset, ir.Imm32(~3U))};
const IR::U32 dword_value{ir.LoadShared(32, false, dword_offset)};
const IR::U32 bit_offset{
ir.IMul(ir.BitwiseAnd(offset, ir.Imm32(2U)), ir.Imm32(8U))};
const IR::U32 value{ir.BitFieldExtract(dword_value, bit_offset, ir.Imm32(16U))};
inst.ReplaceUsesWithAndRemove(ir.UConvert(16, value));
continue;
}
case IR::Opcode::WriteSharedU16: {
const IR::U32 value{ir.UConvert(32, IR::U16{inst.Arg(1)})};
const IR::U32 bit_offset{
ir.IMul(ir.BitwiseAnd(offset, ir.Imm32(2U)), ir.Imm32(8U))};
const IR::U32 dword_offset{ir.BitwiseAnd(offset, ir.Imm32(~3U))};
const IR::U32 dword_value{
ir.LoadShared(32, false, ir.BitwiseAnd(offset, dword_offset))};
const IR::U32 new_dword_value{
ir.BitFieldInsert(dword_value, value, bit_offset, ir.Imm32(16U))};
ir.WriteShared(32, new_dword_value, dword_offset);
inst.Invalidate();
continue;
}
default:
break;
}
}
if (False(atomic_types & IR::Type::U64)) {
switch (inst.GetOpcode()) {
case IR::Opcode::LoadSharedU64: {
const IR::U32 value0{ir.LoadShared(32, false, offset)};
const IR::U32 value1{ir.LoadShared(32, false, ir.IAdd(offset, ir.Imm32(4U)))};
const IR::Value value{ir.PackUint2x32(ir.CompositeConstruct(value0, value1))};
inst.ReplaceUsesWithAndRemove(value);
continue;
}
case IR::Opcode::WriteSharedU64: {
const IR::Value value{ir.UnpackUint2x32(IR::U64{inst.Arg(1)})};
const IR::U32 value0{ir.CompositeExtract(value, 0)};
const IR::U32 value1{ir.CompositeExtract(value, 1)};
ir.WriteShared(32, value0, offset);
ir.WriteShared(32, value1, ir.IAdd(offset, ir.Imm32(4U)));
inst.Invalidate();
continue;
}
default:
break;
}
}
}
}
}
} // namespace Shader::Optimization

View File

@ -10,18 +10,23 @@ namespace Shader::Optimization {
static bool IsSharedAccess(const IR::Inst& inst) { static bool IsSharedAccess(const IR::Inst& inst) {
const auto opcode = inst.GetOpcode(); const auto opcode = inst.GetOpcode();
switch (opcode) { switch (opcode) {
case IR::Opcode::LoadSharedU16:
case IR::Opcode::LoadSharedU32: case IR::Opcode::LoadSharedU32:
case IR::Opcode::LoadSharedU64: case IR::Opcode::LoadSharedU64:
case IR::Opcode::WriteSharedU16:
case IR::Opcode::WriteSharedU32: case IR::Opcode::WriteSharedU32:
case IR::Opcode::WriteSharedU64: case IR::Opcode::WriteSharedU64:
case IR::Opcode::SharedAtomicAnd32:
case IR::Opcode::SharedAtomicIAdd32: case IR::Opcode::SharedAtomicIAdd32:
case IR::Opcode::SharedAtomicIAdd64: case IR::Opcode::SharedAtomicIAdd64:
case IR::Opcode::SharedAtomicOr32: case IR::Opcode::SharedAtomicISub32:
case IR::Opcode::SharedAtomicSMax32:
case IR::Opcode::SharedAtomicUMax32:
case IR::Opcode::SharedAtomicSMin32: case IR::Opcode::SharedAtomicSMin32:
case IR::Opcode::SharedAtomicUMin32: case IR::Opcode::SharedAtomicUMin32:
case IR::Opcode::SharedAtomicSMax32:
case IR::Opcode::SharedAtomicUMax32:
case IR::Opcode::SharedAtomicInc32:
case IR::Opcode::SharedAtomicDec32:
case IR::Opcode::SharedAtomicAnd32:
case IR::Opcode::SharedAtomicOr32:
case IR::Opcode::SharedAtomicXor32: case IR::Opcode::SharedAtomicXor32:
return true; return true;
default: default:
@ -29,26 +34,74 @@ static bool IsSharedAccess(const IR::Inst& inst) {
} }
} }
IR::Type CalculateSharedMemoryTypes(IR::Program& program) {
IR::Type used_types{IR::Type::Void};
for (IR::Block* const block : program.blocks) {
for (IR::Inst& inst : block->Instructions()) {
if (!IsSharedAccess(inst)) {
continue;
}
switch (inst.GetOpcode()) {
case IR::Opcode::LoadSharedU16:
case IR::Opcode::WriteSharedU16:
used_types |= IR::Type::U16;
break;
case IR::Opcode::LoadSharedU32:
case IR::Opcode::WriteSharedU32:
case IR::Opcode::SharedAtomicIAdd32:
case IR::Opcode::SharedAtomicISub32:
case IR::Opcode::SharedAtomicSMin32:
case IR::Opcode::SharedAtomicUMin32:
case IR::Opcode::SharedAtomicSMax32:
case IR::Opcode::SharedAtomicUMax32:
case IR::Opcode::SharedAtomicInc32:
case IR::Opcode::SharedAtomicDec32:
case IR::Opcode::SharedAtomicAnd32:
case IR::Opcode::SharedAtomicOr32:
case IR::Opcode::SharedAtomicXor32:
used_types |= IR::Type::U32;
break;
case IR::Opcode::LoadSharedU64:
case IR::Opcode::WriteSharedU64:
case IR::Opcode::SharedAtomicIAdd64:
used_types |= IR::Type::U64;
break;
default:
break;
}
}
}
return used_types;
}
void SharedMemoryToStoragePass(IR::Program& program, const RuntimeInfo& runtime_info, void SharedMemoryToStoragePass(IR::Program& program, const RuntimeInfo& runtime_info,
const Profile& profile) { const Profile& profile) {
if (program.info.stage != Stage::Compute) { if (program.info.stage != Stage::Compute) {
return; return;
} }
// Only perform the transform if the host shared memory is insufficient
// or the device does not support VK_KHR_workgroup_memory_explicit_layout // Run this pass if:
// * There are shared memory instructions.
// * One of the following is true:
// * Requested shared memory size is too large for the host shared memory.
// * Workgroup explicit memory is not supported and multiple shared memory types are used.
const u32 shared_memory_size = runtime_info.cs_info.shared_memory_size; const u32 shared_memory_size = runtime_info.cs_info.shared_memory_size;
if (shared_memory_size <= profile.max_shared_memory_size && const auto used_types = CalculateSharedMemoryTypes(program);
profile.supports_workgroup_explicit_memory_layout) { if (used_types == IR::Type::Void || (shared_memory_size <= profile.max_shared_memory_size &&
(profile.supports_workgroup_explicit_memory_layout ||
std::popcount(static_cast<u32>(used_types)) == 1))) {
return; return;
} }
// Add buffer binding for shared memory storage buffer.
// Add a buffer binding for shared memory storage buffer.
const u32 binding = static_cast<u32>(program.info.buffers.size()); const u32 binding = static_cast<u32>(program.info.buffers.size());
program.info.buffers.push_back({ program.info.buffers.push_back({
.used_types = IR::Type::U32, .used_types = used_types,
.inline_cbuf = AmdGpu::Buffer::Null(), .inline_cbuf = AmdGpu::Buffer::Null(),
.buffer_type = BufferType::SharedMemory, .buffer_type = BufferType::SharedMemory,
.is_written = true, .is_written = true,
}); });
for (IR::Block* const block : program.blocks) { for (IR::Block* const block : program.blocks) {
for (IR::Inst& inst : block->Instructions()) { for (IR::Inst& inst : block->Instructions()) {
if (!IsSharedAccess(inst)) { if (!IsSharedAccess(inst)) {
@ -56,47 +109,48 @@ void SharedMemoryToStoragePass(IR::Program& program, const RuntimeInfo& runtime_
} }
IR::IREmitter ir{*block, IR::Block::InstructionList::s_iterator_to(inst)}; IR::IREmitter ir{*block, IR::Block::InstructionList::s_iterator_to(inst)};
const IR::U32 handle = ir.Imm32(binding); const IR::U32 handle = ir.Imm32(binding);
// Replace shared atomics first
switch (inst.GetOpcode()) {
case IR::Opcode::SharedAtomicAnd32:
inst.ReplaceUsesWithAndRemove(
ir.BufferAtomicAnd(handle, inst.Arg(0), inst.Arg(1), {}));
continue;
case IR::Opcode::SharedAtomicIAdd32:
case IR::Opcode::SharedAtomicIAdd64:
inst.ReplaceUsesWithAndRemove(
ir.BufferAtomicIAdd(handle, inst.Arg(0), inst.Arg(1), {}));
continue;
case IR::Opcode::SharedAtomicOr32:
inst.ReplaceUsesWithAndRemove(
ir.BufferAtomicOr(handle, inst.Arg(0), inst.Arg(1), {}));
continue;
case IR::Opcode::SharedAtomicSMax32:
case IR::Opcode::SharedAtomicUMax32: {
const bool is_signed = inst.GetOpcode() == IR::Opcode::SharedAtomicSMax32;
inst.ReplaceUsesWithAndRemove(
ir.BufferAtomicIMax(handle, inst.Arg(0), inst.Arg(1), is_signed, {}));
continue;
}
case IR::Opcode::SharedAtomicSMin32:
case IR::Opcode::SharedAtomicUMin32: {
const bool is_signed = inst.GetOpcode() == IR::Opcode::SharedAtomicSMin32;
inst.ReplaceUsesWithAndRemove(
ir.BufferAtomicIMin(handle, inst.Arg(0), inst.Arg(1), is_signed, {}));
continue;
}
case IR::Opcode::SharedAtomicXor32:
inst.ReplaceUsesWithAndRemove(
ir.BufferAtomicXor(handle, inst.Arg(0), inst.Arg(1), {}));
continue;
default:
break;
}
// Replace shared operations.
const IR::U32 offset = ir.IMul(ir.GetAttributeU32(IR::Attribute::WorkgroupIndex), const IR::U32 offset = ir.IMul(ir.GetAttributeU32(IR::Attribute::WorkgroupIndex),
ir.Imm32(shared_memory_size)); ir.Imm32(shared_memory_size));
const IR::U32 address = ir.IAdd(IR::U32{inst.Arg(0)}, offset); const IR::U32 address = ir.IAdd(IR::U32{inst.Arg(0)}, offset);
switch (inst.GetOpcode()) { switch (inst.GetOpcode()) {
case IR::Opcode::SharedAtomicIAdd32:
case IR::Opcode::SharedAtomicIAdd64:
inst.ReplaceUsesWithAndRemove(
ir.BufferAtomicIAdd(handle, address, inst.Arg(1), {}));
continue;
case IR::Opcode::SharedAtomicISub32:
inst.ReplaceUsesWithAndRemove(
ir.BufferAtomicISub(handle, address, inst.Arg(1), {}));
continue;
case IR::Opcode::SharedAtomicSMin32:
case IR::Opcode::SharedAtomicUMin32: {
const bool is_signed = inst.GetOpcode() == IR::Opcode::SharedAtomicSMin32;
inst.ReplaceUsesWithAndRemove(
ir.BufferAtomicIMin(handle, address, inst.Arg(1), is_signed, {}));
continue;
}
case IR::Opcode::SharedAtomicSMax32:
case IR::Opcode::SharedAtomicUMax32: {
const bool is_signed = inst.GetOpcode() == IR::Opcode::SharedAtomicSMax32;
inst.ReplaceUsesWithAndRemove(
ir.BufferAtomicIMax(handle, address, inst.Arg(1), is_signed, {}));
continue;
}
case IR::Opcode::SharedAtomicInc32:
inst.ReplaceUsesWithAndRemove(ir.BufferAtomicInc(handle, address, {}));
continue;
case IR::Opcode::SharedAtomicDec32:
inst.ReplaceUsesWithAndRemove(ir.BufferAtomicDec(handle, address, {}));
continue;
case IR::Opcode::SharedAtomicAnd32:
inst.ReplaceUsesWithAndRemove(ir.BufferAtomicAnd(handle, address, inst.Arg(1), {}));
continue;
case IR::Opcode::SharedAtomicOr32:
inst.ReplaceUsesWithAndRemove(ir.BufferAtomicOr(handle, address, inst.Arg(1), {}));
continue;
case IR::Opcode::SharedAtomicXor32:
inst.ReplaceUsesWithAndRemove(ir.BufferAtomicXor(handle, address, inst.Arg(1), {}));
continue;
case IR::Opcode::LoadSharedU16: case IR::Opcode::LoadSharedU16:
inst.ReplaceUsesWithAndRemove(ir.LoadBufferU16(handle, address, {})); inst.ReplaceUsesWithAndRemove(ir.LoadBufferU16(handle, address, {}));
break; break;
@ -104,10 +158,10 @@ void SharedMemoryToStoragePass(IR::Program& program, const RuntimeInfo& runtime_
inst.ReplaceUsesWithAndRemove(ir.LoadBufferU32(1, handle, address, {})); inst.ReplaceUsesWithAndRemove(ir.LoadBufferU32(1, handle, address, {}));
break; break;
case IR::Opcode::LoadSharedU64: case IR::Opcode::LoadSharedU64:
inst.ReplaceUsesWithAndRemove(ir.LoadBufferU32(2, handle, address, {})); inst.ReplaceUsesWithAndRemove(ir.LoadBufferU64(handle, address, {}));
break; break;
case IR::Opcode::WriteSharedU16: case IR::Opcode::WriteSharedU16:
ir.StoreBufferU16(handle, address, IR::U32{inst.Arg(1)}, {}); ir.StoreBufferU16(handle, address, IR::U16{inst.Arg(1)}, {});
inst.Invalidate(); inst.Invalidate();
break; break;
case IR::Opcode::WriteSharedU32: case IR::Opcode::WriteSharedU32:
@ -115,7 +169,7 @@ void SharedMemoryToStoragePass(IR::Program& program, const RuntimeInfo& runtime_
inst.Invalidate(); inst.Invalidate();
break; break;
case IR::Opcode::WriteSharedU64: case IR::Opcode::WriteSharedU64:
ir.StoreBufferU32(2, handle, address, inst.Arg(1), {}); ir.StoreBufferU64(handle, address, IR::U64{inst.Arg(1)}, {});
inst.Invalidate(); inst.Invalidate();
break; break;
default: default:

View File

@ -265,6 +265,7 @@ using U32F32 = TypedValue<Type::U32 | Type::F32>;
using U64F64 = TypedValue<Type::U64 | Type::F64>; using U64F64 = TypedValue<Type::U64 | Type::F64>;
using U32U64 = TypedValue<Type::U32 | Type::U64>; using U32U64 = TypedValue<Type::U32 | Type::U64>;
using U16U32U64 = TypedValue<Type::U16 | Type::U32 | Type::U64>; using U16U32U64 = TypedValue<Type::U16 | Type::U32 | Type::U64>;
using U8U16U32U64 = TypedValue<Type::U8 | Type::U16 | Type::U32 | Type::U64>;
using F32F64 = TypedValue<Type::F32 | Type::F64>; using F32F64 = TypedValue<Type::F32 | Type::F64>;
using F16F32F64 = TypedValue<Type::F16 | Type::F32 | Type::F64>; using F16F32F64 = TypedValue<Type::F16 | Type::F32 | Type::F64>;
using UAny = TypedValue<Type::U8 | Type::U16 | Type::U32 | Type::U64>; using UAny = TypedValue<Type::U8 | Type::U16 | Type::U32 | Type::U64>;

View File

@ -78,6 +78,7 @@ IR::Program TranslateProgram(std::span<const u32> code, Pools& pools, Info& info
Shader::Optimization::FlattenExtendedUserdataPass(program); Shader::Optimization::FlattenExtendedUserdataPass(program);
Shader::Optimization::ResourceTrackingPass(program); Shader::Optimization::ResourceTrackingPass(program);
Shader::Optimization::LowerBufferFormatToRaw(program); Shader::Optimization::LowerBufferFormatToRaw(program);
Shader::Optimization::SharedMemorySimplifyPass(program, profile);
Shader::Optimization::SharedMemoryToStoragePass(program, runtime_info, profile); Shader::Optimization::SharedMemoryToStoragePass(program, runtime_info, profile);
Shader::Optimization::SharedMemoryBarrierPass(program, runtime_info, profile); Shader::Optimization::SharedMemoryBarrierPass(program, runtime_info, profile);
Shader::Optimization::IdentityRemovalPass(program.blocks); Shader::Optimization::IdentityRemovalPass(program.blocks);

View File

@ -23,6 +23,7 @@ static constexpr size_t DataShareBufferSize = 64_KB;
static constexpr size_t StagingBufferSize = 512_MB; static constexpr size_t StagingBufferSize = 512_MB;
static constexpr size_t UboStreamBufferSize = 128_MB; static constexpr size_t UboStreamBufferSize = 128_MB;
static constexpr size_t DownloadBufferSize = 128_MB; static constexpr size_t DownloadBufferSize = 128_MB;
static constexpr size_t DeviceBufferSize = 128_MB;
static constexpr size_t MaxPageFaults = 1024; static constexpr size_t MaxPageFaults = 1024;
BufferCache::BufferCache(const Vulkan::Instance& instance_, Vulkan::Scheduler& scheduler_, BufferCache::BufferCache(const Vulkan::Instance& instance_, Vulkan::Scheduler& scheduler_,
@ -32,7 +33,8 @@ BufferCache::BufferCache(const Vulkan::Instance& instance_, Vulkan::Scheduler& s
memory{Core::Memory::Instance()}, texture_cache{texture_cache_}, tracker{tracker_}, memory{Core::Memory::Instance()}, texture_cache{texture_cache_}, tracker{tracker_},
staging_buffer{instance, scheduler, MemoryUsage::Upload, StagingBufferSize}, staging_buffer{instance, scheduler, MemoryUsage::Upload, StagingBufferSize},
stream_buffer{instance, scheduler, MemoryUsage::Stream, UboStreamBufferSize}, stream_buffer{instance, scheduler, MemoryUsage::Stream, UboStreamBufferSize},
download_buffer(instance, scheduler, MemoryUsage::Download, DownloadBufferSize), download_buffer{instance, scheduler, MemoryUsage::Download, DownloadBufferSize},
device_buffer{instance, scheduler, MemoryUsage::DeviceLocal, DeviceBufferSize},
gds_buffer{instance, scheduler, MemoryUsage::Stream, 0, AllFlags, DataShareBufferSize}, gds_buffer{instance, scheduler, MemoryUsage::Stream, 0, AllFlags, DataShareBufferSize},
bda_pagetable_buffer{instance, scheduler, MemoryUsage::DeviceLocal, bda_pagetable_buffer{instance, scheduler, MemoryUsage::DeviceLocal,
0, AllFlags, BDA_PAGETABLE_SIZE}, 0, AllFlags, BDA_PAGETABLE_SIZE},
@ -348,7 +350,7 @@ std::pair<Buffer*, u32> BufferCache::ObtainBuffer(VAddr device_addr, u32 size, b
return {&buffer, buffer.Offset(device_addr)}; return {&buffer, buffer.Offset(device_addr)};
} }
std::pair<Buffer*, u32> BufferCache::ObtainViewBuffer(VAddr gpu_addr, u32 size, bool prefer_gpu) { std::pair<Buffer*, u32> BufferCache::ObtainBufferForImage(VAddr gpu_addr, u32 size) {
// Check if any buffer contains the full requested range. // Check if any buffer contains the full requested range.
const u64 page = gpu_addr >> CACHING_PAGEBITS; const u64 page = gpu_addr >> CACHING_PAGEBITS;
const BufferId buffer_id = page_table[page].buffer_id; const BufferId buffer_id = page_table[page].buffer_id;
@ -361,10 +363,10 @@ std::pair<Buffer*, u32> BufferCache::ObtainViewBuffer(VAddr gpu_addr, u32 size,
} }
// If no buffer contains the full requested range but some buffer within was GPU-modified, // If no buffer contains the full requested range but some buffer within was GPU-modified,
// fall back to ObtainBuffer to create a full buffer and avoid losing GPU modifications. // fall back to ObtainBuffer to create a full buffer and avoid losing GPU modifications.
// This is only done if the request prefers to use GPU memory, otherwise we can skip it. if (memory_tracker.IsRegionGpuModified(gpu_addr, size)) {
if (prefer_gpu && memory_tracker.IsRegionGpuModified(gpu_addr, size)) {
return ObtainBuffer(gpu_addr, size, false, false); return ObtainBuffer(gpu_addr, size, false, false);
} }
// In all other cases, just do a CPU copy to the staging buffer. // In all other cases, just do a CPU copy to the staging buffer.
const auto [data, offset] = staging_buffer.Map(size, 16); const auto [data, offset] = staging_buffer.Map(size, 16);
memory->CopySparseMemory(gpu_addr, data, size); memory->CopySparseMemory(gpu_addr, data, size);

View File

@ -80,11 +80,6 @@ public:
return &gds_buffer; return &gds_buffer;
} }
/// Retrieves the host visible device local stream buffer.
[[nodiscard]] StreamBuffer& GetStreamBuffer() noexcept {
return stream_buffer;
}
/// Retrieves the device local DBA page table buffer. /// Retrieves the device local DBA page table buffer.
[[nodiscard]] Buffer* GetBdaPageTableBuffer() noexcept { [[nodiscard]] Buffer* GetBdaPageTableBuffer() noexcept {
return &bda_pagetable_buffer; return &bda_pagetable_buffer;
@ -100,6 +95,20 @@ public:
return slot_buffers[id]; return slot_buffers[id];
} }
/// Retrieves a utility buffer optimized for specified memory usage.
StreamBuffer& GetUtilityBuffer(MemoryUsage usage) noexcept {
switch (usage) {
case MemoryUsage::Stream:
return stream_buffer;
case MemoryUsage::Download:
return download_buffer;
case MemoryUsage::Upload:
return staging_buffer;
case MemoryUsage::DeviceLocal:
return device_buffer;
}
}
/// Invalidates any buffer in the logical page range. /// Invalidates any buffer in the logical page range.
void InvalidateMemory(VAddr device_addr, u64 size, bool unmap); void InvalidateMemory(VAddr device_addr, u64 size, bool unmap);
@ -121,8 +130,7 @@ public:
BufferId buffer_id = {}); BufferId buffer_id = {});
/// Attempts to obtain a buffer without modifying the cache contents. /// Attempts to obtain a buffer without modifying the cache contents.
[[nodiscard]] std::pair<Buffer*, u32> ObtainViewBuffer(VAddr gpu_addr, u32 size, [[nodiscard]] std::pair<Buffer*, u32> ObtainBufferForImage(VAddr gpu_addr, u32 size);
bool prefer_gpu);
/// Return true when a region is registered on the cache /// Return true when a region is registered on the cache
[[nodiscard]] bool IsRegionRegistered(VAddr addr, size_t size); [[nodiscard]] bool IsRegionRegistered(VAddr addr, size_t size);
@ -193,6 +201,7 @@ private:
StreamBuffer staging_buffer; StreamBuffer staging_buffer;
StreamBuffer stream_buffer; StreamBuffer stream_buffer;
StreamBuffer download_buffer; StreamBuffer download_buffer;
StreamBuffer device_buffer;
Buffer gds_buffer; Buffer gds_buffer;
Buffer bda_pagetable_buffer; Buffer bda_pagetable_buffer;
Buffer fault_buffer; Buffer fault_buffer;

View File

@ -445,7 +445,25 @@ bool Instance::CreateDevice() {
workgroup_memory_explicit_layout_features.workgroupMemoryExplicitLayout16BitAccess, workgroup_memory_explicit_layout_features.workgroupMemoryExplicitLayout16BitAccess,
}, },
#ifdef __APPLE__ #ifdef __APPLE__
portability_features, vk::PhysicalDevicePortabilitySubsetFeaturesKHR{
.constantAlphaColorBlendFactors = portability_features.constantAlphaColorBlendFactors,
.events = portability_features.events,
.imageViewFormatReinterpretation = portability_features.imageViewFormatReinterpretation,
.imageViewFormatSwizzle = portability_features.imageViewFormatSwizzle,
.imageView2DOn3DImage = portability_features.imageView2DOn3DImage,
.multisampleArrayImage = portability_features.multisampleArrayImage,
.mutableComparisonSamplers = portability_features.mutableComparisonSamplers,
.pointPolygons = portability_features.pointPolygons,
.samplerMipLodBias = portability_features.samplerMipLodBias,
.separateStencilMaskRef = portability_features.separateStencilMaskRef,
.shaderSampleRateInterpolationFunctions =
portability_features.shaderSampleRateInterpolationFunctions,
.tessellationIsolines = portability_features.tessellationIsolines,
.tessellationPointMode = portability_features.tessellationPointMode,
.triangleFans = portability_features.triangleFans,
.vertexAttributeAccessBeyondStride =
portability_features.vertexAttributeAccessBeyondStride,
},
#endif #endif
}; };

View File

@ -549,7 +549,7 @@ void Rasterizer::BindBuffers(const Shader::Info& stage, Shader::Backend::Binding
const auto* gds_buf = buffer_cache.GetGdsBuffer(); const auto* gds_buf = buffer_cache.GetGdsBuffer();
buffer_infos.emplace_back(gds_buf->Handle(), 0, gds_buf->SizeBytes()); buffer_infos.emplace_back(gds_buf->Handle(), 0, gds_buf->SizeBytes());
} else if (desc.buffer_type == Shader::BufferType::Flatbuf) { } else if (desc.buffer_type == Shader::BufferType::Flatbuf) {
auto& vk_buffer = buffer_cache.GetStreamBuffer(); auto& vk_buffer = buffer_cache.GetUtilityBuffer(VideoCore::MemoryUsage::Stream);
const u32 ubo_size = stage.flattened_ud_buf.size() * sizeof(u32); const u32 ubo_size = stage.flattened_ud_buf.size() * sizeof(u32);
const u64 offset = vk_buffer.Copy(stage.flattened_ud_buf.data(), ubo_size, const u64 offset = vk_buffer.Copy(stage.flattened_ud_buf.data(), ubo_size,
instance.UniformMinAlignment()); instance.UniformMinAlignment());
@ -561,7 +561,7 @@ void Rasterizer::BindBuffers(const Shader::Info& stage, Shader::Backend::Binding
const auto* fault_buffer = buffer_cache.GetFaultBuffer(); const auto* fault_buffer = buffer_cache.GetFaultBuffer();
buffer_infos.emplace_back(fault_buffer->Handle(), 0, fault_buffer->SizeBytes()); buffer_infos.emplace_back(fault_buffer->Handle(), 0, fault_buffer->SizeBytes());
} else if (desc.buffer_type == Shader::BufferType::SharedMemory) { } else if (desc.buffer_type == Shader::BufferType::SharedMemory) {
auto& lds_buffer = buffer_cache.GetStreamBuffer(); auto& lds_buffer = buffer_cache.GetUtilityBuffer(VideoCore::MemoryUsage::Stream);
const auto& cs_program = liverpool->GetCsRegs(); const auto& cs_program = liverpool->GetCsRegs();
const auto lds_size = cs_program.SharedMemSize() * cs_program.NumWorkgroups(); const auto lds_size = cs_program.SharedMemSize() * cs_program.NumWorkgroups();
const auto [data, offset] = const auto [data, offset] =

View File

@ -312,42 +312,121 @@ void Image::Upload(vk::Buffer buffer, u64 offset) {
vk::AccessFlagBits2::eShaderRead | vk::AccessFlagBits2::eTransferRead, {}); vk::AccessFlagBits2::eShaderRead | vk::AccessFlagBits2::eTransferRead, {});
} }
void Image::CopyImage(const Image& image) { void Image::CopyImage(const Image& src_image) {
scheduler->EndRendering(); scheduler->EndRendering();
Transit(vk::ImageLayout::eTransferDstOptimal, vk::AccessFlagBits2::eTransferWrite, {}); Transit(vk::ImageLayout::eTransferDstOptimal, vk::AccessFlagBits2::eTransferWrite, {});
auto cmdbuf = scheduler->CommandBuffer(); auto cmdbuf = scheduler->CommandBuffer();
const auto& src_info = src_image.info;
boost::container::small_vector<vk::ImageCopy, 14> image_copy{}; boost::container::small_vector<vk::ImageCopy, 14> image_copy{};
for (u32 m = 0; m < image.info.resources.levels; ++m) { const u32 num_mips = std::min(src_info.resources.levels, info.resources.levels);
const auto mip_w = std::max(image.info.size.width >> m, 1u); for (u32 m = 0; m < num_mips; ++m) {
const auto mip_h = std::max(image.info.size.height >> m, 1u); const auto mip_w = std::max(src_info.size.width >> m, 1u);
const auto mip_d = std::max(image.info.size.depth >> m, 1u); const auto mip_h = std::max(src_info.size.height >> m, 1u);
const auto mip_d = std::max(src_info.size.depth >> m, 1u);
image_copy.emplace_back(vk::ImageCopy{ image_copy.emplace_back(vk::ImageCopy{
.srcSubresource{ .srcSubresource{
.aspectMask = image.aspect_mask, .aspectMask = src_image.aspect_mask,
.mipLevel = m, .mipLevel = m,
.baseArrayLayer = 0, .baseArrayLayer = 0,
.layerCount = image.info.resources.layers, .layerCount = src_info.resources.layers,
}, },
.dstSubresource{ .dstSubresource{
.aspectMask = image.aspect_mask, .aspectMask = src_image.aspect_mask,
.mipLevel = m, .mipLevel = m,
.baseArrayLayer = 0, .baseArrayLayer = 0,
.layerCount = image.info.resources.layers, .layerCount = src_info.resources.layers,
}, },
.extent = {mip_w, mip_h, mip_d}, .extent = {mip_w, mip_h, mip_d},
}); });
} }
cmdbuf.copyImage(image.image, image.last_state.layout, this->image, this->last_state.layout, cmdbuf.copyImage(src_image.image, src_image.last_state.layout, image, last_state.layout,
image_copy); image_copy);
Transit(vk::ImageLayout::eGeneral, Transit(vk::ImageLayout::eGeneral,
vk::AccessFlagBits2::eShaderRead | vk::AccessFlagBits2::eTransferRead, {}); vk::AccessFlagBits2::eShaderRead | vk::AccessFlagBits2::eTransferRead, {});
} }
void Image::CopyMip(const Image& image, u32 mip, u32 slice) { void Image::CopyImageWithBuffer(Image& src_image, vk::Buffer buffer, u64 offset) {
const auto& src_info = src_image.info;
vk::BufferImageCopy buffer_image_copy = {
.bufferOffset = offset,
.bufferRowLength = 0,
.bufferImageHeight = 0,
.imageSubresource =
{
.aspectMask = src_info.IsDepthStencil() ? vk::ImageAspectFlagBits::eDepth
: vk::ImageAspectFlagBits::eColor,
.mipLevel = 0,
.baseArrayLayer = 0,
.layerCount = 1,
},
.imageOffset =
{
.x = 0,
.y = 0,
.z = 0,
},
.imageExtent =
{
.width = src_info.size.width,
.height = src_info.size.height,
.depth = src_info.size.depth,
},
};
const vk::BufferMemoryBarrier2 pre_copy_barrier = {
.srcStageMask = vk::PipelineStageFlagBits2::eTransfer,
.srcAccessMask = vk::AccessFlagBits2::eTransferRead,
.dstStageMask = vk::PipelineStageFlagBits2::eTransfer,
.dstAccessMask = vk::AccessFlagBits2::eTransferWrite,
.buffer = buffer,
.offset = offset,
.size = VK_WHOLE_SIZE,
};
const vk::BufferMemoryBarrier2 post_copy_barrier = {
.srcStageMask = vk::PipelineStageFlagBits2::eTransfer,
.srcAccessMask = vk::AccessFlagBits2::eTransferWrite,
.dstStageMask = vk::PipelineStageFlagBits2::eTransfer,
.dstAccessMask = vk::AccessFlagBits2::eTransferRead,
.buffer = buffer,
.offset = offset,
.size = VK_WHOLE_SIZE,
};
scheduler->EndRendering();
src_image.Transit(vk::ImageLayout::eTransferSrcOptimal, vk::AccessFlagBits2::eTransferRead, {});
Transit(vk::ImageLayout::eTransferDstOptimal, vk::AccessFlagBits2::eTransferWrite, {});
auto cmdbuf = scheduler->CommandBuffer();
cmdbuf.pipelineBarrier2(vk::DependencyInfo{
.dependencyFlags = vk::DependencyFlagBits::eByRegion,
.bufferMemoryBarrierCount = 1,
.pBufferMemoryBarriers = &pre_copy_barrier,
});
cmdbuf.copyImageToBuffer(src_image.image, vk::ImageLayout::eTransferSrcOptimal, buffer,
buffer_image_copy);
cmdbuf.pipelineBarrier2(vk::DependencyInfo{
.dependencyFlags = vk::DependencyFlagBits::eByRegion,
.bufferMemoryBarrierCount = 1,
.pBufferMemoryBarriers = &post_copy_barrier,
});
buffer_image_copy.imageSubresource.aspectMask =
info.IsDepthStencil() ? vk::ImageAspectFlagBits::eDepth : vk::ImageAspectFlagBits::eColor;
cmdbuf.copyBufferToImage(buffer, image, vk::ImageLayout::eTransferDstOptimal,
buffer_image_copy);
}
void Image::CopyMip(const Image& src_image, u32 mip, u32 slice) {
scheduler->EndRendering(); scheduler->EndRendering();
Transit(vk::ImageLayout::eTransferDstOptimal, vk::AccessFlagBits2::eTransferWrite, {}); Transit(vk::ImageLayout::eTransferDstOptimal, vk::AccessFlagBits2::eTransferWrite, {});
@ -357,26 +436,27 @@ void Image::CopyMip(const Image& image, u32 mip, u32 slice) {
const auto mip_h = std::max(info.size.height >> mip, 1u); const auto mip_h = std::max(info.size.height >> mip, 1u);
const auto mip_d = std::max(info.size.depth >> mip, 1u); const auto mip_d = std::max(info.size.depth >> mip, 1u);
ASSERT(mip_w == image.info.size.width); const auto& src_info = src_image.info;
ASSERT(mip_h == image.info.size.height); ASSERT(mip_w == src_info.size.width);
ASSERT(mip_h == src_info.size.height);
const u32 num_layers = std::min(image.info.resources.layers, info.resources.layers); const u32 num_layers = std::min(src_info.resources.layers, info.resources.layers);
const vk::ImageCopy image_copy{ const vk::ImageCopy image_copy{
.srcSubresource{ .srcSubresource{
.aspectMask = image.aspect_mask, .aspectMask = src_image.aspect_mask,
.mipLevel = 0, .mipLevel = 0,
.baseArrayLayer = 0, .baseArrayLayer = 0,
.layerCount = num_layers, .layerCount = num_layers,
}, },
.dstSubresource{ .dstSubresource{
.aspectMask = image.aspect_mask, .aspectMask = src_image.aspect_mask,
.mipLevel = mip, .mipLevel = mip,
.baseArrayLayer = slice, .baseArrayLayer = slice,
.layerCount = num_layers, .layerCount = num_layers,
}, },
.extent = {mip_w, mip_h, mip_d}, .extent = {mip_w, mip_h, mip_d},
}; };
cmdbuf.copyImage(image.image, image.last_state.layout, this->image, this->last_state.layout, cmdbuf.copyImage(src_image.image, src_image.last_state.layout, image, last_state.layout,
image_copy); image_copy);
Transit(vk::ImageLayout::eGeneral, Transit(vk::ImageLayout::eGeneral,

View File

@ -104,7 +104,8 @@ struct Image {
std::optional<SubresourceRange> range, vk::CommandBuffer cmdbuf = {}); std::optional<SubresourceRange> range, vk::CommandBuffer cmdbuf = {});
void Upload(vk::Buffer buffer, u64 offset); void Upload(vk::Buffer buffer, u64 offset);
void CopyImage(const Image& image); void CopyImage(const Image& src_image);
void CopyImageWithBuffer(Image& src_image, vk::Buffer buffer, u64 offset);
void CopyMip(const Image& src_image, u32 mip, u32 slice); void CopyMip(const Image& src_image, u32 mip, u32 slice);
bool IsTracked() { bool IsTracked() {

View File

@ -8,7 +8,6 @@
#include "common/debug.h" #include "common/debug.h"
#include "video_core/buffer_cache/buffer_cache.h" #include "video_core/buffer_cache/buffer_cache.h"
#include "video_core/page_manager.h" #include "video_core/page_manager.h"
#include "video_core/renderer_vulkan/liverpool_to_vk.h"
#include "video_core/renderer_vulkan/vk_instance.h" #include "video_core/renderer_vulkan/vk_instance.h"
#include "video_core/renderer_vulkan/vk_scheduler.h" #include "video_core/renderer_vulkan/vk_scheduler.h"
#include "video_core/texture_cache/host_compatibility.h" #include "video_core/texture_cache/host_compatibility.h"
@ -126,7 +125,7 @@ void TextureCache::UnmapMemory(VAddr cpu_addr, size_t size) {
ImageId TextureCache::ResolveDepthOverlap(const ImageInfo& requested_info, BindingType binding, ImageId TextureCache::ResolveDepthOverlap(const ImageInfo& requested_info, BindingType binding,
ImageId cache_image_id) { ImageId cache_image_id) {
const auto& cache_image = slot_images[cache_image_id]; auto& cache_image = slot_images[cache_image_id];
if (!cache_image.info.IsDepthStencil() && !requested_info.IsDepthStencil()) { if (!cache_image.info.IsDepthStencil() && !requested_info.IsDepthStencil()) {
return {}; return {};
@ -169,18 +168,21 @@ ImageId TextureCache::ResolveDepthOverlap(const ImageInfo& requested_info, Bindi
} }
if (recreate) { if (recreate) {
auto new_info{requested_info}; auto new_info = requested_info;
new_info.resources = std::max(requested_info.resources, cache_image.info.resources); new_info.resources = std::min(requested_info.resources, cache_image.info.resources);
new_info.UpdateSize();
const auto new_image_id = slot_images.insert(instance, scheduler, new_info); const auto new_image_id = slot_images.insert(instance, scheduler, new_info);
RegisterImage(new_image_id); RegisterImage(new_image_id);
// Inherit image usage // Inherit image usage
auto& new_image = GetImage(new_image_id); auto& new_image = slot_images[new_image_id];
new_image.usage = cache_image.usage; new_image.usage = cache_image.usage;
new_image.flags &= ~ImageFlagBits::Dirty;
// TODO: perform a depth copy here // Perform depth<->color copy using the intermediate copy buffer.
const auto& copy_buffer = buffer_cache.GetUtilityBuffer(MemoryUsage::DeviceLocal);
new_image.CopyImageWithBuffer(cache_image, copy_buffer.Handle(), 0);
// Free the cache image.
FreeImage(cache_image_id); FreeImage(cache_image_id);
return new_image_id; return new_image_id;
} }
@ -461,9 +463,9 @@ ImageView& TextureCache::FindDepthTarget(BaseDesc& desc) {
const ImageId image_id = FindImage(desc); const ImageId image_id = FindImage(desc);
Image& image = slot_images[image_id]; Image& image = slot_images[image_id];
image.flags |= ImageFlagBits::GpuModified; image.flags |= ImageFlagBits::GpuModified;
image.flags &= ~ImageFlagBits::Dirty;
image.usage.depth_target = 1u; image.usage.depth_target = 1u;
image.usage.stencil = image.info.HasStencil(); image.usage.stencil = image.info.HasStencil();
UpdateImage(image_id);
// Register meta data for this depth buffer // Register meta data for this depth buffer
if (!(image.flags & ImageFlagBits::MetaRegistered)) { if (!(image.flags & ImageFlagBits::MetaRegistered)) {
@ -584,12 +586,11 @@ void TextureCache::RefreshImage(Image& image, Vulkan::Scheduler* custom_schedule
const VAddr image_addr = image.info.guest_address; const VAddr image_addr = image.info.guest_address;
const size_t image_size = image.info.guest_size; const size_t image_size = image.info.guest_size;
const auto [vk_buffer, buf_offset] = const auto [vk_buffer, buf_offset] = buffer_cache.ObtainBufferForImage(image_addr, image_size);
buffer_cache.ObtainViewBuffer(image_addr, image_size, is_gpu_dirty);
const auto cmdbuf = sched_ptr->CommandBuffer(); const auto cmdbuf = sched_ptr->CommandBuffer();
// The obtained buffer may be written by a shader so we need to emit a barrier to prevent RAW
// hazard // The obtained buffer may be GPU modified so we need to emit a barrier to prevent RAW hazard
if (auto barrier = vk_buffer->GetBarrier(vk::AccessFlagBits2::eTransferRead, if (auto barrier = vk_buffer->GetBarrier(vk::AccessFlagBits2::eTransferRead,
vk::PipelineStageFlagBits2::eTransfer)) { vk::PipelineStageFlagBits2::eTransfer)) {
cmdbuf.pipelineBarrier2(vk::DependencyInfo{ cmdbuf.pipelineBarrier2(vk::DependencyInfo{