diff --git a/.github/ISSUE_TEMPLATE/game-bug-report.yaml b/.github/ISSUE_TEMPLATE/game-bug-report.yaml index d9ebd8347..848877037 100644 --- a/.github/ISSUE_TEMPLATE/game-bug-report.yaml +++ b/.github/ISSUE_TEMPLATE/game-bug-report.yaml @@ -17,7 +17,7 @@ body: This repository does not provide support for game patches. If you are having issues with patches please refer to [Cheats and Patches Repository](https://github.com/shadps4-emu/ps4_cheats). - Before submitting an issue please check [Game Compatibility Repository](https://github.com/shadps4-emu/shadps4-game-compatibility) for the information about the status of the game. + Before submitting an issue please check [Game Compatibility Repository](https://github.com/shadps4-compatibility/shadps4-game-compatibility) for the information about the status of the game. Please make an effort to make sure your issue isn't already reported. diff --git a/CMakeLists.txt b/CMakeLists.txt index 91829261d..76c177ab2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -126,7 +126,7 @@ execute_process( # If there's no upstream set or the command failed, check remote.pushDefault if (GIT_REMOTE_RESULT OR GIT_REMOTE_NAME STREQUAL "") - message("check default push") + message(STATUS "check default push") execute_process( COMMAND git config --get remote.pushDefault OUTPUT_VARIABLE GIT_REMOTE_NAME @@ -134,30 +134,30 @@ if (GIT_REMOTE_RESULT OR GIT_REMOTE_NAME STREQUAL "") ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE ) - message("got remote: ${GIT_REMOTE_NAME}") + message(STATUS "got remote: ${GIT_REMOTE_NAME}") endif() # If running in GitHub Actions and the above fails if (GIT_REMOTE_RESULT OR GIT_REMOTE_NAME STREQUAL "") - message("check github") + message(STATUS "check github") set(GIT_REMOTE_NAME "origin") # Retrieve environment variables if (DEFINED ENV{GITHUB_HEAD_REF} AND NOT "$ENV{GITHUB_HEAD_REF}" STREQUAL "") - message("github head ref: $ENV{GITHUB_HEAD_REF}") + message(STATUS "github head ref: $ENV{GITHUB_HEAD_REF}") set(GITHUB_HEAD_REF "$ENV{GITHUB_HEAD_REF}") else() set(GITHUB_HEAD_REF "") endif() if (DEFINED ENV{GITHUB_REF} AND NOT "$ENV{GITHUB_REF}" STREQUAL "") - message("github ref: $ENV{GITHUB_REF}") + message(STATUS "github ref: $ENV{GITHUB_REF}") string(REGEX REPLACE "^refs/[^/]*/" "" GITHUB_BRANCH "$ENV{GITHUB_REF}") string(REGEX MATCH "refs/pull/([0-9]+)/merge" MATCHED_REF "$ENV{GITHUB_REF}") if (MATCHED_REF) set(PR_NUMBER "${CMAKE_MATCH_1}") set(GITHUB_BRANCH "") - message("PR number: ${PR_NUMBER}") + message(STATUS "PR number: ${PR_NUMBER}") else() set(PR_NUMBER "") endif() @@ -179,7 +179,7 @@ if (GIT_REMOTE_RESULT OR GIT_REMOTE_NAME STREQUAL "") elseif ("${PR_NUMBER}" STREQUAL "" AND NOT "${GITHUB_REF}" STREQUAL "") set(GIT_BRANCH "${GITHUB_REF}") elseif("${GIT_BRANCH}" STREQUAL "") - message("couldn't find branch") + message(STATUS "couldn't find branch") set(GIT_BRANCH "detached-head") endif() else() @@ -188,13 +188,13 @@ else() if (INDEX GREATER -1) string(SUBSTRING "${GIT_REMOTE_NAME}" 0 "${INDEX}" GIT_REMOTE_NAME) elseif("${GIT_REMOTE_NAME}" STREQUAL "") - message("reset to origin") + message(STATUS "reset to origin") set(GIT_REMOTE_NAME "origin") endif() endif() # Get remote link -message("getting remote link") +message(STATUS "getting remote link") execute_process( COMMAND git config --get remote.${GIT_REMOTE_NAME}.url OUTPUT_VARIABLE GIT_REMOTE_URL @@ -212,7 +212,12 @@ set(APP_VERSION "${EMULATOR_VERSION_MAJOR}.${EMULATOR_VERSION_MINOR}.${EMULATOR_ set(APP_IS_RELEASE false) configure_file("${CMAKE_CURRENT_SOURCE_DIR}/src/common/scm_rev.cpp.in" "${CMAKE_CURRENT_BINARY_DIR}/src/common/scm_rev.cpp" @ONLY) -message("end git things, remote: ${GIT_REMOTE_NAME}, branch: ${GIT_BRANCH}") +message("-- end git things, remote: ${GIT_REMOTE_NAME}, branch: ${GIT_BRANCH}, link: ${GIT_REMOTE_URL}") + +if(NOT GIT_REMOTE_URL MATCHES "shadps4-emu/shadPS4" OR NOT GIT_BRANCH STREQUAL "main") + message(STATUS "not main, disabling auto update") + set(ENABLE_UPDATER OFF) +endif() if(WIN32 AND ENABLE_QT_GUI AND NOT CMAKE_PREFIX_PATH) include("${CMAKE_CURRENT_SOURCE_DIR}/cmake/DetectQtInstallation.cmake") diff --git a/README.md b/README.md index 22fc27a33..06da2d471 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,7 @@ SPDX-License-Identifier: GPL-2.0-or-later **shadPS4** is an early **PlayStation 4** emulator for **Windows**, **Linux** and **macOS** written in C++. If you encounter problems or have doubts, do not hesitate to look at the [**Quickstart**](https://github.com/shadps4-emu/shadPS4/wiki/I.-Quick-start-%5BUsers%5D).\ -To verify that a game works, you can look at [**shadPS4 Game Compatibility**](https://github.com/shadps4-emu/shadps4-game-compatibility).\ +To verify that a game works, you can look at [**shadPS4 Game Compatibility**](https://github.com/shadps4-compatibility/shadps4-game-compatibility).\ To discuss shadPS4 development, suggest ideas or to ask for help, join our [**Discord server**](https://discord.gg/bFJxfftGW6).\ To get the latest news, go to our [**X (Twitter)**](https://x.com/shadps4) or our [**website**](https://shadps4.net/).\ For those who'd like to donate to the project, we now have a [**Kofi page**](https://ko-fi.com/shadps4)! diff --git a/documents/Debugging/Debugging.md b/documents/Debugging/Debugging.md index ef9aab879..d2eda61b7 100644 --- a/documents/Debugging/Debugging.md +++ b/documents/Debugging/Debugging.md @@ -147,7 +147,7 @@ Accurately identifying games will help other developers that own that game recog - If your issue is small or you aren't sure whether you have properly identified something, [join the Discord server](https://discord.gg/MyZRaBngxA) and use the #development channel to concisely explain the issue, as well as any findings you currently have. -- It is recommended that you check the [game compatibility issue tracker](https://github.com/shadps4-emu/shadps4-game-compatibility/issues) and post very short summaries of progress changes there, +- It is recommended that you check the [game compatibility issue tracker](https://github.com/shadps4-compatibility/shadps4-game-compatibility/issues) and post very short summaries of progress changes there, (such as the game now booting into the menu or getting in-game) for organizational and status update purposes. - ⚠ **Do not post theoretical, unproven game-specific issues in the emulator issue tracker that you cannot verify and locate in the emulator source code as being a bug.**\ diff --git a/src/common/config.cpp b/src/common/config.cpp index 6f8563377..a1b12ee5d 100644 --- a/src/common/config.cpp +++ b/src/common/config.cpp @@ -32,6 +32,7 @@ std::filesystem::path find_fs_path_or(const basic_value& v, const K& ky, namespace Config { // General +static int volumeSlider = 100; static bool isNeo = false; static bool isDevKit = false; static bool isPSNSignedIn = false; @@ -108,6 +109,9 @@ static std::string trophyKey = ""; // Expected number of items in the config file static constexpr u64 total_entries = 54; +int getVolumeSlider() { + return volumeSlider; +} bool allowHDR() { return isHDRAllowed; } @@ -157,6 +161,10 @@ std::filesystem::path GetSaveDataPath() { return save_data_path; } +void setVolumeSlider(int volumeValue) { + volumeSlider = volumeValue; +} + void setLoadGameSizeEnabled(bool enable) { load_game_size = enable; } @@ -611,6 +619,7 @@ void load(const std::filesystem::path& path) { if (data.contains("General")) { const toml::value& general = data.at("General"); + volumeSlider = toml::find_or(general, "volumeSlider", volumeSlider); isNeo = toml::find_or(general, "isPS4Pro", isNeo); isDevKit = toml::find_or(general, "isDevKit", isDevKit); isPSNSignedIn = toml::find_or(general, "isPSNSignedIn", isPSNSignedIn); @@ -806,6 +815,7 @@ void save(const std::filesystem::path& path) { fmt::print("Saving new configuration file {}\n", fmt::UTF(path.u8string())); } + data["General"]["volumeSlider"] = volumeSlider; data["General"]["isPS4Pro"] = isNeo; data["General"]["isDevKit"] = isDevKit; data["General"]["isPSNSignedIn"] = isPSNSignedIn; @@ -901,6 +911,7 @@ void save(const std::filesystem::path& path) { void setDefaultValues() { // General + volumeSlider = 100; isNeo = false; isDevKit = false; isPSNSignedIn = false; diff --git a/src/common/config.h b/src/common/config.h index e54425676..4ace4d316 100644 --- a/src/common/config.h +++ b/src/common/config.h @@ -19,6 +19,8 @@ enum HideCursorState : int { Never, Idle, Always }; void load(const std::filesystem::path& path); void save(const std::filesystem::path& path); +int getVolumeSlider(); +void setVolumeSlider(int volumeValue); std::string getTrophyKey(); void setTrophyKey(std::string key); bool getIsFullscreen(); diff --git a/src/core/libraries/audio/audioout.cpp b/src/core/libraries/audio/audioout.cpp index 421289a9d..22b6b17ea 100644 --- a/src/core/libraries/audio/audioout.cpp +++ b/src/core/libraries/audio/audioout.cpp @@ -14,6 +14,7 @@ #include "core/libraries/audio/audioout.h" #include "core/libraries/audio/audioout_backend.h" #include "core/libraries/audio/audioout_error.h" +#include "core/libraries/kernel/time.h" #include "core/libraries/libs.h" namespace Libraries::AudioOut { @@ -168,8 +169,19 @@ int PS4_SYSV_ABI sceAudioOutGetInfoOpenNum() { return ORBIS_OK; } -int PS4_SYSV_ABI sceAudioOutGetLastOutputTime() { - LOG_ERROR(Lib_AudioOut, "(STUBBED) called"); +int PS4_SYSV_ABI sceAudioOutGetLastOutputTime(s32 handle, u64* output_time) { + LOG_DEBUG(Lib_AudioOut, "called, handle: {}, output time: {}", handle, fmt::ptr(output_time)); + if (!output_time) { + return ORBIS_AUDIO_OUT_ERROR_INVALID_POINTER; + } + if (handle >= ports_out.size()) { + return ORBIS_AUDIO_OUT_ERROR_INVALID_PORT; + } + auto& port = ports_out.at(handle - 1); + if (!port.IsOpen()) { + return ORBIS_AUDIO_OUT_ERROR_NOT_OPENED; + } + *output_time = port.last_output_time; return ORBIS_OK; } @@ -396,6 +408,7 @@ s32 PS4_SYSV_ABI sceAudioOutOutput(s32 handle, void* ptr) { if (ptr != nullptr && port.IsOpen()) { std::memcpy(port.output_buffer, ptr, port.BufferSize()); port.output_ready = true; + port.last_output_time = Kernel::sceKernelGetProcessTime(); } } port.output_cv.notify_one(); @@ -523,9 +536,24 @@ s32 PS4_SYSV_ABI sceAudioOutSetVolume(s32 handle, s32 flag, s32* vol) { } port.impl->SetVolume(port.volume); } + AdjustVol(); return ORBIS_OK; } +void AdjustVol() { + if (audio == nullptr) { + return; + } + + for (int i = 0; i < ports_out.size(); i++) { + std::unique_lock lock{ports_out[i].mutex}; + if (!ports_out[i].IsOpen()) { + continue; + } + ports_out[i].impl->SetVolume(ports_out[i].volume); + } +} + int PS4_SYSV_ABI sceAudioOutSetVolumeDown() { LOG_ERROR(Lib_AudioOut, "(STUBBED) called"); return ORBIS_OK; diff --git a/src/core/libraries/audio/audioout.h b/src/core/libraries/audio/audioout.h index 4f799665e..6d3a6399b 100644 --- a/src/core/libraries/audio/audioout.h +++ b/src/core/libraries/audio/audioout.h @@ -96,6 +96,7 @@ struct PortOut { AudioFormatInfo format_info; u32 sample_rate; u32 buffer_frames; + u64 last_output_time; std::array volume; [[nodiscard]] bool IsOpen() const { @@ -127,7 +128,7 @@ int PS4_SYSV_ABI sceAudioOutGetFocusEnablePid(); int PS4_SYSV_ABI sceAudioOutGetHandleStatusInfo(); int PS4_SYSV_ABI sceAudioOutGetInfo(); int PS4_SYSV_ABI sceAudioOutGetInfoOpenNum(); -int PS4_SYSV_ABI sceAudioOutGetLastOutputTime(); +int PS4_SYSV_ABI sceAudioOutGetLastOutputTime(s32 handle, u64* output_time); int PS4_SYSV_ABI sceAudioOutGetPortState(s32 handle, OrbisAudioOutPortState* state); int PS4_SYSV_ABI sceAudioOutGetSimulatedBusUsableStatusByBusType(); int PS4_SYSV_ABI sceAudioOutGetSimulatedHandleStatusInfo(); @@ -181,5 +182,6 @@ int PS4_SYSV_ABI sceAudioOutSystemControlSet(); int PS4_SYSV_ABI sceAudioOutSparkControlSetEqCoef(); int PS4_SYSV_ABI sceAudioOutSetSystemDebugState(); +void AdjustVol(); void RegisterLib(Core::Loader::SymbolsResolver* sym); } // namespace Libraries::AudioOut diff --git a/src/core/libraries/audio/sdl_audio.cpp b/src/core/libraries/audio/sdl_audio.cpp index 9aee2b447..94d624b0c 100644 --- a/src/core/libraries/audio/sdl_audio.cpp +++ b/src/core/libraries/audio/sdl_audio.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include "common/logging/log.h" #include "core/libraries/audio/audioout.h" @@ -41,6 +42,7 @@ public: stream = nullptr; return; } + SDL_SetAudioStreamGain(stream, Config::getVolumeSlider() / 100.0f); } ~SDLPortBackend() override { @@ -77,7 +79,8 @@ public: } // SDL does not have per-channel volumes, for now just take the maximum of the channels. const auto vol = *std::ranges::max_element(ch_volumes); - if (!SDL_SetAudioStreamGain(stream, static_cast(vol) / SCE_AUDIO_OUT_VOLUME_0DB)) { + if (!SDL_SetAudioStreamGain(stream, static_cast(vol) / SCE_AUDIO_OUT_VOLUME_0DB * + Config::getVolumeSlider() / 100.0f)) { LOG_WARNING(Lib_AudioOut, "Failed to change SDL audio stream volume: {}", SDL_GetError()); } diff --git a/src/core/libraries/ime/ime.cpp b/src/core/libraries/ime/ime.cpp index dc87e486d..360f0fc54 100644 --- a/src/core/libraries/ime/ime.cpp +++ b/src/core/libraries/ime/ime.cpp @@ -65,7 +65,7 @@ public: return Error::OK; } - std::unique_lock lock{g_ime_state.queue_mutex}; + std::unique_lock lock{g_ime_state.queue_mutex}; while (!g_ime_state.event_queue.empty()) { OrbisImeEvent event = g_ime_state.event_queue.front(); @@ -144,17 +144,17 @@ int PS4_SYSV_ABI sceImeCheckUpdateTextInfo() { return ORBIS_OK; } -s32 PS4_SYSV_ABI sceImeClose() { - LOG_INFO(Lib_Ime, "(STUBBED) called"); +Error PS4_SYSV_ABI sceImeClose() { + LOG_INFO(Lib_Ime, "called"); if (!g_ime_handler) { - return ORBIS_IME_ERROR_NOT_OPENED; + return Error::NOT_OPENED; } g_ime_handler.release(); g_ime_ui = ImeUi(); g_ime_state = ImeState(); - return ORBIS_OK; + return Error::OK; } int PS4_SYSV_ABI sceImeConfigGet() { @@ -223,32 +223,68 @@ int PS4_SYSV_ABI sceImeGetPanelPositionAndForm() { } Error PS4_SYSV_ABI sceImeGetPanelSize(const OrbisImeParam* param, u32* width, u32* height) { - LOG_INFO(Lib_Ime, "called"); + LOG_INFO(Lib_Ime, "sceImeGetPanelSize called"); - if (!width || !height) { + if (!param) { + LOG_ERROR(Lib_Ime, "sceImeGetPanelSize: param is NULL"); return Error::INVALID_ADDRESS; } + if (!width) { + LOG_ERROR(Lib_Ime, "sceImeGetPanelSize: width pointer is NULL"); + return Error::INVALID_ADDRESS; + } + if (!height) { + LOG_ERROR(Lib_Ime, "sceImeGetPanelSize: height pointer is NULL"); + return Error::INVALID_ADDRESS; + } + + if (static_cast(param->option) & ~0x7BFF) { // Basic check for invalid options + LOG_ERROR(Lib_Ime, "sceImeGetPanelSize: Invalid option 0x{:X}", + static_cast(param->option)); + return Error::INVALID_OPTION; + } switch (param->type) { case OrbisImeType::Default: + *width = 500; // dummy value + *height = 100; // dummy value + LOG_INFO(Lib_Ime, "sceImeGetPanelSize: IME type Default ({})", + static_cast(param->type)); + break; case OrbisImeType::BasicLatin: + *width = 500; // dummy value + *height = 100; // dummy value + LOG_INFO(Lib_Ime, "sceImeGetPanelSize: IME type BasicLatin ({})", + static_cast(param->type)); + break; case OrbisImeType::Url: + *width = 500; // dummy value + *height = 100; // dummy value + LOG_INFO(Lib_Ime, "sceImeGetPanelSize: IME type Url ({})", static_cast(param->type)); + break; case OrbisImeType::Mail: // We set our custom sizes, commented sizes are the original ones *width = 500; // 793 *height = 100; // 408 + LOG_INFO(Lib_Ime, "sceImeGetPanelSize: IME type Mail ({})", static_cast(param->type)); break; case OrbisImeType::Number: *width = 370; *height = 402; + LOG_INFO(Lib_Ime, "sceImeGetPanelSize: IME type Number ({})", + static_cast(param->type)); break; + default: + LOG_ERROR(Lib_Ime, "sceImeGetPanelSize: Invalid IME type ({})", + static_cast(param->type)); + return Error::INVALID_TYPE; } return Error::OK; } -Error PS4_SYSV_ABI sceImeKeyboardClose(s32 userId) { - LOG_INFO(Lib_Ime, "(STUBBED) called"); +Error PS4_SYSV_ABI sceImeKeyboardClose(Libraries::UserService::OrbisUserServiceUserId userId) { + LOG_INFO(Lib_Ime, "called"); if (!g_keyboard_handler) { return Error::NOT_OPENED; @@ -268,9 +304,12 @@ int PS4_SYSV_ABI sceImeKeyboardGetResourceId() { return ORBIS_OK; } -Error PS4_SYSV_ABI sceImeKeyboardOpen(s32 userId, const OrbisImeKeyboardParam* param) { +Error PS4_SYSV_ABI sceImeKeyboardOpen(Libraries::UserService::OrbisUserServiceUserId userId, + const OrbisImeKeyboardParam* param) { LOG_INFO(Lib_Ime, "called"); + LOG_INFO(Lib_Ime, "kValidImeDialogExtOptionMask=0x{:X}", kValidImeDialogExtOptionMask); + if (!param) { return Error::INVALID_ADDRESS; } @@ -308,13 +347,169 @@ Error PS4_SYSV_ABI sceImeOpen(const OrbisImeParam* param, const OrbisImeParamExt LOG_INFO(Lib_Ime, "called"); if (!param) { + LOG_ERROR(Lib_Ime, "sceImeOpen: param is null"); return Error::INVALID_ADDRESS; + } else { + // LOG_DEBUG values for debugging purposes + LOG_DEBUG(Lib_Ime, "param: user_id={}", param->user_id); + LOG_DEBUG(Lib_Ime, "param: type={}", static_cast(param->type)); + LOG_DEBUG(Lib_Ime, "param: supported_languages={:064b}", + static_cast(param->supported_languages)); + LOG_DEBUG(Lib_Ime, "param: enter_label={}", static_cast(param->enter_label)); + LOG_DEBUG(Lib_Ime, "param: input_method={}", static_cast(param->input_method)); + LOG_DEBUG(Lib_Ime, "param: filter={:p}", reinterpret_cast(param->filter)); + LOG_DEBUG(Lib_Ime, "param: option={:032b}", static_cast(param->option)); + LOG_DEBUG(Lib_Ime, "param: maxTextLength={}", param->maxTextLength); + LOG_DEBUG(Lib_Ime, "param: inputTextBuffer={:p}", + static_cast(param->inputTextBuffer)); + LOG_DEBUG(Lib_Ime, "param: posx={}", param->posx); + LOG_DEBUG(Lib_Ime, "param: posy={}", param->posy); + LOG_DEBUG(Lib_Ime, "param: horizontal_alignment={}", + static_cast(param->horizontal_alignment)); + LOG_DEBUG(Lib_Ime, "param: vertical_alignment={}", + static_cast(param->vertical_alignment)); + LOG_DEBUG(Lib_Ime, "param: work={:p}", param->work); + LOG_DEBUG(Lib_Ime, "param: arg={:p}", param->arg); + LOG_DEBUG(Lib_Ime, "param: handler={:p}", reinterpret_cast(param->handler)); } + + if (!extended) { + LOG_INFO(Lib_Ime, "sceImeOpen: extended is null"); + } else { + // LOG_DEBUG values for debugging purposes + LOG_DEBUG(Lib_Ime, "extended: option={:032b}", static_cast(extended->option)); + LOG_DEBUG(Lib_Ime, "extended: color_base={{{},{},{},{}}}", extended->color_base.r, + extended->color_base.g, extended->color_base.b, extended->color_base.a); + LOG_DEBUG(Lib_Ime, "extended: color_line={{{},{},{},{}}}", extended->color_line.r, + extended->color_line.g, extended->color_line.b, extended->color_line.a); + LOG_DEBUG(Lib_Ime, "extended: color_text_field={{{},{},{},{}}}", + extended->color_text_field.r, extended->color_text_field.g, + extended->color_text_field.b, extended->color_text_field.a); + LOG_DEBUG(Lib_Ime, "extended: color_preedit={{{},{},{},{}}}", extended->color_preedit.r, + extended->color_preedit.g, extended->color_preedit.b, extended->color_preedit.a); + LOG_DEBUG(Lib_Ime, "extended: color_button_default={{{},{},{},{}}}", + extended->color_button_default.r, extended->color_button_default.g, + extended->color_button_default.b, extended->color_button_default.a); + LOG_DEBUG(Lib_Ime, "extended: color_button_function={{{},{},{},{}}}", + extended->color_button_function.r, extended->color_button_function.g, + extended->color_button_function.b, extended->color_button_function.a); + LOG_DEBUG(Lib_Ime, "extended: color_button_symbol={{{},{},{},{}}}", + extended->color_button_symbol.r, extended->color_button_symbol.g, + extended->color_button_symbol.b, extended->color_button_symbol.a); + LOG_DEBUG(Lib_Ime, "extended: color_text={{{},{},{},{}}}", extended->color_text.r, + extended->color_text.g, extended->color_text.b, extended->color_text.a); + LOG_DEBUG(Lib_Ime, "extended: color_special={{{},{},{},{}}}", extended->color_special.r, + extended->color_special.g, extended->color_special.b, extended->color_special.a); + LOG_DEBUG(Lib_Ime, "extended: priority={}", static_cast(extended->priority)); + LOG_DEBUG(Lib_Ime, "extended: additional_dictionary_path={:p}", + static_cast(extended->additional_dictionary_path)); + LOG_DEBUG(Lib_Ime, "extended: ext_keyboard_filter={:p}", + reinterpret_cast(extended->ext_keyboard_filter)); + LOG_DEBUG(Lib_Ime, "extended: disable_device={:032b}", + static_cast(extended->disable_device)); + LOG_DEBUG(Lib_Ime, "extended: ext_keyboard_mode={}", extended->ext_keyboard_mode); + } + + if (param->user_id < 1 || param->user_id > 4) { + LOG_ERROR(Lib_Ime, "sceImeOpen: Invalid user_id ({})", static_cast(param->user_id)); + return Error::INVALID_USER_ID; + } + + if (!magic_enum::enum_contains(param->type)) { + LOG_ERROR(Lib_Ime, "sceImeOpen: Invalid type ({})", static_cast(param->type)); + return Error::INVALID_TYPE; + } + + if (static_cast(param->supported_languages) & ~kValidOrbisImeLanguageMask) { + LOG_ERROR(Lib_Ime, "sceImeOpen: supported_languages has invalid bits (0x{:016X})", + static_cast(param->supported_languages)); + return Error::INVALID_SUPPORTED_LANGUAGES; + } + + if (!magic_enum::enum_contains(param->enter_label)) { + LOG_ERROR(Lib_Ime, "sceImeOpen: Invalid enter_label ({})", + static_cast(param->enter_label)); + return Error::INVALID_ENTER_LABEL; + } + + if (!magic_enum::enum_contains(param->input_method)) { + LOG_ERROR(Lib_Ime, "sceImeOpen: Invalid input_method ({})", + static_cast(param->input_method)); + return Error::INVALID_INPUT_METHOD; + } + + if (static_cast(param->option) & ~kValidImeOptionMask) { + LOG_ERROR(Lib_Ime, "sceImeOpen: option has invalid bits set (0x{:X}), mask=(0x{:X})", + static_cast(param->option), kValidImeOptionMask); + return Error::INVALID_OPTION; + } + + if (param->maxTextLength == 0 || param->maxTextLength > ORBIS_IME_DIALOG_MAX_TEXT_LENGTH) { + LOG_ERROR(Lib_Ime, "sceImeOpen: maxTextLength invalid ({})", param->maxTextLength); + return Error::INVALID_MAX_TEXT_LENGTH; + } + + if (!param->inputTextBuffer) { + LOG_ERROR(Lib_Ime, "sceImeOpen: inputTextBuffer is NULL"); + return Error::INVALID_INPUT_TEXT_BUFFER; + } + + bool useHighRes = True(param->option & OrbisImeOption::USE_OVER_2K_COORDINATES); + const float maxWidth = useHighRes ? 3840.0f : 1920.0f; + const float maxHeight = useHighRes ? 2160.0f : 1080.0f; + + if (param->posx < 0.0f || param->posx >= maxWidth) { + LOG_ERROR(Lib_Ime, "sceImeOpen: posx out of range (%.2f), max allowed %.0f", param->posx, + maxWidth); + return Error::INVALID_POSX; + } + if (param->posy < 0.0f || param->posy >= maxHeight) { + LOG_ERROR(Lib_Ime, "sceImeOpen: posy out of range (%.2f), max allowed %.0f", param->posy, + maxHeight); + return Error::INVALID_POSY; + } + + if (!magic_enum::enum_contains(param->horizontal_alignment)) { + LOG_ERROR(Lib_Ime, "sceImeOpen: Invalid horizontal_alignment ({})", + static_cast(param->horizontal_alignment)); + return Error::INVALID_HORIZONTALIGNMENT; + } + if (!magic_enum::enum_contains(param->vertical_alignment)) { + LOG_ERROR(Lib_Ime, "sceImeOpen: Invalid vertical_alignment ({})", + static_cast(param->vertical_alignment)); + return Error::INVALID_VERTICALALIGNMENT; + } + + if (extended) { + u32 ext_option_value = static_cast(extended->option); + if (ext_option_value & ~kValidImeExtOptionMask) { + LOG_ERROR(Lib_Ime, "sceImeOpen: extended->option has invalid bits set (0x{:X})", + ext_option_value); + return Error::INVALID_EXTENDED; + } + } + + if (!param->work) { + LOG_ERROR(Lib_Ime, "sceImeOpen: work buffer is NULL"); + return Error::INVALID_WORK; + } + + for (unsigned i = 0; i < sizeof(param->reserved); ++i) { + if (param->reserved[i] != 0) { + LOG_ERROR(Lib_Ime, "sceImeOpen: reserved field must be zeroed"); + return Error::INVALID_RESERVED; + } + } + + // Todo: validate arg and handler + if (g_ime_handler) { + LOG_ERROR(Lib_Ime, "sceImeOpen: Error BUSY"); return Error::BUSY; } g_ime_handler = std::make_unique(param); + LOG_INFO(Lib_Ime, "sceImeOpen: OK"); return Error::OK; } @@ -324,7 +519,7 @@ int PS4_SYSV_ABI sceImeOpenInternal() { } void PS4_SYSV_ABI sceImeParamInit(OrbisImeParam* param) { - LOG_INFO(Lib_Ime, "called"); + LOG_INFO(Lib_Ime, "sceImeParamInit called"); if (!param) { return; diff --git a/src/core/libraries/ime/ime.h b/src/core/libraries/ime/ime.h index 6691965b8..555ac0f7d 100644 --- a/src/core/libraries/ime/ime.h +++ b/src/core/libraries/ime/ime.h @@ -18,7 +18,7 @@ int PS4_SYSV_ABI InitializeImeModule(); int PS4_SYSV_ABI sceImeCheckFilterText(); int PS4_SYSV_ABI sceImeCheckRemoteEventParam(); int PS4_SYSV_ABI sceImeCheckUpdateTextInfo(); -int PS4_SYSV_ABI sceImeClose(); +Error PS4_SYSV_ABI sceImeClose(); int PS4_SYSV_ABI sceImeConfigGet(); int PS4_SYSV_ABI sceImeConfigSet(); int PS4_SYSV_ABI sceImeConfirmCandidate(); @@ -33,10 +33,11 @@ int PS4_SYSV_ABI sceImeFilterText(); int PS4_SYSV_ABI sceImeForTestFunction(); int PS4_SYSV_ABI sceImeGetPanelPositionAndForm(); Error PS4_SYSV_ABI sceImeGetPanelSize(const OrbisImeParam* param, u32* width, u32* height); -Error PS4_SYSV_ABI sceImeKeyboardClose(s32 userId); +Error PS4_SYSV_ABI sceImeKeyboardClose(Libraries::UserService::OrbisUserServiceUserId userId); int PS4_SYSV_ABI sceImeKeyboardGetInfo(); int PS4_SYSV_ABI sceImeKeyboardGetResourceId(); -Error PS4_SYSV_ABI sceImeKeyboardOpen(s32 userId, const OrbisImeKeyboardParam* param); +Error PS4_SYSV_ABI sceImeKeyboardOpen(Libraries::UserService::OrbisUserServiceUserId userId, + const OrbisImeKeyboardParam* param); int PS4_SYSV_ABI sceImeKeyboardOpenInternal(); int PS4_SYSV_ABI sceImeKeyboardSetMode(); int PS4_SYSV_ABI sceImeKeyboardUpdate(); diff --git a/src/core/libraries/ime/ime_common.h b/src/core/libraries/ime/ime_common.h index 5c0030030..13f2bfa8f 100644 --- a/src/core/libraries/ime/ime_common.h +++ b/src/core/libraries/ime/ime_common.h @@ -3,6 +3,8 @@ #pragma once +#include +#include #include "common/enum.h" #include "common/types.h" #include "core/libraries/rtc/rtc.h" @@ -10,8 +12,28 @@ constexpr u32 ORBIS_IME_MAX_TEXT_LENGTH = 2048; constexpr u32 ORBIS_IME_DIALOG_MAX_TEXT_LENGTH = 2048; +template +const std::underlying_type_t generate_full_mask() { + static_assert(std::is_enum_v, "E must be an enum type."); + static_assert(magic_enum::customize::enum_range::is_flags, + "E must be marked as is_flags = true."); + + using U = std::underlying_type_t; + const auto values = magic_enum::enum_values(); + U mask = 0; + + // Use index-based loop for better constexpr compatibility + for (std::size_t i = 0; i < values.size(); ++i) { + mask |= static_cast(values[i]); + } + + return mask; +} + enum class Error : u32 { OK = 0x0, + + // ImeDialog library BUSY = 0x80bc0001, NOT_OPENED = 0x80bc0002, NO_MEMORY = 0x80bc0003, @@ -46,6 +68,8 @@ enum class Error : u32 { INVALID_RESERVED = 0x80bc0032, INVALID_TIMING = 0x80bc0033, INTERNAL = 0x80bc00ff, + + // Ime library DIALOG_INVALID_TITLE = 0x80bc0101, DIALOG_NOT_RUNNING = 0x80bc0105, DIALOG_NOT_FINISHED = 0x80bc0106, @@ -67,9 +91,44 @@ enum class OrbisImeOption : u32 { DISABLE_POSITION_ADJUSTMENT = 2048, EXPANDED_PREEDIT_BUFFER = 4096, USE_JAPANESE_EISUU_KEY_AS_CAPSLOCK = 8192, - USE_2K_COORDINATES = 16384, + USE_OVER_2K_COORDINATES = 16384, }; DECLARE_ENUM_FLAG_OPERATORS(OrbisImeOption); +template <> +struct magic_enum::customize::enum_range { + static constexpr bool is_flags = true; +}; +const u32 kValidImeOptionMask = generate_full_mask(); + +enum class OrbisImeExtOption : u32 { + DEFAULT = 0x00000000, + SET_PRIORITY = 0x00000002, + PRIORITY_FULL_WIDTH = 0x00000008, + PRIORITY_FIXED_PANEL = 0x00000010, + DISABLE_POINTER = 0x00000040, + ENABLE_ADDITIONAL_DICTIONARY = 0x00000080, + DISABLE_STARTUP_SE = 0x00000100, + DISABLE_LIST_FOR_EXT_KEYBOARD = 0x00000200, + HIDE_KEYPANEL_IF_EXT_KEYBOARD = 0x00000400, + INIT_EXT_KEYBOARD_MODE = 0x00000800, + + ENABLE_ACCESSIBILITY = 0x00001000, // ImeDialog unly + ADDITIONAL_DICTIONARY_PRIORITY_MODE = 0x00004000, // ImeDialog only +}; +DECLARE_ENUM_FLAG_OPERATORS(OrbisImeExtOption); + +constexpr u32 kValidImeExtOptionMask = static_cast( + OrbisImeExtOption::SET_PRIORITY | OrbisImeExtOption::PRIORITY_FULL_WIDTH | + OrbisImeExtOption::PRIORITY_FIXED_PANEL | OrbisImeExtOption::DISABLE_POINTER | + OrbisImeExtOption::ENABLE_ADDITIONAL_DICTIONARY | OrbisImeExtOption::DISABLE_STARTUP_SE | + OrbisImeExtOption::DISABLE_LIST_FOR_EXT_KEYBOARD | + OrbisImeExtOption::HIDE_KEYPANEL_IF_EXT_KEYBOARD | OrbisImeExtOption::INIT_EXT_KEYBOARD_MODE); + +template <> +struct magic_enum::customize::enum_range { + static constexpr bool is_flags = true; +}; +const u32 kValidImeDialogExtOptionMask = generate_full_mask(); enum class OrbisImeLanguage : u64 { DANISH = 0x0000000000000001, @@ -105,6 +164,112 @@ enum class OrbisImeLanguage : u64 { }; DECLARE_ENUM_FLAG_OPERATORS(OrbisImeLanguage); +template <> +struct magic_enum::customize::enum_range { + static constexpr bool is_flags = true; +}; +const u64 kValidOrbisImeLanguageMask = generate_full_mask(); + +enum class OrbisImeDisableDevice : u32 { + DEFAULT = 0x00000000, + CONTROLLER = 0x00000001, + EXT_KEYBOARD = 0x00000002, + REMOTE_OSK = 0x00000004, +}; +DECLARE_ENUM_FLAG_OPERATORS(OrbisImeDisableDevice); +template <> +struct magic_enum::customize::enum_range { + static constexpr bool is_flags = true; +}; +const u32 kValidOrbisImeDisableDeviceMask = generate_full_mask(); + +enum class OrbisImeInputMethodState : u32 { + PREEDIT = 0x01000000, + SELECTED = 0x02000000, + NATIVE = 0x04000000, + NATIVE2 = 0x08000000, + FULL_WIDTH = 0x10000000, +}; +DECLARE_ENUM_FLAG_OPERATORS(OrbisImeInputMethodState); +template <> +struct magic_enum::customize::enum_range { + static constexpr bool is_flags = true; +}; +const u32 kValidOrbisImeInputMethodStateMask = generate_full_mask(); + +enum class OrbisImeInitExtKeyboardMode : u32 { + ISABLE_ARABIC_INDIC_NUMERALS = 0x00000001, + ENABLE_FORMAT_CHARACTERS = 0x00000002, + INPUT_METHOD_STATE_NATIVE = 0x04000000, + INPUT_METHOD_STATE_NATIVE2 = 0x08000000, + INPUT_METHOD_STATE_FULL_WIDTH = 0x10000000, +}; +DECLARE_ENUM_FLAG_OPERATORS(OrbisImeInitExtKeyboardMode); +template <> +struct magic_enum::customize::enum_range { + static constexpr bool is_flags = true; +}; +const u32 kValidOrbisImeInitExtKeyboardModeMask = generate_full_mask(); + +enum class OrbisImeKeycodeState : u32 { + KEYCODE_VALID = 0x00000001, + CHARACTER_VALID = 0x00000002, + WITH_IME = 0x00000004, + FROM_OSK = 0x00000008, + FROM_OSK_SHORTCUT = 0x00000010, + FROM_IME_OPERATION = 0x00000020, + REPLACE_CHARACTER = 0x00000040, + CONTINUOUS_EVENT = 0x00000080, + MODIFIER_L_CTRL = 0x00000100, + MODIFIER_L_SHIFT = 0x00000200, + MODIFIER_L_ALT = 0x00000400, + MODIFIER_L_GUI = 0x00000800, + MODIFIER_R_CTRL = 0x00001000, + MODIFIER_R_SHIFT = 0x00002000, + MODIFIER_R_ALT = 0x00004000, + MODIFIER_R_GUI = 0x00008000, + LED_NUM_LOCK = 0x00010000, + LED_CAPS_LOCK = 0x00020000, + LED_SCROLL_LOCK = 0x00040000, + RESERVED1 = 0x00080000, + RESERVED2 = 0x00100000, + FROM_IME_INPUT = 0x00200000, +}; +DECLARE_ENUM_FLAG_OPERATORS(OrbisImeKeycodeState); +template <> +struct magic_enum::customize::enum_range { + static constexpr bool is_flags = true; +}; +const u32 kValidOrbisImeKeycodeStateMask = generate_full_mask(); + +enum class OrbisImeKeyboardOption : u32 { + Default = 0, + Repeat = 1, + RepeatEachKey = 2, + AddOsk = 4, + EffectiveWithIme = 8, + DisableResume = 16, + DisableCapslockWithoutShift = 32, +}; +DECLARE_ENUM_FLAG_OPERATORS(OrbisImeKeyboardOption) +template <> +struct magic_enum::customize::enum_range { + static constexpr bool is_flags = true; +}; +const u32 kValidOrbisImeKeyboardOptionMask = generate_full_mask(); + +enum class OrbisImeKeyboardMode : u32 { + Auto = 0, + Manual = 1, + Alphabet = 0, + Native = 2, + Part = 4, + Katakana = 8, + Hkana = 16, + ArabicIndicNumerals = 32, + DisableFormatCharacters = 64, +}; + enum class OrbisImeType : u32 { Default = 0, BasicLatin = 1, @@ -260,13 +425,13 @@ struct OrbisImeKeycode { char16_t character; u32 status; OrbisImeKeyboardType type; - s32 user_id; // Todo: switch to OrbisUserServiceUserId + Libraries::UserService::OrbisUserServiceUserId user_id; u32 resource_id; Libraries::Rtc::OrbisRtcTick timestamp; }; struct OrbisImeKeyboardResourceIdArray { - s32 user_id; // Todo: switch to OrbisUserServiceUserId + Libraries::UserService::OrbisUserServiceUserId user_id; u32 resource_id[5]; }; @@ -322,17 +487,6 @@ using OrbisImeTextFilter = PS4_SYSV_ABI int (*)(char16_t* outText, u32* outTextL 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]; @@ -342,9 +496,9 @@ struct OrbisImeKeyboardParam { }; struct OrbisImeParam { - s32 user_id; // Todo: switch to OrbisUserServiceUserId + Libraries::UserService::OrbisUserServiceUserId user_id; OrbisImeType type; - u64 supported_languages; // OrbisImeLanguage flags + OrbisImeLanguage supported_languages; OrbisImeEnterLabel enter_label; OrbisImeInputMethod input_method; OrbisImeTextFilter filter; @@ -369,9 +523,9 @@ struct OrbisImeCaret { }; struct OrbisImeDialogParam { - s32 user_id; + Libraries::UserService::OrbisUserServiceUserId user_id; OrbisImeType type; - u64 supported_languages; // OrbisImeLanguage flags + OrbisImeLanguage supported_languages; OrbisImeEnterLabel enter_label; OrbisImeInputMethod input_method; OrbisImeTextFilter filter; @@ -388,7 +542,7 @@ struct OrbisImeDialogParam { }; struct OrbisImeParamExtended { - u32 option; // OrbisImeExtOption flags + OrbisImeExtOption option; OrbisImeColor color_base; OrbisImeColor color_line; OrbisImeColor color_text_field; @@ -401,7 +555,7 @@ struct OrbisImeParamExtended { OrbisImePanelPriority priority; char* additional_dictionary_path; OrbisImeExtKeyboardFilter ext_keyboard_filter; - u32 disable_device; + OrbisImeDisableDevice disable_device; u32 ext_keyboard_mode; s8 reserved[60]; }; diff --git a/src/core/libraries/ime/ime_dialog.cpp b/src/core/libraries/ime/ime_dialog.cpp index 2b3e6ebde..6578b6249 100644 --- a/src/core/libraries/ime/ime_dialog.cpp +++ b/src/core/libraries/ime/ime_dialog.cpp @@ -197,13 +197,15 @@ 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 & OrbisImeOption::USE_2K_COORDINATES)]) { + param->posx >= + MAX_X_POSITIONS[False(param->option & OrbisImeOption::USE_OVER_2K_COORDINATES)]) { LOG_ERROR(Lib_ImeDialog, "sceImeDialogInit: invalid posx={}", param->posx); return Error::INVALID_POSX; } if (param->posy < 0.0f || - param->posy >= MAX_Y_POSITIONS[False(param->option & OrbisImeOption::USE_2K_COORDINATES)]) { + param->posy >= + MAX_Y_POSITIONS[False(param->option & OrbisImeOption::USE_OVER_2K_COORDINATES)]) { LOG_ERROR(Lib_ImeDialog, "sceImeDialogInit: invalid posy={}", param->posy); return Error::INVALID_POSY; } @@ -242,7 +244,7 @@ Error PS4_SYSV_ABI sceImeDialogInit(OrbisImeDialogParam* param, OrbisImeParamExt return Error::INVALID_EXTENDED; } - if (static_cast(extended->disable_device) & 0x7) { + if (static_cast(extended->disable_device) & ~kValidOrbisImeDisableDeviceMask) { LOG_ERROR(Lib_ImeDialog, "sceImeDialogInit: disable_device has invalid bits set (0x{:X})", static_cast(extended->disable_device)); diff --git a/src/qt_gui/compatibility_info.cpp b/src/qt_gui/compatibility_info.cpp index da32f24ae..eecd1bd47 100644 --- a/src/qt_gui/compatibility_info.cpp +++ b/src/qt_gui/compatibility_info.cpp @@ -22,8 +22,8 @@ void CompatibilityInfoClass::UpdateCompatibilityDatabase(QWidget* parent, bool f if (!forced && LoadCompatibilityFile()) return; - QUrl url("https://github.com/shadps4-emu/shadps4-game-compatibility/releases/latest/download/" - "compatibility_data.json"); + QUrl url("https://github.com/shadps4-compatibility/shadps4-game-compatibility/releases/latest/" + "download/compatibility_data.json"); QNetworkRequest request(url); QNetworkReply* reply = m_network_manager->get(request); diff --git a/src/qt_gui/game_list_frame.cpp b/src/qt_gui/game_list_frame.cpp index e4c40b4f9..e0995d3ef 100644 --- a/src/qt_gui/game_list_frame.cpp +++ b/src/qt_gui/game_list_frame.cpp @@ -91,7 +91,8 @@ GameListFrame::GameListFrame(std::shared_ptr gui_settings, connect(this, &QTableWidget::cellClicked, this, [=, this](int row, int column) { if (column == 2 && m_game_info->m_games[row].compatibility.issue_number != "") { - auto url_issues = "https://github.com/shadps4-emu/shadps4-game-compatibility/issues/"; + auto url_issues = + "https://github.com/shadps4-compatibility/shadps4-game-compatibility/issues/"; QDesktopServices::openUrl( QUrl(url_issues + m_game_info->m_games[row].compatibility.issue_number)); } else if (column == 10) { diff --git a/src/qt_gui/gui_context_menus.h b/src/qt_gui/gui_context_menus.h index 6c384c4bc..110421002 100644 --- a/src/qt_gui/gui_context_menus.h +++ b/src/qt_gui/gui_context_menus.h @@ -581,7 +581,7 @@ public: if (selected == viewCompatibilityReport) { if (m_games[itemID].compatibility.issue_number != "") { auto url_issues = - "https://github.com/shadps4-emu/shadps4-game-compatibility/issues/"; + "https://github.com/shadps4-compatibility/shadps4-game-compatibility/issues/"; QDesktopServices::openUrl( QUrl(url_issues + m_games[itemID].compatibility.issue_number)); } @@ -589,8 +589,8 @@ public: if (selected == submitCompatibilityReport) { if (m_games[itemID].compatibility.issue_number == "") { - QUrl url = - QUrl("https://github.com/shadps4-emu/shadps4-game-compatibility/issues/new"); + QUrl url = QUrl("https://github.com/shadps4-compatibility/" + "shadps4-game-compatibility/issues/new"); QUrlQuery query; query.addQueryItem("template", QString("game_compatibility.yml")); query.addQueryItem( @@ -605,7 +605,7 @@ public: QDesktopServices::openUrl(url); } else { auto url_issues = - "https://github.com/shadps4-emu/shadps4-game-compatibility/issues/"; + "https://github.com/shadps4-compatibility/shadps4-game-compatibility/issues/"; QDesktopServices::openUrl( QUrl(url_issues + m_games[itemID].compatibility.issue_number)); } diff --git a/src/qt_gui/gui_settings.h b/src/qt_gui/gui_settings.h index 4c1eafc95..4d2a1b7f7 100644 --- a/src/qt_gui/gui_settings.h +++ b/src/qt_gui/gui_settings.h @@ -37,6 +37,7 @@ const gui_value gl_showBackgroundImage = gui_value(game_list, "showBackgroundIma const gui_value gl_backgroundImageOpacity = gui_value(game_list, "backgroundImageOpacity", 50); const gui_value gl_playBackgroundMusic = gui_value(game_list, "playBackgroundMusic", true); const gui_value gl_backgroundMusicVolume = gui_value(game_list, "backgroundMusicVolume", 50); +const gui_value gl_VolumeSlider = gui_value(game_list, "volumeSlider", 100); // game grid settings const gui_value gg_icon_size = gui_value(game_grid, "icon_size", 69); diff --git a/src/qt_gui/settings_dialog.cpp b/src/qt_gui/settings_dialog.cpp index f903562f9..b08bb5aee 100644 --- a/src/qt_gui/settings_dialog.cpp +++ b/src/qt_gui/settings_dialog.cpp @@ -11,6 +11,7 @@ #include "common/config.h" #include "common/scm_rev.h" +#include "core/libraries/audio/audioout.h" #include "qt_gui/compatibility_info.h" #ifdef ENABLE_DISCORD_RPC #include "common/discord_rpc_handler.h" @@ -68,6 +69,7 @@ QMap chooseHomeTabMap; int backgroundImageOpacitySlider_backup; int bgm_volume_backup; +int volume_slider_backup; static std::vector m_physical_devices; @@ -149,9 +151,11 @@ SettingsDialog::SettingsDialog(std::shared_ptr gui_settings, } else if (button == ui->buttonBox->button(QDialogButtonBox::Close)) { ui->backgroundImageOpacitySlider->setValue(backgroundImageOpacitySlider_backup); emit BackgroundOpacityChanged(backgroundImageOpacitySlider_backup); + ui->horizontalVolumeSlider->setValue(volume_slider_backup); + Config::setVolumeSlider(volume_slider_backup); ui->BGMVolumeSlider->setValue(bgm_volume_backup); BackgroundMusicPlayer::getInstance().setVolume(bgm_volume_backup); - ResetInstallFolders(); + SyncRealTimeWidgetstoConfig(); } if (Common::Log::IsActive()) { Common::Log::Filter filter; @@ -170,6 +174,12 @@ SettingsDialog::SettingsDialog(std::shared_ptr gui_settings, // GENERAL TAB { + connect(ui->horizontalVolumeSlider, &QSlider::valueChanged, this, [this](int value) { + VolumeSliderChange(value); + Config::setVolumeSlider(value); + Libraries::AudioOut::AdjustVol(); + }); + #ifdef ENABLE_UPDATER #if (QT_VERSION < QT_VERSION_CHECK(6, 7, 0)) connect(ui->updateCheckBox, &QCheckBox::stateChanged, this, [this](int state) { @@ -398,6 +408,8 @@ void SettingsDialog::closeEvent(QCloseEvent* event) { if (!is_saving) { ui->backgroundImageOpacitySlider->setValue(backgroundImageOpacitySlider_backup); emit BackgroundOpacityChanged(backgroundImageOpacitySlider_backup); + ui->horizontalVolumeSlider->setValue(volume_slider_backup); + Config::setVolumeSlider(volume_slider_backup); ui->BGMVolumeSlider->setValue(bgm_volume_backup); BackgroundMusicPlayer::getInstance().setVolume(bgm_volume_backup); } @@ -463,6 +475,8 @@ void SettingsDialog::LoadValuesFromConfig() { ui->radioButton_Bottom->setChecked(side == "bottom"); ui->BGMVolumeSlider->setValue(m_gui_settings->GetValue(gui::gl_backgroundMusicVolume).toInt()); + ui->horizontalVolumeSlider->setValue(m_gui_settings->GetValue(gui::gl_VolumeSlider).toInt()); + ui->volumeText->setText(QString::number(ui->horizontalVolumeSlider->sliderPosition()) + "%"); ui->discordRPCCheckbox->setChecked( toml::find_or(data, "General", "enableDiscordRPC", true)); QString translatedText_FullscreenMode = @@ -532,7 +546,7 @@ void SettingsDialog::LoadValuesFromConfig() { toml::find_or(data, "Input", "isMotionControlsEnabled", true)); ui->removeFolderButton->setEnabled(!ui->gameFoldersListWidget->selectedItems().isEmpty()); - ResetInstallFolders(); + SyncRealTimeWidgetstoConfig(); ui->backgroundImageOpacitySlider->setValue( m_gui_settings->GetValue(gui::gl_backgroundImageOpacity).toInt()); ui->showBackgroundImageCheckBox->setChecked( @@ -541,6 +555,7 @@ void SettingsDialog::LoadValuesFromConfig() { backgroundImageOpacitySlider_backup = m_gui_settings->GetValue(gui::gl_backgroundImageOpacity).toInt(); bgm_volume_backup = m_gui_settings->GetValue(gui::gl_backgroundMusicVolume).toInt(); + volume_slider_backup = m_gui_settings->GetValue(gui::gl_VolumeSlider).toInt(); } void SettingsDialog::InitializeEmulatorLanguages() { @@ -599,6 +614,10 @@ void SettingsDialog::OnCursorStateChanged(s16 index) { } } +void SettingsDialog::VolumeSliderChange(int value) { + ui->volumeText->setText(QString::number(ui->horizontalVolumeSlider->sliderPosition()) + "%"); +} + int SettingsDialog::exec() { return QDialog::exec(); } @@ -719,7 +738,6 @@ bool SettingsDialog::eventFilter(QObject* obj, QEvent* event) { if (qobject_cast(obj)) { bool hovered = (event->type() == QEvent::Enter); QString elementName = obj->objectName(); - if (hovered) { updateNoteTextEdit(elementName); } else { @@ -759,6 +777,7 @@ void SettingsDialog::UpdateSettings() { Config::setCursorState(ui->hideCursorComboBox->currentIndex()); Config::setCursorHideTimeout(ui->idleTimeoutSpinBox->value()); Config::setGpuId(ui->graphicsAdapterBox->currentIndex() - 1); + m_gui_settings->SetValue(gui::gl_VolumeSlider, ui->horizontalVolumeSlider->value()); m_gui_settings->SetValue(gui::gl_backgroundMusicVolume, ui->BGMVolumeSlider->value()); Config::setLanguage(languageIndexes[ui->consoleLanguageComboBox->currentIndex()]); Config::setEnableDiscordRPC(ui->discordRPCCheckbox->isChecked()); @@ -815,9 +834,10 @@ void SettingsDialog::UpdateSettings() { #endif BackgroundMusicPlayer::getInstance().setVolume(ui->BGMVolumeSlider->value()); + Config::setVolumeSlider(ui->horizontalVolumeSlider->value()); } -void SettingsDialog::ResetInstallFolders() { +void SettingsDialog::SyncRealTimeWidgetstoConfig() { ui->gameFoldersListWidget->clear(); std::filesystem::path userdir = Common::FS::GetUserPath(Common::FS::PathType::UserDir); @@ -865,6 +885,7 @@ void SettingsDialog::setDefaultValues() { m_gui_settings->SetValue(gui::gl_backgroundImageOpacity, 50); m_gui_settings->SetValue(gui::gl_playBackgroundMusic, false); m_gui_settings->SetValue(gui::gl_backgroundMusicVolume, 50); + m_gui_settings->SetValue(gui::gl_VolumeSlider, 100); m_gui_settings->SetValue(gui::gen_checkForUpdates, false); m_gui_settings->SetValue(gui::gen_showChangeLog, false); if (Common::g_is_release) { @@ -873,4 +894,4 @@ void SettingsDialog::setDefaultValues() { m_gui_settings->SetValue(gui::gen_updateChannel, "Nightly"); } m_gui_settings->SetValue(gui::gen_guiLanguage, "en_US"); -} \ No newline at end of file +} diff --git a/src/qt_gui/settings_dialog.h b/src/qt_gui/settings_dialog.h index d9fbcb214..13fab36a2 100644 --- a/src/qt_gui/settings_dialog.h +++ b/src/qt_gui/settings_dialog.h @@ -39,12 +39,13 @@ signals: private: void LoadValuesFromConfig(); void UpdateSettings(); - void ResetInstallFolders(); + void SyncRealTimeWidgetstoConfig(); void InitializeEmulatorLanguages(); void OnLanguageChanged(int index); void OnCursorStateChanged(s16 index); void closeEvent(QCloseEvent* event) override; void setDefaultValues(); + void VolumeSliderChange(int value); std::unique_ptr ui; diff --git a/src/qt_gui/settings_dialog.ui b/src/qt_gui/settings_dialog.ui index 8d239b58c..9ebb1cbc1 100644 --- a/src/qt_gui/settings_dialog.ui +++ b/src/qt_gui/settings_dialog.ui @@ -59,7 +59,7 @@ - 5 + 0 @@ -73,8 +73,8 @@ 0 0 - 946 - 536 + 944 + 537 @@ -86,148 +86,7 @@ 6 - - - - 0 - - - 0 - - - 0 - - - - - - 0 - 0 - - - - Emulator - - - false - - - false - - - - 6 - - - 9 - - - 9 - - - 9 - - - 9 - - - - - 10 - - - - - Show Splash - - - - - - - Enable Discord Rich Presence - - - - - - - - - - - - - - 6 - - - 0 - - - - - - 0 - 0 - - - - - 0 - 0 - - - - System - - - - 70 - - - - - Console Language - - - - - - - - - - - - Emulator Language - - - - - - - - - - - - - - - - - Qt::Orientation::Vertical - - - - 20 - 40 - - - - - + Qt::Orientation::Vertical @@ -240,8 +99,8 @@ - - + + Qt::Orientation::Vertical @@ -253,7 +112,7 @@ - + 6 @@ -427,6 +286,228 @@ + + + + 6 + + + 0 + + + + + + 0 + 0 + + + + + 0 + 0 + + + + System + + + + 70 + + + + + Console Language + + + + + + + + + + + + Emulator Language + + + + + + + + + + + + + + + + + Volume + + + + + + + 0 + 0 + + + + + + + true + + + + + 0 + 0 + 414 + 69 + + + + + + + + 0 + 0 + + + + + 60 + 16777215 + + + + 100% + + + Qt::AlignmentFlag::AlignCenter + + + true + + + + + + + Qt::FocusPolicy::StrongFocus + + + 500 + + + 100 + + + 100 + + + Qt::Orientation::Horizontal + + + + + + + + + + + + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + Emulator + + + false + + + false + + + + 6 + + + 9 + + + 9 + + + 9 + + + 9 + + + + + 10 + + + + + Show Splash + + + + + + + Enable Discord Rich Presence + + + + + + + + + + + + + + Qt::Orientation::Vertical + + + + 20 + 40 + + + + @@ -444,8 +525,8 @@ 0 0 - 946 - 536 + 944 + 537 @@ -700,7 +781,7 @@ - + 0 0 @@ -893,8 +974,8 @@ 0 0 - 946 - 536 + 944 + 537 @@ -1188,8 +1269,8 @@ 0 0 - 946 - 536 + 944 + 537 @@ -1430,8 +1511,8 @@ 0 0 - 946 - 536 + 944 + 537 @@ -1684,8 +1765,8 @@ 0 0 - 946 - 536 + 944 + 537 @@ -1826,8 +1907,8 @@ 0 0 - 946 - 536 + 944 + 537 diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_atomic.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_atomic.cpp index 80c8b836b..fe2d64d2f 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_atomic.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_atomic.cpp @@ -268,6 +268,7 @@ Id EmitBufferAtomicFMin32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id addre const auto sign_bit_set = ctx.OpBitFieldUExtract(ctx.U32[1], u32_value, ctx.ConstU32(31u), ctx.ConstU32(1u)); + // FIXME this needs control flow because it currently executes both atomics const auto result = ctx.OpSelect( ctx.F32[1], sign_bit_set, EmitBitCastF32U32(ctx, EmitBufferAtomicUMax32(ctx, inst, handle, address, u32_value)), @@ -302,6 +303,7 @@ Id EmitBufferAtomicFMax32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id addre const auto sign_bit_set = ctx.OpBitFieldUExtract(ctx.U32[1], u32_value, ctx.ConstU32(31u), ctx.ConstU32(1u)); + // FIXME this needs control flow because it currently executes both atomics const auto result = ctx.OpSelect( ctx.F32[1], sign_bit_set, EmitBitCastF32U32(ctx, EmitBufferAtomicUMin32(ctx, inst, handle, address, u32_value)), diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_convert.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_convert.cpp index c75f43393..2f4984f57 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_convert.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_convert.cpp @@ -7,60 +7,32 @@ namespace Shader::Backend::SPIRV { namespace { Id ExtractU16(EmitContext& ctx, Id value) { - if (ctx.profile.support_int16) { - return ctx.OpUConvert(ctx.U16, value); - } else { - return ctx.OpBitFieldUExtract(ctx.U32[1], value, ctx.u32_zero_value, ctx.ConstU32(16u)); - } + return ctx.OpUConvert(ctx.U16, value); } Id ExtractS16(EmitContext& ctx, Id value) { - if (ctx.profile.support_int16) { - return ctx.OpSConvert(ctx.S16, value); - } else { - return ctx.OpBitFieldSExtract(ctx.U32[1], value, ctx.u32_zero_value, ctx.ConstU32(16u)); - } + return ctx.OpSConvert(ctx.S16, value); } Id ExtractU8(EmitContext& ctx, Id value) { - if (ctx.profile.support_int8) { - return ctx.OpUConvert(ctx.U8, value); - } else { - return ctx.OpBitFieldUExtract(ctx.U32[1], value, ctx.u32_zero_value, ctx.ConstU32(8u)); - } + return ctx.OpUConvert(ctx.U8, value); } Id ExtractS8(EmitContext& ctx, Id value) { - if (ctx.profile.support_int8) { - return ctx.OpSConvert(ctx.S8, value); - } else { - return ctx.OpBitFieldSExtract(ctx.U32[1], value, ctx.u32_zero_value, ctx.ConstU32(8u)); - } + return ctx.OpSConvert(ctx.S8, value); } } // Anonymous namespace Id EmitConvertS16F16(EmitContext& ctx, Id value) { - if (ctx.profile.support_int16) { - return ctx.OpSConvert(ctx.U32[1], ctx.OpConvertFToS(ctx.U16, value)); - } else { - return ExtractS16(ctx, ctx.OpConvertFToS(ctx.U32[1], value)); - } + return ctx.OpSConvert(ctx.U32[1], ctx.OpConvertFToS(ctx.U16, value)); } Id EmitConvertS16F32(EmitContext& ctx, Id value) { - if (ctx.profile.support_int16) { - return ctx.OpSConvert(ctx.U32[1], ctx.OpConvertFToS(ctx.U16, value)); - } else { - return ExtractS16(ctx, ctx.OpConvertFToS(ctx.U32[1], value)); - } + return ctx.OpSConvert(ctx.U32[1], ctx.OpConvertFToS(ctx.U16, value)); } Id EmitConvertS16F64(EmitContext& ctx, Id value) { - if (ctx.profile.support_int16) { - return ctx.OpSConvert(ctx.U32[1], ctx.OpConvertFToS(ctx.U16, value)); - } else { - return ExtractS16(ctx, ctx.OpConvertFToS(ctx.U32[1], value)); - } + return ctx.OpSConvert(ctx.U32[1], ctx.OpConvertFToS(ctx.U16, value)); } Id EmitConvertS32F16(EmitContext& ctx, Id value) { @@ -88,27 +60,15 @@ Id EmitConvertS64F64(EmitContext& ctx, Id value) { } Id EmitConvertU16F16(EmitContext& ctx, Id value) { - if (ctx.profile.support_int16) { - return ctx.OpUConvert(ctx.U32[1], ctx.OpConvertFToU(ctx.U16, value)); - } else { - return ExtractU16(ctx, ctx.OpConvertFToU(ctx.U32[1], value)); - } + return ctx.OpUConvert(ctx.U32[1], ctx.OpConvertFToU(ctx.U16, value)); } Id EmitConvertU16F32(EmitContext& ctx, Id value) { - if (ctx.profile.support_int16) { - return ctx.OpUConvert(ctx.U32[1], ctx.OpConvertFToU(ctx.U16, value)); - } else { - return ExtractU16(ctx, ctx.OpConvertFToU(ctx.U32[1], value)); - } + return ctx.OpUConvert(ctx.U32[1], ctx.OpConvertFToU(ctx.U16, value)); } Id EmitConvertU16F64(EmitContext& ctx, Id value) { - if (ctx.profile.support_int16) { - return ctx.OpUConvert(ctx.U32[1], ctx.OpConvertFToU(ctx.U16, value)); - } else { - return ExtractU16(ctx, ctx.OpConvertFToU(ctx.U32[1], value)); - } + return ctx.OpUConvert(ctx.U32[1], ctx.OpConvertFToU(ctx.U16, value)); } Id EmitConvertU32F16(EmitContext& ctx, Id value) { @@ -271,4 +231,12 @@ Id EmitConvertU32U8(EmitContext& ctx, Id value) { return ctx.OpUConvert(ctx.U32[1], value); } +Id EmitConvertS32S8(EmitContext& ctx, Id value) { + return ctx.OpSConvert(ctx.U32[1], value); +} + +Id EmitConvertS32S16(EmitContext& ctx, Id value) { + return ctx.OpSConvert(ctx.U32[1], value); +} + } // namespace Shader::Backend::SPIRV diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h b/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h index 37d5d84c9..a8c58bdba 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h +++ b/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h @@ -488,6 +488,8 @@ Id EmitConvertU16U32(EmitContext& ctx, Id value); Id EmitConvertU32U16(EmitContext& ctx, Id value); Id EmitConvertU8U32(EmitContext& ctx, Id value); Id EmitConvertU32U8(EmitContext& ctx, Id value); +Id EmitConvertS32S8(EmitContext& ctx, Id value); +Id EmitConvertS32S16(EmitContext& ctx, Id value); Id EmitImageSampleRaw(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address1, Id address2, Id address3, Id address4); diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp index f373808d9..a8ffe6ae5 100644 --- a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp +++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp @@ -117,7 +117,9 @@ void EmitContext::DefineArithmeticTypes() { void_id = Name(TypeVoid(), "void_id"); U1[1] = Name(TypeBool(), "bool_id"); U8 = Name(TypeUInt(8), "u8_id"); + S8 = Name(TypeSInt(8), "i8_id"); U16 = Name(TypeUInt(16), "u16_id"); + S16 = Name(TypeSInt(16), "i16_id"); if (info.uses_fp16) { F16[1] = Name(TypeFloat(16), "f16_id"); U16 = Name(TypeUInt(16), "u16_id"); diff --git a/src/shader_recompiler/frontend/copy_shader.cpp b/src/shader_recompiler/frontend/copy_shader.cpp index 4b5869e1d..152ba196b 100644 --- a/src/shader_recompiler/frontend/copy_shader.cpp +++ b/src/shader_recompiler/frontend/copy_shader.cpp @@ -14,7 +14,7 @@ CopyShaderData ParseCopyShader(std::span code) { constexpr u32 token_mov_vcchi = 0xBEEB03FF; ASSERT_MSG(code[0] == token_mov_vcchi, "First instruction is not s_mov_b32 vcc_hi, #imm"); - std::array offsets{}; + std::array offsets{}; offsets.fill(-1); std::array sources{}; @@ -52,6 +52,8 @@ CopyShaderData ParseCopyShader(std::span code) { break; } case Gcn::Opcode::BUFFER_LOAD_DWORD: { + ASSERT_MSG(inst.src[1].code < offsets.size(), + "offsets array for geometry shaders is too short"); offsets[inst.src[1].code] = inst.control.mubuf.offset; if (inst.src[3].field != Gcn::OperandField::ConstZero) { const u32 index = inst.src[3].code; @@ -65,7 +67,7 @@ CopyShaderData ParseCopyShader(std::span code) { } } - if (last_attr != IR::Attribute::Position0) { + if (!IsPosition(last_attr)) { data.num_attrs = static_cast(last_attr) - static_cast(IR::Attribute::Param0) + 1; const auto it = data.attr_map.begin(); const u32 comp_stride = std::next(it)->first - it->first; diff --git a/src/shader_recompiler/frontend/translate/translate.h b/src/shader_recompiler/frontend/translate/translate.h index a29bdc993..ae904b822 100644 --- a/src/shader_recompiler/frontend/translate/translate.h +++ b/src/shader_recompiler/frontend/translate/translate.h @@ -281,9 +281,10 @@ public: // 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); + void BUFFER_LOAD(u32 num_dwords, bool is_inst_typed, bool is_buffer_typed, const GcnInst& inst, + u32 scalar_width = 32, bool is_signed = false); + void BUFFER_STORE(u32 num_dwords, bool is_inst_typed, bool is_buffer_typed, const GcnInst& inst, + u32 scalar_width = 32); template void BUFFER_ATOMIC(AtomicOp op, const GcnInst& inst); diff --git a/src/shader_recompiler/frontend/translate/vector_memory.cpp b/src/shader_recompiler/frontend/translate/vector_memory.cpp index df20f7f73..ec9bc200d 100644 --- a/src/shader_recompiler/frontend/translate/vector_memory.cpp +++ b/src/shader_recompiler/frontend/translate/vector_memory.cpp @@ -28,6 +28,15 @@ void Translator::EmitVectorMemory(const GcnInst& inst) { case Opcode::BUFFER_LOAD_FORMAT_XYZW: return BUFFER_LOAD(4, false, true, inst); + case Opcode::BUFFER_LOAD_UBYTE: + return BUFFER_LOAD(1, false, false, inst, 8, false); + case Opcode::BUFFER_LOAD_SBYTE: + return BUFFER_LOAD(1, false, false, inst, 8, true); + case Opcode::BUFFER_LOAD_USHORT: + return BUFFER_LOAD(1, false, false, inst, 16, false); + case Opcode::BUFFER_LOAD_SSHORT: + return BUFFER_LOAD(1, false, false, inst, 16, true); + case Opcode::BUFFER_LOAD_DWORD: return BUFFER_LOAD(1, false, false, inst); case Opcode::BUFFER_LOAD_DWORDX2: @@ -56,6 +65,11 @@ void Translator::EmitVectorMemory(const GcnInst& inst) { case Opcode::TBUFFER_STORE_FORMAT_XYZW: return BUFFER_STORE(4, true, false, inst); + case Opcode::BUFFER_STORE_BYTE: + return BUFFER_STORE(1, false, false, inst, 8); + case Opcode::BUFFER_STORE_SHORT: + return BUFFER_STORE(1, false, false, inst, 16); + case Opcode::BUFFER_STORE_DWORD: return BUFFER_STORE(1, false, false, inst); case Opcode::BUFFER_STORE_DWORDX2: @@ -186,7 +200,7 @@ void Translator::EmitVectorMemory(const GcnInst& inst) { } void Translator::BUFFER_LOAD(u32 num_dwords, bool is_inst_typed, bool is_buffer_typed, - const GcnInst& inst) { + const GcnInst& inst, u32 scalar_width, bool is_signed) { const auto& mubuf = inst.control.mubuf; const bool is_ring = mubuf.glc && mubuf.slc; const IR::VectorReg vaddr{inst.src[0].code}; @@ -242,7 +256,26 @@ void Translator::BUFFER_LOAD(u32 num_dwords, bool is_inst_typed, bool is_buffer_ ir.SetVectorReg(dst_reg + i, IR::F32{ir.CompositeExtract(value, i)}); } } else { - const IR::Value value = ir.LoadBufferU32(num_dwords, handle, address, buffer_info); + IR::Value value; + switch (scalar_width) { + case 8: { + IR::U8 byte_val = ir.LoadBufferU8(handle, address, buffer_info); + value = is_signed ? ir.SConvert(32, byte_val) : ir.UConvert(32, byte_val); + break; + } + case 16: { + IR::U16 short_val = ir.LoadBufferU16(handle, address, buffer_info); + value = is_signed ? ir.SConvert(32, short_val) : ir.UConvert(32, short_val); + break; + } + case 32: + value = ir.LoadBufferU32(num_dwords, handle, address, buffer_info); + break; + + default: + UNREACHABLE(); + } + if (num_dwords == 1) { ir.SetVectorReg(dst_reg, IR::U32{value}); return; @@ -254,7 +287,7 @@ void Translator::BUFFER_LOAD(u32 num_dwords, bool is_inst_typed, bool is_buffer_ } void Translator::BUFFER_STORE(u32 num_dwords, bool is_inst_typed, bool is_buffer_typed, - const GcnInst& inst) { + const GcnInst& inst, u32 scalar_width) { const auto& mubuf = inst.control.mubuf; const bool is_ring = mubuf.glc && mubuf.slc; const IR::VectorReg vaddr{inst.src[0].code}; @@ -314,8 +347,23 @@ void Translator::BUFFER_STORE(u32 num_dwords, bool is_inst_typed, bool is_buffer } ir.StoreBufferFormat(handle, address, ir.CompositeConstruct(comps), buffer_info); } else { - const auto value = num_dwords == 1 ? comps[0] : ir.CompositeConstruct(comps); - ir.StoreBufferU32(num_dwords, handle, address, value, buffer_info); + IR::Value value = num_dwords == 1 ? comps[0] : ir.CompositeConstruct(comps); + if (scalar_width != 32) { + value = ir.UConvert(scalar_width, IR::U32{value}); + } + switch (scalar_width) { + case 8: + ir.StoreBufferU8(handle, address, IR::U8{value}, buffer_info); + break; + case 16: + ir.StoreBufferU16(handle, address, IR::U16{value}, buffer_info); + break; + case 32: + ir.StoreBufferU32(num_dwords, handle, address, value, buffer_info); + break; + default: + UNREACHABLE(); + } } } diff --git a/src/shader_recompiler/ir/ir_emitter.cpp b/src/shader_recompiler/ir/ir_emitter.cpp index 6ca86b2c0..a6d43d102 100644 --- a/src/shader_recompiler/ir/ir_emitter.cpp +++ b/src/shader_recompiler/ir/ir_emitter.cpp @@ -1979,6 +1979,24 @@ U8U16U32U64 IREmitter::UConvert(size_t result_bitsize, const U8U16U32U64& value) throw NotImplementedException("Conversion from {} to {} bits", value.Type(), result_bitsize); } +U8U16U32U64 IR::IREmitter::SConvert(size_t result_bitsize, const U8U16U32U64& value) { + switch (result_bitsize) { + case 32: + switch (value.Type()) { + case Type::U8: + return Inst(Opcode::ConvertS32S8, value); + case Type::U16: + return Inst(Opcode::ConvertS32S16, value); + default: + break; + } + default: + break; + } + throw NotImplementedException("Signed Conversion from {} to {} bits", value.Type(), + result_bitsize); +} + F16F32F64 IREmitter::FPConvert(size_t result_bitsize, const F16F32F64& value) { switch (result_bitsize) { case 16: diff --git a/src/shader_recompiler/ir/ir_emitter.h b/src/shader_recompiler/ir/ir_emitter.h index a105b042d..e4afb8739 100644 --- a/src/shader_recompiler/ir/ir_emitter.h +++ b/src/shader_recompiler/ir/ir_emitter.h @@ -325,6 +325,7 @@ public: const Value& value); [[nodiscard]] U8U16U32U64 UConvert(size_t result_bitsize, const U8U16U32U64& value); + [[nodiscard]] U8U16U32U64 SConvert(size_t result_bitsize, const U8U16U32U64& value); [[nodiscard]] F16F32F64 FPConvert(size_t result_bitsize, const F16F32F64& value); [[nodiscard]] Value ImageAtomicIAdd(const Value& handle, const Value& coords, diff --git a/src/shader_recompiler/ir/opcodes.inc b/src/shader_recompiler/ir/opcodes.inc index 747a27e35..280cd47ec 100644 --- a/src/shader_recompiler/ir/opcodes.inc +++ b/src/shader_recompiler/ir/opcodes.inc @@ -432,6 +432,8 @@ OPCODE(ConvertU16U32, U16, U32, OPCODE(ConvertU32U16, U32, U16, ) OPCODE(ConvertU8U32, U8, U32, ) OPCODE(ConvertU32U8, U32, U8, ) +OPCODE(ConvertS32S8, U32, U8, ) +OPCODE(ConvertS32S16, U32, U16, ) // Image operations OPCODE(ImageSampleRaw, F32x4, Opaque, F32x4, F32x4, F32x4, F32, Opaque, ) diff --git a/src/shader_recompiler/runtime_info.h b/src/shader_recompiler/runtime_info.h index 6cede44a8..61954bec2 100644 --- a/src/shader_recompiler/runtime_info.h +++ b/src/shader_recompiler/runtime_info.h @@ -171,6 +171,7 @@ enum class MrtSwizzle : u8 { static constexpr u32 MaxColorBuffers = 8; struct PsColorBuffer { + AmdGpu::DataFormat data_format : 6; AmdGpu::NumberFormat num_format : 4; AmdGpu::NumberConversion num_conversion : 3; AmdGpu::Liverpool::ShaderExportFormat export_format : 4; diff --git a/src/video_core/amdgpu/pixel_format.h b/src/video_core/amdgpu/pixel_format.h index bd0f778f4..45c688e57 100644 --- a/src/video_core/amdgpu/pixel_format.h +++ b/src/video_core/amdgpu/pixel_format.h @@ -248,6 +248,15 @@ constexpr CompMapping RemapSwizzle(const DataFormat format, const CompMapping sw result.a = swizzle.r; return result; } + case DataFormat::Format5_6_5: { + // Remap to a more supported component order. + CompMapping result; + result.r = swizzle.b; + result.g = swizzle.g; + result.b = swizzle.r; + result.a = swizzle.a; + return result; + } default: return swizzle; } diff --git a/src/video_core/amdgpu/pm4_cmds.h b/src/video_core/amdgpu/pm4_cmds.h index 23c1b8f21..3df73f3d6 100644 --- a/src/video_core/amdgpu/pm4_cmds.h +++ b/src/video_core/amdgpu/pm4_cmds.h @@ -699,7 +699,7 @@ struct PM4CmdWaitRegMem { struct PM4CmdWriteData { PM4Type3Header header; union { - BitField<8, 11, u32> dst_sel; + BitField<8, 4, u32> dst_sel; BitField<16, 1, u32> wr_one_addr; BitField<20, 1, u32> wr_confirm; BitField<30, 1, u32> engine_sel; diff --git a/src/video_core/buffer_cache/buffer_cache.cpp b/src/video_core/buffer_cache/buffer_cache.cpp index 42e3c61a5..c5e5d18f8 100644 --- a/src/video_core/buffer_cache/buffer_cache.cpp +++ b/src/video_core/buffer_cache/buffer_cache.cpp @@ -961,15 +961,15 @@ bool BufferCache::SynchronizeBufferFromImage(Buffer& buffer, VAddr device_addr, const u32 height = std::max(image.info.size.height >> m, 1u); const u32 depth = image.info.props.is_volume ? std::max(image.info.size.depth >> m, 1u) : 1u; - const auto& [mip_size, mip_pitch, mip_height, mip_ofs] = image.info.mips_layout[m]; + const auto [mip_size, mip_pitch, mip_height, mip_ofs] = image.info.mips_layout[m]; offset += mip_ofs; if (offset + mip_size > max_offset) { break; } copies.push_back({ .bufferOffset = offset, - .bufferRowLength = static_cast(mip_pitch), - .bufferImageHeight = static_cast(mip_height), + .bufferRowLength = mip_pitch, + .bufferImageHeight = mip_height, .imageSubresource{ .aspectMask = image.aspect_mask & ~vk::ImageAspectFlagBits::eStencil, .mipLevel = m, diff --git a/src/video_core/renderer_vulkan/liverpool_to_vk.cpp b/src/video_core/renderer_vulkan/liverpool_to_vk.cpp index cd597e16c..51411be7f 100644 --- a/src/video_core/renderer_vulkan/liverpool_to_vk.cpp +++ b/src/video_core/renderer_vulkan/liverpool_to_vk.cpp @@ -671,7 +671,7 @@ std::span SurfaceFormats() { vk::Format::eR32G32B32A32Sfloat), // 5_6_5 CreateSurfaceFormatInfo(AmdGpu::DataFormat::Format5_6_5, AmdGpu::NumberFormat::Unorm, - vk::Format::eB5G6R5UnormPack16), + vk::Format::eR5G6B5UnormPack16), // 1_5_5_5 CreateSurfaceFormatInfo(AmdGpu::DataFormat::Format1_5_5_5, AmdGpu::NumberFormat::Unorm, vk::Format::eA1R5G5B5UnormPack16), diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp index 4d89c83b2..8094bc260 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp @@ -244,9 +244,24 @@ GraphicsPipeline::GraphicsPipeline( const auto depth_format = instance.GetSupportedFormat(LiverpoolToVK::DepthFormat(key.z_format, key.stencil_format), vk::FormatFeatureFlagBits2::eDepthStencilAttachment); + std::array color_formats; + for (s32 i = 0; i < key.num_color_attachments; ++i) { + const auto& col_buf = key.color_buffers[i]; + const auto format = LiverpoolToVK::SurfaceFormat(col_buf.data_format, col_buf.num_format); + const auto color_format = + instance.GetSupportedFormat(format, vk::FormatFeatureFlagBits2::eColorAttachment); + if (!instance.IsFormatSupported(color_format, + vk::FormatFeatureFlagBits2::eColorAttachment)) { + LOG_WARNING(Render_Vulkan, + "color buffer format {} does not support COLOR_ATTACHMENT_BIT", + vk::to_string(color_format)); + } + color_formats[i] = color_format; + } + const vk::PipelineRenderingCreateInfo pipeline_rendering_ci = { .colorAttachmentCount = key.num_color_attachments, - .pColorAttachmentFormats = key.color_formats.data(), + .pColorAttachmentFormats = color_formats.data(), .depthAttachmentFormat = key.z_format != Liverpool::DepthBuffer::ZFormat::Invalid ? depth_format : vk::Format::eUndefined, diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.h b/src/video_core/renderer_vulkan/vk_graphics_pipeline.h index 75b8c8c73..aaf47ba7d 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.h +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.h @@ -36,7 +36,6 @@ struct GraphicsPipelineKey { std::array vertex_buffer_formats; u32 patch_control_points; u32 num_color_attachments; - std::array color_formats; std::array color_buffers; std::array blend_controls; std::array write_masks; diff --git a/src/video_core/renderer_vulkan/vk_instance.cpp b/src/video_core/renderer_vulkan/vk_instance.cpp index 119c0a367..6e5351ddd 100644 --- a/src/video_core/renderer_vulkan/vk_instance.cpp +++ b/src/video_core/renderer_vulkan/vk_instance.cpp @@ -669,6 +669,12 @@ vk::Format Instance::GetSupportedFormat(const vk::Format format, if (IsFormatSupported(vk::Format::eD32SfloatS8Uint, flags)) { return vk::Format::eD32SfloatS8Uint; } + break; + case vk::Format::eR8Srgb: + if (IsFormatSupported(vk::Format::eR8Unorm, flags)) { + return vk::Format::eR8Unorm; + } + break; default: break; } diff --git a/src/video_core/renderer_vulkan/vk_instance.h b/src/video_core/renderer_vulkan/vk_instance.h index 9be2d9520..b21e00a71 100644 --- a/src/video_core/renderer_vulkan/vk_instance.h +++ b/src/video_core/renderer_vulkan/vk_instance.h @@ -94,6 +94,21 @@ public: return features.shaderFloat64; } + /// Returns true if 64-bit ints are supported in shaders + bool IsShaderInt64Supported() const { + return features.shaderInt64; + } + + /// Returns true if 16-bit ints are supported in shaders + bool IsShaderInt16Supported() const { + return features.shaderInt16; + } + + /// Returns true if 8-bit ints are supported in shaders + bool IsShaderInt8Supported() const { + return vk12_features.shaderInt8; + } + /// Returns true when VK_EXT_custom_border_color is supported bool IsCustomBorderColorSupported() const { return custom_border_color; diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index 4de8fd73b..bce16cbff 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -203,6 +203,9 @@ PipelineCache::PipelineCache(const Instance& instance_, Scheduler& scheduler_, profile = Shader::Profile{ .supported_spirv = SpirvVersion1_6, .subgroup_size = instance.SubgroupSize(), + .support_int8 = instance.IsShaderInt8Supported(), + .support_int16 = instance.IsShaderInt16Supported(), + .support_int64 = instance.IsShaderInt64Supported(), .support_float64 = instance.IsShaderFloat64Supported(), .support_fp32_denorm_preserve = bool(vk12_props.shaderDenormPreserveFloat32), .support_fp32_denorm_flush = bool(vk12_props.shaderDenormFlushToZeroFloat32), @@ -312,7 +315,6 @@ bool PipelineCache::RefreshGraphicsKey() { // attachments. This might be not a case as HW color buffers can be bound in an arbitrary // order. We need to do some arrays compaction at this stage key.num_color_attachments = 0; - key.color_formats.fill(vk::Format::eUndefined); key.color_buffers.fill({}); key.blend_controls.fill({}); key.write_masks.fill({}); @@ -348,16 +350,8 @@ bool PipelineCache::RefreshGraphicsKey() { col_buf.GetDataFmt() == AmdGpu::DataFormat::Format8_8 || col_buf.GetDataFmt() == AmdGpu::DataFormat::Format8_8_8_8); - const auto format = - LiverpoolToVK::SurfaceFormat(col_buf.GetDataFmt(), col_buf.GetNumberFmt()); - key.color_formats[remapped_cb] = format; - if (!instance.IsFormatSupported(format, vk::FormatFeatureFlagBits2::eColorAttachment)) { - LOG_WARNING(Render_Vulkan, - "color buffer format {} does not support COLOR_ATTACHMENT_BIT", - vk::to_string(format)); - } - key.color_buffers[remapped_cb] = Shader::PsColorBuffer{ + .data_format = col_buf.GetDataFmt(), .num_format = col_buf.GetNumberFmt(), .num_conversion = col_buf.GetNumberConversion(), .export_format = regs.color_export_format.GetFormat(cb), @@ -476,9 +470,7 @@ bool PipelineCache::RefreshGraphicsKey() { // Attachment is masked out by either color_target_mask or shader mrt_mask. In the case // of the latter we need to change format to undefined, and either way we need to // increment the index for the null attachment binding. - key.color_formats[remapped_cb] = vk::Format::eUndefined; - key.color_buffers[remapped_cb] = {}; - ++remapped_cb; + key.color_buffers[remapped_cb++] = {}; continue; } diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index c5f894b10..c3e221739 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -1211,11 +1211,13 @@ void Rasterizer::UpdateDepthStencilState() const { } : front_ops; dynamic_state.SetStencilOps(front_ops, back_ops); + const bool stencil_clear = regs.depth_render_control.stencil_clear_enable; const auto front = regs.stencil_ref_front; const auto back = regs.depth_control.backface_enable ? regs.stencil_ref_back : regs.stencil_ref_front; dynamic_state.SetStencilReferences(front.stencil_test_val, back.stencil_test_val); - dynamic_state.SetStencilWriteMasks(front.stencil_write_mask, back.stencil_write_mask); + dynamic_state.SetStencilWriteMasks(!stencil_clear ? front.stencil_write_mask.Value() : 0U, + !stencil_clear ? back.stencil_write_mask.Value() : 0U); dynamic_state.SetStencilCompareMasks(front.stencil_mask, back.stencil_mask); } } diff --git a/src/video_core/texture_cache/blit_helper.cpp b/src/video_core/texture_cache/blit_helper.cpp index 1ad41be00..4f1d17547 100644 --- a/src/video_core/texture_cache/blit_helper.cpp +++ b/src/video_core/texture_cache/blit_helper.cpp @@ -126,13 +126,13 @@ void BlitHelper::BlitColorToMsDepth(Image& source, Image& dest) { .minDepth = 0.f, .maxDepth = 1.f, }; - cmdbuf.setViewport(0, viewport); + cmdbuf.setViewportWithCount(viewport); const vk::Rect2D scissor = { .offset = {0, 0}, .extent = {state.width, state.height}, }; - cmdbuf.setScissor(0, scissor); + cmdbuf.setScissorWithCount(scissor); cmdbuf.draw(3, 1, 0, 0); diff --git a/src/video_core/texture_cache/image.cpp b/src/video_core/texture_cache/image.cpp index 7b8ff4403..4ab2e991c 100644 --- a/src/video_core/texture_cache/image.cpp +++ b/src/video_core/texture_cache/image.cpp @@ -21,7 +21,7 @@ static vk::ImageUsageFlags ImageUsageFlags(const ImageInfo& info) { if (info.IsDepthStencil()) { usage |= vk::ImageUsageFlagBits::eDepthStencilAttachment; } else { - if (!info.IsBlockCoded() && !info.IsPacked()) { + if (!info.IsBlockCoded()) { usage |= vk::ImageUsageFlagBits::eColorAttachment; } // In cases where an image is created as a render/depth target and cleared with compute, diff --git a/src/video_core/texture_cache/image_info.cpp b/src/video_core/texture_cache/image_info.cpp index 769c4211f..ed10a20bf 100644 --- a/src/video_core/texture_cache/image_info.cpp +++ b/src/video_core/texture_cache/image_info.cpp @@ -176,17 +176,6 @@ bool ImageInfo::IsBlockCoded() const { } } -bool ImageInfo::IsPacked() const { - switch (pixel_format) { - case vk::Format::eB5G5R5A1UnormPack16: - [[fallthrough]]; - case vk::Format::eB5G6R5UnormPack16: - return true; - default: - return false; - } -} - bool ImageInfo::IsDepthStencil() const { switch (pixel_format) { case vk::Format::eD16Unorm: diff --git a/src/video_core/texture_cache/image_info.h b/src/video_core/texture_cache/image_info.h index dbd7f7cbb..9fa3b6c3d 100644 --- a/src/video_core/texture_cache/image_info.h +++ b/src/video_core/texture_cache/image_info.h @@ -31,7 +31,6 @@ struct ImageInfo { } bool IsBlockCoded() const; - bool IsPacked() const; bool IsDepthStencil() const; bool HasStencil() const; diff --git a/src/video_core/texture_cache/texture_cache.cpp b/src/video_core/texture_cache/texture_cache.cpp index a6657d8d9..fa24728ad 100644 --- a/src/video_core/texture_cache/texture_cache.cpp +++ b/src/video_core/texture_cache/texture_cache.cpp @@ -605,28 +605,27 @@ void TextureCache::RefreshImage(Image& image, Vulkan::Scheduler* custom_schedule const u32 height = std::max(image.info.size.height >> m, 1u); const u32 depth = image.info.props.is_volume ? std::max(image.info.size.depth >> m, 1u) : 1u; - const auto& mip = image.info.mips_layout[m]; + const auto [mip_size, mip_pitch, mip_height, mip_offset] = image.info.mips_layout[m]; // Protect GPU modified resources from accidental CPU reuploads. if (is_gpu_modified && !is_gpu_dirty) { const u8* addr = std::bit_cast(image.info.guest_address); - const u64 hash = XXH3_64bits(addr + mip.offset, mip.size); + const u64 hash = XXH3_64bits(addr + mip_offset, mip_size); if (image.mip_hashes[m] == hash) { continue; } image.mip_hashes[m] = hash; } - auto mip_pitch = static_cast(mip.pitch); - auto mip_height = static_cast(mip.height); - - auto image_extent_width = mip_pitch ? std::min(mip_pitch, width) : width; - auto image_extent_height = mip_height ? std::min(mip_height, height) : height; + const u32 extent_width = mip_pitch ? std::min(mip_pitch, width) : width; + const u32 extent_height = mip_height ? std::min(mip_height, height) : height; + const u32 height_aligned = + mip_height && image.info.IsTiled() ? std::max(mip_height, 8U) : mip_height; image_copy.push_back({ - .bufferOffset = mip.offset, + .bufferOffset = mip_offset, .bufferRowLength = mip_pitch, - .bufferImageHeight = mip_height, + .bufferImageHeight = height_aligned, .imageSubresource{ .aspectMask = image.aspect_mask & ~vk::ImageAspectFlagBits::eStencil, .mipLevel = m, @@ -634,7 +633,7 @@ void TextureCache::RefreshImage(Image& image, Vulkan::Scheduler* custom_schedule .layerCount = num_layers, }, .imageOffset = {0, 0, 0}, - .imageExtent = {image_extent_width, image_extent_height, depth}, + .imageExtent = {extent_width, extent_height, depth}, }); }