diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 6b87ec418..44aa3dd37 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -43,6 +43,7 @@ jobs: outputs: date: ${{ steps.vars.outputs.date }} shorthash: ${{ steps.vars.outputs.shorthash }} + fullhash: ${{ steps.vars.outputs.fullhash }} steps: - uses: actions/checkout@v4 - name: Get date and git hash @@ -50,8 +51,10 @@ jobs: run: | echo "date=$(date +'%Y-%m-%d')" >> $GITHUB_ENV echo "shorthash=$(git rev-parse --short HEAD)" >> $GITHUB_ENV + echo "fullhash=$(git rev-parse HEAD)" >> $GITHUB_ENV echo "date=$(date +'%Y-%m-%d')" >> $GITHUB_OUTPUT echo "shorthash=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT + echo "fullhash=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT windows-sdl: runs-on: windows-latest @@ -287,7 +290,7 @@ jobs: submodules: recursive - name: Install dependencies - run: sudo apt-get update && sudo apt install -y libx11-dev libxext-dev libwayland-dev libdecor-0-dev libxkbcommon-dev libglfw3-dev libgles2-mesa-dev libfuse2 clang build-essential libasound2-dev libpulse-dev libopenal-dev + run: sudo apt-get update && sudo apt install -y libx11-dev libxext-dev libwayland-dev libdecor-0-dev libxkbcommon-dev libglfw3-dev libgles2-mesa-dev libfuse2 clang build-essential libasound2-dev libpulse-dev libopenal-dev libudev-dev - name: Cache CMake Configuration uses: actions/cache@v4 @@ -343,7 +346,7 @@ jobs: submodules: recursive - name: Install dependencies - run: sudo apt-get update && sudo apt install -y libx11-dev libxext-dev libwayland-dev libdecor-0-dev libxkbcommon-dev libglfw3-dev libgles2-mesa-dev libfuse2 clang build-essential qt6-base-dev qt6-tools-dev qt6-multimedia-dev libasound2-dev libpulse-dev libopenal-dev + run: sudo apt-get update && sudo apt install -y libx11-dev libxext-dev libwayland-dev libdecor-0-dev libxkbcommon-dev libglfw3-dev libgles2-mesa-dev libfuse2 clang build-essential qt6-base-dev qt6-tools-dev qt6-multimedia-dev libasound2-dev libpulse-dev libopenal-dev libudev-dev - name: Cache CMake Configuration uses: actions/cache@v4 @@ -420,9 +423,62 @@ jobs: tag: "Pre-release-shadPS4-${{ needs.get-info.outputs.date }}-${{ needs.get-info.outputs.shorthash }}" draft: false prerelease: true - body: "Full Changelog: [${{ env.last_release_tag }}...${{ needs.get-info.outputs.shorthash }}](https://github.com/shadps4-emu/shadPS4/compare/${{ env.last_release_tag }}...${{ needs.get-info.outputs.shorthash }})" + body: "Full Changelog: [${{ env.last_release_tag }}...${{ needs.get-info.outputs.shorthash }}](https://github.com/shadps4-emu/shadPS4/compare/${{ env.last_release_tag }}...${{ needs.get-info.outputs.fullhash }})" artifacts: ./artifacts/*.zip - + + - name: Publish to Release Repository + env: + GITHUB_TOKEN: ${{ secrets.SHADPS4_TOKEN_REPO }} + run: | + ARTIFACTS_DIR=./artifacts + REPO_WINDOWS="shadps4-emu/shadps4-binaries-Windows" + REPO_LINUX="shadps4-emu/shadps4-binaries-Linux" + REPO_MAC="shadps4-emu/shadps4-binaries-Mac" + + for file in "$ARTIFACTS_DIR"/*.zip; do + filename=$(basename "$file") + REPO="" + + # Determine repository based on file name + if [[ "$filename" == *"win64"* ]]; then + REPO=$REPO_WINDOWS + elif [[ "$filename" == *"linux"* ]] || [[ "$filename" == *"ubuntu64"* ]]; then + REPO=$REPO_LINUX + elif [[ "$filename" == *"macos"* ]]; then + REPO=$REPO_MAC + fi + + # If REPO is empty, skip file + if [[ -z "$REPO" ]]; then + echo "No matching repository for $filename" + continue + fi + + # Check if release already exists and get ID + release_id=$(curl -s -H "Authorization: token $GITHUB_TOKEN" \ + "https://api.github.com/repos/$REPO/releases/tags/Pre-release-shadPS4-${{ needs.get-info.outputs.date }}-${{ needs.get-info.outputs.shorthash }}" | jq -r '.id') + + if [[ "$release_id" == "null" ]]; then + echo "Creating release in $REPO for $filename" + release_id=$(curl -s -X POST -H "Authorization: token $GITHUB_TOKEN" \ + -H "Accept: application/vnd.github.v3+json" \ + -d '{ + "tag_name": "Pre-release-shadPS4-${{ needs.get-info.outputs.date }}-${{ needs.get-info.outputs.shorthash }}", + "name": "Pre-release-shadPS4-${{ needs.get-info.outputs.date }}-${{ needs.get-info.outputs.shorthash }}", + "draft": false, + "prerelease": true, + "body": "Commit: [${{ needs.get-info.outputs.fullhash }}](https://github.com/shadps4-emu/shadPS4/commit/${{ needs.get-info.outputs.fullhash }})" + }' "https://api.github.com/repos/$REPO/releases" | jq -r '.id') + else + echo "Release already exists in $REPO with ID $release_id" + fi + + # Artifact upload + echo "Uploading $filename to release $release_id in $REPO" + upload_url="https://uploads.github.com/repos/$REPO/releases/$release_id/assets?name=$filename" + curl -X POST -H "Authorization: token $GITHUB_TOKEN" -H "Content-Type: application/octet-stream" --data-binary @"$file" "$upload_url" + done + - name: Get current pre-release information env: GITHUB_TOKEN: ${{ secrets.SHADPS4_TOKEN_REPO }} diff --git a/.gitignore b/.gitignore index 9abc4aae4..61d9e32e1 100644 --- a/.gitignore +++ b/.gitignore @@ -382,13 +382,13 @@ FodyWeavers.xsd # VS Code files for those working on multiple tools .vscode/* -!.vscode/settings.json -!.vscode/tasks.json -!.vscode/launch.json -!.vscode/extensions.json +.vscode/settings.json +.vscode/tasks.json +.vscode/launch.json +.vscode/extensions.json *.code-workspace -/CMakeUserPresets.json -/compile_commands.json +/CMakeUserPresets.json +/compile_commands.json # Local History for Visual Studio Code .history/ @@ -411,6 +411,6 @@ FodyWeavers.xsd /out/* /third-party/out/* /src/common/scm_rev.cpp - + # for macOS -**/.DS_Store +**/.DS_Store diff --git a/CMakeLists.txt b/CMakeLists.txt index 46d3249cd..04bd6a331 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -290,8 +290,6 @@ set(SYSTEM_LIBS src/core/libraries/system/commondialog.cpp src/core/libraries/audio3d/audio3d_error.h src/core/libraries/audio3d/audio3d_impl.cpp src/core/libraries/audio3d/audio3d_impl.h - src/core/libraries/ime/ime.cpp - src/core/libraries/ime/ime.h src/core/libraries/game_live_streaming/gamelivestreaming.cpp src/core/libraries/game_live_streaming/gamelivestreaming.h src/core/libraries/remote_play/remoteplay.cpp @@ -311,13 +309,17 @@ set(LIBC_SOURCES src/core/libraries/libc_internal/libc_internal.cpp src/core/libraries/libc_internal/libc_internal.h ) -set(DIALOGS_LIB src/core/libraries/dialogs/error_dialog.cpp - src/core/libraries/dialogs/error_dialog.h - src/core/libraries/dialogs/ime_dialog_ui.cpp - src/core/libraries/dialogs/ime_dialog_ui.h - src/core/libraries/dialogs/ime_dialog.cpp - src/core/libraries/dialogs/ime_dialog.h - src/core/libraries/dialogs/error_codes.h +set(IME_LIB src/core/libraries/ime/error_dialog.cpp + src/core/libraries/ime/error_dialog.h + src/core/libraries/ime/ime_common.h + src/core/libraries/ime/ime_dialog_ui.cpp + src/core/libraries/ime/ime_dialog_ui.h + src/core/libraries/ime/ime_dialog.cpp + src/core/libraries/ime/ime_dialog.h + src/core/libraries/ime/ime_ui.cpp + src/core/libraries/ime/ime_ui.h + src/core/libraries/ime/ime.cpp + src/core/libraries/ime/ime.h ) set(PAD_LIB src/core/libraries/pad/pad.cpp @@ -346,6 +348,13 @@ set(FIBER_LIB src/core/libraries/fiber/fiber.cpp src/core/libraries/fiber/fiber.h ) +set(VDEC_LIB src/core/libraries/videodec/videodec2_impl.cpp + src/core/libraries/videodec/videodec2_impl.h + src/core/libraries/videodec/videodec2.cpp + src/core/libraries/videodec/videodec2.h + src/core/libraries/videodec/videodec2_avc.h +) + set(NP_LIBS src/core/libraries/np_manager/np_manager.cpp src/core/libraries/np_manager/np_manager.h src/core/libraries/np_score/np_score.cpp @@ -499,8 +508,9 @@ set(CORE src/core/aerolib/stubs.cpp ${RANDOM_LIB} ${USBD_LIB} ${MISC_LIBS} - ${DIALOGS_LIB} + ${IME_LIB} ${FIBER_LIB} + ${VDEC_LIB} ${DEV_TOOLS} src/core/debug_state.cpp src/core/debug_state.h diff --git a/src/common/logging/filter.cpp b/src/common/logging/filter.cpp index 051bbd79e..a76c472a7 100644 --- a/src/common/logging/filter.cpp +++ b/src/common/logging/filter.cpp @@ -119,6 +119,7 @@ bool ParseFilterRule(Filter& instance, Iterator begin, Iterator end) { SUB(Lib, Remoteplay) \ SUB(Lib, SharePlay) \ SUB(Lib, Fiber) \ + SUB(Lib, Vdec2) \ CLS(Frontend) \ CLS(Render) \ SUB(Render, Vulkan) \ diff --git a/src/common/logging/types.h b/src/common/logging/types.h index 39319d0dc..fd7dcfd1a 100644 --- a/src/common/logging/types.h +++ b/src/common/logging/types.h @@ -86,6 +86,7 @@ enum class Class : u8 { Lib_Remoteplay, ///< The LibSceRemotePlay implementation Lib_SharePlay, ///< The LibSceSharePlay implemenation Lib_Fiber, ///< The LibSceFiber implementation. + Lib_Vdec2, ///< The LibSceVideodec2 implementation. Frontend, ///< Emulator UI Render, ///< Video Core Render_Vulkan, ///< Vulkan backend diff --git a/src/common/path_util.cpp b/src/common/path_util.cpp index 7551d3b05..53eb123dc 100644 --- a/src/common/path_util.cpp +++ b/src/common/path_util.cpp @@ -86,30 +86,27 @@ static std::filesystem::path GetBundleParentDirectory() { static auto UserPaths = [] { #ifdef __APPLE__ - // Start by assuming the base directory is the bundle's parent directory. - std::filesystem::path base_dir = GetBundleParentDirectory(); - std::filesystem::path user_dir = base_dir / PORTABLE_DIR; - // Check if the "user" directory exists in the current path: + // Set the current path to the directory containing the app bundle. + std::filesystem::current_path(GetBundleParentDirectory()); +#endif + + // Try the portable user directory first. + auto user_dir = std::filesystem::current_path() / PORTABLE_DIR; if (!std::filesystem::exists(user_dir)) { - // If it doesn't exist, use the new hardcoded path: + // If it doesn't exist, use the standard path for the platform instead. + // NOTE: On Windows we currently just create the portable directory instead. +#ifdef __APPLE__ user_dir = std::filesystem::path(getenv("HOME")) / "Library" / "Application Support" / "shadPS4"; - } #elif defined(__linux__) - auto user_dir = std::filesystem::current_path() / PORTABLE_DIR; - // Check if the "user" directory exists in the current path: - if (!std::filesystem::exists(user_dir)) { - // If it doesn't exist, use XDG_DATA_HOME if it is set, and provide a standard default const char* xdg_data_home = getenv("XDG_DATA_HOME"); if (xdg_data_home != nullptr && strlen(xdg_data_home) > 0) { user_dir = std::filesystem::path(xdg_data_home) / "shadPS4"; } else { user_dir = std::filesystem::path(getenv("HOME")) / ".local" / "share" / "shadPS4"; } - } -#else - const auto user_dir = std::filesystem::current_path() / PORTABLE_DIR; #endif + } std::unordered_map paths; diff --git a/src/common/version.h b/src/common/version.h index 12fd17041..5e6599604 100644 --- a/src/common/version.h +++ b/src/common/version.h @@ -8,7 +8,7 @@ namespace Common { -constexpr char VERSION[] = "0.3.1 WIP"; +constexpr char VERSION[] = "0.4.1 WIP"; constexpr bool isRelease = false; } // namespace Common diff --git a/src/core/devtools/layer.cpp b/src/core/devtools/layer.cpp index 38b17c863..264b3be0d 100644 --- a/src/core/devtools/layer.cpp +++ b/src/core/devtools/layer.cpp @@ -19,6 +19,7 @@ using namespace Core::Devtools; using L = Core::Devtools::Layer; static bool show_simple_fps = false; +static bool visibility_toggled = false; static float fps_scale = 1.0f; static bool show_advanced_debug = false; @@ -296,20 +297,24 @@ void L::Draw() { const auto fn = DebugState.flip_frame_count.load(); frame_graph.AddFrame(fn, io.DeltaTime); } - if (IsKeyPressed(ImGuiKey_F10, false)) { if (io.KeyCtrl) { show_advanced_debug = !show_advanced_debug; } else { show_simple_fps = !show_simple_fps; } + visibility_toggled = true; } if (show_simple_fps) { if (Begin("Video Info", nullptr, ImGuiWindowFlags_NoNav | ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoDocking)) { - SetWindowPos("Video Info", {999999.0f, 0.0f}, ImGuiCond_FirstUseEver); + // Set window position to top left if it was toggled on + if (visibility_toggled) { + SetWindowPos("Video Info", {999999.0f, 0.0f}, ImGuiCond_Always); + visibility_toggled = false; + } if (BeginPopupContextWindow()) { #define M(label, value) \ if (MenuItem(label, nullptr, fps_scale == value)) \ diff --git a/src/core/libraries/dialogs/error_codes.h b/src/core/libraries/dialogs/error_codes.h deleted file mode 100644 index 587a2a973..000000000 --- a/src/core/libraries/dialogs/error_codes.h +++ /dev/null @@ -1,12 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -constexpr int ORBIS_ERROR_DIALOG_ERROR_NOT_INITIALIZED = 0x80ED0001; // not initialized -constexpr int ORBIS_ERROR_DIALOG_ERROR_ALREADY_INITIALIZED = 0x80ED0002; // already initialized -constexpr int ORBIS_ERROR_DIALOG_ERROR_PARAM_INVALID = 0x80ED0003; // Parameter is invalid -constexpr int ORBIS_ERROR_DIALOG_ERROR_UNEXPECTED_FATAL = 0x80ED0004; // Unexpected fatal error -constexpr int ORBIS_ERROR_DIALOG_ERROR_INVALID_STATE = 0x80ED0005; // not in a callable state -constexpr int ORBIS_ERROR_DIALOG_ERROR_SERVICE_BUSY = 0x80ED0006; // Process is busy -constexpr int ORBIS_ERROR_DIALOG_ERROR_INVALID_USER_ID = 0x80ED0007; // Invalid user ID diff --git a/src/core/libraries/error_codes.h b/src/core/libraries/error_codes.h index dae285664..66ad5f8b6 100644 --- a/src/core/libraries/error_codes.h +++ b/src/core/libraries/error_codes.h @@ -507,4 +507,87 @@ constexpr int ORBIS_FIBER_ERROR_ALIGNMENT = 0x80590002; constexpr int ORBIS_FIBER_ERROR_RANGE = 0x80590003; constexpr int ORBIS_FIBER_ERROR_INVALID = 0x80590004; constexpr int ORBIS_FIBER_ERROR_PERMISSION = 0x80590005; -constexpr int ORBIS_FIBER_ERROR_STATE = 0x80590006; \ No newline at end of file +constexpr int ORBIS_FIBER_ERROR_STATE = 0x80590006; + +// ImeDialog library +constexpr int ORBIS_ERROR_DIALOG_ERROR_NOT_INITIALIZED = 0x80ED0001; +constexpr int ORBIS_ERROR_DIALOG_ERROR_ALREADY_INITIALIZED = 0x80ED0002; +constexpr int ORBIS_ERROR_DIALOG_ERROR_PARAM_INVALID = 0x80ED0003; +constexpr int ORBIS_ERROR_DIALOG_ERROR_UNEXPECTED_FATAL = 0x80ED0004; +constexpr int ORBIS_ERROR_DIALOG_ERROR_INVALID_STATE = 0x80ED0005; +constexpr int ORBIS_ERROR_DIALOG_ERROR_SERVICE_BUSY = 0x80ED0006; +constexpr int ORBIS_ERROR_DIALOG_ERROR_INVALID_USER_ID = 0x80ED0007; + +// Ime library +constexpr int ORBIS_IME_ERROR_BUSY = 0x80BC0001; +constexpr int ORBIS_IME_ERROR_NOT_OPENED = 0x80BC0002; +constexpr int ORBIS_IME_ERROR_NO_MEMORY = 0x80BC0003; +constexpr int ORBIS_IME_ERROR_CONNECTION_FAILED = 0x80BC0004; +constexpr int ORBIS_IME_ERROR_TOO_MANY_REQUESTS = 0x80BC0005; +constexpr int ORBIS_IME_ERROR_INVALID_TEXT = 0x80BC0006; +constexpr int ORBIS_IME_ERROR_EVENT_OVERFLOW = 0x80BC0007; +constexpr int ORBIS_IME_ERROR_NOT_ACTIVE = 0x80BC0008; +constexpr int ORBIS_IME_ERROR_IME_SUSPENDING = 0x80BC0009; +constexpr int ORBIS_IME_ERROR_DEVICE_IN_USE = 0x80BC000A; +constexpr int ORBIS_IME_ERROR_INVALID_USER_ID = 0x80BC0010; +constexpr int ORBIS_IME_ERROR_INVALID_TYPE = 0x80BC0011; +constexpr int ORBIS_IME_ERROR_INVALID_SUPPORTED_LANGUAGES = 0x80BC0012; +constexpr int ORBIS_IME_ERROR_INVALID_ENTER_LABEL = 0x80BC0013; +constexpr int ORBIS_IME_ERROR_INVALID_INPUT_METHOD = 0x80BC0014; +constexpr int ORBIS_IME_ERROR_INVALID_OPTION = 0x80BC0015; +constexpr int ORBIS_IME_ERROR_INVALID_MAX_TEXT_LENGTH = 0x80BC0016; +constexpr int ORBIS_IME_ERROR_INVALID_INPUT_TEXT_BUFFER = 0x80BC0017; +constexpr int ORBIS_IME_ERROR_INVALID_POSX = 0x80BC0018; +constexpr int ORBIS_IME_ERROR_INVALID_POSY = 0x80BC0019; +constexpr int ORBIS_IME_ERROR_INVALID_HORIZONTAL_ALIGNMENT = 0x80BC001A; +constexpr int ORBIS_IME_ERROR_INVALID_VERTICAL_ALIGNMENT = 0x80BC001B; +constexpr int ORBIS_IME_ERROR_INVALID_EXTENDED = 0x80BC001C; +constexpr int ORBIS_IME_ERROR_INVALID_KEYBOARD_TYPE = 0x80BC001D; +constexpr int ORBIS_IME_ERROR_INVALID_WORK = 0x80BC0020; +constexpr int ORBIS_IME_ERROR_INVALID_ARG = 0x80BC0021; +constexpr int ORBIS_IME_ERROR_INVALID_HANDLER = 0x80BC0022; +constexpr int ORBIS_IME_ERROR_NO_RESOURCE_ID = 0x80BC0023; +constexpr int ORBIS_IME_ERROR_INVALID_MODE = 0x80BC0024; +constexpr int ORBIS_IME_ERROR_INVALID_PARAM = 0x80BC0030; +constexpr int ORBIS_IME_ERROR_INVALID_ADDRESS = 0x80BC0031; +constexpr int ORBIS_IME_ERROR_INVALID_RESERVED = 0x80BC0032; +constexpr int ORBIS_IME_ERROR_INVALID_TIMING = 0x80BC0033; +constexpr int ORBIS_IME_ERROR_INTERNAL = 0x80BC00FF; + +// Videodec2 library +constexpr int ORBIS_VIDEODEC2_ERROR_API_FAIL = 0x811D0100; +constexpr int ORBIS_VIDEODEC2_ERROR_STRUCT_SIZE = 0x811D0101; +constexpr int ORBIS_VIDEODEC2_ERROR_ARGUMENT_POINTER = 0x811D0102; +constexpr int ORBIS_VIDEODEC2_ERROR_DECODER_INSTANCE = 0x811D0103; +constexpr int ORBIS_VIDEODEC2_ERROR_MEMORY_SIZE = 0x811D0104; +constexpr int ORBIS_VIDEODEC2_ERROR_MEMORY_POINTER = 0x811D0105; +constexpr int ORBIS_VIDEODEC2_ERROR_FRAME_BUFFER_SIZE = 0x811D0106; +constexpr int ORBIS_VIDEODEC2_ERROR_FRAME_BUFFER_POINTER = 0x811D0107; +constexpr int ORBIS_VIDEODEC2_ERROR_FRAME_BUFFER_ALIGNMENT = 0x811D0108; +constexpr int ORBIS_VIDEODEC2_ERROR_NOT_ONION_MEMORY = 0x811D0109; +constexpr int ORBIS_VIDEODEC2_ERROR_NOT_GARLIC_MEMORY = 0x811D010A; +constexpr int ORBIS_VIDEODEC2_ERROR_NOT_DIRECT_MEMORY = 0x811D010B; +constexpr int ORBIS_VIDEODEC2_ERROR_MEMORY_INFO = 0x811D010C; +constexpr int ORBIS_VIDEODEC2_ERROR_ACCESS_UNIT_SIZE = 0x811D010D; +constexpr int ORBIS_VIDEODEC2_ERROR_ACCESS_UNIT_POINTER = 0x811D010E; +constexpr int ORBIS_VIDEODEC2_ERROR_OUTPUT_INFO = 0x811D010F; +constexpr int ORBIS_VIDEODEC2_ERROR_COMPUTE_QUEUE = 0x811D0110; +constexpr int ORBIS_VIDEODEC2_ERROR_FATAL_STATE = 0x811D0111; +constexpr int ORBIS_VIDEODEC2_ERROR_PRESET_VALUE = 0x811D0112; +constexpr int ORBIS_VIDEODEC2_ERROR_CONFIG_INFO = 0x811D0200; +constexpr int ORBIS_VIDEODEC2_ERROR_COMPUTE_PIPE_ID = 0x811D0201; +constexpr int ORBIS_VIDEODEC2_ERROR_COMPUTE_QUEUE_ID = 0x811D0202; +constexpr int ORBIS_VIDEODEC2_ERROR_RESOURCE_TYPE = 0x811D0203; +constexpr int ORBIS_VIDEODEC2_ERROR_CODEC_TYPE = 0x811D0204; +constexpr int ORBIS_VIDEODEC2_ERROR_PROFILE_LEVEL = 0x811D0205; +constexpr int ORBIS_VIDEODEC2_ERROR_PIPELINE_DEPTH = 0x811D0206; +constexpr int ORBIS_VIDEODEC2_ERROR_AFFINITY_MASK = 0x811D0207; +constexpr int ORBIS_VIDEODEC2_ERROR_THREAD_PRIORITY = 0x811D0208; +constexpr int ORBIS_VIDEODEC2_ERROR_DPB_FRAME_COUNT = 0x811D0209; +constexpr int ORBIS_VIDEODEC2_ERROR_FRAME_WIDTH_HEIGHT = 0x811D020A; +constexpr int ORBIS_VIDEODEC2_ERROR_EXTRA_CONFIG_INFO = 0x811D020B; +constexpr int ORBIS_VIDEODEC2_ERROR_NEW_SEQUENCE = 0x811D0300; +constexpr int ORBIS_VIDEODEC2_ERROR_ACCESS_UNIT = 0x811D0301; +constexpr int ORBIS_VIDEODEC2_ERROR_OVERSIZE_DECODE = 0x811D0302; +constexpr int ORBIS_VIDEODEC2_ERROR_INVALID_SEQUENCE = 0x811D0303; +constexpr int ORBIS_VIDEODEC2_ERROR_FATAL_STREAM = 0x811D0304; \ No newline at end of file diff --git a/src/core/libraries/fiber/fiber.cpp b/src/core/libraries/fiber/fiber.cpp index bd1575dda..a0bfd6850 100644 --- a/src/core/libraries/fiber/fiber.cpp +++ b/src/core/libraries/fiber/fiber.cpp @@ -15,7 +15,7 @@ namespace Libraries::Fiber { -constexpr static u64 kFiberSignature = 0x054ad954; +static constexpr u64 kFiberSignature = 0x054ad954; thread_local SceFiber* gCurrentFiber = nullptr; thread_local void* gFiberThread = nullptr; diff --git a/src/core/libraries/gnmdriver/gnmdriver.cpp b/src/core/libraries/gnmdriver/gnmdriver.cpp index 4d8aa8817..08f534c72 100644 --- a/src/core/libraries/gnmdriver/gnmdriver.cpp +++ b/src/core/libraries/gnmdriver/gnmdriver.cpp @@ -359,12 +359,13 @@ s32 PS4_SYSV_ABI sceGnmAddEqEvent(SceKernelEqueue eq, u64 id, void* udata) { eq->AddEvent(kernel_event); Platform::IrqC::Instance()->Register( - Platform::InterruptId::GfxEop, + static_cast(id), [=](Platform::InterruptId irq) { - ASSERT_MSG(irq == Platform::InterruptId::GfxEop, + ASSERT_MSG(irq == static_cast(id), "An unexpected IRQ occured"); // We need to convert IRQ# to event id and do // proper filtering in trigger function - eq->TriggerEvent(GnmEventIdents::GfxEop, SceKernelEvent::Filter::GraphicsCore, nullptr); + eq->TriggerEvent(static_cast(id), SceKernelEvent::Filter::GraphicsCore, + nullptr); }, eq); return ORBIS_OK; @@ -468,7 +469,6 @@ int PS4_SYSV_ABI sceGnmDebugHardwareStatus() { s32 PS4_SYSV_ABI sceGnmDeleteEqEvent(SceKernelEqueue eq, u64 id) { LOG_TRACE(Lib_GnmDriver, "called"); - ASSERT_MSG(id == GnmEventIdents::GfxEop); if (!eq) { return ORBIS_KERNEL_ERROR_EBADF; @@ -476,7 +476,7 @@ s32 PS4_SYSV_ABI sceGnmDeleteEqEvent(SceKernelEqueue eq, u64 id) { eq->RemoveEvent(id); - Platform::IrqC::Instance()->Unregister(Platform::InterruptId::GfxEop, eq); + Platform::IrqC::Instance()->Unregister(static_cast(id), eq); return ORBIS_OK; } diff --git a/src/core/libraries/dialogs/error_dialog.cpp b/src/core/libraries/ime/error_dialog.cpp similarity index 100% rename from src/core/libraries/dialogs/error_dialog.cpp rename to src/core/libraries/ime/error_dialog.cpp diff --git a/src/core/libraries/dialogs/error_dialog.h b/src/core/libraries/ime/error_dialog.h similarity index 100% rename from src/core/libraries/dialogs/error_dialog.h rename to src/core/libraries/ime/error_dialog.h diff --git a/src/core/libraries/ime/ime.cpp b/src/core/libraries/ime/ime.cpp index 13a70acf7..0310c5153 100644 --- a/src/core/libraries/ime/ime.cpp +++ b/src/core/libraries/ime/ime.cpp @@ -1,14 +1,108 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include #include "ime.h" +#include "ime_ui.h" #include "common/logging/log.h" +#include "common/singleton.h" #include "core/libraries/error_codes.h" #include "core/libraries/libs.h" +#include "core/linker.h" namespace Libraries::Ime { +static std::queue g_ime_events; +static ImeState g_ime_state{}; +static ImeUi g_ime_ui; + +class ImeHandler { +public: + ImeHandler(const OrbisImeKeyboardParam* param) { + Init(param, false); + } + ImeHandler(const OrbisImeParam* param) { + Init(param, true); + } + ~ImeHandler() = default; + + void Init(const void* param, bool ime_mode) { + if (ime_mode) { + m_param.ime = *(OrbisImeParam*)param; + } else { + m_param.key = *(OrbisImeKeyboardParam*)param; + } + m_ime_mode = ime_mode; + + // Open an event to let the game know the IME has started + OrbisImeEvent openEvent{}; + openEvent.id = (ime_mode ? OrbisImeEventId::OPEN : OrbisImeEventId::KEYBOARD_OPEN); + + if (ime_mode) { + sceImeGetPanelSize(&m_param.ime, &openEvent.param.rect.width, + &openEvent.param.rect.height); + openEvent.param.rect.x = m_param.ime.posx; + openEvent.param.rect.y = m_param.ime.posy; + } else { + openEvent.param.resourceIdArray.userId = 1; + openEvent.param.resourceIdArray.resourceId[0] = 1; + } + + Execute(nullptr, &openEvent, true); + + if (ime_mode) { + g_ime_state = ImeState(&m_param.ime); + g_ime_ui = ImeUi(&g_ime_state, &m_param.ime); + } + } + + s32 Update(OrbisImeEventHandler handler) { + std::unique_lock lock{g_ime_state.queue_mutex}; + + while (!g_ime_state.event_queue.empty()) { + OrbisImeEvent event = g_ime_state.event_queue.front(); + g_ime_state.event_queue.pop(); + Execute(handler, &event, false); + } + + return ORBIS_OK; + } + + void Execute(OrbisImeEventHandler handler, OrbisImeEvent* event, bool use_param_handler) { + const auto* linker = Common::Singleton::Instance(); + + if (m_ime_mode) { + OrbisImeParam param = m_param.ime; + if (use_param_handler) { + linker->ExecuteGuest(param.handler, param.arg, event); + } else { + linker->ExecuteGuest(handler, param.arg, event); + } + } else { + OrbisImeKeyboardParam param = m_param.key; + if (use_param_handler) { + linker->ExecuteGuest(param.handler, param.arg, event); + } else { + linker->ExecuteGuest(handler, param.arg, event); + } + } + } + + bool IsIme() { + return m_ime_mode; + } + +private: + union ImeParam { + OrbisImeKeyboardParam key; + OrbisImeParam ime; + } m_param{}; + bool m_ime_mode = false; +}; + +static std::unique_ptr g_ime_handler; + int PS4_SYSV_ABI FinalizeImeModule() { LOG_ERROR(Lib_Ime, "(STUBBED) called"); return ORBIS_OK; @@ -34,8 +128,19 @@ int PS4_SYSV_ABI sceImeCheckUpdateTextInfo() { return ORBIS_OK; } -int PS4_SYSV_ABI sceImeClose() { - LOG_ERROR(Lib_Ime, "(STUBBED) called"); +s32 PS4_SYSV_ABI sceImeClose() { + LOG_INFO(Lib_Ime, "(STUBBED) called"); + + if (!g_ime_handler) { + return ORBIS_IME_ERROR_NOT_OPENED; + } + if (!g_ime_handler->IsIme()) { + return ORBIS_IME_ERROR_NOT_OPENED; + } + + g_ime_handler.release(); + g_ime_ui = ImeUi(); + g_ime_state = ImeState(); return ORBIS_OK; } @@ -104,13 +209,42 @@ int PS4_SYSV_ABI sceImeGetPanelPositionAndForm() { return ORBIS_OK; } -int PS4_SYSV_ABI sceImeGetPanelSize() { - LOG_ERROR(Lib_Ime, "(STUBBED) called"); +s32 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; + } + + switch (param->type) { + case OrbisImeType::DEFAULT: + case OrbisImeType::BASIC_LATIN: + case OrbisImeType::URL: + case OrbisImeType::MAIL: + // We set our custom sizes, commented sizes are the original ones + *width = 500; // 793 + *height = 100; // 408 + break; + case OrbisImeType::NUMBER: + *width = 370; + *height = 402; + break; + } + return ORBIS_OK; } -int PS4_SYSV_ABI sceImeKeyboardClose() { - LOG_ERROR(Lib_Ime, "(STUBBED) called"); +s32 PS4_SYSV_ABI sceImeKeyboardClose(s32 userId) { + LOG_INFO(Lib_Ime, "(STUBBED) called"); + + if (!g_ime_handler) { + return ORBIS_IME_ERROR_NOT_OPENED; + } + if (g_ime_handler->IsIme()) { + return ORBIS_IME_ERROR_NOT_OPENED; + } + + g_ime_handler.release(); return ORBIS_OK; } @@ -124,9 +258,19 @@ int PS4_SYSV_ABI sceImeKeyboardGetResourceId() { return ORBIS_OK; } -int PS4_SYSV_ABI sceImeKeyboardOpen() { +s32 PS4_SYSV_ABI sceImeKeyboardOpen(s32 userId, const OrbisImeKeyboardParam* param) { LOG_ERROR(Lib_Ime, "(STUBBED) called"); - return ORBIS_OK; + + if (!param) { + return ORBIS_IME_ERROR_INVALID_ADDRESS; + } + if (g_ime_handler) { + return ORBIS_IME_ERROR_BUSY; + } + + // g_ime_handler = std::make_unique(param); + // return ORBIS_OK; + return ORBIS_IME_ERROR_CONNECTION_FAILED; // Fixup } int PS4_SYSV_ABI sceImeKeyboardOpenInternal() { @@ -144,8 +288,19 @@ int PS4_SYSV_ABI sceImeKeyboardUpdate() { return ORBIS_OK; } -int PS4_SYSV_ABI sceImeOpen() { - LOG_ERROR(Lib_Ime, "(STUBBED) called"); +s32 PS4_SYSV_ABI sceImeOpen(const OrbisImeParam* param, const void* extended) { + LOG_INFO(Lib_Ime, "called"); + + if (!g_ime_handler) { + g_ime_handler = std::make_unique(param); + } else { + if (g_ime_handler->IsIme()) { + return ORBIS_IME_ERROR_BUSY; + } + + g_ime_handler->Init((void*)param, true); + } + return ORBIS_OK; } @@ -154,9 +309,15 @@ int PS4_SYSV_ABI sceImeOpenInternal() { return ORBIS_OK; } -int PS4_SYSV_ABI sceImeParamInit() { - LOG_ERROR(Lib_Ime, "(STUBBED) called"); - return ORBIS_OK; +void PS4_SYSV_ABI sceImeParamInit(OrbisImeParam* param) { + LOG_INFO(Lib_Ime, "called"); + + if (!param) { + return; + } + + memset(param, 0, sizeof(OrbisImeParam)); + param->userId = -1; } int PS4_SYSV_ABI sceImeSetCandidateIndex() { @@ -164,7 +325,7 @@ int PS4_SYSV_ABI sceImeSetCandidateIndex() { return ORBIS_OK; } -int PS4_SYSV_ABI sceImeSetCaret() { +int PS4_SYSV_ABI sceImeSetCaret(const OrbisImeCaret* caret) { LOG_ERROR(Lib_Ime, "(STUBBED) called"); return ORBIS_OK; } @@ -179,9 +340,14 @@ int PS4_SYSV_ABI sceImeSetTextGeometry() { return ORBIS_OK; } -int PS4_SYSV_ABI sceImeUpdate() { - LOG_ERROR(Lib_Ime, "(STUBBED) called"); - return ORBIS_OK; +s32 PS4_SYSV_ABI sceImeUpdate(OrbisImeEventHandler handler) { + LOG_TRACE(Lib_Ime, "called"); + + if (!g_ime_handler) { + return ORBIS_IME_ERROR_NOT_OPENED; + } + + return g_ime_handler->Update(handler); } int PS4_SYSV_ABI sceImeVshClearPreedit() { diff --git a/src/core/libraries/ime/ime.h b/src/core/libraries/ime/ime.h index 807616f14..fc98d426a 100644 --- a/src/core/libraries/ime/ime.h +++ b/src/core/libraries/ime/ime.h @@ -5,12 +5,62 @@ #include "common/types.h" +#include "ime_common.h" + namespace Core::Loader { class SymbolsResolver; } namespace Libraries::Ime { +constexpr u32 ORBIS_IME_MAX_TEXT_LENGTH = 2048; + +enum class OrbisImeKeyboardOption : u32 { + DEFAULT = 0, + REPEAT = 1, + REPEAT_EACH_KEY = 2, + ADD_OSK = 4, + EFFECTIVE_WITH_TIME = 8, + DISABLE_RESUME = 16, + DISABLE_CAPSLOCK_WITHOUT_SHIFT = 32, +}; +DECLARE_ENUM_FLAG_OPERATORS(OrbisImeKeyboardOption) + +struct OrbisImeKeyboardParam { + OrbisImeKeyboardOption option; + s8 reserved1[4]; + void* arg; + OrbisImeEventHandler handler; + s8 reserved2[8]; +}; + +struct OrbisImeParam { + s32 userId; + OrbisImeType type; + u64 supportedLanguages; + OrbisImeEnterLabel enterLabel; + OrbisImeInputMethod inputMethod; + OrbisImeTextFilter filter; + u32 option; + u32 maxTextLength; + char16_t* inputTextBuffer; + float posx; + float posy; + OrbisImeHorizontalAlignment horizontalAlignment; + OrbisImeVerticalAlignment verticalAlignment; + 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(); @@ -30,22 +80,22 @@ int PS4_SYSV_ABI sceImeDisableController(); int PS4_SYSV_ABI sceImeFilterText(); int PS4_SYSV_ABI sceImeForTestFunction(); int PS4_SYSV_ABI sceImeGetPanelPositionAndForm(); -int PS4_SYSV_ABI sceImeGetPanelSize(); -int PS4_SYSV_ABI sceImeKeyboardClose(); +s32 PS4_SYSV_ABI sceImeGetPanelSize(const OrbisImeParam* param, u32* width, u32* height); +s32 PS4_SYSV_ABI sceImeKeyboardClose(s32 userId); int PS4_SYSV_ABI sceImeKeyboardGetInfo(); int PS4_SYSV_ABI sceImeKeyboardGetResourceId(); -int PS4_SYSV_ABI sceImeKeyboardOpen(); +s32 PS4_SYSV_ABI sceImeKeyboardOpen(s32 userId, const OrbisImeKeyboardParam* param); int PS4_SYSV_ABI sceImeKeyboardOpenInternal(); int PS4_SYSV_ABI sceImeKeyboardSetMode(); int PS4_SYSV_ABI sceImeKeyboardUpdate(); -int PS4_SYSV_ABI sceImeOpen(); +s32 PS4_SYSV_ABI sceImeOpen(const OrbisImeParam* param, const void* extended); int PS4_SYSV_ABI sceImeOpenInternal(); -int PS4_SYSV_ABI sceImeParamInit(); +void PS4_SYSV_ABI sceImeParamInit(OrbisImeParam* param); int PS4_SYSV_ABI sceImeSetCandidateIndex(); -int PS4_SYSV_ABI sceImeSetCaret(); +s32 PS4_SYSV_ABI sceImeSetCaret(const OrbisImeCaret* caret); int PS4_SYSV_ABI sceImeSetText(); int PS4_SYSV_ABI sceImeSetTextGeometry(); -int PS4_SYSV_ABI sceImeUpdate(); +s32 PS4_SYSV_ABI sceImeUpdate(OrbisImeEventHandler handler); int PS4_SYSV_ABI sceImeVshClearPreedit(); int PS4_SYSV_ABI sceImeVshClose(); int PS4_SYSV_ABI sceImeVshConfirmPreedit(); diff --git a/src/core/libraries/ime/ime_common.h b/src/core/libraries/ime/ime_common.h new file mode 100644 index 000000000..078a6469e --- /dev/null +++ b/src/core/libraries/ime/ime_common.h @@ -0,0 +1,184 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once +#include +#include "common/enum.h" +#include "common/types.h" +#include "core/libraries/rtc/rtc.h" + +enum class OrbisImeType : u32 { + DEFAULT = 0, + BASIC_LATIN = 1, + URL = 2, + MAIL = 3, + NUMBER = 4, +}; + +enum class OrbisImeHorizontalAlignment : u32 { + LEFT = 0, + CENTER = 1, + RIGHT = 2, +}; + +enum class OrbisImeVerticalAlignment : u32 { + TOP = 0, + CENTER = 1, + BOTTOM = 2, +}; + +enum class OrbisImeEnterLabel : u32 { + DEFAULT = 0, + SEND = 1, + SEARCH = 2, + GO = 3, +}; + +enum class OrbisImeInputMethod : u32 { + DEFAULT = 0, +}; + +enum class OrbisImeEventId : u32 { + OPEN = 0, + UPDATE_TEXT = 1, + UPDATE_CARET = 2, + PRESS_CLOSE = 4, + PRESS_ENTER = 5, + ABORT = 6, + CANDIDATE_LIST_START = 7, + CANDIDATE_LIST_END = 8, + CANDIDATE_WORD = 9, + CANDIDATE_INDEX = 10, + CANDIDATE_DONE = 11, + CANDIDATE_CANCEL = 12, + CHANGE_DEVICE = 14, + CHANGE_INPUT_METHOD_STATE = 18, + + KEYBOARD_OPEN = 256, + KEYBOARD_KEYCODE_DOWN = 257, + KEYBOARD_KEYCODE_UP = 258, + KEYBOARD_KEYCODE_REPEAT = 259, + KEYBOARD_CONNECTION = 260, + KEYBOARD_DISCONNECTION = 261, + KEYBOARD_ABORT = 262, +}; + +enum class OrbisImeKeyboardType : u32 { + NONE = 0, + DANISH = 1, + GERMAN = 2, + GERMAN_SW = 3, + ENGLISH_US = 4, + ENGLISH_GB = 5, + SPANISH = 6, + SPANISH_LA = 7, + FINNISH = 8, + FRENCH = 9, + FRENCH_BR = 10, + FRENCH_CA = 11, + FRENCH_SW = 12, + ITALIAN = 13, + DUTCH = 14, + NORWEGIAN = 15, + POLISH = 16, + PORTUGUESE_BR = 17, + PORTUGUESE_PT = 18, + RUSSIAN = 19, + SWEDISH = 20, + TURKISH = 21, + JAPANESE_ROMAN = 22, + JAPANESE_KANA = 23, + KOREAN = 24, + SM_CHINESE = 25, + TR_CHINESE_ZY = 26, + TR_CHINESE_PY_HK = 27, + TR_CHINESE_PY_TW = 28, + TR_CHINESE_CG = 29, + ARABIC_AR = 30, + THAI = 31, + CZECH = 32, + GREEK = 33, + INDONESIAN = 34, + VIETNAMESE = 35, + ROMANIAN = 36, + HUNGARIAN = 37, +}; + +enum class OrbisImeDeviceType : u32 { + NONE = 0, + CONTROLLER = 1, + EXT_KEYBOARD = 2, + REMOTE_OSK = 3, +}; + +struct OrbisImeRect { + f32 x; + f32 y; + u32 width; + u32 height; +}; + +struct OrbisImeTextAreaProperty { + u32 mode; // OrbisImeTextAreaMode + u32 index; + s32 length; +}; + +struct OrbisImeEditText { + char16_t* str; + u32 caretIndex; + u32 areaNum; + OrbisImeTextAreaProperty textArea[4]; +}; + +struct OrbisImeKeycode { + u16 keycode; + char16_t character; + u32 status; + OrbisImeKeyboardType type; + s32 userId; + u32 resourceId; + Libraries::Rtc::OrbisRtcTick timestamp; +}; + +struct OrbisImeKeyboardResourceIdArray { + s32 userId; + u32 resourceId[6]; +}; + +enum class OrbisImeCaretMovementDirection : u32 { + STILL = 0, + LEFT = 1, + RIGHT = 2, + UP = 3, + DOWN = 4, + HOME = 5, + END = 6, + PAGE_UP = 7, + PAGE_DOWN = 8, + TOP = 9, + BOTTOM = 10, +}; + +union OrbisImeEventParam { + OrbisImeRect rect; + OrbisImeEditText text; + OrbisImeCaretMovementDirection caretMove; + OrbisImeKeycode keycode; + OrbisImeKeyboardResourceIdArray resourceIdArray; + char16_t* candidateWord; + s32 candidateIndex; + OrbisImeDeviceType deviceType; + u32 inputMethodState; + s8 reserved[64]; +}; + +struct OrbisImeEvent { + OrbisImeEventId id; + OrbisImeEventParam param; +}; + +typedef PS4_SYSV_ABI int (*OrbisImeTextFilter)(char16_t* outText, u32* outTextLength, + const char16_t* srcText, u32 srcTextLength); + +typedef PS4_SYSV_ABI void (*OrbisImeEventHandler)(void* arg, const OrbisImeEvent* e); diff --git a/src/core/libraries/dialogs/ime_dialog.cpp b/src/core/libraries/ime/ime_dialog.cpp similarity index 100% rename from src/core/libraries/dialogs/ime_dialog.cpp rename to src/core/libraries/ime/ime_dialog.cpp diff --git a/src/core/libraries/dialogs/ime_dialog.h b/src/core/libraries/ime/ime_dialog.h similarity index 76% rename from src/core/libraries/dialogs/ime_dialog.h rename to src/core/libraries/ime/ime_dialog.h index 66cf9fb93..e99d29613 100644 --- a/src/core/libraries/dialogs/ime_dialog.h +++ b/src/core/libraries/ime/ime_dialog.h @@ -5,6 +5,7 @@ #include "common/enum.h" #include "common/types.h" +#include "ime_common.h" namespace Core::Loader { class SymbolsResolver; @@ -68,21 +69,6 @@ enum class OrbisImeDialogEndStatus : u32 { ABORTED = 2, }; -enum class OrbisImeType : u32 { - DEFAULT = 0, - BASIC_LATIN = 1, - URL = 2, - MAIL = 3, - NUMBER = 4, -}; - -enum class OrbisImeEnterLabel : u32 { - DEFAULT = 0, - SEND = 1, - SEARCH = 2, - GO = 3, -}; - enum class OrbisImeDialogOption : u32 { DEFAULT = 0, MULTILINE = 1, @@ -91,25 +77,8 @@ enum class OrbisImeDialogOption : u32 { // TODO: Document missing options LARGE_RESOLUTION = 1024, }; - DECLARE_ENUM_FLAG_OPERATORS(OrbisImeDialogOption) -enum class OrbisImeInputMethod : u32 { - DEFAULT = 0, -}; - -enum class OrbisImeHorizontalAlignment : u32 { - LEFT = 0, - CENTER = 1, - RIGHT = 2, -}; - -enum class OrbisImeVerticalAlignment : u32 { - TOP = 0, - CENTER = 1, - BOTTOM = 2, -}; - enum class OrbisImePanelPriority : u32 { DEFAULT = 0, ALPHABET = 1, @@ -117,47 +86,6 @@ enum class OrbisImePanelPriority : u32 { ACCENT = 3, }; -enum class OrbisImeKeyboardType : u32 { - NONE = 0, - DANISH = 1, - GERMAN = 2, - GERMAN_SW = 3, - ENGLISH_US = 4, - ENGLISH_GB = 5, - SPANISH = 6, - SPANISH_LA = 7, - FINNISH = 8, - FRENCH = 9, - FRENCH_BR = 10, - FRENCH_CA = 11, - FRENCH_SW = 12, - ITALIAN = 13, - DUTCH = 14, - NORWEGIAN = 15, - POLISH = 16, - PORTUGUESE_BR = 17, - PORTUGUESE_PT = 18, - RUSSIAN = 19, - SWEDISH = 20, - TURKISH = 21, - JAPANESE_ROMAN = 22, - JAPANESE_KANA = 23, - KOREAN = 24, - SM_CHINESE = 25, - TR_CHINESE_ZY = 26, - TR_CHINESE_PY_HK = 27, - TR_CHINESE_PY_TW = 28, - TR_CHINESE_CG = 29, - ARABIC_AR = 30, - THAI = 31, - CZECH = 32, - GREEK = 33, - INDONESIAN = 34, - VIETNAMESE = 35, - ROMANIAN = 36, - HUNGARIAN = 37, -}; - struct OrbisImeColor { u8 r; u8 g; @@ -180,9 +108,6 @@ struct OrbisImeKeycode { u64 timestamp; }; -typedef PS4_SYSV_ABI int (*OrbisImeTextFilter)(char16_t* outText, u32* outTextLength, - const char16_t* srcText, u32 srcTextLength); - typedef PS4_SYSV_ABI int (*OrbisImeExtKeyboardFilter)(const OrbisImeKeycode* srcKeycode, u16* outKeycode, u32* outStatus, void* reserved); diff --git a/src/core/libraries/dialogs/ime_dialog_ui.cpp b/src/core/libraries/ime/ime_dialog_ui.cpp similarity index 99% rename from src/core/libraries/dialogs/ime_dialog_ui.cpp rename to src/core/libraries/ime/ime_dialog_ui.cpp index 9d50d2fbb..bba45d0fe 100644 --- a/src/core/libraries/dialogs/ime_dialog_ui.cpp +++ b/src/core/libraries/ime/ime_dialog_ui.cpp @@ -9,8 +9,8 @@ #include "common/assert.h" #include "common/logging/log.h" #include "common/singleton.h" -#include "core/libraries/dialogs/ime_dialog.h" -#include "core/libraries/dialogs/ime_dialog_ui.h" +#include "core/libraries/ime/ime_dialog.h" +#include "core/libraries/ime/ime_dialog_ui.h" #include "core/linker.h" #include "imgui/imgui_std.h" @@ -22,8 +22,9 @@ namespace Libraries::ImeDialog { ImeDialogState::ImeDialogState(const OrbisImeDialogParam* param, const OrbisImeParamExtended* extended) { - if (!param) + if (!param) { return; + } userId = param->userId; is_multiLine = True(param->option & OrbisImeDialogOption::MULTILINE); @@ -344,7 +345,6 @@ void ImeDialogUi::DrawMultiLineInputText() { int ImeDialogUi::InputTextCallback(ImGuiInputTextCallbackData* data) { ImeDialogUi* ui = static_cast(data->UserData); - ASSERT(ui); // Should we filter punctuation? diff --git a/src/core/libraries/dialogs/ime_dialog_ui.h b/src/core/libraries/ime/ime_dialog_ui.h similarity index 98% rename from src/core/libraries/dialogs/ime_dialog_ui.h rename to src/core/libraries/ime/ime_dialog_ui.h index 96c83954a..a4cf6e9f7 100644 --- a/src/core/libraries/dialogs/ime_dialog_ui.h +++ b/src/core/libraries/ime/ime_dialog_ui.h @@ -8,7 +8,7 @@ #include #include "common/cstring.h" #include "common/types.h" -#include "core/libraries/dialogs/ime_dialog.h" +#include "core/libraries/ime/ime_dialog.h" #include "imgui/imgui_layer.h" namespace Libraries::ImeDialog { diff --git a/src/core/libraries/ime/ime_ui.cpp b/src/core/libraries/ime/ime_ui.cpp new file mode 100644 index 000000000..09d361263 --- /dev/null +++ b/src/core/libraries/ime/ime_ui.cpp @@ -0,0 +1,252 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "ime_ui.h" +#include "imgui/imgui_std.h" + +namespace Libraries::Ime { + +using namespace ImGui; + +static constexpr ImVec2 BUTTON_SIZE{100.0f, 30.0f}; + +ImeState::ImeState(const OrbisImeParam* param) { + if (!param) { + return; + } + + work_buffer = param->work; + text_buffer = param->inputTextBuffer; + + std::size_t text_len = std::char_traits::length(text_buffer); + if (!ConvertOrbisToUTF8(text_buffer, text_len, current_text.begin(), + ORBIS_IME_MAX_TEXT_LENGTH * 4)) { + LOG_ERROR(Lib_ImeDialog, "Failed to convert text to utf8 encoding"); + } +} + +ImeState::ImeState(ImeState&& other) noexcept + : input_changed(other.input_changed), work_buffer(other.work_buffer), + text_buffer(other.text_buffer), current_text(std::move(other.current_text)), + event_queue(std::move(other.event_queue)) { + other.text_buffer = nullptr; +} + +ImeState& ImeState::operator=(ImeState&& other) noexcept { + if (this != &other) { + input_changed = other.input_changed; + work_buffer = other.work_buffer; + text_buffer = other.text_buffer; + current_text = std::move(other.current_text); + event_queue = std::move(other.event_queue); + + other.text_buffer = nullptr; + } + return *this; +} + +void ImeState::SendEvent(OrbisImeEvent* event) { + std::unique_lock lock{queue_mutex}; + event_queue.push(*event); +} + +void ImeState::SendEnterEvent() { + OrbisImeEvent enterEvent{}; + enterEvent.id = OrbisImeEventId::PRESS_ENTER; + SendEvent(&enterEvent); +} + +void ImeState::SendCloseEvent() { + OrbisImeEvent closeEvent{}; + closeEvent.id = OrbisImeEventId::PRESS_CLOSE; + closeEvent.param.text.str = reinterpret_cast(work_buffer); + SendEvent(&closeEvent); +} + +bool ImeState::ConvertOrbisToUTF8(const char16_t* orbis_text, std::size_t orbis_text_len, + char* utf8_text, std::size_t utf8_text_len) { + std::fill(utf8_text, utf8_text + utf8_text_len, '\0'); + const ImWchar* orbis_text_ptr = reinterpret_cast(orbis_text); + ImTextStrToUtf8(utf8_text, utf8_text_len, orbis_text_ptr, orbis_text_ptr + orbis_text_len); + + return true; +} + +bool ImeState::ConvertUTF8ToOrbis(const char* utf8_text, std::size_t utf8_text_len, + char16_t* orbis_text, std::size_t orbis_text_len) { + std::fill(orbis_text, orbis_text + orbis_text_len, u'\0'); + ImTextStrFromUtf8(reinterpret_cast(orbis_text), orbis_text_len, utf8_text, nullptr); + + return true; +} + +ImeUi::ImeUi(ImeState* state, const OrbisImeParam* param) : state(state), ime_param(param) { + if (param) { + AddLayer(this); + } +} + +ImeUi::~ImeUi() { + std::scoped_lock lock(draw_mutex); + Free(); +} + +ImeUi& ImeUi::operator=(ImeUi&& other) { + std::scoped_lock lock(draw_mutex, other.draw_mutex); + Free(); + + state = other.state; + ime_param = other.ime_param; + first_render = other.first_render; + other.state = nullptr; + other.ime_param = nullptr; + + AddLayer(this); + return *this; +} + +void ImeUi::Draw() { + std::unique_lock lock{draw_mutex}; + + if (!state) { + return; + } + + const auto& ctx = *GetCurrentContext(); + const auto& io = ctx.IO; + + // TODO: Figure out how to properly translate the positions - + // for example, if a game wants to center the IME panel, + // we have to translate the panel position in a way that it + // still becomes centered, as the game normally calculates + // the position assuming a it's running on a 1920x1080 screen, + // whereas we are running on a 1280x720 window size (by default). + // + // e.g. Panel position calculation from a game: + // param.posx = (1920 / 2) - (panelWidth / 2); + // param.posy = (1080 / 2) - (panelHeight / 2); + const auto size = GetIO().DisplaySize; + f32 pos_x = (ime_param->posx / 1920.0f * (float)size.x); + f32 pos_y = (ime_param->posy / 1080.0f * (float)size.y); + + ImVec2 window_pos = {pos_x, pos_y}; + ImVec2 window_size = {500.0f, 100.0f}; + + // SetNextWindowPos(window_pos); + SetNextWindowPos(ImVec2(io.DisplaySize.x * 0.5f, io.DisplaySize.y * 0.5f), + ImGuiCond_FirstUseEver, ImVec2(0.5f, 0.5f)); + SetNextWindowSize(window_size); + SetNextWindowCollapsed(false); + + if (first_render || !io.NavActive) { + SetNextWindowFocus(); + } + + if (Begin("IME##Ime", nullptr, + ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | + ImGuiWindowFlags_NoSavedSettings)) { + DrawPrettyBackground(); + + DrawInputText(); + SetCursorPosY(GetCursorPosY() + 10.0f); + + const char* button_text; + button_text = "Done##ImeDone"; + + float button_spacing = 10.0f; + float total_button_width = BUTTON_SIZE.x * 2 + button_spacing; + float button_start_pos = (window_size.x - total_button_width) / 2.0f; + + SetCursorPosX(button_start_pos); + + if (Button(button_text, BUTTON_SIZE) || (IsKeyPressed(ImGuiKey_Enter))) { + state->SendEnterEvent(); + } + + SameLine(0.0f, button_spacing); + + if (Button("Close##ImeClose", BUTTON_SIZE)) { + state->SendCloseEvent(); + } + } + End(); + + first_render = false; +} + +void ImeUi::DrawInputText() { + ImVec2 input_size = {GetWindowWidth() - 40.0f, 0.0f}; + SetCursorPosX(20.0f); + if (first_render) { + SetKeyboardFocusHere(); + } + if (InputTextEx("##ImeInput", nullptr, state->current_text.begin(), ime_param->maxTextLength, + input_size, ImGuiInputTextFlags_CallbackAlways, InputTextCallback, this)) { + state->input_changed = true; + } +} + +int ImeUi::InputTextCallback(ImGuiInputTextCallbackData* data) { + ImeUi* ui = static_cast(data->UserData); + ASSERT(ui); + + static int lastCaretPos = -1; + if (lastCaretPos == -1) { + lastCaretPos = data->CursorPos; + } else if (data->CursorPos != lastCaretPos) { + OrbisImeCaretMovementDirection caretDirection = OrbisImeCaretMovementDirection::STILL; + if (data->CursorPos < lastCaretPos) { + caretDirection = OrbisImeCaretMovementDirection::LEFT; + } else if (data->CursorPos > lastCaretPos) { + caretDirection = OrbisImeCaretMovementDirection::RIGHT; + } + + OrbisImeEvent event{}; + event.id = OrbisImeEventId::UPDATE_CARET; + event.param.caretMove = caretDirection; + + lastCaretPos = data->CursorPos; + ui->state->SendEvent(&event); + } + + static std::string lastText; + std::string currentText(data->Buf, data->BufTextLen); + if (currentText != lastText) { + OrbisImeEditText eventParam{}; + eventParam.str = reinterpret_cast(ui->ime_param->work); + eventParam.caretIndex = data->CursorPos; + eventParam.areaNum = 1; + + eventParam.textArea[0].mode = 1; // Edit mode + eventParam.textArea[0].index = data->CursorPos; + eventParam.textArea[0].length = data->BufTextLen; + + if (!ui->state->ConvertUTF8ToOrbis(data->Buf, data->BufTextLen, eventParam.str, + ui->ime_param->maxTextLength)) { + LOG_ERROR(Lib_ImeDialog, "Failed to convert Orbis char to UTF-8"); + return 0; + } + + if (!ui->state->ConvertUTF8ToOrbis(data->Buf, data->BufTextLen, + ui->ime_param->inputTextBuffer, + ui->ime_param->maxTextLength)) { + LOG_ERROR(Lib_ImeDialog, "Failed to convert Orbis char to UTF-8"); + return 0; + } + + OrbisImeEvent event{}; + event.id = OrbisImeEventId::UPDATE_TEXT; + event.param.text = eventParam; + + lastText = currentText; + ui->state->SendEvent(&event); + } + + return 0; +} + +void ImeUi::Free() { + RemoveLayer(this); +} + +}; // namespace Libraries::Ime \ No newline at end of file diff --git a/src/core/libraries/ime/ime_ui.h b/src/core/libraries/ime/ime_ui.h new file mode 100644 index 000000000..ebd70a7c8 --- /dev/null +++ b/src/core/libraries/ime/ime_ui.h @@ -0,0 +1,76 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include +#include +#include "imgui/imgui_layer.h" + +#include "common/cstring.h" +#include "common/types.h" + +#include "ime.h" + +namespace Libraries::Ime { + +class ImeHandler; +class ImeUi; + +class ImeState { + friend class ImeHandler; + friend class ImeUi; + + bool input_changed = false; + + void* work_buffer{}; + + char16_t* text_buffer{}; + + // A character can hold up to 4 bytes in UTF-8 + Common::CString current_text; + + std::queue event_queue; + std::mutex queue_mutex; + +public: + ImeState(const OrbisImeParam* param = nullptr); + ImeState(ImeState&& other) noexcept; + ImeState& operator=(ImeState&& other) noexcept; + + void SendEvent(OrbisImeEvent* event); + void SendEnterEvent(); + void SendCloseEvent(); + +private: + bool ConvertOrbisToUTF8(const char16_t* orbis_text, std::size_t orbis_text_len, char* utf8_text, + std::size_t native_text_len); + bool ConvertUTF8ToOrbis(const char* native_text, std::size_t utf8_text_len, + char16_t* orbis_text, std::size_t orbis_text_len); +}; + +class ImeUi : public ImGui::Layer { + ImeState* state{}; + const OrbisImeParam* ime_param{}; + + bool first_render = true; + std::mutex draw_mutex; + +public: + explicit ImeUi(ImeState* state = nullptr, const OrbisImeParam* param = nullptr); + ~ImeUi() override; + ImeUi(const ImeUi& other) = delete; + ImeUi& operator=(ImeUi&& other); + + void Draw() override; + +private: + void Free(); + + void DrawInputText(); + + static int InputTextCallback(ImGuiInputTextCallbackData* data); +}; + +}; // namespace Libraries::Ime \ No newline at end of file diff --git a/src/core/libraries/kernel/libkernel.cpp b/src/core/libraries/kernel/libkernel.cpp index b013f29bf..c621b4bca 100644 --- a/src/core/libraries/kernel/libkernel.cpp +++ b/src/core/libraries/kernel/libkernel.cpp @@ -256,7 +256,7 @@ int PS4_SYSV_ABI sceKernelConvertUtcToLocaltime(time_t time, time_t* local_time, int PS4_SYSV_ABI sceKernelGetCompiledSdkVersion(int* ver) { int version = Common::ElfInfo::Instance().RawFirmwareVer(); - LOG_INFO(Kernel, "returned system version = {:#x}", version); + LOG_DEBUG(Kernel, "returned system version = {:#x}", version); *ver = version; return (version > 0) ? ORBIS_OK : ORBIS_KERNEL_ERROR_EINVAL; } diff --git a/src/core/libraries/libs.cpp b/src/core/libraries/libs.cpp index caa254fd8..b0365435b 100644 --- a/src/core/libraries/libs.cpp +++ b/src/core/libraries/libs.cpp @@ -8,12 +8,12 @@ #include "core/libraries/audio/audioout.h" #include "core/libraries/audio3d/audio3d.h" #include "core/libraries/avplayer/avplayer.h" -#include "core/libraries/dialogs/error_dialog.h" -#include "core/libraries/dialogs/ime_dialog.h" #include "core/libraries/disc_map/disc_map.h" #include "core/libraries/game_live_streaming/gamelivestreaming.h" #include "core/libraries/gnmdriver/gnmdriver.h" +#include "core/libraries/ime/error_dialog.h" #include "core/libraries/ime/ime.h" +#include "core/libraries/ime/ime_dialog.h" #include "core/libraries/kernel/libkernel.h" #include "core/libraries/libc_internal/libc_internal.h" #include "core/libraries/libpng/pngdec.h" @@ -41,6 +41,7 @@ #include "core/libraries/system/systemservice.h" #include "core/libraries/system/userservice.h" #include "core/libraries/usbd/usbd.h" +#include "core/libraries/videodec/videodec2.h" #include "core/libraries/videoout/video_out.h" namespace Libraries { @@ -80,6 +81,7 @@ void InitHLELibs(Core::Loader::SymbolsResolver* sym) { Libraries::ErrorDialog::RegisterlibSceErrorDialog(sym); Libraries::ImeDialog::RegisterlibSceImeDialog(sym); Libraries::AvPlayer::RegisterlibSceAvPlayer(sym); + Libraries::Vdec2::RegisterlibSceVdec2(sym); Libraries::Audio3d::RegisterlibSceAudio3d(sym); Libraries::Ime::RegisterlibSceIme(sym); Libraries::GameLiveStreaming::RegisterlibSceGameLiveStreaming(sym); diff --git a/src/core/libraries/videodec/videodec2.cpp b/src/core/libraries/videodec/videodec2.cpp new file mode 100644 index 000000000..fe2b82bea --- /dev/null +++ b/src/core/libraries/videodec/videodec2.cpp @@ -0,0 +1,200 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "videodec2.h" +#include "videodec2_impl.h" + +#include "common/assert.h" +#include "common/logging/log.h" +#include "core/libraries/error_codes.h" +#include "core/libraries/libs.h" + +namespace Libraries::Vdec2 { + +static constexpr u64 kMinimumMemorySize = 32_MB; ///> Fake minimum memory size for querying + +s32 PS4_SYSV_ABI +sceVideodec2QueryComputeMemoryInfo(OrbisVideodec2ComputeMemoryInfo* computeMemInfo) { + LOG_INFO(Lib_Vdec2, "called"); + + if (!computeMemInfo) { + return ORBIS_VIDEODEC2_ERROR_ARGUMENT_POINTER; + } + if (computeMemInfo->thisSize != sizeof(OrbisVideodec2ComputeMemoryInfo)) { + return ORBIS_VIDEODEC2_ERROR_STRUCT_SIZE; + } + + computeMemInfo->cpuGpuMemory = nullptr; + computeMemInfo->cpuGpuMemorySize = kMinimumMemorySize; + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI +sceVideodec2AllocateComputeQueue(const OrbisVideodec2ComputeConfigInfo* computeCfgInfo, + const OrbisVideodec2ComputeMemoryInfo* computeMemInfo, + OrbisVideodec2ComputeQueue* computeQueue) { + LOG_INFO(Lib_Vdec2, "called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceVideodec2ReleaseComputeQueue(OrbisVideodec2ComputeQueue computeQueue) { + LOG_INFO(Lib_Vdec2, "called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI +sceVideodec2QueryDecoderMemoryInfo(const OrbisVideodec2DecoderConfigInfo* decoderCfgInfo, + OrbisVideodec2DecoderMemoryInfo* decoderMemInfo) { + LOG_INFO(Lib_Vdec2, "called"); + + if (!decoderCfgInfo || !decoderMemInfo) { + return ORBIS_VIDEODEC2_ERROR_ARGUMENT_POINTER; + } + if (decoderCfgInfo->thisSize != sizeof(OrbisVideodec2DecoderConfigInfo) || + decoderMemInfo->thisSize != sizeof(OrbisVideodec2DecoderMemoryInfo)) { + return ORBIS_VIDEODEC2_ERROR_STRUCT_SIZE; + } + + decoderMemInfo->cpuMemory = nullptr; + decoderMemInfo->gpuMemory = nullptr; + decoderMemInfo->cpuGpuMemory = nullptr; + + decoderMemInfo->cpuGpuMemorySize = kMinimumMemorySize; + decoderMemInfo->cpuMemorySize = kMinimumMemorySize; + decoderMemInfo->gpuMemorySize = kMinimumMemorySize; + + decoderMemInfo->maxFrameBufferSize = kMinimumMemorySize; + decoderMemInfo->frameBufferAlignment = 0x100; + + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceVideodec2CreateDecoder(const OrbisVideodec2DecoderConfigInfo* decoderCfgInfo, + const OrbisVideodec2DecoderMemoryInfo* decoderMemInfo, + OrbisVideodec2Decoder* decoder) { + LOG_INFO(Lib_Vdec2, "called"); + + if (!decoderCfgInfo || !decoderMemInfo || !decoder) { + return ORBIS_VIDEODEC2_ERROR_ARGUMENT_POINTER; + } + if (decoderCfgInfo->thisSize != sizeof(OrbisVideodec2DecoderConfigInfo) || + decoderMemInfo->thisSize != sizeof(OrbisVideodec2DecoderMemoryInfo)) { + return ORBIS_VIDEODEC2_ERROR_STRUCT_SIZE; + } + + *decoder = new VdecDecoder(*decoderCfgInfo, *decoderMemInfo); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceVideodec2DeleteDecoder(OrbisVideodec2Decoder decoder) { + LOG_INFO(Lib_Vdec2, "called"); + + if (!decoder) { + return ORBIS_VIDEODEC2_ERROR_DECODER_INSTANCE; + } + + delete decoder; + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceVideodec2Decode(OrbisVideodec2Decoder decoder, + const OrbisVideodec2InputData* inputData, + OrbisVideodec2FrameBuffer* frameBuffer, + OrbisVideodec2OutputInfo* outputInfo) { + LOG_TRACE(Lib_Vdec2, "called"); + + if (!decoder) { + return ORBIS_VIDEODEC2_ERROR_DECODER_INSTANCE; + } + if (!inputData || !frameBuffer || !outputInfo) { + return ORBIS_VIDEODEC2_ERROR_ARGUMENT_POINTER; + } + if (inputData->thisSize != sizeof(OrbisVideodec2InputData) || + frameBuffer->thisSize != sizeof(OrbisVideodec2FrameBuffer)) { + return ORBIS_VIDEODEC2_ERROR_STRUCT_SIZE; + } + + return decoder->Decode(*inputData, *frameBuffer, *outputInfo); +} + +s32 PS4_SYSV_ABI sceVideodec2Flush(OrbisVideodec2Decoder decoder, + OrbisVideodec2FrameBuffer* frameBuffer, + OrbisVideodec2OutputInfo* outputInfo) { + LOG_INFO(Lib_Vdec2, "called"); + + if (!decoder) { + return ORBIS_VIDEODEC2_ERROR_DECODER_INSTANCE; + } + if (!frameBuffer || !outputInfo) { + return ORBIS_VIDEODEC2_ERROR_ARGUMENT_POINTER; + } + if (frameBuffer->thisSize != sizeof(OrbisVideodec2FrameBuffer) || + outputInfo->thisSize != sizeof(OrbisVideodec2OutputInfo)) { + return ORBIS_VIDEODEC2_ERROR_STRUCT_SIZE; + } + + return decoder->Flush(*frameBuffer, *outputInfo); +} + +s32 PS4_SYSV_ABI sceVideodec2Reset(OrbisVideodec2Decoder decoder) { + LOG_INFO(Lib_Vdec2, "called"); + + if (!decoder) { + return ORBIS_VIDEODEC2_ERROR_DECODER_INSTANCE; + } + + return decoder->Reset(); +} + +s32 PS4_SYSV_ABI sceVideodec2GetPictureInfo(const OrbisVideodec2OutputInfo* outputInfo, + void* p1stPictureInfoOut, void* p2ndPictureInfoOut) { + LOG_TRACE(Lib_Vdec2, "called"); + + if (!outputInfo) { + return ORBIS_VIDEODEC2_ERROR_ARGUMENT_POINTER; + } + if (outputInfo->thisSize != sizeof(OrbisVideodec2OutputInfo)) { + return ORBIS_VIDEODEC2_ERROR_STRUCT_SIZE; + } + if (outputInfo->pictureCount == 0 || gPictureInfos.empty()) { + return ORBIS_OK; + } + + if (p1stPictureInfoOut) { + OrbisVideodec2AvcPictureInfo* picInfo = + static_cast(p1stPictureInfoOut); + if (picInfo->thisSize != sizeof(OrbisVideodec2AvcPictureInfo)) { + return ORBIS_VIDEODEC2_ERROR_STRUCT_SIZE; + } + *picInfo = gPictureInfos.back(); + } + + if (outputInfo->pictureCount > 1) { + UNREACHABLE(); + } + + return ORBIS_OK; +} + +void RegisterlibSceVdec2(Core::Loader::SymbolsResolver* sym) { + LIB_FUNCTION("RnDibcGCPKw", "libSceVideodec2", 1, "libSceVideodec2", 1, 1, + sceVideodec2QueryComputeMemoryInfo); + LIB_FUNCTION("eD+X2SmxUt4", "libSceVideodec2", 1, "libSceVideodec2", 1, 1, + sceVideodec2AllocateComputeQueue); + LIB_FUNCTION("UvtA3FAiF4Y", "libSceVideodec2", 1, "libSceVideodec2", 1, 1, + sceVideodec2ReleaseComputeQueue); + + LIB_FUNCTION("qqMCwlULR+E", "libSceVideodec2", 1, "libSceVideodec2", 1, 1, + sceVideodec2QueryDecoderMemoryInfo); + LIB_FUNCTION("CNNRoRYd8XI", "libSceVideodec2", 1, "libSceVideodec2", 1, 1, + sceVideodec2CreateDecoder); + LIB_FUNCTION("jwImxXRGSKA", "libSceVideodec2", 1, "libSceVideodec2", 1, 1, + sceVideodec2DeleteDecoder); + LIB_FUNCTION("852F5+q6+iM", "libSceVideodec2", 1, "libSceVideodec2", 1, 1, sceVideodec2Decode); + LIB_FUNCTION("l1hXwscLuCY", "libSceVideodec2", 1, "libSceVideodec2", 1, 1, sceVideodec2Flush); + LIB_FUNCTION("wJXikG6QFN8", "libSceVideodec2", 1, "libSceVideodec2", 1, 1, sceVideodec2Reset); + LIB_FUNCTION("NtXRa3dRzU0", "libSceVideodec2", 1, "libSceVideodec2", 1, 1, + sceVideodec2GetPictureInfo); +} + +} // namespace Libraries::Vdec2 \ No newline at end of file diff --git a/src/core/libraries/videodec/videodec2.h b/src/core/libraries/videodec/videodec2.h new file mode 100644 index 000000000..9b230cc54 --- /dev/null +++ b/src/core/libraries/videodec/videodec2.h @@ -0,0 +1,139 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/types.h" + +#include "videodec2_avc.h" + +namespace Core::Loader { +class SymbolsResolver; +} +namespace Libraries::Vdec2 { + +class VdecDecoder; + +using OrbisVideodec2Decoder = VdecDecoder*; +typedef void* OrbisVideodec2ComputeQueue; + +struct OrbisVideodec2DecoderConfigInfo { + u64 thisSize; + u32 resourceType; + u32 codecType; + u32 profile; + u32 maxLevel; + s32 maxFrameWidth; + s32 maxFrameHeight; + s32 maxDpbFrameCount; + u32 decodePipelineDepth; + OrbisVideodec2ComputeQueue computeQueue; + u64 cpuAffinityMask; + s32 cpuThreadPriority; + bool optimizeProgressiveVideo; + bool checkMemoryType; + u8 reserved0; + u8 reserved1; + void* extraConfigInfo; +}; +static_assert(sizeof(OrbisVideodec2DecoderConfigInfo) == 0x48); + +struct OrbisVideodec2DecoderMemoryInfo { + u64 thisSize; + u64 cpuMemorySize; + void* cpuMemory; + u64 gpuMemorySize; + void* gpuMemory; + u64 cpuGpuMemorySize; + void* cpuGpuMemory; + u64 maxFrameBufferSize; + u32 frameBufferAlignment; + u32 reserved0; +}; +static_assert(sizeof(OrbisVideodec2DecoderMemoryInfo) == 0x48); + +struct OrbisVideodec2InputData { + u64 thisSize; + void* auData; + u64 auSize; + u64 ptsData; + u64 dtsData; + u64 attachedData; +}; +static_assert(sizeof(OrbisVideodec2InputData) == 0x30); + +struct OrbisVideodec2OutputInfo { + u64 thisSize; + bool isValid; + bool isErrorFrame; + u8 pictureCount; + u32 codecType; + u32 frameWidth; + u32 framePitch; + u32 frameHeight; + void* frameBuffer; + u64 frameBufferSize; +}; +static_assert(sizeof(OrbisVideodec2OutputInfo) == 0x30); + +struct OrbisVideodec2FrameBuffer { + u64 thisSize; + void* frameBuffer; + u64 frameBufferSize; + bool isAccepted; +}; +static_assert(sizeof(OrbisVideodec2FrameBuffer) == 0x20); + +struct OrbisVideodec2ComputeMemoryInfo { + u64 thisSize; + u64 cpuGpuMemorySize; + void* cpuGpuMemory; +}; +static_assert(sizeof(OrbisVideodec2ComputeMemoryInfo) == 0x18); + +struct OrbisVideodec2ComputeConfigInfo { + u64 thisSize; + u16 computePipeId; + u16 computeQueueId; + bool checkMemoryType; + u8 reserved0; + u16 reserved1; +}; +static_assert(sizeof(OrbisVideodec2ComputeConfigInfo) == 0x10); + +s32 PS4_SYSV_ABI +sceVideodec2QueryComputeMemoryInfo(OrbisVideodec2ComputeMemoryInfo* computeMemInfo); + +s32 PS4_SYSV_ABI +sceVideodec2AllocateComputeQueue(const OrbisVideodec2ComputeConfigInfo* computeCfgInfo, + const OrbisVideodec2ComputeMemoryInfo* computeMemInfo, + OrbisVideodec2ComputeQueue* computeQueue); + +s32 PS4_SYSV_ABI sceVideodec2ReleaseComputeQueue(OrbisVideodec2ComputeQueue computeQueue); + +s32 PS4_SYSV_ABI +sceVideodec2QueryDecoderMemoryInfo(const OrbisVideodec2DecoderConfigInfo* decoderCfgInfo, + OrbisVideodec2DecoderMemoryInfo* decoderMemInfo); + +s32 PS4_SYSV_ABI sceVideodec2CreateDecoder(const OrbisVideodec2DecoderConfigInfo* decoderCfgInfo, + const OrbisVideodec2DecoderMemoryInfo* decoderMemInfo, + OrbisVideodec2Decoder* decoder); + +s32 PS4_SYSV_ABI sceVideodec2DeleteDecoder(OrbisVideodec2Decoder decoder); + +s32 PS4_SYSV_ABI sceVideodec2Decode(OrbisVideodec2Decoder decoder, + const OrbisVideodec2InputData* inputData, + OrbisVideodec2FrameBuffer* frameBuffer, + OrbisVideodec2OutputInfo* outputInfo); + +s32 PS4_SYSV_ABI sceVideodec2Flush(OrbisVideodec2Decoder decoder, + OrbisVideodec2FrameBuffer* frameBuffer, + OrbisVideodec2OutputInfo* outputInfo); + +s32 PS4_SYSV_ABI sceVideodec2Reset(OrbisVideodec2Decoder decoder); + +s32 PS4_SYSV_ABI sceVideodec2GetPictureInfo(const OrbisVideodec2OutputInfo* outputInfo, + void* p1stPictureInfo, void* p2ndPictureInfo); + +void RegisterlibSceVdec2(Core::Loader::SymbolsResolver* sym); +} // namespace Libraries::Vdec2 \ No newline at end of file diff --git a/src/core/libraries/videodec/videodec2_avc.h b/src/core/libraries/videodec/videodec2_avc.h new file mode 100644 index 000000000..4109b5fd2 --- /dev/null +++ b/src/core/libraries/videodec/videodec2_avc.h @@ -0,0 +1,60 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/types.h" + +namespace Libraries::Vdec2 { + +struct OrbisVideodec2AvcPictureInfo { + 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; +}; + +} // namespace Libraries::Vdec2 \ No newline at end of file diff --git a/src/core/libraries/videodec/videodec2_impl.cpp b/src/core/libraries/videodec/videodec2_impl.cpp new file mode 100644 index 000000000..021965e3d --- /dev/null +++ b/src/core/libraries/videodec/videodec2_impl.cpp @@ -0,0 +1,228 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "videodec2_impl.h" + +#include "common/alignment.h" +#include "common/assert.h" +#include "common/logging/log.h" +#include "core/libraries/error_codes.h" + +namespace Libraries::Vdec2 { + +std::vector gPictureInfos; + +static inline void CopyNV12Data(u8* dst, const AVFrame& src) { + std::memcpy(dst, src.data[0], src.width * src.height); + std::memcpy(dst + (src.width * src.height), src.data[1], (src.width * src.height) / 2); +} + +VdecDecoder::VdecDecoder(const OrbisVideodec2DecoderConfigInfo& configInfo, + const OrbisVideodec2DecoderMemoryInfo& memoryInfo) { + ASSERT(configInfo.codecType == 1); /* AVC */ + + const AVCodec* codec = avcodec_find_decoder(AV_CODEC_ID_H264); + ASSERT(codec); + + mCodecContext = avcodec_alloc_context3(codec); + ASSERT(mCodecContext); + mCodecContext->width = configInfo.maxFrameWidth; + mCodecContext->height = configInfo.maxFrameHeight; + + avcodec_open2(mCodecContext, codec, nullptr); +} + +VdecDecoder::~VdecDecoder() { + avcodec_free_context(&mCodecContext); + sws_freeContext(mSwsContext); + + gPictureInfos.clear(); +} + +s32 VdecDecoder::Decode(const OrbisVideodec2InputData& inputData, + OrbisVideodec2FrameBuffer& frameBuffer, + OrbisVideodec2OutputInfo& outputInfo) { + frameBuffer.isAccepted = false; + outputInfo.thisSize = sizeof(OrbisVideodec2OutputInfo); + outputInfo.isValid = false; + outputInfo.isErrorFrame = true; + outputInfo.pictureCount = 0; + + if (!inputData.auData) { + return ORBIS_VIDEODEC2_ERROR_ACCESS_UNIT_POINTER; + } + if (inputData.auSize == 0) { + return ORBIS_VIDEODEC2_ERROR_ACCESS_UNIT_SIZE; + } + + AVPacket* packet = av_packet_alloc(); + if (!packet) { + LOG_ERROR(Lib_Vdec2, "Failed to allocate packet"); + return ORBIS_VIDEODEC2_ERROR_API_FAIL; + } + + packet->data = (u8*)inputData.auData; + packet->size = inputData.auSize; + packet->pts = inputData.ptsData; + packet->dts = inputData.dtsData; + + int ret = avcodec_send_packet(mCodecContext, packet); + if (ret < 0) { + LOG_ERROR(Lib_Vdec2, "Error sending packet to decoder: {}", ret); + av_packet_free(&packet); + return ORBIS_VIDEODEC2_ERROR_API_FAIL; + } + + AVFrame* frame = av_frame_alloc(); + if (frame == nullptr) { + LOG_ERROR(Lib_Vdec2, "Failed to allocate frame"); + av_packet_free(&packet); + return ORBIS_VIDEODEC2_ERROR_API_FAIL; + } + + while (true) { + ret = avcodec_receive_frame(mCodecContext, frame); + if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) { + break; + } else if (ret < 0) { + LOG_ERROR(Lib_Vdec2, "Error receiving frame from decoder: {}", ret); + av_packet_free(&packet); + av_frame_free(&frame); + return ORBIS_VIDEODEC2_ERROR_API_FAIL; + } + + if (frame->format != AV_PIX_FMT_NV12) { + AVFrame* nv12_frame = ConvertNV12Frame(*frame); + ASSERT(nv12_frame); + av_frame_free(&frame); + frame = nv12_frame; + } + + CopyNV12Data((u8*)frameBuffer.frameBuffer, *frame); + frameBuffer.isAccepted = true; + + outputInfo.codecType = 1; // FIXME: Hardcoded to AVC + outputInfo.frameWidth = frame->width; + outputInfo.frameHeight = frame->height; + outputInfo.framePitch = frame->linesize[0]; + outputInfo.frameBufferSize = frameBuffer.frameBufferSize; + outputInfo.frameBuffer = frameBuffer.frameBuffer; + + outputInfo.isValid = true; + outputInfo.isErrorFrame = false; + outputInfo.pictureCount = 1; // TODO: 2 pictures for interlaced video + + if (outputInfo.isValid) { + OrbisVideodec2AvcPictureInfo pictureInfo = {}; + + pictureInfo.thisSize = sizeof(OrbisVideodec2AvcPictureInfo); + 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; + + gPictureInfos.push_back(pictureInfo); + } + } + + av_packet_free(&packet); + av_frame_free(&frame); + return ORBIS_OK; +} + +s32 VdecDecoder::Flush(OrbisVideodec2FrameBuffer& frameBuffer, + OrbisVideodec2OutputInfo& outputInfo) { + frameBuffer.isAccepted = false; + outputInfo.thisSize = sizeof(OrbisVideodec2OutputInfo); + outputInfo.isValid = false; + outputInfo.isErrorFrame = true; + outputInfo.pictureCount = 0; + + AVFrame* frame = av_frame_alloc(); + if (!frame) { + LOG_ERROR(Lib_Vdec2, "Failed to allocate frame"); + return ORBIS_VIDEODEC2_ERROR_API_FAIL; + } + + while (true) { + int ret = avcodec_receive_frame(mCodecContext, frame); + if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) { + break; + } else if (ret < 0) { + LOG_ERROR(Lib_Vdec2, "Error receiving frame from decoder: {}", ret); + av_frame_free(&frame); + return ORBIS_VIDEODEC2_ERROR_API_FAIL; + } + + if (frame->format != AV_PIX_FMT_NV12) { + AVFrame* nv12_frame = ConvertNV12Frame(*frame); + ASSERT(nv12_frame); + av_frame_free(&frame); + frame = nv12_frame; + } + + CopyNV12Data((u8*)frameBuffer.frameBuffer, *frame); + frameBuffer.isAccepted = true; + + outputInfo.codecType = 1; // FIXME: Hardcoded to AVC + outputInfo.frameWidth = frame->width; + outputInfo.frameHeight = frame->height; + outputInfo.framePitch = frame->linesize[0]; + outputInfo.frameBufferSize = frameBuffer.frameBufferSize; + outputInfo.frameBuffer = frameBuffer.frameBuffer; + + outputInfo.isValid = true; + outputInfo.isErrorFrame = false; + outputInfo.pictureCount = 1; // TODO: 2 pictures for interlaced video + + // FIXME: Should we add picture info here too? + } + + av_frame_free(&frame); + return ORBIS_OK; +} + +s32 VdecDecoder::Reset() { + avcodec_flush_buffers(mCodecContext); + gPictureInfos.clear(); + return ORBIS_OK; +} + +AVFrame* VdecDecoder::ConvertNV12Frame(AVFrame& frame) { + AVFrame* nv12_frame = av_frame_alloc(); + nv12_frame->pts = frame.pts; + nv12_frame->pkt_dts = frame.pkt_dts < 0 ? 0 : frame.pkt_dts; + nv12_frame->format = AV_PIX_FMT_NV12; + nv12_frame->width = frame.width; + nv12_frame->height = frame.height; + nv12_frame->sample_aspect_ratio = frame.sample_aspect_ratio; + nv12_frame->crop_top = frame.crop_top; + nv12_frame->crop_bottom = frame.crop_bottom; + nv12_frame->crop_left = frame.crop_left; + nv12_frame->crop_right = frame.crop_right; + + av_frame_get_buffer(nv12_frame, 0); + + if (mSwsContext == nullptr) { + mSwsContext = sws_getContext(frame.width, frame.height, AVPixelFormat(frame.format), + nv12_frame->width, nv12_frame->height, AV_PIX_FMT_NV12, + SWS_FAST_BILINEAR, nullptr, nullptr, nullptr); + } + + const auto res = sws_scale(mSwsContext, frame.data, frame.linesize, 0, frame.height, + nv12_frame->data, nv12_frame->linesize); + if (res < 0) { + LOG_ERROR(Lib_Vdec2, "Could not convert to NV12: {}", av_err2str(res)); + return nullptr; + } + + return nv12_frame; +} + +} // namespace Libraries::Vdec2 \ No newline at end of file diff --git a/src/core/libraries/videodec/videodec2_impl.h b/src/core/libraries/videodec/videodec2_impl.h new file mode 100644 index 000000000..1bcece6e1 --- /dev/null +++ b/src/core/libraries/videodec/videodec2_impl.h @@ -0,0 +1,39 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include + +#include "videodec2.h" + +extern "C" { +#include +#include +#include +} + +namespace Libraries::Vdec2 { + +extern std::vector gPictureInfos; + +class VdecDecoder { +public: + VdecDecoder(const OrbisVideodec2DecoderConfigInfo& configInfo, + const OrbisVideodec2DecoderMemoryInfo& memoryInfo); + ~VdecDecoder(); + + s32 Decode(const OrbisVideodec2InputData& inputData, OrbisVideodec2FrameBuffer& frameBuffer, + OrbisVideodec2OutputInfo& outputInfo); + s32 Flush(OrbisVideodec2FrameBuffer& frameBuffer, OrbisVideodec2OutputInfo& outputInfo); + s32 Reset(); + +private: + AVFrame* ConvertNV12Frame(AVFrame& frame); + +private: + AVCodecContext* mCodecContext = nullptr; + SwsContext* mSwsContext = nullptr; +}; + +} // namespace Libraries::Vdec2 \ No newline at end of file diff --git a/src/core/libraries/videoout/video_out.cpp b/src/core/libraries/videoout/video_out.cpp index 631f77732..31b8a21ca 100644 --- a/src/core/libraries/videoout/video_out.cpp +++ b/src/core/libraries/videoout/video_out.cpp @@ -106,13 +106,13 @@ s32 PS4_SYSV_ABI sceVideoOutRegisterBuffers(s32 handle, s32 startIndex, void* co } s32 PS4_SYSV_ABI sceVideoOutSetFlipRate(s32 handle, s32 rate) { - LOG_INFO(Lib_VideoOut, "called"); + LOG_TRACE(Lib_VideoOut, "called"); driver->GetPort(handle)->flip_rate = rate; return ORBIS_OK; } s32 PS4_SYSV_ABI sceVideoOutIsFlipPending(s32 handle) { - LOG_INFO(Lib_VideoOut, "called"); + LOG_TRACE(Lib_VideoOut, "called"); auto* port = driver->GetPort(handle); std::unique_lock lock{port->port_mutex}; s32 pending = port->flip_status.flipPendingNum; diff --git a/src/core/platform.h b/src/core/platform.h index 2c38dfd39..03bd79e86 100644 --- a/src/core/platform.h +++ b/src/core/platform.h @@ -17,31 +17,35 @@ namespace Platform { enum class InterruptId : u32 { - Compute0RelMem = 0u, - Compute1RelMem = 1u, - Compute2RelMem = 2u, - Compute3RelMem = 3u, - Compute4RelMem = 4u, - Compute5RelMem = 5u, - Compute6RelMem = 6u, - GfxEop = 7u, - GfxFlip = 8u, - GpuIdle = 9u, + Compute0RelMem = 0x00, + Compute1RelMem = 0x01, + Compute2RelMem = 0x02, + Compute3RelMem = 0x03, + Compute4RelMem = 0x04, + Compute5RelMem = 0x05, + Compute6RelMem = 0x06, + GfxEop = 0x40, + GfxFlip = 0x08, + GpuIdle = 0x09, + + InterruptIdMax = 0x40, ///< Max possible value (GfxEop) }; using IrqHandler = std::function; struct IrqController { void RegisterOnce(InterruptId irq, IrqHandler handler) { - ASSERT_MSG(static_cast(irq) < irq_contexts.size(), "Invalid IRQ number"); - auto& ctx = irq_contexts[static_cast(irq)]; + ASSERT_MSG(static_cast(irq) <= static_cast(InterruptId::InterruptIdMax), + "Invalid IRQ number"); + auto& ctx = irq_contexts.try_emplace(irq).first->second; std::unique_lock lock{ctx.m_lock}; ctx.one_time_subscribers.emplace(handler); } void Register(InterruptId irq, IrqHandler handler, void* uid) { - ASSERT_MSG(static_cast(irq) < irq_contexts.size(), "Invalid IRQ number"); - auto& ctx = irq_contexts[static_cast(irq)]; + ASSERT_MSG(static_cast(irq) <= static_cast(InterruptId::InterruptIdMax), + "Invalid IRQ number"); + auto& ctx = irq_contexts.try_emplace(irq).first->second; std::unique_lock lock{ctx.m_lock}; ASSERT_MSG(ctx.persistent_handlers.find(uid) == ctx.persistent_handlers.cend(), @@ -50,15 +54,17 @@ struct IrqController { } void Unregister(InterruptId irq, void* uid) { - ASSERT_MSG(static_cast(irq) < irq_contexts.size(), "Invalid IRQ number"); - auto& ctx = irq_contexts[static_cast(irq)]; + ASSERT_MSG(static_cast(irq) <= static_cast(InterruptId::InterruptIdMax), + "Invalid IRQ number"); + auto& ctx = irq_contexts.try_emplace(irq).first->second; std::unique_lock lock{ctx.m_lock}; ctx.persistent_handlers.erase(uid); } void Signal(InterruptId irq) { - ASSERT_MSG(static_cast(irq) < irq_contexts.size(), "Unexpected IRQ signaled"); - auto& ctx = irq_contexts[static_cast(irq)]; + ASSERT_MSG(static_cast(irq) <= static_cast(InterruptId::InterruptIdMax), + "Unexpected IRQ signaled"); + auto& ctx = irq_contexts.try_emplace(irq).first->second; std::unique_lock lock{ctx.m_lock}; LOG_TRACE(Core, "IRQ signaled: {}", magic_enum::enum_name(irq)); @@ -81,7 +87,7 @@ private: std::queue one_time_subscribers{}; std::mutex m_lock{}; }; - std::array()> irq_contexts{}; + std::unordered_map irq_contexts{}; }; using IrqC = Common::Singleton; diff --git a/src/emulator.cpp b/src/emulator.cpp index 939a18076..ae3cb17dd 100644 --- a/src/emulator.cpp +++ b/src/emulator.cpp @@ -229,7 +229,7 @@ void Emulator::Run(const std::filesystem::path& file) { linker->LoadModule(eboot_path); // check if we have system modules to load - LoadSystemModules(eboot_path); + LoadSystemModules(eboot_path, game_info.game_serial); // Load all prx from game's sce_module folder std::filesystem::path sce_module_folder = file.parent_path() / "sce_module"; @@ -273,8 +273,8 @@ void Emulator::Run(const std::filesystem::path& file) { std::exit(0); } -void Emulator::LoadSystemModules(const std::filesystem::path& file) { - constexpr std::array ModulesToLoad{ +void Emulator::LoadSystemModules(const std::filesystem::path& file, std::string game_serial) { + constexpr std::array ModulesToLoad{ {{"libSceNgs2.sprx", &Libraries::Ngs2::RegisterlibSceNgs2}, {"libSceFiber.sprx", &Libraries::Fiber::RegisterlibSceFiber}, {"libSceUlt.sprx", nullptr}, @@ -284,7 +284,6 @@ void Emulator::LoadSystemModules(const std::filesystem::path& file) { {"libSceDiscMap.sprx", &Libraries::DiscMap::RegisterlibSceDiscMap}, {"libSceRtc.sprx", &Libraries::Rtc::RegisterlibSceRtc}, {"libSceJpegEnc.sprx", nullptr}, - {"libSceFont.sprx", nullptr}, {"libSceRazorCpu.sprx", nullptr}, {"libSceCesCs.sprx", nullptr}, {"libSceRudp.sprx", nullptr}}}; @@ -309,6 +308,14 @@ void Emulator::LoadSystemModules(const std::filesystem::path& file) { LOG_INFO(Loader, "No HLE available for {} module", module_name); } } + if (std::filesystem::exists(sys_module_path / game_serial)) { + for (const auto& entry : + std::filesystem::directory_iterator(sys_module_path / game_serial)) { + LOG_INFO(Loader, "Loading {} from game serial file {}", entry.path().string(), + game_serial); + linker->LoadModule(entry.path()); + } + } } #ifdef ENABLE_QT_GUI diff --git a/src/emulator.h b/src/emulator.h index dc2959af4..e973e9022 100644 --- a/src/emulator.h +++ b/src/emulator.h @@ -29,7 +29,7 @@ public: void UpdatePlayTime(const std::string& serial); private: - void LoadSystemModules(const std::filesystem::path& file); + void LoadSystemModules(const std::filesystem::path& file, std::string game_serial); Core::MemoryManager* memory; Input::GameController* controller; diff --git a/src/qt_gui/check_update.cpp b/src/qt_gui/check_update.cpp index ca6009ca3..a9aba0b84 100644 --- a/src/qt_gui/check_update.cpp +++ b/src/qt_gui/check_update.cpp @@ -145,7 +145,9 @@ void CheckUpdate::CheckForUpdates(const bool showMessage) { return; } - QString currentRev = QString::fromStdString(Common::g_scm_rev).left(7); + QString currentRev = (updateChannel == "Nightly") + ? QString::fromStdString(Common::g_scm_rev).left(7) + : "v." + QString::fromStdString(Common::VERSION); QString currentDate = Common::g_scm_date; QDateTime dateTime = QDateTime::fromString(latestDate, Qt::ISODate); diff --git a/src/qt_gui/translations/it.ts b/src/qt_gui/translations/it.ts index 071b8e978..b6eb13240 100644 --- a/src/qt_gui/translations/it.ts +++ b/src/qt_gui/translations/it.ts @@ -18,7 +18,7 @@ shadPS4 is an experimental open-source emulator for the PlayStation 4. - shadPS4 è un emulatore sperimentale open source per PlayStation 4. + shadPS4 è un emulatore sperimentale open-source per PlayStation 4. @@ -62,7 +62,7 @@ Select which directory you want to install to. - Select which directory you want to install to. + Seleziona in quale cartella vuoi effettuare l'installazione. @@ -143,22 +143,22 @@ Delete... - Delete... + Elimina... Delete Game - Delete Game + Elimina Gioco Delete Update - Delete Update + Elimina Aggiornamento Delete DLC - Delete DLC + Elimina DLC @@ -188,7 +188,7 @@ Game - Game + Gioco @@ -198,7 +198,7 @@ This game has no update to delete! - This game has no update to delete! + Questo gioco non ha alcun aggiornamento da eliminare! @@ -208,7 +208,7 @@ This game has no DLC to delete! - This game has no DLC to delete! + Questo gioco non ha alcun DLC da eliminare! @@ -218,12 +218,12 @@ Delete %1 - Delete %1 + Elimina %1 Are you sure you want to delete %1's %2 directory? - Are you sure you want to delete %1's %2 directory? + Sei sicuro di eliminale la cartella %2 di %1? @@ -336,7 +336,7 @@ Download Cheats/Patches - Scarica Trucchi / Patch + Scarica Trucchi/Patch @@ -480,7 +480,7 @@ Enable Separate Update Folder - Enable Separate Update Folder + Abilità Cartella Aggiornamenti Separata @@ -490,7 +490,7 @@ Is PS4 Pro - Modalità Ps4Pro + Modalità Ps4 Pro @@ -530,12 +530,12 @@ Hide Cursor - Nascondi cursore + Nascondi Cursore Hide Cursor Idle Timeout - Timeout inattivo per nascondere il cursore + Timeout inattività per nascondere il cursore @@ -555,7 +555,7 @@ Graphics Device - Adattatore grafico + Scheda Grafica @@ -575,7 +575,7 @@ Advanced - Avanzato + Avanzate @@ -614,8 +614,8 @@ - Enable Debug Dumping - Abilita Dump Debug + Enable + Abilita Debug Dumping @@ -630,7 +630,7 @@ Enable RenderDoc Debugging - Abilita Debugging RenderDoc + Abilita RenderDoc Debugging @@ -693,7 +693,7 @@ Download Complete - Scaricamento completato + Download completato @@ -748,7 +748,7 @@ PKG and Game versions match: - Le versioni di PKG e del gioco corrispondono: + Le versioni di PKG e del Gioco corrispondono: @@ -1144,7 +1144,7 @@ separateUpdatesCheckBox - Enable Separate Update Folder:\nEnables installing game updates into a separate folder for easy management. + Abilita Cartella Aggiornamenti Separata:\nAbilita l'installazione degli aggiornamenti in una cartella separata per una più facile gestione. @@ -1420,7 +1420,7 @@ Check for Updates at Startup - Verifica aggiornamenti all’avvio + Controlla aggiornamenti all’avvio @@ -1473,4 +1473,4 @@ Impossibile creare il file di script di aggiornamento - \ No newline at end of file + diff --git a/src/qt_gui/translations/sq.ts b/src/qt_gui/translations/sq.ts index 637a754f7..00fd5cb48 100644 --- a/src/qt_gui/translations/sq.ts +++ b/src/qt_gui/translations/sq.ts @@ -143,27 +143,27 @@ Delete... - Delete... + Fshi... Delete Game - Delete Game + Fshi lojën Delete Update - Delete Update + Fshi përditësimin Delete DLC - Delete DLC + Fshi DLC-në Shortcut creation - Krijim i shkurtores + Krijimi i shkurtores @@ -188,27 +188,27 @@ Game - Game + Loja requiresEnableSeparateUpdateFolder_MSG - This feature requires the 'Enable Separate Update Folder' config option to work. If you want to use this feature, please enable it. + Kjo veçori kërkon cilësimin 'Aktivizo dosjen e ndarë të përditësimit' për të punuar. Në qoftë se do ta përdorësh këtë veçori, të lutem aktivizoje. This game has no update to delete! - This game has no update to delete! + Kjo lojë nuk ka përditësim për të fshirë! Update - Update + Përditësim This game has no DLC to delete! - This game has no DLC to delete! + Kjo lojë nuk ka DLC për të fshirë! @@ -218,12 +218,12 @@ Delete %1 - Delete %1 + Fshi %1 Are you sure you want to delete %1's %2 directory? - Are you sure you want to delete %1's %2 directory? + Je i sigurt që do të fsish dosjen %2 të %1? @@ -256,7 +256,7 @@ Configure... - Formëso... + Konfiguro... @@ -480,7 +480,7 @@ Enable Separate Update Folder - Enable Separate Update Folder + Aktivizo dosjen e ndarë të përditësimit @@ -1026,7 +1026,7 @@ DownloadComplete_MSG - Arnat u shkarkuan me sukses! Të gjitha arnat e ofruara për të gjitha lojërat janë shkarkuar, nuk ka nevojë t'i shkarkosh ato individualisht për secilën lojë siç ndodh me Mashtrimet. Nëse patch-i nuk shfaqet, mund të mos ekzistojë për numrin e serisë dhe versionin specifik të lojës. + Arnat u shkarkuan me sukses! Të gjitha arnat e ofruara për të gjitha lojërat janë shkarkuar, nuk ka nevojë t'i shkarkosh ato individualisht për secilën lojë siç ndodh me Mashtrimet. Nëse arna nuk shfaqet, mund të mos ekzistojë për numrin e serikut dhe versionin specifik të lojës. @@ -1046,12 +1046,12 @@ The downloaded patch only works on version: %1 - Patch-i i shkarkuar funksionon vetëm në versionin: %1 + Arna e shkarkuar funksionon vetëm në versionin: %1 You may need to update your game. - Ju mund të duhet të përditësoni lojën tuaj. + Mund të duhet të përditësosh lojën tënde. @@ -1096,7 +1096,7 @@ Can't apply cheats before the game is started - Nuk mund të zbatohen mashtrime para se të fillojë loja. + Nuk mund të zbatohen mashtrime para fillimit të lojës. @@ -1144,7 +1144,7 @@ separateUpdatesCheckBox - Enable Separate Update Folder:\nEnables installing game updates into a separate folder for easy management. + Aktivizo dosjen e ndarë të përditësimit:\nAktivizon instalimin e përditësimeve të lojërave në dosje të veçanta për menaxhim më të lehtë. @@ -1473,4 +1473,4 @@ Krijimi i skedarit skript të përditësimit dështoi - \ No newline at end of file + diff --git a/src/qt_gui/translations/zh_CN.ts b/src/qt_gui/translations/zh_CN.ts index 89e45c7ba..bfcbbaa98 100644 --- a/src/qt_gui/translations/zh_CN.ts +++ b/src/qt_gui/translations/zh_CN.ts @@ -62,7 +62,7 @@ Select which directory you want to install to. - Select which directory you want to install to. + 选择你想要安装到的目录。 @@ -143,22 +143,22 @@ Delete... - Delete... + 删除... Delete Game - Delete Game + 删除游戏 Delete Update - Delete Update + 删除更新 Delete DLC - Delete DLC + 删除DLC @@ -188,27 +188,27 @@ Game - Game + 游戏 requiresEnableSeparateUpdateFolder_MSG - This feature requires the 'Enable Separate Update Folder' config option to work. If you want to use this feature, please enable it. + 这个功能需要‘启用单独的更新目录’配置选项才能正常运行,如果你想要使用这个功能,请启用它。 This game has no update to delete! - This game has no update to delete! + 这个游戏没有更新可以删除! Update - Update + 更新 This game has no DLC to delete! - This game has no DLC to delete! + 这个游戏没有DLC可以删除! @@ -218,12 +218,12 @@ Delete %1 - Delete %1 + 删除 %1 Are you sure you want to delete %1's %2 directory? - Are you sure you want to delete %1's %2 directory? + 你确定要删除 %1 的 %2 目录? @@ -480,7 +480,7 @@ Enable Separate Update Folder - Enable Separate Update Folder + 启用单独的更新目录 @@ -560,7 +560,7 @@ Width - 宽带 + 宽度 @@ -1144,7 +1144,7 @@ separateUpdatesCheckBox - Enable Separate Update Folder:\nEnables installing game updates into a separate folder for easy management. + 启用单独的更新目录:\n启用安装游戏更新到一个单独的目录中以更便于管理。 @@ -1473,4 +1473,4 @@ 无法创建更新脚本文件 - \ No newline at end of file + diff --git a/src/video_core/amdgpu/resource.h b/src/video_core/amdgpu/resource.h index 97df40a0d..83be0b0a4 100644 --- a/src/video_core/amdgpu/resource.h +++ b/src/video_core/amdgpu/resource.h @@ -261,6 +261,13 @@ struct Image { return last_level + 1; } + u32 NumSamples() const { + if (GetType() == ImageType::Color2DMsaa || GetType() == ImageType::Color2DMsaaArray) { + return 1u << last_level; + } + return 1; + } + ImageType GetType() const noexcept { return static_cast(type); } diff --git a/src/video_core/renderer_vulkan/liverpool_to_vk.cpp b/src/video_core/renderer_vulkan/liverpool_to_vk.cpp index 3e43c50e5..43dda4064 100644 --- a/src/video_core/renderer_vulkan/liverpool_to_vk.cpp +++ b/src/video_core/renderer_vulkan/liverpool_to_vk.cpp @@ -447,6 +447,10 @@ std::span SurfaceFormats() { vk::Format::eR8Unorm), CreateSurfaceFormatInfo(AmdGpu::DataFormat::Format8, AmdGpu::NumberFormat::Snorm, vk::Format::eR8Snorm), + CreateSurfaceFormatInfo(AmdGpu::DataFormat::Format8, AmdGpu::NumberFormat::Uscaled, + vk::Format::eR8Uscaled), + CreateSurfaceFormatInfo(AmdGpu::DataFormat::Format8, AmdGpu::NumberFormat::Sscaled, + vk::Format::eR8Sscaled), CreateSurfaceFormatInfo(AmdGpu::DataFormat::Format8, AmdGpu::NumberFormat::Uint, vk::Format::eR8Uint), CreateSurfaceFormatInfo(AmdGpu::DataFormat::Format8, AmdGpu::NumberFormat::Sint, @@ -458,6 +462,10 @@ std::span SurfaceFormats() { vk::Format::eR16Unorm), CreateSurfaceFormatInfo(AmdGpu::DataFormat::Format16, AmdGpu::NumberFormat::Snorm, vk::Format::eR16Snorm), + CreateSurfaceFormatInfo(AmdGpu::DataFormat::Format16, AmdGpu::NumberFormat::Uscaled, + vk::Format::eR16Uscaled), + CreateSurfaceFormatInfo(AmdGpu::DataFormat::Format16, AmdGpu::NumberFormat::Sscaled, + vk::Format::eR16Sscaled), CreateSurfaceFormatInfo(AmdGpu::DataFormat::Format16, AmdGpu::NumberFormat::Uint, vk::Format::eR16Uint), CreateSurfaceFormatInfo(AmdGpu::DataFormat::Format16, AmdGpu::NumberFormat::Sint, @@ -469,6 +477,10 @@ std::span SurfaceFormats() { vk::Format::eR8G8Unorm), CreateSurfaceFormatInfo(AmdGpu::DataFormat::Format8_8, AmdGpu::NumberFormat::Snorm, vk::Format::eR8G8Snorm), + CreateSurfaceFormatInfo(AmdGpu::DataFormat::Format8_8, AmdGpu::NumberFormat::Uscaled, + vk::Format::eR8G8Uscaled), + CreateSurfaceFormatInfo(AmdGpu::DataFormat::Format8_8, AmdGpu::NumberFormat::Sscaled, + vk::Format::eR8G8Sscaled), CreateSurfaceFormatInfo(AmdGpu::DataFormat::Format8_8, AmdGpu::NumberFormat::Uint, vk::Format::eR8G8Uint), CreateSurfaceFormatInfo(AmdGpu::DataFormat::Format8_8, AmdGpu::NumberFormat::Sint, @@ -509,6 +521,14 @@ std::span SurfaceFormats() { vk::Format::eA2B10G10R10UnormPack32), CreateSurfaceFormatInfo(AmdGpu::DataFormat::Format2_10_10_10, AmdGpu::NumberFormat::Snorm, vk::Format::eA2B10G10R10SnormPack32), + CreateSurfaceFormatInfo(AmdGpu::DataFormat::Format2_10_10_10, AmdGpu::NumberFormat::Uscaled, + vk::Format::eA2B10G10R10UscaledPack32), + CreateSurfaceFormatInfo(AmdGpu::DataFormat::Format2_10_10_10, AmdGpu::NumberFormat::Sscaled, + vk::Format::eA2B10G10R10SscaledPack32), + CreateSurfaceFormatInfo(AmdGpu::DataFormat::Format2_10_10_10, AmdGpu::NumberFormat::Uint, + vk::Format::eA2B10G10R10UintPack32), + CreateSurfaceFormatInfo(AmdGpu::DataFormat::Format2_10_10_10, AmdGpu::NumberFormat::Sint, + vk::Format::eA2B10G10R10SintPack32), // 8_8_8_8 CreateSurfaceFormatInfo(AmdGpu::DataFormat::Format8_8_8_8, AmdGpu::NumberFormat::Unorm, vk::Format::eR8G8B8A8Unorm), diff --git a/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp b/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp index 7122ca134..0c3570ab5 100644 --- a/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp +++ b/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp @@ -127,18 +127,33 @@ bool ComputePipeline::BindResources(VideoCore::BufferCache& buffer_cache, // we can skip the whole dispatch and update the tracked state instead. Also, it is not // intended to be consumed and in such rare cases (e.g. HTile introspection, CRAA) we // will need its full emulation anyways. For cases of metadata read a warning will be logged. - for (const auto& desc : info->texture_buffers) { + const auto IsMetaUpdate = [&](const auto& desc) { const VAddr address = desc.GetSharp(*info).base_address; if (desc.is_written) { if (texture_cache.TouchMeta(address, true)) { LOG_TRACE(Render_Vulkan, "Metadata update skipped"); - return false; + return true; } } else { if (texture_cache.IsMeta(address)) { LOG_WARNING(Render_Vulkan, "Unexpected metadata read by a CS shader (buffer)"); } } + return false; + }; + + for (const auto& desc : info->buffers) { + if (desc.is_gds_buffer) { + continue; + } + if (IsMetaUpdate(desc)) { + return false; + } + } + for (const auto& desc : info->texture_buffers) { + if (IsMetaUpdate(desc)) { + return false; + } } BindBuffers(buffer_cache, texture_cache, *info, binding, push_data, set_writes, diff --git a/src/video_core/renderer_vulkan/vk_instance.cpp b/src/video_core/renderer_vulkan/vk_instance.cpp index dda4e0d9f..0edc4228a 100644 --- a/src/video_core/renderer_vulkan/vk_instance.cpp +++ b/src/video_core/renderer_vulkan/vk_instance.cpp @@ -160,22 +160,23 @@ Instance::Instance(Frontend::WindowSDL& window, s32 physical_device_index, // Check and log format support details. for (const auto& format : LiverpoolToVK::SurfaceFormats()) { - if (!IsFormatSupported(GetSupportedFormat(format.vk_format, format.flags), format.flags)) { + if (!IsFormatSupported(format.vk_format, format.flags)) { LOG_WARNING(Render_Vulkan, "Surface format data_format={}, number_format={} is not fully supported " - "(vk_format={}, requested flags={})", + "(vk_format={}, missing features={})", static_cast(format.data_format), static_cast(format.number_format), vk::to_string(format.vk_format), - vk::to_string(format.flags)); + vk::to_string(format.flags & ~GetFormatFeatureFlags(format.vk_format))); } } for (const auto& format : LiverpoolToVK::DepthFormats()) { - if (!IsFormatSupported(GetSupportedFormat(format.vk_format, format.flags), format.flags)) { + if (!IsFormatSupported(format.vk_format, format.flags)) { LOG_WARNING(Render_Vulkan, "Depth format z_format={}, stencil_format={} is not fully supported " - "(vk_format={}, requested flags={})", + "(vk_format={}, missing features={})", static_cast(format.z_format), static_cast(format.stencil_format), - vk::to_string(format.vk_format), vk::to_string(format.flags)); + vk::to_string(format.vk_format), + vk::to_string(format.flags & ~GetFormatFeatureFlags(format.vk_format))); } } } @@ -546,18 +547,21 @@ void Instance::CollectToolingInfo() { } } -bool Instance::IsFormatSupported(const vk::Format format, - const vk::FormatFeatureFlags2 flags) const { - if (format == vk::Format::eUndefined) [[unlikely]] { - return true; - } - +vk::FormatFeatureFlags2 Instance::GetFormatFeatureFlags(vk::Format format) const { const auto it = format_properties.find(format); if (it == format_properties.end()) { UNIMPLEMENTED_MSG("Properties of format {} have not been queried.", vk::to_string(format)); } - return ((it->second.optimalTilingFeatures | it->second.bufferFeatures) & flags) == flags; + return it->second.optimalTilingFeatures | it->second.bufferFeatures; +} + +bool Instance::IsFormatSupported(const vk::Format format, + const vk::FormatFeatureFlags2 flags) const { + if (format == vk::Format::eUndefined) [[unlikely]] { + return true; + } + return (GetFormatFeatureFlags(format) & flags) == flags; } static vk::Format GetAlternativeFormat(const vk::Format format) { diff --git a/src/video_core/renderer_vulkan/vk_instance.h b/src/video_core/renderer_vulkan/vk_instance.h index 474b86e9a..51c2c57c5 100644 --- a/src/video_core/renderer_vulkan/vk_instance.h +++ b/src/video_core/renderer_vulkan/vk_instance.h @@ -275,6 +275,9 @@ private: void CollectDeviceParameters(); void CollectToolingInfo(); + /// Gets the supported feature flags for a format. + [[nodiscard]] vk::FormatFeatureFlags2 GetFormatFeatureFlags(vk::Format format) const; + /// Determines if a format is supported for a set of feature flags. [[nodiscard]] bool IsFormatSupported(vk::Format format, vk::FormatFeatureFlags2 flags) const; diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index a06d82eb3..4ec2a8db4 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -181,26 +181,6 @@ PipelineCache::PipelineCache(const Instance& instance_, Scheduler& scheduler_, PipelineCache::~PipelineCache() = default; const GraphicsPipeline* PipelineCache::GetGraphicsPipeline() { - const auto& regs = liverpool->regs; - // Tessellation is unsupported so skip the draw to avoid locking up the driver. - if (regs.primitive_type == AmdGpu::PrimitiveType::PatchPrimitive) { - return nullptr; - } - // There are several cases (e.g. FCE, FMask/HTile decompression) where we don't need to do an - // actual draw hence can skip pipeline creation. - if (regs.color_control.mode == Liverpool::ColorControl::OperationMode::EliminateFastClear) { - LOG_TRACE(Render_Vulkan, "FCE pass skipped"); - return nullptr; - } - if (regs.color_control.mode == Liverpool::ColorControl::OperationMode::FmaskDecompress) { - // TODO: check for a valid MRT1 to promote the draw to the resolve pass. - LOG_TRACE(Render_Vulkan, "FMask decompression pass skipped"); - return nullptr; - } - if (regs.primitive_type == AmdGpu::PrimitiveType::None) { - LOG_TRACE(Render_Vulkan, "Primitive type 'None' skipped"); - return nullptr; - } if (!RefreshGraphicsKey()) { return nullptr; } diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index 14a73261d..ae7634197 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -41,9 +41,43 @@ void Rasterizer::CpSync() { vk::DependencyFlagBits::eByRegion, ib_barrier, {}, {}); } +bool Rasterizer::FilterDraw() { + const auto& regs = liverpool->regs; + // Tessellation is unsupported so skip the draw to avoid locking up the driver. + if (regs.primitive_type == AmdGpu::PrimitiveType::PatchPrimitive) { + return false; + } + // There are several cases (e.g. FCE, FMask/HTile decompression) where we don't need to do an + // actual draw hence can skip pipeline creation. + if (regs.color_control.mode == Liverpool::ColorControl::OperationMode::EliminateFastClear) { + LOG_TRACE(Render_Vulkan, "FCE pass skipped"); + return false; + } + if (regs.color_control.mode == Liverpool::ColorControl::OperationMode::FmaskDecompress) { + // TODO: check for a valid MRT1 to promote the draw to the resolve pass. + LOG_TRACE(Render_Vulkan, "FMask decompression pass skipped"); + return false; + } + if (regs.color_control.mode == Liverpool::ColorControl::OperationMode::Resolve) { + LOG_TRACE(Render_Vulkan, "Resolve pass"); + Resolve(); + return false; + } + if (regs.primitive_type == AmdGpu::PrimitiveType::None) { + LOG_TRACE(Render_Vulkan, "Primitive type 'None' skipped"); + return false; + } + + return true; +} + void Rasterizer::Draw(bool is_indexed, u32 index_offset) { RENDERER_TRACE; + if (!FilterDraw()) { + return; + } + const auto cmdbuf = scheduler.CommandBuffer(); const auto& regs = liverpool->regs; const GraphicsPipeline* pipeline = pipeline_cache.GetGraphicsPipeline(); @@ -80,6 +114,10 @@ void Rasterizer::Draw(bool is_indexed, u32 index_offset) { void Rasterizer::DrawIndirect(bool is_indexed, VAddr address, u32 offset, u32 size) { RENDERER_TRACE; + if (!FilterDraw()) { + return; + } + const auto cmdbuf = scheduler.CommandBuffer(); const auto& regs = liverpool->regs; const GraphicsPipeline* pipeline = pipeline_cache.GetGraphicsPipeline(); @@ -258,6 +296,54 @@ void Rasterizer::BeginRendering(const GraphicsPipeline& pipeline) { scheduler.BeginRendering(state); } +void Rasterizer::Resolve() { + const auto cmdbuf = scheduler.CommandBuffer(); + + // Read from MRT0, average all samples, and write to MRT1, which is one-sample + const auto& mrt0_hint = liverpool->last_cb_extent[0]; + const auto& mrt1_hint = liverpool->last_cb_extent[1]; + VideoCore::ImageInfo mrt0_info{liverpool->regs.color_buffers[0], mrt0_hint}; + VideoCore::ImageInfo mrt1_info{liverpool->regs.color_buffers[1], mrt1_hint}; + auto& mrt0_image = texture_cache.GetImage(texture_cache.FindImage(mrt0_info)); + auto& mrt1_image = texture_cache.GetImage(texture_cache.FindImage(mrt1_info)); + + VideoCore::SubresourceRange mrt0_range; + mrt0_range.base.layer = liverpool->regs.color_buffers[0].view.slice_start; + mrt0_range.extent.layers = liverpool->regs.color_buffers[0].NumSlices() - mrt0_range.base.layer; + VideoCore::SubresourceRange mrt1_range; + mrt1_range.base.layer = liverpool->regs.color_buffers[1].view.slice_start; + mrt1_range.extent.layers = liverpool->regs.color_buffers[1].NumSlices() - mrt1_range.base.layer; + + vk::ImageResolve region = { + .srcSubresource = + { + .aspectMask = vk::ImageAspectFlagBits::eColor, + .mipLevel = 0, + .baseArrayLayer = mrt0_range.base.layer, + .layerCount = mrt0_range.extent.layers, + }, + .srcOffset = {0, 0, 0}, + .dstSubresource = + { + .aspectMask = vk::ImageAspectFlagBits::eColor, + .mipLevel = 0, + .baseArrayLayer = mrt1_range.base.layer, + .layerCount = mrt1_range.extent.layers, + }, + .dstOffset = {0, 0, 0}, + .extent = {mrt1_image.info.size.width, mrt1_image.info.size.height, 1}, + }; + + mrt0_image.Transit(vk::ImageLayout::eTransferSrcOptimal, vk::AccessFlagBits2::eTransferRead, + mrt0_range); + + mrt1_image.Transit(vk::ImageLayout::eTransferDstOptimal, vk::AccessFlagBits2::eTransferWrite, + mrt1_range); + + cmdbuf.resolveImage(mrt0_image.image, vk::ImageLayout::eTransferSrcOptimal, mrt1_image.image, + vk::ImageLayout::eTransferDstOptimal, region); +} + void Rasterizer::InlineData(VAddr address, const void* value, u32 num_bytes, bool is_gds) { buffer_cache.InlineData(address, value, num_bytes, is_gds); } diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.h b/src/video_core/renderer_vulkan/vk_rasterizer.h index d5cfbfd66..9035ed9dc 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.h +++ b/src/video_core/renderer_vulkan/vk_rasterizer.h @@ -54,11 +54,14 @@ public: private: void BeginRendering(const GraphicsPipeline& pipeline); + void Resolve(); void UpdateDynamicState(const GraphicsPipeline& pipeline); void UpdateViewportScissorState(); void UpdateDepthStencilState(); + bool FilterDraw(); + private: const Instance& instance; Scheduler& scheduler; diff --git a/src/video_core/texture_cache/image_info.cpp b/src/video_core/texture_cache/image_info.cpp index efb74ffae..055753dbc 100644 --- a/src/video_core/texture_cache/image_info.cpp +++ b/src/video_core/texture_cache/image_info.cpp @@ -219,6 +219,7 @@ ImageInfo::ImageInfo(const AmdGpu::Image& image, const Shader::ImageResource& de pitch = image.Pitch(); resources.levels = image.NumLevels(); resources.layers = image.NumLayers(desc.is_array); + num_samples = image.NumSamples(); num_bits = NumBits(image.GetDataFmt()); usage.texture = true; diff --git a/src/video_core/texture_cache/image_view.cpp b/src/video_core/texture_cache/image_view.cpp index db8113499..7dbf1230e 100644 --- a/src/video_core/texture_cache/image_view.cpp +++ b/src/video_core/texture_cache/image_view.cpp @@ -80,7 +80,12 @@ ImageViewInfo::ImageViewInfo(const AmdGpu::Image& image, const Shader::ImageReso } range.base.level = image.base_level; range.base.layer = image.base_array; - range.extent.levels = image.last_level - image.base_level + 1; + if (image.GetType() == AmdGpu::ImageType::Color2DMsaa || + image.GetType() == AmdGpu::ImageType::Color2DMsaaArray) { + range.extent.levels = 1; + } else { + range.extent.levels = image.last_level - image.base_level + 1; + } range.extent.layers = image.last_array - image.base_array + 1; type = ConvertImageViewType(image.GetType()); diff --git a/src/video_core/texture_cache/texture_cache.cpp b/src/video_core/texture_cache/texture_cache.cpp index 279e0d82b..27c288885 100644 --- a/src/video_core/texture_cache/texture_cache.cpp +++ b/src/video_core/texture_cache/texture_cache.cpp @@ -221,7 +221,8 @@ ImageId TextureCache::FindImage(const ImageInfo& info, FindFlags flags) { !IsVulkanFormatCompatible(info.pixel_format, cache_image.info.pixel_format)) { continue; } - ASSERT(cache_image.info.type == info.type || True(flags & FindFlags::RelaxFmt)); + ASSERT((cache_image.info.type == info.type || info.size == Extent3D{1, 1, 1} || + True(flags & FindFlags::RelaxFmt))); image_id = cache_id; } diff --git a/src/video_core/texture_cache/types.h b/src/video_core/texture_cache/types.h index bcef19355..e20d4dcd0 100644 --- a/src/video_core/texture_cache/types.h +++ b/src/video_core/texture_cache/types.h @@ -38,6 +38,10 @@ struct Extent3D { u32 depth; auto operator<=>(const Extent3D&) const = default; + + bool operator==(const Extent3D& other) const { + return width == other.width && height == other.height && depth == other.depth; + } }; struct SubresourceLayers {