Merge branch 'shadps4-emu:main' into shader_cache

This commit is contained in:
Fire Cube 2025-07-13 17:05:44 +02:00 committed by GitHub
commit 9195f59220
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
131 changed files with 6834 additions and 3502 deletions

View File

@ -35,7 +35,7 @@ body:
required: true
- label: I have disabled all patches and cheats and the issue is still present.
required: true
- label: I have all the required [system modules](https://github.com/shadps4-emu/shadps4-game-compatibility?tab=readme-ov-file#informations) installed.
- label: I have all the required [system modules](https://github.com/shadps4-emu/shadPS4/wiki/I.-Quick-start-%5BUsers%5D#4-adding-modules) installed.
required: true
- type: textarea
id: desc

21
CMakeLinuxPresets.json Normal file
View File

@ -0,0 +1,21 @@
{
"version": 9,
"cmakeMinimumRequired": {
"major": 3,
"minor": 30,
"patch": 0
},
"configurePresets": [
{
"name": "x64-Clang-Base",
"hidden": true,
"generator": "Ninja",
"binaryDir": "${sourceDir}/Build/${presetName}",
"cacheVariables": {
"CMAKE_C_COMPILER": "clang",
"CMAKE_CXX_COMPILER": "clang++",
"CMAKE_INSTALL_PREFIX": "${sourceDir}/Build/${presetName}"
}
}
]
}

View File

@ -203,7 +203,7 @@ execute_process(
# Set Version
set(EMULATOR_VERSION_MAJOR "0")
set(EMULATOR_VERSION_MINOR "9")
set(EMULATOR_VERSION_MINOR "10")
set(EMULATOR_VERSION_PATCH "1")
set_source_files_properties(src/shadps4.rc PROPERTIES COMPILE_DEFINITIONS "EMULATOR_VERSION_MAJOR=${EMULATOR_VERSION_MAJOR};EMULATOR_VERSION_MINOR=${EMULATOR_VERSION_MINOR};EMULATOR_VERSION_PATCH=${EMULATOR_VERSION_PATCH}")
@ -685,11 +685,13 @@ set(COMMON src/common/logging/backend.cpp
src/common/path_util.h
src/common/object_pool.h
src/common/polyfill_thread.h
src/common/range_lock.h
src/common/rdtsc.cpp
src/common/rdtsc.h
src/common/recursive_lock.cpp
src/common/recursive_lock.h
src/common/sha1.h
src/common/shared_first_mutex.h
src/common/signal_context.h
src/common/signal_context.cpp
src/common/singleton.h
@ -1072,6 +1074,8 @@ set(QT_GUI src/qt_gui/about_dialog.cpp
src/qt_gui/gui_settings.h
src/qt_gui/settings.cpp
src/qt_gui/settings.h
src/qt_gui/sdl_event_wrapper.cpp
src/qt_gui/sdl_event_wrapper.h
${EMULATOR}
${RESOURCE_FILES}
${TRANSLATIONS}

View File

@ -1,27 +1,12 @@
{
"version": 5,
"version": 9,
"cmakeMinimumRequired": {
"major": 3,
"minor": 24,
"minor": 30,
"patch": 0
},
"include": ["CMake${hostSystemName}Presets.json"],
"configurePresets": [
{
"name": "x64-Clang-Base",
"hidden": true,
"generator": "Ninja",
"binaryDir": "${sourceDir}/Build/${presetName}",
"cacheVariables": {
"CMAKE_C_COMPILER": "clang-cl",
"CMAKE_CXX_COMPILER": "clang-cl",
"CMAKE_INSTALL_PREFIX": "${sourceDir}/Build/${presetName}"
},
"vendor": {
"microsoft.com/VisualStudioSettings/CMake/1.0": {
"intelliSenseMode": "windows-clang-x64"
}
}
},
{
"name": "x64-Clang-Debug",
"displayName": "Clang x64 Debug",
@ -74,4 +59,4 @@
}
}
]
}
}

26
CMakeWindowsPresets.json Normal file
View File

@ -0,0 +1,26 @@
{
"version": 9,
"cmakeMinimumRequired": {
"major": 3,
"minor": 30,
"patch": 0
},
"configurePresets": [
{
"name": "x64-Clang-Base",
"hidden": true,
"generator": "Ninja",
"binaryDir": "${sourceDir}/Build/${presetName}",
"cacheVariables": {
"CMAKE_C_COMPILER": "clang-cl",
"CMAKE_CXX_COMPILER": "clang-cl",
"CMAKE_INSTALL_PREFIX": "${sourceDir}/Build/${presetName}"
},
"vendor": {
"microsoft.com/VisualStudioSettings/CMake/1.0": {
"intelliSenseMode": "windows-clang-x64"
}
}
}
]
}

View File

@ -5,6 +5,8 @@ path = [
"REUSE.toml",
"crowdin.yml",
"CMakeSettings.json",
"CMakeLinuxPresets.json",
"CMakeWindowsPresets.json",
"CMakePresets.json",
".github/FUNDING.yml",
".github/shadps4.png",

View File

@ -1,14 +1,28 @@
# SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
# SPDX-License-Identifier: GPL-2.0-or-later
file(GLOB QT_KITS LIST_DIRECTORIES true "C:/Qt/*/msvc*_64")
list(SORT QT_KITS COMPARE NATURAL)
list(REVERSE QT_KITS)
if(QT_KITS)
list(GET QT_KITS 0 QT_PREFIX)
set(CMAKE_PREFIX_PATH "${QT_PREFIX}" CACHE PATH "Qt prefix autodetected" FORCE)
message(STATUS "Auto-detected Qt prefix: ${QT_PREFIX}")
else()
message(STATUS "findQt.cmake: no QtDirectory found in C:/Qt please set CMAKE_PREFIX_PATH manually")
endif()
set(highest_version "0")
set(CANDIDATE_DRIVES A B C D E F G H I J K L M N O P Q R S T U V W X Y Z)
foreach(drive ${CANDIDATE_DRIVES})
file(GLOB kits LIST_DIRECTORIES true CONFIGURE_DEPENDS "${drive}:/Qt/*/msvc*_64")
foreach(kit IN LISTS kits)
get_filename_component(version_dir "${kit}" DIRECTORY)
get_filename_component(kit_version "${version_dir}" NAME)
message(STATUS "DetectQtInstallation.cmake: Detected Qt: ${kit}")
if (kit_version VERSION_GREATER highest_version)
set(highest_version "${kit_version}")
set(QT_PREFIX "${kit}")
endif()
endforeach()
endforeach()
if(QT_PREFIX)
set(CMAKE_PREFIX_PATH "${QT_PREFIX}" CACHE PATH "Qt prefix autodetected" FORCE)
message(STATUS "DetectQtInstallation.cmake: Choose newest Qt: ${QT_PREFIX}")
else()
message(STATUS "DetectQtInstallation.cmake: No QtDirectory found in <drive>:/Qt please set CMAKE_PREFIX_PATH manually")
endif()

View File

@ -37,6 +37,9 @@
<category translate="no">Game</category>
</categories>
<releases>
<release version="0.10.0" date="2025-07-06">
<url>https://github.com/shadps4-emu/shadPS4/releases/tag/v.0.10.0</url>
</release>
<release version="0.9.0" date="2025-05-22">
<url>https://github.com/shadps4-emu/shadPS4/releases/tag/v.0.9.0</url>
</release>

2
externals/sirit vendored

@ -1 +1 @@
Subproject commit 6b450704f6fedb9413d0c89a9eb59d028eb1e6c0
Subproject commit b4eccb336f1b1169af48dac1e04015985af86e3e

View File

@ -18,6 +18,9 @@ public:
void unlock() {
pthread_mutex_unlock(&mutex);
}
[[nodiscard]] bool try_lock() {
return pthread_mutex_trylock(&mutex) == 0;
}
private:
pthread_mutex_t mutex = PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP;

View File

@ -361,13 +361,6 @@ public:
return *this;
}
inline constexpr BitArray& operator~() {
for (size_t i = 0; i < WORD_COUNT; ++i) {
data[i] = ~data[i];
}
return *this;
}
inline constexpr BitArray operator|(const BitArray& other) const {
BitArray result = *this;
result |= other;
@ -388,7 +381,9 @@ public:
inline constexpr BitArray operator~() const {
BitArray result = *this;
result = ~result;
for (size_t i = 0; i < WORD_COUNT; ++i) {
result.data[i] = ~result.data[i];
}
return result;
}
@ -408,4 +403,4 @@ private:
std::array<u64, WORD_COUNT> data{};
};
} // namespace Common
} // namespace Common

View File

@ -31,29 +31,53 @@ std::filesystem::path find_fs_path_or(const basic_value<TC>& v, const K& ky,
namespace Config {
// General
static bool isNeo = false;
static bool isDevKit = false;
static bool isPSNSignedIn = false;
static bool isTrophyPopupDisabled = false;
static double trophyNotificationDuration = 6.0;
static bool enableDiscordRPC = false;
static u32 screenWidth = 1280;
static u32 screenHeight = 720;
static s32 gpuId = -1; // Vulkan physical device index. Set to negative for auto select
static std::string logFilter;
static std::string logFilter = "";
static std::string logType = "sync";
static std::string userName = "shadPS4";
static std::string chooseHomeTab;
static std::string chooseHomeTab = "General";
static bool isShowSplash = false;
static std::string isSideTrophy = "right";
static bool compatibilityData = false;
static bool checkCompatibilityOnStartup = false;
// Input
static int cursorState = HideCursorState::Idle;
static int cursorHideTimeout = 5; // 5 seconds (default)
static bool useSpecialPad = false;
static int specialPadClass = 1;
static bool isMotionControlsEnabled = true;
static bool isDebugDump = false;
static bool isShaderDebug = false;
static bool isShowSplash = false;
static std::string isSideTrophy = "right";
static bool useUnifiedInputConfig = true;
// These two entries aren't stored in the config
static bool overrideControllerColor = false;
static int controllerCustomColorRGB[3] = {0, 0, 255};
// GPU
static u32 windowWidth = 1280;
static u32 windowHeight = 720;
static u32 internalScreenWidth = 1280;
static u32 internalScreenHeight = 720;
static bool isNullGpu = false;
static bool shouldCopyGPUBuffers = false;
static bool readbacksEnabled = false;
static bool readbackLinearImagesEnabled = false;
static bool directMemoryAccessEnabled = false;
static bool shouldDumpShaders = false;
static bool shouldPatchShaders = true;
static bool shouldPatchShaders = false;
static u32 vblankDivider = 1;
static bool isFullscreen = false;
static std::string fullscreenMode = "Windowed";
static bool isHDRAllowed = false;
// Vulkan
static s32 gpuId = -1;
static bool vkValidation = false;
static bool vkValidationSync = false;
static bool vkValidationGpu = false;
@ -61,32 +85,29 @@ static bool vkCrashDiagnostic = false;
static bool vkHostMarkers = false;
static bool vkGuestMarkers = false;
static bool rdocEnable = false;
static bool isFpsColor = true;
static bool isSeparateLogFilesEnabled = false;
static int cursorState = HideCursorState::Idle;
static int cursorHideTimeout = 5; // 5 seconds (default)
static double trophyNotificationDuration = 6.0;
static bool useUnifiedInputConfig = true;
static bool overrideControllerColor = false;
static int controllerCustomColorRGB[3] = {0, 0, 255};
static bool compatibilityData = false;
static bool checkCompatibilityOnStartup = false;
static std::string trophyKey;
static bool isPSNSignedIn = false;
// Gui
// Debug
static bool isDebugDump = false;
static bool isShaderDebug = false;
static bool isSeparateLogFilesEnabled = false;
static bool isFpsColor = true;
// GUI
static bool load_game_size = true;
static std::vector<GameInstallDir> settings_install_dirs = {};
std::vector<bool> install_dirs_enabled = {};
std::filesystem::path settings_addon_install_dir = {};
std::filesystem::path save_data_path = {};
static bool isFullscreen = false;
static std::string fullscreenMode = "Windowed";
static bool isHDRAllowed = false;
// Language
// Settings
u32 m_language = 1; // english
// Keys
static std::string trophyKey = "";
// Expected number of items in the config file
static constexpr u64 total_entries = 54;
bool allowHDR() {
return isHDRAllowed;
}
@ -176,12 +197,20 @@ double getTrophyNotificationDuration() {
return trophyNotificationDuration;
}
u32 getScreenWidth() {
return screenWidth;
u32 getWindowWidth() {
return windowWidth;
}
u32 getScreenHeight() {
return screenHeight;
u32 getWindowHeight() {
return windowHeight;
}
u32 getInternalScreenWidth() {
return internalScreenHeight;
}
u32 getInternalScreenHeight() {
return internalScreenHeight;
}
s32 getGpuId() {
@ -240,6 +269,18 @@ bool copyGPUCmdBuffers() {
return shouldCopyGPUBuffers;
}
bool readbacks() {
return readbacksEnabled;
}
bool readbackLinearImages() {
return readbackLinearImagesEnabled;
}
bool directMemoryAccess() {
return directMemoryAccessEnabled;
}
bool dumpShaders() {
return shouldDumpShaders;
}
@ -308,12 +349,20 @@ void setGpuId(s32 selectedGpuId) {
gpuId = selectedGpuId;
}
void setScreenWidth(u32 width) {
screenWidth = width;
void setWindowWidth(u32 width) {
windowWidth = width;
}
void setScreenHeight(u32 height) {
screenHeight = height;
void setWindowHeight(u32 height) {
windowHeight = height;
}
void setInternalScreenWidth(u32 width) {
internalScreenWidth = width;
}
void setInternalScreenHeight(u32 height) {
internalScreenHeight = height;
}
void setDebugDump(bool enable) {
@ -344,6 +393,14 @@ void setCopyGPUCmdBuffers(bool enable) {
shouldCopyGPUBuffers = enable;
}
void setReadbacks(bool enable) {
readbacksEnabled = enable;
}
void setDirectMemoryAccess(bool enable) {
directMemoryAccessEnabled = enable;
}
void setDumpShaders(bool enable) {
shouldDumpShaders = enable;
}
@ -387,6 +444,7 @@ void setCursorState(s16 newCursorState) {
void setCursorHideTimeout(int newcursorHideTimeout) {
cursorHideTimeout = newcursorHideTimeout;
}
void setTrophyNotificationDuration(double newTrophyNotificationDuration) {
trophyNotificationDuration = newTrophyNotificationDuration;
}
@ -547,79 +605,104 @@ void load(const std::filesystem::path& path) {
fmt::print("Got exception trying to load config file. Exception: {}\n", ex.what());
return;
}
u64 entry_count = 0;
if (data.contains("General")) {
const toml::value& general = data.at("General");
isNeo = toml::find_or<bool>(general, "isPS4Pro", false);
isDevKit = toml::find_or<bool>(general, "isDevKit", false);
isPSNSignedIn = toml::find_or<bool>(general, "isPSNSignedIn", false);
isTrophyPopupDisabled = toml::find_or<bool>(general, "isTrophyPopupDisabled", false);
trophyNotificationDuration =
toml::find_or<double>(general, "trophyNotificationDuration", 5.0);
enableDiscordRPC = toml::find_or<bool>(general, "enableDiscordRPC", true);
logFilter = toml::find_or<std::string>(general, "logFilter", "");
logType = toml::find_or<std::string>(general, "logType", "sync");
userName = toml::find_or<std::string>(general, "userName", "shadPS4");
isShowSplash = toml::find_or<bool>(general, "showSplash", true);
isSideTrophy = toml::find_or<std::string>(general, "sideTrophy", "right");
compatibilityData = toml::find_or<bool>(general, "compatibilityEnabled", false);
checkCompatibilityOnStartup =
toml::find_or<bool>(general, "checkCompatibilityOnStartup", false);
chooseHomeTab = toml::find_or<std::string>(general, "chooseHomeTab", "Release");
isNeo = toml::find_or<bool>(general, "isPS4Pro", isNeo);
isDevKit = toml::find_or<bool>(general, "isDevKit", isDevKit);
isPSNSignedIn = toml::find_or<bool>(general, "isPSNSignedIn", isPSNSignedIn);
isTrophyPopupDisabled =
toml::find_or<bool>(general, "isTrophyPopupDisabled", isTrophyPopupDisabled);
trophyNotificationDuration = toml::find_or<double>(general, "trophyNotificationDuration",
trophyNotificationDuration);
enableDiscordRPC = toml::find_or<bool>(general, "enableDiscordRPC", enableDiscordRPC);
logFilter = toml::find_or<std::string>(general, "logFilter", logFilter);
logType = toml::find_or<std::string>(general, "logType", logType);
userName = toml::find_or<std::string>(general, "userName", userName);
isShowSplash = toml::find_or<bool>(general, "showSplash", isShowSplash);
isSideTrophy = toml::find_or<std::string>(general, "sideTrophy", isSideTrophy);
compatibilityData = toml::find_or<bool>(general, "compatibilityEnabled", compatibilityData);
checkCompatibilityOnStartup = toml::find_or<bool>(general, "checkCompatibilityOnStartup",
checkCompatibilityOnStartup);
chooseHomeTab = toml::find_or<std::string>(general, "chooseHomeTab", chooseHomeTab);
entry_count += general.size();
}
if (data.contains("Input")) {
const toml::value& input = data.at("Input");
cursorState = toml::find_or<int>(input, "cursorState", HideCursorState::Idle);
cursorHideTimeout = toml::find_or<int>(input, "cursorHideTimeout", 5);
useSpecialPad = toml::find_or<bool>(input, "useSpecialPad", false);
specialPadClass = toml::find_or<int>(input, "specialPadClass", 1);
isMotionControlsEnabled = toml::find_or<bool>(input, "isMotionControlsEnabled", true);
useUnifiedInputConfig = toml::find_or<bool>(input, "useUnifiedInputConfig", true);
cursorState = toml::find_or<int>(input, "cursorState", cursorState);
cursorHideTimeout = toml::find_or<int>(input, "cursorHideTimeout", cursorHideTimeout);
useSpecialPad = toml::find_or<bool>(input, "useSpecialPad", useSpecialPad);
specialPadClass = toml::find_or<int>(input, "specialPadClass", specialPadClass);
isMotionControlsEnabled =
toml::find_or<bool>(input, "isMotionControlsEnabled", isMotionControlsEnabled);
useUnifiedInputConfig =
toml::find_or<bool>(input, "useUnifiedInputConfig", useUnifiedInputConfig);
entry_count += input.size();
}
if (data.contains("GPU")) {
const toml::value& gpu = data.at("GPU");
screenWidth = toml::find_or<int>(gpu, "screenWidth", screenWidth);
screenHeight = toml::find_or<int>(gpu, "screenHeight", screenHeight);
isNullGpu = toml::find_or<bool>(gpu, "nullGpu", false);
shouldCopyGPUBuffers = toml::find_or<bool>(gpu, "copyGPUBuffers", false);
shouldDumpShaders = toml::find_or<bool>(gpu, "dumpShaders", false);
shouldPatchShaders = toml::find_or<bool>(gpu, "patchShaders", true);
vblankDivider = toml::find_or<int>(gpu, "vblankDivider", 1);
isFullscreen = toml::find_or<bool>(gpu, "Fullscreen", false);
fullscreenMode = toml::find_or<std::string>(gpu, "FullscreenMode", "Windowed");
isHDRAllowed = toml::find_or<bool>(gpu, "allowHDR", false);
windowWidth = toml::find_or<int>(gpu, "screenWidth", windowWidth);
windowHeight = toml::find_or<int>(gpu, "screenHeight", windowHeight);
internalScreenWidth = toml::find_or<int>(gpu, "internalScreenWidth", internalScreenWidth);
internalScreenHeight =
toml::find_or<int>(gpu, "internalScreenHeight", internalScreenHeight);
isNullGpu = toml::find_or<bool>(gpu, "nullGpu", isNullGpu);
shouldCopyGPUBuffers = toml::find_or<bool>(gpu, "copyGPUBuffers", shouldCopyGPUBuffers);
readbacksEnabled = toml::find_or<bool>(gpu, "readbacks", readbacksEnabled);
readbackLinearImagesEnabled =
toml::find_or<bool>(gpu, "readbackLinearImages", readbackLinearImagesEnabled);
directMemoryAccessEnabled =
toml::find_or<bool>(gpu, "directMemoryAccess", directMemoryAccessEnabled);
shouldDumpShaders = toml::find_or<bool>(gpu, "dumpShaders", shouldDumpShaders);
shouldPatchShaders = toml::find_or<bool>(gpu, "patchShaders", shouldPatchShaders);
vblankDivider = toml::find_or<int>(gpu, "vblankDivider", vblankDivider);
isFullscreen = toml::find_or<bool>(gpu, "Fullscreen", isFullscreen);
fullscreenMode = toml::find_or<std::string>(gpu, "FullscreenMode", fullscreenMode);
isHDRAllowed = toml::find_or<bool>(gpu, "allowHDR", isHDRAllowed);
entry_count += gpu.size();
}
if (data.contains("Vulkan")) {
const toml::value& vk = data.at("Vulkan");
gpuId = toml::find_or<int>(vk, "gpuId", -1);
vkValidation = toml::find_or<bool>(vk, "validation", false);
vkValidationSync = toml::find_or<bool>(vk, "validation_sync", false);
vkValidationGpu = toml::find_or<bool>(vk, "validation_gpu", true);
vkCrashDiagnostic = toml::find_or<bool>(vk, "crashDiagnostic", false);
vkHostMarkers = toml::find_or<bool>(vk, "hostMarkers", false);
vkGuestMarkers = toml::find_or<bool>(vk, "guestMarkers", false);
rdocEnable = toml::find_or<bool>(vk, "rdocEnable", false);
gpuId = toml::find_or<int>(vk, "gpuId", gpuId);
vkValidation = toml::find_or<bool>(vk, "validation", vkValidation);
vkValidationSync = toml::find_or<bool>(vk, "validation_sync", vkValidationSync);
vkValidationGpu = toml::find_or<bool>(vk, "validation_gpu", vkValidationGpu);
vkCrashDiagnostic = toml::find_or<bool>(vk, "crashDiagnostic", vkCrashDiagnostic);
vkHostMarkers = toml::find_or<bool>(vk, "hostMarkers", vkHostMarkers);
vkGuestMarkers = toml::find_or<bool>(vk, "guestMarkers", vkGuestMarkers);
rdocEnable = toml::find_or<bool>(vk, "rdocEnable", rdocEnable);
entry_count += vk.size();
}
if (data.contains("Debug")) {
const toml::value& debug = data.at("Debug");
isDebugDump = toml::find_or<bool>(debug, "DebugDump", false);
isSeparateLogFilesEnabled = toml::find_or<bool>(debug, "isSeparateLogFilesEnabled", false);
isShaderDebug = toml::find_or<bool>(debug, "CollectShader", false);
isFpsColor = toml::find_or<bool>(debug, "FPSColor", true);
isDebugDump = toml::find_or<bool>(debug, "DebugDump", isDebugDump);
isSeparateLogFilesEnabled =
toml::find_or<bool>(debug, "isSeparateLogFilesEnabled", isSeparateLogFilesEnabled);
isShaderDebug = toml::find_or<bool>(debug, "CollectShader", isShaderDebug);
isFpsColor = toml::find_or<bool>(debug, "FPSColor", isFpsColor);
entry_count += debug.size();
}
if (data.contains("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", load_game_size);
const auto install_dir_array =
toml::find_or<std::vector<std::u8string>>(gui, "installDirs", {});
@ -641,20 +724,32 @@ void load(const std::filesystem::path& path) {
{std::filesystem::path{install_dir_array[i]}, install_dirs_enabled[i]});
}
save_data_path = toml::find_fs_path_or(gui, "saveDataPath", {});
save_data_path = toml::find_fs_path_or(gui, "saveDataPath", save_data_path);
settings_addon_install_dir = toml::find_fs_path_or(gui, "addonInstallDir", {});
settings_addon_install_dir =
toml::find_fs_path_or(gui, "addonInstallDir", settings_addon_install_dir);
entry_count += gui.size();
}
if (data.contains("Settings")) {
const toml::value& settings = data.at("Settings");
m_language = toml::find_or<int>(settings, "consoleLanguage", m_language);
m_language = toml::find_or<int>(settings, "consoleLanguage", 1);
entry_count += settings.size();
}
if (data.contains("Keys")) {
const toml::value& keys = data.at("Keys");
trophyKey = toml::find_or<std::string>(keys, "TrophyKey", "");
trophyKey = toml::find_or<std::string>(keys, "TrophyKey", trophyKey);
entry_count += keys.size();
}
// Run save after loading to generate any missing fields with default values.
if (entry_count != total_entries) {
fmt::print("Outdated config detected, updating config file.\n");
save(path);
}
}
@ -731,10 +826,15 @@ void save(const std::filesystem::path& path) {
data["Input"]["specialPadClass"] = specialPadClass;
data["Input"]["isMotionControlsEnabled"] = isMotionControlsEnabled;
data["Input"]["useUnifiedInputConfig"] = useUnifiedInputConfig;
data["GPU"]["screenWidth"] = screenWidth;
data["GPU"]["screenHeight"] = screenHeight;
data["GPU"]["screenWidth"] = windowWidth;
data["GPU"]["screenHeight"] = windowHeight;
data["GPU"]["internalScreenWidth"] = internalScreenWidth;
data["GPU"]["internalScreenHeight"] = internalScreenHeight;
data["GPU"]["nullGpu"] = isNullGpu;
data["GPU"]["copyGPUBuffers"] = shouldCopyGPUBuffers;
data["GPU"]["readbacks"] = readbacksEnabled;
data["GPU"]["readbackLinearImages"] = readbackLinearImagesEnabled;
data["GPU"]["directMemoryAccess"] = directMemoryAccessEnabled;
data["GPU"]["dumpShaders"] = shouldDumpShaders;
data["GPU"]["patchShaders"] = shouldPatchShaders;
data["GPU"]["vblankDivider"] = vblankDivider;
@ -800,32 +900,53 @@ void save(const std::filesystem::path& path) {
}
void setDefaultValues() {
isHDRAllowed = false;
// General
isNeo = false;
isDevKit = false;
isPSNSignedIn = false;
isFullscreen = false;
isTrophyPopupDisabled = false;
enableDiscordRPC = true;
screenWidth = 1280;
screenHeight = 720;
trophyNotificationDuration = 6.0;
enableDiscordRPC = false;
logFilter = "";
logType = "sync";
userName = "shadPS4";
chooseHomeTab = "General";
cursorState = HideCursorState::Idle;
cursorHideTimeout = 5;
trophyNotificationDuration = 6.0;
useSpecialPad = false;
specialPadClass = 1;
isDebugDump = false;
isShaderDebug = false;
isShowSplash = false;
isSideTrophy = "right";
compatibilityData = false;
checkCompatibilityOnStartup = false;
// Input
cursorState = HideCursorState::Idle;
cursorHideTimeout = 5;
useSpecialPad = false;
specialPadClass = 1;
isMotionControlsEnabled = true;
useUnifiedInputConfig = true;
overrideControllerColor = false;
controllerCustomColorRGB[0] = 0;
controllerCustomColorRGB[1] = 0;
controllerCustomColorRGB[2] = 255;
// GPU
windowWidth = 1280;
windowHeight = 720;
internalScreenWidth = 1280;
internalScreenHeight = 720;
isNullGpu = false;
shouldCopyGPUBuffers = false;
readbacksEnabled = false;
readbackLinearImagesEnabled = false;
directMemoryAccessEnabled = false;
shouldDumpShaders = false;
shouldPatchShaders = false;
vblankDivider = 1;
isFullscreen = false;
fullscreenMode = "Windowed";
isHDRAllowed = false;
// Vulkan
gpuId = -1;
vkValidation = false;
vkValidationSync = false;
vkValidationGpu = false;
@ -833,10 +954,18 @@ void setDefaultValues() {
vkHostMarkers = false;
vkGuestMarkers = false;
rdocEnable = false;
// Debug
isDebugDump = false;
isShaderDebug = false;
isSeparateLogFilesEnabled = false;
isFpsColor = true;
// GUI
load_game_size = true;
// Settings
m_language = 1;
gpuId = -1;
compatibilityData = false;
checkCompatibilityOnStartup = false;
}
constexpr std::string_view GetDefaultKeyboardConfig() {

View File

@ -25,10 +25,14 @@ bool getIsFullscreen();
void setIsFullscreen(bool enable);
std::string getFullscreenMode();
void setFullscreenMode(std::string mode);
u32 getScreenWidth();
u32 getScreenHeight();
void setScreenWidth(u32 width);
void setScreenHeight(u32 height);
u32 getWindowWidth();
u32 getWindowHeight();
void setWindowWidth(u32 width);
void setWindowHeight(u32 height);
u32 getInternalScreenWidth();
u32 getInternalScreenHeight();
void setInternalScreenWidth(u32 width);
void setInternalScreenHeight(u32 height);
bool debugDump();
void setDebugDump(bool enable);
s32 getGpuId();
@ -45,6 +49,11 @@ bool nullGpu();
void setNullGpu(bool enable);
bool copyGPUCmdBuffers();
void setCopyGPUCmdBuffers(bool enable);
bool readbacks();
void setReadbacks(bool enable);
bool readbackLinearImages();
bool directMemoryAccess();
void setDirectMemoryAccess(bool enable);
bool dumpShaders();
void setDumpShaders(bool enable);
u32 vblankDiv();

101
src/common/range_lock.h Normal file
View File

@ -0,0 +1,101 @@
// SPDX-FileCopyrightText: Copyright 2025 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <iterator>
#include <mutex>
namespace Common {
// From boost thread locking
template <typename Iterator>
struct RangeLockGuard {
Iterator begin;
Iterator end;
RangeLockGuard(Iterator begin_, Iterator end_) : begin(begin_), end(end_) {
LockRange(begin, end);
}
void release() {
begin = end;
}
~RangeLockGuard() {
for (; begin != end; ++begin) {
begin->unlock();
}
}
};
template <typename Iterator>
Iterator TryLockRange(Iterator begin, Iterator end) {
using LockType = typename std::iterator_traits<Iterator>::value_type;
if (begin == end) {
return end;
}
std::unique_lock<LockType> guard(*begin, std::try_to_lock);
if (!guard.owns_lock()) {
return begin;
}
Iterator failed = TryLockRange(++begin, end);
if (failed == end) {
guard.release();
}
return failed;
}
template <typename Iterator>
void LockRange(Iterator begin, Iterator end) {
using LockType = typename std::iterator_traits<Iterator>::value_type;
if (begin == end) {
return;
}
bool start_with_begin = true;
Iterator second = begin;
++second;
Iterator next = second;
while (true) {
std::unique_lock<LockType> begin_lock(*begin, std::defer_lock);
if (start_with_begin) {
begin_lock.lock();
const Iterator failed_lock = TryLockRange(next, end);
if (failed_lock == end) {
begin_lock.release();
return;
}
start_with_begin = false;
next = failed_lock;
} else {
RangeLockGuard<Iterator> guard(next, end);
if (begin_lock.try_lock()) {
const Iterator failed_lock = TryLockRange(second, next);
if (failed_lock == next) {
begin_lock.release();
guard.release();
return;
}
start_with_begin = false;
next = failed_lock;
} else {
start_with_begin = true;
next = second;
}
}
}
}
} // namespace Common

View File

@ -0,0 +1,46 @@
// SPDX-FileCopyrightText: Copyright 2025 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <condition_variable>
#include <mutex>
namespace Common {
// Like std::shared_mutex, but reader has priority over writer.
class SharedFirstMutex {
public:
void lock() {
std::unique_lock<std::mutex> lock(mtx);
cv.wait(lock, [this]() { return !writer_active && readers == 0; });
writer_active = true;
}
void unlock() {
std::lock_guard<std::mutex> lock(mtx);
writer_active = false;
cv.notify_all();
}
void lock_shared() {
std::unique_lock<std::mutex> lock(mtx);
cv.wait(lock, [this]() { return !writer_active; });
++readers;
}
void unlock_shared() {
std::lock_guard<std::mutex> lock(mtx);
if (--readers == 0) {
cv.notify_all();
}
}
private:
std::mutex mtx;
std::condition_variable cv;
int readers = 0;
bool writer_active = false;
};
} // namespace Common

View File

@ -302,14 +302,15 @@ struct AddressSpace::Impl {
new_flags = PAGE_READWRITE;
} else if (read && !write) {
new_flags = PAGE_READONLY;
} else if (execute && !read && not write) {
} else if (execute && !read && !write) {
new_flags = PAGE_EXECUTE;
} else if (!read && !write && !execute) {
new_flags = PAGE_NOACCESS;
} else {
LOG_CRITICAL(Common_Memory,
"Unsupported protection flag combination for address {:#x}, size {}",
virtual_addr, size);
"Unsupported protection flag combination for address {:#x}, size {}, "
"read={}, write={}, execute={}",
virtual_addr, size, read, write, execute);
return;
}
@ -357,9 +358,17 @@ enum PosixPageProtection {
[[nodiscard]] constexpr PosixPageProtection ToPosixProt(Core::MemoryProt prot) {
if (True(prot & Core::MemoryProt::CpuReadWrite) ||
True(prot & Core::MemoryProt::GpuReadWrite)) {
return PAGE_READWRITE;
if (True(prot & Core::MemoryProt::CpuExec)) {
return PAGE_EXECUTE_READWRITE;
} else {
return PAGE_READWRITE;
}
} else if (True(prot & Core::MemoryProt::CpuRead) || True(prot & Core::MemoryProt::GpuRead)) {
return PAGE_READONLY;
if (True(prot & Core::MemoryProt::CpuExec)) {
return PAGE_EXECUTE_READ;
} else {
return PAGE_READONLY;
}
} else {
return PAGE_NOACCESS;
}

View File

@ -11,6 +11,7 @@
namespace Core {
enum class MemoryPermission : u32 {
None = 0,
Read = 1 << 0,
Write = 1 << 1,
ReadWrite = Read | Write,

View File

@ -163,7 +163,9 @@ static void GenerateEXTRQ(void* /* address */, const ZydisDecodedOperand* operan
mask = (1ULL << length) - 1;
}
ASSERT_MSG(length + index <= 64, "length + index must be less than or equal to 64.");
if (length + index > 64) {
mask = 0xFFFF'FFFF'FFFF'FFFF;
}
// Get lower qword from xmm register
c.vmovq(scratch1, xmm_dst);
@ -177,8 +179,8 @@ static void GenerateEXTRQ(void* /* address */, const ZydisDecodedOperand* operan
c.mov(scratch2, mask);
c.and_(scratch1, scratch2);
// Writeback to xmm register, extrq instruction says top 64-bits are undefined so we don't
// care to preserve them
// Writeback to xmm register, extrq instruction says top 64-bits are undefined but zeroed on
// AMD CPUs
c.vmovq(xmm_dst, scratch1);
c.pop(scratch2);
@ -287,7 +289,9 @@ static void GenerateINSERTQ(void* /* address */, const ZydisDecodedOperand* oper
mask_value = (1ULL << length) - 1;
}
ASSERT_MSG(length + index <= 64, "length + index must be less than or equal to 64.");
if (length + index > 64) {
mask_value = 0xFFFF'FFFF'FFFF'FFFF;
}
c.vmovq(scratch1, xmm_src);
c.vmovq(scratch2, xmm_dst);
@ -307,8 +311,9 @@ static void GenerateINSERTQ(void* /* address */, const ZydisDecodedOperand* oper
// dst |= src
c.or_(scratch2, scratch1);
// Insert scratch2 into low 64 bits of dst, upper 64 bits are unaffected
c.vpinsrq(xmm_dst, xmm_dst, scratch2, 0);
// Insert scratch2 into low 64 bits of dst, upper 64 bits are undefined but zeroed on AMD
// CPUs
c.vmovq(xmm_dst, scratch2);
c.pop(mask);
c.pop(scratch2);
@ -374,7 +379,7 @@ static void GenerateINSERTQ(void* /* address */, const ZydisDecodedOperand* oper
c.and_(scratch2, mask);
c.or_(scratch2, scratch1);
// Upper 64 bits are undefined in insertq
// Upper 64 bits are undefined in insertq but AMD CPUs zero them
c.vmovq(xmm_dst, scratch2);
c.pop(mask);
@ -635,6 +640,7 @@ static bool TryExecuteIllegalInstruction(void* ctx, void* code_address) {
lowQWordDst >>= index;
lowQWordDst &= mask;
memset((u8*)dst + sizeof(u64), 0, sizeof(u64));
memcpy(dst, &lowQWordDst, sizeof(lowQWordDst));
Common::IncrementRip(ctx, 4);
@ -675,6 +681,7 @@ static bool TryExecuteIllegalInstruction(void* ctx, void* code_address) {
lowQWordDst &= ~(mask << index);
lowQWordDst |= lowQWordSrc << index;
memset((u8*)dst + sizeof(u64), 0, sizeof(u64));
memcpy(dst, &lowQWordDst, sizeof(lowQWordDst));
Common::IncrementRip(ctx, 4);

View File

@ -2834,7 +2834,7 @@ void RegisterlibSceGnmDriver(Core::Loader::SymbolsResolver* sym) {
}
if (Config::copyGPUCmdBuffers()) {
liverpool->reserveCopyBufferSpace();
liverpool->ReserveCopyBufferSpace();
}
Platform::IrqC::Instance()->Register(Platform::InterruptId::GpuIdle, ResetSubmissionLock,

View File

@ -43,8 +43,8 @@ public:
openEvent.param.rect.x = m_param.ime.posx;
openEvent.param.rect.y = m_param.ime.posy;
} else {
openEvent.param.resource_id_array.userId = 1;
openEvent.param.resource_id_array.resourceId[0] = 1;
openEvent.param.resource_id_array.user_id = 1;
openEvent.param.resource_id_array.resource_id[0] = 1;
}
// Are we supposed to call the event handler on init with
@ -59,10 +59,10 @@ public:
}
}
s32 Update(OrbisImeEventHandler handler) {
Error Update(OrbisImeEventHandler handler) {
if (!m_ime_mode) {
/* We don't handle any events for ImeKeyboard */
return ORBIS_OK;
return Error::OK;
}
std::unique_lock lock{g_ime_state.queue_mutex};
@ -73,7 +73,7 @@ public:
Execute(handler, &event, false);
}
return ORBIS_OK;
return Error::OK;
}
void Execute(OrbisImeEventHandler handler, OrbisImeEvent* event, bool use_param_handler) {
@ -94,14 +94,14 @@ public:
}
}
s32 SetText(const char16_t* text, u32 length) {
Error SetText(const char16_t* text, u32 length) {
g_ime_state.SetText(text, length);
return ORBIS_OK;
return Error::OK;
}
s32 SetCaret(const OrbisImeCaret* caret) {
Error SetCaret(const OrbisImeCaret* caret) {
g_ime_state.SetCaret(caret->index);
return ORBIS_OK;
return Error::OK;
}
bool IsIme() {
@ -222,11 +222,11 @@ int PS4_SYSV_ABI sceImeGetPanelPositionAndForm() {
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceImeGetPanelSize(const OrbisImeParam* param, u32* width, u32* height) {
Error PS4_SYSV_ABI sceImeGetPanelSize(const OrbisImeParam* param, u32* width, u32* height) {
LOG_INFO(Lib_Ime, "called");
if (!width || !height) {
return ORBIS_IME_ERROR_INVALID_ADDRESS;
return Error::INVALID_ADDRESS;
}
switch (param->type) {
@ -244,18 +244,18 @@ s32 PS4_SYSV_ABI sceImeGetPanelSize(const OrbisImeParam* param, u32* width, u32*
break;
}
return ORBIS_OK;
return Error::OK;
}
s32 PS4_SYSV_ABI sceImeKeyboardClose(s32 userId) {
Error PS4_SYSV_ABI sceImeKeyboardClose(s32 userId) {
LOG_INFO(Lib_Ime, "(STUBBED) called");
if (!g_keyboard_handler) {
return ORBIS_IME_ERROR_NOT_OPENED;
return Error::NOT_OPENED;
}
g_keyboard_handler.release();
return ORBIS_OK;
return Error::OK;
}
int PS4_SYSV_ABI sceImeKeyboardGetInfo() {
@ -268,25 +268,25 @@ int PS4_SYSV_ABI sceImeKeyboardGetResourceId() {
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceImeKeyboardOpen(s32 userId, const OrbisImeKeyboardParam* param) {
Error PS4_SYSV_ABI sceImeKeyboardOpen(s32 userId, const OrbisImeKeyboardParam* param) {
LOG_INFO(Lib_Ime, "called");
if (!param) {
return ORBIS_IME_ERROR_INVALID_ADDRESS;
return Error::INVALID_ADDRESS;
}
if (!param->arg) {
return ORBIS_IME_ERROR_INVALID_ARG;
return Error::INVALID_ARG;
}
if (!param->handler) {
return ORBIS_IME_ERROR_INVALID_HANDLER;
return Error::INVALID_HANDLER;
}
if (g_keyboard_handler) {
return ORBIS_IME_ERROR_BUSY;
return Error::BUSY;
}
g_keyboard_handler = std::make_unique<ImeHandler>(param);
return ORBIS_OK;
return Error::OK;
}
int PS4_SYSV_ABI sceImeKeyboardOpenInternal() {
@ -304,18 +304,18 @@ int PS4_SYSV_ABI sceImeKeyboardUpdate() {
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceImeOpen(const OrbisImeParam* param, const void* extended) {
Error PS4_SYSV_ABI sceImeOpen(const OrbisImeParam* param, const OrbisImeParamExtended* extended) {
LOG_INFO(Lib_Ime, "called");
if (!param) {
return ORBIS_IME_ERROR_INVALID_ADDRESS;
return Error::INVALID_ADDRESS;
}
if (g_ime_handler) {
return ORBIS_IME_ERROR_BUSY;
return Error::BUSY;
}
g_ime_handler = std::make_unique<ImeHandler>(param);
return ORBIS_OK;
return Error::OK;
}
int PS4_SYSV_ABI sceImeOpenInternal() {
@ -339,27 +339,27 @@ int PS4_SYSV_ABI sceImeSetCandidateIndex() {
return ORBIS_OK;
}
int PS4_SYSV_ABI sceImeSetCaret(const OrbisImeCaret* caret) {
Error PS4_SYSV_ABI sceImeSetCaret(const OrbisImeCaret* caret) {
LOG_TRACE(Lib_Ime, "called");
if (!g_ime_handler) {
return ORBIS_IME_ERROR_NOT_OPENED;
return Error::NOT_OPENED;
}
if (!caret) {
return ORBIS_IME_ERROR_INVALID_ADDRESS;
return Error::INVALID_ADDRESS;
}
return g_ime_handler->SetCaret(caret);
}
s32 PS4_SYSV_ABI sceImeSetText(const char16_t* text, u32 length) {
Error PS4_SYSV_ABI sceImeSetText(const char16_t* text, u32 length) {
LOG_TRACE(Lib_Ime, "called");
if (!g_ime_handler) {
return ORBIS_IME_ERROR_NOT_OPENED;
return Error::NOT_OPENED;
}
if (!text) {
return ORBIS_IME_ERROR_INVALID_ADDRESS;
return Error::INVALID_ADDRESS;
}
return g_ime_handler->SetText(text, length);
@ -370,7 +370,7 @@ int PS4_SYSV_ABI sceImeSetTextGeometry() {
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceImeUpdate(OrbisImeEventHandler handler) {
Error PS4_SYSV_ABI sceImeUpdate(OrbisImeEventHandler handler) {
if (g_ime_handler) {
g_ime_handler->Update(handler);
}
@ -380,10 +380,10 @@ s32 PS4_SYSV_ABI sceImeUpdate(OrbisImeEventHandler handler) {
}
if (!g_ime_handler || !g_keyboard_handler) {
return ORBIS_IME_ERROR_NOT_OPENED;
return Error::NOT_OPENED;
}
return ORBIS_OK;
return Error::OK;
}
int PS4_SYSV_ABI sceImeVshClearPreedit() {

View File

@ -13,72 +13,6 @@ class SymbolsResolver;
namespace Libraries::Ime {
constexpr u32 ORBIS_IME_MAX_TEXT_LENGTH = 2048;
enum class OrbisImeKeyboardOption : u32 {
Default = 0,
Repeat = 1,
RepeatEachKey = 2,
AddOsk = 4,
EffectiveWithIme = 8,
DisableResume = 16,
DisableCapslockWithoutShift = 32,
};
DECLARE_ENUM_FLAG_OPERATORS(OrbisImeKeyboardOption)
enum class OrbisImeOption : u32 {
DEFAULT = 0,
MULTILINE = 1,
NO_AUTO_CAPITALIZATION = 2,
PASSWORD = 4,
LANGUAGES_FORCED = 8,
EXT_KEYBOARD = 16,
NO_LEARNING = 32,
FIXED_POSITION = 64,
DISABLE_RESUME = 256,
DISABLE_AUTO_SPACE = 512,
DISABLE_POSITION_ADJUSTMENT = 2048,
EXPANDED_PREEDIT_BUFFER = 4096,
USE_JAPANESE_EISUU_KEY_AS_CAPSLOCK = 8192,
USE_2K_COORDINATES = 16384,
};
DECLARE_ENUM_FLAG_OPERATORS(OrbisImeOption)
struct OrbisImeKeyboardParam {
OrbisImeKeyboardOption option;
s8 reserved1[4];
void* arg;
OrbisImeEventHandler handler;
s8 reserved2[8];
};
struct OrbisImeParam {
s32 user_id;
OrbisImeType type;
u64 supported_languages;
OrbisImeEnterLabel enter_label;
OrbisImeInputMethod input_method;
OrbisImeTextFilter filter;
OrbisImeOption option;
u32 maxTextLength;
char16_t* inputTextBuffer;
float posx;
float posy;
OrbisImeHorizontalAlignment horizontal_alignment;
OrbisImeVerticalAlignment vertical_alignment;
void* work;
void* arg;
OrbisImeEventHandler handler;
s8 reserved[8];
};
struct OrbisImeCaret {
f32 x;
f32 y;
u32 height;
u32 index;
};
int PS4_SYSV_ABI FinalizeImeModule();
int PS4_SYSV_ABI InitializeImeModule();
int PS4_SYSV_ABI sceImeCheckFilterText();
@ -98,22 +32,22 @@ int PS4_SYSV_ABI sceImeDisableController();
int PS4_SYSV_ABI sceImeFilterText();
int PS4_SYSV_ABI sceImeForTestFunction();
int PS4_SYSV_ABI sceImeGetPanelPositionAndForm();
s32 PS4_SYSV_ABI sceImeGetPanelSize(const OrbisImeParam* param, u32* width, u32* height);
s32 PS4_SYSV_ABI sceImeKeyboardClose(s32 userId);
Error PS4_SYSV_ABI sceImeGetPanelSize(const OrbisImeParam* param, u32* width, u32* height);
Error PS4_SYSV_ABI sceImeKeyboardClose(s32 userId);
int PS4_SYSV_ABI sceImeKeyboardGetInfo();
int PS4_SYSV_ABI sceImeKeyboardGetResourceId();
s32 PS4_SYSV_ABI sceImeKeyboardOpen(s32 userId, const OrbisImeKeyboardParam* param);
Error PS4_SYSV_ABI sceImeKeyboardOpen(s32 userId, const OrbisImeKeyboardParam* param);
int PS4_SYSV_ABI sceImeKeyboardOpenInternal();
int PS4_SYSV_ABI sceImeKeyboardSetMode();
int PS4_SYSV_ABI sceImeKeyboardUpdate();
s32 PS4_SYSV_ABI sceImeOpen(const OrbisImeParam* param, const void* extended);
Error PS4_SYSV_ABI sceImeOpen(const OrbisImeParam* param, const OrbisImeParamExtended* extended);
int PS4_SYSV_ABI sceImeOpenInternal();
void PS4_SYSV_ABI sceImeParamInit(OrbisImeParam* param);
int PS4_SYSV_ABI sceImeSetCandidateIndex();
s32 PS4_SYSV_ABI sceImeSetCaret(const OrbisImeCaret* caret);
s32 PS4_SYSV_ABI sceImeSetText(const char16_t* text, u32 length);
Error PS4_SYSV_ABI sceImeSetCaret(const OrbisImeCaret* caret);
Error PS4_SYSV_ABI sceImeSetText(const char16_t* text, u32 length);
int PS4_SYSV_ABI sceImeSetTextGeometry();
s32 PS4_SYSV_ABI sceImeUpdate(OrbisImeEventHandler handler);
Error PS4_SYSV_ABI sceImeUpdate(OrbisImeEventHandler handler);
int PS4_SYSV_ABI sceImeVshClearPreedit();
int PS4_SYSV_ABI sceImeVshClose();
int PS4_SYSV_ABI sceImeVshConfirmPreedit();

View File

@ -3,9 +3,108 @@
#pragma once
#include "common/enum.h"
#include "common/types.h"
#include "core/libraries/rtc/rtc.h"
constexpr u32 ORBIS_IME_MAX_TEXT_LENGTH = 2048;
constexpr u32 ORBIS_IME_DIALOG_MAX_TEXT_LENGTH = 2048;
enum class Error : u32 {
OK = 0x0,
BUSY = 0x80bc0001,
NOT_OPENED = 0x80bc0002,
NO_MEMORY = 0x80bc0003,
CONNECTION_FAILED = 0x80bc0004,
TOO_MANY_REQUESTS = 0x80bc0005,
INVALID_TEXT = 0x80bc0006,
EVENT_OVERFLOW = 0x80bc0007,
NOT_ACTIVE = 0x80bc0008,
IME_SUSPENDING = 0x80bc0009,
DEVICE_IN_USE = 0x80bc000a,
INVALID_USER_ID = 0x80bc0010,
INVALID_TYPE = 0x80bc0011,
INVALID_SUPPORTED_LANGUAGES = 0x80bc0012,
INVALID_ENTER_LABEL = 0x80bc0013,
INVALID_INPUT_METHOD = 0x80bc0014,
INVALID_OPTION = 0x80bc0015,
INVALID_MAX_TEXT_LENGTH = 0x80bc0016,
INVALID_INPUT_TEXT_BUFFER = 0x80bc0017,
INVALID_POSX = 0x80bc0018,
INVALID_POSY = 0x80bc0019,
INVALID_HORIZONTALIGNMENT = 0x80bc001a,
INVALID_VERTICALALIGNMENT = 0x80bc001b,
INVALID_EXTENDED = 0x80bc001c,
INVALID_KEYBOARD_TYPE = 0x80bc001d,
INVALID_WORK = 0x80bc0020,
INVALID_ARG = 0x80bc0021,
INVALID_HANDLER = 0x80bc0022,
NO_RESOURCE_ID = 0x80bc0023,
INVALID_MODE = 0x80bc0024,
INVALID_PARAM = 0x80bc0030,
INVALID_ADDRESS = 0x80bc0031,
INVALID_RESERVED = 0x80bc0032,
INVALID_TIMING = 0x80bc0033,
INTERNAL = 0x80bc00ff,
DIALOG_INVALID_TITLE = 0x80bc0101,
DIALOG_NOT_RUNNING = 0x80bc0105,
DIALOG_NOT_FINISHED = 0x80bc0106,
DIALOG_NOT_IN_USE = 0x80bc0107
};
enum class OrbisImeOption : u32 {
DEFAULT = 0,
MULTILINE = 1,
NO_AUTO_CAPITALIZATION = 2,
PASSWORD = 4,
LANGUAGES_FORCED = 8,
EXT_KEYBOARD = 16,
NO_LEARNING = 32,
FIXED_POSITION = 64,
DISABLE_COPY_PASTE = 128,
DISABLE_RESUME = 256,
DISABLE_AUTO_SPACE = 512,
DISABLE_POSITION_ADJUSTMENT = 2048,
EXPANDED_PREEDIT_BUFFER = 4096,
USE_JAPANESE_EISUU_KEY_AS_CAPSLOCK = 8192,
USE_2K_COORDINATES = 16384,
};
DECLARE_ENUM_FLAG_OPERATORS(OrbisImeOption);
enum class OrbisImeLanguage : u64 {
DANISH = 0x0000000000000001,
GERMAN = 0x0000000000000002,
ENGLISH_US = 0x0000000000000004,
SPANISH = 0x0000000000000008,
FRENCH = 0x0000000000000010,
ITALIAN = 0x0000000000000020,
DUTCH = 0x0000000000000040,
NORWEGIAN = 0x0000000000000080,
POLISH = 0x0000000000000100,
PORTUGUESE_PT = 0x0000000000000200,
RUSSIAN = 0x0000000000000400,
FINNISH = 0x0000000000000800,
SWEDISH = 0x0000000000001000,
JAPANESE = 0x0000000000002000,
KOREAN = 0x0000000000004000,
SIMPLIFIED_CHINESE = 0x0000000000008000,
TRADITIONAL_CHINESE = 0x0000000000010000,
PORTUGUESE_BR = 0x0000000000020000,
ENGLISH_GB = 0x0000000000040000,
TURKISH = 0x0000000000080000,
SPANISH_LA = 0x0000000000100000,
ARABIC = 0x0000000001000000,
FRENCH_CA = 0x0000000002000000,
THAI = 0x0000000004000000,
CZECH = 0x0000000008000000,
GREEK = 0x0000000010000000,
INDONESIAN = 0x0000000020000000,
VIETNAMESE = 0x0000000040000000,
ROMANIAN = 0x0000000080000000,
HUNGARIAN = 0x0000000100000000,
};
DECLARE_ENUM_FLAG_OPERATORS(OrbisImeLanguage);
enum class OrbisImeType : u32 {
Default = 0,
BasicLatin = 1,
@ -41,6 +140,7 @@ enum class OrbisImeEventId : u32 {
Open = 0,
UpdateText = 1,
UpdateCaret = 2,
ChangeSize = 3,
PressClose = 4,
PressEnter = 5,
Abort = 6,
@ -51,6 +151,10 @@ enum class OrbisImeEventId : u32 {
CandidateDone = 11,
CandidateCancel = 12,
ChangeDevice = 14,
JumpToNextObject = 15,
JumpToBeforeObject = 16,
ChangeWindowType = 17,
ChangeInputMethodState = 18,
KeyboardOpen = 256,
@ -110,6 +214,13 @@ enum class OrbisImeDeviceType : u32 {
RemoteOsk = 3,
};
enum class OrbisImePanelPriority : u32 {
Default = 0,
Alphabet = 1,
Symbol = 2,
Accent = 3,
};
struct OrbisImeRect {
f32 x;
f32 y;
@ -117,8 +228,22 @@ struct OrbisImeRect {
u32 height;
};
struct OrbisImeColor {
u8 r;
u8 g;
u8 b;
u8 a;
};
enum class OrbisImeTextAreaMode : u32 {
Disable = 0,
Edit = 1,
Preedit = 2,
Select = 3,
};
struct OrbisImeTextAreaProperty {
u32 mode; // OrbisImeTextAreaMode
OrbisImeTextAreaMode mode;
u32 index;
s32 length;
};
@ -135,14 +260,14 @@ struct OrbisImeKeycode {
char16_t character;
u32 status;
OrbisImeKeyboardType type;
s32 user_id;
s32 user_id; // Todo: switch to OrbisUserServiceUserId
u32 resource_id;
Libraries::Rtc::OrbisRtcTick timestamp;
};
struct OrbisImeKeyboardResourceIdArray {
s32 userId;
u32 resourceId[5];
s32 user_id; // Todo: switch to OrbisUserServiceUserId
u32 resource_id[5];
};
enum class OrbisImeCaretMovementDirection : u32 {
@ -159,6 +284,16 @@ enum class OrbisImeCaretMovementDirection : u32 {
Bottom = 10,
};
enum class OrbisImePanelType : u32 {
Hide = 0,
Osk = 1,
Dialog = 2,
Candidate = 3,
Edit = 4,
EditAndCandidate = 5,
Accessibility = 6,
};
union OrbisImeEventParam {
OrbisImeRect rect;
OrbisImeEditText text;
@ -168,6 +303,7 @@ union OrbisImeEventParam {
char16_t* candidate_word;
s32 candidate_index;
OrbisImeDeviceType device_type;
OrbisImePanelType panel_type;
u32 input_method_state;
s8 reserved[64];
};
@ -177,7 +313,95 @@ struct OrbisImeEvent {
OrbisImeEventParam param;
};
using OrbisImeExtKeyboardFilter = PS4_SYSV_ABI int (*)(const OrbisImeKeycode* srcKeycode,
u16* outKeycode, u32* outStatus,
void* reserved);
using OrbisImeTextFilter = PS4_SYSV_ABI int (*)(char16_t* outText, u32* outTextLength,
const char16_t* srcText, u32 srcTextLength);
using OrbisImeEventHandler = PS4_SYSV_ABI void (*)(void* arg, const OrbisImeEvent* e);
enum class OrbisImeKeyboardOption : u32 {
Default = 0,
Repeat = 1,
RepeatEachKey = 2,
AddOsk = 4,
EffectiveWithIme = 8,
DisableResume = 16,
DisableCapslockWithoutShift = 32,
};
DECLARE_ENUM_FLAG_OPERATORS(OrbisImeKeyboardOption)
struct OrbisImeKeyboardParam {
OrbisImeKeyboardOption option;
s8 reserved1[4];
void* arg;
OrbisImeEventHandler handler;
s8 reserved2[8];
};
struct OrbisImeParam {
s32 user_id; // Todo: switch to OrbisUserServiceUserId
OrbisImeType type;
u64 supported_languages; // OrbisImeLanguage flags
OrbisImeEnterLabel enter_label;
OrbisImeInputMethod input_method;
OrbisImeTextFilter filter;
OrbisImeOption option;
u32 maxTextLength;
char16_t* inputTextBuffer;
f32 posx;
f32 posy;
OrbisImeHorizontalAlignment horizontal_alignment;
OrbisImeVerticalAlignment vertical_alignment;
void* work;
void* arg;
OrbisImeEventHandler handler;
s8 reserved[8];
};
struct OrbisImeCaret {
f32 x;
f32 y;
u32 height;
u32 index;
};
struct OrbisImeDialogParam {
s32 user_id;
OrbisImeType type;
u64 supported_languages; // OrbisImeLanguage flags
OrbisImeEnterLabel enter_label;
OrbisImeInputMethod input_method;
OrbisImeTextFilter filter;
OrbisImeOption option;
u32 max_text_length;
char16_t* input_text_buffer;
f32 posx;
f32 posy;
OrbisImeHorizontalAlignment horizontal_alignment;
OrbisImeVerticalAlignment vertical_alignment;
const char16_t* placeholder;
const char16_t* title;
s8 reserved[16];
};
struct OrbisImeParamExtended {
u32 option; // OrbisImeExtOption flags
OrbisImeColor color_base;
OrbisImeColor color_line;
OrbisImeColor color_text_field;
OrbisImeColor color_preedit;
OrbisImeColor color_button_default;
OrbisImeColor color_button_function;
OrbisImeColor color_button_symbol;
OrbisImeColor color_text;
OrbisImeColor color_special;
OrbisImePanelPriority priority;
char* additional_dictionary_path;
OrbisImeExtKeyboardFilter ext_keyboard_filter;
u32 disable_device;
u32 ext_keyboard_mode;
s8 reserved[60];
};

View File

@ -20,19 +20,19 @@ static OrbisImeDialogResult g_ime_dlg_result{};
static ImeDialogState g_ime_dlg_state{};
static ImeDialogUi g_ime_dlg_ui;
static bool IsValidOption(OrbisImeDialogOption option, OrbisImeType type) {
if (False(~option &
(OrbisImeDialogOption::Multiline | OrbisImeDialogOption::NoAutoCompletion))) {
static bool IsValidOption(OrbisImeOption option, OrbisImeType type) {
if (False(~option & (OrbisImeOption::MULTILINE |
OrbisImeOption::NO_AUTO_CAPITALIZATION /* NoAutoCompletion */))) {
return false;
}
if (True(option & OrbisImeDialogOption::Multiline) && type != OrbisImeType::Default &&
if (True(option & OrbisImeOption::MULTILINE) && type != OrbisImeType::Default &&
type != OrbisImeType::BasicLatin) {
return false;
}
if (True(option & OrbisImeDialogOption::NoAutoCompletion) && type != OrbisImeType::Number &&
type != OrbisImeType::BasicLatin) {
if (True(option & OrbisImeOption::NO_AUTO_CAPITALIZATION /* NoAutoCompletion */) &&
type != OrbisImeType::Number && type != OrbisImeType::BasicLatin) {
return false;
}
@ -96,7 +96,7 @@ Error PS4_SYSV_ABI sceImeDialogGetPanelSize(const OrbisImeDialogParam* param, u3
case OrbisImeType::Url:
case OrbisImeType::Mail:
*width = 500; // original: 793
if (True(param->option & OrbisImeDialogOption::Multiline)) {
if (True(param->option & OrbisImeOption::MULTILINE)) {
*height = 300; // original: 576
} else {
*height = 150; // original: 476
@ -149,18 +149,20 @@ OrbisImeDialogStatus PS4_SYSV_ABI sceImeDialogGetStatus() {
}
Error PS4_SYSV_ABI sceImeDialogInit(OrbisImeDialogParam* param, OrbisImeParamExtended* extended) {
LOG_INFO(Lib_ImeDialog, ">> sceImeDialogInit: entering, param={}, extended={}",
static_cast<void*>(param), static_cast<void*>(extended));
if (g_ime_dlg_status != OrbisImeDialogStatus::None) {
LOG_INFO(Lib_ImeDialog, "IME dialog is already running");
LOG_ERROR(Lib_ImeDialog, "sceImeDialogInit: busy (status=%u)", (u32)g_ime_dlg_status);
return Error::BUSY;
}
if (param == nullptr) {
LOG_INFO(Lib_ImeDialog, "called with param (NULL)");
LOG_ERROR(Lib_ImeDialog, "sceImeDialogInit: param is null");
return Error::INVALID_ADDRESS;
}
if (!magic_enum::enum_contains(param->type)) {
LOG_INFO(Lib_ImeDialog, "Invalid param->type");
LOG_ERROR(Lib_ImeDialog, "sceImeDialogInit: invalid param->type=%u", (u32)param->type);
return Error::INVALID_ADDRESS;
}
@ -168,16 +170,14 @@ Error PS4_SYSV_ABI sceImeDialogInit(OrbisImeDialogParam* param, OrbisImeParamExt
// TODO: do correct param->supportedLanguages validation
if (param->posx < 0.0f ||
param->posx >=
MAX_X_POSITIONS[False(param->option & OrbisImeDialogOption::LargeResolution)]) {
LOG_INFO(Lib_ImeDialog, "Invalid param->posx");
param->posx >= MAX_X_POSITIONS[False(param->option & OrbisImeOption::USE_2K_COORDINATES)]) {
LOG_ERROR(Lib_ImeDialog, "sceImeDialogInit: invalid posx=%f", param->posx);
return Error::INVALID_POSX;
}
if (param->posy < 0.0f ||
param->posy >=
MAX_Y_POSITIONS[False(param->option & OrbisImeDialogOption::LargeResolution)]) {
LOG_INFO(Lib_ImeDialog, "Invalid param->posy");
param->posy >= MAX_Y_POSITIONS[False(param->option & OrbisImeOption::USE_2K_COORDINATES)]) {
LOG_ERROR(Lib_ImeDialog, "sceImeDialogInit: invalid posy=%f", param->posy);
return Error::INVALID_POSY;
}
@ -192,12 +192,13 @@ Error PS4_SYSV_ABI sceImeDialogInit(OrbisImeDialogParam* param, OrbisImeParamExt
}
if (!IsValidOption(param->option, param->type)) {
LOG_INFO(Lib_ImeDialog, "Invalid param->option");
LOG_ERROR(Lib_ImeDialog, "sceImeDialogInit: invalid option=0x%X for type=%u",
static_cast<u32>(param->option), (u32)param->type);
return Error::INVALID_PARAM;
}
if (param->input_text_buffer == nullptr) {
LOG_INFO(Lib_ImeDialog, "Invalid param->inputTextBuffer");
LOG_ERROR(Lib_ImeDialog, "sceImeDialogInit: input_text_buffer is null");
return Error::INVALID_INPUT_TEXT_BUFFER;
}
@ -220,16 +221,24 @@ Error PS4_SYSV_ABI sceImeDialogInit(OrbisImeDialogParam* param, OrbisImeParamExt
}
}
if (param->max_text_length > ORBIS_IME_DIALOG_MAX_TEXT_LENGTH) {
LOG_INFO(Lib_ImeDialog, "Invalid param->maxTextLength");
if (param->max_text_length == 0 || param->max_text_length > ORBIS_IME_MAX_TEXT_LENGTH) {
LOG_ERROR(Lib_ImeDialog, "sceImeDialogInit: invalid max_text_length=%u",
param->max_text_length);
return Error::INVALID_MAX_TEXT_LENGTH;
}
// Title string validation
if (param->title != nullptr && !std::char_traits<char16_t>::length(param->title)) {
LOG_ERROR(Lib_ImeDialog, "sceImeDialogInit: title is empty");
return Error::INVALID_PARAM;
}
g_ime_dlg_result = {};
g_ime_dlg_state = ImeDialogState(param, extended);
g_ime_dlg_status = OrbisImeDialogStatus::Running;
g_ime_dlg_ui = ImeDialogUi(&g_ime_dlg_state, &g_ime_dlg_status, &g_ime_dlg_result);
LOG_INFO(Lib_ImeDialog, "<< sceImeDialogInit: successful, status now=Running");
return Error::OK;
}

View File

@ -13,50 +13,6 @@ class SymbolsResolver;
namespace Libraries::ImeDialog {
constexpr u32 ORBIS_IME_DIALOG_MAX_TEXT_LENGTH = 2048;
enum class Error : u32 {
OK = 0x0,
BUSY = 0x80bc0001,
NOT_OPENED = 0x80bc0002,
NO_MEMORY = 0x80bc0003,
CONNECTION_FAILED = 0x80bc0004,
TOO_MANY_REQUESTS = 0x80bc0005,
INVALID_TEXT = 0x80bc0006,
EVENT_OVERFLOW = 0x80bc0007,
NOT_ACTIVE = 0x80bc0008,
IME_SUSPENDING = 0x80bc0009,
DEVICE_IN_USE = 0x80bc000a,
INVALID_USER_ID = 0x80bc0010,
INVALID_TYPE = 0x80bc0011,
INVALID_SUPPORTED_LANGUAGES = 0x80bc0012,
INVALID_ENTER_LABEL = 0x80bc0013,
INVALID_INPUT_METHOD = 0x80bc0014,
INVALID_OPTION = 0x80bc0015,
INVALID_MAX_TEXT_LENGTH = 0x80bc0016,
INVALID_INPUT_TEXT_BUFFER = 0x80bc0017,
INVALID_POSX = 0x80bc0018,
INVALID_POSY = 0x80bc0019,
INVALID_HORIZONTALIGNMENT = 0x80bc001a,
INVALID_VERTICALALIGNMENT = 0x80bc001b,
INVALID_EXTENDED = 0x80bc001c,
INVALID_KEYBOARD_TYPE = 0x80bc001d,
INVALID_WORK = 0x80bc0020,
INVALID_ARG = 0x80bc0021,
INVALID_HANDLER = 0x80bc0022,
NO_RESOURCE_ID = 0x80bc0023,
INVALID_MODE = 0x80bc0024,
INVALID_PARAM = 0x80bc0030,
INVALID_ADDRESS = 0x80bc0031,
INVALID_RESERVED = 0x80bc0032,
INVALID_TIMING = 0x80bc0033,
INTERNAL = 0x80bc00ff,
DIALOG_INVALID_TITLE = 0x80bc0101,
DIALOG_NOT_RUNNING = 0x80bc0105,
DIALOG_NOT_FINISHED = 0x80bc0106,
DIALOG_NOT_IN_USE = 0x80bc0107,
};
enum class OrbisImeDialogStatus : u32 {
None = 0,
Running = 1,
@ -69,87 +25,11 @@ enum class OrbisImeDialogEndStatus : u32 {
Aborted = 2,
};
enum class OrbisImeDialogOption : u32 {
Default = 0,
Multiline = 1,
NoAutoCorrection = 2,
NoAutoCompletion = 4,
// TODO: Document missing options
LargeResolution = 1024,
};
DECLARE_ENUM_FLAG_OPERATORS(OrbisImeDialogOption)
enum class OrbisImePanelPriority : u32 {
Default = 0,
Alphabet = 1,
Symbol = 2,
Accent = 3,
};
struct OrbisImeColor {
u8 r;
u8 g;
u8 b;
u8 a;
};
struct OrbisImeDialogResult {
OrbisImeDialogEndStatus endstatus;
s32 reserved[12];
};
struct OrbisImeKeycode {
u16 keycode;
char16_t character;
u32 status;
OrbisImeKeyboardType type;
s32 user_id;
u32 resource_id;
u64 timestamp;
};
using OrbisImeExtKeyboardFilter = PS4_SYSV_ABI int (*)(const OrbisImeKeycode* srcKeycode,
u16* outKeycode, u32* outStatus,
void* reserved);
struct OrbisImeDialogParam {
s32 user_id;
OrbisImeType type;
u64 supported_languages;
OrbisImeEnterLabel enter_label;
OrbisImeInputMethod input_method;
OrbisImeTextFilter filter;
OrbisImeDialogOption option;
u32 max_text_length;
char16_t* input_text_buffer;
float posx;
float posy;
OrbisImeHorizontalAlignment horizontal_alignment;
OrbisImeVerticalAlignment vertical_alignment;
const char16_t* placeholder;
const char16_t* title;
s8 reserved[16];
};
struct OrbisImeParamExtended {
u32 option; // OrbisImeDialogOptionExtended
OrbisImeColor color_base;
OrbisImeColor color_line;
OrbisImeColor color_text_field;
OrbisImeColor color_preedit;
OrbisImeColor color_button_default;
OrbisImeColor color_button_function;
OrbisImeColor color_button_symbol;
OrbisImeColor color_text;
OrbisImeColor color_special;
OrbisImePanelPriority priority;
char* additional_dictionary_path;
OrbisImeExtKeyboardFilter ext_keyboard_filter;
uint32_t disable_device;
uint32_t ext_keyboard_mode;
int8_t reserved[60];
};
Error PS4_SYSV_ABI sceImeDialogAbort();
Error PS4_SYSV_ABI sceImeDialogForceClose();
Error PS4_SYSV_ABI sceImeDialogForTestFunction();

View File

@ -21,12 +21,16 @@ namespace Libraries::ImeDialog {
ImeDialogState::ImeDialogState(const OrbisImeDialogParam* param,
const OrbisImeParamExtended* extended) {
LOG_INFO(Lib_ImeDialog, ">> ImeDialogState::Ctor: param={}, text_buffer={}",
static_cast<const void*>(param),
static_cast<void*>(param ? param->input_text_buffer : nullptr));
if (!param) {
LOG_ERROR(Lib_ImeDialog, " param==nullptr, returning without init");
return;
}
user_id = param->user_id;
is_multi_line = True(param->option & OrbisImeDialogOption::Multiline);
is_multi_line = True(param->option & OrbisImeOption::MULTILINE);
is_numeric = param->type == OrbisImeType::Number;
type = param->type;
enter_label = param->enter_label;
@ -220,6 +224,7 @@ void ImeDialogUi::Free() {
void ImeDialogUi::Draw() {
std::unique_lock lock{draw_mutex};
LOG_INFO(Lib_ImeDialog, ">> ImeDialogUi::Draw: first_render=%d", first_render);
if (!state) {
return;
@ -259,9 +264,13 @@ void ImeDialogUi::Draw() {
}
if (state->is_multi_line) {
LOG_INFO(Lib_ImeDialog, " Drawing multi-line widget…");
DrawMultiLineInputText();
LOG_INFO(Lib_ImeDialog, " Done DrawMultiLineInputText");
} else {
LOG_INFO(Lib_ImeDialog, " Drawing input text widget…");
DrawInputText();
LOG_INFO(Lib_ImeDialog, " Done DrawInputText");
}
SetCursorPosY(GetCursorPosY() + 10.0f);
@ -306,6 +315,7 @@ void ImeDialogUi::Draw() {
End();
first_render = false;
LOG_INFO(Lib_ImeDialog, "<< ImeDialogUi::Draw complete");
}
void ImeDialogUi::DrawInputText() {
@ -316,7 +326,7 @@ void ImeDialogUi::DrawInputText() {
}
const char* placeholder = state->placeholder.empty() ? nullptr : state->placeholder.data();
if (InputTextEx("##ImeDialogInput", placeholder, state->current_text.begin(),
state->max_text_length, input_size, ImGuiInputTextFlags_CallbackCharFilter,
state->max_text_length + 1, input_size, ImGuiInputTextFlags_CallbackCharFilter,
InputTextCallback, this)) {
state->input_changed = true;
}
@ -332,7 +342,7 @@ void ImeDialogUi::DrawMultiLineInputText() {
}
const char* placeholder = state->placeholder.empty() ? nullptr : state->placeholder.data();
if (InputTextEx("##ImeDialogInput", placeholder, state->current_text.begin(),
state->max_text_length, input_size, flags, InputTextCallback, this)) {
state->max_text_length + 1, input_size, flags, InputTextCallback, this)) {
state->input_changed = true;
}
}
@ -341,13 +351,19 @@ int ImeDialogUi::InputTextCallback(ImGuiInputTextCallbackData* data) {
ImeDialogUi* ui = static_cast<ImeDialogUi*>(data->UserData);
ASSERT(ui);
LOG_DEBUG(Lib_ImeDialog, ">> InputTextCallback: EventFlag={}, EventChar={}", data->EventFlag,
data->EventChar);
// Should we filter punctuation?
if (ui->state->is_numeric && (data->EventChar < '0' || data->EventChar > '9') &&
data->EventChar != '\b' && data->EventChar != ',' && data->EventChar != '.') {
LOG_INFO(Lib_ImeDialog, "InputTextCallback: rejecting non-digit char '{}'",
static_cast<char>(data->EventChar));
return 1;
}
if (!ui->state->keyboard_filter) {
LOG_DEBUG(Lib_ImeDialog, "InputTextCallback: no keyboard_filter, accepting char");
return 0;
}
@ -363,20 +379,24 @@ int ImeDialogUi::InputTextCallback(ImGuiInputTextCallbackData* data) {
// the current language?)
.user_id = ui->state->user_id,
.resource_id = 0,
.timestamp = 0,
.timestamp = {0},
};
if (!ui->state->ConvertUTF8ToOrbis(event_char, 4, &src_keycode.character, 1)) {
LOG_ERROR(Lib_ImeDialog, "Failed to convert orbis char to utf8");
LOG_ERROR(Lib_ImeDialog, "InputTextCallback: ConvertUTF8ToOrbis failed");
return 0;
}
LOG_DEBUG(Lib_ImeDialog, "InputTextCallback: converted to Orbis char={:#X}",
static_cast<uint16_t>(src_keycode.character));
src_keycode.keycode = src_keycode.character; // TODO set this to the correct value
u16 out_keycode;
u32 out_status;
ui->state->CallKeyboardFilter(&src_keycode, &out_keycode, &out_status);
bool keep = ui->state->CallKeyboardFilter(&src_keycode, &out_keycode, &out_status);
LOG_DEBUG(Lib_ImeDialog,
"InputTextCallback: CallKeyboardFilter returned %s (keycode=0x%X, status=0x%X)",
keep ? "true" : "false", out_keycode, out_status);
// TODO. set the keycode
return 0;

View File

@ -199,7 +199,7 @@ int ImeUi::InputTextCallback(ImGuiInputTextCallbackData* data) {
eventParam.caret_index = data->CursorPos;
eventParam.area_num = 1;
eventParam.text_area[0].mode = 1; // Edit mode
eventParam.text_area[0].mode = OrbisImeTextAreaMode::Edit;
eventParam.text_area[0].index = data->CursorPos;
eventParam.text_area[0].length = data->BufTextLen;

View File

@ -573,11 +573,12 @@ void* PS4_SYSV_ABI posix_mmap(void* addr, u64 len, s32 prot, s32 flags, s32 fd,
auto* memory = Core::Memory::Instance();
const auto mem_prot = static_cast<Core::MemoryProt>(prot);
const auto mem_flags = static_cast<Core::MemoryMapFlags>(flags);
const auto is_exec = True(mem_prot & Core::MemoryProt::CpuExec);
s32 result = ORBIS_OK;
if (fd == -1) {
result = memory->MapMemory(&addr_out, std::bit_cast<VAddr>(addr), len, mem_prot, mem_flags,
Core::VMAType::Flexible);
Core::VMAType::Flexible, "anon", is_exec);
} else {
result = memory->MapFile(&addr_out, std::bit_cast<VAddr>(addr), len, mem_prot, mem_flags,
fd, phys_addr);
@ -711,6 +712,7 @@ void RegisterMemory(Core::Loader::SymbolsResolver* sym) {
sceKernelConfiguredFlexibleMemorySize);
LIB_FUNCTION("vSMAm3cxYTY", "libkernel", 1, "libkernel", 1, 1, sceKernelMprotect);
LIB_FUNCTION("YQOfxL4QfeU", "libkernel", 1, "libkernel", 1, 1, posix_mprotect);
LIB_FUNCTION("YQOfxL4QfeU", "libScePosix", 1, "libkernel", 1, 1, posix_mprotect);
LIB_FUNCTION("9bfdLIyuwCY", "libkernel", 1, "libkernel", 1, 1, sceKernelMtypeprotect);

View File

@ -171,19 +171,40 @@ s32 PS4_SYSV_ABI sceVideodec2GetPictureInfo(const OrbisVideodec2OutputInfo* outp
LOG_ERROR(Lib_Vdec2, "Invalid struct size");
return ORBIS_VIDEODEC2_ERROR_STRUCT_SIZE;
}
if (outputInfo->pictureCount == 0 || gPictureInfos.empty()) {
if (outputInfo->pictureCount == 0) {
LOG_ERROR(Lib_Vdec2, "No picture info available");
return ORBIS_OK;
}
if (p1stPictureInfoOut) {
OrbisVideodec2AvcPictureInfo* picInfo =
static_cast<OrbisVideodec2AvcPictureInfo*>(p1stPictureInfoOut);
if ((picInfo->thisSize | 16) != sizeof(OrbisVideodec2AvcPictureInfo)) {
LOG_ERROR(Lib_Vdec2, "Invalid struct size");
return ORBIS_VIDEODEC2_ERROR_STRUCT_SIZE;
// If the game uses the older Videodec2 structs, we need to accomodate that.
if (outputInfo->thisSize != sizeof(OrbisVideodec2OutputInfo)) {
if (gLegacyPictureInfos.empty()) {
LOG_ERROR(Lib_Vdec2, "No picture info available");
return ORBIS_OK;
}
if (p1stPictureInfoOut) {
OrbisVideodec2LegacyAvcPictureInfo* picInfo =
static_cast<OrbisVideodec2LegacyAvcPictureInfo*>(p1stPictureInfoOut);
if (picInfo->thisSize != sizeof(OrbisVideodec2LegacyAvcPictureInfo)) {
LOG_ERROR(Lib_Vdec2, "Invalid struct size");
return ORBIS_VIDEODEC2_ERROR_STRUCT_SIZE;
}
*picInfo = gLegacyPictureInfos.back();
}
} else {
if (gPictureInfos.empty()) {
LOG_ERROR(Lib_Vdec2, "No picture info available");
return ORBIS_OK;
}
if (p1stPictureInfoOut) {
OrbisVideodec2AvcPictureInfo* picInfo =
static_cast<OrbisVideodec2AvcPictureInfo*>(p1stPictureInfoOut);
if (picInfo->thisSize != sizeof(OrbisVideodec2AvcPictureInfo)) {
LOG_ERROR(Lib_Vdec2, "Invalid struct size");
return ORBIS_VIDEODEC2_ERROR_STRUCT_SIZE;
}
*picInfo = gPictureInfos.back();
}
*picInfo = gPictureInfos.back();
}
if (outputInfo->pictureCount > 1) {

View File

@ -74,4 +74,57 @@ struct OrbisVideodec2AvcPictureInfo {
};
static_assert(sizeof(OrbisVideodec2AvcPictureInfo) == 0x78);
// An older version of the OrbisVideodec2AvcPictureInfo struct
// Keeping this is needed for compatiblity with older games.
struct OrbisVideodec2LegacyAvcPictureInfo {
u64 thisSize;
bool isValid;
u64 ptsData;
u64 dtsData;
u64 attachedData;
u8 idrPictureflag;
u8 profile_idc;
u8 level_idc;
u32 pic_width_in_mbs_minus1;
u32 pic_height_in_map_units_minus1;
u8 frame_mbs_only_flag;
u8 frame_cropping_flag;
u32 frameCropLeftOffset;
u32 frameCropRightOffset;
u32 frameCropTopOffset;
u32 frameCropBottomOffset;
u8 aspect_ratio_info_present_flag;
u8 aspect_ratio_idc;
u16 sar_width;
u16 sar_height;
u8 video_signal_type_present_flag;
u8 video_format;
u8 video_full_range_flag;
u8 colour_description_present_flag;
u8 colour_primaries;
u8 transfer_characteristics;
u8 matrix_coefficients;
u8 timing_info_present_flag;
u32 num_units_in_tick;
u32 time_scale;
u8 fixed_frame_rate_flag;
u8 bitstream_restriction_flag;
u8 max_dec_frame_buffering;
u8 pic_struct_present_flag;
u8 pic_struct;
u8 field_pic_flag;
u8 bottom_field_flag;
};
static_assert(sizeof(OrbisVideodec2LegacyAvcPictureInfo) == 0x68);
} // namespace Libraries::Vdec2

View File

@ -12,6 +12,7 @@
namespace Libraries::Vdec2 {
std::vector<OrbisVideodec2AvcPictureInfo> gPictureInfos;
std::vector<OrbisVideodec2LegacyAvcPictureInfo> gLegacyPictureInfos;
static inline void CopyNV12Data(u8* dst, const AVFrame& src) {
std::memcpy(dst, src.data[0], src.width * src.height);
@ -117,27 +118,46 @@ s32 VdecDecoder::Decode(const OrbisVideodec2InputData& inputData,
outputInfo.isErrorFrame = false;
outputInfo.pictureCount = 1; // TODO: 2 pictures for interlaced video
// Only set framePitchInBytes if the game uses the newer struct version.
// For proper compatibility with older games, check the inputted OutputInfo struct size.
if (outputInfo.thisSize == sizeof(OrbisVideodec2OutputInfo)) {
// framePitchInBytes only exists in the newer struct.
outputInfo.framePitchInBytes = frame->linesize[0];
}
if (outputInfo.isValid) {
OrbisVideodec2AvcPictureInfo pictureInfo = {};
if (outputInfo.isValid) {
OrbisVideodec2AvcPictureInfo pictureInfo = {};
pictureInfo.thisSize = sizeof(OrbisVideodec2AvcPictureInfo);
pictureInfo.isValid = true;
pictureInfo.thisSize = sizeof(OrbisVideodec2AvcPictureInfo);
pictureInfo.isValid = true;
pictureInfo.ptsData = inputData.ptsData;
pictureInfo.dtsData = inputData.dtsData;
pictureInfo.attachedData = inputData.attachedData;
pictureInfo.ptsData = inputData.ptsData;
pictureInfo.dtsData = inputData.dtsData;
pictureInfo.attachedData = inputData.attachedData;
pictureInfo.frameCropLeftOffset = frame->crop_left;
pictureInfo.frameCropRightOffset = frame->crop_right;
pictureInfo.frameCropTopOffset = frame->crop_top;
pictureInfo.frameCropBottomOffset = frame->crop_bottom;
pictureInfo.frameCropLeftOffset = frame->crop_left;
pictureInfo.frameCropRightOffset = frame->crop_right;
pictureInfo.frameCropTopOffset = frame->crop_top;
pictureInfo.frameCropBottomOffset = frame->crop_bottom;
gPictureInfos.push_back(pictureInfo);
}
} else {
if (outputInfo.isValid) {
// If the game uses the older struct versions, we need to use it too.
OrbisVideodec2LegacyAvcPictureInfo pictureInfo = {};
gPictureInfos.push_back(pictureInfo);
pictureInfo.thisSize = sizeof(OrbisVideodec2LegacyAvcPictureInfo);
pictureInfo.isValid = true;
pictureInfo.ptsData = inputData.ptsData;
pictureInfo.dtsData = inputData.dtsData;
pictureInfo.attachedData = inputData.attachedData;
pictureInfo.frameCropLeftOffset = frame->crop_left;
pictureInfo.frameCropRightOffset = frame->crop_right;
pictureInfo.frameCropTopOffset = frame->crop_top;
pictureInfo.frameCropBottomOffset = frame->crop_bottom;
gLegacyPictureInfos.push_back(pictureInfo);
}
}
}

View File

@ -16,6 +16,7 @@ extern "C" {
namespace Libraries::Vdec2 {
extern std::vector<OrbisVideodec2AvcPictureInfo> gPictureInfos;
extern std::vector<OrbisVideodec2LegacyAvcPictureInfo> gLegacyPictureInfos;
class VdecDecoder {
public:

View File

@ -445,7 +445,8 @@ s32 PS4_SYSV_ABI sceVideoOutConfigureOutputMode_(s32 handle, u32 reserved, const
}
void RegisterLib(Core::Loader::SymbolsResolver* sym) {
driver = std::make_unique<VideoOutDriver>(Config::getScreenWidth(), Config::getScreenHeight());
driver = std::make_unique<VideoOutDriver>(Config::getInternalScreenWidth(),
Config::getInternalScreenHeight());
LIB_FUNCTION("SbU3dwp80lQ", "libSceVideoOut", 1, "libSceVideoOut", 0, 0,
sceVideoOutGetFlipStatus);

View File

@ -342,7 +342,7 @@ s32 MemoryManager::MapMemory(void** out_addr, VAddr virtual_addr, u64 size, Memo
}
}
// Limit the minumum address to SystemManagedVirtualBase to prevent hardware-specific issues.
// Limit the minimum address to SystemManagedVirtualBase to prevent hardware-specific issues.
VAddr mapped_addr = (virtual_addr == 0) ? impl.SystemManagedVirtualBase() : virtual_addr;
// Fixed mapping means the virtual address must exactly match the provided one.
@ -414,9 +414,10 @@ s32 MemoryManager::MapMemory(void** out_addr, VAddr virtual_addr, u64 size, Memo
rasterizer->MapMemory(mapped_addr, size);
}
*out_addr = impl.Map(mapped_addr, size, alignment, phys_addr, is_exec);
TRACK_ALLOC(*out_addr, size, "VMEM");
}
TRACK_ALLOC(*out_addr, size, "VMEM");
return ORBIS_OK;
}
@ -630,6 +631,9 @@ s64 MemoryManager::ProtectBytes(VAddr addr, VirtualMemoryArea vma_base, u64 size
if (True(prot & MemoryProt::CpuReadWrite)) {
perms |= Core::MemoryPermission::ReadWrite;
}
if (True(prot & MemoryProt::CpuExec)) {
perms |= Core::MemoryPermission::Execute;
}
if (True(prot & MemoryProt::GpuRead)) {
perms |= Core::MemoryPermission::Read;
}
@ -649,9 +653,9 @@ s32 MemoryManager::Protect(VAddr addr, u64 size, MemoryProt prot) {
std::scoped_lock lk{mutex};
// Validate protection flags
constexpr static MemoryProt valid_flags = MemoryProt::NoAccess | MemoryProt::CpuRead |
MemoryProt::CpuReadWrite | MemoryProt::GpuRead |
MemoryProt::GpuWrite | MemoryProt::GpuReadWrite;
constexpr static MemoryProt valid_flags =
MemoryProt::NoAccess | MemoryProt::CpuRead | MemoryProt::CpuReadWrite |
MemoryProt::CpuExec | MemoryProt::GpuRead | MemoryProt::GpuWrite | MemoryProt::GpuReadWrite;
MemoryProt invalid_flags = prot & ~valid_flags;
if (invalid_flags != MemoryProt::NoAccess) {

View File

@ -31,6 +31,7 @@ enum class MemoryProt : u32 {
NoAccess = 0,
CpuRead = 1,
CpuReadWrite = 2,
CpuExec = 4,
GpuRead = 16,
GpuWrite = 32,
GpuReadWrite = 48,

View File

@ -132,6 +132,9 @@ void Emulator::Run(std::filesystem::path file, const std::vector<std::string> ar
LOG_INFO(Config, "General LogType: {}", Config::getLogType());
LOG_INFO(Config, "General isNeo: {}", Config::isNeoModeConsole());
LOG_INFO(Config, "GPU isNullGpu: {}", Config::nullGpu());
LOG_INFO(Config, "GPU readbacks: {}", Config::readbacks());
LOG_INFO(Config, "GPU readbackLinearImages: {}", Config::readbackLinearImages());
LOG_INFO(Config, "GPU directMemoryAccess: {}", Config::directMemoryAccess());
LOG_INFO(Config, "GPU shouldDumpShaders: {}", Config::dumpShaders());
LOG_INFO(Config, "GPU vblankDivider: {}", Config::vblankDiv());
LOG_INFO(Config, "Vulkan gpuId: {}", Config::getGpuId());
@ -220,7 +223,7 @@ void Emulator::Run(std::filesystem::path file, const std::vector<std::string> ar
}
}
window = std::make_unique<Frontend::WindowSDL>(
Config::getScreenWidth(), Config::getScreenHeight(), controller, window_title);
Config::getWindowWidth(), Config::getWindowHeight(), controller, window_title);
g_window = window.get();

View File

@ -514,17 +514,14 @@ void ControllerOutput::FinalizeUpdate() {
if (button != SDL_GAMEPAD_BUTTON_INVALID) {
switch (button) {
case SDL_GAMEPAD_BUTTON_TOUCHPAD_LEFT:
LOG_INFO(Input, "Topuchpad left");
controller->SetTouchpadState(0, new_button_state, 0.25f, 0.5f);
controller->CheckButton(0, SDLGamepadToOrbisButton(button), new_button_state);
break;
case SDL_GAMEPAD_BUTTON_TOUCHPAD_CENTER:
LOG_INFO(Input, "Topuchpad center");
controller->SetTouchpadState(0, new_button_state, 0.50f, 0.5f);
controller->CheckButton(0, SDLGamepadToOrbisButton(button), new_button_state);
break;
case SDL_GAMEPAD_BUTTON_TOUCHPAD_RIGHT:
LOG_INFO(Input, "Topuchpad right");
controller->SetTouchpadState(0, new_button_state, 0.75f, 0.5f);
controller->CheckButton(0, SDLGamepadToOrbisButton(button), new_button_state);
break;

View File

@ -177,38 +177,38 @@ const std::map<std::string, u32> string_to_keyboard_key_map = {
{"9", SDLK_9},
// symbols
{"`", SDLK_GRAVE},
{"~", SDLK_TILDE},
{"!", SDLK_EXCLAIM},
{"@", SDLK_AT},
{"#", SDLK_HASH},
{"$", SDLK_DOLLAR},
{"%", SDLK_PERCENT},
{"^", SDLK_CARET},
{"&", SDLK_AMPERSAND},
{"*", SDLK_ASTERISK},
{"(", SDLK_LEFTPAREN},
{")", SDLK_RIGHTPAREN},
{"-", SDLK_MINUS},
{"_", SDLK_UNDERSCORE},
{"=", SDLK_EQUALS},
{"+", SDLK_PLUS},
{"[", SDLK_LEFTBRACKET},
{"]", SDLK_RIGHTBRACKET},
{"{", SDLK_LEFTBRACE},
{"}", SDLK_RIGHTBRACE},
{"\\", SDLK_BACKSLASH},
{"|", SDLK_PIPE},
{";", SDLK_SEMICOLON},
{":", SDLK_COLON},
{"'", SDLK_APOSTROPHE},
{"\"", SDLK_DBLAPOSTROPHE},
{",", SDLK_COMMA},
{"<", SDLK_LESS},
{".", SDLK_PERIOD},
{">", SDLK_GREATER},
{"/", SDLK_SLASH},
{"?", SDLK_QUESTION},
{"grave", SDLK_GRAVE},
{"tilde", SDLK_TILDE},
{"exclamation", SDLK_EXCLAIM},
{"at", SDLK_AT},
{"hash", SDLK_HASH},
{"dollar", SDLK_DOLLAR},
{"percent", SDLK_PERCENT},
{"caret", SDLK_CARET},
{"ampersand", SDLK_AMPERSAND},
{"asterisk", SDLK_ASTERISK},
{"lparen", SDLK_LEFTPAREN},
{"rparen", SDLK_RIGHTPAREN},
{"minus", SDLK_MINUS},
{"underscore", SDLK_UNDERSCORE},
{"equals", SDLK_EQUALS},
{"plus", SDLK_PLUS},
{"lbracket", SDLK_LEFTBRACKET},
{"rbracket", SDLK_RIGHTBRACKET},
{"lbrace", SDLK_LEFTBRACE},
{"rbrace", SDLK_RIGHTBRACE},
{"backslash", SDLK_BACKSLASH},
{"pipe", SDLK_PIPE},
{"semicolon", SDLK_SEMICOLON},
{"colon", SDLK_COLON},
{"apostrophe", SDLK_APOSTROPHE},
{"quote", SDLK_DBLAPOSTROPHE},
{"comma", SDLK_COMMA},
{"less", SDLK_LESS},
{"period", SDLK_PERIOD},
{"greater", SDLK_GREATER},
{"slash", SDLK_SLASH},
{"question", SDLK_QUESTION},
// special keys
{"escape", SDLK_ESCAPE},
@ -252,13 +252,13 @@ const std::map<std::string, u32> string_to_keyboard_key_map = {
{"kp7", SDLK_KP_7},
{"kp8", SDLK_KP_8},
{"kp9", SDLK_KP_9},
{"kp.", SDLK_KP_PERIOD},
{"kp,", SDLK_KP_COMMA},
{"kp/", SDLK_KP_DIVIDE},
{"kp*", SDLK_KP_MULTIPLY},
{"kp-", SDLK_KP_MINUS},
{"kp+", SDLK_KP_PLUS},
{"kp=", SDLK_KP_EQUALS},
{"kpperiod", SDLK_KP_PERIOD},
{"kpcomma", SDLK_KP_COMMA},
{"kpslash", SDLK_KP_DIVIDE},
{"kpasterisk", SDLK_KP_MULTIPLY},
{"kpminus", SDLK_KP_MINUS},
{"kpplus", SDLK_KP_PLUS},
{"kpequals", SDLK_KP_EQUALS},
{"kpenter", SDLK_KP_ENTER},
// mouse

File diff suppressed because it is too large Load Diff

View File

@ -2,7 +2,10 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include <QDialog>
#include <SDL3/SDL.h>
#include <SDL3/SDL_gamepad.h>
#include "game_info.h"
#include "sdl_event_wrapper.h"
namespace Ui {
class ControlSettings;
@ -11,22 +14,56 @@ class ControlSettings;
class ControlSettings : public QDialog {
Q_OBJECT
public:
explicit ControlSettings(std::shared_ptr<GameInfoClass> game_info_get,
QWidget* parent = nullptr);
explicit ControlSettings(std::shared_ptr<GameInfoClass> game_info_get, bool GameRunning,
std::string GameRunningSerial, QWidget* parent = nullptr);
~ControlSettings();
signals:
void PushGamepadEvent();
void AxisChanged();
private Q_SLOTS:
void SaveControllerConfig(bool CloseOnSave);
void SetDefault();
void UpdateLightbarColor();
void CheckMapping(QPushButton*& button);
void StartTimer(QPushButton*& button, bool isButton);
void ConnectAxisInputs(QPushButton*& button);
private:
std::unique_ptr<Ui::ControlSettings> ui;
std::shared_ptr<GameInfoClass> m_game_info;
bool eventFilter(QObject* obj, QEvent* event) override;
void AddBoxItems();
void SetUIValuestoMappings();
void GetGameTitle();
void CheckGamePad();
void processSDLEvents(int Type, int Input, int Value);
void pollSDLEvents();
void SetMapping(QString input);
void DisableMappingButtons();
void EnableMappingButtons();
void Cleanup();
QList<QPushButton*> ButtonsList;
QList<QPushButton*> AxisList;
QSet<QString> pressedButtons;
std::string RunningGameSerial;
bool GameRunning;
bool L2Pressed = false;
bool R2Pressed = false;
bool EnableButtonMapping = false;
bool EnableAxisMapping = false;
bool MappingCompleted = false;
QString mapping;
int MappingTimer;
QTimer* timer;
QPushButton* MappingButton;
SDL_Gamepad* gamepad = nullptr;
SdlEventWrapper::Wrapper* RemapWrapper;
QFuture<void> Polling;
const std::vector<std::string> ControllerInputs = {
"cross", "circle", "square", "triangle", "l1",
@ -39,29 +76,8 @@ private:
"pad_left", "pad_right", "axis_left_x", "axis_left_y", "axis_right_x",
"axis_right_y", "back"};
const QStringList ButtonOutputs = {"cross",
"circle",
"square",
"triangle",
"l1",
"r1",
"l2",
"r2",
"l3",
"r3",
"options",
"pad_up",
"pad_down",
"pad_left",
"pad_right",
"touchpad_left",
"touchpad_center",
"touchpad_right",
"unmapped"};
const QStringList StickOutputs = {"axis_left_x", "axis_left_y", "axis_right_x", "axis_right_y",
"unmapped"};
protected:
void closeEvent(QCloseEvent* event) override {
Cleanup();
}
};

View File

@ -11,8 +11,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>1043</width>
<height>792</height>
<width>1114</width>
<height>794</height>
</rect>
</property>
<property name="windowTitle">
@ -33,8 +33,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>1019</width>
<height>732</height>
<width>1094</width>
<height>744</height>
</rect>
</property>
<widget class="QWidget" name="layoutWidget">
@ -42,8 +42,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>1021</width>
<height>731</height>
<width>1091</width>
<height>741</height>
</rect>
</property>
<layout class="QHBoxLayout" name="RemapLayout">
@ -110,7 +110,7 @@
<widget class="QGroupBox" name="gb_dpad_up">
<property name="minimumSize">
<size>
<width>124</width>
<width>152</width>
<height>0</height>
</size>
</property>
@ -125,12 +125,9 @@
</property>
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<widget class="QComboBox" name="DpadUpBox">
<property name="editable">
<bool>false</bool>
</property>
<property name="sizeAdjustPolicy">
<enum>QComboBox::SizeAdjustPolicy::AdjustToContents</enum>
<widget class="QPushButton" name="DpadUpButton">
<property name="text">
<string>unmapped</string>
</property>
</widget>
</item>
@ -161,7 +158,11 @@
<number>5</number>
</property>
<item>
<widget class="QComboBox" name="DpadLeftBox"/>
<widget class="QPushButton" name="DpadLeftButton">
<property name="text">
<string>unmapped</string>
</property>
</widget>
</item>
</layout>
</widget>
@ -185,9 +186,9 @@
<number>5</number>
</property>
<item>
<widget class="QComboBox" name="DpadRightBox">
<property name="editable">
<bool>false</bool>
<widget class="QPushButton" name="DpadRightButton">
<property name="text">
<string>unmapped</string>
</property>
</widget>
</item>
@ -213,6 +214,12 @@
</property>
<item>
<widget class="QGroupBox" name="gb_dpad_down">
<property name="minimumSize">
<size>
<width>152</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>124</width>
@ -224,21 +231,9 @@
</property>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QComboBox" name="DpadDownBox">
<property name="enabled">
<bool>true</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
<widget class="QPushButton" name="DpadDownButton">
<property name="text">
<string>unmapped</string>
</property>
</widget>
</item>
@ -378,7 +373,7 @@
</property>
<property name="maximumSize">
<size>
<width>124</width>
<width>152</width>
<height>16777215</height>
</size>
</property>
@ -387,9 +382,9 @@
</property>
<layout class="QVBoxLayout" name="verticalLayout_10">
<item>
<widget class="QComboBox" name="LStickUpBox">
<property name="enabled">
<bool>true</bool>
<widget class="QPushButton" name="LStickUpButton">
<property name="text">
<string>unmapped</string>
</property>
</widget>
</item>
@ -420,9 +415,9 @@
<number>5</number>
</property>
<item>
<widget class="QComboBox" name="LStickLeftBox">
<property name="enabled">
<bool>true</bool>
<widget class="QPushButton" name="LStickLeftButton">
<property name="text">
<string>unmapped</string>
</property>
</widget>
</item>
@ -454,9 +449,9 @@
<number>5</number>
</property>
<item>
<widget class="QComboBox" name="LStickRightBox">
<property name="enabled">
<bool>true</bool>
<widget class="QPushButton" name="LStickRightButton">
<property name="text">
<string>unmapped</string>
</property>
</widget>
</item>
@ -484,7 +479,7 @@
<widget class="QGroupBox" name="gb_left_stick_down">
<property name="minimumSize">
<size>
<width>124</width>
<width>152</width>
<height>0</height>
</size>
</property>
@ -499,15 +494,9 @@
</property>
<layout class="QVBoxLayout" name="verticalLayout_8">
<item>
<widget class="QComboBox" name="LStickDownBox">
<property name="enabled">
<bool>true</bool>
</property>
<property name="editable">
<bool>false</bool>
</property>
<property name="frame">
<bool>false</bool>
<widget class="QPushButton" name="LStickDownButton">
<property name="text">
<string>unmapped</string>
</property>
</widget>
</item>
@ -617,149 +606,190 @@
<property name="spacing">
<number>0</number>
</property>
<item>
<layout class="QVBoxLayout" name="layout_l1_l2">
<item>
<widget class="QGroupBox" name="gb_l1">
<property name="title">
<string>L1 / LB</string>
</property>
<layout class="QVBoxLayout" name="gb_l1_layout">
<property name="leftMargin">
<number>5</number>
</property>
<property name="topMargin">
<number>5</number>
</property>
<property name="rightMargin">
<number>5</number>
</property>
<property name="bottomMargin">
<number>5</number>
</property>
<item>
<widget class="QComboBox" name="LBBox"/>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="gb_l2">
<property name="title">
<string>L2 / LT</string>
</property>
<layout class="QVBoxLayout" name="gb_l2_layout">
<property name="leftMargin">
<number>5</number>
</property>
<property name="topMargin">
<number>5</number>
</property>
<property name="rightMargin">
<number>5</number>
</property>
<property name="bottomMargin">
<number>5</number>
</property>
<item>
<widget class="QComboBox" name="LTBox"/>
</item>
</layout>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QVBoxLayout" name="layout_system_buttons">
<item>
<spacer name="verticalSpacer_3">
<property name="orientation">
<enum>Qt::Orientation::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Policy::Preferred</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_4">
<property name="spacing">
<number>10</number>
</property>
<item>
<widget class="QGroupBox" name="groupBox_2">
<property name="title">
<string>Back</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_9">
<item>
<widget class="QComboBox" name="BackBox"/>
</item>
</layout>
</widget>
<layout class="QHBoxLayout" name="horizontalLayout_13">
<item>
<widget class="QGroupBox" name="gb_l1">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="title">
<string>L1</string>
</property>
<layout class="QVBoxLayout" name="gb_l1_layout">
<property name="leftMargin">
<number>5</number>
</property>
<property name="topMargin">
<number>5</number>
</property>
<property name="rightMargin">
<number>5</number>
</property>
<property name="bottomMargin">
<number>5</number>
</property>
<item>
<widget class="QPushButton" name="L1Button">
<property name="text">
<string>unmapped</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Policy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>133</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QGroupBox" name="gb_r1">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="title">
<string>R1</string>
</property>
<layout class="QVBoxLayout" name="gb_r1_layout">
<property name="leftMargin">
<number>5</number>
</property>
<property name="topMargin">
<number>5</number>
</property>
<property name="rightMargin">
<number>5</number>
</property>
<property name="bottomMargin">
<number>5</number>
</property>
<item>
<widget class="QPushButton" name="R1Button">
<property name="text">
<string>unmapped</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_14">
<item>
<widget class="QGroupBox" name="gb_l2">
<property name="title">
<string>L2</string>
</property>
<layout class="QVBoxLayout" name="gb_l2_layout">
<property name="leftMargin">
<number>5</number>
</property>
<property name="topMargin">
<number>5</number>
</property>
<property name="rightMargin">
<number>5</number>
</property>
<property name="bottomMargin">
<number>5</number>
</property>
<item>
<widget class="QPushButton" name="L2Button">
<property name="text">
<string>unmapped</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="gb_start">
<property name="title">
<string>Options</string>
</property>
<layout class="QVBoxLayout" name="gb_start_layout">
<property name="leftMargin">
<number>5</number>
</property>
<property name="topMargin">
<number>5</number>
</property>
<property name="rightMargin">
<number>5</number>
</property>
<property name="bottomMargin">
<number>5</number>
</property>
<item>
<widget class="QPushButton" name="OptionsButton">
<property name="text">
<string>unmapped</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="gb_r2">
<property name="title">
<string>R2</string>
</property>
<layout class="QVBoxLayout" name="gb_r2_layout">
<property name="leftMargin">
<number>5</number>
</property>
<property name="topMargin">
<number>5</number>
</property>
<property name="rightMargin">
<number>5</number>
</property>
<property name="bottomMargin">
<number>5</number>
</property>
<item>
<widget class="QPushButton" name="R2Button">
<property name="text">
<string>unmapped</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</item>
</layout>
</item>
</layout>
</item>
<item>
<layout class="QVBoxLayout" name="layout_r1_r2">
<item>
<widget class="QGroupBox" name="gb_r1">
<property name="title">
<string>R1 / RB</string>
</property>
<layout class="QVBoxLayout" name="gb_r1_layout">
<property name="leftMargin">
<number>5</number>
</property>
<property name="topMargin">
<number>5</number>
</property>
<property name="rightMargin">
<number>5</number>
</property>
<property name="bottomMargin">
<number>5</number>
</property>
<item>
<widget class="QComboBox" name="RBBox"/>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="gb_r2">
<property name="title">
<string>R2 / RT</string>
</property>
<layout class="QVBoxLayout" name="gb_r2_layout">
<property name="leftMargin">
<number>5</number>
</property>
<property name="topMargin">
<number>5</number>
</property>
<property name="rightMargin">
<number>5</number>
</property>
<property name="bottomMargin">
<number>5</number>
</property>
<item>
<widget class="QComboBox" name="RTBox"/>
</item>
</layout>
</widget>
</item>
</layout>
</item>
</layout>
</item>
<item>
@ -806,7 +836,7 @@
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="layout_middle_bottom">
<layout class="QVBoxLayout" name="verticalLayout_9">
<property name="spacing">
<number>10</number>
</property>
@ -814,76 +844,144 @@
<enum>QLayout::SizeConstraint::SetDefaultConstraint</enum>
</property>
<item>
<widget class="QGroupBox" name="gb_l3">
<property name="title">
<string>L3</string>
</property>
<layout class="QVBoxLayout" name="gb_l3_layout">
<property name="leftMargin">
<number>5</number>
</property>
<property name="topMargin">
<number>5</number>
</property>
<property name="rightMargin">
<number>5</number>
</property>
<property name="bottomMargin">
<number>5</number>
</property>
<item>
<widget class="QComboBox" name="LClickBox"/>
</item>
</layout>
</widget>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QGroupBox" name="gb_l3">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="title">
<string>L3</string>
</property>
<layout class="QVBoxLayout" name="gb_l3_layout">
<property name="leftMargin">
<number>5</number>
</property>
<property name="topMargin">
<number>5</number>
</property>
<property name="rightMargin">
<number>5</number>
</property>
<property name="bottomMargin">
<number>5</number>
</property>
<item>
<widget class="QPushButton" name="L3Button">
<property name="text">
<string>unmapped</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Policy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>133</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QGroupBox" name="gb_r3">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="title">
<string>R3</string>
</property>
<layout class="QVBoxLayout" name="gb_r3_layout">
<property name="leftMargin">
<number>5</number>
</property>
<property name="topMargin">
<number>5</number>
</property>
<property name="rightMargin">
<number>5</number>
</property>
<property name="bottomMargin">
<number>5</number>
</property>
<item>
<widget class="QPushButton" name="R3Button">
<property name="text">
<string>unmapped</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QGroupBox" name="gb_start">
<property name="title">
<string>Options / Start</string>
</property>
<layout class="QVBoxLayout" name="gb_start_layout">
<property name="leftMargin">
<number>5</number>
</property>
<property name="topMargin">
<number>5</number>
</property>
<property name="rightMargin">
<number>5</number>
</property>
<property name="bottomMargin">
<number>5</number>
</property>
<item>
<widget class="QComboBox" name="StartBox"/>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="gb_r3">
<property name="title">
<string>R3</string>
</property>
<layout class="QVBoxLayout" name="gb_r3_layout">
<property name="leftMargin">
<number>5</number>
</property>
<property name="topMargin">
<number>5</number>
</property>
<property name="rightMargin">
<number>5</number>
</property>
<property name="bottomMargin">
<number>5</number>
</property>
<item>
<widget class="QComboBox" name="RClickBox"/>
</item>
</layout>
</widget>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QGroupBox" name="gb_touchleft">
<property name="title">
<string>Touchpad Left</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_5">
<item>
<widget class="QPushButton" name="TouchpadLeftButton">
<property name="text">
<string>unmapped</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="gb_touchcenter">
<property name="title">
<string>Touchpad Center</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_11">
<item>
<widget class="QPushButton" name="TouchpadCenterButton">
<property name="text">
<string>unmapped</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="gb_touchright">
<property name="title">
<string>Touchpad Right</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_12">
<item>
<widget class="QPushButton" name="TouchpadRightButton">
<property name="text">
<string>unmapped</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</item>
</layout>
</item>
@ -1104,7 +1202,7 @@
</property>
<property name="minimumSize">
<size>
<width>124</width>
<width>152</width>
<height>0</height>
</size>
</property>
@ -1115,19 +1213,13 @@
</size>
</property>
<property name="title">
<string>Triangle / Y</string>
<string>Triangle</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<widget class="QComboBox" name="YBox">
<property name="enabled">
<bool>true</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
<widget class="QPushButton" name="TriangleButton">
<property name="text">
<string>unmapped</string>
</property>
</widget>
</item>
@ -1142,7 +1234,7 @@
<item>
<widget class="QGroupBox" name="gb_square">
<property name="title">
<string>Square / X</string>
<string>Square</string>
</property>
<layout class="QVBoxLayout" name="gb_square_layout">
<property name="leftMargin">
@ -1158,7 +1250,11 @@
<number>5</number>
</property>
<item>
<widget class="QComboBox" name="XBox"/>
<widget class="QPushButton" name="SquareButton">
<property name="text">
<string>unmapped</string>
</property>
</widget>
</item>
</layout>
</widget>
@ -1166,7 +1262,7 @@
<item>
<widget class="QGroupBox" name="gb_circle">
<property name="title">
<string>Circle / B</string>
<string>Circle</string>
</property>
<layout class="QVBoxLayout" name="gb_circle_layout">
<property name="leftMargin">
@ -1182,7 +1278,11 @@
<number>5</number>
</property>
<item>
<widget class="QComboBox" name="BBox"/>
<widget class="QPushButton" name="CircleButton">
<property name="text">
<string>unmapped</string>
</property>
</widget>
</item>
</layout>
</widget>
@ -1208,7 +1308,7 @@
<widget class="QGroupBox" name="gb_cross">
<property name="minimumSize">
<size>
<width>124</width>
<width>152</width>
<height>0</height>
</size>
</property>
@ -1219,11 +1319,15 @@
</size>
</property>
<property name="title">
<string>Cross / A</string>
<string>Cross</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_5">
<item>
<widget class="QComboBox" name="ABox"/>
<widget class="QPushButton" name="CrossButton">
<property name="text">
<string>unmapped</string>
</property>
</widget>
</item>
</layout>
</widget>
@ -1361,7 +1465,7 @@
</property>
<property name="maximumSize">
<size>
<width>124</width>
<width>152</width>
<height>1231321</height>
</size>
</property>
@ -1370,9 +1474,9 @@
</property>
<layout class="QVBoxLayout" name="verticalLayout_7">
<item>
<widget class="QComboBox" name="RStickUpBox">
<property name="enabled">
<bool>true</bool>
<widget class="QPushButton" name="RStickUpButton">
<property name="text">
<string>unmapped</string>
</property>
</widget>
</item>
@ -1403,9 +1507,9 @@
<number>5</number>
</property>
<item>
<widget class="QComboBox" name="RStickLeftBox">
<property name="enabled">
<bool>true</bool>
<widget class="QPushButton" name="RStickLeftButton">
<property name="text">
<string>unmapped</string>
</property>
</widget>
</item>
@ -1431,7 +1535,11 @@
<number>5</number>
</property>
<item>
<widget class="QComboBox" name="RStickRightBox"/>
<widget class="QPushButton" name="RStickRightButton">
<property name="text">
<string>unmapped</string>
</property>
</widget>
</item>
</layout>
</widget>
@ -1457,7 +1565,7 @@
<widget class="QGroupBox" name="gb_right_stick_down">
<property name="minimumSize">
<size>
<width>124</width>
<width>152</width>
<height>0</height>
</size>
</property>
@ -1472,9 +1580,9 @@
</property>
<layout class="QVBoxLayout" name="verticalLayout_6">
<item>
<widget class="QComboBox" name="RStickDownBox">
<property name="enabled">
<bool>true</bool>
<widget class="QPushButton" name="RStickDownButton">
<property name="text">
<string>unmapped</string>
</property>
</widget>
</item>

View File

@ -7,16 +7,20 @@
#include <QMouseEvent>
#include <QPushButton>
#include <QWheelEvent>
#include <SDL3/SDL_events.h>
#include "common/path_util.h"
#include "input/input_handler.h"
#include "kbm_config_dialog.h"
#include "kbm_gui.h"
#include "kbm_help_dialog.h"
#include "ui_kbm_gui.h"
HelpDialog* HelpWindow;
KBMSettings::KBMSettings(std::shared_ptr<GameInfoClass> game_info_get, QWidget* parent)
: QDialog(parent), m_game_info(game_info_get), ui(new Ui::KBMSettings) {
KBMSettings::KBMSettings(std::shared_ptr<GameInfoClass> game_info_get, bool isGameRunning,
std::string GameRunningSerial, QWidget* parent)
: QDialog(parent), m_game_info(game_info_get), GameRunning(isGameRunning),
RunningGameSerial(GameRunningSerial), ui(new Ui::KBMSettings) {
ui->setupUi(this);
ui->PerGameCheckBox->setChecked(!Config::GetUseUnifiedInputConfig());
@ -144,6 +148,8 @@ tr("Do you want to overwrite existing mappings with the mappings from the Common
QString SOSString = tr("Speed Offset (def 0.125):") + " " + SOSValue;
ui->SpeedOffsetLabel->setText(SOSString);
});
connect(this, &KBMSettings::PushKBMEvent, this, [this]() { CheckMapping(MappingButton); });
}
void KBMSettings::ButtonConnects() {
@ -269,9 +275,17 @@ void KBMSettings::SaveKBMConfig(bool close_on_save) {
output_string = line.substr(0, equal_pos - 1);
input_string = line.substr(equal_pos + 2);
if (std::find(ControllerInputs.begin(), ControllerInputs.end(), input_string) !=
ControllerInputs.end() ||
output_string == "analog_deadzone" || output_string == "override_controller_color") {
bool controllerInputdetected = false;
for (std::string input : ControllerInputs) {
// Needed to avoid detecting backspace while detecting back
if (input_string.contains(input) && !input_string.contains("backspace")) {
controllerInputdetected = true;
break;
}
}
if (controllerInputdetected || output_string == "analog_deadzone" ||
output_string == "override_controller_color") {
lines.push_back(line);
}
}
@ -322,6 +336,11 @@ QString(tr("Cannot bind any unique input more than once. Duplicate inputs mapped
Config::SetUseUnifiedInputConfig(!ui->PerGameCheckBox->isChecked());
Config::save(Common::FS::GetUserPath(Common::FS::PathType::UserDir) / "config.toml");
if (GameRunning) {
Config::GetUseUnifiedInputConfig() ? Input::ParseInputConfig("default")
: Input::ParseInputConfig(RunningGameSerial);
}
if (close_on_save)
QWidget::close();
}
@ -388,8 +407,16 @@ void KBMSettings::SetUIValuestoMappings(std::string config_id) {
std::string output_string = line.substr(0, equal_pos - 1);
std::string input_string = line.substr(equal_pos + 2);
if (std::find(ControllerInputs.begin(), ControllerInputs.end(), input_string) ==
ControllerInputs.end()) {
bool controllerInputdetected = false;
for (std::string input : ControllerInputs) {
// Needed to avoid detecting backspace while detecting back
if (input_string.contains(input) && !input_string.contains("backspace")) {
controllerInputdetected = true;
break;
}
}
if (!controllerInputdetected) {
if (output_string == "cross") {
ui->CrossButton->setText(QString::fromStdString(input_string));
} else if (output_string == "circle") {
@ -518,7 +545,6 @@ void KBMSettings::StartTimer(QPushButton*& button) {
MappingTimer = 3;
EnableMapping = true;
MappingCompleted = false;
modifier = "";
mapping = button->text();
DisableMappingButtons();
@ -711,92 +737,98 @@ bool KBMSettings::eventFilter(QObject* obj, QEvent* event) {
break;
// symbols
case Qt::Key_QuoteLeft:
pressedKeys.insert("grave");
break;
case Qt::Key_AsciiTilde:
pressedKeys.insert("tilde");
break;
case Qt::Key_Exclam:
pressedKeys.insert("!");
pressedKeys.insert("exclamation");
break;
case Qt::Key_At:
pressedKeys.insert("@");
pressedKeys.insert("at");
break;
case Qt::Key_NumberSign:
pressedKeys.insert("#");
pressedKeys.insert("hash");
break;
case Qt::Key_Dollar:
pressedKeys.insert("$");
pressedKeys.insert("dollar");
break;
case Qt::Key_Percent:
pressedKeys.insert("%");
pressedKeys.insert("percent");
break;
case Qt::Key_AsciiCircum:
pressedKeys.insert("^");
pressedKeys.insert("caret");
break;
case Qt::Key_Ampersand:
pressedKeys.insert("&");
pressedKeys.insert("ampersand");
break;
case Qt::Key_Asterisk:
pressedKeys.insert(GetModifiedButton(Qt::KeypadModifier, "kp*", "*"));
pressedKeys.insert(GetModifiedButton(Qt::KeypadModifier, "kpasterisk", "asterisk"));
break;
case Qt::Key_ParenLeft:
pressedKeys.insert("(");
pressedKeys.insert("lparen");
break;
case Qt::Key_ParenRight:
pressedKeys.insert(")");
pressedKeys.insert("rparen");
break;
case Qt::Key_Minus:
pressedKeys.insert(GetModifiedButton(Qt::KeypadModifier, "kp-", "-"));
pressedKeys.insert(GetModifiedButton(Qt::KeypadModifier, "kpminus", "minus"));
break;
case Qt::Key_Underscore:
pressedKeys.insert("_");
pressedKeys.insert("underscore");
break;
case Qt::Key_Equal:
pressedKeys.insert(GetModifiedButton(Qt::KeypadModifier, "kp=", "="));
pressedKeys.insert(GetModifiedButton(Qt::KeypadModifier, "kpequals", "equals"));
break;
case Qt::Key_Plus:
pressedKeys.insert(GetModifiedButton(Qt::KeypadModifier, "kp+", "+"));
pressedKeys.insert(GetModifiedButton(Qt::KeypadModifier, "kpplus", "plus"));
break;
case Qt::Key_BracketLeft:
pressedKeys.insert("[");
pressedKeys.insert("lbracket");
break;
case Qt::Key_BracketRight:
pressedKeys.insert("]");
pressedKeys.insert("rbracket");
break;
case Qt::Key_BraceLeft:
pressedKeys.insert("{");
pressedKeys.insert("lbrace");
break;
case Qt::Key_BraceRight:
pressedKeys.insert("}");
pressedKeys.insert("rbrace");
break;
case Qt::Key_Backslash:
pressedKeys.insert("\\");
pressedKeys.insert("backslash");
break;
case Qt::Key_Bar:
pressedKeys.insert("|");
pressedKeys.insert("pipe");
break;
case Qt::Key_Semicolon:
pressedKeys.insert(";");
pressedKeys.insert("semicolon");
break;
case Qt::Key_Colon:
pressedKeys.insert(":");
pressedKeys.insert("colon");
break;
case Qt::Key_Apostrophe:
pressedKeys.insert("'");
pressedKeys.insert("apostrophe");
break;
case Qt::Key_QuoteDbl:
pressedKeys.insert("\"");
pressedKeys.insert("quote");
break;
case Qt::Key_Comma:
pressedKeys.insert(GetModifiedButton(Qt::KeypadModifier, "kp,", ","));
pressedKeys.insert(GetModifiedButton(Qt::KeypadModifier, "kpcomma", "comma"));
break;
case Qt::Key_Less:
pressedKeys.insert("<");
pressedKeys.insert("less");
break;
case Qt::Key_Period:
pressedKeys.insert(GetModifiedButton(Qt::KeypadModifier, "kp.", "."));
pressedKeys.insert(GetModifiedButton(Qt::KeypadModifier, "kpperiod", "period"));
break;
case Qt::Key_Greater:
pressedKeys.insert(">");
pressedKeys.insert("greater");
break;
case Qt::Key_Slash:
pressedKeys.insert(GetModifiedButton(Qt::KeypadModifier, "kp/", "/"));
pressedKeys.insert(GetModifiedButton(Qt::KeypadModifier, "kpslash", "slash"));
break;
case Qt::Key_Question:
pressedKeys.insert("question");
@ -867,7 +899,6 @@ bool KBMSettings::eventFilter(QObject* obj, QEvent* event) {
}
break;
case Qt::Key_Meta:
activateWindow();
#ifdef _WIN32
pressedKeys.insert("lwin");
#else
@ -878,7 +909,6 @@ bool KBMSettings::eventFilter(QObject* obj, QEvent* event) {
pressedKeys.insert("space");
break;
case Qt::Key_Up:
activateWindow();
pressedKeys.insert("up");
break;
case Qt::Key_Down:
@ -903,81 +933,99 @@ bool KBMSettings::eventFilter(QObject* obj, QEvent* event) {
}
return true;
}
}
if (event->type() == QEvent::MouseButtonPress) {
QMouseEvent* mouseEvent = static_cast<QMouseEvent*>(event);
if (pressedKeys.size() < 3) {
switch (mouseEvent->button()) {
case Qt::LeftButton:
pressedKeys.insert("leftbutton");
break;
case Qt::RightButton:
pressedKeys.insert("rightbutton");
break;
case Qt::MiddleButton:
pressedKeys.insert("middlebutton");
break;
case Qt::XButton1:
pressedKeys.insert("sidebuttonback");
break;
case Qt::XButton2:
pressedKeys.insert("sidebuttonforward");
break;
if (event->type() == QEvent::MouseButtonPress) {
QMouseEvent* mouseEvent = static_cast<QMouseEvent*>(event);
if (pressedKeys.size() < 3) {
switch (mouseEvent->button()) {
case Qt::LeftButton:
pressedKeys.insert("leftbutton");
break;
case Qt::RightButton:
pressedKeys.insert("rightbutton");
break;
case Qt::MiddleButton:
pressedKeys.insert("middlebutton");
break;
case Qt::XButton1:
pressedKeys.insert("sidebuttonback");
break;
case Qt::XButton2:
pressedKeys.insert("sidebuttonforward");
break;
// default case
default:
break;
// bottom text
// default case
default:
break;
// bottom text
}
return true;
}
return true;
}
}
const QList<QPushButton*> AxisList = {
ui->LStickUpButton, ui->LStickDownButton, ui->LStickLeftButton, ui->LStickRightButton,
ui->RStickUpButton, ui->LStickDownButton, ui->LStickLeftButton, ui->RStickRightButton};
const QList<QPushButton*> AxisList = {
ui->LStickUpButton, ui->LStickDownButton, ui->LStickLeftButton, ui->LStickRightButton,
ui->RStickUpButton, ui->LStickDownButton, ui->LStickLeftButton, ui->RStickRightButton};
if (event->type() == QEvent::Wheel) {
QWheelEvent* wheelEvent = static_cast<QWheelEvent*>(event);
if (pressedKeys.size() < 3) {
if (wheelEvent->angleDelta().y() > 5) {
if (std::find(AxisList.begin(), AxisList.end(), MappingButton) == AxisList.end()) {
pressedKeys.insert("mousewheelup");
} else {
QMessageBox::information(this, tr("Cannot set mapping"),
tr("Mousewheel cannot be mapped to stick outputs"));
if (event->type() == QEvent::Wheel) {
QWheelEvent* wheelEvent = static_cast<QWheelEvent*>(event);
if (pressedKeys.size() < 3) {
if (wheelEvent->angleDelta().y() > 5) {
if (std::find(AxisList.begin(), AxisList.end(), MappingButton) ==
AxisList.end()) {
pressedKeys.insert("mousewheelup");
if (QApplication::keyboardModifiers() == Qt::NoModifier)
emit PushKBMEvent();
} else {
QMessageBox::information(
this, tr("Cannot set mapping"),
tr("Mousewheel cannot be mapped to stick outputs"));
}
} else if (wheelEvent->angleDelta().y() < -5) {
if (std::find(AxisList.begin(), AxisList.end(), MappingButton) ==
AxisList.end()) {
pressedKeys.insert("mousewheeldown");
if (QApplication::keyboardModifiers() == Qt::NoModifier)
emit PushKBMEvent();
} else {
QMessageBox::information(
this, tr("Cannot set mapping"),
tr("Mousewheel cannot be mapped to stick outputs"));
}
}
} else if (wheelEvent->angleDelta().y() < -5) {
if (std::find(AxisList.begin(), AxisList.end(), MappingButton) == AxisList.end()) {
pressedKeys.insert("mousewheeldown");
} else {
QMessageBox::information(this, tr("Cannot set mapping"),
tr("Mousewheel cannot be mapped to stick outputs"));
}
}
if (wheelEvent->angleDelta().x() > 5) {
if (std::find(AxisList.begin(), AxisList.end(), MappingButton) == AxisList.end()) {
// QT changes scrolling to horizontal for all widgets with the alt modifier
pressedKeys.insert(
GetModifiedButton(Qt::AltModifier, "mousewheelup", "mousewheelright"));
} else {
QMessageBox::information(this, tr("Cannot set mapping"),
tr("Mousewheel cannot be mapped to stick outputs"));
}
} else if (wheelEvent->angleDelta().x() < -5) {
if (std::find(AxisList.begin(), AxisList.end(), MappingButton) == AxisList.end()) {
pressedKeys.insert(
GetModifiedButton(Qt::AltModifier, "mousewheeldown", "mousewheelleft"));
} else {
QMessageBox::information(this, tr("Cannot set mapping"),
tr("Mousewheel cannot be mapped to stick outputs"));
if (wheelEvent->angleDelta().x() > 5) {
if (std::find(AxisList.begin(), AxisList.end(), MappingButton) ==
AxisList.end()) {
// QT changes scrolling to horizontal for all widgets with the alt modifier
pressedKeys.insert(
GetModifiedButton(Qt::AltModifier, "mousewheelup", "mousewheelright"));
if (QApplication::keyboardModifiers() == Qt::NoModifier)
emit PushKBMEvent();
} else {
QMessageBox::information(
this, tr("Cannot set mapping"),
tr("Mousewheel cannot be mapped to stick outputs"));
}
} else if (wheelEvent->angleDelta().x() < -5) {
if (std::find(AxisList.begin(), AxisList.end(), MappingButton) ==
AxisList.end()) {
pressedKeys.insert(
GetModifiedButton(Qt::AltModifier, "mousewheeldown", "mousewheelleft"));
if (QApplication::keyboardModifiers() == Qt::NoModifier)
emit PushKBMEvent();
} else {
QMessageBox::information(
this, tr("Cannot set mapping"),
tr("Mousewheel cannot be mapped to stick outputs"));
}
}
}
}
}
if (event->type() == QEvent::KeyRelease || event->type() == QEvent::MouseButtonRelease)
emit PushKBMEvent();
}
return QDialog::eventFilter(obj, event);
}
KBMSettings::~KBMSettings() {}
KBMSettings::~KBMSettings() {}

View File

@ -23,9 +23,13 @@ class KBMSettings;
class KBMSettings : public QDialog {
Q_OBJECT
public:
explicit KBMSettings(std::shared_ptr<GameInfoClass> game_info_get, QWidget* parent = nullptr);
explicit KBMSettings(std::shared_ptr<GameInfoClass> game_info_get, bool GameRunning,
std::string GameRunningSerial, QWidget* parent = nullptr);
~KBMSettings();
signals:
void PushKBMEvent();
private Q_SLOTS:
void SaveKBMConfig(bool CloseOnSave);
void SetDefault();
@ -44,13 +48,15 @@ private:
void DisableMappingButtons();
void EnableMappingButtons();
void SetMapping(QString input);
void Cleanup();
std::string RunningGameSerial;
QSet<QString> pressedKeys;
bool GameRunning;
bool EnableMapping = false;
bool MappingCompleted = false;
bool HelpWindowOpen = false;
QString mapping;
QString modifier;
int MappingTimer;
QTimer* timer;
QPushButton* MappingButton;

View File

@ -121,35 +121,6 @@ To view the config file's syntax, check out the Syntax tab, for keybind names, v
This project began because I disliked the original, unchangeable keybinds. Rather than waiting for someone else to do it, I implemented this myself. From the default keybinds, you can clearly tell this was a project built for Bloodborne, but obviously, you can make adjustments however you like.)";
}
QString HelpDialog::faq() {
return R"(
Q: What are the emulator-wide keybinds?
A:
-F12: Triggers Renderdoc capture
-F11: Toggles fullscreen
-F10: Toggles FPS counter
-Ctrl+F10: Open the debug menu
-F9: Pauses the emulator if the debug menu is open
-F8: Reparses the config file while in-game
-F7: Toggles mouse capture and mouse input
-F6: Toggles mouse-to-gyro emulation
Q: How do I switch between mouse and controller joystick input? Why is it even required?
A: Pressing F7 toggles between mouse and controller joystick input. It is required because the program polls the mouse input, which means it checks mouse movement every frame. If it didn't move, the code would manually set the emulator's virtual controller to 0 (to the center), even if other input devices would update it.
Q: What happens if I accidentally make a typo in the config?
A: The code recognises the line as wrong and skips it, so the rest of the file will get parsed, but that line in question will be treated like a comment line. You can find these lines in the log if you search for 'input_handler'.
Q: I want to bind <input> to <output>, but your code doesn't support <input>!
A: Some keys are intentionally omitted, but if you read the bindings through, and you're sure it is not there and isn't one of the intentionally disabled ones, open an issue on https://github.com/shadps4-emu/shadPS4.
Q: What does default.ini do?
A: If you're using per-game configs, it's the base from which all new games generate their config file. If you use the unified config, then default.ini is used for every game directly instead.
Q: What does the use Per-game Config checkbox do?
A: It controls whether the config is loaded from CUSAXXXXX.ini for a game or from default.ini. This way, if you only want to manage one set of bindings, you can do so, but if you want to use a different setup for every game, that's possible as well.)";
}
QString HelpDialog::syntax() {
return R"(
Below is the file format for mouse, keyboard, and controller inputs:
@ -187,13 +158,12 @@ Keyboard:
Numbers:
'0', '1', ..., '9'
Keypad:
'kp 0', 'kp 1', ..., 'kp 9',
'kp .', 'kp ,', 'kp /', 'kp *', 'kp -', 'kp +', 'kp =', 'kp enter'
'kp0', 'kp1', ..., 'kp9',
'kpperiod', 'kpcomma', 'kpslash', 'kpasterisk', 'kpminus', 'kpplus', 'kpequals', 'kpenter'
Symbols:
'`', '~', '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '-', '_', '=', '+', '{', '}', '[', ']', '\', '|',
';', ':', ''', '"', ',', '<', '.', '>', '/', '?'
(See below)
Special keys:
'escape (text editor only)', 'printscreen', 'scrolllock', 'pausebreak',
'escape' (text editor only), 'printscreen', 'scrolllock', 'pausebreak',
'backspace', 'insert', 'delete', 'home', 'end', 'pgup', 'pgdown', 'tab',
'capslock', 'enter', 'space'
Arrow keys:
@ -228,7 +198,38 @@ Controller:
'l2'
Invalid Inputs:
'F1-F12' are reserved for emulator-wide keybinds, and cannot be bound to controller inputs.)";
'F1-F12' are reserved for emulator-wide keybinds, and cannot be bound to controller inputs.
Symbols (expanded):
` 'grave'
~ 'tilde'
! 'exclamation'
@ 'at'
# 'hash'
$ 'dollar'
% 'percent'
^ 'caret'
& 'ampersand'
* 'asterisk'
( 'lparen'
- 'minus'
_ 'underscore'
= 'equals'
+ 'plus'
[ 'lbracket'
{ 'lbrace'
\ 'backslash'
| 'pipe'
; 'semicolon'
: 'colon'
' 'apostrophe'
" 'quote'
, 'comma'
< 'less'
. 'period'
> 'greater'
/ 'slash'
? 'question')";
}
QString HelpDialog::special() {
@ -267,3 +268,35 @@ You can find these here, with detailed comments, examples, and suggestions for m
'mouse_gyro_roll_mode':
Controls whether moving the mouse sideways causes a panning or a rolling motion while mouse-to-gyro emulation is active.)";
}
QString HelpDialog::faq() {
return R"(
Q: What are the emulator-wide keybinds?
A:
-F12: Triggers Renderdoc capture
-F11: Toggles fullscreen
-F10: Toggles FPS counter
-Ctrl+F10: Open the debug menu
-F9: Pauses the emulator if the debug menu is open
-F8: Reparses the config file while in-game
-F7: Toggles mouse capture and mouse input
-F6: Toggles mouse-to-gyro emulation
Q: How do I switch between mouse and controller joystick input? Why is it even required?
A: Pressing F7 toggles between mouse and controller joystick input. It is required because the program polls the mouse input, which means it checks mouse movement every frame. If it didn't move, the code would manually set the emulator's virtual controller to 0 (to the center), even if other input devices would update it.
Q: What in the world is a 'grave' key?
A: (`). It represents one of the many symbols you can bind to a key. You can find the various symbols and their names in the Bindings tab.
Q: What happens if I accidentally make a typo in the config?
A: The code recognises the line as wrong and skips it, so the rest of the file will get parsed, but that line in question will be treated like a comment line. You can find these lines in the log if you search for 'input_handler'.
Q: I want to bind <input> to <output>, but your code doesn't support <input>!
A: Some keys are intentionally omitted, but if you read the bindings through, and you're sure it is not there and isn't one of the intentionally disabled ones, open an issue on https://github.com/shadps4-emu/shadPS4.
Q: What does default.ini do?
A: If you're using per-game configs, it's the base from which all new games generate their config file. If you use the unified config, then default.ini is used for every game directly instead.
Q: What does the use Per-game Config checkbox do?
A: It controls whether the config is loaded from CUSAXXXXX.ini for a game or from default.ini. This way, if you only want to manage one set of bindings, you can do so, but if you want to use a different setup for every game, that's possible as well.)";
}

View File

@ -473,12 +473,13 @@ void MainWindow::CreateConnects() {
});
connect(ui->controllerButton, &QPushButton::clicked, this, [this]() {
auto configWindow = new ControlSettings(m_game_info, this);
configWindow->exec();
ControlSettings* remapWindow =
new ControlSettings(m_game_info, isGameRunning, runningGameSerial, this);
remapWindow->exec();
});
connect(ui->keyboardButton, &QPushButton::clicked, this, [this]() {
auto kbmWindow = new KBMSettings(m_game_info, this);
auto kbmWindow = new KBMSettings(m_game_info, isGameRunning, runningGameSerial, this);
kbmWindow->exec();
});
@ -846,12 +847,14 @@ void MainWindow::StartGame() {
if (m_game_list_frame->currentItem()) {
int itemID = m_game_list_frame->currentItem()->row();
Common::FS::PathToQString(gamePath, m_game_info->m_games[itemID].path / "eboot.bin");
runningGameSerial = m_game_info->m_games[itemID].serial;
}
} else if (table_mode == 1) {
if (m_game_grid_frame->cellClicked) {
int itemID = (m_game_grid_frame->crtRow * m_game_grid_frame->columnCnt) +
m_game_grid_frame->crtColumn;
Common::FS::PathToQString(gamePath, m_game_info->m_games[itemID].path / "eboot.bin");
runningGameSerial = m_game_info->m_games[itemID].serial;
}
} else {
if (m_elf_viewer->currentItem()) {

View File

@ -75,11 +75,13 @@ private:
void PlayBackgroundMusic();
QIcon RecolorIcon(const QIcon& icon, bool isWhite);
void StartEmulator(std::filesystem::path);
bool isIconBlack = false;
bool isTableList = true;
bool isGameRunning = false;
bool isWhite = false;
bool is_paused = false;
std::string runningGameSerial = "";
QActionGroup* m_icon_size_act_group = nullptr;
QActionGroup* m_list_mode_act_group = nullptr;

View File

@ -0,0 +1,47 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "sdl_event_wrapper.h"
using namespace SdlEventWrapper;
Wrapper* Wrapper::WrapperInstance = nullptr;
bool Wrapper::wrapperActive = false;
Wrapper::Wrapper(QObject* parent) : QObject(parent) {}
Wrapper* Wrapper::GetInstance() {
if (WrapperInstance == nullptr) {
WrapperInstance = new Wrapper();
}
return WrapperInstance;
}
bool Wrapper::ProcessEvent(SDL_Event* event) {
switch (event->type) {
case SDL_EVENT_WINDOW_RESTORED:
return false;
case SDL_EVENT_WINDOW_EXPOSED:
return false;
case SDL_EVENT_GAMEPAD_ADDED:
return false;
case SDL_EVENT_GAMEPAD_REMOVED:
return false;
case SDL_EVENT_QUIT:
emit SDLEvent(SDL_EVENT_QUIT, 0, 0);
return true;
case SDL_EVENT_GAMEPAD_BUTTON_DOWN:
emit SDLEvent(SDL_EVENT_GAMEPAD_BUTTON_DOWN, event->gbutton.button, 0);
return true;
case SDL_EVENT_GAMEPAD_BUTTON_UP:
emit SDLEvent(SDL_EVENT_GAMEPAD_BUTTON_UP, event->gbutton.button, 0);
return true;
case SDL_EVENT_GAMEPAD_AXIS_MOTION:
emit SDLEvent(SDL_EVENT_GAMEPAD_AXIS_MOTION, event->gaxis.axis, event->gaxis.value);
return true;
// block all other SDL events while wrapper is active
default:
return true;
}
}
Wrapper::~Wrapper() {}

View File

@ -0,0 +1,25 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <QObject>
#include <SDL3/SDL_events.h>
namespace SdlEventWrapper {
class Wrapper : public QObject {
Q_OBJECT
public:
explicit Wrapper(QObject* parent = nullptr);
~Wrapper();
bool ProcessEvent(SDL_Event* event);
static Wrapper* GetInstance();
static bool wrapperActive;
static Wrapper* WrapperInstance;
signals:
void SDLEvent(int Type, int Input, int Value);
};
} // namespace SdlEventWrapper

View File

@ -762,8 +762,8 @@ void SettingsDialog::UpdateSettings() {
m_gui_settings->SetValue(gui::gl_backgroundMusicVolume, ui->BGMVolumeSlider->value());
Config::setLanguage(languageIndexes[ui->consoleLanguageComboBox->currentIndex()]);
Config::setEnableDiscordRPC(ui->discordRPCCheckbox->isChecked());
Config::setScreenWidth(ui->widthSpinBox->value());
Config::setScreenHeight(ui->heightSpinBox->value());
Config::setWindowWidth(ui->widthSpinBox->value());
Config::setWindowHeight(ui->heightSpinBox->value());
Config::setVblankDiv(ui->vblankSpinBox->value());
Config::setDumpShaders(ui->dumpShadersCheckBox->isChecked());
Config::setNullGpu(ui->nullGpuCheckBox->isChecked());

View File

@ -453,34 +453,10 @@
<source>Use per-game configs</source>
<translation>استخدام إعدادات كل لُعْبَة</translation>
</message>
<message>
<source>L1 / LB</source>
<translation>L1 / LB</translation>
</message>
<message>
<source>L2 / LT</source>
<translation>L2 / LT</translation>
</message>
<message>
<source>Back</source>
<translation>رجوع</translation>
</message>
<message>
<source>R1 / RB</source>
<translation>R1 / RB</translation>
</message>
<message>
<source>R2 / RT</source>
<translation>R2 / RT</translation>
</message>
<message>
<source>L3</source>
<translation>L3</translation>
</message>
<message>
<source>Options / Start</source>
<translation>الخيارات / البَدْء</translation>
</message>
<message>
<source>R3</source>
<translation>R3</translation>
@ -489,22 +465,6 @@
<source>Face Buttons</source>
<translation>الأزرار</translation>
</message>
<message>
<source>Triangle / Y</source>
<translation>مثلث / Y</translation>
</message>
<message>
<source>Square / X</source>
<translation>مربع / X</translation>
</message>
<message>
<source>Circle / B</source>
<translation>دائرة / B</translation>
</message>
<message>
<source>Cross / A</source>
<translation>إكس / A</translation>
</message>
<message>
<source>Right Stick Deadzone (def:2, max:127)</source>
<translation>النقطة العمياء للعصا اليمنى (الافتراضي: 2، الحد الأقصى: 127)</translation>
@ -565,6 +525,74 @@
<source>Cancel</source>
<translation>إلغاء</translation>
</message>
<message>
<source>unmapped</source>
<translation type="unfinished">unmapped</translation>
</message>
<message>
<source>L1</source>
<translation type="unfinished">L1</translation>
</message>
<message>
<source>R1</source>
<translation type="unfinished">R1</translation>
</message>
<message>
<source>L2</source>
<translation type="unfinished">L2</translation>
</message>
<message>
<source>Options</source>
<translation type="unfinished">Options</translation>
</message>
<message>
<source>R2</source>
<translation type="unfinished">R2</translation>
</message>
<message>
<source>Touchpad Left</source>
<translation type="unfinished">Touchpad Left</translation>
</message>
<message>
<source>Touchpad Center</source>
<translation type="unfinished">Touchpad Center</translation>
</message>
<message>
<source>Touchpad Right</source>
<translation type="unfinished">Touchpad Right</translation>
</message>
<message>
<source>Triangle</source>
<translation type="unfinished">Triangle</translation>
</message>
<message>
<source>Square</source>
<translation type="unfinished">Square</translation>
</message>
<message>
<source>Circle</source>
<translation type="unfinished">Circle</translation>
</message>
<message>
<source>Cross</source>
<translation type="unfinished">Cross</translation>
</message>
<message>
<source>Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons:
%1</source>
<translation type="unfinished">Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons:
%1</translation>
</message>
<message>
<source>Press a button</source>
<translation type="unfinished">Press a button</translation>
</message>
<message>
<source>Move analog stick</source>
<translation type="unfinished">Move analog stick</translation>
</message>
</context>
<context>
<name>EditorDialog</name>
@ -748,6 +776,10 @@
<source>Last updated</source>
<translation>آخر تحديث</translation>
</message>
<message>
<source>Favorite</source>
<translation type="unfinished">Favorite</translation>
</message>
</context>
<context>
<name>GameListUtils</name>
@ -950,6 +982,14 @@
<source>SFO Viewer for </source>
<translation>عارض SFO لـ </translation>
</message>
<message>
<source>Remove from Favorites</source>
<translation type="unfinished">Remove from Favorites</translation>
</message>
<message>
<source>Add to Favorites</source>
<translation type="unfinished">Add to Favorites</translation>
</message>
</context>
<context>
<name>HelpDialog</name>

View File

@ -453,34 +453,10 @@
<source>Use per-game configs</source>
<translation>Fes servir configuracions per cada joc</translation>
</message>
<message>
<source>L1 / LB</source>
<translation>L1 / LB</translation>
</message>
<message>
<source>L2 / LT</source>
<translation>L2 / LT</translation>
</message>
<message>
<source>Back</source>
<translation>Torna</translation>
</message>
<message>
<source>R1 / RB</source>
<translation>R1 / RB</translation>
</message>
<message>
<source>R2 / RT</source>
<translation>R2 / RT</translation>
</message>
<message>
<source>L3</source>
<translation>L3</translation>
</message>
<message>
<source>Options / Start</source>
<translation>Opcions / Executa</translation>
</message>
<message>
<source>R3</source>
<translation>R3</translation>
@ -489,22 +465,6 @@
<source>Face Buttons</source>
<translation>Botons d'acció</translation>
</message>
<message>
<source>Triangle / Y</source>
<translation>Triangle / Y</translation>
</message>
<message>
<source>Square / X</source>
<translation>Quadrat / X</translation>
</message>
<message>
<source>Circle / B</source>
<translation>Cercle / B</translation>
</message>
<message>
<source>Cross / A</source>
<translation>Creu / A</translation>
</message>
<message>
<source>Right Stick Deadzone (def:2, max:127)</source>
<translation>Zona morta de la palanca dreta (per defecte:2 màxim:127)</translation>
@ -565,6 +525,74 @@
<source>Cancel</source>
<translation>Cancel·la</translation>
</message>
<message>
<source>unmapped</source>
<translation>sense assignar</translation>
</message>
<message>
<source>L1</source>
<translation>L1</translation>
</message>
<message>
<source>R1</source>
<translation>R1</translation>
</message>
<message>
<source>L2</source>
<translation>L2</translation>
</message>
<message>
<source>Options</source>
<translation>Opcions</translation>
</message>
<message>
<source>R2</source>
<translation>R2</translation>
</message>
<message>
<source>Touchpad Left</source>
<translation>Touchpad esquerra</translation>
</message>
<message>
<source>Touchpad Center</source>
<translation>Touchpad centre</translation>
</message>
<message>
<source>Touchpad Right</source>
<translation>Touchpad dreta</translation>
</message>
<message>
<source>Triangle</source>
<translation>Triangle</translation>
</message>
<message>
<source>Square</source>
<translation>Quadrat</translation>
</message>
<message>
<source>Circle</source>
<translation>Cercle</translation>
</message>
<message>
<source>Cross</source>
<translation>Creu</translation>
</message>
<message>
<source>Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons:
%1</source>
<translation>No es pot assignar una entrada més d'una vegada. S'han assignat de manera duplicada pels següents botons:
%1</translation>
</message>
<message>
<source>Press a button</source>
<translation>Clica un botó</translation>
</message>
<message>
<source>Move analog stick</source>
<translation>Mou la palanca</translation>
</message>
</context>
<context>
<name>EditorDialog</name>
@ -748,6 +776,10 @@
<source>Last updated</source>
<translation>Darrera actualització</translation>
</message>
<message>
<source>Favorite</source>
<translation>Preferit</translation>
</message>
</context>
<context>
<name>GameListUtils</name>
@ -950,6 +982,14 @@
<source>SFO Viewer for </source>
<translation>Visualitzador SFO per </translation>
</message>
<message>
<source>Remove from Favorites</source>
<translation>Esborra dels preferits</translation>
</message>
<message>
<source>Add to Favorites</source>
<translation>Afegeix a preferits</translation>
</message>
</context>
<context>
<name>HelpDialog</name>

View File

@ -453,34 +453,10 @@
<source>Use per-game configs</source>
<translation type="unfinished">Use per-game configs</translation>
</message>
<message>
<source>L1 / LB</source>
<translation type="unfinished">L1 / LB</translation>
</message>
<message>
<source>L2 / LT</source>
<translation type="unfinished">L2 / LT</translation>
</message>
<message>
<source>Back</source>
<translation type="unfinished">Back</translation>
</message>
<message>
<source>R1 / RB</source>
<translation type="unfinished">R1 / RB</translation>
</message>
<message>
<source>R2 / RT</source>
<translation type="unfinished">R2 / RT</translation>
</message>
<message>
<source>L3</source>
<translation type="unfinished">L3</translation>
</message>
<message>
<source>Options / Start</source>
<translation type="unfinished">Options / Start</translation>
</message>
<message>
<source>R3</source>
<translation type="unfinished">R3</translation>
@ -489,22 +465,6 @@
<source>Face Buttons</source>
<translation type="unfinished">Face Buttons</translation>
</message>
<message>
<source>Triangle / Y</source>
<translation type="unfinished">Triangle / Y</translation>
</message>
<message>
<source>Square / X</source>
<translation type="unfinished">Square / X</translation>
</message>
<message>
<source>Circle / B</source>
<translation type="unfinished">Circle / B</translation>
</message>
<message>
<source>Cross / A</source>
<translation type="unfinished">Cross / A</translation>
</message>
<message>
<source>Right Stick Deadzone (def:2, max:127)</source>
<translation type="unfinished">Right Stick Deadzone (def:2, max:127)</translation>
@ -565,6 +525,74 @@
<source>Cancel</source>
<translation type="unfinished">Cancel</translation>
</message>
<message>
<source>unmapped</source>
<translation type="unfinished">unmapped</translation>
</message>
<message>
<source>L1</source>
<translation type="unfinished">L1</translation>
</message>
<message>
<source>R1</source>
<translation type="unfinished">R1</translation>
</message>
<message>
<source>L2</source>
<translation type="unfinished">L2</translation>
</message>
<message>
<source>Options</source>
<translation type="unfinished">Options</translation>
</message>
<message>
<source>R2</source>
<translation type="unfinished">R2</translation>
</message>
<message>
<source>Touchpad Left</source>
<translation type="unfinished">Touchpad Left</translation>
</message>
<message>
<source>Touchpad Center</source>
<translation type="unfinished">Touchpad Center</translation>
</message>
<message>
<source>Touchpad Right</source>
<translation type="unfinished">Touchpad Right</translation>
</message>
<message>
<source>Triangle</source>
<translation type="unfinished">Triangle</translation>
</message>
<message>
<source>Square</source>
<translation type="unfinished">Square</translation>
</message>
<message>
<source>Circle</source>
<translation type="unfinished">Circle</translation>
</message>
<message>
<source>Cross</source>
<translation type="unfinished">Cross</translation>
</message>
<message>
<source>Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons:
%1</source>
<translation type="unfinished">Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons:
%1</translation>
</message>
<message>
<source>Press a button</source>
<translation type="unfinished">Press a button</translation>
</message>
<message>
<source>Move analog stick</source>
<translation type="unfinished">Move analog stick</translation>
</message>
</context>
<context>
<name>EditorDialog</name>
@ -748,6 +776,10 @@
<source>Last updated</source>
<translation>Sidst opdateret</translation>
</message>
<message>
<source>Favorite</source>
<translation type="unfinished">Favorite</translation>
</message>
</context>
<context>
<name>GameListUtils</name>
@ -950,6 +982,14 @@
<source>SFO Viewer for </source>
<translation type="unfinished">SFO Viewer for </translation>
</message>
<message>
<source>Remove from Favorites</source>
<translation type="unfinished">Remove from Favorites</translation>
</message>
<message>
<source>Add to Favorites</source>
<translation type="unfinished">Add to Favorites</translation>
</message>
</context>
<context>
<name>HelpDialog</name>

View File

@ -453,34 +453,10 @@
<source>Use per-game configs</source>
<translation>Benutze Per-Game Einstellungen</translation>
</message>
<message>
<source>L1 / LB</source>
<translation>L1 / LB</translation>
</message>
<message>
<source>L2 / LT</source>
<translation>L2 / LT</translation>
</message>
<message>
<source>Back</source>
<translation>Zurück</translation>
</message>
<message>
<source>R1 / RB</source>
<translation>R1 / RB</translation>
</message>
<message>
<source>R2 / RT</source>
<translation>R2 / RT</translation>
</message>
<message>
<source>L3</source>
<translation>L3</translation>
</message>
<message>
<source>Options / Start</source>
<translation>Options / Start</translation>
</message>
<message>
<source>R3</source>
<translation>R3</translation>
@ -489,22 +465,6 @@
<source>Face Buttons</source>
<translation>Aktionstasten</translation>
</message>
<message>
<source>Triangle / Y</source>
<translation>Dreieck / Y</translation>
</message>
<message>
<source>Square / X</source>
<translation>Quadrat / X</translation>
</message>
<message>
<source>Circle / B</source>
<translation>Kreis / B</translation>
</message>
<message>
<source>Cross / A</source>
<translation>Kreuz / A</translation>
</message>
<message>
<source>Right Stick Deadzone (def:2, max:127)</source>
<translation>Rechter Stick tote Zone (def:2, max:127)</translation>
@ -565,6 +525,74 @@
<source>Cancel</source>
<translation>Abbrechen</translation>
</message>
<message>
<source>unmapped</source>
<translation type="unfinished">unmapped</translation>
</message>
<message>
<source>L1</source>
<translation type="unfinished">L1</translation>
</message>
<message>
<source>R1</source>
<translation type="unfinished">R1</translation>
</message>
<message>
<source>L2</source>
<translation type="unfinished">L2</translation>
</message>
<message>
<source>Options</source>
<translation type="unfinished">Options</translation>
</message>
<message>
<source>R2</source>
<translation type="unfinished">R2</translation>
</message>
<message>
<source>Touchpad Left</source>
<translation type="unfinished">Touchpad Left</translation>
</message>
<message>
<source>Touchpad Center</source>
<translation type="unfinished">Touchpad Center</translation>
</message>
<message>
<source>Touchpad Right</source>
<translation type="unfinished">Touchpad Right</translation>
</message>
<message>
<source>Triangle</source>
<translation type="unfinished">Triangle</translation>
</message>
<message>
<source>Square</source>
<translation type="unfinished">Square</translation>
</message>
<message>
<source>Circle</source>
<translation type="unfinished">Circle</translation>
</message>
<message>
<source>Cross</source>
<translation type="unfinished">Cross</translation>
</message>
<message>
<source>Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons:
%1</source>
<translation type="unfinished">Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons:
%1</translation>
</message>
<message>
<source>Press a button</source>
<translation type="unfinished">Press a button</translation>
</message>
<message>
<source>Move analog stick</source>
<translation type="unfinished">Move analog stick</translation>
</message>
</context>
<context>
<name>EditorDialog</name>
@ -748,6 +776,10 @@
<source>Last updated</source>
<translation>Zuletzt aktualisiert</translation>
</message>
<message>
<source>Favorite</source>
<translation type="unfinished">Favorite</translation>
</message>
</context>
<context>
<name>GameListUtils</name>
@ -950,6 +982,14 @@
<source>SFO Viewer for </source>
<translation>SFO-Betrachter für </translation>
</message>
<message>
<source>Remove from Favorites</source>
<translation type="unfinished">Remove from Favorites</translation>
</message>
<message>
<source>Add to Favorites</source>
<translation type="unfinished">Add to Favorites</translation>
</message>
</context>
<context>
<name>HelpDialog</name>

View File

@ -453,34 +453,10 @@
<source>Use per-game configs</source>
<translation type="unfinished">Use per-game configs</translation>
</message>
<message>
<source>L1 / LB</source>
<translation type="unfinished">L1 / LB</translation>
</message>
<message>
<source>L2 / LT</source>
<translation type="unfinished">L2 / LT</translation>
</message>
<message>
<source>Back</source>
<translation type="unfinished">Back</translation>
</message>
<message>
<source>R1 / RB</source>
<translation type="unfinished">R1 / RB</translation>
</message>
<message>
<source>R2 / RT</source>
<translation type="unfinished">R2 / RT</translation>
</message>
<message>
<source>L3</source>
<translation type="unfinished">L3</translation>
</message>
<message>
<source>Options / Start</source>
<translation type="unfinished">Options / Start</translation>
</message>
<message>
<source>R3</source>
<translation type="unfinished">R3</translation>
@ -489,22 +465,6 @@
<source>Face Buttons</source>
<translation type="unfinished">Face Buttons</translation>
</message>
<message>
<source>Triangle / Y</source>
<translation type="unfinished">Triangle / Y</translation>
</message>
<message>
<source>Square / X</source>
<translation type="unfinished">Square / X</translation>
</message>
<message>
<source>Circle / B</source>
<translation type="unfinished">Circle / B</translation>
</message>
<message>
<source>Cross / A</source>
<translation type="unfinished">Cross / A</translation>
</message>
<message>
<source>Right Stick Deadzone (def:2, max:127)</source>
<translation type="unfinished">Right Stick Deadzone (def:2, max:127)</translation>
@ -565,6 +525,74 @@
<source>Cancel</source>
<translation type="unfinished">Cancel</translation>
</message>
<message>
<source>unmapped</source>
<translation type="unfinished">unmapped</translation>
</message>
<message>
<source>L1</source>
<translation type="unfinished">L1</translation>
</message>
<message>
<source>R1</source>
<translation type="unfinished">R1</translation>
</message>
<message>
<source>L2</source>
<translation type="unfinished">L2</translation>
</message>
<message>
<source>Options</source>
<translation type="unfinished">Options</translation>
</message>
<message>
<source>R2</source>
<translation type="unfinished">R2</translation>
</message>
<message>
<source>Touchpad Left</source>
<translation type="unfinished">Touchpad Left</translation>
</message>
<message>
<source>Touchpad Center</source>
<translation type="unfinished">Touchpad Center</translation>
</message>
<message>
<source>Touchpad Right</source>
<translation type="unfinished">Touchpad Right</translation>
</message>
<message>
<source>Triangle</source>
<translation type="unfinished">Triangle</translation>
</message>
<message>
<source>Square</source>
<translation type="unfinished">Square</translation>
</message>
<message>
<source>Circle</source>
<translation type="unfinished">Circle</translation>
</message>
<message>
<source>Cross</source>
<translation type="unfinished">Cross</translation>
</message>
<message>
<source>Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons:
%1</source>
<translation type="unfinished">Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons:
%1</translation>
</message>
<message>
<source>Press a button</source>
<translation type="unfinished">Press a button</translation>
</message>
<message>
<source>Move analog stick</source>
<translation type="unfinished">Move analog stick</translation>
</message>
</context>
<context>
<name>EditorDialog</name>
@ -748,6 +776,10 @@
<source>Last updated</source>
<translation>Τελευταία ενημέρωση</translation>
</message>
<message>
<source>Favorite</source>
<translation type="unfinished">Favorite</translation>
</message>
</context>
<context>
<name>GameListUtils</name>
@ -950,6 +982,14 @@
<source>SFO Viewer for </source>
<translation type="unfinished">SFO Viewer for </translation>
</message>
<message>
<source>Remove from Favorites</source>
<translation type="unfinished">Remove from Favorites</translation>
</message>
<message>
<source>Add to Favorites</source>
<translation type="unfinished">Add to Favorites</translation>
</message>
</context>
<context>
<name>HelpDialog</name>

View File

@ -453,34 +453,10 @@
<source>Use per-game configs</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>L1 / LB</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>L2 / LT</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Back</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>R1 / RB</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>R2 / RT</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>L3</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Options / Start</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>R3</source>
<translation type="unfinished"></translation>
@ -489,22 +465,6 @@
<source>Face Buttons</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Triangle / Y</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Square / X</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Circle / B</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Cross / A</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Right Stick Deadzone (def:2, max:127)</source>
<translation type="unfinished"></translation>
@ -565,6 +525,72 @@
<source>Cancel</source>
<translation type="unfinished">Cancel</translation>
</message>
<message>
<source>unmapped</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>L1</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>R1</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>L2</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Options</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>R2</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Touchpad Left</source>
<translation type="unfinished">Touchpad Left</translation>
</message>
<message>
<source>Touchpad Center</source>
<translation type="unfinished">Touchpad Center</translation>
</message>
<message>
<source>Touchpad Right</source>
<translation type="unfinished">Touchpad Right</translation>
</message>
<message>
<source>Triangle</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Square</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Circle</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Cross</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons:
%1</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Press a button</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Move analog stick</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>EditorDialog</name>

View File

@ -453,34 +453,10 @@
<source>Use per-game configs</source>
<translation>Usar configuraciones por juego</translation>
</message>
<message>
<source>L1 / LB</source>
<translation>L1/LB</translation>
</message>
<message>
<source>L2 / LT</source>
<translation>L2/LT</translation>
</message>
<message>
<source>Back</source>
<translation>Back</translation>
</message>
<message>
<source>R1 / RB</source>
<translation>R1/RB</translation>
</message>
<message>
<source>R2 / RT</source>
<translation>R2/RT</translation>
</message>
<message>
<source>L3</source>
<translation>L3</translation>
</message>
<message>
<source>Options / Start</source>
<translation>Options/Start</translation>
</message>
<message>
<source>R3</source>
<translation>R3</translation>
@ -489,22 +465,6 @@
<source>Face Buttons</source>
<translation>Botones de acción</translation>
</message>
<message>
<source>Triangle / Y</source>
<translation>Triángulo/Y</translation>
</message>
<message>
<source>Square / X</source>
<translation>Cuadrado/X</translation>
</message>
<message>
<source>Circle / B</source>
<translation>Círculo/B</translation>
</message>
<message>
<source>Cross / A</source>
<translation>Cruz / A</translation>
</message>
<message>
<source>Right Stick Deadzone (def:2, max:127)</source>
<translation>Zona muerta del stick derecho (defecto: 2, máx.: 127)</translation>
@ -565,6 +525,72 @@
<source>Cancel</source>
<translation>Cancelar</translation>
</message>
<message>
<source>unmapped</source>
<translation>sin vincular</translation>
</message>
<message>
<source>L1</source>
<translation>L1</translation>
</message>
<message>
<source>R1</source>
<translation>R1</translation>
</message>
<message>
<source>L2</source>
<translation>L2</translation>
</message>
<message>
<source>Options</source>
<translation>Opciones</translation>
</message>
<message>
<source>R2</source>
<translation>R2</translation>
</message>
<message>
<source>Touchpad Left</source>
<translation>Izquierda del Touchpad</translation>
</message>
<message>
<source>Touchpad Center</source>
<translation>Centro del Touchpad</translation>
</message>
<message>
<source>Touchpad Right</source>
<translation>Derecha del Touchpad</translation>
</message>
<message>
<source>Triangle</source>
<translation>Triángulo</translation>
</message>
<message>
<source>Square</source>
<translation>Cuadrado</translation>
</message>
<message>
<source>Circle</source>
<translation>Círculo</translation>
</message>
<message>
<source>Cross</source>
<translation>Equis</translation>
</message>
<message>
<source>Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons:
%1</source>
<translation>No se puede asignar un control único más de una vez. Controles duplicados asignados a los siguientes botones</translation>
</message>
<message>
<source>Press a button</source>
<translation>Presiona un botón</translation>
</message>
<message>
<source>Move analog stick</source>
<translation>Mueve el stick analógico</translation>
</message>
</context>
<context>
<name>EditorDialog</name>
@ -748,6 +774,10 @@
<source>Last updated</source>
<translation>Última actualización</translation>
</message>
<message>
<source>Favorite</source>
<translation>Favorito</translation>
</message>
</context>
<context>
<name>GameListUtils</name>
@ -950,6 +980,14 @@
<source>SFO Viewer for </source>
<translation>Visualizador de SFO para </translation>
</message>
<message>
<source>Remove from Favorites</source>
<translation>Eliminar de Favoritos</translation>
</message>
<message>
<source>Add to Favorites</source>
<translation>Añadir a favoritos</translation>
</message>
</context>
<context>
<name>HelpDialog</name>
@ -1180,21 +1218,19 @@
<source>Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons:
%1</source>
<translation type="unfinished">Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons:
%1</translation>
<translation>No se puede asignar un control único más de una vez. Controles duplicados asignados a los siguientes botones:</translation>
</message>
<message>
<source>Touchpad Left</source>
<translation type="unfinished">Touchpad Left</translation>
<translation>Izquierda del Touchpad</translation>
</message>
<message>
<source>Touchpad Center</source>
<translation type="unfinished">Touchpad Center</translation>
<translation>Centro del Touchpad</translation>
</message>
<message>
<source>Touchpad Right</source>
<translation type="unfinished">Touchpad Right</translation>
<translation>Derecha del Touchpad</translation>
</message>
</context>
<context>

View File

@ -453,34 +453,10 @@
<source>Use per-game configs</source>
<translation>از پیکربندیهای مخصوص هر بازی استفاده کنید</translation>
</message>
<message>
<source>L1 / LB</source>
<translation type="unfinished">L1 / LB</translation>
</message>
<message>
<source>L2 / LT</source>
<translation type="unfinished">L2 / LT</translation>
</message>
<message>
<source>Back</source>
<translation type="unfinished">Back</translation>
</message>
<message>
<source>R1 / RB</source>
<translation type="unfinished">R1 / RB</translation>
</message>
<message>
<source>R2 / RT</source>
<translation type="unfinished">R2 / RT</translation>
</message>
<message>
<source>L3</source>
<translation type="unfinished">L3</translation>
</message>
<message>
<source>Options / Start</source>
<translation type="unfinished">Options / Start</translation>
</message>
<message>
<source>R3</source>
<translation>R3</translation>
@ -489,22 +465,6 @@
<source>Face Buttons</source>
<translation type="unfinished">Face Buttons</translation>
</message>
<message>
<source>Triangle / Y</source>
<translation>مثلث / Y</translation>
</message>
<message>
<source>Square / X</source>
<translation type="unfinished">Square / X</translation>
</message>
<message>
<source>Circle / B</source>
<translation type="unfinished">Circle / B</translation>
</message>
<message>
<source>Cross / A</source>
<translation type="unfinished">Cross / A</translation>
</message>
<message>
<source>Right Stick Deadzone (def:2, max:127)</source>
<translation type="unfinished">Right Stick Deadzone (def:2, max:127)</translation>
@ -565,6 +525,74 @@
<source>Cancel</source>
<translation type="unfinished">Cancel</translation>
</message>
<message>
<source>unmapped</source>
<translation type="unfinished">unmapped</translation>
</message>
<message>
<source>L1</source>
<translation type="unfinished">L1</translation>
</message>
<message>
<source>R1</source>
<translation type="unfinished">R1</translation>
</message>
<message>
<source>L2</source>
<translation type="unfinished">L2</translation>
</message>
<message>
<source>Options</source>
<translation type="unfinished">Options</translation>
</message>
<message>
<source>R2</source>
<translation type="unfinished">R2</translation>
</message>
<message>
<source>Touchpad Left</source>
<translation type="unfinished">Touchpad Left</translation>
</message>
<message>
<source>Touchpad Center</source>
<translation type="unfinished">Touchpad Center</translation>
</message>
<message>
<source>Touchpad Right</source>
<translation type="unfinished">Touchpad Right</translation>
</message>
<message>
<source>Triangle</source>
<translation type="unfinished">Triangle</translation>
</message>
<message>
<source>Square</source>
<translation type="unfinished">Square</translation>
</message>
<message>
<source>Circle</source>
<translation type="unfinished">Circle</translation>
</message>
<message>
<source>Cross</source>
<translation type="unfinished">Cross</translation>
</message>
<message>
<source>Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons:
%1</source>
<translation type="unfinished">Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons:
%1</translation>
</message>
<message>
<source>Press a button</source>
<translation type="unfinished">Press a button</translation>
</message>
<message>
<source>Move analog stick</source>
<translation type="unfinished">Move analog stick</translation>
</message>
</context>
<context>
<name>EditorDialog</name>
@ -748,6 +776,10 @@
<source>Last updated</source>
<translation>آخرین بهروزرسانی</translation>
</message>
<message>
<source>Favorite</source>
<translation type="unfinished">Favorite</translation>
</message>
</context>
<context>
<name>GameListUtils</name>
@ -950,6 +982,14 @@
<source>SFO Viewer for </source>
<translation>SFO مشاهده </translation>
</message>
<message>
<source>Remove from Favorites</source>
<translation type="unfinished">Remove from Favorites</translation>
</message>
<message>
<source>Add to Favorites</source>
<translation type="unfinished">Add to Favorites</translation>
</message>
</context>
<context>
<name>HelpDialog</name>

View File

@ -453,34 +453,10 @@
<source>Use per-game configs</source>
<translation>Käytä pelikohtaisia asetuksia</translation>
</message>
<message>
<source>L1 / LB</source>
<translation>L1 / LB</translation>
</message>
<message>
<source>L2 / LT</source>
<translation>L2 / LT</translation>
</message>
<message>
<source>Back</source>
<translation>Back</translation>
</message>
<message>
<source>R1 / RB</source>
<translation>R1 / RB</translation>
</message>
<message>
<source>R2 / RT</source>
<translation>R2 / RT</translation>
</message>
<message>
<source>L3</source>
<translation>L3</translation>
</message>
<message>
<source>Options / Start</source>
<translation>Options / Start</translation>
</message>
<message>
<source>R3</source>
<translation>R3</translation>
@ -489,22 +465,6 @@
<source>Face Buttons</source>
<translation>Etunäppäimet</translation>
</message>
<message>
<source>Triangle / Y</source>
<translation>Kolmio / Y</translation>
</message>
<message>
<source>Square / X</source>
<translation>Neliö / X</translation>
</message>
<message>
<source>Circle / B</source>
<translation>Ympyrä / B</translation>
</message>
<message>
<source>Cross / A</source>
<translation>Rasti / A</translation>
</message>
<message>
<source>Right Stick Deadzone (def:2, max:127)</source>
<translation>Oikean Analogin Deadzone (oletus:2 max:127)</translation>
@ -565,6 +525,74 @@
<source>Cancel</source>
<translation>Peruuta</translation>
</message>
<message>
<source>unmapped</source>
<translation type="unfinished">unmapped</translation>
</message>
<message>
<source>L1</source>
<translation type="unfinished">L1</translation>
</message>
<message>
<source>R1</source>
<translation type="unfinished">R1</translation>
</message>
<message>
<source>L2</source>
<translation type="unfinished">L2</translation>
</message>
<message>
<source>Options</source>
<translation type="unfinished">Options</translation>
</message>
<message>
<source>R2</source>
<translation type="unfinished">R2</translation>
</message>
<message>
<source>Touchpad Left</source>
<translation type="unfinished">Touchpad Left</translation>
</message>
<message>
<source>Touchpad Center</source>
<translation type="unfinished">Touchpad Center</translation>
</message>
<message>
<source>Touchpad Right</source>
<translation type="unfinished">Touchpad Right</translation>
</message>
<message>
<source>Triangle</source>
<translation type="unfinished">Triangle</translation>
</message>
<message>
<source>Square</source>
<translation type="unfinished">Square</translation>
</message>
<message>
<source>Circle</source>
<translation type="unfinished">Circle</translation>
</message>
<message>
<source>Cross</source>
<translation type="unfinished">Cross</translation>
</message>
<message>
<source>Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons:
%1</source>
<translation type="unfinished">Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons:
%1</translation>
</message>
<message>
<source>Press a button</source>
<translation type="unfinished">Press a button</translation>
</message>
<message>
<source>Move analog stick</source>
<translation type="unfinished">Move analog stick</translation>
</message>
</context>
<context>
<name>EditorDialog</name>
@ -748,6 +776,10 @@
<source>Last updated</source>
<translation>Viimeksi päivitetty</translation>
</message>
<message>
<source>Favorite</source>
<translation type="unfinished">Favorite</translation>
</message>
</context>
<context>
<name>GameListUtils</name>
@ -950,6 +982,14 @@
<source>SFO Viewer for </source>
<translation type="unfinished">SFO Viewer for </translation>
</message>
<message>
<source>Remove from Favorites</source>
<translation type="unfinished">Remove from Favorites</translation>
</message>
<message>
<source>Add to Favorites</source>
<translation type="unfinished">Add to Favorites</translation>
</message>
</context>
<context>
<name>HelpDialog</name>

View File

@ -453,34 +453,10 @@
<source>Use per-game configs</source>
<translation>Utiliser les configurations par jeu</translation>
</message>
<message>
<source>L1 / LB</source>
<translation>L1 / LB</translation>
</message>
<message>
<source>L2 / LT</source>
<translation>L2 / LT</translation>
</message>
<message>
<source>Back</source>
<translation>Retour</translation>
</message>
<message>
<source>R1 / RB</source>
<translation>R1 / RB</translation>
</message>
<message>
<source>R2 / RT</source>
<translation>R2 / RT</translation>
</message>
<message>
<source>L3</source>
<translation>L3</translation>
</message>
<message>
<source>Options / Start</source>
<translation>Options / Start</translation>
</message>
<message>
<source>R3</source>
<translation>R3</translation>
@ -489,22 +465,6 @@
<source>Face Buttons</source>
<translation>Touches d'action</translation>
</message>
<message>
<source>Triangle / Y</source>
<translation>Triangle / Y</translation>
</message>
<message>
<source>Square / X</source>
<translation>Carré / X</translation>
</message>
<message>
<source>Circle / B</source>
<translation>Rond / B</translation>
</message>
<message>
<source>Cross / A</source>
<translation>Croix / A</translation>
</message>
<message>
<source>Right Stick Deadzone (def:2, max:127)</source>
<translation>Joystick Gauche Deadzone (def:2 max:127)</translation>
@ -565,6 +525,74 @@
<source>Cancel</source>
<translation>Annuler</translation>
</message>
<message>
<source>unmapped</source>
<translation type="unfinished">unmapped</translation>
</message>
<message>
<source>L1</source>
<translation type="unfinished">L1</translation>
</message>
<message>
<source>R1</source>
<translation type="unfinished">R1</translation>
</message>
<message>
<source>L2</source>
<translation type="unfinished">L2</translation>
</message>
<message>
<source>Options</source>
<translation type="unfinished">Options</translation>
</message>
<message>
<source>R2</source>
<translation type="unfinished">R2</translation>
</message>
<message>
<source>Touchpad Left</source>
<translation type="unfinished">Touchpad Left</translation>
</message>
<message>
<source>Touchpad Center</source>
<translation type="unfinished">Touchpad Center</translation>
</message>
<message>
<source>Touchpad Right</source>
<translation type="unfinished">Touchpad Right</translation>
</message>
<message>
<source>Triangle</source>
<translation type="unfinished">Triangle</translation>
</message>
<message>
<source>Square</source>
<translation type="unfinished">Square</translation>
</message>
<message>
<source>Circle</source>
<translation type="unfinished">Circle</translation>
</message>
<message>
<source>Cross</source>
<translation type="unfinished">Cross</translation>
</message>
<message>
<source>Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons:
%1</source>
<translation type="unfinished">Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons:
%1</translation>
</message>
<message>
<source>Press a button</source>
<translation type="unfinished">Press a button</translation>
</message>
<message>
<source>Move analog stick</source>
<translation type="unfinished">Move analog stick</translation>
</message>
</context>
<context>
<name>EditorDialog</name>
@ -748,6 +776,10 @@
<source>Last updated</source>
<translation>Dernière mise à jour</translation>
</message>
<message>
<source>Favorite</source>
<translation type="unfinished">Favorite</translation>
</message>
</context>
<context>
<name>GameListUtils</name>
@ -950,6 +982,14 @@
<source>SFO Viewer for </source>
<translation>Visionneuse SFO pour </translation>
</message>
<message>
<source>Remove from Favorites</source>
<translation type="unfinished">Remove from Favorites</translation>
</message>
<message>
<source>Add to Favorites</source>
<translation type="unfinished">Add to Favorites</translation>
</message>
</context>
<context>
<name>HelpDialog</name>

View File

@ -453,34 +453,10 @@
<source>Use per-game configs</source>
<translation type="unfinished">Use per-game configs</translation>
</message>
<message>
<source>L1 / LB</source>
<translation type="unfinished">L1 / LB</translation>
</message>
<message>
<source>L2 / LT</source>
<translation type="unfinished">L2 / LT</translation>
</message>
<message>
<source>Back</source>
<translation type="unfinished">Back</translation>
</message>
<message>
<source>R1 / RB</source>
<translation type="unfinished">R1 / RB</translation>
</message>
<message>
<source>R2 / RT</source>
<translation type="unfinished">R2 / RT</translation>
</message>
<message>
<source>L3</source>
<translation type="unfinished">L3</translation>
</message>
<message>
<source>Options / Start</source>
<translation type="unfinished">Options / Start</translation>
</message>
<message>
<source>R3</source>
<translation type="unfinished">R3</translation>
@ -489,22 +465,6 @@
<source>Face Buttons</source>
<translation type="unfinished">Face Buttons</translation>
</message>
<message>
<source>Triangle / Y</source>
<translation type="unfinished">Triangle / Y</translation>
</message>
<message>
<source>Square / X</source>
<translation type="unfinished">Square / X</translation>
</message>
<message>
<source>Circle / B</source>
<translation type="unfinished">Circle / B</translation>
</message>
<message>
<source>Cross / A</source>
<translation type="unfinished">Cross / A</translation>
</message>
<message>
<source>Right Stick Deadzone (def:2, max:127)</source>
<translation type="unfinished">Right Stick Deadzone (def:2, max:127)</translation>
@ -565,6 +525,74 @@
<source>Cancel</source>
<translation type="unfinished">Cancel</translation>
</message>
<message>
<source>unmapped</source>
<translation type="unfinished">unmapped</translation>
</message>
<message>
<source>L1</source>
<translation type="unfinished">L1</translation>
</message>
<message>
<source>R1</source>
<translation type="unfinished">R1</translation>
</message>
<message>
<source>L2</source>
<translation type="unfinished">L2</translation>
</message>
<message>
<source>Options</source>
<translation type="unfinished">Options</translation>
</message>
<message>
<source>R2</source>
<translation type="unfinished">R2</translation>
</message>
<message>
<source>Touchpad Left</source>
<translation type="unfinished">Touchpad Left</translation>
</message>
<message>
<source>Touchpad Center</source>
<translation type="unfinished">Touchpad Center</translation>
</message>
<message>
<source>Touchpad Right</source>
<translation type="unfinished">Touchpad Right</translation>
</message>
<message>
<source>Triangle</source>
<translation type="unfinished">Triangle</translation>
</message>
<message>
<source>Square</source>
<translation type="unfinished">Square</translation>
</message>
<message>
<source>Circle</source>
<translation type="unfinished">Circle</translation>
</message>
<message>
<source>Cross</source>
<translation type="unfinished">Cross</translation>
</message>
<message>
<source>Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons:
%1</source>
<translation type="unfinished">Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons:
%1</translation>
</message>
<message>
<source>Press a button</source>
<translation type="unfinished">Press a button</translation>
</message>
<message>
<source>Move analog stick</source>
<translation type="unfinished">Move analog stick</translation>
</message>
</context>
<context>
<name>EditorDialog</name>
@ -748,6 +776,10 @@
<source>Last updated</source>
<translation>Utoljára frissítve</translation>
</message>
<message>
<source>Favorite</source>
<translation type="unfinished">Favorite</translation>
</message>
</context>
<context>
<name>GameListUtils</name>
@ -950,6 +982,14 @@
<source>SFO Viewer for </source>
<translation type="unfinished">SFO Viewer for </translation>
</message>
<message>
<source>Remove from Favorites</source>
<translation type="unfinished">Remove from Favorites</translation>
</message>
<message>
<source>Add to Favorites</source>
<translation type="unfinished">Add to Favorites</translation>
</message>
</context>
<context>
<name>HelpDialog</name>

View File

@ -453,34 +453,10 @@
<source>Use per-game configs</source>
<translation type="unfinished">Use per-game configs</translation>
</message>
<message>
<source>L1 / LB</source>
<translation type="unfinished">L1 / LB</translation>
</message>
<message>
<source>L2 / LT</source>
<translation type="unfinished">L2 / LT</translation>
</message>
<message>
<source>Back</source>
<translation type="unfinished">Back</translation>
</message>
<message>
<source>R1 / RB</source>
<translation type="unfinished">R1 / RB</translation>
</message>
<message>
<source>R2 / RT</source>
<translation type="unfinished">R2 / RT</translation>
</message>
<message>
<source>L3</source>
<translation type="unfinished">L3</translation>
</message>
<message>
<source>Options / Start</source>
<translation type="unfinished">Options / Start</translation>
</message>
<message>
<source>R3</source>
<translation type="unfinished">R3</translation>
@ -489,22 +465,6 @@
<source>Face Buttons</source>
<translation type="unfinished">Face Buttons</translation>
</message>
<message>
<source>Triangle / Y</source>
<translation type="unfinished">Triangle / Y</translation>
</message>
<message>
<source>Square / X</source>
<translation type="unfinished">Square / X</translation>
</message>
<message>
<source>Circle / B</source>
<translation type="unfinished">Circle / B</translation>
</message>
<message>
<source>Cross / A</source>
<translation type="unfinished">Cross / A</translation>
</message>
<message>
<source>Right Stick Deadzone (def:2, max:127)</source>
<translation type="unfinished">Right Stick Deadzone (def:2, max:127)</translation>
@ -565,6 +525,74 @@
<source>Cancel</source>
<translation type="unfinished">Cancel</translation>
</message>
<message>
<source>unmapped</source>
<translation type="unfinished">unmapped</translation>
</message>
<message>
<source>L1</source>
<translation type="unfinished">L1</translation>
</message>
<message>
<source>R1</source>
<translation type="unfinished">R1</translation>
</message>
<message>
<source>L2</source>
<translation type="unfinished">L2</translation>
</message>
<message>
<source>Options</source>
<translation type="unfinished">Options</translation>
</message>
<message>
<source>R2</source>
<translation type="unfinished">R2</translation>
</message>
<message>
<source>Touchpad Left</source>
<translation type="unfinished">Touchpad Left</translation>
</message>
<message>
<source>Touchpad Center</source>
<translation type="unfinished">Touchpad Center</translation>
</message>
<message>
<source>Touchpad Right</source>
<translation type="unfinished">Touchpad Right</translation>
</message>
<message>
<source>Triangle</source>
<translation type="unfinished">Triangle</translation>
</message>
<message>
<source>Square</source>
<translation type="unfinished">Square</translation>
</message>
<message>
<source>Circle</source>
<translation type="unfinished">Circle</translation>
</message>
<message>
<source>Cross</source>
<translation type="unfinished">Cross</translation>
</message>
<message>
<source>Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons:
%1</source>
<translation type="unfinished">Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons:
%1</translation>
</message>
<message>
<source>Press a button</source>
<translation type="unfinished">Press a button</translation>
</message>
<message>
<source>Move analog stick</source>
<translation type="unfinished">Move analog stick</translation>
</message>
</context>
<context>
<name>EditorDialog</name>
@ -748,6 +776,10 @@
<source>Last updated</source>
<translation>Terakhir diperbarui</translation>
</message>
<message>
<source>Favorite</source>
<translation type="unfinished">Favorite</translation>
</message>
</context>
<context>
<name>GameListUtils</name>
@ -950,6 +982,14 @@
<source>SFO Viewer for </source>
<translation type="unfinished">SFO Viewer for </translation>
</message>
<message>
<source>Remove from Favorites</source>
<translation type="unfinished">Remove from Favorites</translation>
</message>
<message>
<source>Add to Favorites</source>
<translation type="unfinished">Add to Favorites</translation>
</message>
</context>
<context>
<name>HelpDialog</name>

View File

@ -453,34 +453,10 @@
<source>Use per-game configs</source>
<translation>Usa configurazioni per gioco</translation>
</message>
<message>
<source>L1 / LB</source>
<translation>L1 / LB</translation>
</message>
<message>
<source>L2 / LT</source>
<translation>L2 / LT</translation>
</message>
<message>
<source>Back</source>
<translation>Indietro</translation>
</message>
<message>
<source>R1 / RB</source>
<translation>R1 / RB</translation>
</message>
<message>
<source>R2 / RT</source>
<translation>R2 / RT</translation>
</message>
<message>
<source>L3</source>
<translation>L3</translation>
</message>
<message>
<source>Options / Start</source>
<translation>Opzioni / Avvio</translation>
</message>
<message>
<source>R3</source>
<translation>R3</translation>
@ -489,22 +465,6 @@
<source>Face Buttons</source>
<translation>Pulsanti Frontali</translation>
</message>
<message>
<source>Triangle / Y</source>
<translation>Triangolo / Y</translation>
</message>
<message>
<source>Square / X</source>
<translation>Quadrato / X</translation>
</message>
<message>
<source>Circle / B</source>
<translation>Cerchio / B</translation>
</message>
<message>
<source>Cross / A</source>
<translation>Croce / A</translation>
</message>
<message>
<source>Right Stick Deadzone (def:2, max:127)</source>
<translation>Zona Morta Levetta Destra (def:2 max:127)</translation>
@ -565,6 +525,74 @@
<source>Cancel</source>
<translation>Annulla</translation>
</message>
<message>
<source>unmapped</source>
<translation>non mappato</translation>
</message>
<message>
<source>L1</source>
<translation>L1</translation>
</message>
<message>
<source>R1</source>
<translation>R1</translation>
</message>
<message>
<source>L2</source>
<translation>L2</translation>
</message>
<message>
<source>Options</source>
<translation>Opzioni</translation>
</message>
<message>
<source>R2</source>
<translation>R2</translation>
</message>
<message>
<source>Touchpad Left</source>
<translation>Touchpad Sinistra</translation>
</message>
<message>
<source>Touchpad Center</source>
<translation>Touchpad Centrale</translation>
</message>
<message>
<source>Touchpad Right</source>
<translation>Touchpad Destra</translation>
</message>
<message>
<source>Triangle</source>
<translation>Triangolo</translation>
</message>
<message>
<source>Square</source>
<translation>Quadrato</translation>
</message>
<message>
<source>Circle</source>
<translation>Cerchio</translation>
</message>
<message>
<source>Cross</source>
<translation>Croce</translation>
</message>
<message>
<source>Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons:
%1</source>
<translation>Non è possibile associare più di una volta qualsiasi input univoco. Sono presenti input duplicati mappati ai seguenti pulsanti:
%1</translation>
</message>
<message>
<source>Press a button</source>
<translation>Premi un pulsante</translation>
</message>
<message>
<source>Move analog stick</source>
<translation>Muovi levetta analogica</translation>
</message>
</context>
<context>
<name>EditorDialog</name>
@ -748,6 +776,10 @@
<source>Last updated</source>
<translation>Ultimo aggiornamento</translation>
</message>
<message>
<source>Favorite</source>
<translation>Preferiti</translation>
</message>
</context>
<context>
<name>GameListUtils</name>
@ -950,6 +982,14 @@
<source>SFO Viewer for </source>
<translation>Visualizzatore SFO per </translation>
</message>
<message>
<source>Remove from Favorites</source>
<translation>Rimuovi dai Preferiti</translation>
</message>
<message>
<source>Add to Favorites</source>
<translation>Aggiungi ai preferiti</translation>
</message>
</context>
<context>
<name>HelpDialog</name>
@ -1186,15 +1226,15 @@
</message>
<message>
<source>Touchpad Left</source>
<translation type="unfinished">Touchpad Left</translation>
<translation>Touchpad Sinistra</translation>
</message>
<message>
<source>Touchpad Center</source>
<translation type="unfinished">Touchpad Center</translation>
<translation>Touchpad Centrale</translation>
</message>
<message>
<source>Touchpad Right</source>
<translation type="unfinished">Touchpad Right</translation>
<translation>Touchpad Destra</translation>
</message>
</context>
<context>

View File

@ -453,34 +453,10 @@
<source>Use per-game configs</source>
<translation type="unfinished">Use per-game configs</translation>
</message>
<message>
<source>L1 / LB</source>
<translation>L1 / LB</translation>
</message>
<message>
<source>L2 / LT</source>
<translation>L2 / LT</translation>
</message>
<message>
<source>Back</source>
<translation></translation>
</message>
<message>
<source>R1 / RB</source>
<translation>R1 / RB</translation>
</message>
<message>
<source>R2 / RT</source>
<translation>R2 / RT</translation>
</message>
<message>
<source>L3</source>
<translation>L3</translation>
</message>
<message>
<source>Options / Start</source>
<translation>Options / Start</translation>
</message>
<message>
<source>R3</source>
<translation>R3</translation>
@ -489,22 +465,6 @@
<source>Face Buttons</source>
<translation type="unfinished">Face Buttons</translation>
</message>
<message>
<source>Triangle / Y</source>
<translation> / Y</translation>
</message>
<message>
<source>Square / X</source>
<translation> / X</translation>
</message>
<message>
<source>Circle / B</source>
<translation> / B</translation>
</message>
<message>
<source>Cross / A</source>
<translation> / A</translation>
</message>
<message>
<source>Right Stick Deadzone (def:2, max:127)</source>
<translation>既定:2, 最大:127</translation>
@ -565,6 +525,74 @@
<source>Cancel</source>
<translation></translation>
</message>
<message>
<source>unmapped</source>
<translation type="unfinished">unmapped</translation>
</message>
<message>
<source>L1</source>
<translation type="unfinished">L1</translation>
</message>
<message>
<source>R1</source>
<translation type="unfinished">R1</translation>
</message>
<message>
<source>L2</source>
<translation type="unfinished">L2</translation>
</message>
<message>
<source>Options</source>
<translation type="unfinished">Options</translation>
</message>
<message>
<source>R2</source>
<translation type="unfinished">R2</translation>
</message>
<message>
<source>Touchpad Left</source>
<translation type="unfinished">Touchpad Left</translation>
</message>
<message>
<source>Touchpad Center</source>
<translation type="unfinished">Touchpad Center</translation>
</message>
<message>
<source>Touchpad Right</source>
<translation type="unfinished">Touchpad Right</translation>
</message>
<message>
<source>Triangle</source>
<translation type="unfinished">Triangle</translation>
</message>
<message>
<source>Square</source>
<translation type="unfinished">Square</translation>
</message>
<message>
<source>Circle</source>
<translation type="unfinished">Circle</translation>
</message>
<message>
<source>Cross</source>
<translation type="unfinished">Cross</translation>
</message>
<message>
<source>Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons:
%1</source>
<translation type="unfinished">Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons:
%1</translation>
</message>
<message>
<source>Press a button</source>
<translation type="unfinished">Press a button</translation>
</message>
<message>
<source>Move analog stick</source>
<translation type="unfinished">Move analog stick</translation>
</message>
</context>
<context>
<name>EditorDialog</name>
@ -748,6 +776,10 @@
<source>Last updated</source>
<translation></translation>
</message>
<message>
<source>Favorite</source>
<translation type="unfinished">Favorite</translation>
</message>
</context>
<context>
<name>GameListUtils</name>
@ -950,6 +982,14 @@
<source>SFO Viewer for </source>
<translation type="unfinished">SFO Viewer for </translation>
</message>
<message>
<source>Remove from Favorites</source>
<translation type="unfinished">Remove from Favorites</translation>
</message>
<message>
<source>Add to Favorites</source>
<translation type="unfinished">Add to Favorites</translation>
</message>
</context>
<context>
<name>HelpDialog</name>

View File

@ -453,34 +453,10 @@
<source>Use per-game configs</source>
<translation> </translation>
</message>
<message>
<source>L1 / LB</source>
<translation>L1 / LB</translation>
</message>
<message>
<source>L2 / LT</source>
<translation>L2 / LT</translation>
</message>
<message>
<source>Back</source>
<translation></translation>
</message>
<message>
<source>R1 / RB</source>
<translation>R1 / RB</translation>
</message>
<message>
<source>R2 / RT</source>
<translation>R2 / RT</translation>
</message>
<message>
<source>L3</source>
<translation>L3</translation>
</message>
<message>
<source>Options / Start</source>
<translation> / </translation>
</message>
<message>
<source>R3</source>
<translation>R3</translation>
@ -489,22 +465,6 @@
<source>Face Buttons</source>
<translation type="unfinished">Face Buttons</translation>
</message>
<message>
<source>Triangle / Y</source>
<translation type="unfinished">Triangle / Y</translation>
</message>
<message>
<source>Square / X</source>
<translation type="unfinished">Square / X</translation>
</message>
<message>
<source>Circle / B</source>
<translation type="unfinished">Circle / B</translation>
</message>
<message>
<source>Cross / A</source>
<translation type="unfinished">Cross / A</translation>
</message>
<message>
<source>Right Stick Deadzone (def:2, max:127)</source>
<translation type="unfinished">Right Stick Deadzone (def:2, max:127)</translation>
@ -565,6 +525,74 @@
<source>Cancel</source>
<translation type="unfinished">Cancel</translation>
</message>
<message>
<source>unmapped</source>
<translation type="unfinished">unmapped</translation>
</message>
<message>
<source>L1</source>
<translation type="unfinished">L1</translation>
</message>
<message>
<source>R1</source>
<translation type="unfinished">R1</translation>
</message>
<message>
<source>L2</source>
<translation type="unfinished">L2</translation>
</message>
<message>
<source>Options</source>
<translation type="unfinished">Options</translation>
</message>
<message>
<source>R2</source>
<translation type="unfinished">R2</translation>
</message>
<message>
<source>Touchpad Left</source>
<translation type="unfinished">Touchpad Left</translation>
</message>
<message>
<source>Touchpad Center</source>
<translation type="unfinished">Touchpad Center</translation>
</message>
<message>
<source>Touchpad Right</source>
<translation type="unfinished">Touchpad Right</translation>
</message>
<message>
<source>Triangle</source>
<translation type="unfinished">Triangle</translation>
</message>
<message>
<source>Square</source>
<translation type="unfinished">Square</translation>
</message>
<message>
<source>Circle</source>
<translation type="unfinished">Circle</translation>
</message>
<message>
<source>Cross</source>
<translation type="unfinished">Cross</translation>
</message>
<message>
<source>Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons:
%1</source>
<translation type="unfinished">Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons:
%1</translation>
</message>
<message>
<source>Press a button</source>
<translation type="unfinished">Press a button</translation>
</message>
<message>
<source>Move analog stick</source>
<translation type="unfinished">Move analog stick</translation>
</message>
</context>
<context>
<name>EditorDialog</name>
@ -748,6 +776,10 @@
<source>Last updated</source>
<translation> </translation>
</message>
<message>
<source>Favorite</source>
<translation type="unfinished">Favorite</translation>
</message>
</context>
<context>
<name>GameListUtils</name>
@ -950,6 +982,14 @@
<source>SFO Viewer for </source>
<translation type="unfinished">SFO Viewer for </translation>
</message>
<message>
<source>Remove from Favorites</source>
<translation type="unfinished">Remove from Favorites</translation>
</message>
<message>
<source>Add to Favorites</source>
<translation type="unfinished">Add to Favorites</translation>
</message>
</context>
<context>
<name>HelpDialog</name>

View File

@ -453,34 +453,10 @@
<source>Use per-game configs</source>
<translation type="unfinished">Use per-game configs</translation>
</message>
<message>
<source>L1 / LB</source>
<translation type="unfinished">L1 / LB</translation>
</message>
<message>
<source>L2 / LT</source>
<translation type="unfinished">L2 / LT</translation>
</message>
<message>
<source>Back</source>
<translation>Atgal</translation>
</message>
<message>
<source>R1 / RB</source>
<translation type="unfinished">R1 / RB</translation>
</message>
<message>
<source>R2 / RT</source>
<translation type="unfinished">R2 / RT</translation>
</message>
<message>
<source>L3</source>
<translation type="unfinished">L3</translation>
</message>
<message>
<source>Options / Start</source>
<translation type="unfinished">Options / Start</translation>
</message>
<message>
<source>R3</source>
<translation type="unfinished">R3</translation>
@ -489,22 +465,6 @@
<source>Face Buttons</source>
<translation type="unfinished">Face Buttons</translation>
</message>
<message>
<source>Triangle / Y</source>
<translation type="unfinished">Triangle / Y</translation>
</message>
<message>
<source>Square / X</source>
<translation type="unfinished">Square / X</translation>
</message>
<message>
<source>Circle / B</source>
<translation type="unfinished">Circle / B</translation>
</message>
<message>
<source>Cross / A</source>
<translation type="unfinished">Cross / A</translation>
</message>
<message>
<source>Right Stick Deadzone (def:2, max:127)</source>
<translation type="unfinished">Right Stick Deadzone (def:2, max:127)</translation>
@ -565,6 +525,74 @@
<source>Cancel</source>
<translation>Atšaukti</translation>
</message>
<message>
<source>unmapped</source>
<translation type="unfinished">unmapped</translation>
</message>
<message>
<source>L1</source>
<translation type="unfinished">L1</translation>
</message>
<message>
<source>R1</source>
<translation type="unfinished">R1</translation>
</message>
<message>
<source>L2</source>
<translation type="unfinished">L2</translation>
</message>
<message>
<source>Options</source>
<translation type="unfinished">Options</translation>
</message>
<message>
<source>R2</source>
<translation type="unfinished">R2</translation>
</message>
<message>
<source>Touchpad Left</source>
<translation type="unfinished">Touchpad Left</translation>
</message>
<message>
<source>Touchpad Center</source>
<translation type="unfinished">Touchpad Center</translation>
</message>
<message>
<source>Touchpad Right</source>
<translation type="unfinished">Touchpad Right</translation>
</message>
<message>
<source>Triangle</source>
<translation type="unfinished">Triangle</translation>
</message>
<message>
<source>Square</source>
<translation type="unfinished">Square</translation>
</message>
<message>
<source>Circle</source>
<translation type="unfinished">Circle</translation>
</message>
<message>
<source>Cross</source>
<translation type="unfinished">Cross</translation>
</message>
<message>
<source>Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons:
%1</source>
<translation type="unfinished">Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons:
%1</translation>
</message>
<message>
<source>Press a button</source>
<translation type="unfinished">Press a button</translation>
</message>
<message>
<source>Move analog stick</source>
<translation type="unfinished">Move analog stick</translation>
</message>
</context>
<context>
<name>EditorDialog</name>
@ -748,6 +776,10 @@
<source>Last updated</source>
<translation>Paskutinį kartą atnaujinta</translation>
</message>
<message>
<source>Favorite</source>
<translation type="unfinished">Favorite</translation>
</message>
</context>
<context>
<name>GameListUtils</name>
@ -950,6 +982,14 @@
<source>SFO Viewer for </source>
<translation type="unfinished">SFO Viewer for </translation>
</message>
<message>
<source>Remove from Favorites</source>
<translation type="unfinished">Remove from Favorites</translation>
</message>
<message>
<source>Add to Favorites</source>
<translation type="unfinished">Add to Favorites</translation>
</message>
</context>
<context>
<name>HelpDialog</name>

View File

@ -453,34 +453,10 @@
<source>Use per-game configs</source>
<translation>Bruk oppsett per spill</translation>
</message>
<message>
<source>L1 / LB</source>
<translation>L1 / LB</translation>
</message>
<message>
<source>L2 / LT</source>
<translation>L2 / LT</translation>
</message>
<message>
<source>Back</source>
<translation>Tilbake</translation>
</message>
<message>
<source>R1 / RB</source>
<translation>R1 / RB</translation>
</message>
<message>
<source>R2 / RT</source>
<translation>R2 / RT</translation>
</message>
<message>
<source>L3</source>
<translation>L3</translation>
</message>
<message>
<source>Options / Start</source>
<translation>Options / Start</translation>
</message>
<message>
<source>R3</source>
<translation>R3</translation>
@ -489,22 +465,6 @@
<source>Face Buttons</source>
<translation>Handlingsknapper</translation>
</message>
<message>
<source>Triangle / Y</source>
<translation>Triangel / Y</translation>
</message>
<message>
<source>Square / X</source>
<translation>Firkant / X</translation>
</message>
<message>
<source>Circle / B</source>
<translation>Sirkel / B</translation>
</message>
<message>
<source>Cross / A</source>
<translation>Kryss / A</translation>
</message>
<message>
<source>Right Stick Deadzone (def:2, max:127)</source>
<translation>Høyre analog dødsone (def:2, maks:127)</translation>
@ -535,7 +495,7 @@
</message>
<message>
<source>Override Lightbar Color</source>
<translation>Overstyr farge lyslinja</translation>
<translation>Overstyr farge lyslisten</translation>
</message>
<message>
<source>Override Color</source>
@ -565,6 +525,74 @@
<source>Cancel</source>
<translation>Avbryt</translation>
</message>
<message>
<source>unmapped</source>
<translation>Ikke tildelt</translation>
</message>
<message>
<source>L1</source>
<translation>L1</translation>
</message>
<message>
<source>R1</source>
<translation>R1</translation>
</message>
<message>
<source>L2</source>
<translation>L2</translation>
</message>
<message>
<source>Options</source>
<translation>Options</translation>
</message>
<message>
<source>R2</source>
<translation>R2</translation>
</message>
<message>
<source>Touchpad Left</source>
<translation>Venstre berøringsplate</translation>
</message>
<message>
<source>Touchpad Center</source>
<translation>Midt berøringsplate</translation>
</message>
<message>
<source>Touchpad Right</source>
<translation>Høyre berøringsplate</translation>
</message>
<message>
<source>Triangle</source>
<translation>Triangel</translation>
</message>
<message>
<source>Square</source>
<translation>Firkant</translation>
</message>
<message>
<source>Circle</source>
<translation>Sirkel</translation>
</message>
<message>
<source>Cross</source>
<translation>Kryss</translation>
</message>
<message>
<source>Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons:
%1</source>
<translation>Kan ikke tildele samme inndata mer enn én gang. Dupliserte inndata tildeles følgende taster:
%1</translation>
</message>
<message>
<source>Press a button</source>
<translation>Trykk en knapp</translation>
</message>
<message>
<source>Move analog stick</source>
<translation>Flytt analog stikke</translation>
</message>
</context>
<context>
<name>EditorDialog</name>
@ -748,6 +776,10 @@
<source>Last updated</source>
<translation>Sist oppdatert</translation>
</message>
<message>
<source>Favorite</source>
<translation>Favoritter</translation>
</message>
</context>
<context>
<name>GameListUtils</name>
@ -950,6 +982,14 @@
<source>SFO Viewer for </source>
<translation>SFO-viser for </translation>
</message>
<message>
<source>Remove from Favorites</source>
<translation>Fjern fra favoritter</translation>
</message>
<message>
<source>Add to Favorites</source>
<translation>Legg til i favoritter</translation>
</message>
</context>
<context>
<name>HelpDialog</name>
@ -1078,7 +1118,7 @@
</message>
<message>
<source>note: click Help Button/Special Keybindings for more information</source>
<translation>Merk: Trykk hjelpeknappen for mer informasjon</translation>
<translation>Merk: Trykk «Hjelp»-knappen for mer informasjon</translation>
</message>
<message>
<source>Face Buttons</source>
@ -1186,15 +1226,15 @@
</message>
<message>
<source>Touchpad Left</source>
<translation>Berøringsplate venstre</translation>
<translation>Venstre berøringsplate</translation>
</message>
<message>
<source>Touchpad Center</source>
<translation>Berøringsplate midten</translation>
<translation>Midt berøringsplate</translation>
</message>
<message>
<source>Touchpad Right</source>
<translation>Berøringsplate høyre</translation>
<translation>Høyre berøringsplate</translation>
</message>
</context>
<context>

View File

@ -453,34 +453,10 @@
<source>Use per-game configs</source>
<translation type="unfinished">Use per-game configs</translation>
</message>
<message>
<source>L1 / LB</source>
<translation type="unfinished">L1 / LB</translation>
</message>
<message>
<source>L2 / LT</source>
<translation type="unfinished">L2 / LT</translation>
</message>
<message>
<source>Back</source>
<translation type="unfinished">Back</translation>
</message>
<message>
<source>R1 / RB</source>
<translation type="unfinished">R1 / RB</translation>
</message>
<message>
<source>R2 / RT</source>
<translation type="unfinished">R2 / RT</translation>
</message>
<message>
<source>L3</source>
<translation type="unfinished">L3</translation>
</message>
<message>
<source>Options / Start</source>
<translation type="unfinished">Options / Start</translation>
</message>
<message>
<source>R3</source>
<translation type="unfinished">R3</translation>
@ -489,22 +465,6 @@
<source>Face Buttons</source>
<translation type="unfinished">Face Buttons</translation>
</message>
<message>
<source>Triangle / Y</source>
<translation type="unfinished">Triangle / Y</translation>
</message>
<message>
<source>Square / X</source>
<translation type="unfinished">Square / X</translation>
</message>
<message>
<source>Circle / B</source>
<translation type="unfinished">Circle / B</translation>
</message>
<message>
<source>Cross / A</source>
<translation type="unfinished">Cross / A</translation>
</message>
<message>
<source>Right Stick Deadzone (def:2, max:127)</source>
<translation type="unfinished">Right Stick Deadzone (def:2, max:127)</translation>
@ -565,6 +525,74 @@
<source>Cancel</source>
<translation type="unfinished">Cancel</translation>
</message>
<message>
<source>unmapped</source>
<translation type="unfinished">unmapped</translation>
</message>
<message>
<source>L1</source>
<translation type="unfinished">L1</translation>
</message>
<message>
<source>R1</source>
<translation type="unfinished">R1</translation>
</message>
<message>
<source>L2</source>
<translation type="unfinished">L2</translation>
</message>
<message>
<source>Options</source>
<translation type="unfinished">Options</translation>
</message>
<message>
<source>R2</source>
<translation type="unfinished">R2</translation>
</message>
<message>
<source>Touchpad Left</source>
<translation type="unfinished">Touchpad Left</translation>
</message>
<message>
<source>Touchpad Center</source>
<translation type="unfinished">Touchpad Center</translation>
</message>
<message>
<source>Touchpad Right</source>
<translation type="unfinished">Touchpad Right</translation>
</message>
<message>
<source>Triangle</source>
<translation type="unfinished">Triangle</translation>
</message>
<message>
<source>Square</source>
<translation type="unfinished">Square</translation>
</message>
<message>
<source>Circle</source>
<translation type="unfinished">Circle</translation>
</message>
<message>
<source>Cross</source>
<translation type="unfinished">Cross</translation>
</message>
<message>
<source>Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons:
%1</source>
<translation type="unfinished">Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons:
%1</translation>
</message>
<message>
<source>Press a button</source>
<translation type="unfinished">Press a button</translation>
</message>
<message>
<source>Move analog stick</source>
<translation type="unfinished">Move analog stick</translation>
</message>
</context>
<context>
<name>EditorDialog</name>
@ -582,7 +610,7 @@
</message>
<message>
<source>Could not open the file for reading</source>
<translation/>
<translation type="unfinished">Could not open the file for reading</translation>
</message>
<message>
<source>Could not open the file for writing</source>
@ -748,6 +776,10 @@
<source>Last updated</source>
<translation>Laatst bijgewerkt</translation>
</message>
<message>
<source>Favorite</source>
<translation type="unfinished">Favorite</translation>
</message>
</context>
<context>
<name>GameListUtils</name>
@ -950,6 +982,14 @@
<source>SFO Viewer for </source>
<translation type="unfinished">SFO Viewer for </translation>
</message>
<message>
<source>Remove from Favorites</source>
<translation type="unfinished">Remove from Favorites</translation>
</message>
<message>
<source>Add to Favorites</source>
<translation type="unfinished">Add to Favorites</translation>
</message>
</context>
<context>
<name>HelpDialog</name>

View File

@ -453,34 +453,10 @@
<source>Use per-game configs</source>
<translation>Użyj osobnej konfiguracji dla każdej gry</translation>
</message>
<message>
<source>L1 / LB</source>
<translation>L1 / LB</translation>
</message>
<message>
<source>L2 / LT</source>
<translation>L2 / LT</translation>
</message>
<message>
<source>Back</source>
<translation>Wstecz</translation>
</message>
<message>
<source>R1 / RB</source>
<translation>R1 / RB</translation>
</message>
<message>
<source>R2 / RT</source>
<translation>R2 / RT</translation>
</message>
<message>
<source>L3</source>
<translation>L3</translation>
</message>
<message>
<source>Options / Start</source>
<translation>Opcje / Start</translation>
</message>
<message>
<source>R3</source>
<translation>R3</translation>
@ -489,22 +465,6 @@
<source>Face Buttons</source>
<translation>Przyciski akcji</translation>
</message>
<message>
<source>Triangle / Y</source>
<translation>Trójkąt / Y</translation>
</message>
<message>
<source>Square / X</source>
<translation>Kwadrat / X</translation>
</message>
<message>
<source>Circle / B</source>
<translation>Kółko / B</translation>
</message>
<message>
<source>Cross / A</source>
<translation>Krzyżyk / A</translation>
</message>
<message>
<source>Right Stick Deadzone (def:2, max:127)</source>
<translation>Martwa strefa prawego drążka (def:2 max:127)</translation>
@ -565,6 +525,74 @@
<source>Cancel</source>
<translation>Anuluj</translation>
</message>
<message>
<source>unmapped</source>
<translation type="unfinished">unmapped</translation>
</message>
<message>
<source>L1</source>
<translation type="unfinished">L1</translation>
</message>
<message>
<source>R1</source>
<translation type="unfinished">R1</translation>
</message>
<message>
<source>L2</source>
<translation type="unfinished">L2</translation>
</message>
<message>
<source>Options</source>
<translation type="unfinished">Options</translation>
</message>
<message>
<source>R2</source>
<translation type="unfinished">R2</translation>
</message>
<message>
<source>Touchpad Left</source>
<translation type="unfinished">Touchpad Left</translation>
</message>
<message>
<source>Touchpad Center</source>
<translation type="unfinished">Touchpad Center</translation>
</message>
<message>
<source>Touchpad Right</source>
<translation type="unfinished">Touchpad Right</translation>
</message>
<message>
<source>Triangle</source>
<translation type="unfinished">Triangle</translation>
</message>
<message>
<source>Square</source>
<translation type="unfinished">Square</translation>
</message>
<message>
<source>Circle</source>
<translation type="unfinished">Circle</translation>
</message>
<message>
<source>Cross</source>
<translation type="unfinished">Cross</translation>
</message>
<message>
<source>Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons:
%1</source>
<translation type="unfinished">Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons:
%1</translation>
</message>
<message>
<source>Press a button</source>
<translation type="unfinished">Press a button</translation>
</message>
<message>
<source>Move analog stick</source>
<translation type="unfinished">Move analog stick</translation>
</message>
</context>
<context>
<name>EditorDialog</name>
@ -748,6 +776,10 @@
<source>Last updated</source>
<translation>Ostatnia aktualizacja</translation>
</message>
<message>
<source>Favorite</source>
<translation type="unfinished">Favorite</translation>
</message>
</context>
<context>
<name>GameListUtils</name>
@ -950,6 +982,14 @@
<source>SFO Viewer for </source>
<translation>Menedżer plików SFO dla </translation>
</message>
<message>
<source>Remove from Favorites</source>
<translation type="unfinished">Remove from Favorites</translation>
</message>
<message>
<source>Add to Favorites</source>
<translation type="unfinished">Add to Favorites</translation>
</message>
</context>
<context>
<name>HelpDialog</name>

View File

@ -453,34 +453,10 @@
<source>Use per-game configs</source>
<translation>Usar configurações por jogo</translation>
</message>
<message>
<source>L1 / LB</source>
<translation>L1 / LB</translation>
</message>
<message>
<source>L2 / LT</source>
<translation>L2 / LT</translation>
</message>
<message>
<source>Back</source>
<translation>Voltar</translation>
</message>
<message>
<source>R1 / RB</source>
<translation>R1 / RB</translation>
</message>
<message>
<source>R2 / RT</source>
<translation>R2 / RT</translation>
</message>
<message>
<source>L3</source>
<translation>L3</translation>
</message>
<message>
<source>Options / Start</source>
<translation>Options / Start</translation>
</message>
<message>
<source>R3</source>
<translation>R3</translation>
@ -489,22 +465,6 @@
<source>Face Buttons</source>
<translation>Botões de Ação</translation>
</message>
<message>
<source>Triangle / Y</source>
<translation>Triângulo / Y</translation>
</message>
<message>
<source>Square / X</source>
<translation>Quadrado / X</translation>
</message>
<message>
<source>Circle / B</source>
<translation>Círculo / B</translation>
</message>
<message>
<source>Cross / A</source>
<translation>Cruz / A</translation>
</message>
<message>
<source>Right Stick Deadzone (def:2, max:127)</source>
<translation>Zona Morta do Analógico Direito (Pad: 2, Máx: 127)</translation>
@ -565,6 +525,74 @@
<source>Cancel</source>
<translation>Cancelar</translation>
</message>
<message>
<source>unmapped</source>
<translation>não mapeado</translation>
</message>
<message>
<source>L1</source>
<translation>L1</translation>
</message>
<message>
<source>R1</source>
<translation>R1</translation>
</message>
<message>
<source>L2</source>
<translation>L2</translation>
</message>
<message>
<source>Options</source>
<translation>Opções</translation>
</message>
<message>
<source>R2</source>
<translation>R2</translation>
</message>
<message>
<source>Touchpad Left</source>
<translation>Touchpad Esquerdo</translation>
</message>
<message>
<source>Touchpad Center</source>
<translation>Centro do Touchpad</translation>
</message>
<message>
<source>Touchpad Right</source>
<translation>Touchpad Direito</translation>
</message>
<message>
<source>Triangle</source>
<translation>Triângulo</translation>
</message>
<message>
<source>Square</source>
<translation>Quadrado</translation>
</message>
<message>
<source>Circle</source>
<translation>Círculo</translation>
</message>
<message>
<source>Cross</source>
<translation>Cruz</translation>
</message>
<message>
<source>Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons:
%1</source>
<translation>Não é possível atribuir a mesma entrada mais de uma vez. Entradas duplicadas foram atribuídas aos seguintes botões:
%1</translation>
</message>
<message>
<source>Press a button</source>
<translation>Pressione um botão</translation>
</message>
<message>
<source>Move analog stick</source>
<translation>Mover analógico</translation>
</message>
</context>
<context>
<name>EditorDialog</name>
@ -748,6 +776,10 @@
<source>Last updated</source>
<translation>Última atualização</translation>
</message>
<message>
<source>Favorite</source>
<translation>Favorito</translation>
</message>
</context>
<context>
<name>GameListUtils</name>
@ -950,6 +982,14 @@
<source>SFO Viewer for </source>
<translation>Visualizador de SFO para </translation>
</message>
<message>
<source>Remove from Favorites</source>
<translation>Remover dos Favoritos</translation>
</message>
<message>
<source>Add to Favorites</source>
<translation>Adicionar aos Favoritos</translation>
</message>
</context>
<context>
<name>HelpDialog</name>

View File

@ -453,34 +453,10 @@
<source>Use per-game configs</source>
<translation>Utilizar configurações por jogo</translation>
</message>
<message>
<source>L1 / LB</source>
<translation>L1 / LB</translation>
</message>
<message>
<source>L2 / LT</source>
<translation>L2 / LT</translation>
</message>
<message>
<source>Back</source>
<translation>Voltar</translation>
</message>
<message>
<source>R1 / RB</source>
<translation>R1 / RB</translation>
</message>
<message>
<source>R2 / RT</source>
<translation>R2 / RT</translation>
</message>
<message>
<source>L3</source>
<translation>L3</translation>
</message>
<message>
<source>Options / Start</source>
<translation>Opções / Start</translation>
</message>
<message>
<source>R3</source>
<translation>R3</translation>
@ -489,22 +465,6 @@
<source>Face Buttons</source>
<translation>Botões Frontais</translation>
</message>
<message>
<source>Triangle / Y</source>
<translation>Triângulo / Y</translation>
</message>
<message>
<source>Square / X</source>
<translation>Quadrado / X</translation>
</message>
<message>
<source>Circle / B</source>
<translation>Círculo / B</translation>
</message>
<message>
<source>Cross / A</source>
<translation>Cruz / A</translation>
</message>
<message>
<source>Right Stick Deadzone (def:2, max:127)</source>
<translation>Zona Morta do Manípulo Direito (def: 2, max: 127)</translation>
@ -565,6 +525,74 @@
<source>Cancel</source>
<translation>Cancelar</translation>
</message>
<message>
<source>unmapped</source>
<translation type="unfinished">unmapped</translation>
</message>
<message>
<source>L1</source>
<translation type="unfinished">L1</translation>
</message>
<message>
<source>R1</source>
<translation type="unfinished">R1</translation>
</message>
<message>
<source>L2</source>
<translation type="unfinished">L2</translation>
</message>
<message>
<source>Options</source>
<translation type="unfinished">Options</translation>
</message>
<message>
<source>R2</source>
<translation type="unfinished">R2</translation>
</message>
<message>
<source>Touchpad Left</source>
<translation type="unfinished">Touchpad Left</translation>
</message>
<message>
<source>Touchpad Center</source>
<translation type="unfinished">Touchpad Center</translation>
</message>
<message>
<source>Touchpad Right</source>
<translation type="unfinished">Touchpad Right</translation>
</message>
<message>
<source>Triangle</source>
<translation type="unfinished">Triangle</translation>
</message>
<message>
<source>Square</source>
<translation type="unfinished">Square</translation>
</message>
<message>
<source>Circle</source>
<translation type="unfinished">Circle</translation>
</message>
<message>
<source>Cross</source>
<translation type="unfinished">Cross</translation>
</message>
<message>
<source>Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons:
%1</source>
<translation type="unfinished">Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons:
%1</translation>
</message>
<message>
<source>Press a button</source>
<translation type="unfinished">Press a button</translation>
</message>
<message>
<source>Move analog stick</source>
<translation type="unfinished">Move analog stick</translation>
</message>
</context>
<context>
<name>EditorDialog</name>
@ -748,6 +776,10 @@
<source>Last updated</source>
<translation>Última atualização</translation>
</message>
<message>
<source>Favorite</source>
<translation type="unfinished">Favorite</translation>
</message>
</context>
<context>
<name>GameListUtils</name>
@ -950,6 +982,14 @@
<source>SFO Viewer for </source>
<translation>Visualizador SFO para </translation>
</message>
<message>
<source>Remove from Favorites</source>
<translation type="unfinished">Remove from Favorites</translation>
</message>
<message>
<source>Add to Favorites</source>
<translation type="unfinished">Add to Favorites</translation>
</message>
</context>
<context>
<name>HelpDialog</name>

View File

@ -453,34 +453,10 @@
<source>Use per-game configs</source>
<translation type="unfinished">Use per-game configs</translation>
</message>
<message>
<source>L1 / LB</source>
<translation type="unfinished">L1 / LB</translation>
</message>
<message>
<source>L2 / LT</source>
<translation type="unfinished">L2 / LT</translation>
</message>
<message>
<source>Back</source>
<translation type="unfinished">Back</translation>
</message>
<message>
<source>R1 / RB</source>
<translation type="unfinished">R1 / RB</translation>
</message>
<message>
<source>R2 / RT</source>
<translation type="unfinished">R2 / RT</translation>
</message>
<message>
<source>L3</source>
<translation type="unfinished">L3</translation>
</message>
<message>
<source>Options / Start</source>
<translation type="unfinished">Options / Start</translation>
</message>
<message>
<source>R3</source>
<translation type="unfinished">R3</translation>
@ -489,22 +465,6 @@
<source>Face Buttons</source>
<translation type="unfinished">Face Buttons</translation>
</message>
<message>
<source>Triangle / Y</source>
<translation type="unfinished">Triangle / Y</translation>
</message>
<message>
<source>Square / X</source>
<translation type="unfinished">Square / X</translation>
</message>
<message>
<source>Circle / B</source>
<translation type="unfinished">Circle / B</translation>
</message>
<message>
<source>Cross / A</source>
<translation type="unfinished">Cross / A</translation>
</message>
<message>
<source>Right Stick Deadzone (def:2, max:127)</source>
<translation type="unfinished">Right Stick Deadzone (def:2, max:127)</translation>
@ -565,6 +525,74 @@
<source>Cancel</source>
<translation type="unfinished">Cancel</translation>
</message>
<message>
<source>unmapped</source>
<translation type="unfinished">unmapped</translation>
</message>
<message>
<source>L1</source>
<translation type="unfinished">L1</translation>
</message>
<message>
<source>R1</source>
<translation type="unfinished">R1</translation>
</message>
<message>
<source>L2</source>
<translation type="unfinished">L2</translation>
</message>
<message>
<source>Options</source>
<translation type="unfinished">Options</translation>
</message>
<message>
<source>R2</source>
<translation type="unfinished">R2</translation>
</message>
<message>
<source>Touchpad Left</source>
<translation type="unfinished">Touchpad Left</translation>
</message>
<message>
<source>Touchpad Center</source>
<translation type="unfinished">Touchpad Center</translation>
</message>
<message>
<source>Touchpad Right</source>
<translation type="unfinished">Touchpad Right</translation>
</message>
<message>
<source>Triangle</source>
<translation type="unfinished">Triangle</translation>
</message>
<message>
<source>Square</source>
<translation type="unfinished">Square</translation>
</message>
<message>
<source>Circle</source>
<translation type="unfinished">Circle</translation>
</message>
<message>
<source>Cross</source>
<translation type="unfinished">Cross</translation>
</message>
<message>
<source>Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons:
%1</source>
<translation type="unfinished">Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons:
%1</translation>
</message>
<message>
<source>Press a button</source>
<translation type="unfinished">Press a button</translation>
</message>
<message>
<source>Move analog stick</source>
<translation type="unfinished">Move analog stick</translation>
</message>
</context>
<context>
<name>EditorDialog</name>
@ -748,6 +776,10 @@
<source>Last updated</source>
<translation>Ultima actualizare</translation>
</message>
<message>
<source>Favorite</source>
<translation type="unfinished">Favorite</translation>
</message>
</context>
<context>
<name>GameListUtils</name>
@ -950,6 +982,14 @@
<source>SFO Viewer for </source>
<translation type="unfinished">SFO Viewer for </translation>
</message>
<message>
<source>Remove from Favorites</source>
<translation type="unfinished">Remove from Favorites</translation>
</message>
<message>
<source>Add to Favorites</source>
<translation type="unfinished">Add to Favorites</translation>
</message>
</context>
<context>
<name>HelpDialog</name>

View File

@ -453,34 +453,10 @@
<source>Use per-game configs</source>
<translation>Использовать настройки для каждой игры</translation>
</message>
<message>
<source>L1 / LB</source>
<translation>L1 / LB</translation>
</message>
<message>
<source>L2 / LT</source>
<translation>L2 / LT</translation>
</message>
<message>
<source>Back</source>
<translation>Назад</translation>
</message>
<message>
<source>R1 / RB</source>
<translation>R1 / RB</translation>
</message>
<message>
<source>R2 / RT</source>
<translation>R2 / RT</translation>
</message>
<message>
<source>L3</source>
<translation>L3</translation>
</message>
<message>
<source>Options / Start</source>
<translation>Options / Start</translation>
</message>
<message>
<source>R3</source>
<translation>R3</translation>
@ -489,22 +465,6 @@
<source>Face Buttons</source>
<translation>Кнопки действий</translation>
</message>
<message>
<source>Triangle / Y</source>
<translation>Треугольник / Y</translation>
</message>
<message>
<source>Square / X</source>
<translation>Квадрат / X</translation>
</message>
<message>
<source>Circle / B</source>
<translation>Круг / B</translation>
</message>
<message>
<source>Cross / A</source>
<translation>Крест / A</translation>
</message>
<message>
<source>Right Stick Deadzone (def:2, max:127)</source>
<translation>Мёртвая зона правого стика (по умолч:2 макс:127)</translation>
@ -565,6 +525,74 @@
<source>Cancel</source>
<translation>Отмена</translation>
</message>
<message>
<source>unmapped</source>
<translation>не назначено</translation>
</message>
<message>
<source>L1</source>
<translation>L1</translation>
</message>
<message>
<source>R1</source>
<translation>R1</translation>
</message>
<message>
<source>L2</source>
<translation>L2</translation>
</message>
<message>
<source>Options</source>
<translation>Options</translation>
</message>
<message>
<source>R2</source>
<translation>R2</translation>
</message>
<message>
<source>Touchpad Left</source>
<translation>Тачпад слева</translation>
</message>
<message>
<source>Touchpad Center</source>
<translation>Тачпад центр</translation>
</message>
<message>
<source>Touchpad Right</source>
<translation>Тачпад справа</translation>
</message>
<message>
<source>Triangle</source>
<translation>Треугольник</translation>
</message>
<message>
<source>Square</source>
<translation>Квадрат</translation>
</message>
<message>
<source>Circle</source>
<translation>Круг</translation>
</message>
<message>
<source>Cross</source>
<translation>Крест</translation>
</message>
<message>
<source>Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons:
%1</source>
<translation>Невозможно привязать уникальный ввод более одного раза. Дублированные вводы назначены на следующие кнопки:
%1</translation>
</message>
<message>
<source>Press a button</source>
<translation>Нажмите кнопку</translation>
</message>
<message>
<source>Move analog stick</source>
<translation>Двиньте аналоговый стик</translation>
</message>
</context>
<context>
<name>EditorDialog</name>
@ -748,6 +776,10 @@
<source>Last updated</source>
<translation>Последнее обновление</translation>
</message>
<message>
<source>Favorite</source>
<translation>Избранное</translation>
</message>
</context>
<context>
<name>GameListUtils</name>
@ -950,6 +982,14 @@
<source>SFO Viewer for </source>
<translation>Просмотр SFO для</translation>
</message>
<message>
<source>Remove from Favorites</source>
<translation>Удалить из избранного</translation>
</message>
<message>
<source>Add to Favorites</source>
<translation>Добавить в избранное</translation>
</message>
</context>
<context>
<name>HelpDialog</name>

View File

@ -453,34 +453,10 @@
<source>Use per-game configs</source>
<translation type="unfinished">Use per-game configs</translation>
</message>
<message>
<source>L1 / LB</source>
<translation type="unfinished">L1 / LB</translation>
</message>
<message>
<source>L2 / LT</source>
<translation type="unfinished">L2 / LT</translation>
</message>
<message>
<source>Back</source>
<translation type="unfinished">Back</translation>
</message>
<message>
<source>R1 / RB</source>
<translation type="unfinished">R1 / RB</translation>
</message>
<message>
<source>R2 / RT</source>
<translation type="unfinished">R2 / RT</translation>
</message>
<message>
<source>L3</source>
<translation type="unfinished">L3</translation>
</message>
<message>
<source>Options / Start</source>
<translation type="unfinished">Options / Start</translation>
</message>
<message>
<source>R3</source>
<translation type="unfinished">R3</translation>
@ -489,22 +465,6 @@
<source>Face Buttons</source>
<translation type="unfinished">Face Buttons</translation>
</message>
<message>
<source>Triangle / Y</source>
<translation type="unfinished">Triangle / Y</translation>
</message>
<message>
<source>Square / X</source>
<translation type="unfinished">Square / X</translation>
</message>
<message>
<source>Circle / B</source>
<translation type="unfinished">Circle / B</translation>
</message>
<message>
<source>Cross / A</source>
<translation type="unfinished">Cross / A</translation>
</message>
<message>
<source>Right Stick Deadzone (def:2, max:127)</source>
<translation type="unfinished">Right Stick Deadzone (def:2, max:127)</translation>
@ -565,6 +525,74 @@
<source>Cancel</source>
<translation type="unfinished">Cancel</translation>
</message>
<message>
<source>unmapped</source>
<translation type="unfinished">unmapped</translation>
</message>
<message>
<source>L1</source>
<translation type="unfinished">L1</translation>
</message>
<message>
<source>R1</source>
<translation type="unfinished">R1</translation>
</message>
<message>
<source>L2</source>
<translation type="unfinished">L2</translation>
</message>
<message>
<source>Options</source>
<translation type="unfinished">Options</translation>
</message>
<message>
<source>R2</source>
<translation type="unfinished">R2</translation>
</message>
<message>
<source>Touchpad Left</source>
<translation type="unfinished">Touchpad Left</translation>
</message>
<message>
<source>Touchpad Center</source>
<translation type="unfinished">Touchpad Center</translation>
</message>
<message>
<source>Touchpad Right</source>
<translation type="unfinished">Touchpad Right</translation>
</message>
<message>
<source>Triangle</source>
<translation type="unfinished">Triangle</translation>
</message>
<message>
<source>Square</source>
<translation type="unfinished">Square</translation>
</message>
<message>
<source>Circle</source>
<translation type="unfinished">Circle</translation>
</message>
<message>
<source>Cross</source>
<translation type="unfinished">Cross</translation>
</message>
<message>
<source>Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons:
%1</source>
<translation type="unfinished">Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons:
%1</translation>
</message>
<message>
<source>Press a button</source>
<translation type="unfinished">Press a button</translation>
</message>
<message>
<source>Move analog stick</source>
<translation type="unfinished">Move analog stick</translation>
</message>
</context>
<context>
<name>EditorDialog</name>
@ -748,6 +776,10 @@
<source>Last updated</source>
<translation type="unfinished">Last updated</translation>
</message>
<message>
<source>Favorite</source>
<translation type="unfinished">Favorite</translation>
</message>
</context>
<context>
<name>GameListUtils</name>
@ -950,6 +982,14 @@
<source>SFO Viewer for </source>
<translation type="unfinished">SFO Viewer for </translation>
</message>
<message>
<source>Remove from Favorites</source>
<translation type="unfinished">Remove from Favorites</translation>
</message>
<message>
<source>Add to Favorites</source>
<translation type="unfinished">Add to Favorites</translation>
</message>
</context>
<context>
<name>HelpDialog</name>

View File

@ -453,34 +453,10 @@
<source>Use per-game configs</source>
<translation>Përdor konfigurime veçanta për secilën lojë</translation>
</message>
<message>
<source>L1 / LB</source>
<translation>L1 / LB</translation>
</message>
<message>
<source>L2 / LT</source>
<translation>L2 / LT</translation>
</message>
<message>
<source>Back</source>
<translation>Mbrapa</translation>
</message>
<message>
<source>R1 / RB</source>
<translation>R1 / RB</translation>
</message>
<message>
<source>R2 / RT</source>
<translation>R2 / RT</translation>
</message>
<message>
<source>L3</source>
<translation>L3</translation>
</message>
<message>
<source>Options / Start</source>
<translation>Options / Start</translation>
</message>
<message>
<source>R3</source>
<translation>R3</translation>
@ -489,22 +465,6 @@
<source>Face Buttons</source>
<translation>Butonat kryesore</translation>
</message>
<message>
<source>Triangle / Y</source>
<translation>Trekëndësh / Y</translation>
</message>
<message>
<source>Square / X</source>
<translation>Katror / X</translation>
</message>
<message>
<source>Circle / B</source>
<translation>Rreth / B</translation>
</message>
<message>
<source>Cross / A</source>
<translation>Kryq / A</translation>
</message>
<message>
<source>Right Stick Deadzone (def:2, max:127)</source>
<translation>Zona e vdekur e levës djathtë (def:2, max:127)</translation>
@ -565,6 +525,74 @@
<source>Cancel</source>
<translation>Anulo</translation>
</message>
<message>
<source>unmapped</source>
<translation>pacaktuar</translation>
</message>
<message>
<source>L1</source>
<translation>L1</translation>
</message>
<message>
<source>R1</source>
<translation>R1</translation>
</message>
<message>
<source>L2</source>
<translation>L2</translation>
</message>
<message>
<source>Options</source>
<translation>Options</translation>
</message>
<message>
<source>R2</source>
<translation>R2</translation>
</message>
<message>
<source>Touchpad Left</source>
<translation>Paneli me Prekje Majtas</translation>
</message>
<message>
<source>Touchpad Center</source>
<translation>Paneli me Prekje Qendër</translation>
</message>
<message>
<source>Touchpad Right</source>
<translation>Paneli me Prekje Djathtas</translation>
</message>
<message>
<source>Triangle</source>
<translation>Trekëndësh</translation>
</message>
<message>
<source>Square</source>
<translation>Katror</translation>
</message>
<message>
<source>Circle</source>
<translation>Rreth</translation>
</message>
<message>
<source>Cross</source>
<translation>Kryq</translation>
</message>
<message>
<source>Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons:
%1</source>
<translation>Nuk mund caktohet e njëjta hyrje unike shumë se një herë. Hyrjet e dyfishta janë caktuar butonët e mëposhtëm:
%1</translation>
</message>
<message>
<source>Press a button</source>
<translation>Shtyp një buton</translation>
</message>
<message>
<source>Move analog stick</source>
<translation>Lëviz levën</translation>
</message>
</context>
<context>
<name>EditorDialog</name>
@ -748,6 +776,10 @@
<source>Last updated</source>
<translation>Përditësuar për herë fundit</translation>
</message>
<message>
<source>Favorite</source>
<translation> Preferuarat</translation>
</message>
</context>
<context>
<name>GameListUtils</name>
@ -950,6 +982,14 @@
<source>SFO Viewer for </source>
<translation>Shikuesi SFO për </translation>
</message>
<message>
<source>Remove from Favorites</source>
<translation>Hiq nga Preferuarat</translation>
</message>
<message>
<source>Add to Favorites</source>
<translation>Shto Preferuarat</translation>
</message>
</context>
<context>
<name>HelpDialog</name>
@ -1186,15 +1226,15 @@
</message>
<message>
<source>Touchpad Left</source>
<translation type="unfinished">Touchpad Left</translation>
<translation>Paneli me Prekje Majtas</translation>
</message>
<message>
<source>Touchpad Center</source>
<translation type="unfinished">Touchpad Center</translation>
<translation>Paneli me Prekje Qendër</translation>
</message>
<message>
<source>Touchpad Right</source>
<translation type="unfinished">Touchpad Right</translation>
<translation>Paneli me Prekje Djathtas</translation>
</message>
</context>
<context>

View File

@ -453,34 +453,10 @@
<source>Use per-game configs</source>
<translation type="unfinished">Use per-game configs</translation>
</message>
<message>
<source>L1 / LB</source>
<translation type="unfinished">L1 / LB</translation>
</message>
<message>
<source>L2 / LT</source>
<translation type="unfinished">L2 / LT</translation>
</message>
<message>
<source>Back</source>
<translation type="unfinished">Back</translation>
</message>
<message>
<source>R1 / RB</source>
<translation type="unfinished">R1 / RB</translation>
</message>
<message>
<source>R2 / RT</source>
<translation type="unfinished">R2 / RT</translation>
</message>
<message>
<source>L3</source>
<translation type="unfinished">L3</translation>
</message>
<message>
<source>Options / Start</source>
<translation type="unfinished">Options / Start</translation>
</message>
<message>
<source>R3</source>
<translation type="unfinished">R3</translation>
@ -489,22 +465,6 @@
<source>Face Buttons</source>
<translation type="unfinished">Face Buttons</translation>
</message>
<message>
<source>Triangle / Y</source>
<translation type="unfinished">Triangle / Y</translation>
</message>
<message>
<source>Square / X</source>
<translation type="unfinished">Square / X</translation>
</message>
<message>
<source>Circle / B</source>
<translation type="unfinished">Circle / B</translation>
</message>
<message>
<source>Cross / A</source>
<translation type="unfinished">Cross / A</translation>
</message>
<message>
<source>Right Stick Deadzone (def:2, max:127)</source>
<translation type="unfinished">Right Stick Deadzone (def:2, max:127)</translation>
@ -565,6 +525,74 @@
<source>Cancel</source>
<translation type="unfinished">Cancel</translation>
</message>
<message>
<source>unmapped</source>
<translation type="unfinished">unmapped</translation>
</message>
<message>
<source>L1</source>
<translation type="unfinished">L1</translation>
</message>
<message>
<source>R1</source>
<translation type="unfinished">R1</translation>
</message>
<message>
<source>L2</source>
<translation type="unfinished">L2</translation>
</message>
<message>
<source>Options</source>
<translation type="unfinished">Options</translation>
</message>
<message>
<source>R2</source>
<translation type="unfinished">R2</translation>
</message>
<message>
<source>Touchpad Left</source>
<translation type="unfinished">Touchpad Left</translation>
</message>
<message>
<source>Touchpad Center</source>
<translation type="unfinished">Touchpad Center</translation>
</message>
<message>
<source>Touchpad Right</source>
<translation type="unfinished">Touchpad Right</translation>
</message>
<message>
<source>Triangle</source>
<translation type="unfinished">Triangle</translation>
</message>
<message>
<source>Square</source>
<translation type="unfinished">Square</translation>
</message>
<message>
<source>Circle</source>
<translation type="unfinished">Circle</translation>
</message>
<message>
<source>Cross</source>
<translation type="unfinished">Cross</translation>
</message>
<message>
<source>Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons:
%1</source>
<translation type="unfinished">Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons:
%1</translation>
</message>
<message>
<source>Press a button</source>
<translation type="unfinished">Press a button</translation>
</message>
<message>
<source>Move analog stick</source>
<translation type="unfinished">Move analog stick</translation>
</message>
</context>
<context>
<name>EditorDialog</name>
@ -748,6 +776,10 @@
<source>Last updated</source>
<translation type="unfinished">Last updated</translation>
</message>
<message>
<source>Favorite</source>
<translation type="unfinished">Favorite</translation>
</message>
</context>
<context>
<name>GameListUtils</name>
@ -950,6 +982,14 @@
<source>SFO Viewer for </source>
<translation type="unfinished">SFO Viewer for </translation>
</message>
<message>
<source>Remove from Favorites</source>
<translation type="unfinished">Remove from Favorites</translation>
</message>
<message>
<source>Add to Favorites</source>
<translation type="unfinished">Add to Favorites</translation>
</message>
</context>
<context>
<name>HelpDialog</name>

View File

@ -453,34 +453,10 @@
<source>Use per-game configs</source>
<translation>Använd konfigurationer per spel</translation>
</message>
<message>
<source>L1 / LB</source>
<translation>L1 / LB</translation>
</message>
<message>
<source>L2 / LT</source>
<translation>L2 / LT</translation>
</message>
<message>
<source>Back</source>
<translation>Bakåt</translation>
</message>
<message>
<source>R1 / RB</source>
<translation>R1 / RB</translation>
</message>
<message>
<source>R2 / RT</source>
<translation>R2 / RT</translation>
</message>
<message>
<source>L3</source>
<translation>L3</translation>
</message>
<message>
<source>Options / Start</source>
<translation>Options / Start</translation>
</message>
<message>
<source>R3</source>
<translation>R3</translation>
@ -489,22 +465,6 @@
<source>Face Buttons</source>
<translation>Handlingsknappar</translation>
</message>
<message>
<source>Triangle / Y</source>
<translation>Triangel / Y</translation>
</message>
<message>
<source>Square / X</source>
<translation>Fyrkant / X</translation>
</message>
<message>
<source>Circle / B</source>
<translation>Cirkel / B</translation>
</message>
<message>
<source>Cross / A</source>
<translation>Kryss / A</translation>
</message>
<message>
<source>Right Stick Deadzone (def:2, max:127)</source>
<translation>Dödläge för höger spak (standard:2, max:127)</translation>
@ -565,6 +525,74 @@
<source>Cancel</source>
<translation>Avbryt</translation>
</message>
<message>
<source>unmapped</source>
<translation>omappad</translation>
</message>
<message>
<source>L1</source>
<translation>L1</translation>
</message>
<message>
<source>R1</source>
<translation>R1</translation>
</message>
<message>
<source>L2</source>
<translation>L2</translation>
</message>
<message>
<source>Options</source>
<translation>Options</translation>
</message>
<message>
<source>R2</source>
<translation>R2</translation>
</message>
<message>
<source>Touchpad Left</source>
<translation>Pekplatta vänster</translation>
</message>
<message>
<source>Touchpad Center</source>
<translation>Pekplatta i mitten</translation>
</message>
<message>
<source>Touchpad Right</source>
<translation>Pekplatta höger</translation>
</message>
<message>
<source>Triangle</source>
<translation>Triangel</translation>
</message>
<message>
<source>Square</source>
<translation>Fyrkant</translation>
</message>
<message>
<source>Circle</source>
<translation>Cirkel</translation>
</message>
<message>
<source>Cross</source>
<translation>Kors</translation>
</message>
<message>
<source>Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons:
%1</source>
<translation>Det går inte att binda samma unika inmatning mer än en gång. Dubbletta inmatningar är mappade till följande knappar:
%1</translation>
</message>
<message>
<source>Press a button</source>
<translation>Tryck en knapp</translation>
</message>
<message>
<source>Move analog stick</source>
<translation>Rör analog spak</translation>
</message>
</context>
<context>
<name>EditorDialog</name>
@ -748,6 +776,10 @@
<source>Last updated</source>
<translation>Senast uppdaterad</translation>
</message>
<message>
<source>Favorite</source>
<translation>Favorit</translation>
</message>
</context>
<context>
<name>GameListUtils</name>
@ -950,6 +982,14 @@
<source>SFO Viewer for </source>
<translation>SFO-visare för </translation>
</message>
<message>
<source>Remove from Favorites</source>
<translation>Ta bort från favoriter</translation>
</message>
<message>
<source>Add to Favorites</source>
<translation>Lägg till i favoriter</translation>
</message>
</context>
<context>
<name>HelpDialog</name>

View File

@ -453,34 +453,10 @@
<source>Use per-game configs</source>
<translation>Oyuna özel yapılandırma kullan</translation>
</message>
<message>
<source>L1 / LB</source>
<translation>L1 / LB</translation>
</message>
<message>
<source>L2 / LT</source>
<translation>L2 / LT</translation>
</message>
<message>
<source>Back</source>
<translation>Geri</translation>
</message>
<message>
<source>R1 / RB</source>
<translation>R1 / RB</translation>
</message>
<message>
<source>R2 / RT</source>
<translation>R2 / RT</translation>
</message>
<message>
<source>L3</source>
<translation>L3</translation>
</message>
<message>
<source>Options / Start</source>
<translation>Seçenekler / Başlat</translation>
</message>
<message>
<source>R3</source>
<translation>R3</translation>
@ -489,22 +465,6 @@
<source>Face Buttons</source>
<translation>Eylem Düğmeleri</translation>
</message>
<message>
<source>Triangle / Y</source>
<translation>Üçgen / Y</translation>
</message>
<message>
<source>Square / X</source>
<translation>Kare / X</translation>
</message>
<message>
<source>Circle / B</source>
<translation>Daire / B</translation>
</message>
<message>
<source>Cross / A</source>
<translation>Çarpı / A</translation>
</message>
<message>
<source>Right Stick Deadzone (def:2, max:127)</source>
<translation>Sağ Analog Ö Bölgesi (varsayılan: 2, en çok: 127)</translation>
@ -565,6 +525,74 @@
<source>Cancel</source>
<translation>İptal</translation>
</message>
<message>
<source>unmapped</source>
<translation>atanmamış</translation>
</message>
<message>
<source>L1</source>
<translation type="unfinished">L1</translation>
</message>
<message>
<source>R1</source>
<translation type="unfinished">R1</translation>
</message>
<message>
<source>L2</source>
<translation type="unfinished">L2</translation>
</message>
<message>
<source>Options</source>
<translation>Seçenekler</translation>
</message>
<message>
<source>R2</source>
<translation type="unfinished">R2</translation>
</message>
<message>
<source>Touchpad Left</source>
<translation type="unfinished">Touchpad Left</translation>
</message>
<message>
<source>Touchpad Center</source>
<translation type="unfinished">Touchpad Center</translation>
</message>
<message>
<source>Touchpad Right</source>
<translation type="unfinished">Touchpad Right</translation>
</message>
<message>
<source>Triangle</source>
<translation>Üçgen</translation>
</message>
<message>
<source>Square</source>
<translation>Kare</translation>
</message>
<message>
<source>Circle</source>
<translation>Yuvarlak</translation>
</message>
<message>
<source>Cross</source>
<translation>Çarpı</translation>
</message>
<message>
<source>Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons:
%1</source>
<translation>Aynı tuş birden fazla kez atanamaz. Aşağıdaki tuşlara birden fazla giriş atanmış:
%1</translation>
</message>
<message>
<source>Press a button</source>
<translation>Bir Düğmeye Bas</translation>
</message>
<message>
<source>Move analog stick</source>
<translation type="unfinished">Move analog stick</translation>
</message>
</context>
<context>
<name>EditorDialog</name>
@ -748,6 +776,10 @@
<source>Last updated</source>
<translation>Son güncelleme</translation>
</message>
<message>
<source>Favorite</source>
<translation>Sık Kullanılan</translation>
</message>
</context>
<context>
<name>GameListUtils</name>
@ -950,6 +982,14 @@
<source>SFO Viewer for </source>
<translation>SFO Görüntüleyici: </translation>
</message>
<message>
<source>Remove from Favorites</source>
<translation type="unfinished">Remove from Favorites</translation>
</message>
<message>
<source>Add to Favorites</source>
<translation>Favorilere Ekle</translation>
</message>
</context>
<context>
<name>HelpDialog</name>
@ -1180,7 +1220,7 @@
<source>Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons:
%1</source>
<translation type="unfinished">Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons:
<translation>Aynı tuş birden fazla kez atanamaz. Aşağıdaki tuşlara birden fazla giriş atanmış:
%1</translation>
</message>

View File

@ -453,34 +453,10 @@
<source>Use per-game configs</source>
<translation>Використовувати ігрові конфігурації</translation>
</message>
<message>
<source>L1 / LB</source>
<translation>L1 / Лівий Бампер</translation>
</message>
<message>
<source>L2 / LT</source>
<translation>L2 / Лівий Тригер</translation>
</message>
<message>
<source>Back</source>
<translation>Назад</translation>
</message>
<message>
<source>R1 / RB</source>
<translation>R1 / Правий Бампер</translation>
</message>
<message>
<source>R2 / RT</source>
<translation>R2 / Правий Тригер</translation>
</message>
<message>
<source>L3</source>
<translation>Кнопка лівого стику</translation>
</message>
<message>
<source>Options / Start</source>
<translation>Опції / Старт</translation>
</message>
<message>
<source>R3</source>
<translation>Кнопка правого стику</translation>
@ -489,22 +465,6 @@
<source>Face Buttons</source>
<translation>Лицьові кнопки</translation>
</message>
<message>
<source>Triangle / Y</source>
<translation>Трикутник / Y</translation>
</message>
<message>
<source>Square / X</source>
<translation>Квадрат / X</translation>
</message>
<message>
<source>Circle / B</source>
<translation>Коло / B</translation>
</message>
<message>
<source>Cross / A</source>
<translation>Хрест / A</translation>
</message>
<message>
<source>Right Stick Deadzone (def:2, max:127)</source>
<translation>Мертва зона правого стику (за замов.: 2, максимум: 127)</translation>
@ -565,6 +525,74 @@
<source>Cancel</source>
<translation>Відмінити</translation>
</message>
<message>
<source>unmapped</source>
<translation type="unfinished">unmapped</translation>
</message>
<message>
<source>L1</source>
<translation type="unfinished">L1</translation>
</message>
<message>
<source>R1</source>
<translation type="unfinished">R1</translation>
</message>
<message>
<source>L2</source>
<translation type="unfinished">L2</translation>
</message>
<message>
<source>Options</source>
<translation type="unfinished">Options</translation>
</message>
<message>
<source>R2</source>
<translation type="unfinished">R2</translation>
</message>
<message>
<source>Touchpad Left</source>
<translation type="unfinished">Touchpad Left</translation>
</message>
<message>
<source>Touchpad Center</source>
<translation type="unfinished">Touchpad Center</translation>
</message>
<message>
<source>Touchpad Right</source>
<translation type="unfinished">Touchpad Right</translation>
</message>
<message>
<source>Triangle</source>
<translation type="unfinished">Triangle</translation>
</message>
<message>
<source>Square</source>
<translation type="unfinished">Square</translation>
</message>
<message>
<source>Circle</source>
<translation type="unfinished">Circle</translation>
</message>
<message>
<source>Cross</source>
<translation type="unfinished">Cross</translation>
</message>
<message>
<source>Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons:
%1</source>
<translation type="unfinished">Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons:
%1</translation>
</message>
<message>
<source>Press a button</source>
<translation type="unfinished">Press a button</translation>
</message>
<message>
<source>Move analog stick</source>
<translation type="unfinished">Move analog stick</translation>
</message>
</context>
<context>
<name>EditorDialog</name>
@ -748,6 +776,10 @@
<source>Last updated</source>
<translation>Останнє оновлення</translation>
</message>
<message>
<source>Favorite</source>
<translation type="unfinished">Favorite</translation>
</message>
</context>
<context>
<name>GameListUtils</name>
@ -950,6 +982,14 @@
<source>SFO Viewer for </source>
<translation>Перегляд SFO </translation>
</message>
<message>
<source>Remove from Favorites</source>
<translation type="unfinished">Remove from Favorites</translation>
</message>
<message>
<source>Add to Favorites</source>
<translation type="unfinished">Add to Favorites</translation>
</message>
</context>
<context>
<name>HelpDialog</name>

View File

@ -453,34 +453,10 @@
<source>Use per-game configs</source>
<translation>Cấu hình riêng cho từng game</translation>
</message>
<message>
<source>L1 / LB</source>
<translation>L1 / LB</translation>
</message>
<message>
<source>L2 / LT</source>
<translation>L2 / LT</translation>
</message>
<message>
<source>Back</source>
<translation>Quay Lại</translation>
</message>
<message>
<source>R1 / RB</source>
<translation>R1 / RB</translation>
</message>
<message>
<source>R2 / RT</source>
<translation>R2 / RT</translation>
</message>
<message>
<source>L3</source>
<translation>L3</translation>
</message>
<message>
<source>Options / Start</source>
<translation>Tuỳ chọn / Bắt đu</translation>
</message>
<message>
<source>R3</source>
<translation>R3</translation>
@ -489,22 +465,6 @@
<source>Face Buttons</source>
<translation>Nút bấm mặt trước</translation>
</message>
<message>
<source>Triangle / Y</source>
<translation>Tam giác / Y</translation>
</message>
<message>
<source>Square / X</source>
<translation>Vuông / X</translation>
</message>
<message>
<source>Circle / B</source>
<translation>Tròn / B</translation>
</message>
<message>
<source>Cross / A</source>
<translation>Chéo / A</translation>
</message>
<message>
<source>Right Stick Deadzone (def:2, max:127)</source>
<translation type="unfinished">Right Stick Deadzone (def:2, max:127)</translation>
@ -565,6 +525,74 @@
<source>Cancel</source>
<translation>Hủy</translation>
</message>
<message>
<source>unmapped</source>
<translation type="unfinished">unmapped</translation>
</message>
<message>
<source>L1</source>
<translation type="unfinished">L1</translation>
</message>
<message>
<source>R1</source>
<translation type="unfinished">R1</translation>
</message>
<message>
<source>L2</source>
<translation type="unfinished">L2</translation>
</message>
<message>
<source>Options</source>
<translation type="unfinished">Options</translation>
</message>
<message>
<source>R2</source>
<translation type="unfinished">R2</translation>
</message>
<message>
<source>Touchpad Left</source>
<translation type="unfinished">Touchpad Left</translation>
</message>
<message>
<source>Touchpad Center</source>
<translation type="unfinished">Touchpad Center</translation>
</message>
<message>
<source>Touchpad Right</source>
<translation type="unfinished">Touchpad Right</translation>
</message>
<message>
<source>Triangle</source>
<translation type="unfinished">Triangle</translation>
</message>
<message>
<source>Square</source>
<translation type="unfinished">Square</translation>
</message>
<message>
<source>Circle</source>
<translation type="unfinished">Circle</translation>
</message>
<message>
<source>Cross</source>
<translation type="unfinished">Cross</translation>
</message>
<message>
<source>Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons:
%1</source>
<translation type="unfinished">Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons:
%1</translation>
</message>
<message>
<source>Press a button</source>
<translation type="unfinished">Press a button</translation>
</message>
<message>
<source>Move analog stick</source>
<translation type="unfinished">Move analog stick</translation>
</message>
</context>
<context>
<name>EditorDialog</name>
@ -748,6 +776,10 @@
<source>Last updated</source>
<translation>Cập nhật lần cuối</translation>
</message>
<message>
<source>Favorite</source>
<translation type="unfinished">Favorite</translation>
</message>
</context>
<context>
<name>GameListUtils</name>
@ -924,7 +956,7 @@
</message>
<message>
<source>No log file found for this game!</source>
<translation>!</translation>
<translation/>
</message>
<message>
<source>Failed to convert icon.</source>
@ -950,6 +982,14 @@
<source>SFO Viewer for </source>
<translation type="unfinished">SFO Viewer for </translation>
</message>
<message>
<source>Remove from Favorites</source>
<translation type="unfinished">Remove from Favorites</translation>
</message>
<message>
<source>Add to Favorites</source>
<translation type="unfinished">Add to Favorites</translation>
</message>
</context>
<context>
<name>HelpDialog</name>

View File

@ -453,34 +453,10 @@
<source>Use per-game configs</source>
<translation>使</translation>
</message>
<message>
<source>L1 / LB</source>
<translation>L1 / LB</translation>
</message>
<message>
<source>L2 / LT</source>
<translation>L2 / LT</translation>
</message>
<message>
<source>Back</source>
<translation>Back</translation>
</message>
<message>
<source>R1 / RB</source>
<translation>R1 / RB</translation>
</message>
<message>
<source>R2 / RT</source>
<translation>R2 / RT</translation>
</message>
<message>
<source>L3</source>
<translation>L3</translation>
</message>
<message>
<source>Options / Start</source>
<translation>Options / Start</translation>
</message>
<message>
<source>R3</source>
<translation>R3</translation>
@ -489,22 +465,6 @@
<source>Face Buttons</source>
<translation></translation>
</message>
<message>
<source>Triangle / Y</source>
<translation> / Y</translation>
</message>
<message>
<source>Square / X</source>
<translation> / X</translation>
</message>
<message>
<source>Circle / B</source>
<translation> / B</translation>
</message>
<message>
<source>Cross / A</source>
<translation> / A</translation>
</message>
<message>
<source>Right Stick Deadzone (def:2, max:127)</source>
<translation>2 127</translation>
@ -565,6 +525,74 @@
<source>Cancel</source>
<translation></translation>
</message>
<message>
<source>unmapped</source>
<translation></translation>
</message>
<message>
<source>L1</source>
<translation>L1</translation>
</message>
<message>
<source>R1</source>
<translation>R1</translation>
</message>
<message>
<source>L2</source>
<translation>L2</translation>
</message>
<message>
<source>Options</source>
<translation></translation>
</message>
<message>
<source>R2</source>
<translation>R2</translation>
</message>
<message>
<source>Touchpad Left</source>
<translation></translation>
</message>
<message>
<source>Touchpad Center</source>
<translation></translation>
</message>
<message>
<source>Touchpad Right</source>
<translation></translation>
</message>
<message>
<source>Triangle</source>
<translation></translation>
</message>
<message>
<source>Square</source>
<translation></translation>
</message>
<message>
<source>Circle</source>
<translation></translation>
</message>
<message>
<source>Cross</source>
<translation></translation>
</message>
<message>
<source>Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons:
%1</source>
<translation>
%1</translation>
</message>
<message>
<source>Press a button</source>
<translation></translation>
</message>
<message>
<source>Move analog stick</source>
<translation></translation>
</message>
</context>
<context>
<name>EditorDialog</name>
@ -748,6 +776,10 @@
<source>Last updated</source>
<translation></translation>
</message>
<message>
<source>Favorite</source>
<translation></translation>
</message>
</context>
<context>
<name>GameListUtils</name>
@ -950,6 +982,14 @@
<source>SFO Viewer for </source>
<translation>SFO - </translation>
</message>
<message>
<source>Remove from Favorites</source>
<translation></translation>
</message>
<message>
<source>Add to Favorites</source>
<translation></translation>
</message>
</context>
<context>
<name>HelpDialog</name>

View File

@ -453,34 +453,10 @@
<source>Use per-game configs</source>
<translation>使</translation>
</message>
<message>
<source>L1 / LB</source>
<translation>L1 / LB</translation>
</message>
<message>
<source>L2 / LT</source>
<translation>L2 / LT</translation>
</message>
<message>
<source>Back</source>
<translation>Back</translation>
</message>
<message>
<source>R1 / RB</source>
<translation>R1 / RB</translation>
</message>
<message>
<source>R2 / RT</source>
<translation>R2 / RT</translation>
</message>
<message>
<source>L3</source>
<translation>L3</translation>
</message>
<message>
<source>Options / Start</source>
<translation>Options / Start</translation>
</message>
<message>
<source>R3</source>
<translation>R3</translation>
@ -489,22 +465,6 @@
<source>Face Buttons</source>
<translation></translation>
</message>
<message>
<source>Triangle / Y</source>
<translation> / Y</translation>
</message>
<message>
<source>Square / X</source>
<translation> / X</translation>
</message>
<message>
<source>Circle / B</source>
<translation> / B</translation>
</message>
<message>
<source>Cross / A</source>
<translation> / A</translation>
</message>
<message>
<source>Right Stick Deadzone (def:2, max:127)</source>
<translation>2 127</translation>
@ -565,6 +525,74 @@
<source>Cancel</source>
<translation></translation>
</message>
<message>
<source>unmapped</source>
<translation type="unfinished">unmapped</translation>
</message>
<message>
<source>L1</source>
<translation type="unfinished">L1</translation>
</message>
<message>
<source>R1</source>
<translation type="unfinished">R1</translation>
</message>
<message>
<source>L2</source>
<translation type="unfinished">L2</translation>
</message>
<message>
<source>Options</source>
<translation type="unfinished">Options</translation>
</message>
<message>
<source>R2</source>
<translation type="unfinished">R2</translation>
</message>
<message>
<source>Touchpad Left</source>
<translation type="unfinished">Touchpad Left</translation>
</message>
<message>
<source>Touchpad Center</source>
<translation type="unfinished">Touchpad Center</translation>
</message>
<message>
<source>Touchpad Right</source>
<translation type="unfinished">Touchpad Right</translation>
</message>
<message>
<source>Triangle</source>
<translation type="unfinished">Triangle</translation>
</message>
<message>
<source>Square</source>
<translation type="unfinished">Square</translation>
</message>
<message>
<source>Circle</source>
<translation type="unfinished">Circle</translation>
</message>
<message>
<source>Cross</source>
<translation type="unfinished">Cross</translation>
</message>
<message>
<source>Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons:
%1</source>
<translation type="unfinished">Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons:
%1</translation>
</message>
<message>
<source>Press a button</source>
<translation type="unfinished">Press a button</translation>
</message>
<message>
<source>Move analog stick</source>
<translation type="unfinished">Move analog stick</translation>
</message>
</context>
<context>
<name>EditorDialog</name>
@ -748,6 +776,10 @@
<source>Last updated</source>
<translation></translation>
</message>
<message>
<source>Favorite</source>
<translation type="unfinished">Favorite</translation>
</message>
</context>
<context>
<name>GameListUtils</name>
@ -950,6 +982,14 @@
<source>SFO Viewer for </source>
<translation>SFO </translation>
</message>
<message>
<source>Remove from Favorites</source>
<translation type="unfinished">Remove from Favorites</translation>
</message>
<message>
<source>Add to Favorites</source>
<translation type="unfinished">Add to Favorites</translation>
</message>
</context>
<context>
<name>HelpDialog</name>

View File

@ -20,6 +20,10 @@
#include "sdl_window.h"
#include "video_core/renderdoc.h"
#ifdef ENABLE_QT_GUI
#include "qt_gui/sdl_event_wrapper.h"
#endif
#ifdef __APPLE__
#include "SDL3/SDL_metal.h"
#endif
@ -340,6 +344,13 @@ void WindowSDL::WaitEvent() {
return;
}
#ifdef ENABLE_QT_GUI
if (SdlEventWrapper::Wrapper::wrapperActive) {
if (SdlEventWrapper::Wrapper::GetInstance()->ProcessEvent(&event))
return;
}
#endif
if (ImGui::Core::ProcessEvent(&event)) {
return;
}

View File

@ -300,7 +300,7 @@ void SetupCapabilities(const Info& info, const Profile& profile, EmitContext& ct
if (stage == LogicalStage::TessellationControl || stage == LogicalStage::TessellationEval) {
ctx.AddCapability(spv::Capability::Tessellation);
}
if (info.dma_types != IR::Type::Void) {
if (info.uses_dma) {
ctx.AddCapability(spv::Capability::PhysicalStorageBufferAddresses);
ctx.AddExtension("SPV_KHR_physical_storage_buffer");
}
@ -310,6 +310,19 @@ void SetupCapabilities(const Info& info, const Profile& profile, EmitContext& ct
ctx.AddCapability(spv::Capability::WorkgroupMemoryExplicitLayoutKHR);
ctx.AddCapability(spv::Capability::WorkgroupMemoryExplicitLayout16BitAccessKHR);
}
if (info.uses_buffer_int64_atomics || info.uses_shared_int64_atomics) {
if (info.uses_buffer_int64_atomics) {
ASSERT_MSG(ctx.profile.supports_buffer_int64_atomics,
"Shader requires support for atomic Int64 buffer operations that your "
"Vulkan instance does not advertise");
}
if (info.uses_shared_int64_atomics) {
ASSERT_MSG(ctx.profile.supports_shared_int64_atomics,
"Shader requires support for atomic Int64 shared memory operations that "
"your Vulkan instance does not advertise");
}
ctx.AddCapability(spv::Capability::Int64Atomics);
}
}
void DefineEntryPoint(const Info& info, EmitContext& ctx, Id main) {

View File

@ -7,7 +7,11 @@
#include "shader_recompiler/backend/spirv/spirv_emit_context.h"
namespace Shader::Backend::SPIRV {
namespace {
using PointerType = EmitContext::PointerType;
using PointerSize = EmitContext::PointerSize;
std::pair<Id, Id> AtomicArgs(EmitContext& ctx) {
const Id scope{ctx.ConstU32(static_cast<u32>(spv::Scope::Device))};
const Id semantics{ctx.u32_zero_value};
@ -50,25 +54,30 @@ Id SharedAtomicU64(EmitContext& ctx, Id offset, Id value,
});
}
Id SharedAtomicU64IncDec(EmitContext& ctx, Id offset,
Id (Sirit::Module::*atomic_func)(Id, Id, Id, Id)) {
const Id shift_id{ctx.ConstU32(3U)};
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, 8u)};
const Id pointer{ctx.EmitSharedMemoryAccess(ctx.shared_u64, ctx.shared_memory_u64, index)};
const auto [scope, semantics]{AtomicArgs(ctx)};
return AccessBoundsCheck<64>(ctx, index, ctx.ConstU32(num_elements), [&] {
return (ctx.*atomic_func)(ctx.U64, pointer, scope, semantics);
});
}
template <bool is_float = false>
Id BufferAtomicU32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value,
Id (Sirit::Module::*atomic_func)(Id, Id, Id, Id, Id)) {
const auto& buffer = ctx.buffers[handle];
const auto type = [&] {
if constexpr (is_float) {
return ctx.F32[1];
} else {
return ctx.U32[1];
}
}();
if (Sirit::ValidId(buffer.offset)) {
address = ctx.OpIAdd(ctx.U32[1], address, buffer.offset);
const Id type = is_float ? ctx.F32[1] : ctx.U32[1];
if (const Id offset = buffer.Offset(PointerSize::B32); Sirit::ValidId(offset)) {
address = ctx.OpIAdd(ctx.U32[1], address, 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 [id, pointer_type] = buffer.Alias(PointerType::U32);
const Id ptr = ctx.OpAccessChain(pointer_type, id, ctx.u32_zero_value, address);
const auto [scope, semantics]{AtomicArgs(ctx)};
return AccessBoundsCheck<32, 1, is_float>(ctx, index, buffer.size_dwords, [&] {
return AccessBoundsCheck<32, 1, is_float>(ctx, address, buffer.Size(PointerSize::B32), [&] {
return (ctx.*atomic_func)(type, ptr, scope, semantics, value);
});
}
@ -76,14 +85,13 @@ 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);
if (const Id offset = buffer.Offset(PointerSize::B32); Sirit::ValidId(offset)) {
address = ctx.OpIAdd(ctx.U32[1], address, 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 [id, pointer_type] = buffer.Alias(PointerType::U32);
const Id ptr = ctx.OpAccessChain(pointer_type, id, ctx.u32_zero_value, address);
const auto [scope, semantics]{AtomicArgs(ctx)};
return AccessBoundsCheck<32>(ctx, index, buffer.size_dwords, [&] {
return AccessBoundsCheck<32>(ctx, address, buffer.Size(PointerSize::B32), [&] {
return (ctx.*atomic_func)(ctx.U32[1], ptr, scope, semantics);
});
}
@ -92,14 +100,13 @@ Id BufferAtomicU32CmpSwap(EmitContext& ctx, IR::Inst* inst, u32 handle, Id addre
Id cmp_value,
Id (Sirit::Module::*atomic_func)(Id, Id, Id, Id, Id, Id, Id)) {
const auto& buffer = ctx.buffers[handle];
if (Sirit::ValidId(buffer.offset)) {
address = ctx.OpIAdd(ctx.U32[1], address, buffer.offset);
if (const Id offset = buffer.Offset(PointerSize::B32); Sirit::ValidId(offset)) {
address = ctx.OpIAdd(ctx.U32[1], address, 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 [id, pointer_type] = buffer.Alias(PointerType::U32);
const Id ptr = ctx.OpAccessChain(pointer_type, id, ctx.u32_zero_value, address);
const auto [scope, semantics]{AtomicArgs(ctx)};
return AccessBoundsCheck<32>(ctx, index, buffer.size_dwords, [&] {
return AccessBoundsCheck<32>(ctx, address, buffer.Size(PointerSize::B32), [&] {
return (ctx.*atomic_func)(ctx.U32[1], ptr, scope, semantics, semantics, value, cmp_value);
});
}
@ -107,14 +114,13 @@ Id BufferAtomicU32CmpSwap(EmitContext& ctx, IR::Inst* inst, u32 handle, Id addre
Id BufferAtomicU64(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value,
Id (Sirit::Module::*atomic_func)(Id, Id, Id, Id, Id)) {
const auto& buffer = ctx.buffers[handle];
if (Sirit::ValidId(buffer.offset)) {
address = ctx.OpIAdd(ctx.U32[1], address, buffer.offset);
if (const Id offset = buffer.Offset(PointerSize::B64); Sirit::ValidId(offset)) {
address = ctx.OpIAdd(ctx.U32[1], address, offset);
}
const Id index = ctx.OpShiftRightLogical(ctx.U32[1], address, ctx.ConstU32(3u));
const auto [id, pointer_type] = buffer[EmitContext::PointerType::U64];
const Id ptr = ctx.OpAccessChain(pointer_type, id, ctx.u32_zero_value, index);
const auto [id, pointer_type] = buffer.Alias(PointerType::U64);
const Id ptr = ctx.OpAccessChain(pointer_type, id, ctx.u32_zero_value, address);
const auto [scope, semantics]{AtomicArgs(ctx)};
return AccessBoundsCheck<64>(ctx, index, buffer.size_qwords, [&] {
return AccessBoundsCheck<64>(ctx, address, buffer.Size(PointerSize::B64), [&] {
return (ctx.*atomic_func)(ctx.U64, ptr, scope, semantics, value);
});
}
@ -148,42 +154,82 @@ Id EmitSharedAtomicUMax32(EmitContext& ctx, Id offset, Id value) {
return SharedAtomicU32(ctx, offset, value, &Sirit::Module::OpAtomicUMax);
}
Id EmitSharedAtomicUMax64(EmitContext& ctx, Id offset, Id value) {
return SharedAtomicU64(ctx, offset, value, &Sirit::Module::OpAtomicUMax);
}
Id EmitSharedAtomicSMax32(EmitContext& ctx, Id offset, Id value) {
return SharedAtomicU32(ctx, offset, value, &Sirit::Module::OpAtomicSMax);
}
Id EmitSharedAtomicSMax64(EmitContext& ctx, Id offset, Id value) {
return SharedAtomicU64(ctx, offset, value, &Sirit::Module::OpAtomicSMax);
}
Id EmitSharedAtomicUMin32(EmitContext& ctx, Id offset, Id value) {
return SharedAtomicU32(ctx, offset, value, &Sirit::Module::OpAtomicUMin);
}
Id EmitSharedAtomicUMin64(EmitContext& ctx, Id offset, Id value) {
return SharedAtomicU64(ctx, offset, value, &Sirit::Module::OpAtomicUMin);
}
Id EmitSharedAtomicSMin32(EmitContext& ctx, Id offset, Id value) {
return SharedAtomicU32(ctx, offset, value, &Sirit::Module::OpAtomicSMin);
}
Id EmitSharedAtomicSMin64(EmitContext& ctx, Id offset, Id value) {
return SharedAtomicU64(ctx, offset, value, &Sirit::Module::OpAtomicSMin);
}
Id EmitSharedAtomicAnd32(EmitContext& ctx, Id offset, Id value) {
return SharedAtomicU32(ctx, offset, value, &Sirit::Module::OpAtomicAnd);
}
Id EmitSharedAtomicAnd64(EmitContext& ctx, Id offset, Id value) {
return SharedAtomicU64(ctx, offset, value, &Sirit::Module::OpAtomicAnd);
}
Id EmitSharedAtomicOr32(EmitContext& ctx, Id offset, Id value) {
return SharedAtomicU32(ctx, offset, value, &Sirit::Module::OpAtomicOr);
}
Id EmitSharedAtomicOr64(EmitContext& ctx, Id offset, Id value) {
return SharedAtomicU64(ctx, offset, value, &Sirit::Module::OpAtomicOr);
}
Id EmitSharedAtomicXor32(EmitContext& ctx, Id offset, Id value) {
return SharedAtomicU32(ctx, offset, value, &Sirit::Module::OpAtomicXor);
}
Id EmitSharedAtomicXor64(EmitContext& ctx, Id offset, Id value) {
return SharedAtomicU64(ctx, offset, value, &Sirit::Module::OpAtomicXor);
}
Id EmitSharedAtomicISub32(EmitContext& ctx, Id offset, Id value) {
return SharedAtomicU32(ctx, offset, value, &Sirit::Module::OpAtomicISub);
}
Id EmitSharedAtomicISub64(EmitContext& ctx, Id offset, Id value) {
return SharedAtomicU64(ctx, offset, value, &Sirit::Module::OpAtomicISub);
}
Id EmitSharedAtomicInc32(EmitContext& ctx, Id offset) {
return SharedAtomicU32IncDec(ctx, offset, &Sirit::Module::OpAtomicIIncrement);
}
Id EmitSharedAtomicInc64(EmitContext& ctx, Id offset) {
return SharedAtomicU64IncDec(ctx, offset, &Sirit::Module::OpAtomicIIncrement);
}
Id EmitSharedAtomicDec32(EmitContext& ctx, Id offset) {
return SharedAtomicU32IncDec(ctx, offset, &Sirit::Module::OpAtomicIDecrement);
}
Id EmitSharedAtomicDec64(EmitContext& ctx, Id offset) {
return SharedAtomicU64IncDec(ctx, offset, &Sirit::Module::OpAtomicIDecrement);
}
Id EmitBufferAtomicIAdd32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value) {
return BufferAtomicU32(ctx, inst, handle, address, value, &Sirit::Module::OpAtomicIAdd);
}
@ -200,10 +246,18 @@ Id EmitBufferAtomicSMin32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id addre
return BufferAtomicU32(ctx, inst, handle, address, value, &Sirit::Module::OpAtomicSMin);
}
Id EmitBufferAtomicSMin64(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value) {
return BufferAtomicU64(ctx, inst, handle, address, value, &Sirit::Module::OpAtomicSMin);
}
Id EmitBufferAtomicUMin32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value) {
return BufferAtomicU32(ctx, inst, handle, address, value, &Sirit::Module::OpAtomicUMin);
}
Id EmitBufferAtomicUMin64(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value) {
return BufferAtomicU64(ctx, inst, handle, address, value, &Sirit::Module::OpAtomicUMin);
}
Id EmitBufferAtomicFMin32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value) {
if (ctx.profile.supports_buffer_fp32_atomic_min_max) {
return BufferAtomicU32<true>(ctx, inst, handle, address, value,
@ -226,10 +280,18 @@ Id EmitBufferAtomicSMax32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id addre
return BufferAtomicU32(ctx, inst, handle, address, value, &Sirit::Module::OpAtomicSMax);
}
Id EmitBufferAtomicSMax64(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value) {
return BufferAtomicU64(ctx, inst, handle, address, value, &Sirit::Module::OpAtomicSMax);
}
Id EmitBufferAtomicUMax32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value) {
return BufferAtomicU32(ctx, inst, handle, address, value, &Sirit::Module::OpAtomicUMax);
}
Id EmitBufferAtomicUMax64(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value) {
return BufferAtomicU64(ctx, inst, handle, address, value, &Sirit::Module::OpAtomicUMax);
}
Id EmitBufferAtomicFMax32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value) {
if (ctx.profile.supports_buffer_fp32_atomic_min_max) {
return BufferAtomicU32<true>(ctx, inst, handle, address, value,
@ -360,7 +422,7 @@ Id EmitImageAtomicExchange32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id co
Id EmitDataAppend(EmitContext& ctx, u32 gds_addr, u32 binding) {
const auto& buffer = ctx.buffers[binding];
const auto [id, pointer_type] = buffer[EmitContext::PointerType::U32];
const auto [id, pointer_type] = buffer.Alias(PointerType::U32);
const Id ptr = ctx.OpAccessChain(pointer_type, id, ctx.u32_zero_value, ctx.ConstU32(gds_addr));
const auto [scope, semantics]{AtomicArgs(ctx)};
return ctx.OpAtomicIIncrement(ctx.U32[1], ptr, scope, semantics);
@ -368,7 +430,7 @@ Id EmitDataAppend(EmitContext& ctx, u32 gds_addr, u32 binding) {
Id EmitDataConsume(EmitContext& ctx, u32 gds_addr, u32 binding) {
const auto& buffer = ctx.buffers[binding];
const auto [id, pointer_type] = buffer[EmitContext::PointerType::U32];
const auto [id, pointer_type] = buffer.Alias(PointerType::U32);
const Id ptr = ctx.OpAccessChain(pointer_type, id, ctx.u32_zero_value, ctx.ConstU32(gds_addr));
const auto [scope, semantics]{AtomicArgs(ctx)};
return ctx.OpAtomicIDecrement(ctx.U32[1], ptr, scope, semantics);

View File

@ -2,7 +2,9 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/assert.h"
#include "common/config.h"
#include "common/logging/log.h"
#include "shader_recompiler/backend/spirv/emit_spirv_bounds.h"
#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
#include "shader_recompiler/backend/spirv/spirv_emit_context.h"
#include "shader_recompiler/ir/attribute.h"
@ -11,8 +13,6 @@
#include <magic_enum/magic_enum.hpp>
#include "emit_spirv_bounds.h"
namespace Shader::Backend::SPIRV {
namespace {
@ -164,9 +164,13 @@ void EmitGetGotoVariable(EmitContext&) {
}
using PointerType = EmitContext::PointerType;
using PointerSize = EmitContext::PointerSize;
Id EmitReadConst(EmitContext& ctx, IR::Inst* inst, Id addr, Id offset) {
const u32 flatbuf_off_dw = inst->Flags<u32>();
if (!Config::directMemoryAccess()) {
return ctx.EmitFlatbufferLoad(ctx.ConstU32(flatbuf_off_dw));
}
// We can only provide a fallback for immediate offsets.
if (flatbuf_off_dw == 0) {
return ctx.OpFunctionCall(ctx.U32[1], ctx.read_const_dynamic, addr, offset);
@ -179,14 +183,15 @@ Id EmitReadConst(EmitContext& ctx, IR::Inst* inst, Id addr, Id offset) {
template <PointerType type>
Id ReadConstBuffer(EmitContext& ctx, u32 handle, Id index) {
const auto& buffer = ctx.buffers[handle];
index = ctx.OpIAdd(ctx.U32[1], index, buffer.offset_dwords);
const auto [id, pointer_type] = buffer[type];
if (const Id offset = buffer.Offset(PointerSize::B32); Sirit::ValidId(offset)) {
index = ctx.OpIAdd(ctx.U32[1], index, offset);
}
const auto [id, pointer_type] = buffer.Alias(type);
const auto value_type = type == PointerType::U32 ? ctx.U32[1] : ctx.F32[1];
const Id ptr{ctx.OpAccessChain(pointer_type, id, ctx.u32_zero_value, index)};
const Id result{ctx.OpLoad(value_type, ptr)};
if (Sirit::ValidId(buffer.size_dwords)) {
const Id in_bounds = ctx.OpULessThan(ctx.U1[1], index, buffer.size_dwords);
if (const Id size = buffer.Size(PointerSize::B32); Sirit::ValidId(size)) {
const Id in_bounds = ctx.OpULessThan(ctx.U1[1], index, size);
return ctx.OpSelect(value_type, in_bounds, result, ctx.u32_zero_value);
}
return result;
@ -419,25 +424,24 @@ void EmitSetPatch(EmitContext& ctx, IR::Patch patch, Id value) {
template <u32 N, PointerType alias>
static Id EmitLoadBufferB32xN(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address) {
constexpr bool is_float = alias == PointerType::F32;
const auto flags = inst->Flags<IR::BufferInstInfo>();
const auto& spv_buffer = ctx.buffers[handle];
if (Sirit::ValidId(spv_buffer.offset)) {
address = ctx.OpIAdd(ctx.U32[1], address, spv_buffer.offset);
if (const Id offset = spv_buffer.Offset(PointerSize::B32); Sirit::ValidId(offset)) {
address = ctx.OpIAdd(ctx.U32[1], address, offset);
}
const Id index = ctx.OpShiftRightLogical(ctx.U32[1], address, ctx.ConstU32(2u));
const auto& data_types = alias == PointerType::U32 ? ctx.U32 : ctx.F32;
const auto [id, pointer_type] = spv_buffer[alias];
const auto [id, pointer_type] = spv_buffer.Alias(alias);
boost::container::static_vector<Id, N> ids;
for (u32 i = 0; i < N; i++) {
const Id index_i = i == 0 ? index : ctx.OpIAdd(ctx.U32[1], index, ctx.ConstU32(i));
const Id index_i = i == 0 ? address : ctx.OpIAdd(ctx.U32[1], address, ctx.ConstU32(i));
const Id ptr_i = ctx.OpAccessChain(pointer_type, id, ctx.u32_zero_value, index_i);
const Id result_i = ctx.OpLoad(data_types[1], ptr_i);
if (!flags.typed) {
// Untyped loads have bounds checking per-component.
ids.push_back(LoadAccessBoundsCheck < 32, 1,
alias ==
PointerType::F32 > (ctx, index_i, spv_buffer.size_dwords, result_i));
ids.push_back(LoadAccessBoundsCheck<32, 1, is_float>(
ctx, index_i, spv_buffer.Size(PointerSize::B32), result_i));
} else {
ids.push_back(result_i);
}
@ -446,33 +450,32 @@ 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);
if (flags.typed) {
// Typed loads have single bounds check for the whole load.
return LoadAccessBoundsCheck < 32, N,
alias == PointerType::F32 > (ctx, index, spv_buffer.size_dwords, result);
return LoadAccessBoundsCheck<32, N, is_float>(ctx, address,
spv_buffer.Size(PointerSize::B32), result);
}
return result;
}
Id EmitLoadBufferU8(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);
if (const Id offset = spv_buffer.Offset(PointerSize::B8); Sirit::ValidId(offset)) {
address = ctx.OpIAdd(ctx.U32[1], address, offset);
}
const auto [id, pointer_type] = spv_buffer[PointerType::U8];
const auto [id, pointer_type] = spv_buffer.Alias(PointerType::U8);
const Id ptr{ctx.OpAccessChain(pointer_type, id, ctx.u32_zero_value, address)};
const Id result{ctx.OpLoad(ctx.U8, ptr)};
return LoadAccessBoundsCheck<8>(ctx, address, spv_buffer.size, result);
return LoadAccessBoundsCheck<8>(ctx, address, spv_buffer.Size(PointerSize::B8), result);
}
Id EmitLoadBufferU16(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);
if (const Id offset = spv_buffer.Offset(PointerSize::B16); Sirit::ValidId(offset)) {
address = ctx.OpIAdd(ctx.U32[1], address, offset);
}
const auto [id, pointer_type] = spv_buffer[PointerType::U16];
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 auto [id, pointer_type] = spv_buffer.Alias(PointerType::U16);
const Id ptr{ctx.OpAccessChain(pointer_type, id, ctx.u32_zero_value, address)};
const Id result{ctx.OpLoad(ctx.U16, ptr)};
return LoadAccessBoundsCheck<16>(ctx, index, spv_buffer.size_shorts, result);
return LoadAccessBoundsCheck<16>(ctx, address, spv_buffer.Size(PointerSize::B16), result);
}
Id EmitLoadBufferU32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address) {
@ -493,14 +496,13 @@ Id EmitLoadBufferU32x4(EmitContext& ctx, IR::Inst* inst, u32 handle, Id 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);
if (const Id offset = spv_buffer.Offset(PointerSize::B64); Sirit::ValidId(offset)) {
address = ctx.OpIAdd(ctx.U32[1], address, 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 auto [id, pointer_type] = spv_buffer.Alias(PointerType::U64);
const Id ptr{ctx.OpAccessChain(pointer_type, id, ctx.u64_zero_value, address)};
const Id result{ctx.OpLoad(ctx.U64, ptr)};
return LoadAccessBoundsCheck<64>(ctx, index, spv_buffer.size_qwords, result);
return LoadAccessBoundsCheck<64>(ctx, address, spv_buffer.Size(PointerSize::B64), result);
}
Id EmitLoadBufferF32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address) {
@ -526,18 +528,18 @@ Id EmitLoadBufferFormatF32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id addr
template <u32 N, PointerType alias>
static void EmitStoreBufferB32xN(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address,
Id value) {
constexpr bool is_float = alias == PointerType::F32;
const auto flags = inst->Flags<IR::BufferInstInfo>();
const auto& spv_buffer = ctx.buffers[handle];
if (Sirit::ValidId(spv_buffer.offset)) {
address = ctx.OpIAdd(ctx.U32[1], address, spv_buffer.offset);
if (const Id offset = spv_buffer.Offset(PointerSize::B32); Sirit::ValidId(offset)) {
address = ctx.OpIAdd(ctx.U32[1], address, offset);
}
const Id index = ctx.OpShiftRightLogical(ctx.U32[1], address, ctx.ConstU32(2u));
const auto& data_types = alias == PointerType::U32 ? ctx.U32 : ctx.F32;
const auto [id, pointer_type] = spv_buffer[alias];
const auto [id, pointer_type] = spv_buffer.Alias(alias);
auto store = [&] {
for (u32 i = 0; i < N; i++) {
const Id index_i = i == 0 ? index : ctx.OpIAdd(ctx.U32[1], index, ctx.ConstU32(i));
const Id index_i = i == 0 ? address : ctx.OpIAdd(ctx.U32[1], address, ctx.ConstU32(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);
auto store_i = [&] {
@ -546,8 +548,8 @@ static void EmitStoreBufferB32xN(EmitContext& ctx, IR::Inst* inst, u32 handle, I
};
if (!flags.typed) {
// Untyped stores have bounds checking per-component.
AccessBoundsCheck<32, 1, alias == PointerType::F32>(
ctx, index_i, spv_buffer.size_dwords, store_i);
AccessBoundsCheck<32, 1, is_float>(ctx, index_i, spv_buffer.Size(PointerSize::B32),
store_i);
} else {
store_i();
}
@ -557,8 +559,7 @@ static void EmitStoreBufferB32xN(EmitContext& ctx, IR::Inst* inst, u32 handle, I
if (flags.typed) {
// Typed stores have single bounds check for the whole store.
AccessBoundsCheck<32, N, alias == PointerType::F32>(ctx, index, spv_buffer.size_dwords,
store);
AccessBoundsCheck<32, N, is_float>(ctx, address, spv_buffer.Size(PointerSize::B32), store);
} else {
store();
}
@ -566,12 +567,12 @@ static void EmitStoreBufferB32xN(EmitContext& ctx, IR::Inst* inst, u32 handle, I
void EmitStoreBufferU8(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);
if (const Id offset = spv_buffer.Offset(PointerSize::B8); Sirit::ValidId(offset)) {
address = ctx.OpIAdd(ctx.U32[1], address, offset);
}
const auto [id, pointer_type] = spv_buffer[PointerType::U8];
const auto [id, pointer_type] = spv_buffer.Alias(PointerType::U8);
const Id ptr{ctx.OpAccessChain(pointer_type, id, ctx.u32_zero_value, address)};
AccessBoundsCheck<8>(ctx, address, spv_buffer.size, [&] {
AccessBoundsCheck<8>(ctx, address, spv_buffer.Size(PointerSize::B8), [&] {
ctx.OpStore(ptr, value);
return Id{};
});
@ -579,13 +580,12 @@ void EmitStoreBufferU8(EmitContext& ctx, IR::Inst*, u32 handle, Id address, Id v
void EmitStoreBufferU16(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);
if (const Id offset = spv_buffer.Offset(PointerSize::B16); Sirit::ValidId(offset)) {
address = ctx.OpIAdd(ctx.U32[1], address, offset);
}
const auto [id, pointer_type] = spv_buffer[PointerType::U16];
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)};
AccessBoundsCheck<16>(ctx, index, spv_buffer.size_shorts, [&] {
const auto [id, pointer_type] = spv_buffer.Alias(PointerType::U16);
const Id ptr{ctx.OpAccessChain(pointer_type, id, ctx.u32_zero_value, address)};
AccessBoundsCheck<16>(ctx, address, spv_buffer.Size(PointerSize::B16), [&] {
ctx.OpStore(ptr, value);
return Id{};
});
@ -609,13 +609,12 @@ void EmitStoreBufferU32x4(EmitContext& ctx, IR::Inst* inst, u32 handle, Id addre
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);
if (const Id offset = spv_buffer.Offset(PointerSize::B64); Sirit::ValidId(offset)) {
address = ctx.OpIAdd(ctx.U32[1], address, 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, [&] {
const auto [id, pointer_type] = spv_buffer.Alias(PointerType::U64);
const Id ptr{ctx.OpAccessChain(pointer_type, id, ctx.u64_zero_value, address)};
AccessBoundsCheck<64>(ctx, address, spv_buffer.Size(PointerSize::B64), [&] {
ctx.OpStore(ptr, value);
return Id{};
});

View File

@ -91,10 +91,14 @@ Id EmitBufferAtomicIAdd32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id addre
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 EmitBufferAtomicSMin64(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 EmitBufferAtomicUMin64(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value);
Id EmitBufferAtomicFMin32(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 EmitBufferAtomicSMax64(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 EmitBufferAtomicUMax64(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value);
Id EmitBufferAtomicFMax32(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);
@ -135,15 +139,25 @@ void EmitWriteSharedU64(EmitContext& ctx, Id offset, Id value);
Id EmitSharedAtomicIAdd32(EmitContext& ctx, Id offset, Id value);
Id EmitSharedAtomicIAdd64(EmitContext& ctx, Id offset, Id value);
Id EmitSharedAtomicUMax32(EmitContext& ctx, Id offset, Id value);
Id EmitSharedAtomicUMax64(EmitContext& ctx, Id offset, Id value);
Id EmitSharedAtomicSMax32(EmitContext& ctx, Id offset, Id value);
Id EmitSharedAtomicSMax64(EmitContext& ctx, Id offset, Id value);
Id EmitSharedAtomicUMin32(EmitContext& ctx, Id offset, Id value);
Id EmitSharedAtomicUMin64(EmitContext& ctx, Id offset, Id value);
Id EmitSharedAtomicSMin32(EmitContext& ctx, Id offset, Id value);
Id EmitSharedAtomicSMin64(EmitContext& ctx, Id offset, Id value);
Id EmitSharedAtomicAnd32(EmitContext& ctx, Id offset, Id value);
Id EmitSharedAtomicAnd64(EmitContext& ctx, Id offset, Id value);
Id EmitSharedAtomicOr32(EmitContext& ctx, Id offset, Id value);
Id EmitSharedAtomicOr64(EmitContext& ctx, Id offset, Id value);
Id EmitSharedAtomicXor32(EmitContext& ctx, Id offset, Id value);
Id EmitSharedAtomicXor64(EmitContext& ctx, Id offset, Id value);
Id EmitSharedAtomicInc32(EmitContext& ctx, Id offset);
Id EmitSharedAtomicInc64(EmitContext& ctx, Id offset);
Id EmitSharedAtomicDec32(EmitContext& ctx, Id offset);
Id EmitSharedAtomicDec64(EmitContext& ctx, Id offset);
Id EmitSharedAtomicISub32(EmitContext& ctx, Id offset, Id value);
Id EmitSharedAtomicISub64(EmitContext& ctx, Id offset, Id value);
Id EmitCompositeConstructU32x2(EmitContext& ctx, IR::Inst* inst, Id e1, Id e2);
Id EmitCompositeConstructU32x3(EmitContext& ctx, IR::Inst* inst, Id e1, Id e2, Id e3);
@ -349,7 +363,7 @@ Id EmitFPIsInf32(EmitContext& ctx, Id value);
Id EmitFPIsInf64(EmitContext& ctx, Id value);
Id EmitIAdd32(EmitContext& ctx, IR::Inst* inst, Id a, Id b);
Id EmitIAdd64(EmitContext& ctx, Id a, Id b);
Id EmitIAddCary32(EmitContext& ctx, Id a, Id b);
Id EmitIAddCarry32(EmitContext& ctx, Id a, Id b);
Id EmitISub32(EmitContext& ctx, Id a, Id b);
Id EmitISub64(EmitContext& ctx, Id a, Id b);
Id EmitSMulHi(EmitContext& ctx, Id a, Id b);
@ -404,14 +418,20 @@ Id EmitULessThan32(EmitContext& ctx, Id lhs, Id rhs);
Id EmitULessThan64(EmitContext& ctx, Id lhs, Id rhs);
Id EmitIEqual32(EmitContext& ctx, Id lhs, Id rhs);
Id EmitIEqual64(EmitContext& ctx, Id lhs, Id rhs);
Id EmitSLessThanEqual(EmitContext& ctx, Id lhs, Id rhs);
Id EmitULessThanEqual(EmitContext& ctx, Id lhs, Id rhs);
Id EmitSGreaterThan(EmitContext& ctx, Id lhs, Id rhs);
Id EmitUGreaterThan(EmitContext& ctx, Id lhs, Id rhs);
Id EmitSLessThanEqual32(EmitContext& ctx, Id lhs, Id rhs);
Id EmitSLessThanEqual64(EmitContext& ctx, Id lhs, Id rhs);
Id EmitULessThanEqual32(EmitContext& ctx, Id lhs, Id rhs);
Id EmitULessThanEqual64(EmitContext& ctx, Id lhs, Id rhs);
Id EmitSGreaterThan32(EmitContext& ctx, Id lhs, Id rhs);
Id EmitSGreaterThan64(EmitContext& ctx, Id lhs, Id rhs);
Id EmitUGreaterThan32(EmitContext& ctx, Id lhs, Id rhs);
Id EmitUGreaterThan64(EmitContext& ctx, Id lhs, Id rhs);
Id EmitINotEqual32(EmitContext& ctx, Id lhs, Id rhs);
Id EmitINotEqual64(EmitContext& ctx, Id lhs, Id rhs);
Id EmitSGreaterThanEqual(EmitContext& ctx, Id lhs, Id rhs);
Id EmitUGreaterThanEqual(EmitContext& ctx, Id lhs, Id rhs);
Id EmitSGreaterThanEqual32(EmitContext& ctx, Id lhs, Id rhs);
Id EmitSGreaterThanEqual64(EmitContext& ctx, Id lhs, Id rhs);
Id EmitUGreaterThanEqual32(EmitContext& ctx, Id lhs, Id rhs);
Id EmitUGreaterThanEqual64(EmitContext& ctx, Id lhs, Id rhs);
Id EmitLogicalOr(EmitContext& ctx, Id a, Id b);
Id EmitLogicalAnd(EmitContext& ctx, Id a, Id b);
Id EmitLogicalXor(EmitContext& ctx, Id a, Id b);
@ -509,8 +529,10 @@ Id EmitLaneId(EmitContext& ctx);
Id EmitWarpId(EmitContext& ctx);
Id EmitQuadShuffle(EmitContext& ctx, Id value, Id index);
Id EmitReadFirstLane(EmitContext& ctx, Id value);
Id EmitReadLane(EmitContext& ctx, Id value, u32 lane);
Id EmitReadLane(EmitContext& ctx, Id value, Id lane);
Id EmitWriteLane(EmitContext& ctx, Id value, Id write_value, u32 lane);
Id EmitBallot(EmitContext& ctx, Id bit);
Id EmitBallotFindLsb(EmitContext& ctx, Id mask);
Id EmitDataAppend(EmitContext& ctx, u32 gds_addr, u32 binding);
Id EmitDataConsume(EmitContext& ctx, u32 gds_addr, u32 binding);

View File

@ -60,7 +60,7 @@ Id EmitIAdd64(EmitContext& ctx, Id a, Id b) {
return ctx.OpIAdd(ctx.U64, a, b);
}
Id EmitIAddCary32(EmitContext& ctx, Id a, Id b) {
Id EmitIAddCarry32(EmitContext& ctx, Id a, Id b) {
return ctx.OpIAddCarry(ctx.full_result_u32x2, a, b);
}
@ -371,19 +371,35 @@ Id EmitIEqual64(EmitContext& ctx, Id lhs, Id rhs) {
return ctx.OpIEqual(ctx.U1[1], lhs, rhs);
}
Id EmitSLessThanEqual(EmitContext& ctx, Id lhs, Id rhs) {
Id EmitSLessThanEqual32(EmitContext& ctx, Id lhs, Id rhs) {
return ctx.OpSLessThanEqual(ctx.U1[1], lhs, rhs);
}
Id EmitULessThanEqual(EmitContext& ctx, Id lhs, Id rhs) {
Id EmitSLessThanEqual64(EmitContext& ctx, Id lhs, Id rhs) {
return ctx.OpSLessThanEqual(ctx.U1[1], lhs, rhs);
}
Id EmitULessThanEqual32(EmitContext& ctx, Id lhs, Id rhs) {
return ctx.OpULessThanEqual(ctx.U1[1], lhs, rhs);
}
Id EmitSGreaterThan(EmitContext& ctx, Id lhs, Id rhs) {
Id EmitULessThanEqual64(EmitContext& ctx, Id lhs, Id rhs) {
return ctx.OpULessThanEqual(ctx.U1[1], lhs, rhs);
}
Id EmitSGreaterThan32(EmitContext& ctx, Id lhs, Id rhs) {
return ctx.OpSGreaterThan(ctx.U1[1], lhs, rhs);
}
Id EmitUGreaterThan(EmitContext& ctx, Id lhs, Id rhs) {
Id EmitSGreaterThan64(EmitContext& ctx, Id lhs, Id rhs) {
return ctx.OpSGreaterThan(ctx.U1[1], lhs, rhs);
}
Id EmitUGreaterThan32(EmitContext& ctx, Id lhs, Id rhs) {
return ctx.OpUGreaterThan(ctx.U1[1], lhs, rhs);
}
Id EmitUGreaterThan64(EmitContext& ctx, Id lhs, Id rhs) {
return ctx.OpUGreaterThan(ctx.U1[1], lhs, rhs);
}
@ -395,11 +411,19 @@ Id EmitINotEqual64(EmitContext& ctx, Id lhs, Id rhs) {
return ctx.OpINotEqual(ctx.U1[1], lhs, rhs);
}
Id EmitSGreaterThanEqual(EmitContext& ctx, Id lhs, Id rhs) {
Id EmitSGreaterThanEqual32(EmitContext& ctx, Id lhs, Id rhs) {
return ctx.OpSGreaterThanEqual(ctx.U1[1], lhs, rhs);
}
Id EmitUGreaterThanEqual(EmitContext& ctx, Id lhs, Id rhs) {
Id EmitSGreaterThanEqual64(EmitContext& ctx, Id lhs, Id rhs) {
return ctx.OpSGreaterThanEqual(ctx.U1[1], lhs, rhs);
}
Id EmitUGreaterThanEqual32(EmitContext& ctx, Id lhs, Id rhs) {
return ctx.OpUGreaterThanEqual(ctx.U1[1], lhs, rhs);
}
Id EmitUGreaterThanEqual64(EmitContext& ctx, Id lhs, Id rhs) {
return ctx.OpUGreaterThanEqual(ctx.U1[1], lhs, rhs);
}

View File

@ -26,13 +26,20 @@ Id EmitReadFirstLane(EmitContext& ctx, Id value) {
return ctx.OpGroupNonUniformBroadcastFirst(ctx.U32[1], SubgroupScope(ctx), value);
}
Id EmitReadLane(EmitContext& ctx, Id value, u32 lane) {
return ctx.OpGroupNonUniformBroadcast(ctx.U32[1], SubgroupScope(ctx), value,
ctx.ConstU32(lane));
Id EmitReadLane(EmitContext& ctx, Id value, Id lane) {
return ctx.OpGroupNonUniformBroadcast(ctx.U32[1], SubgroupScope(ctx), value, lane);
}
Id EmitWriteLane(EmitContext& ctx, Id value, Id write_value, u32 lane) {
return ctx.u32_zero_value;
}
Id EmitBallot(EmitContext& ctx, Id bit) {
return ctx.OpGroupNonUniformBallot(ctx.U32[4], SubgroupScope(ctx), bit);
}
Id EmitBallotFindLsb(EmitContext& ctx, Id mask) {
return ctx.OpGroupNonUniformBallotFindLSB(ctx.U32[1], SubgroupScope(ctx), mask);
}
} // namespace Shader::Backend::SPIRV

View File

@ -71,11 +71,12 @@ EmitContext::EmitContext(const Profile& profile_, const RuntimeInfo& runtime_inf
Bindings& binding_)
: Sirit::Module(profile_.supported_spirv), info{info_}, runtime_info{runtime_info_},
profile{profile_}, stage{info.stage}, l_stage{info.l_stage}, binding{binding_} {
if (info.dma_types != IR::Type::Void) {
if (info.uses_dma) {
SetMemoryModel(spv::AddressingModel::PhysicalStorageBuffer64, spv::MemoryModel::GLSL450);
} else {
SetMemoryModel(spv::AddressingModel::Logical, spv::MemoryModel::GLSL450);
}
String(fmt::format("{:#x}", info.pgm_hash));
AddCapability(spv::Capability::Shader);
DefineArithmeticTypes();
@ -169,34 +170,8 @@ void EmitContext::DefineArithmeticTypes() {
if (info.uses_fp64) {
frexp_result_f64 = Name(TypeStruct(F64[1], S32[1]), "frexp_result_f64");
}
if (True(info.dma_types & IR::Type::F64)) {
physical_pointer_types[PointerType::F64] =
TypePointer(spv::StorageClass::PhysicalStorageBuffer, F64[1]);
}
if (True(info.dma_types & IR::Type::U64)) {
physical_pointer_types[PointerType::U64] =
TypePointer(spv::StorageClass::PhysicalStorageBuffer, U64);
}
if (True(info.dma_types & IR::Type::F32)) {
physical_pointer_types[PointerType::F32] =
TypePointer(spv::StorageClass::PhysicalStorageBuffer, F32[1]);
}
if (True(info.dma_types & IR::Type::U32)) {
physical_pointer_types[PointerType::U32] =
TypePointer(spv::StorageClass::PhysicalStorageBuffer, U32[1]);
}
if (True(info.dma_types & IR::Type::F16)) {
physical_pointer_types[PointerType::F16] =
TypePointer(spv::StorageClass::PhysicalStorageBuffer, F16[1]);
}
if (True(info.dma_types & IR::Type::U16)) {
physical_pointer_types[PointerType::U16] =
TypePointer(spv::StorageClass::PhysicalStorageBuffer, U16);
}
if (True(info.dma_types & IR::Type::U8)) {
physical_pointer_types[PointerType::U8] =
TypePointer(spv::StorageClass::PhysicalStorageBuffer, U8);
if (info.uses_dma) {
physical_pointer_type_u32 = TypePointer(spv::StorageClass::PhysicalStorageBuffer, U32[1]);
}
}
@ -239,7 +214,7 @@ Id EmitContext::GetBufferSize(const u32 sharp_idx) {
// Can this be done with memory access? Like we do now with ReadConst
const auto& srt_flatbuf = buffers[flatbuf_index];
ASSERT(srt_flatbuf.buffer_type == BufferType::Flatbuf);
const auto [id, pointer_type] = srt_flatbuf[PointerType::U32];
const auto [id, pointer_type] = srt_flatbuf.Alias(PointerType::U32);
const auto rsrc1{
OpLoad(U32[1], OpAccessChain(pointer_type, id, u32_zero_value, ConstU32(sharp_idx + 1)))};
@ -255,39 +230,70 @@ Id EmitContext::GetBufferSize(const u32 sharp_idx) {
}
void EmitContext::DefineBufferProperties() {
if (!profile.needs_buffer_offsets && profile.supports_robust_buffer_access) {
return;
}
for (u32 i = 0; i < buffers.size(); i++) {
BufferDefinition& buffer = buffers[i];
auto& buffer = buffers[i];
const auto& desc = info.buffers[i];
const u32 binding = buffer.binding;
if (buffer.buffer_type != BufferType::Guest) {
continue;
}
const u32 binding = buffer.binding;
const u32 half = PushData::BufOffsetIndex + (binding >> 4);
const u32 comp = (binding & 0xf) >> 2;
const u32 offset = (binding & 0x3) << 3;
const Id ptr{OpAccessChain(TypePointer(spv::StorageClass::PushConstant, U32[1]),
push_data_block, ConstU32(half), ConstU32(comp))};
const Id value{OpLoad(U32[1], ptr)};
buffer.offset = OpBitFieldUExtract(U32[1], value, ConstU32(offset), ConstU32(8U));
Name(buffer.offset, fmt::format("buf{}_off", binding));
buffer.offset_dwords = OpShiftRightLogical(U32[1], buffer.offset, ConstU32(2U));
Name(buffer.offset_dwords, fmt::format("buf{}_dword_off", binding));
// Only need to load size if performing bounds checks and the buffer is both guest and not
// inline.
if (!profile.supports_robust_buffer_access && buffer.buffer_type == BufferType::Guest) {
const BufferResource& desc = info.buffers[i];
if (desc.sharp_idx == std::numeric_limits<u32>::max()) {
buffer.size = ConstU32(desc.inline_cbuf.GetSize());
} else {
buffer.size = GetBufferSize(desc.sharp_idx);
// Only load and apply buffer offsets if host GPU alignment is larger than guest.
if (profile.needs_buffer_offsets) {
const u32 half = PushData::BufOffsetIndex + (binding >> 4);
const u32 comp = (binding & 0xf) >> 2;
const u32 offset = (binding & 0x3) << 3;
const Id ptr{OpAccessChain(TypePointer(spv::StorageClass::PushConstant, U32[1]),
push_data_block, ConstU32(half), ConstU32(comp))};
const Id value{OpLoad(U32[1], ptr)};
const Id buf_offset{OpBitFieldUExtract(U32[1], value, ConstU32(offset), ConstU32(8U))};
Name(buf_offset, fmt::format("buf{}_off", binding));
buffer.Offset(PointerSize::B8) = buf_offset;
if (True(desc.used_types & IR::Type::U16)) {
const Id buf_word_offset{OpShiftRightLogical(U32[1], buf_offset, ConstU32(1U))};
Name(buf_word_offset, fmt::format("buf{}_word_off", binding));
buffer.Offset(PointerSize::B16) = buf_word_offset;
}
if (True(desc.used_types & IR::Type::U32)) {
const Id buf_dword_offset{OpShiftRightLogical(U32[1], buf_offset, ConstU32(2U))};
Name(buf_dword_offset, fmt::format("buf{}_dword_off", binding));
buffer.Offset(PointerSize::B32) = buf_dword_offset;
}
if (True(desc.used_types & IR::Type::U64)) {
const Id buf_qword_offset{OpShiftRightLogical(U32[1], buf_offset, ConstU32(3U))};
Name(buf_qword_offset, fmt::format("buf{}_qword_off", binding));
buffer.Offset(PointerSize::B64) = buf_qword_offset;
}
}
// Only load size if performing bounds checks.
if (!profile.supports_robust_buffer_access) {
const Id buf_size{desc.sharp_idx == std::numeric_limits<u32>::max()
? ConstU32(desc.inline_cbuf.GetSize())
: GetBufferSize(desc.sharp_idx)};
Name(buf_size, fmt::format("buf{}_size", binding));
buffer.Size(PointerSize::B8) = buf_size;
if (True(desc.used_types & IR::Type::U16)) {
const Id buf_word_size{OpShiftRightLogical(U32[1], buf_size, ConstU32(1U))};
Name(buf_word_size, fmt::format("buf{}_short_size", binding));
buffer.Size(PointerSize::B16) = buf_word_size;
}
if (True(desc.used_types & IR::Type::U32)) {
const Id buf_dword_size{OpShiftRightLogical(U32[1], buf_size, ConstU32(2U))};
Name(buf_dword_size, fmt::format("buf{}_dword_size", binding));
buffer.Size(PointerSize::B32) = buf_dword_size;
}
if (True(desc.used_types & IR::Type::U64)) {
const Id buf_qword_size{OpShiftRightLogical(U32[1], buf_size, ConstU32(3U))};
Name(buf_qword_size, fmt::format("buf{}_qword_size", binding));
buffer.Size(PointerSize::B64) = buf_qword_size;
}
Name(buffer.size, fmt::format("buf{}_size", binding));
buffer.size_shorts = OpShiftRightLogical(U32[1], buffer.size, ConstU32(1U));
Name(buffer.size_shorts, fmt::format("buf{}_short_size", binding));
buffer.size_dwords = OpShiftRightLogical(U32[1], buffer.size, ConstU32(2U));
Name(buffer.size_dwords, fmt::format("buf{}_dword_size", binding));
buffer.size_qwords = OpShiftRightLogical(U32[1], buffer.size, ConstU32(3U));
Name(buffer.size_qwords, fmt::format("buf{}_qword_size", binding));
}
}
}
@ -695,7 +701,7 @@ void EmitContext::DefineOutputs() {
void EmitContext::DefinePushDataBlock() {
// Create push constants block for instance steps rates
const Id struct_type{Name(TypeStruct(U32[1], U32[1], F32[1], F32[1], F32[1], F32[1], U32[4],
U32[4], U32[4], U32[4], U32[4], U32[4]),
U32[4], U32[4], U32[4], U32[4], U32[4], U32[2]),
"AuxData")};
Decorate(struct_type, spv::Decoration::Block);
MemberName(struct_type, PushData::Step0Index, "sr0");
@ -710,6 +716,7 @@ void EmitContext::DefinePushDataBlock() {
MemberName(struct_type, PushData::UdRegsIndex + 3, "ud_regs3");
MemberName(struct_type, PushData::BufOffsetIndex + 0, "buf_offsets0");
MemberName(struct_type, PushData::BufOffsetIndex + 1, "buf_offsets1");
MemberName(struct_type, PushData::BufOffsetIndex + 2, "buf_offsets2");
MemberDecorate(struct_type, PushData::Step0Index, spv::Decoration::Offset, 0U);
MemberDecorate(struct_type, PushData::Step1Index, spv::Decoration::Offset, 4U);
MemberDecorate(struct_type, PushData::XOffsetIndex, spv::Decoration::Offset, 8U);
@ -722,6 +729,7 @@ void EmitContext::DefinePushDataBlock() {
MemberDecorate(struct_type, PushData::UdRegsIndex + 3, spv::Decoration::Offset, 72U);
MemberDecorate(struct_type, PushData::BufOffsetIndex + 0, spv::Decoration::Offset, 88U);
MemberDecorate(struct_type, PushData::BufOffsetIndex + 1, spv::Decoration::Offset, 104U);
MemberDecorate(struct_type, PushData::BufOffsetIndex + 2, spv::Decoration::Offset, 120U);
push_data_block = DefineVar(struct_type, spv::StorageClass::PushConstant);
Name(push_data_block, "push_data");
interfaces.push_back(push_data_block);
@ -779,20 +787,6 @@ EmitContext::BufferSpv EmitContext::DefineBuffer(bool is_storage, bool is_writte
};
void EmitContext::DefineBuffers() {
if (!profile.supports_robust_buffer_access &&
info.readconst_types == Info::ReadConstType::None) {
// In case Flatbuf has not already been bound by IR and is needed
// to query buffer sizes, bind it now.
info.buffers.push_back({
.used_types = IR::Type::U32,
// We can't guarantee that flatbuf will not grow past UBO
// limit if there are a lot of ReadConsts. (We could specialize)
.inline_cbuf = AmdGpu::Buffer::Placeholder(std::numeric_limits<u32>::max()),
.buffer_type = BufferType::Flatbuf,
});
// In the future we may want to read buffer sizes from GPU memory if available.
// info.readconst_types |= Info::ReadConstType::Immediate;
}
for (const auto& desc : info.buffers) {
const auto buf_sharp = desc.GetSharp(info);
const bool is_storage = desc.IsStorage(buf_sharp, profile);
@ -809,23 +803,23 @@ void EmitContext::DefineBuffers() {
// Define aliases depending on the shader usage.
auto& spv_buffer = buffers.emplace_back(binding.buffer++, desc.buffer_type);
if (True(desc.used_types & IR::Type::U64)) {
spv_buffer[PointerType::U64] =
spv_buffer.Alias(PointerType::U64) =
DefineBuffer(is_storage, desc.is_written, 3, desc.buffer_type, U64);
}
if (True(desc.used_types & IR::Type::U32)) {
spv_buffer[PointerType::U32] =
spv_buffer.Alias(PointerType::U32) =
DefineBuffer(is_storage, desc.is_written, 2, desc.buffer_type, U32[1]);
}
if (True(desc.used_types & IR::Type::F32)) {
spv_buffer[PointerType::F32] =
spv_buffer.Alias(PointerType::F32) =
DefineBuffer(is_storage, desc.is_written, 2, desc.buffer_type, F32[1]);
}
if (True(desc.used_types & IR::Type::U16)) {
spv_buffer[PointerType::U16] =
spv_buffer.Alias(PointerType::U16) =
DefineBuffer(is_storage, desc.is_written, 1, desc.buffer_type, U16);
}
if (True(desc.used_types & IR::Type::U8)) {
spv_buffer[PointerType::U8] =
spv_buffer.Alias(PointerType::U8) =
DefineBuffer(is_storage, desc.is_written, 0, desc.buffer_type, U8);
}
++binding.unified;
@ -1154,7 +1148,7 @@ Id EmitContext::DefineGetBdaPointer() {
const auto page{OpShiftRightLogical(U64, address, caching_pagebits)};
const auto page32{OpUConvert(U32[1], page)};
const auto& bda_buffer{buffers[bda_pagetable_index]};
const auto [bda_buffer_id, bda_pointer_type] = bda_buffer[PointerType::U64];
const auto [bda_buffer_id, bda_pointer_type] = bda_buffer.Alias(PointerType::U64);
const auto bda_ptr{OpAccessChain(bda_pointer_type, bda_buffer_id, u32_zero_value, page32)};
const auto bda{OpLoad(U64, bda_ptr)};
@ -1166,14 +1160,14 @@ Id EmitContext::DefineGetBdaPointer() {
// First time acces, mark as fault
AddLabel(fault_label);
const auto& fault_buffer{buffers[fault_buffer_index]};
const auto [fault_buffer_id, fault_pointer_type] = fault_buffer[PointerType::U8];
const auto page_div8{OpShiftRightLogical(U32[1], page32, ConstU32(3U))};
const auto page_mod8{OpBitwiseAnd(U32[1], page32, ConstU32(7U))};
const auto page_mask{OpShiftLeftLogical(U8, u8_one_value, page_mod8)};
const auto [fault_buffer_id, fault_pointer_type] = fault_buffer.Alias(PointerType::U32);
const auto page_div32{OpShiftRightLogical(U32[1], page32, ConstU32(5U))};
const auto page_mod32{OpBitwiseAnd(U32[1], page32, ConstU32(31U))};
const auto page_mask{OpShiftLeftLogical(U32[1], u32_one_value, page_mod32)};
const auto fault_ptr{
OpAccessChain(fault_pointer_type, fault_buffer_id, u32_zero_value, page_div8)};
const auto fault_value{OpLoad(U8, fault_ptr)};
const auto fault_value_masked{OpBitwiseOr(U8, fault_value, page_mask)};
OpAccessChain(fault_pointer_type, fault_buffer_id, u32_zero_value, page_div32)};
const auto fault_value{OpLoad(U32[1], fault_ptr)};
const auto fault_value_masked{OpBitwiseOr(U32[1], fault_value, page_mask)};
OpStore(fault_ptr, fault_value_masked);
// Return null pointer
@ -1211,17 +1205,11 @@ Id EmitContext::DefineReadConst(bool dynamic) {
const auto offset_bytes{OpShiftLeftLogical(U32[1], offset, ConstU32(2U))};
const auto addr{OpIAdd(U64, base_addr, OpUConvert(U64, offset_bytes))};
const auto result = EmitMemoryRead(U32[1], addr, [&]() {
const auto result = EmitDwordMemoryRead(addr, [&]() {
if (dynamic) {
return u32_zero_value;
} else {
const auto& flatbuf_buffer{buffers[flatbuf_index]};
ASSERT(flatbuf_buffer.binding >= 0 &&
flatbuf_buffer.buffer_type == BufferType::Flatbuf);
const auto [flatbuf_buffer_id, flatbuf_pointer_type] = flatbuf_buffer[PointerType::U32];
const auto ptr{OpAccessChain(flatbuf_pointer_type, flatbuf_buffer_id, u32_zero_value,
flatbuf_offset)};
return OpLoad(U32[1], ptr);
return EmitFlatbufferLoad(flatbuf_offset);
}
});
@ -1239,7 +1227,7 @@ void EmitContext::DefineFunctions() {
uf11_to_f32 = DefineUfloatM5ToFloat32(6, "uf11_to_f32");
uf10_to_f32 = DefineUfloatM5ToFloat32(5, "uf10_to_f32");
}
if (info.dma_types != IR::Type::Void) {
if (info.uses_dma) {
get_bda_pointer = DefineGetBdaPointer();
}

View File

@ -42,17 +42,6 @@ public:
Bindings& binding);
~EmitContext();
enum class PointerType : u32 {
U8,
U16,
F16,
U32,
F32,
U64,
F64,
NumAlias,
};
Id Def(const IR::Value& value);
void DefineBufferProperties();
@ -155,25 +144,7 @@ public:
return last_label;
}
PointerType PointerTypeFromType(Id type) {
if (type.value == U8.value)
return PointerType::U8;
if (type.value == U16.value)
return PointerType::U16;
if (type.value == F16[1].value)
return PointerType::F16;
if (type.value == U32[1].value)
return PointerType::U32;
if (type.value == F32[1].value)
return PointerType::F32;
if (type.value == U64.value)
return PointerType::U64;
if (type.value == F64[1].value)
return PointerType::F64;
UNREACHABLE_MSG("Unknown type for pointer");
}
Id EmitMemoryRead(Id type, Id address, auto&& fallback) {
Id EmitDwordMemoryRead(Id address, auto&& fallback) {
const Id available_label = OpLabel();
const Id fallback_label = OpLabel();
const Id merge_label = OpLabel();
@ -185,10 +156,8 @@ public:
// Available
AddLabel(available_label);
const auto pointer_type = PointerTypeFromType(type);
const Id pointer_type_id = physical_pointer_types[pointer_type];
const Id addr_ptr = OpConvertUToPtr(pointer_type_id, addr);
const Id result = OpLoad(type, addr_ptr, spv::MemoryAccessMask::Aligned, 4u);
const Id addr_ptr = OpConvertUToPtr(physical_pointer_type_u32, addr);
const Id result = OpLoad(U32[1], addr_ptr, spv::MemoryAccessMask::Aligned, 4u);
OpBranch(merge_label);
// Fallback
@ -199,7 +168,7 @@ public:
// Merge
AddLabel(merge_label);
const Id final_result =
OpPhi(type, fallback_result, fallback_label, result, available_label);
OpPhi(U32[1], fallback_result, fallback_label, result, available_label);
return final_result;
}
@ -211,6 +180,16 @@ public:
return OpAccessChain(result_type, shared_mem, index);
}
Id EmitFlatbufferLoad(Id flatbuf_offset) {
const auto& flatbuf_buffer{buffers[flatbuf_index]};
ASSERT(flatbuf_buffer.binding >= 0 && flatbuf_buffer.buffer_type == BufferType::Flatbuf);
const auto [flatbuf_buffer_id, flatbuf_pointer_type] =
flatbuf_buffer.aliases[u32(PointerType::U32)];
const auto ptr{
OpAccessChain(flatbuf_pointer_type, flatbuf_buffer_id, u32_zero_value, flatbuf_offset)};
return OpLoad(U32[1], ptr);
}
Info& info;
const RuntimeInfo& runtime_info;
const Profile& profile;
@ -314,6 +293,24 @@ public:
bool is_storage = false;
};
enum class PointerType : u32 {
U8,
U16,
U32,
F32,
U64,
F64,
NumAlias,
};
enum class PointerSize : u32 {
B8,
B16,
B32,
B64,
NumClass,
};
struct BufferSpv {
Id id;
Id pointer_type;
@ -322,32 +319,23 @@ public:
struct BufferDefinition {
u32 binding;
BufferType buffer_type;
Id offset;
Id offset_dwords;
Id size;
Id size_shorts;
Id size_dwords;
Id size_qwords;
std::array<Id, u32(PointerSize::NumClass)> offsets;
std::array<Id, u32(PointerSize::NumClass)> sizes;
std::array<BufferSpv, u32(PointerType::NumAlias)> aliases;
const BufferSpv& operator[](PointerType alias) const {
return aliases[u32(alias)];
template <class Self>
auto& Alias(this Self& self, PointerType alias) {
return self.aliases[u32(alias)];
}
BufferSpv& operator[](PointerType alias) {
return aliases[u32(alias)];
}
};
struct PhysicalPointerTypes {
std::array<Id, u32(PointerType::NumAlias)> types;
const Id& operator[](PointerType type) const {
return types[u32(type)];
template <class Self>
auto& Offset(this Self& self, PointerSize size) {
return self.offsets[u32(size)];
}
Id& operator[](PointerType type) {
return types[u32(type)];
template <class Self>
auto& Size(this Self& self, PointerSize size) {
return self.sizes[u32(size)];
}
};
@ -356,12 +344,12 @@ public:
boost::container::small_vector<BufferDefinition, 16> buffers;
boost::container::small_vector<TextureDefinition, 8> images;
boost::container::small_vector<Id, 4> samplers;
PhysicalPointerTypes physical_pointer_types;
std::unordered_map<u32, Id> first_to_last_label_map;
size_t flatbuf_index{};
size_t bda_pagetable_index{};
size_t fault_buffer_index{};
Id physical_pointer_type_u32;
Id sampler_type{};
Id sampler_pointer_type{};

View File

@ -188,14 +188,15 @@ void CFG::SplitDivergenceScopes() {
const bool is_close = is_close_scope(inst);
if ((is_close || index == blk->end_index) && curr_begin != -1) {
// If there are no instructions inside scope don't do anything.
if (index - curr_begin == 1) {
if (index - curr_begin == 1 && is_close) {
curr_begin = -1;
continue;
}
// If all instructions in the scope ignore exec masking, we shouldn't insert a
// scope.
const auto start = inst_list.begin() + curr_begin + 1;
if (!std::ranges::all_of(start, inst_list.begin() + index, IgnoresExecMask)) {
if (!std::ranges::all_of(start, inst_list.begin() + index + !is_close,
IgnoresExecMask)) {
// Determine the first instruction affected by the exec mask.
do {
++curr_begin;

View File

@ -397,7 +397,7 @@ constexpr std::array<InstFormat, 27> InstructionFormatSOPP = {{
// 17 = S_SENDMSGHALT
{InstClass::ScalarProgFlow, InstCategory::FlowControl, 0, 1, ScalarType::Any, ScalarType::Any},
// 18 = S_TRAP
{InstClass::Undefined, InstCategory::Undefined, 0, 1, ScalarType::Any, ScalarType::Any},
{InstClass::Undefined, InstCategory::FlowControl, 0, 1, ScalarType::Any, ScalarType::Any},
// 19 = S_ICACHE_INV
{InstClass::ScalarCache, InstCategory::FlowControl, 0, 1, ScalarType::Any, ScalarType::Any},
// 20 = S_INCPERFLEVEL

View File

@ -3,7 +3,6 @@
#include "shader_recompiler/frontend/translate/translate.h"
#include "shader_recompiler/ir/reg.h"
#include "shader_recompiler/profile.h"
#include "shader_recompiler/runtime_info.h"
namespace Shader::Gcn {
@ -12,29 +11,29 @@ void Translator::EmitDataShare(const GcnInst& inst) {
switch (inst.opcode) {
// DS
case Opcode::DS_ADD_U32:
return DS_ADD_U32(inst, false);
return DS_OP(inst, AtomicOp::Add, false);
case Opcode::DS_ADD_U64:
return DS_ADD_U64(inst, false);
return DS_OP<IR::U64>(inst, AtomicOp::Add, false);
case Opcode::DS_SUB_U32:
return DS_SUB_U32(inst, false);
return DS_OP(inst, AtomicOp::Sub, false);
case Opcode::DS_INC_U32:
return DS_INC_U32(inst, false);
return DS_OP(inst, AtomicOp::Inc, false);
case Opcode::DS_DEC_U32:
return DS_DEC_U32(inst, false);
return DS_OP(inst, AtomicOp::Dec, false);
case Opcode::DS_MIN_I32:
return DS_MIN_U32(inst, true, false);
return DS_OP(inst, AtomicOp::Smin, false);
case Opcode::DS_MAX_I32:
return DS_MAX_U32(inst, true, false);
return DS_OP(inst, AtomicOp::Smax, false);
case Opcode::DS_MIN_U32:
return DS_MIN_U32(inst, false, false);
return DS_OP(inst, AtomicOp::Umin, false);
case Opcode::DS_MAX_U32:
return DS_MAX_U32(inst, false, false);
return DS_OP(inst, AtomicOp::Umax, false);
case Opcode::DS_AND_B32:
return DS_AND_B32(inst, false);
return DS_OP(inst, AtomicOp::And, false);
case Opcode::DS_OR_B32:
return DS_OR_B32(inst, false);
return DS_OP(inst, AtomicOp::Or, false);
case Opcode::DS_XOR_B32:
return DS_XOR_B32(inst, false);
return DS_OP(inst, AtomicOp::Xor, false);
case Opcode::DS_WRITE_B32:
return DS_WRITE(32, false, false, false, inst);
case Opcode::DS_WRITE2_B32:
@ -42,19 +41,19 @@ void Translator::EmitDataShare(const GcnInst& inst) {
case Opcode::DS_WRITE2ST64_B32:
return DS_WRITE(32, false, true, true, inst);
case Opcode::DS_ADD_RTN_U32:
return DS_ADD_U32(inst, true);
return DS_OP(inst, AtomicOp::Add, true);
case Opcode::DS_SUB_RTN_U32:
return DS_SUB_U32(inst, true);
return DS_OP(inst, AtomicOp::Sub, true);
case Opcode::DS_MIN_RTN_U32:
return DS_MIN_U32(inst, false, true);
return DS_OP(inst, AtomicOp::Umin, true);
case Opcode::DS_MAX_RTN_U32:
return DS_MAX_U32(inst, false, true);
return DS_OP(inst, AtomicOp::Umax, true);
case Opcode::DS_AND_RTN_B32:
return DS_AND_B32(inst, true);
return DS_OP(inst, AtomicOp::And, true);
case Opcode::DS_OR_RTN_B32:
return DS_OR_B32(inst, true);
return DS_OP(inst, AtomicOp::Or, true);
case Opcode::DS_XOR_RTN_B32:
return DS_XOR_B32(inst, true);
return DS_OP(inst, AtomicOp::Xor, true);
case Opcode::DS_SWIZZLE_B32:
return DS_SWIZZLE_B32(inst);
case Opcode::DS_READ_B32:
@ -117,92 +116,63 @@ void Translator::V_WRITELANE_B32(const GcnInst& inst) {
// DS
void Translator::DS_ADD_U32(const GcnInst& inst, bool rtn) {
template <typename T>
void Translator::DS_OP(const GcnInst& inst, AtomicOp op, bool rtn) {
const bool is_gds = inst.control.ds.gds;
const IR::U32 addr{GetSrc(inst.src[0])};
const IR::U32 data{GetSrc(inst.src[1])};
const T data = [&] {
if (op == AtomicOp::Inc || op == AtomicOp::Dec) {
return T{};
}
if constexpr (std::is_same_v<T, IR::U32>) {
return GetSrc(inst.src[1]);
} else {
return GetSrc64(inst.src[1]);
}
}();
const IR::U32 offset =
ir.Imm32((u32(inst.control.ds.offset1) << 8u) + u32(inst.control.ds.offset0));
const IR::U32 addr_offset = ir.IAdd(addr, offset);
const IR::Value original_val = ir.SharedAtomicIAdd(addr_offset, data);
const T original_val = [&] -> T {
switch (op) {
case AtomicOp::Add:
return ir.SharedAtomicIAdd(addr_offset, data, is_gds);
case AtomicOp::Umin:
return ir.SharedAtomicIMin(addr_offset, data, false, is_gds);
case AtomicOp::Smin:
return ir.SharedAtomicIMin(addr_offset, data, true, is_gds);
case AtomicOp::Umax:
return ir.SharedAtomicIMax(addr_offset, data, false, is_gds);
case AtomicOp::Smax:
return ir.SharedAtomicIMax(addr_offset, data, true, is_gds);
case AtomicOp::And:
return ir.SharedAtomicAnd(addr_offset, data, is_gds);
case AtomicOp::Or:
return ir.SharedAtomicOr(addr_offset, data, is_gds);
case AtomicOp::Xor:
return ir.SharedAtomicXor(addr_offset, data, is_gds);
case AtomicOp::Sub:
return ir.SharedAtomicISub(addr_offset, data, is_gds);
case AtomicOp::Inc:
return ir.SharedAtomicInc<T>(addr_offset, is_gds);
case AtomicOp::Dec:
return ir.SharedAtomicDec<T>(addr_offset, is_gds);
default:
UNREACHABLE();
}
}();
if (rtn) {
SetDst(inst.dst[0], IR::U32{original_val});
}
}
void Translator::DS_ADD_U64(const GcnInst& inst, bool rtn) {
const IR::U32 addr{GetSrc(inst.src[0])};
const IR::U64 data{GetSrc64(inst.src[1])};
const IR::U32 offset =
ir.Imm32((u32(inst.control.ds.offset1) << 8u) + u32(inst.control.ds.offset0));
const IR::U32 addr_offset = ir.IAdd(addr, offset);
const IR::Value original_val = ir.SharedAtomicIAdd(addr_offset, data);
if (rtn) {
SetDst64(inst.dst[0], IR::U64{original_val});
}
}
void Translator::DS_MIN_U32(const GcnInst& inst, bool is_signed, bool rtn) {
const IR::U32 addr{GetSrc(inst.src[0])};
const IR::U32 data{GetSrc(inst.src[1])};
const IR::U32 offset =
ir.Imm32((u32(inst.control.ds.offset1) << 8u) + u32(inst.control.ds.offset0));
const IR::U32 addr_offset = ir.IAdd(addr, offset);
const IR::Value original_val = ir.SharedAtomicIMin(addr_offset, data, is_signed);
if (rtn) {
SetDst(inst.dst[0], IR::U32{original_val});
}
}
void Translator::DS_MAX_U32(const GcnInst& inst, bool is_signed, bool rtn) {
const IR::U32 addr{GetSrc(inst.src[0])};
const IR::U32 data{GetSrc(inst.src[1])};
const IR::U32 offset =
ir.Imm32((u32(inst.control.ds.offset1) << 8u) + u32(inst.control.ds.offset0));
const IR::U32 addr_offset = ir.IAdd(addr, offset);
const IR::Value original_val = ir.SharedAtomicIMax(addr_offset, data, is_signed);
if (rtn) {
SetDst(inst.dst[0], IR::U32{original_val});
}
}
void Translator::DS_AND_B32(const GcnInst& inst, bool rtn) {
const IR::U32 addr{GetSrc(inst.src[0])};
const IR::U32 data{GetSrc(inst.src[1])};
const IR::U32 offset =
ir.Imm32((u32(inst.control.ds.offset1) << 8u) + u32(inst.control.ds.offset0));
const IR::U32 addr_offset = ir.IAdd(addr, offset);
const IR::Value original_val = ir.SharedAtomicAnd(addr_offset, data);
if (rtn) {
SetDst(inst.dst[0], IR::U32{original_val});
}
}
void Translator::DS_OR_B32(const GcnInst& inst, bool rtn) {
const IR::U32 addr{GetSrc(inst.src[0])};
const IR::U32 data{GetSrc(inst.src[1])};
const IR::U32 offset =
ir.Imm32((u32(inst.control.ds.offset1) << 8u) + u32(inst.control.ds.offset0));
const IR::U32 addr_offset = ir.IAdd(addr, offset);
const IR::Value original_val = ir.SharedAtomicOr(addr_offset, data);
if (rtn) {
SetDst(inst.dst[0], IR::U32{original_val});
}
}
void Translator::DS_XOR_B32(const GcnInst& inst, bool rtn) {
const IR::U32 addr{GetSrc(inst.src[0])};
const IR::U32 data{GetSrc(inst.src[1])};
const IR::U32 offset =
ir.Imm32((u32(inst.control.ds.offset1) << 8u) + u32(inst.control.ds.offset0));
const IR::U32 addr_offset = ir.IAdd(addr, offset);
const IR::Value original_val = ir.SharedAtomicXor(addr_offset, data);
if (rtn) {
SetDst(inst.dst[0], IR::U32{original_val});
if constexpr (std::is_same_v<T, IR::U32>) {
SetDst(inst.dst[0], original_val);
} else {
SetDst64(inst.dst[0], original_val);
}
}
}
void Translator::DS_WRITE(int bit_size, bool is_signed, bool is_pair, bool stride64,
const GcnInst& inst) {
const bool is_gds = inst.control.ds.gds;
const IR::U32 addr{ir.GetVectorReg(IR::VectorReg(inst.src[0].code))};
const IR::VectorReg data0{inst.src[1].code};
const IR::VectorReg data1{inst.src[2].code};
@ -220,33 +190,85 @@ void Translator::DS_WRITE(int bit_size, bool is_signed, bool is_pair, bool strid
ir.WriteShared(64,
ir.PackUint2x32(ir.CompositeConstruct(ir.GetVectorReg(data0),
ir.GetVectorReg(data0 + 1))),
addr0);
addr0, is_gds);
} else if (bit_size == 32) {
ir.WriteShared(32, ir.GetVectorReg(data0), addr0);
ir.WriteShared(32, ir.GetVectorReg(data0), addr0, is_gds);
} else if (bit_size == 16) {
ir.WriteShared(16, ir.UConvert(16, ir.GetVectorReg(data0)), addr0);
ir.WriteShared(16, ir.UConvert(16, ir.GetVectorReg(data0)), addr0, is_gds);
}
const IR::U32 addr1 = ir.IAdd(addr, ir.Imm32(u32(inst.control.ds.offset1 * adj)));
if (bit_size == 64) {
ir.WriteShared(64,
ir.PackUint2x32(ir.CompositeConstruct(ir.GetVectorReg(data1),
ir.GetVectorReg(data1 + 1))),
addr1);
addr1, is_gds);
} else if (bit_size == 32) {
ir.WriteShared(32, ir.GetVectorReg(data1), addr1);
ir.WriteShared(32, ir.GetVectorReg(data1), addr1, is_gds);
} else if (bit_size == 16) {
ir.WriteShared(16, ir.UConvert(16, ir.GetVectorReg(data1)), addr1);
ir.WriteShared(16, ir.UConvert(16, ir.GetVectorReg(data1)), addr1, is_gds);
}
} else {
const IR::U32 addr0 = ir.IAdd(addr, ir.Imm32(offset));
if (bit_size == 64) {
const IR::Value data =
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, is_gds);
} else if (bit_size == 32) {
ir.WriteShared(bit_size, ir.GetVectorReg(data0), addr0);
ir.WriteShared(bit_size, ir.GetVectorReg(data0), addr0, is_gds);
} else if (bit_size == 16) {
ir.WriteShared(bit_size, ir.UConvert(16, ir.GetVectorReg(data0)), addr0);
ir.WriteShared(bit_size, ir.UConvert(16, ir.GetVectorReg(data0)), addr0, is_gds);
}
}
}
void Translator::DS_READ(int bit_size, bool is_signed, bool is_pair, bool stride64,
const GcnInst& inst) {
const bool is_gds = inst.control.ds.gds;
const IR::U32 addr{ir.GetVectorReg(IR::VectorReg(inst.src[0].code))};
IR::VectorReg dst_reg{inst.dst[0].code};
const u32 offset = (inst.control.ds.offset1 << 8u) + inst.control.ds.offset0;
if (info.stage == Stage::Fragment) {
ASSERT_MSG(!is_pair && bit_size == 32 && offset % 256 == 0,
"Unexpected shared memory offset alignment: {}", offset);
ir.SetVectorReg(dst_reg, ir.GetVectorReg(GetScratchVgpr(offset)));
return;
}
if (is_pair) {
// Pair loads are either 32 or 64-bit
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::Value data0 = ir.LoadShared(bit_size, is_signed, addr0, is_gds);
if (bit_size == 64) {
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, 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::Value data1 = ir.LoadShared(bit_size, is_signed, addr1, is_gds);
if (bit_size == 64) {
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, 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 {
const IR::U32 addr0 = ir.IAdd(addr, ir.Imm32(offset));
const IR::Value data = ir.LoadShared(bit_size, is_signed, addr0, is_gds);
if (bit_size == 64) {
const auto vector = ir.UnpackUint2x32(IR::U64{data});
ir.SetVectorReg(dst_reg, IR::U32{ir.CompositeExtract(vector, 0)});
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) {
ir.SetVectorReg(dst_reg++, IR::U32{ir.UConvert(32, IR::U16{data})});
}
}
}
@ -263,91 +285,6 @@ void Translator::DS_SWIZZLE_B32(const GcnInst& inst) {
SetDst(inst.dst[0], ir.QuadShuffle(src, index));
}
void Translator::DS_INC_U32(const GcnInst& inst, bool rtn) {
const IR::U32 addr{GetSrc(inst.src[0])};
const IR::U32 offset =
ir.Imm32((u32(inst.control.ds.offset1) << 8u) + u32(inst.control.ds.offset0));
const IR::U32 addr_offset = ir.IAdd(addr, offset);
const IR::Value original_val = ir.SharedAtomicInc(addr_offset);
if (rtn) {
SetDst(inst.dst[0], IR::U32{original_val});
}
}
void Translator::DS_DEC_U32(const GcnInst& inst, bool rtn) {
const IR::U32 addr{GetSrc(inst.src[0])};
const IR::U32 offset =
ir.Imm32((u32(inst.control.ds.offset1) << 8u) + u32(inst.control.ds.offset0));
const IR::U32 addr_offset = ir.IAdd(addr, offset);
const IR::Value original_val = ir.SharedAtomicDec(addr_offset);
if (rtn) {
SetDst(inst.dst[0], IR::U32{original_val});
}
}
void Translator::DS_SUB_U32(const GcnInst& inst, bool rtn) {
const IR::U32 addr{GetSrc(inst.src[0])};
const IR::U32 data{GetSrc(inst.src[1])};
const IR::U32 offset =
ir.Imm32((u32(inst.control.ds.offset1) << 8u) + u32(inst.control.ds.offset0));
const IR::U32 addr_offset = ir.IAdd(addr, offset);
const IR::Value original_val = ir.SharedAtomicISub(addr_offset, data);
if (rtn) {
SetDst(inst.dst[0], IR::U32{original_val});
}
}
void Translator::DS_READ(int bit_size, bool is_signed, bool is_pair, bool stride64,
const GcnInst& inst) {
const IR::U32 addr{ir.GetVectorReg(IR::VectorReg(inst.src[0].code))};
IR::VectorReg dst_reg{inst.dst[0].code};
const u32 offset = (inst.control.ds.offset1 << 8u) + inst.control.ds.offset0;
if (info.stage == Stage::Fragment) {
ASSERT_MSG(!is_pair && bit_size == 32 && offset % 256 == 0,
"Unexpected shared memory offset alignment: {}", offset);
ir.SetVectorReg(dst_reg, ir.GetVectorReg(GetScratchVgpr(offset)));
return;
}
if (is_pair) {
// Pair loads are either 32 or 64-bit
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::Value data0 = ir.LoadShared(bit_size, is_signed, addr0);
if (bit_size == 64) {
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, 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::Value data1 = ir.LoadShared(bit_size, is_signed, addr1);
if (bit_size == 64) {
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, 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 {
const IR::U32 addr0 = ir.IAdd(addr, ir.Imm32(offset));
const IR::Value data = ir.LoadShared(bit_size, is_signed, addr0);
if (bit_size == 64) {
const auto vector = ir.UnpackUint2x32(IR::U64{data});
ir.SetVectorReg(dst_reg, IR::U32{ir.CompositeExtract(vector, 0)});
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) {
ir.SetVectorReg(dst_reg++, IR::U32{ir.UConvert(32, IR::U16{data})});
}
}
}
void Translator::DS_APPEND(const GcnInst& inst) {
const u32 inst_offset = (u32(inst.control.ds.offset1) << 8u) + inst.control.ds.offset0;
const IR::U32 gds_offset = ir.IAdd(ir.GetM0(), ir.Imm32(inst_offset));

View File

@ -1,7 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <bit>
#include "common/assert.h"
#include "shader_recompiler/frontend/translate/translate.h"
@ -587,6 +586,15 @@ void Translator::S_MOV(const GcnInst& inst) {
}
void Translator::S_MOV_B64(const GcnInst& inst) {
// Moving SGPR to SGPR is used for thread masks, like most operations, but it can also be used
// for moving sharps.
if (inst.dst[0].field == OperandField::ScalarGPR &&
inst.src[0].field == OperandField::ScalarGPR) {
ir.SetScalarReg(IR::ScalarReg(inst.dst[0].code),
ir.GetScalarReg(IR::ScalarReg(inst.src[0].code)));
ir.SetScalarReg(IR::ScalarReg(inst.dst[0].code + 1),
ir.GetScalarReg(IR::ScalarReg(inst.src[0].code + 1)));
}
const IR::U1 src = [&] {
switch (inst.src[0].field) {
case OperandField::VccLo:
@ -672,8 +680,9 @@ void Translator::S_FF1_I32_B32(const GcnInst& inst) {
}
void Translator::S_FF1_I32_B64(const GcnInst& inst) {
const IR::U64 src0{GetSrc64(inst.src[0])};
const IR::U32 result{ir.FindILsb(src0)};
ASSERT(inst.src[0].field == OperandField::ScalarGPR);
const IR::U32 result{
ir.BallotFindLsb(ir.Ballot(ir.GetThreadBitScalarReg(IR::ScalarReg(inst.src[0].code))))};
SetDst(inst.dst[0], result);
}

View File

@ -16,6 +16,9 @@ void Translator::EmitFlowControl(u32 pc, const GcnInst& inst) {
case Opcode::S_SETPRIO:
LOG_WARNING(Render_Vulkan, "S_SETPRIO instruction!");
return;
case Opcode::S_TRAP:
LOG_WARNING(Render_Vulkan, "S_TRAP instruction!");
return;
case Opcode::S_GETPC_B64:
return S_GETPC_B64(pc, inst);
case Opcode::S_SETPC_B64:

View File

@ -20,7 +20,7 @@ namespace Shader::Gcn {
enum class ConditionOp : u32 {
F,
EQ,
LG,
LG, // NE
GT,
GE,
LT,
@ -230,7 +230,7 @@ public:
// VOPC
void V_CMP_F32(ConditionOp op, bool set_exec, const GcnInst& inst);
void V_CMP_U32(ConditionOp op, bool is_signed, bool set_exec, const GcnInst& inst);
void V_CMP_NE_U64(const GcnInst& inst);
void V_CMP_U64(ConditionOp op, bool is_signed, bool set_exec, const GcnInst& inst);
void V_CMP_CLASS_F32(const GcnInst& inst);
// VOP3a
@ -270,27 +270,20 @@ public:
// Data share
// DS
void DS_ADD_U32(const GcnInst& inst, bool rtn);
void DS_ADD_U64(const GcnInst& inst, bool rtn);
void DS_MIN_U32(const GcnInst& inst, bool is_signed, bool rtn);
void DS_MAX_U32(const GcnInst& inst, bool is_signed, bool rtn);
template <typename T = IR::U32>
void DS_OP(const GcnInst& inst, AtomicOp op, bool rtn);
void DS_WRITE(int bit_size, bool is_signed, bool is_pair, bool stride64, const GcnInst& inst);
void DS_SWIZZLE_B32(const GcnInst& inst);
void DS_AND_B32(const GcnInst& inst, bool rtn);
void DS_OR_B32(const GcnInst& inst, bool rtn);
void DS_XOR_B32(const GcnInst& inst, bool rtn);
void DS_READ(int bit_size, bool is_signed, bool is_pair, bool stride64, const GcnInst& inst);
void DS_SWIZZLE_B32(const GcnInst& inst);
void DS_APPEND(const GcnInst& inst);
void DS_CONSUME(const GcnInst& inst);
void DS_SUB_U32(const GcnInst& inst, bool rtn);
void DS_INC_U32(const GcnInst& inst, bool rtn);
void DS_DEC_U32(const GcnInst& inst, bool rtn);
// Buffer Memory
// MUBUF / MTBUF
void BUFFER_LOAD(u32 num_dwords, bool is_inst_typed, bool is_buffer_typed, const GcnInst& inst);
void BUFFER_STORE(u32 num_dwords, bool is_inst_typed, bool is_buffer_typed,
const GcnInst& inst);
template <typename T = IR::U32>
void BUFFER_ATOMIC(AtomicOp op, const GcnInst& inst);
// Image Memory

View File

@ -327,8 +327,10 @@ void Translator::EmitVectorAlu(const GcnInst& inst) {
return V_CMP_U32(ConditionOp::TRU, false, true, inst);
// V_CMP_{OP8}_U64
case Opcode::V_CMP_EQ_U64:
return V_CMP_U64(ConditionOp::EQ, false, false, inst);
case Opcode::V_CMP_NE_U64:
return V_CMP_NE_U64(inst);
return V_CMP_U64(ConditionOp::LG, false, false, inst);
case Opcode::V_CMP_CLASS_F32:
return V_CMP_CLASS_F32(inst);
@ -556,27 +558,33 @@ void Translator::V_BCNT_U32_B32(const GcnInst& inst) {
void Translator::V_MBCNT_U32_B32(bool is_low, const GcnInst& inst) {
if (!is_low) {
// v_mbcnt_hi_u32_b32 v2, -1, 0
// v_mbcnt_hi_u32_b32 vX, -1, 0
if (inst.src[0].field == OperandField::SignedConstIntNeg && inst.src[0].code == 193 &&
inst.src[1].field == OperandField::ConstZero) {
return;
}
// v_mbcnt_hi_u32_b32 vX, exec_hi, 0
if (inst.src[0].field == OperandField::ExecHi &&
inst.src[1].field == OperandField::ConstZero) {
return;
// v_mbcnt_hi_u32_b32 vX, exec_hi, 0/vZ
if ((inst.src[0].field == OperandField::ExecHi ||
inst.src[0].field == OperandField::VccHi ||
inst.src[0].field == OperandField::ScalarGPR) &&
(inst.src[1].field == OperandField::ConstZero ||
inst.src[1].field == OperandField::VectorGPR)) {
return SetDst(inst.dst[0], GetSrc(inst.src[1]));
}
UNREACHABLE();
} else {
// v_mbcnt_lo_u32_b32 v2, -1, vX
// v_mbcnt_lo_u32_b32 vY, -1, vX
// used combined with above to fetch lane id in non-compute stages
if (inst.src[0].field == OperandField::SignedConstIntNeg && inst.src[0].code == 193) {
SetDst(inst.dst[0], ir.LaneId());
return SetDst(inst.dst[0], ir.LaneId());
}
// v_mbcnt_lo_u32_b32 v20, exec_lo, vX
// used combined in above for append buffer indexing.
if (inst.src[0].field == OperandField::ExecLo) {
SetDst(inst.dst[0], ir.Imm32(0));
// v_mbcnt_lo_u32_b32 vY, exec_lo, vX
// used combined with above for append buffer indexing.
if (inst.src[0].field == OperandField::ExecLo || inst.src[0].field == OperandField::VccLo ||
inst.src[0].field == OperandField::ScalarGPR) {
return SetDst(inst.dst[0], GetSrc(inst.src[1]));
}
UNREACHABLE();
}
}
@ -617,12 +625,15 @@ void Translator::V_ADDC_U32(const GcnInst& inst) {
const IR::U32 src0{GetSrc(inst.src[0])};
const IR::U32 src1{GetSrc(inst.src[1])};
const IR::U32 carry{GetCarryIn(inst)};
const IR::U32 result{ir.IAdd(ir.IAdd(src0, src1), carry)};
SetDst(inst.dst[0], result);
const IR::Value tmp1{ir.IAddCarry(src0, src1)};
const IR::U32 result1{ir.CompositeExtract(tmp1, 0)};
const IR::U32 carry_out1{ir.CompositeExtract(tmp1, 1)};
const IR::Value tmp2{ir.IAddCarry(result1, carry)};
const IR::U32 result2{ir.CompositeExtract(tmp2, 0)};
const IR::U32 carry_out2{ir.CompositeExtract(tmp2, 1)};
SetDst(inst.dst[0], result2);
const IR::U1 less_src0{ir.ILessThan(result, src0, false)};
const IR::U1 less_src1{ir.ILessThan(result, src1, false)};
const IR::U1 did_overflow{ir.LogicalOr(less_src0, less_src1)};
const IR::U1 did_overflow{ir.INotEqual(ir.BitwiseOr(carry_out1, carry_out2), ir.Imm32(0))};
SetCarryOut(inst, did_overflow);
}
@ -996,39 +1007,32 @@ void Translator::V_CMP_U32(ConditionOp op, bool is_signed, bool set_exec, const
}
}
void Translator::V_CMP_NE_U64(const GcnInst& inst) {
const auto get_src = [&](const InstOperand& operand) {
switch (operand.field) {
case OperandField::VccLo:
return ir.GetVcc();
case OperandField::ExecLo:
return ir.GetExec();
case OperandField::ScalarGPR:
return ir.GetThreadBitScalarReg(IR::ScalarReg(operand.code));
case OperandField::ConstZero:
return ir.Imm1(false);
void Translator::V_CMP_U64(ConditionOp op, bool is_signed, bool set_exec, const GcnInst& inst) {
const IR::U64 src0{GetSrc64(inst.src[0])};
const IR::U64 src1{GetSrc64(inst.src[1])};
const IR::U1 result = [&] {
switch (op) {
case ConditionOp::EQ:
return ir.IEqual(src0, src1);
case ConditionOp::LG: // NE
return ir.INotEqual(src0, src1);
default:
UNREACHABLE();
UNREACHABLE_MSG("Unsupported V_CMP_U64 condition operation: {}", u32(op));
}
};
const IR::U1 src0{get_src(inst.src[0])};
auto op = [&inst, this](auto x) {
switch (inst.src[1].field) {
case OperandField::ConstZero:
return x;
case OperandField::SignedConstIntNeg:
return ir.LogicalNot(x);
default:
UNREACHABLE_MSG("unhandled V_CMP_NE_U64 source argument {}", u32(inst.src[1].field));
}
};
}();
if (is_signed) {
UNREACHABLE_MSG("V_CMP_U64 with signed integers is not supported");
}
if (set_exec) {
UNREACHABLE_MSG("Exec setting for V_CMP_U64 is not supported");
}
switch (inst.dst[1].field) {
case OperandField::VccLo:
ir.SetVcc(op(src0));
break;
return ir.SetVcc(result);
case OperandField::ScalarGPR:
ir.SetThreadBitScalarReg(IR::ScalarReg(inst.dst[1].code), op(src0));
break;
return ir.SetThreadBitScalarReg(IR::ScalarReg(inst.dst[1].code), result);
default:
UNREACHABLE();
}

View File

@ -74,12 +74,20 @@ void Translator::EmitVectorMemory(const GcnInst& inst) {
return BUFFER_ATOMIC(AtomicOp::CmpSwap, inst);
case Opcode::BUFFER_ATOMIC_SMIN:
return BUFFER_ATOMIC(AtomicOp::Smin, inst);
case Opcode::BUFFER_ATOMIC_SMIN_X2:
return BUFFER_ATOMIC<IR::U64>(AtomicOp::Smin, inst);
case Opcode::BUFFER_ATOMIC_UMIN:
return BUFFER_ATOMIC(AtomicOp::Umin, inst);
case Opcode::BUFFER_ATOMIC_UMIN_X2:
return BUFFER_ATOMIC<IR::U64>(AtomicOp::Umin, inst);
case Opcode::BUFFER_ATOMIC_SMAX:
return BUFFER_ATOMIC(AtomicOp::Smax, inst);
case Opcode::BUFFER_ATOMIC_SMAX_X2:
return BUFFER_ATOMIC<IR::U64>(AtomicOp::Smax, inst);
case Opcode::BUFFER_ATOMIC_UMAX:
return BUFFER_ATOMIC(AtomicOp::Umax, inst);
case Opcode::BUFFER_ATOMIC_UMAX_X2:
return BUFFER_ATOMIC<IR::U64>(AtomicOp::Umax, inst);
case Opcode::BUFFER_ATOMIC_AND:
return BUFFER_ATOMIC(AtomicOp::And, inst);
case Opcode::BUFFER_ATOMIC_OR:
@ -184,9 +192,10 @@ void Translator::BUFFER_LOAD(u32 num_dwords, bool is_inst_typed, bool is_buffer_
const IR::VectorReg vaddr{inst.src[0].code};
const IR::ScalarReg sharp{inst.src[2].code * 4};
const IR::Value soffset{GetSrc(inst.src[3])};
const bool has_soffset = !soffset.IsImmediate() || soffset.U32() != 0;
if (info.stage != Stage::Geometry) {
ASSERT_MSG(soffset.IsImmediate() && soffset.U32() == 0,
"Non immediate offset not supported");
ASSERT_MSG(!has_soffset || !mubuf.offen,
"Having both scalar and vector offsets is not supported");
}
const IR::Value address = [&] -> IR::Value {
@ -196,15 +205,21 @@ void Translator::BUFFER_LOAD(u32 num_dwords, bool is_inst_typed, bool is_buffer_
if (mubuf.idxen && mubuf.offen) {
return ir.CompositeConstruct(ir.GetVectorReg(vaddr), ir.GetVectorReg(vaddr + 1));
}
if (mubuf.idxen && has_soffset) {
return ir.CompositeConstruct(ir.GetVectorReg(vaddr), soffset);
}
if (mubuf.idxen || mubuf.offen) {
return ir.GetVectorReg(vaddr);
}
if (has_soffset) {
return soffset;
}
return {};
}();
IR::BufferInstInfo buffer_info{};
buffer_info.index_enable.Assign(mubuf.idxen);
buffer_info.offset_enable.Assign(mubuf.offen);
buffer_info.offset_enable.Assign(mubuf.offen || has_soffset);
buffer_info.inst_offset.Assign(mubuf.offset);
buffer_info.globally_coherent.Assign(mubuf.glc);
buffer_info.system_coherent.Assign(mubuf.slc);
@ -304,6 +319,7 @@ void Translator::BUFFER_STORE(u32 num_dwords, bool is_inst_typed, bool is_buffer
}
}
template <typename T>
void Translator::BUFFER_ATOMIC(AtomicOp op, const GcnInst& inst) {
const auto& mubuf = inst.control.mubuf;
const IR::VectorReg vaddr{inst.src[0].code};
@ -328,7 +344,17 @@ void Translator::BUFFER_ATOMIC(AtomicOp op, const GcnInst& inst) {
buffer_info.globally_coherent.Assign(mubuf.glc);
buffer_info.system_coherent.Assign(mubuf.slc);
IR::Value vdata_val = ir.GetVectorReg<Shader::IR::U32>(vdata);
IR::Value vdata_val = [&] {
if constexpr (std::is_same_v<T, IR::U32>) {
return ir.GetVectorReg<Shader::IR::U32>(vdata);
} else if constexpr (std::is_same_v<T, IR::U64>) {
return ir.PackUint2x32(
ir.CompositeConstruct(ir.GetVectorReg<Shader::IR::U32>(vdata),
ir.GetVectorReg<Shader::IR::U32>(vdata + 1)));
} else {
static_assert(false, "buffer_atomic: type not supported");
}
}();
const IR::Value handle =
ir.CompositeConstruct(ir.GetScalarReg(srsrc), ir.GetScalarReg(srsrc + 1),
ir.GetScalarReg(srsrc + 2), ir.GetScalarReg(srsrc + 3));

View File

@ -25,7 +25,7 @@ namespace Shader {
static constexpr size_t NumUserDataRegs = 16;
static constexpr size_t NumImages = 64;
static constexpr size_t NumBuffers = 32;
static constexpr size_t NumBuffers = 40;
static constexpr size_t NumSamplers = 16;
static constexpr size_t NumFMasks = 8;
@ -225,6 +225,8 @@ struct Info {
bool uses_fp64{};
bool uses_pack_10_11_11{};
bool uses_unpack_10_11_11{};
bool uses_buffer_int64_atomics{};
bool uses_shared_int64_atomics{};
bool stores_tess_level_outer{};
bool stores_tess_level_inner{};
bool translation_failed{};
@ -238,7 +240,7 @@ struct Info {
Dynamic = 1 << 1,
};
ReadConstType readconst_types{};
IR::Type dma_types{IR::Type::Void};
bool uses_dma{false};
explicit Info(Stage stage_, LogicalStage l_stage_, ShaderParams params)
: stage{stage_}, l_stage{l_stage_}, pgm_hash{params.hash}, pgm_base{params.Base()},

View File

@ -291,78 +291,137 @@ void IREmitter::SetPatch(Patch patch, const F32& value) {
Inst(Opcode::SetPatch, patch, value);
}
Value IREmitter::LoadShared(int bit_size, bool is_signed, const U32& offset) {
Value IREmitter::LoadShared(int bit_size, bool is_signed, const U32& offset, bool is_gds) {
switch (bit_size) {
case 16:
return Inst<U16>(Opcode::LoadSharedU16, offset);
return Inst<U16>(Opcode::LoadSharedU16, Flags{is_gds}, offset);
case 32:
return Inst<U32>(Opcode::LoadSharedU32, offset);
return Inst<U32>(Opcode::LoadSharedU32, Flags{is_gds}, offset);
case 64:
return Inst<U64>(Opcode::LoadSharedU64, offset);
return Inst<U64>(Opcode::LoadSharedU64, Flags{is_gds}, offset);
default:
UNREACHABLE_MSG("Invalid bit size {}", bit_size);
}
}
void IREmitter::WriteShared(int bit_size, const Value& value, const U32& offset) {
void IREmitter::WriteShared(int bit_size, const Value& value, const U32& offset, bool is_gds) {
switch (bit_size) {
case 16:
Inst(Opcode::WriteSharedU16, offset, value);
Inst(Opcode::WriteSharedU16, Flags{is_gds}, offset, value);
break;
case 32:
Inst(Opcode::WriteSharedU32, offset, value);
Inst(Opcode::WriteSharedU32, Flags{is_gds}, offset, value);
break;
case 64:
Inst(Opcode::WriteSharedU64, offset, value);
Inst(Opcode::WriteSharedU64, Flags{is_gds}, offset, value);
break;
default:
UNREACHABLE_MSG("Invalid bit size {}", bit_size);
}
}
U32U64 IREmitter::SharedAtomicIAdd(const U32& address, const U32U64& data) {
U32U64 IREmitter::SharedAtomicIAdd(const U32& address, const U32U64& data, bool is_gds) {
switch (data.Type()) {
case Type::U32:
return Inst<U32>(Opcode::SharedAtomicIAdd32, address, data);
return Inst<U32>(Opcode::SharedAtomicIAdd32, Flags{is_gds}, address, data);
case Type::U64:
return Inst<U64>(Opcode::SharedAtomicIAdd64, address, data);
return Inst<U64>(Opcode::SharedAtomicIAdd64, Flags{is_gds}, address, data);
default:
ThrowInvalidType(data.Type());
}
}
U32 IREmitter::SharedAtomicIMin(const U32& address, const U32& data, bool is_signed) {
return is_signed ? Inst<U32>(Opcode::SharedAtomicSMin32, address, data)
: Inst<U32>(Opcode::SharedAtomicUMin32, address, data);
U32U64 IREmitter::SharedAtomicIMin(const U32& address, const U32U64& data, bool is_signed,
bool is_gds) {
switch (data.Type()) {
case Type::U32:
return Inst<U32>(is_signed ? Opcode::SharedAtomicSMin32 : Opcode::SharedAtomicUMin32,
Flags{is_gds}, address, data);
case Type::U64:
return Inst<U64>(is_signed ? Opcode::SharedAtomicSMin64 : Opcode::SharedAtomicUMin64,
Flags{is_gds}, address, data);
default:
ThrowInvalidType(data.Type());
}
}
U32 IREmitter::SharedAtomicIMax(const U32& address, const U32& data, bool is_signed) {
return is_signed ? Inst<U32>(Opcode::SharedAtomicSMax32, address, data)
: Inst<U32>(Opcode::SharedAtomicUMax32, address, data);
U32U64 IREmitter::SharedAtomicIMax(const U32& address, const U32U64& data, bool is_signed,
bool is_gds) {
switch (data.Type()) {
case Type::U32:
return Inst<U32>(is_signed ? Opcode::SharedAtomicSMax32 : Opcode::SharedAtomicUMax32,
Flags{is_gds}, address, data);
case Type::U64:
return Inst<U64>(is_signed ? Opcode::SharedAtomicSMax64 : Opcode::SharedAtomicUMax64,
Flags{is_gds}, address, data);
default:
ThrowInvalidType(data.Type());
}
}
U32 IREmitter::SharedAtomicAnd(const U32& address, const U32& data) {
return Inst<U32>(Opcode::SharedAtomicAnd32, address, data);
U32U64 IREmitter::SharedAtomicAnd(const U32& address, const U32U64& data, bool is_gds) {
switch (data.Type()) {
case Type::U32:
return Inst<U32>(Opcode::SharedAtomicAnd32, Flags{is_gds}, address, data);
case Type::U64:
return Inst<U64>(Opcode::SharedAtomicAnd64, Flags{is_gds}, address, data);
default:
ThrowInvalidType(data.Type());
}
}
U32 IREmitter::SharedAtomicOr(const U32& address, const U32& data) {
U32U64 IREmitter::SharedAtomicOr(const U32& address, const U32U64& data, bool is_gds) {
switch (data.Type()) {
case Type::U32:
return Inst<U32>(Opcode::SharedAtomicAnd32, Flags{is_gds}, address, data);
case Type::U64:
return Inst<U64>(Opcode::SharedAtomicAnd64, Flags{is_gds}, address, data);
default:
ThrowInvalidType(data.Type());
}
return Inst<U32>(Opcode::SharedAtomicOr32, address, data);
}
U32 IREmitter::SharedAtomicXor(const U32& address, const U32& data) {
return Inst<U32>(Opcode::SharedAtomicXor32, address, data);
U32U64 IREmitter::SharedAtomicXor(const U32& address, const U32U64& data, bool is_gds) {
switch (data.Type()) {
case Type::U32:
return Inst<U32>(Opcode::SharedAtomicXor32, Flags{is_gds}, address, data);
case Type::U64:
return Inst<U64>(Opcode::SharedAtomicXor64, Flags{is_gds}, address, data);
default:
ThrowInvalidType(data.Type());
}
}
U32 IREmitter::SharedAtomicInc(const U32& address) {
return Inst<U32>(Opcode::SharedAtomicInc32, address);
U32U64 IREmitter::SharedAtomicISub(const U32& address, const U32U64& data, bool is_gds) {
switch (data.Type()) {
case Type::U32:
return Inst<U32>(Opcode::SharedAtomicISub32, Flags{is_gds}, address, data);
case Type::U64:
return Inst<U64>(Opcode::SharedAtomicISub64, Flags{is_gds}, address, data);
default:
ThrowInvalidType(data.Type());
}
}
U32 IREmitter::SharedAtomicDec(const U32& address) {
return Inst<U32>(Opcode::SharedAtomicDec32, address);
template <>
U32 IREmitter::SharedAtomicInc(const U32& address, bool is_gds) {
return Inst<U32>(Opcode::SharedAtomicInc32, Flags{is_gds}, address);
}
U32 IREmitter::SharedAtomicISub(const U32& address, const U32& data) {
return Inst<U32>(Opcode::SharedAtomicISub32, address, data);
template <>
U64 IREmitter::SharedAtomicInc(const U32& address, bool is_gds) {
return Inst<U64>(Opcode::SharedAtomicInc64, Flags{is_gds}, address);
}
template <>
U32 IREmitter::SharedAtomicDec(const U32& address, bool is_gds) {
return Inst<U32>(Opcode::SharedAtomicDec32, Flags{is_gds}, address);
}
template <>
U64 IREmitter::SharedAtomicDec(const U32& address, bool is_gds) {
return Inst<U64>(Opcode::SharedAtomicDec64, Flags{is_gds}, address);
}
U32 IREmitter::ReadConst(const Value& base, const U32& offset) {
@ -500,8 +559,16 @@ Value IREmitter::BufferAtomicISub(const Value& handle, const Value& address, con
Value IREmitter::BufferAtomicIMin(const Value& handle, const Value& address, const Value& value,
bool is_signed, BufferInstInfo info) {
return is_signed ? Inst(Opcode::BufferAtomicSMin32, Flags{info}, handle, address, value)
: Inst(Opcode::BufferAtomicUMin32, Flags{info}, handle, address, value);
switch (value.Type()) {
case Type::U32:
return is_signed ? Inst(Opcode::BufferAtomicSMin32, Flags{info}, handle, address, value)
: Inst(Opcode::BufferAtomicUMin32, Flags{info}, handle, address, value);
case Type::U64:
return is_signed ? Inst(Opcode::BufferAtomicSMin64, Flags{info}, handle, address, value)
: Inst(Opcode::BufferAtomicUMin64, Flags{info}, handle, address, value);
default:
ThrowInvalidType(value.Type());
}
}
Value IREmitter::BufferAtomicFMin(const Value& handle, const Value& address, const Value& value,
@ -511,8 +578,16 @@ Value IREmitter::BufferAtomicFMin(const Value& handle, const Value& address, con
Value IREmitter::BufferAtomicIMax(const Value& handle, const Value& address, const Value& value,
bool is_signed, BufferInstInfo info) {
return is_signed ? Inst(Opcode::BufferAtomicSMax32, Flags{info}, handle, address, value)
: Inst(Opcode::BufferAtomicUMax32, Flags{info}, handle, address, value);
switch (value.Type()) {
case Type::U32:
return is_signed ? Inst(Opcode::BufferAtomicSMax32, Flags{info}, handle, address, value)
: Inst(Opcode::BufferAtomicUMax32, Flags{info}, handle, address, value);
case Type::U64:
return is_signed ? Inst(Opcode::BufferAtomicSMax64, Flags{info}, handle, address, value)
: Inst(Opcode::BufferAtomicUMax64, Flags{info}, handle, address, value);
default:
ThrowInvalidType(value.Type());
}
}
Value IREmitter::BufferAtomicFMax(const Value& handle, const Value& address, const Value& value,
@ -585,6 +660,14 @@ U32 IREmitter::WriteLane(const U32& value, const U32& write_value, const U32& la
return Inst<U32>(Opcode::WriteLane, value, write_value, lane);
}
Value IREmitter::Ballot(const U1& bit) {
return Inst(Opcode::Ballot, bit);
}
U32 IREmitter::BallotFindLsb(const Value& mask) {
return Inst<U32>(Opcode::BallotFindLsb, mask);
}
F32F64 IREmitter::FPAdd(const F32F64& a, const F32F64& b) {
if (a.Type() != b.Type()) {
UNREACHABLE_MSG("Mismatching types {} and {}", a.Type(), b.Type());
@ -1408,13 +1491,13 @@ U32U64 IREmitter::IAdd(const U32U64& a, const U32U64& b) {
}
}
Value IREmitter::IAddCary(const U32& a, const U32& b) {
Value IREmitter::IAddCarry(const U32& a, const U32& b) {
if (a.Type() != b.Type()) {
UNREACHABLE_MSG("Mismatching types {} and {}", a.Type(), b.Type());
}
switch (a.Type()) {
case Type::U32:
return Inst<U32>(Opcode::IAddCary32, a, b);
return Inst(Opcode::IAddCarry32, a, b);
default:
ThrowInvalidType(a.Type());
}
@ -1704,12 +1787,32 @@ U1 IREmitter::IEqual(const U32U64& lhs, const U32U64& rhs) {
}
}
U1 IREmitter::ILessThanEqual(const U32& lhs, const U32& rhs, bool is_signed) {
return Inst<U1>(is_signed ? Opcode::SLessThanEqual : Opcode::ULessThanEqual, lhs, rhs);
U1 IREmitter::ILessThanEqual(const U32U64& lhs, const U32U64& rhs, bool is_signed) {
if (lhs.Type() != rhs.Type()) {
UNREACHABLE_MSG("Mismatching types {} and {}", lhs.Type(), rhs.Type());
}
switch (lhs.Type()) {
case Type::U32:
return Inst<U1>(is_signed ? Opcode::SLessThanEqual32 : Opcode::ULessThanEqual32, lhs, rhs);
case Type::U64:
return Inst<U1>(is_signed ? Opcode::SLessThanEqual64 : Opcode::ULessThanEqual64, lhs, rhs);
default:
ThrowInvalidType(lhs.Type());
}
}
U1 IREmitter::IGreaterThan(const U32& lhs, const U32& rhs, bool is_signed) {
return Inst<U1>(is_signed ? Opcode::SGreaterThan : Opcode::UGreaterThan, lhs, rhs);
U1 IREmitter::IGreaterThan(const U32U64& lhs, const U32U64& rhs, bool is_signed) {
if (lhs.Type() != rhs.Type()) {
UNREACHABLE_MSG("Mismatching types {} and {}", lhs.Type(), rhs.Type());
}
switch (lhs.Type()) {
case Type::U32:
return Inst<U1>(is_signed ? Opcode::SGreaterThan32 : Opcode::UGreaterThan32, lhs, rhs);
case Type::U64:
return Inst<U1>(is_signed ? Opcode::SGreaterThan64 : Opcode::UGreaterThan64, lhs, rhs);
default:
ThrowInvalidType(lhs.Type());
}
}
U1 IREmitter::INotEqual(const U32U64& lhs, const U32U64& rhs) {
@ -1726,8 +1829,20 @@ U1 IREmitter::INotEqual(const U32U64& lhs, const U32U64& rhs) {
}
}
U1 IREmitter::IGreaterThanEqual(const U32& lhs, const U32& rhs, bool is_signed) {
return Inst<U1>(is_signed ? Opcode::SGreaterThanEqual : Opcode::UGreaterThanEqual, lhs, rhs);
U1 IREmitter::IGreaterThanEqual(const U32U64& lhs, const U32U64& rhs, bool is_signed) {
if (lhs.Type() != rhs.Type()) {
UNREACHABLE_MSG("Mismatching types {} and {}", lhs.Type(), rhs.Type());
}
switch (lhs.Type()) {
case Type::U32:
return Inst<U1>(is_signed ? Opcode::SGreaterThanEqual32 : Opcode::UGreaterThanEqual32, lhs,
rhs);
case Type::U64:
return Inst<U1>(is_signed ? Opcode::SGreaterThanEqual64 : Opcode::UGreaterThanEqual64, lhs,
rhs);
default:
ThrowInvalidType(lhs.Type());
}
}
U1 IREmitter::LogicalOr(const U1& a, const U1& b) {

View File

@ -96,18 +96,24 @@ public:
[[nodiscard]] F32 GetPatch(Patch patch);
void SetPatch(Patch patch, const F32& value);
[[nodiscard]] Value LoadShared(int bit_size, bool is_signed, const U32& offset);
void WriteShared(int bit_size, const Value& value, const U32& offset);
[[nodiscard]] Value LoadShared(int bit_size, bool is_signed, const U32& offset,
bool is_gds = false);
void WriteShared(int bit_size, const Value& value, const U32& offset, bool is_gds = false);
[[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 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 SharedAtomicOr(const U32& address, const U32& data);
[[nodiscard]] U32 SharedAtomicXor(const U32& address, const U32& data);
[[nodiscard]] U32U64 SharedAtomicIAdd(const U32& address, const U32U64& data, bool is_gds);
[[nodiscard]] U32U64 SharedAtomicISub(const U32& address, const U32U64& data, bool is_gds);
[[nodiscard]] U32U64 SharedAtomicIMin(const U32& address, const U32U64& data, bool is_signed,
bool is_gds);
[[nodiscard]] U32U64 SharedAtomicIMax(const U32& address, const U32U64& data, bool is_signed,
bool is_gds);
[[nodiscard]] U32U64 SharedAtomicAnd(const U32& address, const U32U64& data, bool is_gds);
[[nodiscard]] U32U64 SharedAtomicOr(const U32& address, const U32U64& data, bool is_gds);
[[nodiscard]] U32U64 SharedAtomicXor(const U32& address, const U32U64& data, bool is_gds);
template <typename T = U32>
[[nodiscard]] T SharedAtomicInc(const U32& address, bool is_gds);
template <typename T = U32>
[[nodiscard]] T SharedAtomicDec(const U32& address, bool is_gds);
[[nodiscard]] U32 ReadConst(const Value& base, const U32& offset);
[[nodiscard]] U32 ReadConstBuffer(const Value& handle, const U32& index);
@ -170,6 +176,8 @@ public:
[[nodiscard]] U32 ReadFirstLane(const U32& value);
[[nodiscard]] U32 ReadLane(const U32& value, const U32& lane);
[[nodiscard]] U32 WriteLane(const U32& value, const U32& write_value, const U32& lane);
[[nodiscard]] Value Ballot(const U1& bit);
[[nodiscard]] U32 BallotFindLsb(const Value& mask);
[[nodiscard]] Value CompositeConstruct(const Value& e1, const Value& e2);
[[nodiscard]] Value CompositeConstruct(const Value& e1, const Value& e2, const Value& e3);
@ -254,7 +262,7 @@ public:
[[nodiscard]] F32F64 FPMedTri(const F32F64& a, const F32F64& b, const F32F64& c);
[[nodiscard]] U32U64 IAdd(const U32U64& a, const U32U64& b);
[[nodiscard]] Value IAddCary(const U32& a, const U32& b);
[[nodiscard]] Value IAddCarry(const U32& a, const U32& b);
[[nodiscard]] U32U64 ISub(const U32U64& a, const U32U64& b);
[[nodiscard]] U32 IMulHi(const U32& a, const U32& b, bool is_signed = false);
[[nodiscard]] U32U64 IMul(const U32U64& a, const U32U64& b);
@ -299,10 +307,10 @@ public:
[[nodiscard]] U1 ILessThan(const U32U64& lhs, const U32U64& rhs, bool is_signed);
[[nodiscard]] U1 IEqual(const U32U64& lhs, const U32U64& rhs);
[[nodiscard]] U1 ILessThanEqual(const U32& lhs, const U32& rhs, bool is_signed);
[[nodiscard]] U1 IGreaterThan(const U32& lhs, const U32& rhs, bool is_signed);
[[nodiscard]] U1 ILessThanEqual(const U32U64& lhs, const U32U64& rhs, bool is_signed);
[[nodiscard]] U1 IGreaterThan(const U32U64& lhs, const U32U64& rhs, bool is_signed);
[[nodiscard]] U1 INotEqual(const U32U64& lhs, const U32U64& rhs);
[[nodiscard]] U1 IGreaterThanEqual(const U32& lhs, const U32& rhs, bool is_signed);
[[nodiscard]] U1 IGreaterThanEqual(const U32U64& lhs, const U32U64& rhs, bool is_signed);
[[nodiscard]] U1 LogicalOr(const U1& a, const U1& b);
[[nodiscard]] U1 LogicalAnd(const U1& a, const U1& b);

Some files were not shown because too many files have changed in this diff Show More