diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 86ca81c38..ceb915f6a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -30,7 +30,7 @@ jobs: - name: Install run: | wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add - - sudo add-apt-repository 'deb http://apt.llvm.org/jammy/ llvm-toolchain-jammy-19 main' + sudo add-apt-repository 'deb http://apt.llvm.org/noble/ llvm-toolchain-noble-19 main' sudo apt update sudo apt install clang-format-19 - name: Build @@ -205,12 +205,12 @@ jobs: run: | mkdir upload mv ${{github.workspace}}/build/shadps4 upload - cp ${{github.workspace}}/build/externals/MoltenVK/libMoltenVK.dylib upload - tar cf shadps4-macos-sdl.tar.gz -C upload . + mv ${{github.workspace}}/build/MoltenVK_icd.json upload + mv ${{github.workspace}}/build/libMoltenVK.dylib upload - uses: actions/upload-artifact@v4 with: name: shadps4-macos-sdl-${{ needs.get-info.outputs.date }}-${{ needs.get-info.outputs.shorthash }} - path: shadps4-macos-sdl.tar.gz + path: upload/ macos-qt: runs-on: macos-15 @@ -281,8 +281,13 @@ jobs: with: submodules: recursive + - name: Add LLVM repository + run: | + wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add - + sudo add-apt-repository 'deb http://apt.llvm.org/noble/ llvm-toolchain-noble-19 main' + - 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 libudev-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-19 mold build-essential libasound2-dev libpulse-dev libopenal-dev libudev-dev - name: Cache CMake Configuration uses: actions/cache@v4 @@ -304,7 +309,7 @@ jobs: key: ${{ env.cache-name }}-${{ hashFiles('**/CMakeLists.txt', 'cmake/**') }} - name: Configure CMake - run: cmake --fresh -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE=ON -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache + run: cmake --fresh -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE=ON -DCMAKE_C_COMPILER=clang-19 -DCMAKE_CXX_COMPILER=clang++-19 -DCMAKE_EXE_LINKER_FLAGS="-fuse-ld=mold" -DCMAKE_SHARED_LINKER_FLAGS="-fuse-ld=mold" -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache - name: Build run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} --parallel $(nproc) @@ -337,8 +342,13 @@ jobs: with: submodules: recursive + - name: Add LLVM repository + run: | + wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add - + sudo add-apt-repository 'deb http://apt.llvm.org/noble/ llvm-toolchain-noble-19 main' + - 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 libudev-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-19 mold 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 @@ -360,7 +370,7 @@ jobs: key: ${{ env.cache-name }}-${{ hashFiles('**/CMakeLists.txt', 'cmake/**') }} - name: Configure CMake - run: cmake --fresh -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE=ON -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -DENABLE_QT_GUI=ON -DENABLE_UPDATER=ON -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache + run: cmake --fresh -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE=ON -DCMAKE_C_COMPILER=clang-19 -DCMAKE_CXX_COMPILER=clang++-19 -DCMAKE_EXE_LINKER_FLAGS="-fuse-ld=mold" -DCMAKE_SHARED_LINKER_FLAGS="-fuse-ld=mold" -DENABLE_QT_GUI=ON -DENABLE_UPDATER=ON -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache - name: Build run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} --parallel $(nproc) @@ -385,7 +395,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 gcc-14 build-essential libasound2-dev libpulse-dev libopenal-dev libudev-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 gcc-14 mold build-essential libasound2-dev libpulse-dev libopenal-dev libudev-dev - name: Cache CMake Configuration uses: actions/cache@v4 @@ -407,7 +417,7 @@ jobs: key: ${{ env.cache-name }}-${{ hashFiles('**/CMakeLists.txt', 'cmake/**') }} - name: Configure CMake - run: cmake --fresh -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE=ON -DCMAKE_C_COMPILER=gcc-14 -DCMAKE_CXX_COMPILER=g++-14 -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache + run: cmake --fresh -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE=ON -DCMAKE_C_COMPILER=gcc-14 -DCMAKE_CXX_COMPILER=g++-14 -DCMAKE_EXE_LINKER_FLAGS="-fuse-ld=mold" -DCMAKE_SHARED_LINKER_FLAGS="-fuse-ld=mold" -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache - name: Build run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} --parallel $(nproc) @@ -421,7 +431,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 gcc-14 build-essential qt6-base-dev qt6-tools-dev qt6-multimedia-dev libasound2-dev libpulse-dev libopenal-dev libudev-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 gcc-14 mold 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 @@ -443,7 +453,7 @@ jobs: key: ${{ env.cache-name }}-${{ hashFiles('**/CMakeLists.txt', 'cmake/**') }} - name: Configure CMake - run: cmake --fresh -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE=ON -DCMAKE_C_COMPILER=gcc-14 -DCMAKE_CXX_COMPILER=g++-14 -DENABLE_QT_GUI=ON -DENABLE_UPDATER=ON -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache + run: cmake --fresh -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE=ON -DCMAKE_C_COMPILER=gcc-14 -DCMAKE_CXX_COMPILER=g++-14 -DCMAKE_EXE_LINKER_FLAGS="-fuse-ld=mold" -DCMAKE_SHARED_LINKER_FLAGS="-fuse-ld=mold" -DENABLE_QT_GUI=ON -DENABLE_UPDATER=ON -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache - name: Build run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} --parallel $(nproc) diff --git a/.gitmodules b/.gitmodules index 9daefe305..065a4570f 100644 --- a/.gitmodules +++ b/.gitmodules @@ -97,7 +97,7 @@ shallow = true [submodule "externals/MoltenVK/SPIRV-Cross"] path = externals/MoltenVK/SPIRV-Cross - url = https://github.com/billhollings/SPIRV-Cross + url = https://github.com/KhronosGroup/SPIRV-Cross shallow = true [submodule "externals/MoltenVK/MoltenVK"] path = externals/MoltenVK/MoltenVK diff --git a/CMakeLists.txt b/CMakeLists.txt old mode 100755 new mode 100644 index beea8a1e0..9b10d0e5b --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -202,7 +202,7 @@ execute_process( # Set Version set(EMULATOR_VERSION_MAJOR "0") -set(EMULATOR_VERSION_MINOR "7") +set(EMULATOR_VERSION_MINOR "8") set(EMULATOR_VERSION_PATCH "1") set_source_files_properties(src/shadps4.rc PROPERTIES COMPILE_DEFINITIONS "EMULATOR_VERSION_MAJOR=${EMULATOR_VERSION_MAJOR};EMULATOR_VERSION_MINOR=${EMULATOR_VERSION_MINOR};EMULATOR_VERSION_PATCH=${EMULATOR_VERSION_PATCH}") @@ -371,11 +371,19 @@ set(NETWORK_LIBS src/core/libraries/network/http.cpp src/core/libraries/network/net_ctl_obj.cpp src/core/libraries/network/net_ctl_obj.h src/core/libraries/network/net_ctl_codes.h + src/core/libraries/network/net_util.cpp + src/core/libraries/network/net_util.h + src/core/libraries/network/net_error.h src/core/libraries/network/net.h src/core/libraries/network/ssl.cpp src/core/libraries/network/ssl.h src/core/libraries/network/ssl2.cpp src/core/libraries/network/ssl2.h + src/core/libraries/network/sys_net.cpp + src/core/libraries/network/sys_net.h + src/core/libraries/network/posix_sockets.cpp + src/core/libraries/network/p2p_sockets.cpp + src/core/libraries/network/sockets.h ) set(AVPLAYER_LIB src/core/libraries/avplayer/avplayer_common.cpp @@ -589,6 +597,8 @@ set(MISC_LIBS src/core/libraries/screenshot/screenshot.cpp src/core/libraries/move/move.h src/core/libraries/ulobjmgr/ulobjmgr.cpp src/core/libraries/ulobjmgr/ulobjmgr.h + src/core/libraries/signin_dialog/signindialog.cpp + src/core/libraries/signin_dialog/signindialog.h ) set(DEV_TOOLS src/core/devtools/layer.cpp @@ -840,6 +850,7 @@ set(SHADER_RECOMPILER src/shader_recompiler/exception.h src/shader_recompiler/ir/passes/identity_removal_pass.cpp src/shader_recompiler/ir/passes/ir_passes.h src/shader_recompiler/ir/passes/lower_buffer_format_to_raw.cpp + src/shader_recompiler/ir/passes/lower_fp64_to_fp32.cpp src/shader_recompiler/ir/passes/readlane_elimination_pass.cpp src/shader_recompiler/ir/passes/resource_tracking_pass.cpp src/shader_recompiler/ir/passes/ring_access_elimination.cpp @@ -1083,34 +1094,43 @@ if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux" AND ENABLE_USERFAULTFD) endif() if (APPLE) - if (ENABLE_QT_GUI) - # Include MoltenVK in the app bundle, along with an ICD file so it can be found by the system Vulkan loader if used for loading layers. - set(MVK_ICD ${CMAKE_CURRENT_SOURCE_DIR}/externals/MoltenVK/MoltenVK_icd.json) - target_sources(shadps4 PRIVATE ${MVK_ICD}) - set_source_files_properties(${MVK_ICD} PROPERTIES MACOSX_PACKAGE_LOCATION Resources/vulkan/icd.d) + # Include MoltenVK, along with an ICD file so it can be found by the system Vulkan loader if used for loading layers. + if (ENABLE_QT_GUI) + set(MVK_BUNDLE_PATH "Resources/vulkan/icd.d") + set_property(TARGET shadps4 APPEND PROPERTY BUILD_RPATH "@executable_path/../${MVK_BUNDLE_PATH}") + set(MVK_DST ${CMAKE_CURRENT_BINARY_DIR}/shadps4.app/Contents/${MVK_BUNDLE_PATH}) + else() + set_property(TARGET shadps4 APPEND PROPERTY BUILD_RPATH "@executable_path") + set(MVK_DST ${CMAKE_CURRENT_BINARY_DIR}) + endif() - set(MVK_DYLIB_SRC ${CMAKE_CURRENT_BINARY_DIR}/externals/MoltenVK/libMoltenVK.dylib) - set(MVK_DYLIB_DST ${CMAKE_CURRENT_BINARY_DIR}/shadps4.app/Contents/Frameworks/libMoltenVK.dylib) - add_custom_command( - OUTPUT ${MVK_DYLIB_DST} - DEPENDS ${MVK_DYLIB_SRC} - COMMAND cmake -E copy ${MVK_DYLIB_SRC} ${MVK_DYLIB_DST}) - add_custom_target(CopyMoltenVK DEPENDS ${MVK_DYLIB_DST}) - add_dependencies(CopyMoltenVK MoltenVK) - add_dependencies(shadps4 CopyMoltenVK) - set_property(TARGET shadps4 APPEND PROPERTY BUILD_RPATH "@executable_path/../Frameworks") - else() - # For non-bundled SDL build, just do a normal library link. - target_link_libraries(shadps4 PRIVATE MoltenVK) - endif() + set(MVK_DYLIB_SRC ${CMAKE_CURRENT_BINARY_DIR}/externals/MoltenVK/libMoltenVK.dylib) + set(MVK_DYLIB_DST ${MVK_DST}/libMoltenVK.dylib) + set(MVK_ICD_SRC ${CMAKE_CURRENT_SOURCE_DIR}/externals/MoltenVK/MoltenVK/MoltenVK/icd/MoltenVK_icd.json) + set(MVK_ICD_DST ${MVK_DST}/MoltenVK_icd.json) - if (ARCHITECTURE STREQUAL "x86_64") - # Reserve system-managed memory space. - target_link_options(shadps4 PRIVATE -Wl,-no_pie,-no_fixup_chains,-no_huge,-pagezero_size,0x4000,-segaddr,TCB_SPACE,0x4000,-segaddr,SYSTEM_MANAGED,0x400000,-segaddr,SYSTEM_RESERVED,0x7FFFFC000,-image_base,0x20000000000) - endif() + add_custom_command( + OUTPUT ${MVK_DST} + COMMAND ${CMAKE_COMMAND} -E make_directory ${MVK_DST}) + add_custom_command( + OUTPUT ${MVK_ICD_DST} + DEPENDS ${MVK_ICD_SRC} ${MVK_DST} + COMMAND ${CMAKE_COMMAND} -E copy ${MVK_ICD_SRC} ${MVK_ICD_DST}) + add_custom_command( + OUTPUT ${MVK_DYLIB_DST} + DEPENDS ${MVK_DYLIB_SRC} ${MVK_DST} + COMMAND ${CMAKE_COMMAND} -E copy ${MVK_DYLIB_SRC} ${MVK_DYLIB_DST}) + add_custom_target(CopyMoltenVK DEPENDS ${MVK_ICD_DST} ${MVK_DYLIB_DST}) + add_dependencies(CopyMoltenVK MoltenVK) + add_dependencies(shadps4 CopyMoltenVK) - # Replacement for std::chrono::time_zone - target_link_libraries(shadps4 PRIVATE date::date-tz) + if (ARCHITECTURE STREQUAL "x86_64") + # Reserve system-managed memory space. + target_link_options(shadps4 PRIVATE -Wl,-no_pie,-no_fixup_chains,-no_huge,-pagezero_size,0x4000,-segaddr,TCB_SPACE,0x4000,-segaddr,SYSTEM_MANAGED,0x400000,-segaddr,SYSTEM_RESERVED,0x7FFFFC000,-image_base,0x20000000000) + endif() + + # Replacement for std::chrono::time_zone + target_link_libraries(shadps4 PRIVATE date::date-tz) endif() if (NOT ENABLE_QT_GUI) diff --git a/README.md b/README.md index e24d57807..985bba586 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ SPDX-License-Identifier: GPL-2.0-or-later

- + diff --git a/dist/net.shadps4.shadPS4.metainfo.xml b/dist/net.shadps4.shadPS4.metainfo.xml index 99f9e070d..9f7b4f9c5 100644 --- a/dist/net.shadps4.shadPS4.metainfo.xml +++ b/dist/net.shadps4.shadPS4.metainfo.xml @@ -37,6 +37,9 @@ Game + + https://github.com/shadps4-emu/shadPS4/releases/tag/v.0.8.0 + https://github.com/shadps4-emu/shadPS4/releases/tag/v.0.7.0 diff --git a/externals/MoltenVK/MoltenVK b/externals/MoltenVK/MoltenVK index 067fc6c85..87a8e8b13 160000 --- a/externals/MoltenVK/MoltenVK +++ b/externals/MoltenVK/MoltenVK @@ -1 +1 @@ -Subproject commit 067fc6c85b02f37dfda58eeda49d8458e093ed60 +Subproject commit 87a8e8b13d4ad8835367fea1ebad1896d0460946 diff --git a/externals/MoltenVK/MoltenVK_icd.json b/externals/MoltenVK/MoltenVK_icd.json deleted file mode 100644 index 2c3319263..000000000 --- a/externals/MoltenVK/MoltenVK_icd.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "file_format_version": "1.0.0", - "ICD": { - "library_path": "../../../Frameworks/libMoltenVK.dylib", - "api_version": "1.2.0", - "is_portability_driver": true - } -} diff --git a/externals/MoltenVK/SPIRV-Cross b/externals/MoltenVK/SPIRV-Cross index 185833a61..791877574 160000 --- a/externals/MoltenVK/SPIRV-Cross +++ b/externals/MoltenVK/SPIRV-Cross @@ -1 +1 @@ -Subproject commit 185833a61cbe29ce3bfb5a499ffb3dfeaee3bbe7 +Subproject commit 7918775748c5e2f5c40d9918ce68825035b5a1e1 diff --git a/externals/sirit b/externals/sirit index 427a42c9e..09a1416ab 160000 --- a/externals/sirit +++ b/externals/sirit @@ -1 +1 @@ -Subproject commit 427a42c9ed99b38204d9107bc3dc14e92458acf1 +Subproject commit 09a1416ab1b59ddfebd2618412f118f2004f3b2c diff --git a/src/common/io_file.cpp b/src/common/io_file.cpp index 3efadc6ea..6fa9062a7 100644 --- a/src/common/io_file.cpp +++ b/src/common/io_file.cpp @@ -131,9 +131,7 @@ namespace { case SeekOrigin::End: return SEEK_END; default: - LOG_ERROR(Common_Filesystem, "Unsupported origin {}, defaulting to SEEK_SET", - static_cast(origin)); - return SEEK_SET; + UNREACHABLE_MSG("Impossible SeekOrigin {}", static_cast(origin)); } } diff --git a/src/common/io_file.h b/src/common/io_file.h index fb20a2bc5..45787a092 100644 --- a/src/common/io_file.h +++ b/src/common/io_file.h @@ -61,8 +61,6 @@ enum class SeekOrigin : u32 { SetOrigin, // Seeks from the start of the file. CurrentPosition, // Seeks from the current file pointer position. End, // Seeks from the end of the file. - SeekHole, // Seeks from the start of the next hole in the file. - SeekData, // Seeks from the start of the next non-hole region in the file. }; class IOFile final { diff --git a/src/common/logging/filter.cpp b/src/common/logging/filter.cpp index 867d62916..622af93cc 100644 --- a/src/common/logging/filter.cpp +++ b/src/common/logging/filter.cpp @@ -137,6 +137,7 @@ bool ParseFilterRule(Filter& instance, Iterator begin, Iterator end) { SUB(Lib, NpParty) \ SUB(Lib, Zlib) \ SUB(Lib, Hmd) \ + SUB(Lib, SigninDialog) \ CLS(Frontend) \ CLS(Render) \ SUB(Render, Vulkan) \ diff --git a/src/common/logging/types.h b/src/common/logging/types.h index e5714a81a..27a87e082 100644 --- a/src/common/logging/types.h +++ b/src/common/logging/types.h @@ -104,6 +104,7 @@ enum class Class : u8 { Lib_NpParty, ///< The LibSceNpParty implementation Lib_Zlib, ///< The LibSceZlib implementation. Lib_Hmd, ///< The LibSceHmd implementation. + Lib_SigninDialog, ///< The LibSigninDialog implementation. Frontend, ///< Emulator UI Render, ///< Video Core Render_Vulkan, ///< Vulkan backend diff --git a/src/common/memory_patcher.cpp b/src/common/memory_patcher.cpp index 2a8b26acb..cb51828cc 100644 --- a/src/common/memory_patcher.cpp +++ b/src/common/memory_patcher.cpp @@ -23,7 +23,7 @@ namespace MemoryPatcher { -uintptr_t g_eboot_address; +EXPORT uintptr_t g_eboot_address; uint64_t g_eboot_image_size; std::string g_game_serial; std::string patchFile; @@ -169,7 +169,8 @@ void OnGameLoaded() { if (type == "mask_jump32") patchMask = MemoryPatcher::PatchMask::Mask_Jump32; - if (type == "mask" || type == "mask_jump32" && !maskOffsetStr.empty()) { + if ((type == "mask" || type == "mask_jump32") && + !maskOffsetStr.empty()) { maskOffsetValue = std::stoi(maskOffsetStr, 0, 10); } diff --git a/src/common/memory_patcher.h b/src/common/memory_patcher.h index 29045a6a2..968903a85 100644 --- a/src/common/memory_patcher.h +++ b/src/common/memory_patcher.h @@ -6,9 +6,15 @@ #include #include +#if defined(WIN32) +#define EXPORT __declspec(dllexport) +#else +#define EXPORT __attribute__((visibility("default"))) +#endif + namespace MemoryPatcher { -extern uintptr_t g_eboot_address; +extern EXPORT uintptr_t g_eboot_address; extern uint64_t g_eboot_image_size; extern std::string g_game_serial; extern std::string patchFile; diff --git a/src/common/path_util.cpp b/src/common/path_util.cpp index 702d0fabc..1a6ff9ec8 100644 --- a/src/common/path_util.cpp +++ b/src/common/path_util.cpp @@ -60,7 +60,7 @@ static CFURLRef UntranslocateBundlePath(const CFURLRef bundle_path) { return nullptr; } -static std::filesystem::path GetBundleParentDirectory() { +static std::optional GetBundleParentDirectory() { if (CFBundleRef bundle_ref = CFBundleGetMainBundle()) { if (CFURLRef bundle_url_ref = CFBundleCopyBundleURL(bundle_ref)) { SCOPE_EXIT { @@ -83,14 +83,16 @@ static std::filesystem::path GetBundleParentDirectory() { } } } - return std::filesystem::current_path(); + return std::nullopt; } #endif static auto UserPaths = [] { -#ifdef __APPLE__ +#if defined(__APPLE__) && defined(ENABLE_QT_GUI) // Set the current path to the directory containing the app bundle. - std::filesystem::current_path(GetBundleParentDirectory()); + if (const auto bundle_dir = GetBundleParentDirectory()) { + std::filesystem::current_path(*bundle_dir); + } #endif // Try the portable user directory first. diff --git a/src/core/address_space.h b/src/core/address_space.h index 7ccc2cd1e..d7f3efc75 100644 --- a/src/core/address_space.h +++ b/src/core/address_space.h @@ -19,8 +19,6 @@ enum class MemoryPermission : u32 { }; DECLARE_ENUM_FLAG_OPERATORS(MemoryPermission) -constexpr VAddr CODE_BASE_OFFSET = 0x100000000ULL; - constexpr VAddr SYSTEM_MANAGED_MIN = 0x00000400000ULL; constexpr VAddr SYSTEM_MANAGED_MAX = 0x07FFFFBFFFULL; constexpr VAddr SYSTEM_RESERVED_MIN = 0x07FFFFC000ULL; diff --git a/src/core/cpu_patches.cpp b/src/core/cpu_patches.cpp index c8106b270..8937ef04b 100644 --- a/src/core/cpu_patches.cpp +++ b/src/core/cpu_patches.cpp @@ -464,9 +464,8 @@ static std::pair TryPatch(u8* code, PatchModule* module) { if (needs_trampoline && instruction.length < 5) { // Trampoline is needed but instruction is too short to patch. - // Return false and length to fall back to the illegal instruction handler, - // or to signal to AOT compilation that this instruction should be skipped and - // handled at runtime. + // Return false and length to signal to AOT compilation that this instruction + // should be skipped and handled at runtime. return std::make_pair(false, instruction.length); } @@ -512,136 +511,137 @@ static std::pair TryPatch(u8* code, PatchModule* module) { #if defined(ARCH_X86_64) +static bool Is4ByteExtrqOrInsertq(void* code_address) { + u8* bytes = (u8*)code_address; + if (bytes[0] == 0x66 && bytes[1] == 0x0F && bytes[2] == 0x79) { + return true; // extrq + } else if (bytes[0] == 0xF2 && bytes[1] == 0x0F && bytes[2] == 0x79) { + return true; // insertq + } else { + return false; + } +} + static bool TryExecuteIllegalInstruction(void* ctx, void* code_address) { - ZydisDecodedInstruction instruction; - ZydisDecodedOperand operands[ZYDIS_MAX_OPERAND_COUNT]; - const auto status = - Common::Decoder::Instance()->decodeInstruction(instruction, operands, code_address); + // We need to decode the instruction to find out what it is. Normally we'd use a fully fleshed + // out decoder like Zydis, however Zydis does a bunch of stuff that impact performance that we + // don't care about. We can get information about the instruction a lot faster by writing a mini + // decoder here, since we know it is definitely an extrq or an insertq. If for some reason we + // need to interpret more instructions in the future (I don't see why we would), we can revert + // to using Zydis. + ZydisMnemonic mnemonic; + u8* bytes = (u8*)code_address; + if (bytes[0] == 0x66) { + mnemonic = ZYDIS_MNEMONIC_EXTRQ; + } else if (bytes[0] == 0xF2) { + mnemonic = ZYDIS_MNEMONIC_INSERTQ; + } else { + ZydisDecodedInstruction instruction; + ZydisDecodedOperand operands[ZYDIS_MAX_OPERAND_COUNT]; + const auto status = + Common::Decoder::Instance()->decodeInstruction(instruction, operands, code_address); + LOG_ERROR(Core, "Unhandled illegal instruction at code address {}: {}", + fmt::ptr(code_address), + ZYAN_SUCCESS(status) ? ZydisMnemonicGetString(instruction.mnemonic) + : "Failed to decode"); + return false; + } - switch (instruction.mnemonic) { + ASSERT(bytes[1] == 0x0F && bytes[2] == 0x79); + + // Note: It's guaranteed that there's no REX prefix in these instructions checked by + // Is4ByteExtrqOrInsertq + u8 modrm = bytes[3]; + u8 rm = modrm & 0b111; + u8 reg = (modrm >> 3) & 0b111; + u8 mod = (modrm >> 6) & 0b11; + + ASSERT(mod == 0b11); // Any instruction we interpret here uses reg/reg addressing only + + int dstIndex = reg; + int srcIndex = rm; + + switch (mnemonic) { case ZYDIS_MNEMONIC_EXTRQ: { - bool immediateForm = operands[1].type == ZYDIS_OPERAND_TYPE_IMMEDIATE && - operands[2].type == ZYDIS_OPERAND_TYPE_IMMEDIATE; - if (immediateForm) { - LOG_CRITICAL(Core, "EXTRQ immediate form should have been patched at code address: {}", - fmt::ptr(code_address)); - return false; + const auto dst = Common::GetXmmPointer(ctx, dstIndex); + const auto src = Common::GetXmmPointer(ctx, srcIndex); + + u64 lowQWordSrc; + memcpy(&lowQWordSrc, src, sizeof(lowQWordSrc)); + + u64 lowQWordDst; + memcpy(&lowQWordDst, dst, sizeof(lowQWordDst)); + + u64 length = lowQWordSrc & 0x3F; + u64 mask; + if (length == 0) { + length = 64; // for the check below + mask = 0xFFFF'FFFF'FFFF'FFFF; } else { - ASSERT_MSG(operands[0].type == ZYDIS_OPERAND_TYPE_REGISTER && - operands[1].type == ZYDIS_OPERAND_TYPE_REGISTER && - operands[0].reg.value >= ZYDIS_REGISTER_XMM0 && - operands[0].reg.value <= ZYDIS_REGISTER_XMM15 && - operands[1].reg.value >= ZYDIS_REGISTER_XMM0 && - operands[1].reg.value <= ZYDIS_REGISTER_XMM15, - "Unexpected operand types for EXTRQ instruction"); - - const auto dstIndex = operands[0].reg.value - ZYDIS_REGISTER_XMM0; - const auto srcIndex = operands[1].reg.value - ZYDIS_REGISTER_XMM0; - - const auto dst = Common::GetXmmPointer(ctx, dstIndex); - const auto src = Common::GetXmmPointer(ctx, srcIndex); - - u64 lowQWordSrc; - memcpy(&lowQWordSrc, src, sizeof(lowQWordSrc)); - - u64 lowQWordDst; - memcpy(&lowQWordDst, dst, sizeof(lowQWordDst)); - - u64 length = lowQWordSrc & 0x3F; - u64 mask; - if (length == 0) { - length = 64; // for the check below - mask = 0xFFFF'FFFF'FFFF'FFFF; - } else { - mask = (1ULL << length) - 1; - } - - u64 index = (lowQWordSrc >> 8) & 0x3F; - if (length + index > 64) { - // Undefined behavior if length + index is bigger than 64 according to the spec, - // we'll warn and continue execution. - LOG_TRACE(Core, - "extrq at {} with length {} and index {} is bigger than 64, " - "undefined behavior", - fmt::ptr(code_address), length, index); - } - - lowQWordDst >>= index; - lowQWordDst &= mask; - - memcpy(dst, &lowQWordDst, sizeof(lowQWordDst)); - - Common::IncrementRip(ctx, instruction.length); - - return true; + mask = (1ULL << length) - 1; } - break; + + u64 index = (lowQWordSrc >> 8) & 0x3F; + if (length + index > 64) { + // Undefined behavior if length + index is bigger than 64 according to the spec, + // we'll warn and continue execution. + LOG_TRACE(Core, + "extrq at {} with length {} and index {} is bigger than 64, " + "undefined behavior", + fmt::ptr(code_address), length, index); + } + + lowQWordDst >>= index; + lowQWordDst &= mask; + + memcpy(dst, &lowQWordDst, sizeof(lowQWordDst)); + + Common::IncrementRip(ctx, 4); + + return true; } case ZYDIS_MNEMONIC_INSERTQ: { - bool immediateForm = operands[2].type == ZYDIS_OPERAND_TYPE_IMMEDIATE && - operands[3].type == ZYDIS_OPERAND_TYPE_IMMEDIATE; - if (immediateForm) { - LOG_CRITICAL(Core, - "INSERTQ immediate form should have been patched at code address: {}", - fmt::ptr(code_address)); - return false; + const auto dst = Common::GetXmmPointer(ctx, dstIndex); + const auto src = Common::GetXmmPointer(ctx, srcIndex); + + u64 lowQWordSrc, highQWordSrc; + memcpy(&lowQWordSrc, src, sizeof(lowQWordSrc)); + memcpy(&highQWordSrc, (u8*)src + 8, sizeof(highQWordSrc)); + + u64 lowQWordDst; + memcpy(&lowQWordDst, dst, sizeof(lowQWordDst)); + + u64 length = highQWordSrc & 0x3F; + u64 mask; + if (length == 0) { + length = 64; // for the check below + mask = 0xFFFF'FFFF'FFFF'FFFF; } else { - ASSERT_MSG(operands[2].type == ZYDIS_OPERAND_TYPE_UNUSED && - operands[3].type == ZYDIS_OPERAND_TYPE_UNUSED, - "operands 2 and 3 must be unused for register form."); - - ASSERT_MSG(operands[0].type == ZYDIS_OPERAND_TYPE_REGISTER && - operands[1].type == ZYDIS_OPERAND_TYPE_REGISTER, - "operands 0 and 1 must be registers."); - - const auto dstIndex = operands[0].reg.value - ZYDIS_REGISTER_XMM0; - const auto srcIndex = operands[1].reg.value - ZYDIS_REGISTER_XMM0; - - const auto dst = Common::GetXmmPointer(ctx, dstIndex); - const auto src = Common::GetXmmPointer(ctx, srcIndex); - - u64 lowQWordSrc, highQWordSrc; - memcpy(&lowQWordSrc, src, sizeof(lowQWordSrc)); - memcpy(&highQWordSrc, (u8*)src + 8, sizeof(highQWordSrc)); - - u64 lowQWordDst; - memcpy(&lowQWordDst, dst, sizeof(lowQWordDst)); - - u64 length = highQWordSrc & 0x3F; - u64 mask; - if (length == 0) { - length = 64; // for the check below - mask = 0xFFFF'FFFF'FFFF'FFFF; - } else { - mask = (1ULL << length) - 1; - } - - u64 index = (highQWordSrc >> 8) & 0x3F; - if (length + index > 64) { - // Undefined behavior if length + index is bigger than 64 according to the spec, - // we'll warn and continue execution. - LOG_TRACE(Core, - "insertq at {} with length {} and index {} is bigger than 64, " - "undefined behavior", - fmt::ptr(code_address), length, index); - } - - lowQWordSrc &= mask; - lowQWordDst &= ~(mask << index); - lowQWordDst |= lowQWordSrc << index; - - memcpy(dst, &lowQWordDst, sizeof(lowQWordDst)); - - Common::IncrementRip(ctx, instruction.length); - - return true; + mask = (1ULL << length) - 1; } - break; + + u64 index = (highQWordSrc >> 8) & 0x3F; + if (length + index > 64) { + // Undefined behavior if length + index is bigger than 64 according to the spec, + // we'll warn and continue execution. + LOG_TRACE(Core, + "insertq at {} with length {} and index {} is bigger than 64, " + "undefined behavior", + fmt::ptr(code_address), length, index); + } + + lowQWordSrc &= mask; + lowQWordDst &= ~(mask << index); + lowQWordDst |= lowQWordSrc << index; + + memcpy(dst, &lowQWordDst, sizeof(lowQWordDst)); + + Common::IncrementRip(ctx, 4); + + return true; } default: { - LOG_ERROR(Core, "Unhandled illegal instruction at code address {}: {}", - fmt::ptr(code_address), ZydisMnemonicGetString(instruction.mnemonic)); - return false; + UNREACHABLE(); } } @@ -695,9 +695,22 @@ static bool PatchesAccessViolationHandler(void* context, void* /* fault_address static bool PatchesIllegalInstructionHandler(void* context) { void* code_address = Common::GetRip(context); - if (!TryPatchJit(code_address)) { + if (Is4ByteExtrqOrInsertq(code_address)) { + // The instruction is not big enough for a relative jump, don't try to patch it and pass it + // to our illegal instruction interpreter directly return TryExecuteIllegalInstruction(context, code_address); + } else { + if (!TryPatchJit(code_address)) { + ZydisDecodedInstruction instruction; + ZydisDecodedOperand operands[ZYDIS_MAX_OPERAND_COUNT]; + const auto status = + Common::Decoder::Instance()->decodeInstruction(instruction, operands, code_address); + LOG_ERROR(Core, "Failed to patch address {:x} -- mnemonic: {}", (u64)code_address, + ZYAN_SUCCESS(status) ? ZydisMnemonicGetString(instruction.mnemonic) + : "Failed to decode"); + } } + return true; } diff --git a/src/core/devices/base_device.h b/src/core/devices/base_device.h index 36614b8f4..0cbbd3a00 100644 --- a/src/core/devices/base_device.h +++ b/src/core/devices/base_device.h @@ -40,6 +40,10 @@ public: return ORBIS_KERNEL_ERROR_EBADF; } + virtual size_t pwritev(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt, u64 offset) { + return ORBIS_KERNEL_ERROR_EBADF; + } + virtual s64 lseek(s64 offset, int whence) { return ORBIS_KERNEL_ERROR_EBADF; } diff --git a/src/core/devices/random_device.cpp b/src/core/devices/random_device.cpp index 50934e3b8..b2754fe58 100644 --- a/src/core/devices/random_device.cpp +++ b/src/core/devices/random_device.cpp @@ -2,6 +2,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include +#include #include "common/logging/log.h" #include "random_device.h" diff --git a/src/core/devices/srandom_device.cpp b/src/core/devices/srandom_device.cpp index ab78ddbe2..5e51b1c39 100644 --- a/src/core/devices/srandom_device.cpp +++ b/src/core/devices/srandom_device.cpp @@ -2,6 +2,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include +#include #include "common/logging/log.h" #include "srandom_device.h" diff --git a/src/core/devices/urandom_device.cpp b/src/core/devices/urandom_device.cpp index c001aab83..7318a6ff7 100644 --- a/src/core/devices/urandom_device.cpp +++ b/src/core/devices/urandom_device.cpp @@ -2,6 +2,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include +#include #include "common/logging/log.h" #include "urandom_device.h" diff --git a/src/core/devtools/layer.cpp b/src/core/devtools/layer.cpp index 94b39e801..a93178de5 100644 --- a/src/core/devtools/layer.cpp +++ b/src/core/devtools/layer.cpp @@ -1,11 +1,11 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include "SDL3/SDL_log.h" #include "layer.h" #include +#include "SDL3/SDL_log.h" #include "common/config.h" #include "common/singleton.h" #include "common/types.h" diff --git a/src/core/devtools/options.cpp b/src/core/devtools/options.cpp index 2def42071..f4b0ceb9a 100644 --- a/src/core/devtools/options.cpp +++ b/src/core/devtools/options.cpp @@ -1,9 +1,14 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include "options.h" + +#include #include -#include "options.h" +#include "video_core/renderer_vulkan/vk_presenter.h" + +extern std::unique_ptr presenter; namespace Core::Devtools { @@ -12,6 +17,7 @@ TOptions Options; void LoadOptionsConfig(const char* line) { char str[512]; int i; + float f; if (sscanf(line, "disassembler_cli_isa=%511[^\n]", str) == 1) { Options.disassembler_cli_isa = str; return; @@ -24,12 +30,26 @@ void LoadOptionsConfig(const char* line) { Options.frame_dump_render_on_collapse = i != 0; return; } + if (sscanf(line, "fsr_enabled=%d", &i) == 1) { + presenter->GetFsrSettingsRef().enable = i != 0; + return; + } + if (sscanf(line, "fsr_rcas_enabled=%d", &i) == 1) { + presenter->GetFsrSettingsRef().use_rcas = i != 0; + return; + } + if (sscanf(line, "fsr_rcas_attenuation=%f", &f) == 1) { + presenter->GetFsrSettingsRef().rcas_attenuation = f; + } } void SerializeOptionsConfig(ImGuiTextBuffer* buf) { buf->appendf("disassembler_cli_isa=%s\n", Options.disassembler_cli_isa.c_str()); buf->appendf("disassembler_cli_spv=%s\n", Options.disassembler_cli_spv.c_str()); buf->appendf("frame_dump_render_on_collapse=%d\n", Options.frame_dump_render_on_collapse); + buf->appendf("fsr_enabled=%d\n", presenter->GetFsrSettingsRef().enable); + buf->appendf("fsr_rcas_enabled=%d\n", presenter->GetFsrSettingsRef().use_rcas); + buf->appendf("fsr_rcas_attenuation=%f\n", presenter->GetFsrSettingsRef().rcas_attenuation); } } // namespace Core::Devtools diff --git a/src/core/devtools/widget/frame_dump.cpp b/src/core/devtools/widget/frame_dump.cpp index 646ccb6d6..2445bdcb5 100644 --- a/src/core/devtools/widget/frame_dump.cpp +++ b/src/core/devtools/widget/frame_dump.cpp @@ -2,6 +2,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include +#include #include #include #include diff --git a/src/core/libraries/gnmdriver/gnmdriver.cpp b/src/core/libraries/gnmdriver/gnmdriver.cpp index 25ac4921c..f2f40e0e3 100644 --- a/src/core/libraries/gnmdriver/gnmdriver.cpp +++ b/src/core/libraries/gnmdriver/gnmdriver.cpp @@ -505,9 +505,10 @@ s32 PS4_SYSV_ABI sceGnmDrawIndexIndirectCountMulti(u32* cmdbuf, u32 size, u32 da u32 flags) { LOG_TRACE(Lib_GnmDriver, "called"); - if ((!sceKernelIsNeoMode() || !UseNeoCompatSequences) && !cmdbuf && (size == 16) && - (shader_stage < ShaderStages::Max) && (vertex_sgpr_offset < 0x10u) && - (instance_sgpr_offset < 0x10u)) { + if ((!sceKernelIsNeoMode() || !UseNeoCompatSequences) && cmdbuf && (size == 16) && + (vertex_sgpr_offset < 0x10u) && (instance_sgpr_offset < 0x10u) && + (shader_stage == ShaderStages::Vs || shader_stage == ShaderStages::Es || + shader_stage == ShaderStages::Ls)) { cmdbuf = WriteHeader(cmdbuf, 2); cmdbuf = WriteBody(cmdbuf, 0u); @@ -535,10 +536,33 @@ s32 PS4_SYSV_ABI sceGnmDrawIndexIndirectCountMulti(u32* cmdbuf, u32 size, u32 da return -1; } -int PS4_SYSV_ABI sceGnmDrawIndexIndirectMulti() { - LOG_ERROR(Lib_GnmDriver, "(STUBBED) called"); - UNREACHABLE(); - return ORBIS_OK; +int PS4_SYSV_ABI sceGnmDrawIndexIndirectMulti(u32* cmdbuf, u32 size, u32 data_offset, u32 max_count, + u32 shader_stage, u32 vertex_sgpr_offset, + u32 instance_sgpr_offset, u32 flags) { + LOG_TRACE(Lib_GnmDriver, "called"); + + if (cmdbuf && (size == 11) && (vertex_sgpr_offset < 0x10u) && (instance_sgpr_offset < 0x10u) && + (shader_stage == ShaderStages::Vs || shader_stage == ShaderStages::Es || + shader_stage == ShaderStages::Ls)) { + + const auto predicate = flags & 1 ? PM4Predicate::PredEnable : PM4Predicate::PredDisable; + cmdbuf = WriteHeader( + cmdbuf, 6, PM4ShaderType::ShaderGraphics, predicate); + + const auto sgpr_offset = indirect_sgpr_offsets[shader_stage]; + + cmdbuf[0] = data_offset; + cmdbuf[1] = vertex_sgpr_offset == 0 ? 0 : (vertex_sgpr_offset & 0xffffu) + sgpr_offset; + cmdbuf[2] = instance_sgpr_offset == 0 ? 0 : (instance_sgpr_offset & 0xffffu) + sgpr_offset; + cmdbuf[3] = max_count; + cmdbuf[4] = sizeof(DrawIndexedIndirectArgs); + cmdbuf[5] = sceKernelIsNeoMode() ? flags & 0xe0000000u : 0; + + cmdbuf += 6; + WriteTrailingNop<3>(cmdbuf); + return ORBIS_OK; + } + return -1; } int PS4_SYSV_ABI sceGnmDrawIndexMultiInstanced() { diff --git a/src/core/libraries/gnmdriver/gnmdriver.h b/src/core/libraries/gnmdriver/gnmdriver.h index 94d06c85f..a3d4968d3 100644 --- a/src/core/libraries/gnmdriver/gnmdriver.h +++ b/src/core/libraries/gnmdriver/gnmdriver.h @@ -51,7 +51,9 @@ s32 PS4_SYSV_ABI sceGnmDrawIndexIndirectCountMulti(u32* cmdbuf, u32 size, u32 da u32 max_count, u64 count_addr, u32 shader_stage, u32 vertex_sgpr_offset, u32 instance_sgpr_offset, u32 flags); -int PS4_SYSV_ABI sceGnmDrawIndexIndirectMulti(); +int PS4_SYSV_ABI sceGnmDrawIndexIndirectMulti(u32* cmdbuf, u32 size, u32 data_offset, u32 max_count, + u32 shader_stage, u32 vertex_sgpr_offset, + u32 instance_sgpr_offset, u32 flags); int PS4_SYSV_ABI sceGnmDrawIndexMultiInstanced(); s32 PS4_SYSV_ABI sceGnmDrawIndexOffset(u32* cmdbuf, u32 size, u32 index_offset, u32 index_count, u32 flags); diff --git a/src/core/libraries/ime/ime_dialog.cpp b/src/core/libraries/ime/ime_dialog.cpp index 9151aa64e..bee185787 100644 --- a/src/core/libraries/ime/ime_dialog.cpp +++ b/src/core/libraries/ime/ime_dialog.cpp @@ -83,9 +83,35 @@ int PS4_SYSV_ABI sceImeDialogGetPanelPositionAndForm() { return ORBIS_OK; } -int PS4_SYSV_ABI sceImeDialogGetPanelSize() { - LOG_ERROR(Lib_ImeDialog, "(STUBBED) called"); - return ORBIS_OK; +Error PS4_SYSV_ABI sceImeDialogGetPanelSize(const OrbisImeDialogParam* param, u32* width, + u32* height) { + LOG_INFO(Lib_ImeDialog, "called"); + + if (!width || !height) { + return Error::INVALID_ADDRESS; + } + switch (param->type) { + case OrbisImeType::Default: + case OrbisImeType::BasicLatin: + case OrbisImeType::Url: + case OrbisImeType::Mail: + *width = 500; // original: 793 + if (True(param->option & OrbisImeDialogOption::Multiline)) { + *height = 300; // original: 576 + } else { + *height = 150; // original: 476 + } + break; + case OrbisImeType::Number: + *width = 370; + *height = 470; + break; + default: + LOG_ERROR(Lib_ImeDialog, "Unknown OrbisImeType: {}", (u32)param->type); + return Error::INVALID_PARAM; + } + + return Error::OK; } int PS4_SYSV_ABI sceImeDialogGetPanelSizeExtended() { diff --git a/src/core/libraries/ime/ime_dialog.h b/src/core/libraries/ime/ime_dialog.h index c8b228498..526e5f022 100644 --- a/src/core/libraries/ime/ime_dialog.h +++ b/src/core/libraries/ime/ime_dialog.h @@ -13,7 +13,7 @@ class SymbolsResolver; namespace Libraries::ImeDialog { -constexpr u32 ORBIS_IME_DIALOG_MAX_TEXT_LENGTH = 0x78; +constexpr u32 ORBIS_IME_DIALOG_MAX_TEXT_LENGTH = 2048; enum class Error : u32 { OK = 0x0, @@ -155,7 +155,8 @@ Error PS4_SYSV_ABI sceImeDialogForceClose(); Error PS4_SYSV_ABI sceImeDialogForTestFunction(); int PS4_SYSV_ABI sceImeDialogGetCurrentStarState(); int PS4_SYSV_ABI sceImeDialogGetPanelPositionAndForm(); -int PS4_SYSV_ABI sceImeDialogGetPanelSize(); +Error PS4_SYSV_ABI sceImeDialogGetPanelSize(const OrbisImeDialogParam* param, u32* width, + u32* height); int PS4_SYSV_ABI sceImeDialogGetPanelSizeExtended(); Error PS4_SYSV_ABI sceImeDialogGetResult(OrbisImeDialogResult* result); OrbisImeDialogStatus PS4_SYSV_ABI sceImeDialogGetStatus(); diff --git a/src/core/libraries/kernel/equeue.h b/src/core/libraries/kernel/equeue.h index 11c09bb37..2bd7ef510 100644 --- a/src/core/libraries/kernel/equeue.h +++ b/src/core/libraries/kernel/equeue.h @@ -61,6 +61,18 @@ struct SceKernelEvent { void* udata = nullptr; /* opaque user data identifier */ }; +struct OrbisVideoOutEventHint { + u64 event_id : 8; + u64 video_id : 8; + u64 flip_arg : 48; +}; + +struct OrbisVideoOutEventData { + u64 time : 12; + u64 count : 4; + u64 flip_arg : 48; +}; + struct EqueueEvent { SceKernelEvent event; void* data = nullptr; @@ -84,19 +96,18 @@ struct EqueueEvent { void TriggerDisplay(void* data) { is_triggered = true; - auto hint = reinterpret_cast(data); - if (hint != 0) { - auto hint_h = static_cast(hint >> 8) & 0xFFFFFF; - auto ident_h = static_cast(event.ident >> 40); - if ((static_cast(hint) & 0xFF) == event.ident && event.ident != 0xFE && - ((hint_h ^ ident_h) & 0xFF) == 0) { + if (data != nullptr) { + auto event_data = static_cast(event.data); + auto event_hint_raw = reinterpret_cast(data); + auto event_hint = static_cast(event_hint_raw); + if (event_hint.event_id == event.ident && event.ident != 0xfe) { auto time = Common::FencedRDTSC(); - auto mask = 0xF000; - if ((static_cast(event.data) & 0xF000) != 0xF000) { - mask = (static_cast(event.data) + 0x1000) & 0xF000; + auto counter = event_data.count; + if (counter != 0xf) { + counter++; } - event.data = (mask | static_cast(static_cast(time) & 0xFFF) | - (hint & 0xFFFFFFFFFFFF0000)); + event.data = + (time & 0xfff) | (counter << 0xc) | (event_hint_raw & 0xffffffffffff0000); } } } diff --git a/src/core/libraries/kernel/file_system.cpp b/src/core/libraries/kernel/file_system.cpp index bc34dff98..ad372325c 100644 --- a/src/core/libraries/kernel/file_system.cpp +++ b/src/core/libraries/kernel/file_system.cpp @@ -67,10 +67,16 @@ s32 PS4_SYSV_ABI open(const char* raw_path, s32 flags, u16 mode) { bool write = (flags & 0x3) == ORBIS_KERNEL_O_WRONLY; bool rdwr = (flags & 0x3) == ORBIS_KERNEL_O_RDWR; + if (!read && !write && !rdwr) { + // Start by checking for invalid flags. + *__Error() = POSIX_EINVAL; + return -1; + } + bool nonblock = (flags & ORBIS_KERNEL_O_NONBLOCK) != 0; bool append = (flags & ORBIS_KERNEL_O_APPEND) != 0; - bool fsync = (flags & ORBIS_KERNEL_O_FSYNC) != 0; - bool sync = (flags & ORBIS_KERNEL_O_SYNC) != 0; + // Flags fsync and sync behave the same + bool sync = (flags & ORBIS_KERNEL_O_SYNC) != 0 || (flags & ORBIS_KERNEL_O_FSYNC) != 0; bool create = (flags & ORBIS_KERNEL_O_CREAT) != 0; bool truncate = (flags & ORBIS_KERNEL_O_TRUNC) != 0; bool excl = (flags & ORBIS_KERNEL_O_EXCL) != 0; @@ -78,6 +84,10 @@ s32 PS4_SYSV_ABI open(const char* raw_path, s32 flags, u16 mode) { bool direct = (flags & ORBIS_KERNEL_O_DIRECT) != 0; bool directory = (flags & ORBIS_KERNEL_O_DIRECTORY) != 0; + if (sync || direct || dsync || nonblock) { + LOG_WARNING(Kernel_Fs, "flags {:#x} not fully handled", flags); + } + std::string_view path{raw_path}; u32 handle = h->CreateHandle(); auto* file = h->GetFile(handle); @@ -94,84 +104,127 @@ s32 PS4_SYSV_ABI open(const char* raw_path, s32 flags, u16 mode) { } } - if (directory) { - file->type = Core::FileSys::FileType::Directory; - file->m_guest_name = path; - file->m_host_name = mnt->GetHostPath(file->m_guest_name); - if (!std::filesystem::is_directory(file->m_host_name)) { // directory doesn't exist + bool read_only = false; + file->m_guest_name = path; + file->m_host_name = mnt->GetHostPath(file->m_guest_name, &read_only); + bool exists = std::filesystem::exists(file->m_host_name); + s32 e = 0; + + if (create) { + if (excl && exists) { + // Error if file exists h->DeleteHandle(handle); - *__Error() = POSIX_ENOENT; + *__Error() = POSIX_EEXIST; + return -1; + } + + if (read_only) { + // Can't create files in a read only directory + h->DeleteHandle(handle); + *__Error() = POSIX_EROFS; + return -1; + } + // Create a file if it doesn't exist + Common::FS::IOFile out(file->m_host_name, Common::FS::FileAccessMode::Write); + } else if (!exists) { + // If we're not creating a file, and it doesn't exist, return ENOENT + h->DeleteHandle(handle); + *__Error() = POSIX_ENOENT; + return -1; + } + + if (std::filesystem::is_directory(file->m_host_name) || directory) { + // Directories can be opened even if the directory flag isn't set. + // In these cases, error behavior is identical to the directory code path. + directory = true; + } + + if (directory) { + if (!std::filesystem::is_directory(file->m_host_name)) { + // If the opened file is not a directory, return ENOTDIR. + // This will trigger when create & directory is specified, this is expected. + h->DeleteHandle(handle); + *__Error() = POSIX_ENOTDIR; + return -1; + } + + file->type = Core::FileSys::FileType::Directory; + + // Populate directory contents + mnt->IterateDirectory(file->m_guest_name, + [&file](const auto& ent_path, const auto ent_is_file) { + auto& dir_entry = file->dirents.emplace_back(); + dir_entry.name = ent_path.filename().string(); + dir_entry.isFile = ent_is_file; + }); + file->dirents_index = 0; + + if (read) { + e = file->f.Open(file->m_host_name, Common::FS::FileAccessMode::Read); + } else if (write || rdwr) { + // Cannot open directories with any type of write access + h->DeleteHandle(handle); + *__Error() = POSIX_EISDIR; + return -1; + } + + if (e == EACCES) { + // Hack to bypass some platform limitations, ignore the error and continue as normal. + LOG_WARNING(Kernel_Fs, "Opening directories is not fully supported on this platform"); + e = 0; + } + + if (truncate) { + // Cannot open directories with truncate + h->DeleteHandle(handle); + *__Error() = POSIX_EISDIR; return -1; - } else { - if (create) { - return handle; // dir already exists - } else { - mnt->IterateDirectory(file->m_guest_name, - [&file](const auto& ent_path, const auto ent_is_file) { - auto& dir_entry = file->dirents.emplace_back(); - dir_entry.name = ent_path.filename().string(); - dir_entry.isFile = ent_is_file; - }); - file->dirents_index = 0; - } } } else { - file->m_guest_name = path; - file->m_host_name = mnt->GetHostPath(file->m_guest_name); - bool exists = std::filesystem::exists(file->m_host_name); - int e = 0; + file->type = Core::FileSys::FileType::Regular; - if (create) { - if (excl && exists) { - // Error if file exists - h->DeleteHandle(handle); - *__Error() = POSIX_EEXIST; - return -1; - } - // Create file if it doesn't exist - Common::FS::IOFile out(file->m_host_name, Common::FS::FileAccessMode::Write); - } else if (!exists) { - // File to open doesn't exist, return ENOENT + if (truncate && read_only) { + // Can't open files with truncate flag in a read only directory h->DeleteHandle(handle); - *__Error() = POSIX_ENOENT; + *__Error() = POSIX_EROFS; return -1; + } else if (truncate) { + // Open the file as read-write so we can truncate regardless of flags. + // Since open starts by closing the file, this won't interfere with later open calls. + e = file->f.Open(file->m_host_name, Common::FS::FileAccessMode::ReadWrite); + if (e == 0) { + // If the file was opened successfully, reduce size to 0 + file->f.SetSize(0); + } } if (read) { // Read only e = file->f.Open(file->m_host_name, Common::FS::FileAccessMode::Read); + } else if (read_only) { + // Can't open files with write/read-write access in a read only directory + h->DeleteHandle(handle); + *__Error() = POSIX_EROFS; + return -1; + } else if (append) { + // Append can be specified with rdwr or write, but we treat it as a separate mode. + e = file->f.Open(file->m_host_name, Common::FS::FileAccessMode::Append); } else if (write) { // Write only - if (append) { - e = file->f.Open(file->m_host_name, Common::FS::FileAccessMode::Append); - } else { - e = file->f.Open(file->m_host_name, Common::FS::FileAccessMode::Write); - } + e = file->f.Open(file->m_host_name, Common::FS::FileAccessMode::Write); } else if (rdwr) { // Read and write - if (append) { - e = file->f.Open(file->m_host_name, Common::FS::FileAccessMode::Append); - } else { - e = file->f.Open(file->m_host_name, Common::FS::FileAccessMode::ReadWrite); - } - } else { - // Invalid flags - *__Error() = POSIX_EINVAL; - return -1; - } - - if (truncate && e == 0) { - // If the file was opened successfully and truncate was enabled, reduce size to 0 - file->f.SetSize(0); - } - - if (e != 0) { - // Open failed in platform-specific code, errno needs to be converted. - h->DeleteHandle(handle); - SetPosixErrno(e); - return -1; + e = file->f.Open(file->m_host_name, Common::FS::FileAccessMode::ReadWrite); } } + + if (e != 0) { + // Open failed in platform-specific code, errno needs to be converted. + h->DeleteHandle(handle); + SetPosixErrno(e); + return -1; + } + file->is_opened = true; return handle; } @@ -365,10 +418,10 @@ s64 PS4_SYSV_ABI posix_lseek(s32 fd, s64 offset, s32 whence) { origin = Common::FS::SeekOrigin::CurrentPosition; } else if (whence == 2) { origin = Common::FS::SeekOrigin::End; - } else if (whence == 3) { - origin = Common::FS::SeekOrigin::SeekHole; - } else if (whence == 4) { - origin = Common::FS::SeekOrigin::SeekData; + } else if (whence == 3 || whence == 4) { + // whence parameter belongs to an unsupported POSIX extension + *__Error() = POSIX_ENOTTY; + return -1; } else { // whence parameter is invalid *__Error() = POSIX_EINVAL; @@ -486,13 +539,13 @@ s32 PS4_SYSV_ABI posix_rmdir(const char* path) { const std::filesystem::path dir_name = mnt->GetHostPath(path, &ro); - if (dir_name.empty() || !std::filesystem::is_directory(dir_name)) { - *__Error() = POSIX_ENOTDIR; + if (ro) { + *__Error() = POSIX_EROFS; return -1; } - if (ro) { - *__Error() = POSIX_EROFS; + if (dir_name.empty() || !std::filesystem::is_directory(dir_name)) { + *__Error() = POSIX_ENOTDIR; return -1; } @@ -523,8 +576,7 @@ s32 PS4_SYSV_ABI sceKernelRmdir(const char* path) { s32 PS4_SYSV_ABI posix_stat(const char* path, OrbisKernelStat* sb) { LOG_INFO(Kernel_Fs, "(PARTIAL) path = {}", path); auto* mnt = Common::Singleton::Instance(); - bool ro = false; - const auto path_name = mnt->GetHostPath(path, &ro); + const auto path_name = mnt->GetHostPath(path); std::memset(sb, 0, sizeof(OrbisKernelStat)); const bool is_dir = std::filesystem::is_directory(path_name); const bool is_file = std::filesystem::is_regular_file(path_name); @@ -545,9 +597,6 @@ s32 PS4_SYSV_ABI posix_stat(const char* path, OrbisKernelStat* sb) { sb->st_blocks = (sb->st_size + 511) / 512; // TODO incomplete } - if (ro) { - sb->st_mode &= ~0000555u; - } return ORBIS_OK; } @@ -877,7 +926,7 @@ s32 PS4_SYSV_ABI sceKernelGetdirentries(s32 fd, char* buf, s32 nbytes, s64* base return result; } -s64 PS4_SYSV_ABI posix_pwrite(s32 fd, void* buf, size_t nbytes, s64 offset) { +s64 PS4_SYSV_ABI posix_pwritev(s32 fd, const SceKernelIovec* iov, s32 iovcnt, s64 offset) { if (offset < 0) { *__Error() = POSIX_EINVAL; return -1; @@ -893,7 +942,7 @@ s64 PS4_SYSV_ABI posix_pwrite(s32 fd, void* buf, size_t nbytes, s64 offset) { std::scoped_lock lk{file->m_mutex}; if (file->type == Core::FileSys::FileType::Device) { - s64 result = file->device->pwrite(buf, nbytes, offset); + s64 result = file->device->pwritev(iov, iovcnt, offset); if (result < 0) { ErrSceToPosix(result); return -1; @@ -908,7 +957,16 @@ s64 PS4_SYSV_ABI posix_pwrite(s32 fd, void* buf, size_t nbytes, s64 offset) { *__Error() = POSIX_EIO; return -1; } - return file->f.WriteRaw(buf, nbytes); + size_t total_written = 0; + for (int i = 0; i < iovcnt; i++) { + total_written += file->f.WriteRaw(iov[i].iov_base, iov[i].iov_len); + } + return total_written; +} + +s64 PS4_SYSV_ABI posix_pwrite(s32 fd, void* buf, size_t nbytes, s64 offset) { + SceKernelIovec iovec{buf, nbytes}; + return posix_pwritev(fd, &iovec, 1, offset); } s64 PS4_SYSV_ABI sceKernelPwrite(s32 fd, void* buf, size_t nbytes, s64 offset) { @@ -920,6 +978,15 @@ s64 PS4_SYSV_ABI sceKernelPwrite(s32 fd, void* buf, size_t nbytes, s64 offset) { return result; } +s64 PS4_SYSV_ABI sceKernelPwritev(s32 fd, const SceKernelIovec* iov, s32 iovcnt, s64 offset) { + s64 result = posix_pwritev(fd, iov, iovcnt, offset); + if (result < 0) { + LOG_ERROR(Kernel_Fs, "error = {}", *__Error()); + return ErrnoToSceKernelError(*__Error()); + } + return result; +} + s32 PS4_SYSV_ABI posix_unlink(const char* path) { if (path == nullptr) { *__Error() = POSIX_EINVAL; @@ -1017,7 +1084,10 @@ void RegisterFileSystem(Core::Loader::SymbolsResolver* sym) { LIB_FUNCTION("sfKygSjIbI8", "libkernel", 1, "libkernel", 1, 1, getdirentries); LIB_FUNCTION("taRWhTJFTgE", "libkernel", 1, "libkernel", 1, 1, sceKernelGetdirentries); LIB_FUNCTION("C2kJ-byS5rM", "libkernel", 1, "libkernel", 1, 1, posix_pwrite); + LIB_FUNCTION("FCcmRZhWtOk", "libScePosix", 1, "libkernel", 1, 1, posix_pwritev); + LIB_FUNCTION("FCcmRZhWtOk", "libkernel", 1, "libkernel", 1, 1, posix_pwritev); LIB_FUNCTION("nKWi-N2HBV4", "libkernel", 1, "libkernel", 1, 1, sceKernelPwrite); + LIB_FUNCTION("mBd4AfLP+u8", "libkernel", 1, "libkernel", 1, 1, sceKernelPwritev); LIB_FUNCTION("AUXVxWeJU-A", "libkernel", 1, "libkernel", 1, 1, sceKernelUnlink); } diff --git a/src/core/libraries/kernel/kernel.cpp b/src/core/libraries/kernel/kernel.cpp index 33602bfe8..959a8605a 100644 --- a/src/core/libraries/kernel/kernel.cpp +++ b/src/core/libraries/kernel/kernel.cpp @@ -24,6 +24,7 @@ #include "core/libraries/kernel/threads/exception.h" #include "core/libraries/kernel/time.h" #include "core/libraries/libs.h" +#include "core/libraries/network/sys_net.h" #ifdef _WIN64 #include @@ -196,10 +197,6 @@ const char* PS4_SYSV_ABI sceKernelGetFsSandboxRandomWord() { return path; } -int PS4_SYSV_ABI posix_connect() { - return -1; -} - int PS4_SYSV_ABI _sigprocmask() { return ORBIS_OK; } @@ -225,7 +222,6 @@ void RegisterKernel(Core::Loader::SymbolsResolver* sym) { LIB_OBJ("f7uOxY9mM1U", "libkernel", 1, "libkernel", 1, 1, &g_stack_chk_guard); LIB_FUNCTION("PfccT7qURYE", "libkernel", 1, "libkernel", 1, 1, kernel_ioctl); LIB_FUNCTION("JGfTMBOdUJo", "libkernel", 1, "libkernel", 1, 1, sceKernelGetFsSandboxRandomWord); - LIB_FUNCTION("XVL8So3QJUk", "libkernel", 1, "libkernel", 1, 1, posix_connect); LIB_FUNCTION("6xVpy0Fdq+I", "libkernel", 1, "libkernel", 1, 1, _sigprocmask); LIB_FUNCTION("Xjoosiw+XPI", "libkernel", 1, "libkernel", 1, 1, sceKernelUuidCreate); LIB_FUNCTION("Ou3iL1abvng", "libkernel", 1, "libkernel", 1, 1, stack_chk_fail); @@ -234,6 +230,25 @@ void RegisterKernel(Core::Loader::SymbolsResolver* sym) { LIB_FUNCTION("k+AXqu2-eBc", "libScePosix", 1, "libkernel", 1, 1, posix_getpagesize); LIB_FUNCTION("NWtTN10cJzE", "libSceLibcInternalExt", 1, "libSceLibcInternal", 1, 1, sceLibcHeapGetTraceInfo); + + // network + LIB_FUNCTION("XVL8So3QJUk", "libkernel", 1, "libkernel", 1, 1, Libraries::Net::sys_connect); + LIB_FUNCTION("TU-d9PfIHPM", "libkernel", 1, "libkernel", 1, 1, Libraries::Net::sys_socketex); + LIB_FUNCTION("KuOmgKoqCdY", "libkernel", 1, "libkernel", 1, 1, Libraries::Net::sys_bind); + LIB_FUNCTION("pxnCmagrtao", "libkernel", 1, "libkernel", 1, 1, Libraries::Net::sys_listen); + LIB_FUNCTION("3e+4Iv7IJ8U", "libkernel", 1, "libkernel", 1, 1, Libraries::Net::sys_accept); + LIB_FUNCTION("TU-d9PfIHPM", "libScePosix", 1, "libkernel", 1, 1, Libraries::Net::sys_socket); + LIB_FUNCTION("oBr313PppNE", "libScePosix", 1, "libkernel", 1, 1, Libraries::Net::sys_sendto); + LIB_FUNCTION("lUk6wrGXyMw", "libScePosix", 1, "libkernel", 1, 1, Libraries::Net::sys_recvfrom); + LIB_FUNCTION("fFxGkxF2bVo", "libScePosix", 1, "libkernel", 1, 1, + Libraries::Net::sys_setsockopt); + LIB_FUNCTION("RenI1lL1WFk", "libScePosix", 1, "libkernel", 1, 1, + Libraries::Net::sys_getsockname); + LIB_FUNCTION("KuOmgKoqCdY", "libScePosix", 1, "libkernel", 1, 1, Libraries::Net::sys_bind); + LIB_FUNCTION("5jRCs2axtr4", "libScePosix", 1, "libkernel", 1, 1, + Libraries::Net::sceNetInetNtop); // TODO fix it to sys_ ... + LIB_FUNCTION("4n51s0zEf0c", "libScePosix", 1, "libkernel", 1, 1, + Libraries::Net::sceNetInetPton); // TODO fix it to sys_ ... } } // namespace Libraries::Kernel diff --git a/src/core/libraries/kernel/kernel.h b/src/core/libraries/kernel/kernel.h index 58911727d..4d68aa357 100644 --- a/src/core/libraries/kernel/kernel.h +++ b/src/core/libraries/kernel/kernel.h @@ -18,6 +18,7 @@ namespace Libraries::Kernel { void ErrSceToPosix(int result); int ErrnoToSceKernelError(int e); void SetPosixErrno(int e); +int* PS4_SYSV_ABI __Error(); template struct WrapperImpl; diff --git a/src/core/libraries/kernel/memory.cpp b/src/core/libraries/kernel/memory.cpp index 8a0c91479..495ddc52f 100644 --- a/src/core/libraries/kernel/memory.cpp +++ b/src/core/libraries/kernel/memory.cpp @@ -126,9 +126,6 @@ s32 PS4_SYSV_ABI sceKernelAvailableDirectMemorySize(u64 searchStart, u64 searchE s32 PS4_SYSV_ABI sceKernelVirtualQuery(const void* addr, int flags, OrbisVirtualQueryInfo* info, size_t infoSize) { LOG_INFO(Kernel_Vmm, "called addr = {}, flags = {:#x}", fmt::ptr(addr), flags); - if (!addr) { - return ORBIS_KERNEL_ERROR_EACCES; - } auto* memory = Core::Memory::Instance(); return memory->VirtualQuery(std::bit_cast(addr), flags, info); } @@ -136,7 +133,6 @@ s32 PS4_SYSV_ABI sceKernelVirtualQuery(const void* addr, int flags, OrbisVirtual s32 PS4_SYSV_ABI sceKernelReserveVirtualRange(void** addr, u64 len, int flags, u64 alignment) { LOG_INFO(Kernel_Vmm, "addr = {}, len = {:#x}, flags = {:#x}, alignment = {:#x}", fmt::ptr(*addr), len, flags, alignment); - if (addr == nullptr) { LOG_ERROR(Kernel_Vmm, "Address is invalid!"); return ORBIS_KERNEL_ERROR_EINVAL; @@ -155,9 +151,12 @@ s32 PS4_SYSV_ABI sceKernelReserveVirtualRange(void** addr, u64 len, int flags, u auto* memory = Core::Memory::Instance(); const VAddr in_addr = reinterpret_cast(*addr); const auto map_flags = static_cast(flags); - memory->Reserve(addr, in_addr, len, map_flags, alignment); - return ORBIS_OK; + s32 result = memory->Reserve(addr, in_addr, len, map_flags, alignment); + if (result == 0) { + LOG_INFO(Kernel_Vmm, "out_addr = {}", fmt::ptr(*addr)); + } + return result; } int PS4_SYSV_ABI sceKernelMapNamedDirectMemory(void** addr, u64 len, int prot, int flags, @@ -172,10 +171,12 @@ int PS4_SYSV_ABI sceKernelMapNamedDirectMemory(void** addr, u64 len, int prot, i LOG_ERROR(Kernel_Vmm, "Map size is either zero or not 16KB aligned!"); return ORBIS_KERNEL_ERROR_EINVAL; } + if (!Common::Is16KBAligned(directMemoryStart)) { LOG_ERROR(Kernel_Vmm, "Start address is not 16KB aligned!"); return ORBIS_KERNEL_ERROR_EINVAL; } + if (alignment != 0) { if ((!std::has_single_bit(alignment) && !Common::Is16KBAligned(alignment))) { LOG_ERROR(Kernel_Vmm, "Alignment value is invalid!"); @@ -183,14 +184,19 @@ int PS4_SYSV_ABI sceKernelMapNamedDirectMemory(void** addr, u64 len, int prot, i } } + if (std::strlen(name) >= ORBIS_KERNEL_MAXIMUM_NAME_LENGTH) { + LOG_ERROR(Kernel_Vmm, "name exceeds 32 bytes!"); + return ORBIS_KERNEL_ERROR_ENAMETOOLONG; + } + const VAddr in_addr = reinterpret_cast(*addr); const auto mem_prot = static_cast(prot); const auto map_flags = static_cast(flags); auto* memory = Core::Memory::Instance(); const auto ret = - memory->MapMemory(addr, in_addr, len, mem_prot, map_flags, Core::VMAType::Direct, "", false, - directMemoryStart, alignment); + memory->MapMemory(addr, in_addr, len, mem_prot, map_flags, Core::VMAType::Direct, name, + false, directMemoryStart, alignment); LOG_INFO(Kernel_Vmm, "out_addr = {}", fmt::ptr(*addr)); return ret; @@ -199,7 +205,8 @@ int PS4_SYSV_ABI sceKernelMapNamedDirectMemory(void** addr, u64 len, int prot, i int PS4_SYSV_ABI sceKernelMapDirectMemory(void** addr, u64 len, int prot, int flags, s64 directMemoryStart, u64 alignment) { LOG_INFO(Kernel_Vmm, "called, redirected to sceKernelMapNamedDirectMemory"); - return sceKernelMapNamedDirectMemory(addr, len, prot, flags, directMemoryStart, alignment, ""); + return sceKernelMapNamedDirectMemory(addr, len, prot, flags, directMemoryStart, alignment, + "anon"); } s32 PS4_SYSV_ABI sceKernelMapNamedFlexibleMemory(void** addr_in_out, std::size_t len, int prot, @@ -210,17 +217,16 @@ s32 PS4_SYSV_ABI sceKernelMapNamedFlexibleMemory(void** addr_in_out, std::size_t return ORBIS_KERNEL_ERROR_EINVAL; } - static constexpr size_t MaxNameSize = 32; - if (std::strlen(name) > MaxNameSize) { - LOG_ERROR(Kernel_Vmm, "name exceeds 32 bytes!"); - return ORBIS_KERNEL_ERROR_ENAMETOOLONG; - } - if (name == nullptr) { LOG_ERROR(Kernel_Vmm, "name is invalid!"); return ORBIS_KERNEL_ERROR_EFAULT; } + if (std::strlen(name) >= ORBIS_KERNEL_MAXIMUM_NAME_LENGTH) { + LOG_ERROR(Kernel_Vmm, "name exceeds 32 bytes!"); + return ORBIS_KERNEL_ERROR_ENAMETOOLONG; + } + const VAddr in_addr = reinterpret_cast(*addr_in_out); const auto mem_prot = static_cast(prot); const auto map_flags = static_cast(flags); @@ -236,7 +242,7 @@ s32 PS4_SYSV_ABI sceKernelMapNamedFlexibleMemory(void** addr_in_out, std::size_t s32 PS4_SYSV_ABI sceKernelMapFlexibleMemory(void** addr_in_out, std::size_t len, int prot, int flags) { - return sceKernelMapNamedFlexibleMemory(addr_in_out, len, prot, flags, ""); + return sceKernelMapNamedFlexibleMemory(addr_in_out, len, prot, flags, "anon"); } int PS4_SYSV_ABI sceKernelQueryMemoryProtection(void* addr, void** start, void** end, u32* prot) { @@ -304,7 +310,7 @@ s32 PS4_SYSV_ABI sceKernelBatchMap2(OrbisKernelBatchMapEntry* entries, int numEn case MemoryOpTypes::ORBIS_KERNEL_MAP_OP_MAP_DIRECT: { result = sceKernelMapNamedDirectMemory(&entries[i].start, entries[i].length, entries[i].protection, flags, - static_cast(entries[i].offset), 0, ""); + static_cast(entries[i].offset), 0, "anon"); LOG_INFO(Kernel_Vmm, "entry = {}, operation = {}, len = {:#x}, offset = {:#x}, type = {}, " "result = {}", @@ -326,7 +332,7 @@ s32 PS4_SYSV_ABI sceKernelBatchMap2(OrbisKernelBatchMapEntry* entries, int numEn } case MemoryOpTypes::ORBIS_KERNEL_MAP_OP_MAP_FLEXIBLE: { result = sceKernelMapNamedFlexibleMemory(&entries[i].start, entries[i].length, - entries[i].protection, flags, ""); + entries[i].protection, flags, "anon"); LOG_INFO(Kernel_Vmm, "entry = {}, operation = {}, len = {:#x}, type = {}, " "result = {}", @@ -356,16 +362,16 @@ s32 PS4_SYSV_ABI sceKernelBatchMap2(OrbisKernelBatchMapEntry* entries, int numEn } s32 PS4_SYSV_ABI sceKernelSetVirtualRangeName(const void* addr, size_t len, const char* name) { - static constexpr size_t MaxNameSize = 32; - if (std::strlen(name) > MaxNameSize) { - LOG_ERROR(Kernel_Vmm, "name exceeds 32 bytes!"); - return ORBIS_KERNEL_ERROR_ENAMETOOLONG; - } - if (name == nullptr) { LOG_ERROR(Kernel_Vmm, "name is invalid!"); return ORBIS_KERNEL_ERROR_EFAULT; } + + if (std::strlen(name) >= ORBIS_KERNEL_MAXIMUM_NAME_LENGTH) { + LOG_ERROR(Kernel_Vmm, "name exceeds 32 bytes!"); + return ORBIS_KERNEL_ERROR_ENAMETOOLONG; + } + auto* memory = Core::Memory::Instance(); memory->NameVirtualRange(std::bit_cast(addr), len, name); return ORBIS_OK; diff --git a/src/core/libraries/kernel/memory.h b/src/core/libraries/kernel/memory.h index 400b6c3fc..6acb559d1 100644 --- a/src/core/libraries/kernel/memory.h +++ b/src/core/libraries/kernel/memory.h @@ -47,6 +47,8 @@ enum MemoryOpTypes : u32 { ORBIS_KERNEL_MAP_OP_TYPE_PROTECT = 4 }; +constexpr u32 ORBIS_KERNEL_MAXIMUM_NAME_LENGTH = 32; + struct OrbisQueryInfo { uintptr_t start; uintptr_t end; @@ -59,14 +61,12 @@ struct OrbisVirtualQueryInfo { size_t offset; s32 protection; s32 memory_type; - union { - BitField<0, 1, u32> is_flexible; - BitField<1, 1, u32> is_direct; - BitField<2, 1, u32> is_stack; - BitField<3, 1, u32> is_pooled; - BitField<4, 1, u32> is_committed; - }; - std::array name; + u32 is_flexible : 1; + u32 is_direct : 1; + u32 is_stack : 1; + u32 is_pooled : 1; + u32 is_committed : 1; + char name[ORBIS_KERNEL_MAXIMUM_NAME_LENGTH]; }; struct OrbisKernelBatchMapEntry { diff --git a/src/core/libraries/kernel/process.cpp b/src/core/libraries/kernel/process.cpp index 02f8a538d..8a37e78d5 100644 --- a/src/core/libraries/kernel/process.cpp +++ b/src/core/libraries/kernel/process.cpp @@ -127,6 +127,62 @@ int PS4_SYSV_ABI sceKernelGetModuleInfoFromAddr(VAddr addr, int flags, return ORBIS_OK; } +s32 PS4_SYSV_ABI sceKernelGetModuleInfo(s32 handle, Core::OrbisKernelModuleInfo* info) { + if (info == nullptr) { + return ORBIS_KERNEL_ERROR_EFAULT; + } + if (info->st_size != sizeof(Core::OrbisKernelModuleInfo)) { + return ORBIS_KERNEL_ERROR_EINVAL; + } + + auto* linker = Common::Singleton::Instance(); + auto* module = linker->GetModule(handle); + if (module == nullptr) { + return ORBIS_KERNEL_ERROR_ESRCH; + } + *info = module->GetModuleInfo(); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceKernelGetModuleInfoInternal(s32 handle, Core::OrbisKernelModuleInfoEx* info) { + if (info == nullptr) { + return ORBIS_KERNEL_ERROR_EFAULT; + } + if (info->st_size != sizeof(Core::OrbisKernelModuleInfoEx)) { + return ORBIS_KERNEL_ERROR_EINVAL; + } + + auto* linker = Common::Singleton::Instance(); + auto* module = linker->GetModule(handle); + if (module == nullptr) { + return ORBIS_KERNEL_ERROR_ESRCH; + } + *info = module->GetModuleInfoEx(); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceKernelGetModuleList(s32* handles, u64 num_array, u64* out_count) { + if (handles == nullptr || out_count == nullptr) { + return ORBIS_KERNEL_ERROR_EFAULT; + } + + auto* linker = Common::Singleton::Instance(); + u64 count = 0; + auto* module = linker->GetModule(count); + while (module != nullptr && count < num_array) { + handles[count] = count; + count++; + module = linker->GetModule(count); + } + + if (count == num_array && module != nullptr) { + return ORBIS_KERNEL_ERROR_ENOMEM; + } + + *out_count = count; + return ORBIS_OK; +} + s32 PS4_SYSV_ABI exit(s32 status) { UNREACHABLE_MSG("Exiting with status code {}", status); return 0; @@ -141,6 +197,9 @@ void RegisterProcess(Core::Loader::SymbolsResolver* sym) { LIB_FUNCTION("LwG8g3niqwA", "libkernel", 1, "libkernel", 1, 1, sceKernelDlsym); LIB_FUNCTION("RpQJJVKTiFM", "libkernel", 1, "libkernel", 1, 1, sceKernelGetModuleInfoForUnwind); LIB_FUNCTION("f7KBOafysXo", "libkernel", 1, "libkernel", 1, 1, sceKernelGetModuleInfoFromAddr); + LIB_FUNCTION("kUpgrXIrz7Q", "libkernel", 1, "libkernel", 1, 1, sceKernelGetModuleInfo); + LIB_FUNCTION("HZO7xOos4xc", "libkernel", 1, "libkernel", 1, 1, sceKernelGetModuleInfoInternal); + LIB_FUNCTION("IuxnUuXk6Bg", "libkernel", 1, "libkernel", 1, 1, sceKernelGetModuleList); LIB_FUNCTION("6Z83sYWFlA8", "libkernel", 1, "libkernel", 1, 1, exit); } diff --git a/src/core/libraries/kernel/threads/pthread.cpp b/src/core/libraries/kernel/threads/pthread.cpp index c4127ecf2..e791e74bf 100644 --- a/src/core/libraries/kernel/threads/pthread.cpp +++ b/src/core/libraries/kernel/threads/pthread.cpp @@ -289,7 +289,12 @@ int PS4_SYSV_ABI posix_pthread_create_name_np(PthreadT* thread, const PthreadAtt /* Create thread */ new_thread->native_thr = Core::NativeThread(); int ret = new_thread->native_thr.Create(RunThread, new_thread, &new_thread->attr); + ASSERT_MSG(ret == 0, "Failed to create thread with error {}", ret); + + if (attr != nullptr && *attr != nullptr && (*attr)->cpuset != nullptr) { + new_thread->SetAffinity((*attr)->cpuset); + } if (ret) { *thread = nullptr; } @@ -521,6 +526,69 @@ int PS4_SYSV_ABI posix_pthread_setcancelstate(PthreadCancelState state, return 0; } +int Pthread::SetAffinity(const Cpuset* cpuset) { + const auto processor_count = std::thread::hardware_concurrency(); + if (processor_count < 8) { + return 0; + } + if (cpuset == nullptr) { + return POSIX_EINVAL; + } + + u64 mask = cpuset->bits; + + uintptr_t handle = native_thr.GetHandle(); + if (handle == 0) { + return POSIX_ESRCH; + } + + // We don't use this currently because some games gets performance problems + // when applying affinity even on strong hardware + /* + #ifdef _WIN64 + DWORD_PTR affinity_mask = static_cast(mask); + if (!SetThreadAffinityMask(reinterpret_cast(handle), affinity_mask)) { + return POSIX_EINVAL; + } + + #elif defined(__linux__) + cpu_set_t cpu_set; + CPU_ZERO(&cpu_set); + + u64 mask = cpuset->bits; + for (int cpu = 0; cpu < std::min(64, CPU_SETSIZE); ++cpu) { + if (mask & (1ULL << cpu)) { + CPU_SET(cpu, &cpu_set); + } + } + + int result = + pthread_setaffinity_np(static_cast(handle), sizeof(cpu_set_t), &cpu_set); + if (result != 0) { + return POSIX_EINVAL; + } + #endif + */ + return 0; +} + +int PS4_SYSV_ABI posix_pthread_setaffinity_np(PthreadT thread, size_t cpusetsize, + const Cpuset* cpusetp) { + if (thread == nullptr || cpusetp == nullptr) { + return POSIX_EINVAL; + } + thread->attr.cpusetsize = cpusetsize; + return thread->SetAffinity(cpusetp); +} + +int PS4_SYSV_ABI scePthreadSetaffinity(PthreadT thread, const Cpuset mask) { + int result = posix_pthread_setaffinity_np(thread, 0x10, &mask); + if (result != 0) { + return ErrnoToSceKernelError(result); + } + return 0; +} + void RegisterThread(Core::Loader::SymbolsResolver* sym) { // Posix LIB_FUNCTION("Z4QosVuAsA0", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_once); @@ -544,6 +612,7 @@ void RegisterThread(Core::Loader::SymbolsResolver* sym) { LIB_FUNCTION("Z4QosVuAsA0", "libkernel", 1, "libkernel", 1, 1, posix_pthread_once); LIB_FUNCTION("EotR8a3ASf4", "libkernel", 1, "libkernel", 1, 1, posix_pthread_self); LIB_FUNCTION("OxhIB8LB-PQ", "libkernel", 1, "libkernel", 1, 1, posix_pthread_create); + LIB_FUNCTION("5KWrg7-ZqvE", "libkernel", 1, "libkernel", 1, 1, posix_pthread_setaffinity_np); // Orbis LIB_FUNCTION("14bOACANTBo", "libkernel", 1, "libkernel", 1, 1, ORBIS(posix_pthread_once)); @@ -566,6 +635,7 @@ void RegisterThread(Core::Loader::SymbolsResolver* sym) { LIB_FUNCTION("W0Hpm2X0uPE", "libkernel", 1, "libkernel", 1, 1, ORBIS(posix_pthread_setprio)); LIB_FUNCTION("rNhWz+lvOMU", "libkernel", 1, "libkernel", 1, 1, _sceKernelSetThreadDtors); LIB_FUNCTION("6XG4B33N09g", "libkernel", 1, "libkernel", 1, 1, sched_yield); + LIB_FUNCTION("bt3CTBKmGyI", "libkernel", 1, "libkernel", 1, 1, scePthreadSetaffinity) } } // namespace Libraries::Kernel diff --git a/src/core/libraries/kernel/threads/pthread.h b/src/core/libraries/kernel/threads/pthread.h index 089156776..09eed11b8 100644 --- a/src/core/libraries/kernel/threads/pthread.h +++ b/src/core/libraries/kernel/threads/pthread.h @@ -332,6 +332,8 @@ struct Pthread { return true; } } + + int SetAffinity(const Cpuset* cpuset); }; using PthreadT = Pthread*; diff --git a/src/core/libraries/libc_internal/printf.h b/src/core/libraries/libc_internal/printf.h index fe63481a0..9c22e922c 100644 --- a/src/core/libraries/libc_internal/printf.h +++ b/src/core/libraries/libc_internal/printf.h @@ -56,7 +56,6 @@ #include #include -#include #include #include #include diff --git a/src/core/libraries/libs.cpp b/src/core/libraries/libs.cpp index 3f5baf640..3826ff793 100644 --- a/src/core/libraries/libs.cpp +++ b/src/core/libraries/libs.cpp @@ -45,6 +45,7 @@ #include "core/libraries/save_data/savedata.h" #include "core/libraries/screenshot/screenshot.h" #include "core/libraries/share_play/shareplay.h" +#include "core/libraries/signin_dialog/signindialog.h" #include "core/libraries/system/commondialog.h" #include "core/libraries/system/msgdialog.h" #include "core/libraries/system/posix.h" @@ -120,6 +121,7 @@ void InitHLELibs(Core::Loader::SymbolsResolver* sym) { Libraries::Hmd::RegisterlibSceHmd(sym); Libraries::DiscMap::RegisterlibSceDiscMap(sym); Libraries::Ulobjmgr::RegisterlibSceUlobjmgr(sym); + Libraries::SigninDialog::RegisterlibSceSigninDialog(sym); } } // namespace Libraries diff --git a/src/core/libraries/network/net.cpp b/src/core/libraries/network/net.cpp index 161fc5214..1f024277f 100644 --- a/src/core/libraries/network/net.cpp +++ b/src/core/libraries/network/net.cpp @@ -10,16 +10,24 @@ #include #endif +#include #include "common/assert.h" #include "common/logging/log.h" +#include "common/singleton.h" #include "core/libraries/error_codes.h" #include "core/libraries/libs.h" #include "core/libraries/network/net.h" +#include "net_error.h" +#include "net_util.h" +#include "netctl.h" +#include "sys_net.h" namespace Libraries::Net { static thread_local int32_t net_errno = 0; +static bool g_isNetInitialized = true; // TODO init it properly + int PS4_SYSV_ABI in6addr_any() { LOG_ERROR(Lib_Net, "(STUBBED) called"); return ORBIS_OK; @@ -61,8 +69,45 @@ int PS4_SYSV_ABI sce_net_in6addr_nodelocal_allnodes() { } OrbisNetId PS4_SYSV_ABI sceNetAccept(OrbisNetId s, OrbisNetSockaddr* addr, u32* paddrlen) { - LOG_ERROR(Lib_Net, "(STUBBED) called"); - return ORBIS_OK; + if (!g_isNetInitialized) { + return ORBIS_NET_ERROR_ENOTINIT; + } + int result; + int err; + int positiveErr; + + do { + result = sys_accept(s, addr, paddrlen); + + if (result >= 0) { + return result; // Success + } + + err = *Libraries::Kernel::__Error(); // Standard errno + + // Convert to positive error for comparison + int positiveErr = (err < 0) ? -err : err; + + if ((positiveErr & 0xfff0000) != 0) { + // Unknown/fatal error range + *sceNetErrnoLoc() = ORBIS_NET_ERETURN; + return -positiveErr; + } + + // Retry if interrupted + } while (positiveErr == ORBIS_NET_EINTR); + + if (positiveErr == ORBIS_NET_EADDRINUSE) { + result = -ORBIS_NET_EBADF; + } else if (positiveErr == ORBIS_NET_EALREADY) { + result = -ORBIS_NET_EINTR; + } else { + result = -positiveErr; + } + + *sceNetErrnoLoc() = -result; + + return (-result) | ORBIS_NET_ERROR_BASE; // Convert to official ORBIS_NET_ERROR code } int PS4_SYSV_ABI sceNetAddrConfig6GetInfo() { @@ -121,8 +166,45 @@ int PS4_SYSV_ABI sceNetBandwidthControlSetPolicy() { } int PS4_SYSV_ABI sceNetBind(OrbisNetId s, const OrbisNetSockaddr* addr, u32 addrlen) { - LOG_ERROR(Lib_Net, "(STUBBED) called"); - return ORBIS_OK; + if (!g_isNetInitialized) { + return ORBIS_NET_ERROR_ENOTINIT; + } + int result; + int err; + int positiveErr; + + do { + result = sys_bind(s, addr, addrlen); + + if (result >= 0) { + return result; // Success + } + + err = *Libraries::Kernel::__Error(); // Standard errno + + // Convert to positive error for comparison + int positiveErr = (err < 0) ? -err : err; + + if ((positiveErr & 0xfff0000) != 0) { + // Unknown/fatal error range + *sceNetErrnoLoc() = ORBIS_NET_ERETURN; + return -positiveErr; + } + + // Retry if interrupted + } while (positiveErr == ORBIS_NET_EINTR); + + if (positiveErr == ORBIS_NET_EADDRINUSE) { + result = -ORBIS_NET_EBADF; + } else if (positiveErr == ORBIS_NET_EALREADY) { + result = -ORBIS_NET_EINTR; + } else { + result = -positiveErr; + } + + *sceNetErrnoLoc() = -result; + + return (-result) | ORBIS_NET_ERROR_BASE; // Convert to official ORBIS_NET_ERROR code } int PS4_SYSV_ABI sceNetClearDnsCache() { @@ -465,9 +547,46 @@ int PS4_SYSV_ABI sceNetConfigWlanSetDeviceConfig() { return ORBIS_OK; } -int PS4_SYSV_ABI sceNetConnect() { - LOG_ERROR(Lib_Net, "(STUBBED) called"); - return ORBIS_OK; +int PS4_SYSV_ABI sceNetConnect(OrbisNetId s, const OrbisNetSockaddr* addr, u32 addrlen) { + if (!g_isNetInitialized) { + return ORBIS_NET_ERROR_ENOTINIT; + } + int result; + int err; + int positiveErr; + + do { + result = sys_connect(s, addr, addrlen); + + if (result >= 0) { + return result; // Success + } + + err = *Libraries::Kernel::__Error(); // Standard errno + + // Convert to positive error for comparison + int positiveErr = (err < 0) ? -err : err; + + if ((positiveErr & 0xfff0000) != 0) { + // Unknown/fatal error range + *sceNetErrnoLoc() = ORBIS_NET_ERETURN; + return -positiveErr; + } + + // Retry if interrupted + } while (positiveErr == ORBIS_NET_EINTR); + + if (positiveErr == ORBIS_NET_EADDRINUSE) { + result = -ORBIS_NET_EBADF; + } else if (positiveErr == ORBIS_NET_EALREADY) { + result = -ORBIS_NET_EINTR; + } else { + result = -positiveErr; + } + + *sceNetErrnoLoc() = -result; + + return (-result) | ORBIS_NET_ERROR_BASE; // Convert to official ORBIS_NET_ERROR code } int PS4_SYSV_ABI sceNetControl() { @@ -640,8 +759,15 @@ int PS4_SYSV_ABI sceNetGetIfnameNumList() { return ORBIS_OK; } -int PS4_SYSV_ABI sceNetGetMacAddress() { - LOG_ERROR(Lib_Net, "(STUBBED) called"); +int PS4_SYSV_ABI sceNetGetMacAddress(Libraries::NetCtl::OrbisNetEtherAddr* addr, int flags) { + if (addr == nullptr) { + LOG_ERROR(Lib_Net, "addr is null!"); + return ORBIS_NET_EINVAL; + } + auto* netinfo = Common::Singleton::Instance(); + netinfo->RetrieveEthernetAddr(); + memcpy(addr->data, netinfo->GetEthernetAddr().data(), 6); + return ORBIS_OK; } @@ -655,9 +781,46 @@ int PS4_SYSV_ABI sceNetGetNameToIndex() { return ORBIS_OK; } -int PS4_SYSV_ABI sceNetGetpeername() { - LOG_ERROR(Lib_Net, "(STUBBED) called"); - return ORBIS_OK; +int PS4_SYSV_ABI sceNetGetpeername(OrbisNetId s, OrbisNetSockaddr* addr, u32* paddrlen) { + if (!g_isNetInitialized) { + return ORBIS_NET_ERROR_ENOTINIT; + } + int result; + int err; + int positiveErr; + + do { + result = sys_getpeername(s, addr, paddrlen); + + if (result >= 0) { + return result; // Success + } + + err = *Libraries::Kernel::__Error(); // Standard errno + + // Convert to positive error for comparison + int positiveErr = (err < 0) ? -err : err; + + if ((positiveErr & 0xfff0000) != 0) { + // Unknown/fatal error range + *sceNetErrnoLoc() = ORBIS_NET_ERETURN; + return -positiveErr; + } + + // Retry if interrupted + } while (positiveErr == ORBIS_NET_EINTR); + + if (positiveErr == ORBIS_NET_EADDRINUSE) { + result = -ORBIS_NET_EBADF; + } else if (positiveErr == ORBIS_NET_EALREADY) { + result = -ORBIS_NET_EINTR; + } else { + result = -positiveErr; + } + + *sceNetErrnoLoc() = -result; + + return (-result) | ORBIS_NET_ERROR_BASE; // Convert to official ORBIS_NET_ERROR code } int PS4_SYSV_ABI sceNetGetRandom() { @@ -681,13 +844,87 @@ int PS4_SYSV_ABI sceNetGetSockInfo6() { } int PS4_SYSV_ABI sceNetGetsockname(OrbisNetId s, OrbisNetSockaddr* addr, u32* paddrlen) { - LOG_ERROR(Lib_Net, "(STUBBED) called"); - return ORBIS_OK; + if (!g_isNetInitialized) { + return ORBIS_NET_ERROR_ENOTINIT; + } + int result; + int err; + int positiveErr; + + do { + result = sys_getsockname(s, addr, paddrlen); + + if (result >= 0) { + return result; // Success + } + + err = *Libraries::Kernel::__Error(); // Standard errno + + // Convert to positive error for comparison + int positiveErr = (err < 0) ? -err : err; + + if ((positiveErr & 0xfff0000) != 0) { + // Unknown/fatal error range + *sceNetErrnoLoc() = ORBIS_NET_ERETURN; + return -positiveErr; + } + + // Retry if interrupted + } while (positiveErr == ORBIS_NET_EINTR); + + if (positiveErr == ORBIS_NET_EADDRINUSE) { + result = -ORBIS_NET_EBADF; + } else if (positiveErr == ORBIS_NET_EALREADY) { + result = -ORBIS_NET_EINTR; + } else { + result = -positiveErr; + } + + *sceNetErrnoLoc() = -result; + + return (-result) | ORBIS_NET_ERROR_BASE; // Convert to official ORBIS_NET_ERROR code } int PS4_SYSV_ABI sceNetGetsockopt(OrbisNetId s, int level, int optname, void* optval, u32* optlen) { - LOG_ERROR(Lib_Net, "(STUBBED) called"); - return ORBIS_OK; + if (!g_isNetInitialized) { + return ORBIS_NET_ERROR_ENOTINIT; + } + int result; + int err; + int positiveErr; + + do { + result = sys_getsockopt(s, level, optname, optval, optlen); + + if (result >= 0) { + return result; // Success + } + + err = *Libraries::Kernel::__Error(); // Standard errno + + // Convert to positive error for comparison + int positiveErr = (err < 0) ? -err : err; + + if ((positiveErr & 0xfff0000) != 0) { + // Unknown/fatal error range + *sceNetErrnoLoc() = ORBIS_NET_ERETURN; + return -positiveErr; + } + + // Retry if interrupted + } while (positiveErr == ORBIS_NET_EINTR); + + if (positiveErr == ORBIS_NET_EADDRINUSE) { + result = -ORBIS_NET_EBADF; + } else if (positiveErr == ORBIS_NET_EALREADY) { + result = -ORBIS_NET_EINTR; + } else { + result = -positiveErr; + } + + *sceNetErrnoLoc() = -result; + + return (-result) | ORBIS_NET_ERROR_BASE; // Convert to official ORBIS_NET_ERROR code } int PS4_SYSV_ABI sceNetGetStatisticsInfo() { @@ -781,9 +1018,46 @@ int PS4_SYSV_ABI sceNetIoctl() { return ORBIS_OK; } -int PS4_SYSV_ABI sceNetListen() { - LOG_ERROR(Lib_Net, "(STUBBED) called"); - return ORBIS_OK; +int PS4_SYSV_ABI sceNetListen(OrbisNetId s, int backlog) { + if (!g_isNetInitialized) { + return ORBIS_NET_ERROR_ENOTINIT; + } + int result; + int err; + int positiveErr; + + do { + result = sys_listen(s, backlog); + + if (result >= 0) { + return result; // Success + } + + err = *Libraries::Kernel::__Error(); // Standard errno + + // Convert to positive error for comparison + int positiveErr = (err < 0) ? -err : err; + + if ((positiveErr & 0xfff0000) != 0) { + // Unknown/fatal error range + *sceNetErrnoLoc() = ORBIS_NET_ERETURN; + return -positiveErr; + } + + // Retry if interrupted + } while (positiveErr == ORBIS_NET_EINTR); + + if (positiveErr == ORBIS_NET_EADDRINUSE) { + result = -ORBIS_NET_EBADF; + } else if (positiveErr == ORBIS_NET_EALREADY) { + result = -ORBIS_NET_EINTR; + } else { + result = -positiveErr; + } + + *sceNetErrnoLoc() = -result; + + return (-result) | ORBIS_NET_ERROR_BASE; // Convert to official ORBIS_NET_ERROR code } int PS4_SYSV_ABI sceNetMemoryAllocate() { @@ -829,20 +1103,131 @@ int PS4_SYSV_ABI sceNetPppoeStop() { return ORBIS_OK; } -int PS4_SYSV_ABI sceNetRecv() { - LOG_ERROR(Lib_Net, "(STUBBED) called"); - return ORBIS_OK; +int PS4_SYSV_ABI sceNetRecv(OrbisNetId s, void* buf, u64 len, int flags) { + if (!g_isNetInitialized) { + return ORBIS_NET_ERROR_ENOTINIT; + } + int result; + int err; + int positiveErr; + + do { + result = sys_recvfrom(s, buf, len, flags | 0x40000000, nullptr, 0); + + if (result >= 0) { + return result; // Success + } + + err = *Libraries::Kernel::__Error(); // Standard errno + + // Convert to positive error for comparison + int positiveErr = (err < 0) ? -err : err; + + if ((positiveErr & 0xfff0000) != 0) { + // Unknown/fatal error range + *sceNetErrnoLoc() = ORBIS_NET_ERETURN; + return -positiveErr; + } + + // Retry if interrupted + } while (positiveErr == ORBIS_NET_EINTR); + + if (positiveErr == ORBIS_NET_EADDRINUSE) { + result = -ORBIS_NET_EBADF; + } else if (positiveErr == ORBIS_NET_EALREADY) { + result = -ORBIS_NET_EINTR; + } else { + result = -positiveErr; + } + + *sceNetErrnoLoc() = -result; + + return (-result) | ORBIS_NET_ERROR_BASE; // Convert to official ORBIS_NET_ERROR code } -int PS4_SYSV_ABI sceNetRecvfrom(OrbisNetId s, void* buf, size_t len, int flags, - OrbisNetSockaddr* addr, u32* paddrlen) { - LOG_ERROR(Lib_Net, "(STUBBED) called"); - return ORBIS_OK; +int PS4_SYSV_ABI sceNetRecvfrom(OrbisNetId s, void* buf, u64 len, int flags, OrbisNetSockaddr* addr, + u32* paddrlen) { + if (!g_isNetInitialized) { + return ORBIS_NET_ERROR_ENOTINIT; + } + int result; + int err; + int positiveErr; + + do { + result = sys_recvfrom(s, buf, len, flags | 0x40000000, addr, paddrlen); + + if (result >= 0) { + return result; // Success + } + + err = *Libraries::Kernel::__Error(); // Standard errno + + // Convert to positive error for comparison + int positiveErr = (err < 0) ? -err : err; + + if ((positiveErr & 0xfff0000) != 0) { + // Unknown/fatal error range + *sceNetErrnoLoc() = ORBIS_NET_ERETURN; + return -positiveErr; + } + + // Retry if interrupted + } while (positiveErr == ORBIS_NET_EINTR); + + if (positiveErr == ORBIS_NET_EADDRINUSE) { + result = -ORBIS_NET_EBADF; + } else if (positiveErr == ORBIS_NET_EALREADY) { + result = -ORBIS_NET_EINTR; + } else { + result = -positiveErr; + } + + *sceNetErrnoLoc() = -result; + + return (-result) | ORBIS_NET_ERROR_BASE; // Convert to official ORBIS_NET_ERROR code } -int PS4_SYSV_ABI sceNetRecvmsg() { - LOG_ERROR(Lib_Net, "(STUBBED) called"); - return ORBIS_OK; +int PS4_SYSV_ABI sceNetRecvmsg(OrbisNetId s, OrbisNetMsghdr* msg, int flags) { + if (!g_isNetInitialized) { + return ORBIS_NET_ERROR_ENOTINIT; + } + int result; + int err; + int positiveErr; + + do { + result = sys_recvmsg(s, msg, flags | 0x40000000); + + if (result >= 0) { + return result; // Success + } + + err = *Libraries::Kernel::__Error(); // Standard errno + + // Convert to positive error for comparison + int positiveErr = (err < 0) ? -err : err; + + if ((positiveErr & 0xfff0000) != 0) { + // Unknown/fatal error range + *sceNetErrnoLoc() = ORBIS_NET_ERETURN; + return -positiveErr; + } + + // Retry if interrupted + } while (positiveErr == ORBIS_NET_EINTR); + + if (positiveErr == ORBIS_NET_EADDRINUSE) { + result = -ORBIS_NET_EBADF; + } else if (positiveErr == ORBIS_NET_EALREADY) { + result = -ORBIS_NET_EINTR; + } else { + result = -positiveErr; + } + + *sceNetErrnoLoc() = -result; + + return (-result) | ORBIS_NET_ERROR_BASE; // Convert to official ORBIS_NET_ERROR code } int PS4_SYSV_ABI sceNetResolverAbort() { @@ -915,19 +1300,131 @@ int PS4_SYSV_ABI sceNetResolverStartNtoaMultipleRecordsEx() { return ORBIS_OK; } -int PS4_SYSV_ABI sceNetSend() { - LOG_ERROR(Lib_Net, "(STUBBED) called"); - return ORBIS_OK; +int PS4_SYSV_ABI sceNetSend(OrbisNetId s, const void* buf, u64 len, int flags) { + if (!g_isNetInitialized) { + return ORBIS_NET_ERROR_ENOTINIT; + } + int result; + int err; + int positiveErr; + + do { + result = sys_sendto(s, buf, len, flags | 0x40020000, nullptr, 0); + + if (result >= 0) { + return result; // Success + } + + err = *Libraries::Kernel::__Error(); // Standard errno + + // Convert to positive error for comparison + int positiveErr = (err < 0) ? -err : err; + + if ((positiveErr & 0xfff0000) != 0) { + // Unknown/fatal error range + *sceNetErrnoLoc() = ORBIS_NET_ERETURN; + return -positiveErr; + } + + // Retry if interrupted + } while (positiveErr == ORBIS_NET_EINTR); + + if (positiveErr == ORBIS_NET_EADDRINUSE) { + result = -ORBIS_NET_EBADF; + } else if (positiveErr == ORBIS_NET_EALREADY) { + result = -ORBIS_NET_EINTR; + } else { + result = -positiveErr; + } + + *sceNetErrnoLoc() = -result; + + return (-result) | ORBIS_NET_ERROR_BASE; // Convert to official ORBIS_NET_ERROR code } -int PS4_SYSV_ABI sceNetSendmsg() { - LOG_ERROR(Lib_Net, "(STUBBED) called"); - return ORBIS_OK; +int PS4_SYSV_ABI sceNetSendmsg(OrbisNetId s, const OrbisNetMsghdr* msg, int flags) { + if (!g_isNetInitialized) { + return ORBIS_NET_ERROR_ENOTINIT; + } + int result; + int err; + int positiveErr; + + do { + result = sys_sendmsg(s, msg, flags | 0x40020000); + + if (result >= 0) { + return result; // Success + } + + err = *Libraries::Kernel::__Error(); // Standard errno + + // Convert to positive error for comparison + int positiveErr = (err < 0) ? -err : err; + + if ((positiveErr & 0xfff0000) != 0) { + // Unknown/fatal error range + *sceNetErrnoLoc() = ORBIS_NET_ERETURN; + return -positiveErr; + } + + // Retry if interrupted + } while (positiveErr == ORBIS_NET_EINTR); + + if (positiveErr == ORBIS_NET_EADDRINUSE) { + result = -ORBIS_NET_EBADF; + } else if (positiveErr == ORBIS_NET_EALREADY) { + result = -ORBIS_NET_EINTR; + } else { + result = -positiveErr; + } + + *sceNetErrnoLoc() = -result; + + return (-result) | ORBIS_NET_ERROR_BASE; // Convert to official ORBIS_NET_ERROR code } -int PS4_SYSV_ABI sceNetSendto() { - LOG_ERROR(Lib_Net, "(STUBBED) called"); - return ORBIS_OK; +int PS4_SYSV_ABI sceNetSendto(OrbisNetId s, const void* buf, u64 len, int flags, + const OrbisNetSockaddr* addr, u32 addrlen) { + if (!g_isNetInitialized) { + return ORBIS_NET_ERROR_ENOTINIT; + } + int result; + int err; + int positiveErr; + + do { + result = sys_sendto(s, buf, len, flags | 0x40020000, addr, addrlen); + + if (result >= 0) { + return result; // Success + } + + err = *Libraries::Kernel::__Error(); // Standard errno + + // Convert to positive error for comparison + int positiveErr = (err < 0) ? -err : err; + + if ((positiveErr & 0xfff0000) != 0) { + // Unknown/fatal error range + *sceNetErrnoLoc() = ORBIS_NET_ERETURN; + return -positiveErr; + } + + // Retry if interrupted + } while (positiveErr == ORBIS_NET_EINTR); + + if (positiveErr == ORBIS_NET_EADDRINUSE) { + result = -ORBIS_NET_EBADF; + } else if (positiveErr == ORBIS_NET_EALREADY) { + result = -ORBIS_NET_EINTR; + } else { + result = -positiveErr; + } + + *sceNetErrnoLoc() = -result; + + return (-result) | ORBIS_NET_ERROR_BASE; // Convert to official ORBIS_NET_ERROR code } int PS4_SYSV_ABI sceNetSetDns6Info() { @@ -950,9 +1447,47 @@ int PS4_SYSV_ABI sceNetSetDnsInfoToKernel() { return ORBIS_OK; } -int PS4_SYSV_ABI sceNetSetsockopt() { - LOG_ERROR(Lib_Net, "(STUBBED) called"); - return ORBIS_OK; +int PS4_SYSV_ABI sceNetSetsockopt(OrbisNetId s, int level, int optname, const void* optval, + u32 optlen) { + if (!g_isNetInitialized) { + return ORBIS_NET_ERROR_ENOTINIT; + } + int result; + int err; + int positiveErr; + + do { + result = sys_setsockopt(s, level, optname, optval, optlen); + + if (result >= 0) { + return result; // Success + } + + err = *Libraries::Kernel::__Error(); // Standard errno + + // Convert to positive error for comparison + int positiveErr = (err < 0) ? -err : err; + + if ((positiveErr & 0xfff0000) != 0) { + // Unknown/fatal error range + *sceNetErrnoLoc() = ORBIS_NET_ERETURN; + return -positiveErr; + } + + // Retry if interrupted + } while (positiveErr == ORBIS_NET_EINTR); + + if (positiveErr == ORBIS_NET_EADDRINUSE) { + result = -ORBIS_NET_EBADF; + } else if (positiveErr == ORBIS_NET_EALREADY) { + result = -ORBIS_NET_EINTR; + } else { + result = -positiveErr; + } + + *sceNetErrnoLoc() = -result; + + return (-result) | ORBIS_NET_ERROR_BASE; // Convert to official ORBIS_NET_ERROR code } int PS4_SYSV_ABI sceNetShowIfconfig() { @@ -1035,24 +1570,172 @@ int PS4_SYSV_ABI sceNetShowRouteWithMemory() { return ORBIS_OK; } -int PS4_SYSV_ABI sceNetShutdown() { - LOG_ERROR(Lib_Net, "(STUBBED) called"); - return ORBIS_OK; +int PS4_SYSV_ABI sceNetShutdown(OrbisNetId s, int how) { + if (!g_isNetInitialized) { + return ORBIS_NET_ERROR_ENOTINIT; + } + int result; + int err; + int positiveErr; + + do { + result = sys_shutdown(s, how); + + if (result >= 0) { + return result; // Success + } + + err = *Libraries::Kernel::__Error(); // Standard errno + + // Convert to positive error for comparison + int positiveErr = (err < 0) ? -err : err; + + if ((positiveErr & 0xfff0000) != 0) { + // Unknown/fatal error range + *sceNetErrnoLoc() = ORBIS_NET_ERETURN; + return -positiveErr; + } + + // Retry if interrupted + } while (positiveErr == ORBIS_NET_EINTR); + + if (positiveErr == ORBIS_NET_EADDRINUSE) { + result = -ORBIS_NET_EBADF; + } else if (positiveErr == ORBIS_NET_EALREADY) { + result = -ORBIS_NET_EINTR; + } else { + result = -positiveErr; + } + + *sceNetErrnoLoc() = -result; + + return (-result) | ORBIS_NET_ERROR_BASE; // Convert to official ORBIS_NET_ERROR code } -int PS4_SYSV_ABI sceNetSocket(const char* name, int family, int type, int protocol) { - LOG_ERROR(Lib_Net, "(STUBBED) called"); - return ORBIS_OK; +OrbisNetId PS4_SYSV_ABI sceNetSocket(const char* name, int family, int type, int protocol) { + if (!g_isNetInitialized) { + return ORBIS_NET_ERROR_ENOTINIT; + } + int result; + int err; + int positiveErr; + + do { + result = sys_socketex(name, family, type, protocol); + + if (result >= 0) { + return result; // Success + } + + err = *Libraries::Kernel::__Error(); // Standard errno + + // Convert to positive error for comparison + int positiveErr = (err < 0) ? -err : err; + + if ((positiveErr & 0xfff0000) != 0) { + // Unknown/fatal error range + *sceNetErrnoLoc() = ORBIS_NET_ERETURN; + return -positiveErr; + } + + // Retry if interrupted + } while (positiveErr == ORBIS_NET_EINTR); + + if (positiveErr == ORBIS_NET_EADDRINUSE) { + result = -ORBIS_NET_EBADF; + } else if (positiveErr == ORBIS_NET_EALREADY) { + result = -ORBIS_NET_EINTR; + } else { + result = -positiveErr; + } + + *sceNetErrnoLoc() = -result; + + return (-result) | ORBIS_NET_ERROR_BASE; // Convert to official ORBIS_NET_ERROR code } -int PS4_SYSV_ABI sceNetSocketAbort() { - LOG_ERROR(Lib_Net, "(STUBBED) called"); - return ORBIS_OK; +int PS4_SYSV_ABI sceNetSocketAbort(OrbisNetId s, int flags) { + if (!g_isNetInitialized) { + return ORBIS_NET_ERROR_ENOTINIT; + } + int result; + int err; + int positiveErr; + + do { + result = sys_netabort(s, flags); + + if (result >= 0) { + return result; // Success + } + + err = *Libraries::Kernel::__Error(); // Standard errno + + // Convert to positive error for comparison + int positiveErr = (err < 0) ? -err : err; + + if ((positiveErr & 0xfff0000) != 0) { + // Unknown/fatal error range + *sceNetErrnoLoc() = ORBIS_NET_ERETURN; + return -positiveErr; + } + + // Retry if interrupted + } while (positiveErr == ORBIS_NET_EINTR); + + if (positiveErr == ORBIS_NET_EADDRINUSE) { + result = -ORBIS_NET_EBADF; + } else if (positiveErr == ORBIS_NET_EALREADY) { + result = -ORBIS_NET_EINTR; + } else { + result = -positiveErr; + } + + *sceNetErrnoLoc() = -result; + + return (-result) | ORBIS_NET_ERROR_BASE; // Convert to official ORBIS_NET_ERROR code } -int PS4_SYSV_ABI sceNetSocketClose() { - LOG_ERROR(Lib_Net, "(STUBBED) called"); - return ORBIS_OK; +int PS4_SYSV_ABI sceNetSocketClose(OrbisNetId s) { + if (!g_isNetInitialized) { + return ORBIS_NET_ERROR_ENOTINIT; + } + int result; + int err; + int positiveErr; + + do { + result = sys_socketclose(s); + + if (result >= 0) { + return result; // Success + } + + err = *Libraries::Kernel::__Error(); // Standard errno + + // Convert to positive error for comparison + int positiveErr = (err < 0) ? -err : err; + + if ((positiveErr & 0xfff0000) != 0) { + // Unknown/fatal error range + *sceNetErrnoLoc() = ORBIS_NET_ERETURN; + return -positiveErr; + } + + // Retry if interrupted + } while (positiveErr == ORBIS_NET_EINTR); + + if (positiveErr == ORBIS_NET_EADDRINUSE) { + result = -ORBIS_NET_EBADF; + } else if (positiveErr == ORBIS_NET_EALREADY) { + result = -ORBIS_NET_EINTR; + } else { + result = -positiveErr; + } + + *sceNetErrnoLoc() = -result; + + return (-result) | ORBIS_NET_ERROR_BASE; // Convert to official ORBIS_NET_ERROR code } int PS4_SYSV_ABI sceNetSyncCreate() { diff --git a/src/core/libraries/network/net.h b/src/core/libraries/network/net.h index beef38b56..812ee6bd7 100644 --- a/src/core/libraries/network/net.h +++ b/src/core/libraries/network/net.h @@ -4,6 +4,7 @@ #pragma once #include "common/types.h" +#include "netctl.h" namespace Core::Loader { class SymbolsResolver; @@ -19,6 +20,63 @@ class SymbolsResolver; namespace Libraries::Net { +enum OrbisNetSocketType : u32 { + ORBIS_NET_SOCK_STREAM = 1, + ORBIS_NET_SOCK_DGRAM = 2, + ORBIS_NET_SOCK_RAW = 3, + ORBIS_NET_SOCK_DGRAM_P2P = 6, + ORBIS_NET_SOCK_STREAM_P2P = 10 +}; + +enum OrbisNetProtocol : u32 { + ORBIS_NET_IPPROTO_IP = 0, + ORBIS_NET_IPPROTO_ICMP = 1, + ORBIS_NET_IPPROTO_IGMP = 2, + ORBIS_NET_IPPROTO_TCP = 6, + ORBIS_NET_IPPROTO_UDP = 17, + ORBIS_NET_SOL_SOCKET = 0xFFFF +}; + +enum OrbisNetSocketOption : u32 { + /* IP */ + ORBIS_NET_IP_HDRINCL = 2, + ORBIS_NET_IP_TOS = 3, + ORBIS_NET_IP_TTL = 4, + ORBIS_NET_IP_MULTICAST_IF = 9, + ORBIS_NET_IP_MULTICAST_TTL = 10, + ORBIS_NET_IP_MULTICAST_LOOP = 11, + ORBIS_NET_IP_ADD_MEMBERSHIP = 12, + ORBIS_NET_IP_DROP_MEMBERSHIP = 13, + ORBIS_NET_IP_TTLCHK = 23, + ORBIS_NET_IP_MAXTTL = 24, + /* TCP */ + ORBIS_NET_TCP_NODELAY = 1, + ORBIS_NET_TCP_MAXSEG = 2, + ORBIS_NET_TCP_MSS_TO_ADVERTISE = 3, + /* SOCKET */ + ORBIS_NET_SO_REUSEADDR = 0x00000004, + ORBIS_NET_SO_KEEPALIVE = 0x00000008, + ORBIS_NET_SO_BROADCAST = 0x00000020, + ORBIS_NET_SO_LINGER = 0x00000080, + ORBIS_NET_SO_REUSEPORT = 0x00000200, + ORBIS_NET_SO_ONESBCAST = 0x00010000, + ORBIS_NET_SO_USECRYPTO = 0x00020000, + ORBIS_NET_SO_USESIGNATURE = 0x00040000, + ORBIS_NET_SO_SNDBUF = 0x1001, + ORBIS_NET_SO_RCVBUF = 0x1002, + ORBIS_NET_SO_ERROR = 0x1007, + ORBIS_NET_SO_TYPE = 0x1008, + ORBIS_NET_SO_SNDTIMEO = 0x1105, + ORBIS_NET_SO_RCVTIMEO = 0x1106, + ORBIS_NET_SO_ERROR_EX = 0x1107, + ORBIS_NET_SO_ACCEPTTIMEO = 0x1108, + ORBIS_NET_SO_CONNECTTIMEO = 0x1109, + ORBIS_NET_SO_NBIO = 0x1200, + ORBIS_NET_SO_POLICY = 0x1201, + ORBIS_NET_SO_NAME = 0x1202, + ORBIS_NET_SO_PRIORITY = 0x1203 +}; + using OrbisNetId = s32; struct OrbisNetSockaddr { @@ -27,6 +85,30 @@ struct OrbisNetSockaddr { char sa_data[14]; }; +struct OrbisNetSockaddrIn { + u8 sin_len; + u8 sin_family; + u16 sin_port; + u32 sin_addr; + u16 sin_vport; + char sin_zero[6]; +}; + +struct OrbisNetIovec { + void* iov_base; + u64 iov_len; +}; + +struct OrbisNetMsghdr { + void* msg_name; + u32 msg_namelen; + OrbisNetIovec* msg_iov; + int msg_iovlen; + void* msg_control; + u32 msg_controllen; + int msg_flags; +}; + int PS4_SYSV_ABI in6addr_any(); int PS4_SYSV_ABI in6addr_loopback(); int PS4_SYSV_ABI sce_net_dummy(); @@ -116,7 +198,7 @@ int PS4_SYSV_ABI sceNetConfigWlanInfraLeave(); int PS4_SYSV_ABI sceNetConfigWlanInfraScanJoin(); int PS4_SYSV_ABI sceNetConfigWlanScan(); int PS4_SYSV_ABI sceNetConfigWlanSetDeviceConfig(); -int PS4_SYSV_ABI sceNetConnect(); +int PS4_SYSV_ABI sceNetConnect(OrbisNetId s, const OrbisNetSockaddr* addr, u32 addrlen); int PS4_SYSV_ABI sceNetControl(); int PS4_SYSV_ABI sceNetDhcpdStart(); int PS4_SYSV_ABI sceNetDhcpdStop(); @@ -151,10 +233,10 @@ int PS4_SYSV_ABI sceNetGetIfList(); int PS4_SYSV_ABI sceNetGetIfListOnce(); int PS4_SYSV_ABI sceNetGetIfName(); int PS4_SYSV_ABI sceNetGetIfnameNumList(); -int PS4_SYSV_ABI sceNetGetMacAddress(); +int PS4_SYSV_ABI sceNetGetMacAddress(Libraries::NetCtl::OrbisNetEtherAddr* addr, int flags); int PS4_SYSV_ABI sceNetGetMemoryPoolStats(); int PS4_SYSV_ABI sceNetGetNameToIndex(); -int PS4_SYSV_ABI sceNetGetpeername(); +int PS4_SYSV_ABI sceNetGetpeername(OrbisNetId s, OrbisNetSockaddr* addr, u32* paddrlen); int PS4_SYSV_ABI sceNetGetRandom(); int PS4_SYSV_ABI sceNetGetRouteInfo(); int PS4_SYSV_ABI sceNetGetSockInfo(); @@ -177,7 +259,7 @@ int PS4_SYSV_ABI sceNetInfoDumpStop(); int PS4_SYSV_ABI sceNetInit(); int PS4_SYSV_ABI sceNetInitParam(); int PS4_SYSV_ABI sceNetIoctl(); -int PS4_SYSV_ABI sceNetListen(); +int PS4_SYSV_ABI sceNetListen(OrbisNetId s, int backlog); int PS4_SYSV_ABI sceNetMemoryAllocate(); int PS4_SYSV_ABI sceNetMemoryFree(); u32 PS4_SYSV_ABI sceNetNtohl(u32 net32); @@ -187,10 +269,10 @@ int PS4_SYSV_ABI sceNetPoolCreate(const char* name, int size, int flags); int PS4_SYSV_ABI sceNetPoolDestroy(); int PS4_SYSV_ABI sceNetPppoeStart(); int PS4_SYSV_ABI sceNetPppoeStop(); -int PS4_SYSV_ABI sceNetRecv(); -int PS4_SYSV_ABI sceNetRecvfrom(OrbisNetId s, void* buf, size_t len, int flags, - OrbisNetSockaddr* addr, u32* paddrlen); -int PS4_SYSV_ABI sceNetRecvmsg(); +int PS4_SYSV_ABI sceNetRecv(OrbisNetId s, void* buf, u64 len, int flags); +int PS4_SYSV_ABI sceNetRecvfrom(OrbisNetId s, void* buf, u64 len, int flags, OrbisNetSockaddr* addr, + u32* paddrlen); +int PS4_SYSV_ABI sceNetRecvmsg(OrbisNetId s, OrbisNetMsghdr* msg, int flags); int PS4_SYSV_ABI sceNetResolverAbort(); int PS4_SYSV_ABI sceNetResolverConnect(); int PS4_SYSV_ABI sceNetResolverConnectAbort(); @@ -205,14 +287,16 @@ int PS4_SYSV_ABI sceNetResolverStartNtoa(); int PS4_SYSV_ABI sceNetResolverStartNtoa6(); int PS4_SYSV_ABI sceNetResolverStartNtoaMultipleRecords(); int PS4_SYSV_ABI sceNetResolverStartNtoaMultipleRecordsEx(); -int PS4_SYSV_ABI sceNetSend(); -int PS4_SYSV_ABI sceNetSendmsg(); -int PS4_SYSV_ABI sceNetSendto(); +int PS4_SYSV_ABI sceNetSend(OrbisNetId s, const void* buf, u64 len, int flags); +int PS4_SYSV_ABI sceNetSendmsg(OrbisNetId s, const OrbisNetMsghdr* msg, int flags); +int PS4_SYSV_ABI sceNetSendto(OrbisNetId s, const void* buf, u64 len, int flags, + const OrbisNetSockaddr* addr, u32 addrlen); int PS4_SYSV_ABI sceNetSetDns6Info(); int PS4_SYSV_ABI sceNetSetDns6InfoToKernel(); int PS4_SYSV_ABI sceNetSetDnsInfo(); int PS4_SYSV_ABI sceNetSetDnsInfoToKernel(); -int PS4_SYSV_ABI sceNetSetsockopt(); +int PS4_SYSV_ABI sceNetSetsockopt(OrbisNetId s, int level, int optname, const void* optval, + u32 optlen); int PS4_SYSV_ABI sceNetShowIfconfig(); int PS4_SYSV_ABI sceNetShowIfconfigForBuffer(); int PS4_SYSV_ABI sceNetShowIfconfigWithMemory(); @@ -229,10 +313,10 @@ int PS4_SYSV_ABI sceNetShowRoute6ForBuffer(); int PS4_SYSV_ABI sceNetShowRoute6WithMemory(); int PS4_SYSV_ABI sceNetShowRouteForBuffer(); int PS4_SYSV_ABI sceNetShowRouteWithMemory(); -int PS4_SYSV_ABI sceNetShutdown(); -int PS4_SYSV_ABI sceNetSocket(const char* name, int family, int type, int protocol); -int PS4_SYSV_ABI sceNetSocketAbort(); -int PS4_SYSV_ABI sceNetSocketClose(); +int PS4_SYSV_ABI sceNetShutdown(OrbisNetId s, int how); +OrbisNetId PS4_SYSV_ABI sceNetSocket(const char* name, int family, int type, int protocol); +int PS4_SYSV_ABI sceNetSocketAbort(OrbisNetId s, int flags); +int PS4_SYSV_ABI sceNetSocketClose(OrbisNetId s); int PS4_SYSV_ABI sceNetSyncCreate(); int PS4_SYSV_ABI sceNetSyncDestroy(); int PS4_SYSV_ABI sceNetSyncGet(); diff --git a/src/core/libraries/network/net_error.h b/src/core/libraries/network/net_error.h new file mode 100644 index 000000000..ab65300c0 --- /dev/null +++ b/src/core/libraries/network/net_error.h @@ -0,0 +1,162 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +// net errno codes +constexpr int ORBIS_NET_EPERM = 1; +constexpr int ORBIS_NET_ENOENT = 2; +constexpr int ORBIS_NET_EINTR = 4; +constexpr int ORBIS_NET_EBADF = 9; +constexpr int ORBIS_NET_EACCES = 13; +constexpr int ORBIS_NET_EFAULT = 14; +constexpr int ORBIS_NET_ENOTBLK = 15; +constexpr int ORBIS_NET_EBUSY = 16; +constexpr int ORBIS_NET_EEXIST = 17; +constexpr int ORBIS_NET_ENODEV = 19; +constexpr int ORBIS_NET_EINVAL = 22; +constexpr int ORBIS_NET_EMFILE = 24; +constexpr int ORBIS_NET_ENOSPC = 28; +constexpr int ORBIS_NET_EPIPE = 32; +constexpr int ORBIS_NET_EAGAIN = 35; +constexpr int ORBIS_NET_EWOULDBLOCK = 35; +constexpr int ORBIS_NET_EINPROGRESS = 36; +constexpr int ORBIS_NET_EALREADY = 37; +constexpr int ORBIS_NET_ENOTSOCK = 38; +constexpr int ORBIS_NET_EDESTADDRREQ = 39; +constexpr int ORBIS_NET_EMSGSIZE = 40; +constexpr int ORBIS_NET_EPROTOTYPE = 41; +constexpr int ORBIS_NET_ENOPROTOOPT = 42; +constexpr int ORBIS_NET_EPROTONOSUPPORT = 43; +constexpr int ORBIS_NET_EOPNOTSUPP = 45; +constexpr int ORBIS_NET_EAFNOSUPPORT = 47; +constexpr int ORBIS_NET_EADDRINUSE = 48; +constexpr int ORBIS_NET_EADDRNOTAVAIL = 49; +constexpr int ORBIS_NET_ENETDOWN = 50; +constexpr int ORBIS_NET_ENETUNREACH = 51; +constexpr int ORBIS_NET_ENETRESET = 52; +constexpr int ORBIS_NET_ECONNABORTED = 53; +constexpr int ORBIS_NET_ECONNRESET = 54; +constexpr int ORBIS_NET_EISCONN = 56; +constexpr int ORBIS_NET_ENOTCONN = 57; +constexpr int ORBIS_NET_ETOOMANYREFS = 59; +constexpr int ORBIS_NET_ETIMEDOUT = 60; +constexpr int ORBIS_NET_ECONNREFUSED = 61; +constexpr int ORBIS_NET_ELOOP = 62; +constexpr int ORBIS_NET_ENAMETOOLONG = 63; +constexpr int ORBIS_NET_EHOSTDOWN = 64; +constexpr int ORBIS_NET_EHOSTUNREACH = 65; +constexpr int ORBIS_NET_ENOTEMPTY = 66; +constexpr int ORBIS_NET_EPROCUNAVAIL = 76; +constexpr int ORBIS_NET_EPROTO = 92; +constexpr int ORBIS_NET_EADHOC = 160; +constexpr int ORBIS_NET_EINACTIVEDISABLED = 163; +constexpr int ORBIS_NET_ENODATA = 164; +constexpr int ORBIS_NET_EDESC = 165; +constexpr int ORBIS_NET_EDESCTIMEDOUT = 166; +constexpr int ORBIS_NET_ENOTINIT = 200; +constexpr int ORBIS_NET_ENOLIBMEM = 201; +constexpr int ORBIS_NET_ECALLBACK = 203; +constexpr int ORBIS_NET_EINTERNAL = 204; +constexpr int ORBIS_NET_ERETURN = 205; +constexpr int ORBIS_NET_ENOALLOCMEM = 206; + +// errno for dns resolver +constexpr int ORBIS_NET_RESOLVER_EINTERNAL = 220; +constexpr int ORBIS_NET_RESOLVER_EBUSY = 221; +constexpr int ORBIS_NET_RESOLVER_ENOSPACE = 222; +constexpr int ORBIS_NET_RESOLVER_EPACKET = 223; +constexpr int ORBIS_NET_RESOLVER_ENODNS = 225; +constexpr int ORBIS_NET_RESOLVER_ETIMEDOUT = 226; +constexpr int ORBIS_NET_RESOLVER_ENOSUPPORT = 227; +constexpr int ORBIS_NET_RESOLVER_EFORMAT = 228; +constexpr int ORBIS_NET_RESOLVER_ESERVERFAILURE = 229; +constexpr int ORBIS_NET_RESOLVER_ENOHOST = 230; +constexpr int ORBIS_NET_RESOLVER_ENOTIMPLEMENTED = 231; +constexpr int ORBIS_NET_RESOLVER_ESERVERREFUSED = 232; +constexpr int ORBIS_NET_RESOLVER_ENORECORD = 233; +constexpr int ORBIS_NET_RESOLVER_EALIGNMENT = 234; + +// common errno +constexpr int ORBIS_NET_ENOMEM = 12; +constexpr int ORBIS_NET_ENOBUFS = 55; + +// error codes +constexpr int ORBIS_NET_ERROR_BASE = 0x80410100; // not existed used for calculation +constexpr int ORBIS_NET_ERROR_EPERM = 0x80410101; +constexpr int ORBIS_NET_ERROR_ENOENT = 0x80410102; +constexpr int ORBIS_NET_ERROR_EINTR = 0x80410104; +constexpr int ORBIS_NET_ERROR_EBADF = 0x80410109; +constexpr int ORBIS_NET_ERROR_ENOMEM = 0x8041010c; +constexpr int ORBIS_NET_ERROR_EACCES = 0x8041010d; +constexpr int ORBIS_NET_ERROR_EFAULT = 0x8041010e; +constexpr int ORBIS_NET_ERROR_ENOTBLK = 0x8041010f; +constexpr int ORBIS_NET_ERROR_EEXIST = 0x80410111; +constexpr int ORBIS_NET_ERROR_ENODEV = 0x80410113; +constexpr int ORBIS_NET_ERROR_EINVAL = 0x80410116; +constexpr int ORBIS_NET_ERROR_ENFILE = 0x80410117; +constexpr int ORBIS_NET_ERROR_EMFILE = 0x80410118; +constexpr int ORBIS_NET_ERROR_ENOSPC = 0x8041011c; +constexpr int ORBIS_NET_ERROR_EPIPE = 0x80410120; +constexpr int ORBIS_NET_ERROR_EAGAIN = 0x80410123; +constexpr int ORBIS_NET_ERROR_EWOULDBLOCK = 0x80410123; +constexpr int ORBIS_NET_ERROR_EINPROGRESS = 0x80410124; +constexpr int ORBIS_NET_ERROR_EALREADY = 0x80410125; +constexpr int ORBIS_NET_ERROR_ENOTSOCK = 0x80410126; +constexpr int ORBIS_NET_ERROR_EDESTADDRREQ = 0x80410127; +constexpr int ORBIS_NET_ERROR_EMSGSIZE = 0x80410128; +constexpr int ORBIS_NET_ERROR_EPROTOTYPE = 0x80410129; +constexpr int ORBIS_NET_ERROR_ENOPROTOOPT = 0x8041012a; +constexpr int ORBIS_NET_ERROR_EPROTONOSUPPORT = 0x8041012b; +constexpr int ORBIS_NET_ERROR_EOPNOTSUPP = 0x8041012d; +constexpr int ORBIS_NET_ERROR_EPFNOSUPPORT = 0x8041012e; +constexpr int ORBIS_NET_ERROR_EAFNOSUPPORT = 0x8041012f; +constexpr int ORBIS_NET_ERROR_EADDRINUSE = 0x80410130; +constexpr int ORBIS_NET_ERROR_EADDRNOTAVAIL = 0x80410131; +constexpr int ORBIS_NET_ERROR_ENETDOWN = 0x80410132; +constexpr int ORBIS_NET_ERROR_ENETUNREACH = 0x80410133; +constexpr int ORBIS_NET_ERROR_ENETRESET = 0x80410134; +constexpr int ORBIS_NET_ERROR_ECONNABORTED = 0x80410135; +constexpr int ORBIS_NET_ERROR_ECONNRESET = 0x80410136; +constexpr int ORBIS_NET_ERROR_ENOBUFS = 0x80410137; +constexpr int ORBIS_NET_ERROR_EISCONN = 0x80410138; +constexpr int ORBIS_NET_ERROR_ENOTCONN = 0x80410139; +constexpr int ORBIS_NET_ERROR_ESHUTDOWN = 0x8041013a; +constexpr int ORBIS_NET_ERROR_ETOOMANYREFS = 0x8041013b; +constexpr int ORBIS_NET_ERROR_ETIMEDOUT = 0x8041013c; +constexpr int ORBIS_NET_ERROR_ECONNREFUSED = 0x8041013d; +constexpr int ORBIS_NET_ERROR_ELOOP = 0x8041013e; +constexpr int ORBIS_NET_ERROR_ENAMETOOLONG = 0x8041013f; +constexpr int ORBIS_NET_ERROR_EHOSTDOWN = 0x80410140; +constexpr int ORBIS_NET_ERROR_EHOSTUNREACH = 0x80410141; +constexpr int ORBIS_NET_ERROR_ENOTEMPTY = 0x80410142; +constexpr int ORBIS_NET_ERROR_EPROCUNAVAIL = 0x8041014C; +constexpr int ORBIS_NET_ERROR_ECANCELED = 0x80410157; +constexpr int ORBIS_NET_ERROR_EPROTO = 0x8041015C; +constexpr int ORBIS_NET_ERROR_EADHOC = 0x804101a0; +constexpr int ORBIS_NET_ERROR_ERESERVED161 = 0x804101a1; +constexpr int ORBIS_NET_ERROR_ERESERVED162 = 0x804101a2; +constexpr int ORBIS_NET_ERROR_EINACTIVEDISABLED = 0x804101a3; +constexpr int ORBIS_NET_ERROR_ENODATA = 0x804101a4; +constexpr int ORBIS_NET_ERROR_EDESC = 0x804101a5; +constexpr int ORBIS_NET_ERROR_EDESCTIMEDOUT = 0x804101a6; +constexpr int ORBIS_NET_ERROR_ENOTINIT = 0x804101c8; +constexpr int ORBIS_NET_ERROR_ENOLIBMEM = 0x804101c9; +constexpr int ORBIS_NET_ERROR_ECALLBACK = 0x804101cb; +constexpr int ORBIS_NET_ERROR_EINTERNAL = 0x804101cc; +constexpr int ORBIS_NET_ERROR_ERETURN = 0x804101cd; +constexpr int ORBIS_NET_ERROR_ENOALLOCMEM = 0x804101ce; +constexpr int ORBIS_NET_ERROR_RESOLVER_EINTERNAL = 0x804101dc; +constexpr int ORBIS_NET_ERROR_RESOLVER_EBUSY = 0x804101dd; +constexpr int ORBIS_NET_ERROR_RESOLVER_ENOSPACE = 0x804101de; +constexpr int ORBIS_NET_ERROR_RESOLVER_EPACKET = 0x804101df; +constexpr int ORBIS_NET_ERROR_RESOLVER_ENODNS = 0x804101e1; +constexpr int ORBIS_NET_ERROR_RESOLVER_ETIMEDOUT = 0x804101e2; +constexpr int ORBIS_NET_ERROR_RESOLVER_ENOSUPPORT = 0x804101e3; +constexpr int ORBIS_NET_ERROR_RESOLVER_EFORMAT = 0x804101e4; +constexpr int ORBIS_NET_ERROR_RESOLVER_ESERVERFAILURE = 0x804101e5; +constexpr int ORBIS_NET_ERROR_RESOLVER_ENOHOST = 0x804101e6; +constexpr int ORBIS_NET_ERROR_RESOLVER_ENOTIMPLEMENTED = 0x804101e7; +constexpr int ORBIS_NET_ERROR_RESOLVER_ESERVERREFUSED = 0x804101e8; +constexpr int ORBIS_NET_ERROR_RESOLVER_ENORECORD = 0x804101e9; +constexpr int ORBIS_NET_ERROR_RESOLVER_EALIGNMENT = 0x804101ea; diff --git a/src/core/libraries/network/net_util.cpp b/src/core/libraries/network/net_util.cpp new file mode 100644 index 000000000..d0f0a81da --- /dev/null +++ b/src/core/libraries/network/net_util.cpp @@ -0,0 +1,110 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#ifdef _WIN32 +#define _WINSOCK_DEPRECATED_NO_WARNINGS +#include +#include +#include +typedef SOCKET net_socket; +typedef int socklen_t; +#else +#include +#include +#include +#include +#include +#include +#include +#include +#include +typedef int net_socket; +#endif +#if defined(__APPLE__) +#include +#include +#endif + +#include +#include +#include +#include +#include +#include "net_util.h" + +namespace NetUtil { + +const std::array& NetUtilInternal::GetEthernetAddr() const { + return ether_address; +} + +bool NetUtilInternal::RetrieveEthernetAddr() { + std::scoped_lock lock{m_mutex}; +#ifdef _WIN32 + std::vector adapter_infos(sizeof(IP_ADAPTER_INFO)); + ULONG size_infos = sizeof(IP_ADAPTER_INFO); + + if (GetAdaptersInfo(reinterpret_cast(adapter_infos.data()), &size_infos) == + ERROR_BUFFER_OVERFLOW) + adapter_infos.resize(size_infos); + + if (GetAdaptersInfo(reinterpret_cast(adapter_infos.data()), &size_infos) == + NO_ERROR && + size_infos) { + PIP_ADAPTER_INFO info = reinterpret_cast(adapter_infos.data()); + memcpy(ether_address.data(), info[0].Address, 6); + return true; + } +#elif defined(__APPLE__) + ifaddrs* ifap; + + if (getifaddrs(&ifap) == 0) { + ifaddrs* p; + for (p = ifap; p; p = p->ifa_next) { + if (p->ifa_addr->sa_family == AF_LINK) { + sockaddr_dl* sdp = reinterpret_cast(p->ifa_addr); + memcpy(ether_address.data(), sdp->sdl_data + sdp->sdl_nlen, 6); + freeifaddrs(ifap); + return true; + } + } + freeifaddrs(ifap); + } +#else + ifreq ifr; + ifconf ifc; + char buf[1024]; + int success = 0; + + int sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP); + if (sock == -1) + return false; + + ifc.ifc_len = sizeof(buf); + ifc.ifc_buf = buf; + if (ioctl(sock, SIOCGIFCONF, &ifc) == -1) + return false; + + ifreq* it = ifc.ifc_req; + const ifreq* const end = it + (ifc.ifc_len / sizeof(ifreq)); + + for (; it != end; ++it) { + strcpy(ifr.ifr_name, it->ifr_name); + if (ioctl(sock, SIOCGIFFLAGS, &ifr) == 0) { + if (!(ifr.ifr_flags & IFF_LOOPBACK)) { + if (ioctl(sock, SIOCGIFHWADDR, &ifr) == 0) { + success = 1; + break; + } + } + } + } + + if (success) { + memcpy(ether_address.data(), ifr.ifr_hwaddr.sa_data, 6); + return true; + } +#endif + return false; +} +} // namespace NetUtil \ No newline at end of file diff --git a/src/core/libraries/network/net_util.h b/src/core/libraries/network/net_util.h new file mode 100644 index 000000000..be9dc15a1 --- /dev/null +++ b/src/core/libraries/network/net_util.h @@ -0,0 +1,24 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include "common/types.h" + +namespace NetUtil { + +class NetUtilInternal { +public: + explicit NetUtilInternal() = default; + ~NetUtilInternal() = default; + +private: + std::array ether_address{}; + std::mutex m_mutex; + +public: + const std::array& GetEthernetAddr() const; + bool RetrieveEthernetAddr(); +}; +} // namespace NetUtil \ No newline at end of file diff --git a/src/core/libraries/network/netctl.cpp b/src/core/libraries/network/netctl.cpp index 00d980663..38225c48c 100644 --- a/src/core/libraries/network/netctl.cpp +++ b/src/core/libraries/network/netctl.cpp @@ -12,11 +12,13 @@ #include #endif +#include #include "common/logging/log.h" #include "core/libraries/error_codes.h" #include "core/libraries/libs.h" #include "core/libraries/network/net_ctl_codes.h" #include "core/libraries/network/netctl.h" +#include "net_util.h" namespace Libraries::NetCtl { @@ -162,6 +164,14 @@ int PS4_SYSV_ABI sceNetCtlGetInfo(int code, OrbisNetCtlInfo* info) { case ORBIS_NET_CTL_INFO_DEVICE: info->device = ORBIS_NET_CTL_DEVICE_WIRED; break; + case ORBIS_NET_CTL_INFO_ETHER_ADDR: { + auto* netinfo = Common::Singleton::Instance(); + netinfo->RetrieveEthernetAddr(); + memcpy(info->ether_addr.data, netinfo->GetEthernetAddr().data(), 6); + } break; + case ORBIS_NET_CTL_INFO_MTU: + info->mtu = 1500; // default value + break; case ORBIS_NET_CTL_INFO_LINK: info->link = ORBIS_NET_CTL_LINK_DISCONNECTED; break; @@ -183,6 +193,7 @@ int PS4_SYSV_ABI sceNetCtlGetInfo(int code, OrbisNetCtlInfo* info) { } break; } + default: LOG_ERROR(Lib_NetCtl, "{} unsupported code", code); } diff --git a/src/core/libraries/network/netctl.h b/src/core/libraries/network/netctl.h index 4992fffa9..203c75822 100644 --- a/src/core/libraries/network/netctl.h +++ b/src/core/libraries/network/netctl.h @@ -49,8 +49,26 @@ union OrbisNetCtlInfo { // GetInfo codes constexpr int ORBIS_NET_CTL_INFO_DEVICE = 1; +constexpr int ORBIS_NET_CTL_INFO_ETHER_ADDR = 2; +constexpr int ORBIS_NET_CTL_INFO_MTU = 3; constexpr int ORBIS_NET_CTL_INFO_LINK = 4; +constexpr int ORBIS_NET_CTL_INFO_BSSID = 5; +constexpr int ORBIS_NET_CTL_INFO_SSID = 6; +constexpr int ORBIS_NET_CTL_INFO_WIFI_SECURITY = 7; +constexpr int ORBIS_NET_CTL_INFO_RSSI_DBM = 8; +constexpr int ORBIS_NET_CTL_INFO_RSSI_PERCENTAGE = 9; +constexpr int ORBIS_NET_CTL_INFO_CHANNEL = 10; +constexpr int ORBIS_NET_CTL_INFO_IP_CONFIG = 11; +constexpr int ORBIS_NET_CTL_INFO_DHCP_HOSTNAME = 12; +constexpr int ORBIS_NET_CTL_INFO_PPPOE_AUTH_NAME = 13; constexpr int ORBIS_NET_CTL_INFO_IP_ADDRESS = 14; +constexpr int ORBIS_NET_CTL_INFO_NETMASK = 15; +constexpr int ORBIS_NET_CTL_INFO_DEFAULT_ROUTE = 16; +constexpr int ORBIS_NET_CTL_INFO_PRIMARY_DNS = 17; +constexpr int ORBIS_NET_CTL_INFO_SECONDARY_DNS = 18; +constexpr int ORBIS_NET_CTL_INFO_HTTP_PROXY_CONFIG = 19; +constexpr int ORBIS_NET_CTL_INFO_HTTP_PROXY_SERVER = 20; +constexpr int ORBIS_NET_CTL_INFO_HTTP_PROXY_PORT = 21; int PS4_SYSV_ABI sceNetBweCheckCallbackIpcInt(); int PS4_SYSV_ABI sceNetBweClearEventIpcInt(); diff --git a/src/core/libraries/network/p2p_sockets.cpp b/src/core/libraries/network/p2p_sockets.cpp new file mode 100644 index 000000000..e9b710bb3 --- /dev/null +++ b/src/core/libraries/network/p2p_sockets.cpp @@ -0,0 +1,60 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include "net.h" +#include "net_error.h" +#include "sockets.h" + +namespace Libraries::Net { + +int P2PSocket::Close() { + LOG_ERROR(Lib_Net, "(STUBBED) called"); + return -1; +} +int P2PSocket::SetSocketOptions(int level, int optname, const void* optval, u32 optlen) { + LOG_ERROR(Lib_Net, "(STUBBED) called"); + return -1; +} +int P2PSocket::GetSocketOptions(int level, int optname, void* optval, u32* optlen) { + LOG_ERROR(Lib_Net, "(STUBBED) called"); + return -1; +} + +int P2PSocket::Bind(const OrbisNetSockaddr* addr, u32 addrlen) { + LOG_ERROR(Lib_Net, "(STUBBED) called"); + return -1; +} + +int P2PSocket::Listen(int backlog) { + LOG_ERROR(Lib_Net, "(STUBBED) called"); + return -1; +} + +int P2PSocket::SendPacket(const void* msg, u32 len, int flags, const OrbisNetSockaddr* to, + u32 tolen) { + LOG_ERROR(Lib_Net, "(STUBBED) called"); + return -1; +} + +int P2PSocket::ReceivePacket(void* buf, u32 len, int flags, OrbisNetSockaddr* from, u32* fromlen) { + LOG_ERROR(Lib_Net, "(STUBBED) called"); + return -1; +} + +SocketPtr P2PSocket::Accept(OrbisNetSockaddr* addr, u32* addrlen) { + LOG_ERROR(Lib_Net, "(STUBBED) called"); + return nullptr; +} + +int P2PSocket::Connect(const OrbisNetSockaddr* addr, u32 namelen) { + LOG_ERROR(Lib_Net, "(STUBBED) called"); + return -1; +} + +int P2PSocket::GetSocketAddress(OrbisNetSockaddr* name, u32* namelen) { + LOG_ERROR(Lib_Net, "(STUBBED) called"); + return -1; +} + +} // namespace Libraries::Net \ No newline at end of file diff --git a/src/core/libraries/network/posix_sockets.cpp b/src/core/libraries/network/posix_sockets.cpp new file mode 100644 index 000000000..140e4fd22 --- /dev/null +++ b/src/core/libraries/network/posix_sockets.cpp @@ -0,0 +1,359 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include "net.h" +#include "net_error.h" +#include "sockets.h" + +namespace Libraries::Net { + +#ifdef _WIN32 +#define ERROR_CASE(errname) \ + case (WSA##errname): \ + return ORBIS_NET_ERROR_##errname; +#else +#define ERROR_CASE(errname) \ + case (errname): \ + return ORBIS_NET_ERROR_##errname; +#endif + +static int ConvertReturnErrorCode(int retval) { + if (retval < 0) { +#ifdef _WIN32 + switch (WSAGetLastError()) { +#else + switch (errno) { +#endif +#ifndef _WIN32 // These errorcodes don't exist in WinSock + ERROR_CASE(EPERM) + ERROR_CASE(ENOENT) + // ERROR_CASE(ESRCH) + // ERROR_CASE(EIO) + // ERROR_CASE(ENXIO) + // ERROR_CASE(E2BIG) + // ERROR_CASE(ENOEXEC) + // ERROR_CASE(EDEADLK) + ERROR_CASE(ENOMEM) + // ERROR_CASE(ECHILD) + // ERROR_CASE(EBUSY) + ERROR_CASE(EEXIST) + // ERROR_CASE(EXDEV) + ERROR_CASE(ENODEV) + // ERROR_CASE(ENOTDIR) + // ERROR_CASE(EISDIR) + ERROR_CASE(ENFILE) + // ERROR_CASE(ENOTTY) + // ERROR_CASE(ETXTBSY) + // ERROR_CASE(EFBIG) + ERROR_CASE(ENOSPC) + // ERROR_CASE(ESPIPE) + // ERROR_CASE(EROFS) + // ERROR_CASE(EMLINK) + ERROR_CASE(EPIPE) + // ERROR_CASE(EDOM) + // ERROR_CASE(ERANGE) + // ERROR_CASE(ENOLCK) + // ERROR_CASE(ENOSYS) + // ERROR_CASE(EIDRM) + // ERROR_CASE(EOVERFLOW) + // ERROR_CASE(EILSEQ) + // ERROR_CASE(ENOTSUP) + ERROR_CASE(ECANCELED) + // ERROR_CASE(EBADMSG) + ERROR_CASE(ENODATA) + // ERROR_CASE(ENOSR) + // ERROR_CASE(ENOSTR) + // ERROR_CASE(ETIME) +#endif + ERROR_CASE(EINTR) + ERROR_CASE(EBADF) + ERROR_CASE(EACCES) + ERROR_CASE(EFAULT) + ERROR_CASE(EINVAL) + ERROR_CASE(EMFILE) + ERROR_CASE(EWOULDBLOCK) + ERROR_CASE(EINPROGRESS) + ERROR_CASE(EALREADY) + ERROR_CASE(ENOTSOCK) + ERROR_CASE(EDESTADDRREQ) + ERROR_CASE(EMSGSIZE) + ERROR_CASE(EPROTOTYPE) + ERROR_CASE(ENOPROTOOPT) + ERROR_CASE(EPROTONOSUPPORT) +#if defined(__APPLE__) || defined(_WIN32) + ERROR_CASE(EOPNOTSUPP) +#endif + ERROR_CASE(EAFNOSUPPORT) + ERROR_CASE(EADDRINUSE) + ERROR_CASE(EADDRNOTAVAIL) + ERROR_CASE(ENETDOWN) + ERROR_CASE(ENETUNREACH) + ERROR_CASE(ENETRESET) + ERROR_CASE(ECONNABORTED) + ERROR_CASE(ECONNRESET) + ERROR_CASE(ENOBUFS) + ERROR_CASE(EISCONN) + ERROR_CASE(ENOTCONN) + ERROR_CASE(ETIMEDOUT) + ERROR_CASE(ECONNREFUSED) + ERROR_CASE(ELOOP) + ERROR_CASE(ENAMETOOLONG) + ERROR_CASE(EHOSTUNREACH) + ERROR_CASE(ENOTEMPTY) + } + return ORBIS_NET_ERROR_EINTERNAL; + } + // if it is 0 or positive return it as it is + return retval; +} + +static int ConvertLevels(int level) { + switch (level) { + case ORBIS_NET_SOL_SOCKET: + return SOL_SOCKET; + case ORBIS_NET_IPPROTO_IP: + return IPPROTO_IP; + case ORBIS_NET_IPPROTO_TCP: + return IPPROTO_TCP; + } + return -1; +} + +static void convertOrbisNetSockaddrToPosix(const OrbisNetSockaddr* src, sockaddr* dst) { + if (src == nullptr || dst == nullptr) + return; + memset(dst, 0, sizeof(sockaddr)); + const OrbisNetSockaddrIn* src_in = (const OrbisNetSockaddrIn*)src; + sockaddr_in* dst_in = (sockaddr_in*)dst; + dst_in->sin_family = src_in->sin_family; + dst_in->sin_port = src_in->sin_port; + memcpy(&dst_in->sin_addr, &src_in->sin_addr, 4); +} + +static void convertPosixSockaddrToOrbis(sockaddr* src, OrbisNetSockaddr* dst) { + if (src == nullptr || dst == nullptr) + return; + memset(dst, 0, sizeof(OrbisNetSockaddr)); + OrbisNetSockaddrIn* dst_in = (OrbisNetSockaddrIn*)dst; + sockaddr_in* src_in = (sockaddr_in*)src; + dst_in->sin_family = static_cast(src_in->sin_family); + dst_in->sin_port = src_in->sin_port; + memcpy(&dst_in->sin_addr, &src_in->sin_addr, 4); +} + +int PosixSocket::Close() { +#ifdef _WIN32 + auto out = closesocket(sock); +#else + auto out = ::close(sock); +#endif + return ConvertReturnErrorCode(out); +} + +int PosixSocket::Bind(const OrbisNetSockaddr* addr, u32 addrlen) { + sockaddr addr2; + convertOrbisNetSockaddrToPosix(addr, &addr2); + return ConvertReturnErrorCode(::bind(sock, &addr2, sizeof(sockaddr_in))); +} + +int PosixSocket::Listen(int backlog) { + return ConvertReturnErrorCode(::listen(sock, backlog)); +} + +int PosixSocket::SendPacket(const void* msg, u32 len, int flags, const OrbisNetSockaddr* to, + u32 tolen) { + if (to != nullptr) { + sockaddr addr; + convertOrbisNetSockaddrToPosix(to, &addr); + return ConvertReturnErrorCode( + sendto(sock, (const char*)msg, len, flags, &addr, sizeof(sockaddr_in))); + } else { + return ConvertReturnErrorCode(send(sock, (const char*)msg, len, flags)); + } +} + +int PosixSocket::ReceivePacket(void* buf, u32 len, int flags, OrbisNetSockaddr* from, + u32* fromlen) { + if (from != nullptr) { + sockaddr addr; + int res = recvfrom(sock, (char*)buf, len, flags, &addr, (socklen_t*)fromlen); + convertPosixSockaddrToOrbis(&addr, from); + *fromlen = sizeof(OrbisNetSockaddrIn); + return ConvertReturnErrorCode(res); + } else { + return ConvertReturnErrorCode(recv(sock, (char*)buf, len, flags)); + } +} + +SocketPtr PosixSocket::Accept(OrbisNetSockaddr* addr, u32* addrlen) { + sockaddr addr2; + net_socket new_socket = ::accept(sock, &addr2, (socklen_t*)addrlen); +#ifdef _WIN32 + if (new_socket != INVALID_SOCKET) { +#else + if (new_socket >= 0) { +#endif + convertPosixSockaddrToOrbis(&addr2, addr); + *addrlen = sizeof(OrbisNetSockaddrIn); + return std::make_shared(new_socket); + } + return nullptr; +} + +int PosixSocket::Connect(const OrbisNetSockaddr* addr, u32 namelen) { + sockaddr addr2; + convertOrbisNetSockaddrToPosix(addr, &addr2); + return ::connect(sock, &addr2, sizeof(sockaddr_in)); +} + +int PosixSocket::GetSocketAddress(OrbisNetSockaddr* name, u32* namelen) { + sockaddr addr; + convertOrbisNetSockaddrToPosix(name, &addr); + if (name != nullptr) { + *namelen = sizeof(sockaddr_in); + } + int res = getsockname(sock, &addr, (socklen_t*)namelen); + if (res >= 0) { + convertPosixSockaddrToOrbis(&addr, name); + *namelen = sizeof(OrbisNetSockaddrIn); + } + return res; +} + +#define CASE_SETSOCKOPT(opt) \ + case ORBIS_NET_##opt: \ + return ConvertReturnErrorCode(setsockopt(sock, level, opt, (const char*)optval, optlen)) + +#define CASE_SETSOCKOPT_VALUE(opt, value) \ + case opt: \ + if (optlen != sizeof(*value)) { \ + return ORBIS_NET_ERROR_EFAULT; \ + } \ + memcpy(value, optval, optlen); \ + return 0 + +int PosixSocket::SetSocketOptions(int level, int optname, const void* optval, u32 optlen) { + level = ConvertLevels(level); + if (level == SOL_SOCKET) { + switch (optname) { + CASE_SETSOCKOPT(SO_REUSEADDR); + CASE_SETSOCKOPT(SO_KEEPALIVE); + CASE_SETSOCKOPT(SO_BROADCAST); + CASE_SETSOCKOPT(SO_LINGER); + CASE_SETSOCKOPT(SO_SNDBUF); + CASE_SETSOCKOPT(SO_RCVBUF); + CASE_SETSOCKOPT(SO_SNDTIMEO); + CASE_SETSOCKOPT(SO_RCVTIMEO); + CASE_SETSOCKOPT(SO_ERROR); + CASE_SETSOCKOPT(SO_TYPE); + CASE_SETSOCKOPT_VALUE(ORBIS_NET_SO_REUSEPORT, &sockopt_so_reuseport); + CASE_SETSOCKOPT_VALUE(ORBIS_NET_SO_ONESBCAST, &sockopt_so_onesbcast); + CASE_SETSOCKOPT_VALUE(ORBIS_NET_SO_USECRYPTO, &sockopt_so_usecrypto); + CASE_SETSOCKOPT_VALUE(ORBIS_NET_SO_USESIGNATURE, &sockopt_so_usesignature); + case ORBIS_NET_SO_NAME: + return ORBIS_NET_ERROR_EINVAL; // don't support set for name + case ORBIS_NET_SO_NBIO: { + if (optlen != sizeof(sockopt_so_nbio)) { + return ORBIS_NET_ERROR_EFAULT; + } + memcpy(&sockopt_so_nbio, optval, optlen); +#ifdef _WIN32 + static_assert(sizeof(u_long) == sizeof(sockopt_so_nbio), + "type used for ioctlsocket value does not have the expected size"); + return ConvertReturnErrorCode(ioctlsocket(sock, FIONBIO, (u_long*)&sockopt_so_nbio)); +#else + return ConvertReturnErrorCode(ioctl(sock, FIONBIO, &sockopt_so_nbio)); +#endif + } + } + } else if (level == IPPROTO_IP) { + switch (optname) { + CASE_SETSOCKOPT(IP_HDRINCL); + CASE_SETSOCKOPT(IP_TOS); + CASE_SETSOCKOPT(IP_TTL); + CASE_SETSOCKOPT(IP_MULTICAST_IF); + CASE_SETSOCKOPT(IP_MULTICAST_TTL); + CASE_SETSOCKOPT(IP_MULTICAST_LOOP); + CASE_SETSOCKOPT(IP_ADD_MEMBERSHIP); + CASE_SETSOCKOPT(IP_DROP_MEMBERSHIP); + CASE_SETSOCKOPT_VALUE(ORBIS_NET_IP_TTLCHK, &sockopt_ip_ttlchk); + CASE_SETSOCKOPT_VALUE(ORBIS_NET_IP_MAXTTL, &sockopt_ip_maxttl); + } + } else if (level == IPPROTO_TCP) { + switch (optname) { + CASE_SETSOCKOPT(TCP_NODELAY); + CASE_SETSOCKOPT(TCP_MAXSEG); + CASE_SETSOCKOPT_VALUE(ORBIS_NET_TCP_MSS_TO_ADVERTISE, &sockopt_tcp_mss_to_advertise); + } + } + + UNREACHABLE_MSG("Unknown level ={} optname ={}", level, optname); + return 0; +} + +#define CASE_GETSOCKOPT(opt) \ + case ORBIS_NET_##opt: { \ + socklen_t optlen_temp = *optlen; \ + auto retval = \ + ConvertReturnErrorCode(getsockopt(sock, level, opt, (char*)optval, &optlen_temp)); \ + *optlen = optlen_temp; \ + return retval; \ + } +#define CASE_GETSOCKOPT_VALUE(opt, value) \ + case opt: \ + if (*optlen < sizeof(value)) { \ + *optlen = sizeof(value); \ + return ORBIS_NET_ERROR_EFAULT; \ + } \ + *optlen = sizeof(value); \ + *(decltype(value)*)optval = value; \ + return 0; + +int PosixSocket::GetSocketOptions(int level, int optname, void* optval, u32* optlen) { + level = ConvertLevels(level); + if (level == SOL_SOCKET) { + switch (optname) { + CASE_GETSOCKOPT(SO_REUSEADDR); + CASE_GETSOCKOPT(SO_KEEPALIVE); + CASE_GETSOCKOPT(SO_BROADCAST); + CASE_GETSOCKOPT(SO_LINGER); + CASE_GETSOCKOPT(SO_SNDBUF); + CASE_GETSOCKOPT(SO_RCVBUF); + CASE_GETSOCKOPT(SO_SNDTIMEO); + CASE_GETSOCKOPT(SO_RCVTIMEO); + CASE_GETSOCKOPT(SO_ERROR); + CASE_GETSOCKOPT(SO_TYPE); + CASE_GETSOCKOPT_VALUE(ORBIS_NET_SO_NBIO, sockopt_so_nbio); + CASE_GETSOCKOPT_VALUE(ORBIS_NET_SO_REUSEPORT, sockopt_so_reuseport); + CASE_GETSOCKOPT_VALUE(ORBIS_NET_SO_ONESBCAST, sockopt_so_onesbcast); + CASE_GETSOCKOPT_VALUE(ORBIS_NET_SO_USECRYPTO, sockopt_so_usecrypto); + CASE_GETSOCKOPT_VALUE(ORBIS_NET_SO_USESIGNATURE, sockopt_so_usesignature); + CASE_GETSOCKOPT_VALUE(ORBIS_NET_SO_NAME, + (char)0); // writes an empty string to the output buffer + } + } else if (level == IPPROTO_IP) { + switch (optname) { + CASE_GETSOCKOPT(IP_HDRINCL); + CASE_GETSOCKOPT(IP_TOS); + CASE_GETSOCKOPT(IP_TTL); + CASE_GETSOCKOPT(IP_MULTICAST_IF); + CASE_GETSOCKOPT(IP_MULTICAST_TTL); + CASE_GETSOCKOPT(IP_MULTICAST_LOOP); + CASE_GETSOCKOPT(IP_ADD_MEMBERSHIP); + CASE_GETSOCKOPT(IP_DROP_MEMBERSHIP); + CASE_GETSOCKOPT_VALUE(ORBIS_NET_IP_TTLCHK, sockopt_ip_ttlchk); + CASE_GETSOCKOPT_VALUE(ORBIS_NET_IP_MAXTTL, sockopt_ip_maxttl); + } + } else if (level == IPPROTO_TCP) { + switch (optname) { + CASE_GETSOCKOPT(TCP_NODELAY); + CASE_GETSOCKOPT(TCP_MAXSEG); + CASE_GETSOCKOPT_VALUE(ORBIS_NET_TCP_MSS_TO_ADVERTISE, sockopt_tcp_mss_to_advertise); + } + } + UNREACHABLE_MSG("Unknown level ={} optname ={}", level, optname); + return 0; +} + +} // namespace Libraries::Net \ No newline at end of file diff --git a/src/core/libraries/network/sockets.h b/src/core/libraries/network/sockets.h new file mode 100644 index 000000000..e41671d88 --- /dev/null +++ b/src/core/libraries/network/sockets.h @@ -0,0 +1,112 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#ifdef _WIN32 +#define _WINSOCK_DEPRECATED_NO_WARNINGS +#include +#include +#include +typedef SOCKET net_socket; +typedef int socklen_t; +#else +#include +#include +#include +#include +#include +#include +#include +#include +typedef int net_socket; +#endif +#include +#include +#include +#include "net.h" + +namespace Libraries::Net { + +struct Socket; + +typedef std::shared_ptr SocketPtr; + +struct Socket { + explicit Socket(int domain, int type, int protocol) {} + virtual ~Socket() = default; + virtual int Close() = 0; + virtual int SetSocketOptions(int level, int optname, const void* optval, u32 optlen) = 0; + virtual int GetSocketOptions(int level, int optname, void* optval, u32* optlen) = 0; + virtual int Bind(const OrbisNetSockaddr* addr, u32 addrlen) = 0; + virtual int Listen(int backlog) = 0; + virtual int SendPacket(const void* msg, u32 len, int flags, const OrbisNetSockaddr* to, + u32 tolen) = 0; + virtual SocketPtr Accept(OrbisNetSockaddr* addr, u32* addrlen) = 0; + virtual int ReceivePacket(void* buf, u32 len, int flags, OrbisNetSockaddr* from, + u32* fromlen) = 0; + virtual int Connect(const OrbisNetSockaddr* addr, u32 namelen) = 0; + virtual int GetSocketAddress(OrbisNetSockaddr* name, u32* namelen) = 0; +}; + +struct PosixSocket : public Socket { + net_socket sock; + int sockopt_so_reuseport = 0; + int sockopt_so_onesbcast = 0; + int sockopt_so_usecrypto = 0; + int sockopt_so_usesignature = 0; + int sockopt_so_nbio = 0; + int sockopt_ip_ttlchk = 0; + int sockopt_ip_maxttl = 0; + int sockopt_tcp_mss_to_advertise = 0; + explicit PosixSocket(int domain, int type, int protocol) + : Socket(domain, type, protocol), sock(socket(domain, type, protocol)) {} + explicit PosixSocket(net_socket sock) : Socket(0, 0, 0), sock(sock) {} + int Close() override; + int SetSocketOptions(int level, int optname, const void* optval, u32 optlen) override; + int GetSocketOptions(int level, int optname, void* optval, u32* optlen) override; + int Bind(const OrbisNetSockaddr* addr, u32 addrlen) override; + int Listen(int backlog) override; + int SendPacket(const void* msg, u32 len, int flags, const OrbisNetSockaddr* to, + u32 tolen) override; + int ReceivePacket(void* buf, u32 len, int flags, OrbisNetSockaddr* from, u32* fromlen) override; + SocketPtr Accept(OrbisNetSockaddr* addr, u32* addrlen) override; + int Connect(const OrbisNetSockaddr* addr, u32 namelen) override; + int GetSocketAddress(OrbisNetSockaddr* name, u32* namelen) override; +}; + +struct P2PSocket : public Socket { + explicit P2PSocket(int domain, int type, int protocol) : Socket(domain, type, protocol) {} + int Close() override; + int SetSocketOptions(int level, int optname, const void* optval, u32 optlen) override; + int GetSocketOptions(int level, int optname, void* optval, u32* optlen) override; + int Bind(const OrbisNetSockaddr* addr, u32 addrlen) override; + int Listen(int backlog) override; + int SendPacket(const void* msg, u32 len, int flags, const OrbisNetSockaddr* to, + u32 tolen) override; + int ReceivePacket(void* buf, u32 len, int flags, OrbisNetSockaddr* from, u32* fromlen) override; + SocketPtr Accept(OrbisNetSockaddr* addr, u32* addrlen) override; + int Connect(const OrbisNetSockaddr* addr, u32 namelen) override; + int GetSocketAddress(OrbisNetSockaddr* name, u32* namelen) override; +}; + +class NetInternal { +public: + explicit NetInternal() = default; + ~NetInternal() = default; + SocketPtr FindSocket(int sockid) { + std::scoped_lock lock{m_mutex}; + const auto it = socks.find(sockid); + if (it != socks.end()) { + return it->second; + } + return 0; + } + +public: + std::mutex m_mutex; + typedef std::map NetSockets; + NetSockets socks; + int next_sock_id = 0; +}; +} // namespace Libraries::Net \ No newline at end of file diff --git a/src/core/libraries/network/sys_net.cpp b/src/core/libraries/network/sys_net.cpp new file mode 100644 index 000000000..fbf2a2456 --- /dev/null +++ b/src/core/libraries/network/sys_net.cpp @@ -0,0 +1,229 @@ +#include "sys_net.h" +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include +#include +#include "common/singleton.h" +#include "net_error.h" +#include "sockets.h" +#include "sys_net.h" + +namespace Libraries::Net { + +int PS4_SYSV_ABI sys_connect(OrbisNetId s, const OrbisNetSockaddr* addr, u32 addrlen) { + auto* netcall = Common::Singleton::Instance(); + auto sock = netcall->FindSocket(s); + if (!sock) { + *Libraries::Kernel::__Error() = ORBIS_NET_ERROR_EBADF; + LOG_ERROR(Lib_Net, "socket id is invalid = {}", s); + return -1; + } + int returncode = sock->Connect(addr, addrlen); + if (returncode >= 0) { + return returncode; + } + *Libraries::Kernel::__Error() = returncode; + LOG_ERROR(Lib_Net, "error code returned : {:#x}", (u32)returncode); + return -1; +} +int PS4_SYSV_ABI sys_bind(OrbisNetId s, const OrbisNetSockaddr* addr, u32 addrlen) { + auto* netcall = Common::Singleton::Instance(); + auto sock = netcall->FindSocket(s); + if (!sock) { + *Libraries::Kernel::__Error() = ORBIS_NET_ERROR_EBADF; + LOG_ERROR(Lib_Net, "socket id is invalid = {}", s); + return -1; + } + int returncode = sock->Bind(addr, addrlen); + if (returncode >= 0) { + return returncode; + } + *Libraries::Kernel::__Error() = returncode; + LOG_ERROR(Lib_Net, "error code returned : {:#x}", (u32)returncode); + return -1; +} +int PS4_SYSV_ABI sys_accept(OrbisNetId s, OrbisNetSockaddr* addr, u32* paddrlen) { + auto* netcall = Common::Singleton::Instance(); + auto sock = netcall->FindSocket(s); + if (!sock) { + *Libraries::Kernel::__Error() = ORBIS_NET_EBADF; + LOG_ERROR(Lib_Net, "socket id is invalid = {}", s); + return -1; + } + auto new_sock = sock->Accept(addr, paddrlen); + if (!new_sock) { + *Libraries::Kernel::__Error() = ORBIS_NET_EBADF; + LOG_ERROR(Lib_Net, "error creating new socket for accepting"); + return -1; + } + auto id = ++netcall->next_sock_id; + netcall->socks.emplace(id, new_sock); + return id; +} +int PS4_SYSV_ABI sys_getpeername(OrbisNetId s, const OrbisNetSockaddr* addr, u32* paddrlen) { + LOG_ERROR(Lib_Net, "(STUBBED) called"); + return -1; +} +int PS4_SYSV_ABI sys_getsockname(OrbisNetId s, OrbisNetSockaddr* addr, u32* paddrlen) { + auto* netcall = Common::Singleton::Instance(); + auto sock = netcall->FindSocket(s); + if (!sock) { + *Libraries::Kernel::__Error() = ORBIS_NET_ERROR_EBADF; + LOG_ERROR(Lib_Net, "socket id is invalid = {}", s); + return -1; + } + int returncode = sock->GetSocketAddress(addr, paddrlen); + if (returncode >= 0) { + return returncode; + } + *Libraries::Kernel::__Error() = returncode; + LOG_ERROR(Lib_Net, "error code returned : {:#x}", (u32)returncode); + return -1; +} +int PS4_SYSV_ABI sys_getsockopt(OrbisNetId s, int level, int optname, void* optval, u32* optlen) { + auto* netcall = Common::Singleton::Instance(); + auto sock = netcall->FindSocket(s); + if (!sock) { + *Libraries::Kernel::__Error() = ORBIS_NET_ERROR_EBADF; + LOG_ERROR(Lib_Net, "socket id is invalid = {}", s); + return -1; + } + int returncode = sock->GetSocketOptions(level, optname, optval, optlen); + if (returncode >= 0) { + return returncode; + } + *Libraries::Kernel::__Error() = returncode; + LOG_ERROR(Lib_Net, "error code returned : {:#x}", (u32)returncode); + return -1; +} +int PS4_SYSV_ABI sys_listen(OrbisNetId s, int backlog) { + auto* netcall = Common::Singleton::Instance(); + auto sock = netcall->FindSocket(s); + if (!sock) { + *Libraries::Kernel::__Error() = ORBIS_NET_ERROR_EBADF; + LOG_ERROR(Lib_Net, "socket id is invalid = {}", s); + return -1; + } + int returncode = sock->Listen(backlog); + if (returncode >= 0) { + return returncode; + } + *Libraries::Kernel::__Error() = returncode; + LOG_ERROR(Lib_Net, "error code returned : {:#x}", (u32)returncode); + return -1; +} +int PS4_SYSV_ABI sys_setsockopt(OrbisNetId s, int level, int optname, const void* optval, + u32 optlen) { + auto* netcall = Common::Singleton::Instance(); + auto sock = netcall->FindSocket(s); + if (!sock) { + *Libraries::Kernel::__Error() = ORBIS_NET_ERROR_EBADF; + LOG_ERROR(Lib_Net, "socket id is invalid = {}", s); + return -1; + } + int returncode = sock->SetSocketOptions(level, optname, optval, optlen); + if (returncode >= 0) { + return returncode; + } + *Libraries::Kernel::__Error() = returncode; + LOG_ERROR(Lib_Net, "error code returned : {:#x}", (u32)returncode); + return -1; +} +int PS4_SYSV_ABI sys_shutdown(OrbisNetId s, int how) { + return -1; +} +int PS4_SYSV_ABI sys_socketex(const char* name, int family, int type, int protocol) { + if (name == nullptr) { + LOG_INFO(Lib_Net, "name = no-named family = {} type = {} protocol = {}", family, type, + protocol); + } else { + LOG_INFO(Lib_Net, "name = {} family = {} type = {} protocol = {}", std::string(name), + family, type, protocol); + } + SocketPtr sock; + switch (type) { + case ORBIS_NET_SOCK_STREAM: + case ORBIS_NET_SOCK_DGRAM: + case ORBIS_NET_SOCK_RAW: + sock = std::make_shared(family, type, protocol); + break; + case ORBIS_NET_SOCK_DGRAM_P2P: + case ORBIS_NET_SOCK_STREAM_P2P: + sock = std::make_shared(family, type, protocol); + break; + default: + UNREACHABLE_MSG("Unknown type {}", type); + } + auto* netcall = Common::Singleton::Instance(); + auto id = ++netcall->next_sock_id; + netcall->socks.emplace(id, sock); + return id; +} +int PS4_SYSV_ABI sys_socket(int family, int type, int protocol) { + return sys_socketex(nullptr, family, type, protocol); +} +int PS4_SYSV_ABI sys_netabort(OrbisNetId s, int flags) { + LOG_ERROR(Lib_Net, "(STUBBED) called"); + return -1; +} +int PS4_SYSV_ABI sys_socketclose(OrbisNetId s) { + auto* netcall = Common::Singleton::Instance(); + auto sock = netcall->FindSocket(s); + if (!sock) { + *Libraries::Kernel::__Error() = ORBIS_NET_ERROR_EBADF; + LOG_ERROR(Lib_Net, "socket id is invalid = {}", s); + return -1; + } + int returncode = sock->Close(); + if (returncode >= 0) { + return returncode; + } + *Libraries::Kernel::__Error() = returncode; + LOG_ERROR(Lib_Net, "error code returned : {:#x}", (u32)returncode); + return -1; +} +int PS4_SYSV_ABI sys_sendto(OrbisNetId s, const void* buf, u64 len, int flags, + const OrbisNetSockaddr* addr, u32 addrlen) { + auto* netcall = Common::Singleton::Instance(); + auto sock = netcall->FindSocket(s); + if (!sock) { + *Libraries::Kernel::__Error() = ORBIS_NET_ERROR_EBADF; + LOG_ERROR(Lib_Net, "socket id is invalid = {}", s); + return -1; + } + int returncode = sock->SendPacket(buf, len, flags, addr, addrlen); + if (returncode >= 0) { + return returncode; + } + *Libraries::Kernel::__Error() = returncode; + LOG_ERROR(Lib_Net, "error code returned : {:#x}", (u32)returncode); + return -1; +} +int PS4_SYSV_ABI sys_sendmsg(OrbisNetId s, const OrbisNetMsghdr* msg, int flags) { + LOG_ERROR(Lib_Net, "(STUBBED) called"); + return -1; +} +int PS4_SYSV_ABI sys_recvfrom(OrbisNetId s, void* buf, u64 len, int flags, OrbisNetSockaddr* addr, + u32* paddrlen) { + auto* netcall = Common::Singleton::Instance(); + auto sock = netcall->FindSocket(s); + if (!sock) { + *Libraries::Kernel::__Error() = ORBIS_NET_ERROR_EBADF; + LOG_ERROR(Lib_Net, "socket id is invalid = {}", s); + return -1; + } + int returncode = sock->ReceivePacket(buf, len, flags, addr, paddrlen); + if (returncode >= 0) { + return returncode; + } + *Libraries::Kernel::__Error() = returncode; + LOG_ERROR(Lib_Net, "error code returned : {:#x}", (u32)returncode); + return -1; +} +int PS4_SYSV_ABI sys_recvmsg(OrbisNetId s, OrbisNetMsghdr* msg, int flags) { + LOG_ERROR(Lib_Net, "(STUBBED) called"); + return -1; +} +} // namespace Libraries::Net \ No newline at end of file diff --git a/src/core/libraries/network/sys_net.h b/src/core/libraries/network/sys_net.h new file mode 100644 index 000000000..4366ea0f8 --- /dev/null +++ b/src/core/libraries/network/sys_net.h @@ -0,0 +1,31 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/types.h" +#include "net.h" + +namespace Libraries::Net { + +int PS4_SYSV_ABI sys_connect(OrbisNetId s, const OrbisNetSockaddr* addr, u32 addrlen); +int PS4_SYSV_ABI sys_bind(OrbisNetId s, const OrbisNetSockaddr* addr, u32 addrlen); +int PS4_SYSV_ABI sys_accept(OrbisNetId s, OrbisNetSockaddr* addr, u32* paddrlen); +int PS4_SYSV_ABI sys_getpeername(OrbisNetId s, const OrbisNetSockaddr* addr, u32* paddrlen); +int PS4_SYSV_ABI sys_getsockname(OrbisNetId s, OrbisNetSockaddr* addr, u32* paddrlen); +int PS4_SYSV_ABI sys_getsockopt(OrbisNetId s, int level, int optname, void* optval, u32* optlen); +int PS4_SYSV_ABI sys_listen(OrbisNetId s, int backlog); +int PS4_SYSV_ABI sys_setsockopt(OrbisNetId s, int level, int optname, const void* optval, + u32 optlen); +int PS4_SYSV_ABI sys_shutdown(OrbisNetId s, int how); +int PS4_SYSV_ABI sys_socketex(const char* name, int family, int type, int protocol); +int PS4_SYSV_ABI sys_socket(int family, int type, int protocol); +int PS4_SYSV_ABI sys_netabort(OrbisNetId s, int flags); +int PS4_SYSV_ABI sys_socketclose(OrbisNetId s); +int PS4_SYSV_ABI sys_sendto(OrbisNetId s, const void* buf, u64 len, int flags, + const OrbisNetSockaddr* addr, u32 addrlen); +int PS4_SYSV_ABI sys_sendmsg(OrbisNetId s, const OrbisNetMsghdr* msg, int flags); +int PS4_SYSV_ABI sys_recvfrom(OrbisNetId s, void* buf, u64 len, int flags, OrbisNetSockaddr* addr, + u32* paddrlen); +int PS4_SYSV_ABI sys_recvmsg(OrbisNetId s, OrbisNetMsghdr* msg, int flags); +} // namespace Libraries::Net \ No newline at end of file diff --git a/src/core/libraries/save_data/dialog/savedatadialog_ui.cpp b/src/core/libraries/save_data/dialog/savedatadialog_ui.cpp index a6ca8744d..edb5caa07 100644 --- a/src/core/libraries/save_data/dialog/savedatadialog_ui.cpp +++ b/src/core/libraries/save_data/dialog/savedatadialog_ui.cpp @@ -49,13 +49,11 @@ void SaveDialogResult::CopyTo(OrbisSaveDataDialogResult& result) const { result.mode = this->mode; result.result = this->result; result.buttonId = this->button_id; - if (mode == SaveDataDialogMode::LIST || ElfInfo::Instance().FirmwareVer() >= ElfInfo::FW_45) { - if (result.dirName != nullptr) { - result.dirName->data.FromString(this->dir_name); - } - if (result.param != nullptr && this->param.GetString(SaveParams::MAINTITLE).has_value()) { - result.param->FromSFO(this->param); - } + if (result.dirName != nullptr) { + result.dirName->data.FromString(this->dir_name); + } + if (result.param != nullptr && this->param.GetString(SaveParams::MAINTITLE).has_value()) { + result.param->FromSFO(this->param); } result.userData = this->user_data; } @@ -345,12 +343,15 @@ SaveDialogUi::SaveDialogUi(SaveDialogUi&& other) noexcept } } -SaveDialogUi& SaveDialogUi::operator=(SaveDialogUi other) { +SaveDialogUi& SaveDialogUi::operator=(SaveDialogUi&& other) noexcept { std::scoped_lock lock(draw_mutex, other.draw_mutex); using std::swap; - swap(state, other.state); - swap(status, other.status); - swap(result, other.result); + state = other.state; + other.state = nullptr; + status = other.status; + other.status = nullptr; + result = other.result; + other.result = nullptr; if (status && *status == Status::RUNNING) { first_render = true; AddLayer(this); diff --git a/src/core/libraries/save_data/dialog/savedatadialog_ui.h b/src/core/libraries/save_data/dialog/savedatadialog_ui.h index aa67e1f5f..dc97268f4 100644 --- a/src/core/libraries/save_data/dialog/savedatadialog_ui.h +++ b/src/core/libraries/save_data/dialog/savedatadialog_ui.h @@ -300,7 +300,8 @@ public: ~SaveDialogUi() override; SaveDialogUi(const SaveDialogUi& other) = delete; SaveDialogUi(SaveDialogUi&& other) noexcept; - SaveDialogUi& operator=(SaveDialogUi other); + SaveDialogUi& operator=(SaveDialogUi& other) = delete; + SaveDialogUi& operator=(SaveDialogUi&& other) noexcept; void Finish(ButtonId buttonId, CommonDialog::Result r = CommonDialog::Result::OK); diff --git a/src/core/libraries/signin_dialog/signindialog.cpp b/src/core/libraries/signin_dialog/signindialog.cpp new file mode 100644 index 000000000..0e4eb63a2 --- /dev/null +++ b/src/core/libraries/signin_dialog/signindialog.cpp @@ -0,0 +1,64 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +// Generated By moduleGenerator +#include "common/logging/log.h" +#include "core/libraries/error_codes.h" +#include "core/libraries/libs.h" +#include "signindialog.h" + +namespace Libraries::SigninDialog { + +s32 PS4_SYSV_ABI sceSigninDialogInitialize() { + LOG_ERROR(Lib_SigninDialog, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceSigninDialogOpen() { + LOG_ERROR(Lib_SigninDialog, "(STUBBED) called"); + return ORBIS_OK; +} + +Status PS4_SYSV_ABI sceSigninDialogGetStatus() { + LOG_ERROR(Lib_SigninDialog, "(STUBBED) called, return 'finished' status"); + return Status::FINISHED; +} + +Status PS4_SYSV_ABI sceSigninDialogUpdateStatus() { + LOG_ERROR(Lib_SigninDialog, "(STUBBED) called, return 'finished' status"); + return Status::FINISHED; +} + +s32 PS4_SYSV_ABI sceSigninDialogGetResult() { + LOG_ERROR(Lib_SigninDialog, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceSigninDialogClose() { + LOG_ERROR(Lib_SigninDialog, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceSigninDialogTerminate() { + LOG_ERROR(Lib_SigninDialog, "(STUBBED) called"); + return ORBIS_OK; +} + +void RegisterlibSceSigninDialog(Core::Loader::SymbolsResolver* sym) { + LIB_FUNCTION("mlYGfmqE3fQ", "libSceSigninDialog", 1, "libSceSigninDialog", 1, 1, + sceSigninDialogInitialize); + LIB_FUNCTION("JlpJVoRWv7U", "libSceSigninDialog", 1, "libSceSigninDialog", 1, 1, + sceSigninDialogOpen); + LIB_FUNCTION("2m077aeC+PA", "libSceSigninDialog", 1, "libSceSigninDialog", 1, 1, + sceSigninDialogGetStatus); + LIB_FUNCTION("Bw31liTFT3A", "libSceSigninDialog", 1, "libSceSigninDialog", 1, 1, + sceSigninDialogUpdateStatus); + LIB_FUNCTION("nqG7rqnYw1U", "libSceSigninDialog", 1, "libSceSigninDialog", 1, 1, + sceSigninDialogGetResult); + LIB_FUNCTION("M3OkENHcyiU", "libSceSigninDialog", 1, "libSceSigninDialog", 1, 1, + sceSigninDialogClose); + LIB_FUNCTION("LXlmS6PvJdU", "libSceSigninDialog", 1, "libSceSigninDialog", 1, 1, + sceSigninDialogTerminate); +}; + +} // namespace Libraries::SigninDialog diff --git a/src/core/libraries/signin_dialog/signindialog.h b/src/core/libraries/signin_dialog/signindialog.h new file mode 100644 index 000000000..8726ad1f6 --- /dev/null +++ b/src/core/libraries/signin_dialog/signindialog.h @@ -0,0 +1,29 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once +#include "common/types.h" + +namespace Core::Loader { +class SymbolsResolver; +} + +enum class Status : u32 { + NONE = 0, + INITIALIZED = 1, + RUNNING = 2, + FINISHED = 3, +}; + +namespace Libraries::SigninDialog { + +s32 PS4_SYSV_ABI sceSigninDialogInitialize(); +s32 PS4_SYSV_ABI sceSigninDialogOpen(); +Status PS4_SYSV_ABI sceSigninDialogGetStatus(); +Status PS4_SYSV_ABI sceSigninDialogUpdateStatus(); +s32 PS4_SYSV_ABI sceSigninDialogGetResult(); +s32 PS4_SYSV_ABI sceSigninDialogClose(); +s32 PS4_SYSV_ABI sceSigninDialogTerminate(); + +void RegisterlibSceSigninDialog(Core::Loader::SymbolsResolver* sym); +} // namespace Libraries::SigninDialog diff --git a/src/core/libraries/system/sysmodule.cpp b/src/core/libraries/system/sysmodule.cpp index 6c73764f2..d9e78e4ab 100644 --- a/src/core/libraries/system/sysmodule.cpp +++ b/src/core/libraries/system/sysmodule.cpp @@ -19,11 +19,40 @@ int PS4_SYSV_ABI sceSysmoduleGetModuleHandleInternal() { return ORBIS_OK; } -s32 PS4_SYSV_ABI sceSysmoduleGetModuleInfoForUnwind(VAddr addr, s32 flags, void* info) { - LOG_ERROR(Lib_SysModule, "(STUBBED) called"); - Kernel::OrbisModuleInfoForUnwind module_info; - module_info.st_size = 0x130; - s32 res = Kernel::sceKernelGetModuleInfoForUnwind(addr, flags, &module_info); +s32 PS4_SYSV_ABI sceSysmoduleGetModuleInfoForUnwind(VAddr addr, s32 flags, + Kernel::OrbisModuleInfoForUnwind* info) { + LOG_TRACE(Lib_SysModule, "sceSysmoduleGetModuleInfoForUnwind(addr=0x{:X}, flags=0x{:X})", addr, + flags); + + s32 res = Kernel::sceKernelGetModuleInfoForUnwind(addr, flags, info); + if (res != 0) { + return res; + } + + static constexpr std::array modules_to_hide = { + "libc.prx", + "libc.sprx", + "libSceAudioLatencyEstimation.prx", + "libSceFace.prx", + "libSceFaceTracker.prx", + "libSceFios2.prx", + "libSceFios2.sprx", + "libSceFontGsm.prx", + "libSceHand.prx", + "libSceHandTracker.prx", + "libSceHeadTracker.prx", + "libSceJobManager.prx", + "libSceNpCppWebApi.prx", + "libSceNpToolkit.prx", + "libSceNpToolkit2.prx", + "libSceS3DConversion.prx", + "libSceSmart.prx", + }; + + const std::string_view module_name = info->name.data(); + if (std::ranges::find(modules_to_hide, module_name) != modules_to_hide.end()) { + std::ranges::fill(info->name, '\0'); + } return res; } @@ -56,7 +85,6 @@ int PS4_SYSV_ABI sceSysmoduleIsLoadedInternal(OrbisSysModuleInternal id) { } int PS4_SYSV_ABI sceSysmoduleLoadModule(OrbisSysModule id) { - auto color_name = magic_enum::enum_name(id); LOG_ERROR(Lib_SysModule, "(DUMMY) called module = {}", magic_enum::enum_name(id)); return ORBIS_OK; } diff --git a/src/core/libraries/system/sysmodule.h b/src/core/libraries/system/sysmodule.h index dfbdca162..9a5fe9513 100644 --- a/src/core/libraries/system/sysmodule.h +++ b/src/core/libraries/system/sysmodule.h @@ -4,6 +4,7 @@ #pragma once #include "common/types.h" +#include "core/libraries/kernel/process.h" namespace Core::Loader { class SymbolsResolver; @@ -152,7 +153,8 @@ enum class OrbisSysModuleInternal : u32 { }; int PS4_SYSV_ABI sceSysmoduleGetModuleHandleInternal(); -s32 PS4_SYSV_ABI sceSysmoduleGetModuleInfoForUnwind(VAddr addr, s32 flags, void* info); +s32 PS4_SYSV_ABI sceSysmoduleGetModuleInfoForUnwind(VAddr addr, s32 flags, + Kernel::OrbisModuleInfoForUnwind* info); int PS4_SYSV_ABI sceSysmoduleIsCalledFromSysModule(); int PS4_SYSV_ABI sceSysmoduleIsCameraPreloaded(); int PS4_SYSV_ABI sceSysmoduleIsLoaded(OrbisSysModule id); diff --git a/src/core/libraries/videoout/video_out.cpp b/src/core/libraries/videoout/video_out.cpp index 3c839dadd..c5208b6dd 100644 --- a/src/core/libraries/videoout/video_out.cpp +++ b/src/core/libraries/videoout/video_out.cpp @@ -220,7 +220,7 @@ s32 PS4_SYSV_ABI sceVideoOutGetEventData(const Kernel::SceKernelEvent* ev, s64* if (ev->ident != static_cast(OrbisVideoOutInternalEventId::Flip) || ev->data == 0) { *data = event_data; } else { - *data = event_data | 0xFFFF000000000000; + *data = event_data | 0xffff000000000000; } return ORBIS_OK; } @@ -233,7 +233,8 @@ s32 PS4_SYSV_ABI sceVideoOutGetEventCount(const Kernel::SceKernelEvent* ev) { return ORBIS_VIDEO_OUT_ERROR_INVALID_EVENT; } - return (ev->data >> 0xc) & 0xf; + auto event_data = static_cast(ev->data); + return event_data.count; } s32 PS4_SYSV_ABI sceVideoOutGetFlipStatus(s32 handle, FlipStatus* status) { diff --git a/src/core/libraries/videoout/video_out.h b/src/core/libraries/videoout/video_out.h index f3e661de4..7db09530b 100644 --- a/src/core/libraries/videoout/video_out.h +++ b/src/core/libraries/videoout/video_out.h @@ -111,6 +111,12 @@ struct SceVideoOutColorSettings { u32 reserved[3]; }; +struct OrbisVideoOutEventData { + u64 time : 12; + u64 count : 4; + u64 flip_arg : 48; +}; + void PS4_SYSV_ABI sceVideoOutSetBufferAttribute(BufferAttribute* attribute, PixelFormat pixelFormat, u32 tilingMode, u32 aspectRatio, u32 width, u32 height, u32 pitchInPixel); @@ -128,8 +134,8 @@ s32 PS4_SYSV_ABI sceVideoOutGetResolutionStatus(s32 handle, SceVideoOutResolutio s32 PS4_SYSV_ABI sceVideoOutOpen(SceUserServiceUserId userId, s32 busType, s32 index, const void* param); s32 PS4_SYSV_ABI sceVideoOutClose(s32 handle); -int PS4_SYSV_ABI sceVideoOutGetEventId(const Kernel::SceKernelEvent* ev); -int PS4_SYSV_ABI sceVideoOutGetEventData(const Kernel::SceKernelEvent* ev, int64_t* data); +s32 PS4_SYSV_ABI sceVideoOutGetEventId(const Kernel::SceKernelEvent* ev); +s32 PS4_SYSV_ABI sceVideoOutGetEventData(const Kernel::SceKernelEvent* ev, s64* data); s32 PS4_SYSV_ABI sceVideoOutColorSettingsSetGamma(SceVideoOutColorSettings* settings, float gamma); s32 PS4_SYSV_ABI sceVideoOutAdjustColor(s32 handle, const SceVideoOutColorSettings* settings); diff --git a/src/core/linker.cpp b/src/core/linker.cpp index 69deb464f..0f86376af 100644 --- a/src/core/linker.cpp +++ b/src/core/linker.cpp @@ -112,18 +112,6 @@ void Linker::Execute(const std::vector args) { 0, "SceKernelInternalMemory"); ASSERT_MSG(ret == 0, "Unable to perform sceKernelInternalMemory mapping"); - // Simulate libSceGnmDriver initialization, which maps a chunk of direct memory. - // Some games fail without accurately emulating this behavior. - s64 phys_addr{}; - ret = Libraries::Kernel::sceKernelAllocateDirectMemory( - 0, Libraries::Kernel::sceKernelGetDirectMemorySize(), 0x10000, 0x10000, 3, &phys_addr); - if (ret == 0) { - void* addr{reinterpret_cast(0xfe0000000)}; - ret = Libraries::Kernel::sceKernelMapNamedDirectMemory(&addr, 0x10000, 0x13, 0, phys_addr, - 0x10000, "SceGnmDriver"); - } - ASSERT_MSG(ret == 0, "Unable to emulate libSceGnmDriver initialization"); - main_thread.Run([this, module, args](std::stop_token) { Common::SetCurrentThreadName("GAME_MainThread"); LoadSharedLibraries(); diff --git a/src/core/linker.h b/src/core/linker.h index 63dfc37e8..028e18ead 100644 --- a/src/core/linker.h +++ b/src/core/linker.h @@ -83,7 +83,7 @@ public: } Module* GetModule(s32 index) const { - if (index >= 0 || index < m_modules.size()) { + if (index >= 0 && index < m_modules.size()) { return m_modules.at(index).get(); } return nullptr; diff --git a/src/core/memory.cpp b/src/core/memory.cpp index cb80d6be4..9861e813a 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -75,7 +75,8 @@ u64 MemoryManager::ClampRangeSize(VAddr virtual_addr, u64 size) { // Clamp size to the remaining size of the current VMA. auto vma = FindVMA(virtual_addr); - ASSERT_MSG(vma != vma_map.end(), "Attempted to access invalid GPU address {:#x}", virtual_addr); + ASSERT_MSG(vma->second.Contains(virtual_addr, 0), + "Attempted to access invalid GPU address {:#x}", virtual_addr); u64 clamped_size = vma->second.base + vma->second.size - virtual_addr; ++vma; @@ -96,6 +97,8 @@ u64 MemoryManager::ClampRangeSize(VAddr virtual_addr, u64 size) { bool MemoryManager::TryWriteBacking(void* address, const void* data, u32 num_bytes) { const VAddr virtual_addr = std::bit_cast(address); const auto& vma = FindVMA(virtual_addr)->second; + ASSERT_MSG(vma.Contains(virtual_addr, 0), + "Attempting to access out of bounds memory at address {:#x}", virtual_addr); if (vma.type != VMAType::Direct) { return false; } @@ -142,19 +145,19 @@ PAddr MemoryManager::Allocate(PAddr search_start, PAddr search_end, size_t size, auto mapping_start = search_start > dmem_area->second.base ? Common::AlignUp(search_start, alignment) : Common::AlignUp(dmem_area->second.base, alignment); - auto mapping_end = Common::AlignUp(mapping_start + size, alignment); + auto mapping_end = mapping_start + size; // Find the first free, large enough dmem area in the range. - while ((!dmem_area->second.is_free || dmem_area->second.GetEnd() < mapping_end) && - dmem_area != dmem_map.end()) { + while (!dmem_area->second.is_free || dmem_area->second.GetEnd() < mapping_end) { // The current dmem_area isn't suitable, move to the next one. dmem_area++; + if (dmem_area == dmem_map.end()) { + break; + } // Update local variables based on the new dmem_area - mapping_start = search_start > dmem_area->second.base - ? Common::AlignUp(search_start, alignment) - : Common::AlignUp(dmem_area->second.base, alignment); - mapping_end = Common::AlignUp(mapping_start + size, alignment); + mapping_start = Common::AlignUp(dmem_area->second.base, alignment); + mapping_end = mapping_start + size; } if (dmem_area == dmem_map.end()) { @@ -174,7 +177,6 @@ void MemoryManager::Free(PAddr phys_addr, size_t size) { std::scoped_lock lk{mutex}; auto dmem_area = CarveDmemArea(phys_addr, size); - ASSERT(dmem_area != dmem_map.end() && dmem_area->second.size >= size); // Release any dmem mappings that reference this physical block. std::vector> remove_list; @@ -218,12 +220,18 @@ int MemoryManager::PoolReserve(void** out_addr, VAddr virtual_addr, size_t size, vma = FindVMA(mapped_addr)->second; } const size_t remaining_size = vma.base + vma.size - mapped_addr; - ASSERT_MSG(vma.type == VMAType::Free && remaining_size >= size); + ASSERT_MSG(vma.type == VMAType::Free && remaining_size >= size, + "Memory region {:#x} to {:#x} is not large enough to reserve {:#x} to {:#x}", + vma.base, vma.base + vma.size, virtual_addr, virtual_addr + size); } // Find the first free area starting with provided virtual address. if (False(flags & MemoryMapFlags::Fixed)) { mapped_addr = SearchFree(mapped_addr, size, alignment); + if (mapped_addr == -1) { + // No suitable memory areas to map to + return ORBIS_KERNEL_ERROR_ENOMEM; + } } // Add virtual memory area @@ -231,7 +239,7 @@ int MemoryManager::PoolReserve(void** out_addr, VAddr virtual_addr, size_t size, auto& new_vma = new_vma_handle->second; new_vma.disallow_merge = True(flags & MemoryMapFlags::NoCoalesce); new_vma.prot = MemoryProt::NoAccess; - new_vma.name = ""; + new_vma.name = "anon"; new_vma.type = VMAType::PoolReserved; MergeAdjacent(vma_map, new_vma_handle); @@ -249,19 +257,25 @@ int MemoryManager::Reserve(void** out_addr, VAddr virtual_addr, size_t size, Mem // Fixed mapping means the virtual address must exactly match the provided one. if (True(flags & MemoryMapFlags::Fixed)) { - auto& vma = FindVMA(mapped_addr)->second; + auto vma = FindVMA(mapped_addr)->second; // If the VMA is mapped, unmap the region first. if (vma.IsMapped()) { UnmapMemoryImpl(mapped_addr, size); vma = FindVMA(mapped_addr)->second; } const size_t remaining_size = vma.base + vma.size - mapped_addr; - ASSERT_MSG(vma.type == VMAType::Free && remaining_size >= size); + ASSERT_MSG(vma.type == VMAType::Free && remaining_size >= size, + "Memory region {:#x} to {:#x} is not large enough to reserve {:#x} to {:#x}", + vma.base, vma.base + vma.size, virtual_addr, virtual_addr + size); } // Find the first free area starting with provided virtual address. if (False(flags & MemoryMapFlags::Fixed)) { mapped_addr = SearchFree(mapped_addr, size, alignment); + if (mapped_addr == -1) { + // No suitable memory areas to map to + return ORBIS_KERNEL_ERROR_ENOMEM; + } } // Add virtual memory area @@ -269,7 +283,7 @@ int MemoryManager::Reserve(void** out_addr, VAddr virtual_addr, size_t size, Mem auto& new_vma = new_vma_handle->second; new_vma.disallow_merge = True(flags & MemoryMapFlags::NoCoalesce); new_vma.prot = MemoryProt::NoAccess; - new_vma.name = ""; + new_vma.name = "anon"; new_vma.type = VMAType::Reserved; MergeAdjacent(vma_map, new_vma_handle); @@ -290,7 +304,9 @@ int MemoryManager::PoolCommit(VAddr virtual_addr, size_t size, MemoryProt prot) // This should return SCE_KERNEL_ERROR_ENOMEM but shouldn't normally happen. const auto& vma = FindVMA(mapped_addr)->second; const size_t remaining_size = vma.base + vma.size - mapped_addr; - ASSERT_MSG(!vma.IsMapped() && remaining_size >= size); + ASSERT_MSG(!vma.IsMapped() && remaining_size >= size, + "Memory region {:#x} to {:#x} isn't free enough to map region {:#x} to {:#x}", + vma.base, vma.base + vma.size, virtual_addr, virtual_addr + size); // Perform the mapping. void* out_addr = impl.Map(mapped_addr, size, alignment, -1, false); @@ -304,7 +320,10 @@ int MemoryManager::PoolCommit(VAddr virtual_addr, size_t size, MemoryProt prot) new_vma.is_exec = false; new_vma.phys_base = 0; - rasterizer->MapMemory(mapped_addr, size); + if (IsValidGpuMapping(mapped_addr, size)) { + rasterizer->MapMemory(mapped_addr, size); + } + return ORBIS_OK; } @@ -327,15 +346,34 @@ int MemoryManager::MapMemory(void** out_addr, VAddr virtual_addr, size_t size, M // Fixed mapping means the virtual address must exactly match the provided one. if (True(flags & MemoryMapFlags::Fixed)) { - // This should return SCE_KERNEL_ERROR_ENOMEM but shouldn't normally happen. - const auto& vma = FindVMA(mapped_addr)->second; - const size_t remaining_size = vma.base + vma.size - mapped_addr; - ASSERT_MSG(!vma.IsMapped() && remaining_size >= size); + auto vma = FindVMA(mapped_addr)->second; + size_t remaining_size = vma.base + vma.size - mapped_addr; + // There's a possible edge case where we're mapping to a partially reserved range. + // To account for this, unmap any reserved areas within this mapping range first. + auto unmap_addr = mapped_addr; + auto unmap_size = size; + while (!vma.IsMapped() && unmap_addr < mapped_addr + size && remaining_size < size) { + auto unmapped = UnmapBytesFromEntry(unmap_addr, vma, unmap_size); + unmap_addr += unmapped; + unmap_size -= unmapped; + vma = FindVMA(unmap_addr)->second; + } + + // This should return SCE_KERNEL_ERROR_ENOMEM but rarely happens. + vma = FindVMA(mapped_addr)->second; + remaining_size = vma.base + vma.size - mapped_addr; + ASSERT_MSG(!vma.IsMapped() && remaining_size >= size, + "Memory region {:#x} to {:#x} isn't free enough to map region {:#x} to {:#x}", + vma.base, vma.base + vma.size, virtual_addr, virtual_addr + size); } // Find the first free area starting with provided virtual address. if (False(flags & MemoryMapFlags::Fixed)) { mapped_addr = SearchFree(mapped_addr, size, alignment); + if (mapped_addr == -1) { + // No suitable memory areas to map to + return ORBIS_KERNEL_ERROR_ENOMEM; + } } // Perform the mapping. @@ -355,7 +393,10 @@ int MemoryManager::MapMemory(void** out_addr, VAddr virtual_addr, size_t size, M if (type == VMAType::Flexible) { flexible_usage += size; } - rasterizer->MapMemory(mapped_addr, size); + + if (IsValidGpuMapping(mapped_addr, size)) { + rasterizer->MapMemory(mapped_addr, size); + } return ORBIS_OK; } @@ -368,12 +409,18 @@ int MemoryManager::MapFile(void** out_addr, VAddr virtual_addr, size_t size, Mem // Find first free area to map the file. if (False(flags & MemoryMapFlags::Fixed)) { mapped_addr = SearchFree(mapped_addr, size_aligned, 1); + if (mapped_addr == -1) { + // No suitable memory areas to map to + return ORBIS_KERNEL_ERROR_ENOMEM; + } } if (True(flags & MemoryMapFlags::Fixed)) { const auto& vma = FindVMA(virtual_addr)->second; const size_t remaining_size = vma.base + vma.size - virtual_addr; - ASSERT_MSG(!vma.IsMapped() && remaining_size >= size); + ASSERT_MSG(!vma.IsMapped() && remaining_size >= size, + "Memory region {:#x} to {:#x} isn't free enough to map region {:#x} to {:#x}", + vma.base, vma.base + vma.size, virtual_addr, virtual_addr + size); } // Map the file. @@ -406,7 +453,9 @@ void MemoryManager::PoolDecommit(VAddr virtual_addr, size_t size) { const auto start_in_vma = virtual_addr - vma_base_addr; const auto type = vma_base.type; - rasterizer->UnmapMemory(virtual_addr, size); + if (IsValidGpuMapping(virtual_addr, size)) { + rasterizer->UnmapMemory(virtual_addr, size); + } // Mark region as free and attempt to coalesce it with neighbours. const auto new_it = CarveVMA(virtual_addr, size); @@ -446,7 +495,10 @@ u64 MemoryManager::UnmapBytesFromEntry(VAddr virtual_addr, VirtualMemoryArea vma if (type == VMAType::Flexible) { flexible_usage -= adjusted_size; } - rasterizer->UnmapMemory(virtual_addr, adjusted_size); + + if (IsValidGpuMapping(virtual_addr, adjusted_size)) { + rasterizer->UnmapMemory(virtual_addr, adjusted_size); + } // Mark region as free and attempt to coalesce it with neighbours. const auto new_it = CarveVMA(virtual_addr, adjusted_size); @@ -473,6 +525,8 @@ s32 MemoryManager::UnmapMemoryImpl(VAddr virtual_addr, u64 size) { do { auto it = FindVMA(virtual_addr + unmapped_bytes); auto& vma_base = it->second; + ASSERT_MSG(vma_base.Contains(virtual_addr + unmapped_bytes, 0), + "Address {:#x} is out of bounds", virtual_addr + unmapped_bytes); auto unmapped = UnmapBytesFromEntry(virtual_addr + unmapped_bytes, vma_base, size - unmapped_bytes); ASSERT_MSG(unmapped > 0, "Failed to unmap memory, progress is impossible"); @@ -487,7 +541,10 @@ int MemoryManager::QueryProtection(VAddr addr, void** start, void** end, u32* pr const auto it = FindVMA(addr); const auto& vma = it->second; - ASSERT_MSG(vma.type != VMAType::Free, "Provided address is not mapped"); + if (!vma.Contains(addr, 0) || vma.IsFree()) { + LOG_ERROR(Kernel_Vmm, "Address {:#x} is not mapped", addr); + return ORBIS_KERNEL_ERROR_EACCES; + } if (start != nullptr) { *start = reinterpret_cast(vma.base); @@ -557,6 +614,8 @@ s32 MemoryManager::Protect(VAddr addr, size_t size, MemoryProt prot) { do { auto it = FindVMA(addr + protected_bytes); auto& vma_base = it->second; + ASSERT_MSG(vma_base.Contains(addr + protected_bytes, 0), "Address {:#x} is out of bounds", + addr + protected_bytes); auto result = 0; result = ProtectBytes(addr + protected_bytes, vma_base, size - protected_bytes, prot); if (result < 0) { @@ -573,8 +632,16 @@ int MemoryManager::VirtualQuery(VAddr addr, int flags, ::Libraries::Kernel::OrbisVirtualQueryInfo* info) { std::scoped_lock lk{mutex}; - auto it = FindVMA(addr); - if (it->second.type == VMAType::Free && flags == 1) { + // FindVMA on addresses before the vma_map return garbage data. + auto query_addr = + addr < impl.SystemManagedVirtualBase() ? impl.SystemManagedVirtualBase() : addr; + if (addr < query_addr && flags == 0) { + LOG_WARNING(Kernel_Vmm, "VirtualQuery on free memory region"); + return ORBIS_KERNEL_ERROR_EACCES; + } + auto it = FindVMA(query_addr); + + while (it->second.type == VMAType::Free && flags == 1 && it != --vma_map.end()) { ++it; } if (it->second.type == VMAType::Free) { @@ -587,15 +654,17 @@ int MemoryManager::VirtualQuery(VAddr addr, int flags, info->end = vma.base + vma.size; info->offset = vma.phys_base; info->protection = static_cast(vma.prot); - info->is_flexible.Assign(vma.type == VMAType::Flexible); - info->is_direct.Assign(vma.type == VMAType::Direct); - info->is_stack.Assign(vma.type == VMAType::Stack); - info->is_pooled.Assign(vma.type == VMAType::PoolReserved || vma.type == VMAType::Pooled); - info->is_committed.Assign(vma.IsMapped()); - vma.name.copy(info->name.data(), std::min(info->name.size(), vma.name.size())); + info->is_flexible = vma.type == VMAType::Flexible ? 1 : 0; + info->is_direct = vma.type == VMAType::Direct ? 1 : 0; + info->is_stack = vma.type == VMAType::Stack ? 1 : 0; + info->is_pooled = vma.type == VMAType::PoolReserved || vma.type == VMAType::Pooled ? 1 : 0; + info->is_committed = vma.IsMapped() ? 1 : 0; + + strncpy(info->name, vma.name.data(), ::Libraries::Kernel::ORBIS_KERNEL_MAXIMUM_NAME_LENGTH); + if (vma.type == VMAType::Direct) { const auto dmem_it = FindDmemArea(vma.phys_base); - ASSERT(dmem_it != dmem_map.end()); + ASSERT_MSG(vma.phys_base <= dmem_it->second.GetEnd(), "vma.phys_base is not in dmem_map!"); info->memory_type = dmem_it->second.memory_type; } else { info->memory_type = ::Libraries::Kernel::SCE_KERNEL_WB_ONION; @@ -609,11 +678,11 @@ int MemoryManager::DirectMemoryQuery(PAddr addr, bool find_next, std::scoped_lock lk{mutex}; auto dmem_area = FindDmemArea(addr); - while (dmem_area != dmem_map.end() && dmem_area->second.is_free && find_next) { + while (dmem_area != --dmem_map.end() && dmem_area->second.is_free && find_next) { dmem_area++; } - if (dmem_area == dmem_map.end() || dmem_area->second.is_free) { + if (dmem_area->second.is_free) { LOG_ERROR(Core, "Unable to find allocated direct memory region to query!"); return ORBIS_KERNEL_ERROR_EACCES; } @@ -693,36 +762,56 @@ VAddr MemoryManager::SearchFree(VAddr virtual_addr, size_t size, u32 alignment) virtual_addr = min_search_address; } + // If the requested address is beyond the maximum our code can handle, throw an assert + auto max_search_address = impl.UserVirtualBase() + impl.UserVirtualSize(); + ASSERT_MSG(virtual_addr <= max_search_address, "Input address {:#x} is out of bounds", + virtual_addr); + auto it = FindVMA(virtual_addr); - ASSERT_MSG(it != vma_map.end(), "Specified mapping address was not found!"); // If the VMA is free and contains the requested mapping we are done. if (it->second.IsFree() && it->second.Contains(virtual_addr, size)) { return virtual_addr; } + // Search for the first free VMA that fits our mapping. - const auto is_suitable = [&] { + while (it != vma_map.end()) { if (!it->second.IsFree()) { - return false; + it++; + continue; } + const auto& vma = it->second; virtual_addr = Common::AlignUp(vma.base, alignment); // Sometimes the alignment itself might be larger than the VMA. if (virtual_addr > vma.base + vma.size) { - return false; + it++; + continue; } + + // Make sure the address is within our defined bounds + if (virtual_addr >= max_search_address) { + // There are no free mappings within our safely usable address space. + break; + } + + // If there's enough space in the VMA, return the address. const size_t remaining_size = vma.base + vma.size - virtual_addr; - return remaining_size >= size; - }; - while (!is_suitable()) { - ++it; + if (remaining_size >= size) { + return virtual_addr; + } + it++; } - return virtual_addr; + + // Couldn't find a suitable VMA, return an error. + LOG_ERROR(Kernel_Vmm, "Couldn't find a free mapping for address {:#x}, size {:#x}", + virtual_addr, size); + return -1; } MemoryManager::VMAHandle MemoryManager::CarveVMA(VAddr virtual_addr, size_t size) { auto vma_handle = FindVMA(virtual_addr); - ASSERT_MSG(vma_handle != vma_map.end(), "Virtual address not in vm_map"); + ASSERT_MSG(vma_handle->second.Contains(virtual_addr, 0), "Virtual address not in vm_map"); const VirtualMemoryArea& vma = vma_handle->second; ASSERT_MSG(vma.base <= virtual_addr, "Adding a mapping to already mapped region"); @@ -751,7 +840,7 @@ MemoryManager::VMAHandle MemoryManager::CarveVMA(VAddr virtual_addr, size_t size MemoryManager::DMemHandle MemoryManager::CarveDmemArea(PAddr addr, size_t size) { auto dmem_handle = FindDmemArea(addr); - ASSERT_MSG(dmem_handle != dmem_map.end(), "Physical address not in dmem_map"); + ASSERT_MSG(addr <= dmem_handle->second.GetEnd(), "Physical address not in dmem_map"); const DirectMemoryArea& area = dmem_handle->second; ASSERT_MSG(area.base <= addr, "Adding an allocation to already allocated region"); @@ -806,7 +895,7 @@ int MemoryManager::GetDirectMemoryType(PAddr addr, int* directMemoryTypeOut, auto dmem_area = FindDmemArea(addr); - if (dmem_area == dmem_map.end() || dmem_area->second.is_free) { + if (addr > dmem_area->second.GetEnd() || dmem_area->second.is_free) { LOG_ERROR(Core, "Unable to find allocated direct memory region to check type!"); return ORBIS_KERNEL_ERROR_ENOENT; } diff --git a/src/core/memory.h b/src/core/memory.h index a6a55e288..3a204eb96 100644 --- a/src/core/memory.h +++ b/src/core/memory.h @@ -157,6 +157,12 @@ public: return impl.SystemReservedVirtualBase(); } + bool IsValidGpuMapping(VAddr virtual_addr, u64 size) { + // The PS4's GPU can only handle 40 bit addresses. + const VAddr max_gpu_address{0x10000000000}; + return virtual_addr + size < max_gpu_address; + } + bool IsValidAddress(const void* addr) const noexcept { const VAddr virtual_addr = reinterpret_cast(addr); const auto end_it = std::prev(vma_map.end()); @@ -186,7 +192,7 @@ public: int PoolCommit(VAddr virtual_addr, size_t size, MemoryProt prot); int MapMemory(void** out_addr, VAddr virtual_addr, size_t size, MemoryProt prot, - MemoryMapFlags flags, VMAType type, std::string_view name = "", + MemoryMapFlags flags, VMAType type, std::string_view name = "anon", bool is_exec = false, PAddr phys_addr = -1, u64 alignment = 0); int MapFile(void** out_addr, VAddr virtual_addr, size_t size, MemoryProt prot, diff --git a/src/core/module.cpp b/src/core/module.cpp index 1004f4404..f31bbed6c 100644 --- a/src/core/module.cpp +++ b/src/core/module.cpp @@ -19,8 +19,7 @@ namespace Core { using EntryFunc = PS4_SYSV_ABI int (*)(size_t args, const void* argp, void* param); -static u64 LoadOffset = CODE_BASE_OFFSET; -static constexpr u64 CODE_BASE_INCR = 0x010000000u; +static constexpr u64 ModuleLoadBase = 0x800000000; static u64 GetAlignedSize(const elf_program_header& phdr) { return (phdr.p_align != 0 ? (phdr.p_memsz + (phdr.p_align - 1)) & ~(phdr.p_align - 1) @@ -84,7 +83,7 @@ static std::string StringToNid(std::string_view symbol) { } Module::Module(Core::MemoryManager* memory_, const std::filesystem::path& file_, u32& max_tls_index) - : memory{memory_}, file{file_}, name{file.stem().string()} { + : memory{memory_}, file{file_}, name{file.filename().string()} { elf.Open(file); if (elf.IsElfFile()) { LoadModuleToMemory(max_tls_index); @@ -113,10 +112,8 @@ void Module::LoadModuleToMemory(u32& max_tls_index) { // Map module segments (and possible TLS trampolines) void** out_addr = reinterpret_cast(&base_virtual_addr); - memory->MapMemory(out_addr, memory->SystemReservedVirtualBase() + LoadOffset, - aligned_base_size + TrampolineSize, MemoryProt::CpuReadWrite, - MemoryMapFlags::Fixed, VMAType::Code, name, true); - LoadOffset += CODE_BASE_INCR * (1 + aligned_base_size / CODE_BASE_INCR); + memory->MapMemory(out_addr, ModuleLoadBase, aligned_base_size + TrampolineSize, + MemoryProt::CpuReadWrite, MemoryMapFlags::NoFlags, VMAType::Code, name, true); LOG_INFO(Core_Linker, "Loading module {} to {}", name, fmt::ptr(*out_addr)); #ifdef ARCH_X86_64 @@ -135,10 +132,14 @@ void Module::LoadModuleToMemory(u32& max_tls_index) { if (do_map) { elf.LoadSegment(segment_addr, phdr.p_offset, phdr.p_filesz); } - auto& segment = info.segments[info.num_segments++]; - segment.address = segment_addr; - segment.prot = phdr.p_flags; - segment.size = GetAlignedSize(phdr); + if (info.num_segments < 4) { + auto& segment = info.segments[info.num_segments++]; + segment.address = segment_addr; + segment.prot = phdr.p_flags; + segment.size = GetAlignedSize(phdr); + } else { + LOG_ERROR(Core_Linker, "Attempting to add too many segments!"); + } }; for (u16 i = 0; i < elf_header.e_phnum; i++) { @@ -225,7 +226,7 @@ void Module::LoadModuleToMemory(u32& max_tls_index) { LOG_INFO(Core_Linker, "program entry addr ..........: {:#018x}", entry_addr); if (MemoryPatcher::g_eboot_address == 0) { - if (name == "eboot") { + if (name == "eboot.bin") { MemoryPatcher::g_eboot_address = base_virtual_addr; MemoryPatcher::g_eboot_image_size = base_size; MemoryPatcher::OnGameLoaded(); diff --git a/src/core/tls.h b/src/core/tls.h index 6edd6a297..46ca8153b 100644 --- a/src/core/tls.h +++ b/src/core/tls.h @@ -5,6 +5,8 @@ #include "common/types.h" +void* memset(void* ptr, int value, size_t num); + namespace Xbyak { class CodeGenerator; } @@ -41,9 +43,18 @@ Tcb* GetTcbBase(); /// Makes sure TLS is initialized for the thread before entering guest. void EnsureThreadInitialized(); +template +__attribute__((optnone)) void ClearStack() { + volatile void* buf = alloca(size); + memset(const_cast(buf), 0, size); + buf = nullptr; +} + template ReturnType ExecuteGuest(PS4_SYSV_ABI ReturnType (*func)(FuncArgs...), CallArgs&&... args) { EnsureThreadInitialized(); + // clear stack to avoid trash from EnsureThreadInitialized + ClearStack<13_KB>(); return func(std::forward(args)...); } diff --git a/src/emulator.cpp b/src/emulator.cpp index 1a71b99cb..ebb34054b 100644 --- a/src/emulator.cpp +++ b/src/emulator.cpp @@ -10,13 +10,16 @@ #include "common/logging/log.h" #ifdef ENABLE_QT_GUI #include -#include "common/memory_patcher.h" #endif #include "common/assert.h" #ifdef ENABLE_DISCORD_RPC #include "common/discord_rpc_handler.h" #endif +#ifdef _WIN32 +#include +#endif #include "common/elf_info.h" +#include "common/memory_patcher.h" #include "common/ntapi.h" #include "common/path_util.h" #include "common/polyfill_thread.h" @@ -46,27 +49,10 @@ Emulator::Emulator() { #ifdef _WIN32 Common::NtApi::Initialize(); SetPriorityClass(GetCurrentProcess(), ABOVE_NORMAL_PRIORITY_CLASS); -#endif - - // Create stdin/stdout/stderr - Common::Singleton::Instance()->CreateStdHandles(); - - // Defer until after logging is initialized. - memory = Core::Memory::Instance(); - controller = Common::Singleton::Instance(); - linker = Common::Singleton::Instance(); - - // Load renderdoc module. - VideoCore::LoadRenderDoc(); - - // Start the timer (Play Time) -#ifdef ENABLE_QT_GUI - start_time = std::chrono::steady_clock::now(); - const auto user_dir = Common::FS::GetUserPath(Common::FS::PathType::UserDir); - QString filePath = QString::fromStdString((user_dir / "play_time.txt").string()); - QFile file(filePath); - ASSERT_MSG(file.open(QIODevice::ReadWrite | QIODevice::Text), - "Error opening or creating play_time.txt"); + // need to init this in order for winsock2 to work + WORD versionWanted = MAKEWORD(2, 2); + WSADATA wsaData; + WSAStartup(versionWanted, &wsaData); #endif } @@ -91,58 +77,93 @@ void Emulator::Run(const std::filesystem::path& file, const std::vector::Instance(); - mnt->Mount(game_folder, "/app0"); + mnt->Mount(game_folder, "/app0", true); // Certain games may use /hostapp as well such as CUSA001100 - mnt->Mount(game_folder, "/hostapp"); + mnt->Mount(game_folder, "/hostapp", true); - auto& game_info = Common::ElfInfo::Instance(); + const auto param_sfo_path = mnt->GetHostPath("/app0/sce_sys/param.sfo"); + const auto param_sfo_exists = std::filesystem::exists(param_sfo_path); - // Loading param.sfo file if exists + // Load param.sfo details if it exists std::string id; std::string title; std::string app_version; u32 fw_version; Common::PSFAttributes psf_attributes{}; - - const auto param_sfo_path = mnt->GetHostPath("/app0/sce_sys/param.sfo"); - if (!std::filesystem::exists(param_sfo_path) || !Config::getSeparateLogFilesEnabled()) { - Common::Log::Initialize(); - Common::Log::Start(); - } - - if (std::filesystem::exists(param_sfo_path)) { + if (param_sfo_exists) { auto* param_sfo = Common::Singleton::Instance(); - const bool success = param_sfo->Open(param_sfo_path); - ASSERT_MSG(success, "Failed to open param.sfo"); + ASSERT_MSG(param_sfo->Open(param_sfo_path), "Failed to open param.sfo"); + const auto content_id = param_sfo->GetString("CONTENT_ID"); ASSERT_MSG(content_id.has_value(), "Failed to get CONTENT_ID"); + id = std::string(*content_id, 7, 9); - - if (Config::getSeparateLogFilesEnabled()) { - Common::Log::Initialize(id + ".log"); - Common::Log::Start(); + title = param_sfo->GetString("TITLE").value_or("Unknown title"); + fw_version = param_sfo->GetInteger("SYSTEM_VER").value_or(0x4700000); + app_version = param_sfo->GetString("APP_VER").value_or("Unknown version"); + if (const auto raw_attributes = param_sfo->GetInteger("ATTRIBUTE")) { + psf_attributes.raw = *raw_attributes; } - LOG_INFO(Loader, "Starting shadps4 emulator v{} ", Common::g_version); - LOG_INFO(Loader, "Revision {}", Common::g_scm_rev); - LOG_INFO(Loader, "Branch {}", Common::g_scm_branch); - LOG_INFO(Loader, "Description {}", Common::g_scm_desc); - LOG_INFO(Loader, "Remote {}", Common::g_scm_remote_url); + } - LOG_INFO(Config, "General LogType: {}", Config::getLogType()); - LOG_INFO(Config, "General isNeo: {}", Config::isNeoModeConsole()); - LOG_INFO(Config, "GPU isNullGpu: {}", Config::nullGpu()); - LOG_INFO(Config, "GPU shouldDumpShaders: {}", Config::dumpShaders()); - LOG_INFO(Config, "GPU vblankDivider: {}", Config::vblankDiv()); - LOG_INFO(Config, "Vulkan gpuId: {}", Config::getGpuId()); - LOG_INFO(Config, "Vulkan vkValidation: {}", Config::vkValidationEnabled()); - LOG_INFO(Config, "Vulkan vkValidationSync: {}", Config::vkValidationSyncEnabled()); - LOG_INFO(Config, "Vulkan vkValidationGpu: {}", Config::vkValidationGpuEnabled()); - LOG_INFO(Config, "Vulkan crashDiagnostics: {}", Config::getVkCrashDiagnosticEnabled()); - LOG_INFO(Config, "Vulkan hostMarkers: {}", Config::getVkHostMarkersEnabled()); - LOG_INFO(Config, "Vulkan guestMarkers: {}", Config::getVkGuestMarkersEnabled()); - LOG_INFO(Config, "Vulkan rdocEnable: {}", Config::isRdocEnabled()); + // Initialize logging as soon as possible + if (!id.empty() && Config::getSeparateLogFilesEnabled()) { + Common::Log::Initialize(id + ".log"); + } else { + Common::Log::Initialize(); + } + Common::Log::Start(); + LOG_INFO(Loader, "Starting shadps4 emulator v{} ", Common::g_version); + LOG_INFO(Loader, "Revision {}", Common::g_scm_rev); + LOG_INFO(Loader, "Branch {}", Common::g_scm_branch); + LOG_INFO(Loader, "Description {}", Common::g_scm_desc); + LOG_INFO(Loader, "Remote {}", Common::g_scm_remote_url); + + LOG_INFO(Config, "General LogType: {}", Config::getLogType()); + LOG_INFO(Config, "General isNeo: {}", Config::isNeoModeConsole()); + LOG_INFO(Config, "GPU isNullGpu: {}", Config::nullGpu()); + LOG_INFO(Config, "GPU shouldDumpShaders: {}", Config::dumpShaders()); + LOG_INFO(Config, "GPU vblankDivider: {}", Config::vblankDiv()); + LOG_INFO(Config, "Vulkan gpuId: {}", Config::getGpuId()); + LOG_INFO(Config, "Vulkan vkValidation: {}", Config::vkValidationEnabled()); + LOG_INFO(Config, "Vulkan vkValidationSync: {}", Config::vkValidationSyncEnabled()); + LOG_INFO(Config, "Vulkan vkValidationGpu: {}", Config::vkValidationGpuEnabled()); + LOG_INFO(Config, "Vulkan crashDiagnostics: {}", Config::getVkCrashDiagnosticEnabled()); + LOG_INFO(Config, "Vulkan hostMarkers: {}", Config::getVkHostMarkersEnabled()); + LOG_INFO(Config, "Vulkan guestMarkers: {}", Config::getVkGuestMarkersEnabled()); + LOG_INFO(Config, "Vulkan rdocEnable: {}", Config::isRdocEnabled()); + + if (param_sfo_exists) { + LOG_INFO(Loader, "Game id: {} Title: {}", id, title); + LOG_INFO(Loader, "Fw: {:#x} App Version: {}", fw_version, app_version); + } + if (!args.empty()) { + const auto argc = std::min(args.size(), 32); + for (auto i = 0; i < argc; i++) { + LOG_INFO(Loader, "Game argument {}: {}", i, args[i]); + } + if (args.size() > 32) { + LOG_ERROR(Loader, "Too many game arguments, only passing the first 32"); + } + } + + // Create stdin/stdout/stderr + Common::Singleton::Instance()->CreateStdHandles(); + + // Initialize components + memory = Core::Memory::Instance(); + controller = Common::Singleton::Instance(); + linker = Common::Singleton::Instance(); + + // Load renderdoc module + VideoCore::LoadRenderDoc(); + + // Initialize patcher and trophies + if (!id.empty()) { + MemoryPatcher::g_game_serial = id; Libraries::NpTrophy::game_serial = id; + const auto trophyDir = Common::FS::GetUserPath(Common::FS::PathType::MetaDataDir) / id / "TrophyFiles"; if (!std::filesystem::exists(trophyDir)) { @@ -151,41 +172,9 @@ void Emulator::Run(const std::filesystem::path& file, const std::vectorstart(60000); // 60000 ms = 1 minute -#endif - title = param_sfo->GetString("TITLE").value_or("Unknown title"); - LOG_INFO(Loader, "Game id: {} Title: {}", id, title); - fw_version = param_sfo->GetInteger("SYSTEM_VER").value_or(0x4700000); - app_version = param_sfo->GetString("APP_VER").value_or("Unknown version"); - LOG_INFO(Loader, "Fw: {:#x} App Version: {}", fw_version, app_version); - if (const auto raw_attributes = param_sfo->GetInteger("ATTRIBUTE")) { - psf_attributes.raw = *raw_attributes; - } - if (!args.empty()) { - int argc = std::min(args.size(), 32); - for (int i = 0; i < argc; i++) { - LOG_INFO(Loader, "Game argument {}: {}", i, args[i]); - } - if (args.size() > 32) { - LOG_ERROR(Loader, "Too many game arguments, only passing the first 32"); - } - } - } - - const auto pic1_path = mnt->GetHostPath("/app0/sce_sys/pic1.png"); - if (std::filesystem::exists(pic1_path)) { - game_info.splash_path = pic1_path; } + auto& game_info = Common::ElfInfo::Instance(); game_info.initialized = true; game_info.game_serial = id; game_info.title = title; @@ -194,6 +183,11 @@ void Emulator::Run(const std::filesystem::path& file, const std::vectorGetHostPath("/app0/sce_sys/pic1.png"); + if (std::filesystem::exists(pic1_path)) { + game_info.splash_path = pic1_path; + } + std::string game_title = fmt::format("{} - {} <{}>", id, title, app_version); std::string window_title = ""; if (Common::g_is_release) { @@ -224,11 +218,15 @@ void Emulator::Run(const std::filesystem::path& file, const std::vectorMount(mount_data_dir, "/data"); // should just exist, manually create with game serial + + // Mounting temp folders const auto& mount_temp_dir = Common::FS::GetUserPath(Common::FS::PathType::TempDataDir) / id; - if (!std::filesystem::exists(mount_temp_dir)) { - std::filesystem::create_directory(mount_temp_dir); + if (std::filesystem::exists(mount_temp_dir)) { + // Temp folder should be cleared on each boot. + std::filesystem::remove_all(mount_temp_dir); } - mnt->Mount(mount_temp_dir, "/temp0"); // called in app_content ==> stat/mkdir + std::filesystem::create_directory(mount_temp_dir); + mnt->Mount(mount_temp_dir, "/temp0"); mnt->Mount(mount_temp_dir, "/temp"); const auto& mount_download_dir = @@ -273,6 +271,25 @@ void Emulator::Run(const std::filesystem::path& file, const std::vectorstart(60000); // 60000 ms = 1 minute + + start_time = std::chrono::steady_clock::now(); + const auto user_dir = Common::FS::GetUserPath(Common::FS::PathType::UserDir); + QString filePath = QString::fromStdString((user_dir / "play_time.txt").string()); + QFile file(filePath); + ASSERT_MSG(file.open(QIODevice::ReadWrite | QIODevice::Text), + "Error opening or creating play_time.txt"); + } +#endif + linker->Execute(args); window->InitTimers(); diff --git a/src/imgui/imgui_texture.h b/src/imgui/imgui_texture.h index 1a38066d0..d84eda6b7 100644 --- a/src/imgui/imgui_texture.h +++ b/src/imgui/imgui_texture.h @@ -4,8 +4,11 @@ #pragma once #include +#include #include +#include "common/types.h" + namespace ImGui { namespace Core::TextureManager { diff --git a/src/imgui/renderer/imgui_core.cpp b/src/imgui/renderer/imgui_core.cpp index 50ce41ebf..d143232dc 100644 --- a/src/imgui/renderer/imgui_core.cpp +++ b/src/imgui/renderer/imgui_core.cpp @@ -112,6 +112,8 @@ void Initialize(const ::Vulkan::Instance& instance, const Frontend::WindowSDL& w if (const auto dpi = SDL_GetWindowDisplayScale(window.GetSDLWindow()); dpi > 0.0f) { GetIO().FontGlobalScale = dpi; } + + std::at_quick_exit([] { SaveIniSettingsToDisk(GetIO().IniFilename); }); } void OnResize() { diff --git a/src/main.cpp b/src/main.cpp index 6b334e446..85581774b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -154,7 +154,7 @@ int main(int argc, char* argv[]) { // If no game directory is set and no command line argument, prompt for it if (Config::getGameInstallDirs().empty()) { std::cout << "Warning: No game folder set, please set it by calling shadps4" - " with the --add-game-folder argument"; + " with the --add-game-folder argument\n"; } if (!has_game_argument) { diff --git a/src/qt_gui/cheats_patches.cpp b/src/qt_gui/cheats_patches.cpp index 7239affd5..67f616f87 100644 --- a/src/qt_gui/cheats_patches.cpp +++ b/src/qt_gui/cheats_patches.cpp @@ -1387,7 +1387,7 @@ void CheatsPatches::applyPatch(const QString& patchName, bool enabled) { if (type == "mask_jump32") patchMask = MemoryPatcher::PatchMask::Mask_Jump32; - if (type == "mask" || type == "mask_jump32" && !maskOffsetStr.toStdString().empty()) { + if ((type == "mask" || type == "mask_jump32") && !maskOffsetStr.toStdString().empty()) { maskOffsetValue = std::stoi(maskOffsetStr.toStdString(), 0, 10); } diff --git a/src/qt_gui/main_window_ui.h b/src/qt_gui/main_window_ui.h index 2c4d4480b..4d3481c07 100644 --- a/src/qt_gui/main_window_ui.h +++ b/src/qt_gui/main_window_ui.h @@ -106,8 +106,6 @@ public: toggleLabelsAct = new QAction(MainWindow); toggleLabelsAct->setObjectName("toggleLabelsAct"); - toggleLabelsAct->setText( - QCoreApplication::translate("MainWindow", "Show Labels Under Icons")); toggleLabelsAct->setCheckable(true); toggleLabelsAct->setChecked(Config::getShowLabelsUnderIcons()); @@ -413,6 +411,8 @@ public: setThemeTokyoNight->setText("Tokyo Night"); setThemeOled->setText("OLED"); toolBar->setWindowTitle(QCoreApplication::translate("MainWindow", "toolBar", nullptr)); + toggleLabelsAct->setText( + QCoreApplication::translate("MainWindow", "Show Labels Under Icons")); } // retranslateUi }; diff --git a/src/qt_gui/translations/ar_SA.ts b/src/qt_gui/translations/ar_SA.ts index 7ef3c6171..e434b3259 100644 --- a/src/qt_gui/translations/ar_SA.ts +++ b/src/qt_gui/translations/ar_SA.ts @@ -66,7 +66,7 @@ You can delete the cheats you don't want after downloading them. - يمكنك حذف الشفرات التي لا تريدها بعد تنزيلها. + يمكنك حذف الشفرات التي لا 'تريدها بعد تنزيلها. Do you want to delete the selected file?\n%1 @@ -74,11 +74,11 @@ Select Patch File: - إختر ملف الباتش: + اختر مِلَف التصحيح: Download Patches - تحميل الباتشات + تحميل ملفات التصحيح Save @@ -98,15 +98,15 @@ No patch selected. - لم يتم اختيار أي تصحيح. + لم يتم تحديد أي مِلَف تصحيح. Unable to open files.json for reading. - تعذر فتح files.json للقراءة. + تعذّر فتح مِلَف files.json للقراءة. No patch file found for the current serial. - لم يتم العثور على مِلَفّ باتش للسيريال الحالي. + لم يتم العثور على مِلَف تصحيح للسيريال الحالي. Unable to open the file for reading. @@ -126,11 +126,11 @@ Options saved successfully. - تم حفظ الخيارات بنجاح. + تم حفظ الإعدادات. Invalid Source - مصدر غير صالح + المصدر غير صالح The selected source is invalid. @@ -138,11 +138,11 @@ File Exists - الملف موجود + المِلَف موجود مسبقًا File already exists. Do you want to replace it? - يوجد ملف بنفس الاسم. هل ترغب في استبداله؟ + المِلَف موجود مسبقًا. هل ترغب في استبداله؟ Failed to save file: @@ -158,7 +158,7 @@ No Cheats found for this game in this version of the selected repository,try another repository or a different version of the game. - لم يتم العثور على شفرات لهذه اللعبة في هذه النسخة من المستودع المحدد. حاول استخدام مستودع آخر أو نسخة مختلفة من اللعبة. + لم يتم العثور على شفرات لهذه اللعبة في هذا الإصدار من المستودع المحدد. جرّب مستودعًا آخر أو إصدارًا مختلفًا من اللعبة. Cheats Downloaded Successfully @@ -182,7 +182,7 @@ Patches Downloaded Successfully! All Patches available for all games have been downloaded, there is no need to download them individually for each game as happens in Cheats. If the patch does not appear, it may be that it does not exist for the specific serial and version of the game. - تم تنزيل التصحيحات بنجاح! تم تنزيل جميع التصحيحات لجميع الألعاب، ولا داعي لتنزيلها بشكل فردي لكل لعبة كما هو الحال مع الغش. إذا لم يظهر التحديث، قد يكون السبب أنه غير متوفر للإصدار وسيريال اللعبة المحدد. + تم تنزيل التصحيحات بنجاح! تم تنزيل جميع التصحيحات المتوفرة لجميع الألعاب، ولا حاجة إلى تنزيلها بشكل فردي لكل لعبة كما هو الحال مع الشفرات. إذا لم يظهر التصحيح، فقد لا يكون متوفرًا للسيريال أو الإصدار المحدد من اللعبة. Failed to parse JSON data from HTML. @@ -190,15 +190,15 @@ Failed to retrieve HTML page. - .HTML فشل في استرجاع صفحة + فشل في جلب صفحة HTML. The game is in version: %1 - النسخة الحالية للعبة هي: %1 + إصدار اللعبة الحالي: %1 The downloaded patch only works on version: %1 - الباتش الذي تم تنزيله يعمل فقط على الإصدار: %1 + التصحيح الذي تم تنزيله يعمل فقط مع الإصدار:%1 You may need to update your game. @@ -206,7 +206,7 @@ Incompatibility Notice - إشعار عدم التوافق + إشعار بعدم التوافق Failed to open file: @@ -238,7 +238,7 @@ Can't apply cheats before the game is started - لا يمكن تطبيق الغش قبل بدء اللعبة. + لا 'يمكن تطبيق الشفرات قبل بَدْء اللعبة Close @@ -249,7 +249,7 @@ CheckUpdate Auto Updater - محدث تلقائي + التحديثات التلقائية Error @@ -261,7 +261,7 @@ The Auto Updater allows up to 60 update checks per hour.\nYou have reached this limit. Please try again later. - يتيح التحديث التلقائي ما يصل إلى 60 عملية تحقق من التحديث في الساعة.\nلقد وصلت إلى هذا الحد. الرجاء المحاولة مرة أخرى لاحقًا. + تسمح التحديثات التلقائية بـ 60 عملية تحقق من التحديث في الساعة.\nلقد وصلت إلى الحد المسموح به. الرجاء المحاولة لاحقًا. Failed to parse update information. diff --git a/src/qt_gui/translations/vi_VN.ts b/src/qt_gui/translations/vi_VN.ts index e0bc7e4fe..c657888bf 100644 --- a/src/qt_gui/translations/vi_VN.ts +++ b/src/qt_gui/translations/vi_VN.ts @@ -384,23 +384,23 @@ Nothing - Không có gì + Không chạy được Boots - Giày ủng + Chạy được Menus - Menu + Vào được menu Ingame - Trong game + Vào được trò chơi Playable - Có thể chơi + Chơi được @@ -411,7 +411,7 @@ D-Pad - D-Pad + D-Pad Up @@ -447,7 +447,7 @@ Common Config - Common Config + Cài Đặt Chung Use per-game configs @@ -551,26 +551,26 @@ Save - Save + Lưu Apply - Apply + Áp dụng Restore Defaults - Restore Defaults + Khôi Phục Mặc Định Cancel - Cancel + Hủy EditorDialog Edit Keyboard + Mouse and Controller input bindings - Edit Keyboard + Mouse and Controller input bindings + Tùy chỉnh phím được gán cho Bàn phím + Chuột và Tay cầm Use Per-Game configs @@ -578,7 +578,7 @@ Error - Error + Lỗi Could not open the file for reading @@ -590,15 +590,15 @@ Save Changes - Save Changes + Lưu Thay Đổi Do you want to save changes? - Do you want to save changes? + Bạn có muốn lưu thay đổi? Help - Help + Trợ giúp Do you want to reset your custom default config to the original default config? @@ -706,15 +706,15 @@ h - h + giờ m - m + phút s - s + giây Compatibility is untested @@ -722,23 +722,23 @@ Game does not initialize properly / crashes the emulator - Game does not initialize properly / crashes the emulator + Trò chơi không được khởi chạy đúng cách / khiến giả lập bị văng Game boots, but only displays a blank screen - Game boots, but only displays a blank screen + Trò chơi có thể khởi chạy, nhưng chẳng hiện gì cả Game displays an image but does not go past the menu - Game displays an image but does not go past the menu + Trò chơi hiển thị được hình ảnh, nhưng không thể tiếp tục từ menu Game has game-breaking glitches or unplayable performance - Game has game-breaking glitches or unplayable performance + Trò chơi có lỗi ảnh hưởng đến trải nghiệm, hoặc hiệu năng khi chơi không ổn định Game can be completed with playable performance and no major glitches - Game can be completed with playable performance and no major glitches + Trò chơi có thể được hoàn thành từ đầu đến cuối, hiệu năng ổn định và không có lỗi ảnh hưởng đến trải nghiệm Click to see details on github @@ -1170,19 +1170,19 @@ Save - Save + Lưu Apply - Apply + Áp dụng Restore Defaults - Restore Defaults + Khôi Phục Mặc Định Cancel - Cancel + Hủy @@ -1193,7 +1193,7 @@ Boot Game - Boot Game + Khởi Chạy Trò Chơi Check for Updates @@ -1201,7 +1201,7 @@ About shadPS4 - About shadPS4 + Thông Tin Về shadPS4 Configure... @@ -1213,23 +1213,23 @@ Open shadPS4 Folder - Open shadPS4 Folder + Mở Thư Mục Của shadPS4 Exit - Exit + Thoát Exit shadPS4 - Exit shadPS4 + Thoát shadPS4 Exit the application. - Exit the application. + Thoát ứng dụng. Show Game List - Show Game List + Hiển Thị Danh Sách Trò Chơi Game List Refresh diff --git a/src/shader_recompiler/backend/spirv/emit_spirv.cpp b/src/shader_recompiler/backend/spirv/emit_spirv.cpp index 036df24d8..9ebb842cc 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv.cpp @@ -270,6 +270,10 @@ void SetupCapabilities(const Info& info, const Profile& profile, EmitContext& ct if (info.has_image_query) { ctx.AddCapability(spv::Capability::ImageQuery); } + if (info.uses_atomic_float_min_max && profile.supports_image_fp32_atomic_min_max) { + ctx.AddExtension("SPV_EXT_shader_atomic_float_min_max"); + ctx.AddCapability(spv::Capability::AtomicFloat32MinMaxEXT); + } if (info.uses_lane_id) { ctx.AddCapability(spv::Capability::GroupNonUniform); } @@ -335,8 +339,7 @@ void DefineEntryPoint(const Info& info, EmitContext& ctx, Id main) { ctx.AddExecutionMode(main, spv::ExecutionMode::OriginUpperLeft); } if (info.has_discard) { - ctx.AddExtension("SPV_EXT_demote_to_helper_invocation"); - ctx.AddCapability(spv::Capability::DemoteToHelperInvocationEXT); + ctx.AddCapability(spv::Capability::DemoteToHelperInvocation); } if (info.stores.GetAny(IR::Attribute::Depth)) { ctx.AddExecutionMode(main, spv::ExecutionMode::DepthReplacing); diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_atomic.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_atomic.cpp index c6ec65606..c3799fb4b 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_atomic.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_atomic.cpp @@ -38,6 +38,7 @@ Id BufferAtomicU32BoundsCheck(EmitContext& ctx, Id index, Id buffer_size, auto e const Id ib_label = ctx.OpLabel(); const Id oob_label = ctx.OpLabel(); const Id end_label = ctx.OpLabel(); + ctx.OpSelectionMerge(end_label, spv::SelectionControlMask::MaskNone); ctx.OpBranchConditional(in_bounds, ib_label, oob_label); ctx.AddLabel(ib_label); const Id ib_result = emit_func(); @@ -74,6 +75,14 @@ Id ImageAtomicU32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, Id va const auto [scope, semantics]{AtomicArgs(ctx)}; return (ctx.*atomic_func)(ctx.U32[1], pointer, scope, semantics, value); } + +Id ImageAtomicF32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, Id value, + Id (Sirit::Module::*atomic_func)(Id, Id, Id, Id, Id)) { + const auto& texture = ctx.images[handle & 0xFFFF]; + const Id pointer{ctx.OpImageTexelPointer(ctx.image_f32, texture.id, coords, ctx.ConstU32(0U))}; + const auto [scope, semantics]{AtomicArgs(ctx)}; + return (ctx.*atomic_func)(ctx.F32[1], pointer, scope, semantics, value); +} } // Anonymous namespace Id EmitSharedAtomicIAdd32(EmitContext& ctx, Id offset, Id value) { @@ -186,6 +195,40 @@ Id EmitImageAtomicUMax32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords return ImageAtomicU32(ctx, inst, handle, coords, value, &Sirit::Module::OpAtomicUMax); } +Id EmitImageAtomicFMax32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, Id value) { + if (ctx.profile.supports_image_fp32_atomic_min_max) { + return ImageAtomicF32(ctx, inst, handle, coords, value, &Sirit::Module::OpAtomicFMax); + } + + const auto u32_value = ctx.OpBitcast(ctx.U32[1], value); + const auto sign_bit_set = + ctx.OpBitFieldUExtract(ctx.U32[1], u32_value, ctx.ConstU32(31u), ctx.ConstU32(1u)); + + const auto result = ctx.OpSelect( + ctx.F32[1], sign_bit_set, + EmitBitCastF32U32(ctx, EmitImageAtomicUMin32(ctx, inst, handle, coords, u32_value)), + EmitBitCastF32U32(ctx, EmitImageAtomicSMax32(ctx, inst, handle, coords, u32_value))); + + return result; +} + +Id EmitImageAtomicFMin32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, Id value) { + if (ctx.profile.supports_image_fp32_atomic_min_max) { + return ImageAtomicF32(ctx, inst, handle, coords, value, &Sirit::Module::OpAtomicFMin); + } + + const auto u32_value = ctx.OpBitcast(ctx.U32[1], value); + const auto sign_bit_set = + ctx.OpBitFieldUExtract(ctx.U32[1], u32_value, ctx.ConstU32(31u), ctx.ConstU32(1u)); + + const auto result = ctx.OpSelect( + ctx.F32[1], sign_bit_set, + EmitBitCastF32U32(ctx, EmitImageAtomicUMax32(ctx, inst, handle, coords, u32_value)), + EmitBitCastF32U32(ctx, EmitImageAtomicSMin32(ctx, inst, handle, coords, u32_value))); + + return result; +} + Id EmitImageAtomicInc32(EmitContext&, IR::Inst*, u32, Id, Id) { // TODO: This is not yet implemented throw NotImplementedException("SPIR-V Instruction"); diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_bitwise_conversion.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_bitwise_conversion.cpp index 56a6abc05..43655ba3f 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_bitwise_conversion.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_bitwise_conversion.cpp @@ -64,10 +64,6 @@ Id EmitBitCastU32F32(EmitContext& ctx, Id value) { return ctx.OpBitcast(ctx.U32[1], value); } -Id EmitBitCastU64F64(EmitContext& ctx, Id value) { - return ctx.OpBitcast(ctx.U64, value); -} - Id EmitBitCastF16U16(EmitContext& ctx, Id value) { return ctx.OpBitcast(ctx.F16[1], value); } @@ -76,10 +72,6 @@ Id EmitBitCastF32U32(EmitContext& ctx, Id value) { return ctx.OpBitcast(ctx.F32[1], value); } -void EmitBitCastF64U64(EmitContext&) { - UNREACHABLE_MSG("SPIR-V Instruction"); -} - Id EmitPackUint2x32(EmitContext& ctx, Id value) { return ctx.OpBitcast(ctx.U64, value); } @@ -88,10 +80,14 @@ Id EmitUnpackUint2x32(EmitContext& ctx, Id value) { return ctx.OpBitcast(ctx.U32[2], value); } -Id EmitPackFloat2x32(EmitContext& ctx, Id value) { +Id EmitPackDouble2x32(EmitContext& ctx, Id value) { return ctx.OpBitcast(ctx.F64[1], value); } +Id EmitUnpackDouble2x32(EmitContext& ctx, Id value) { + return ctx.OpBitcast(ctx.U32[2], value); +} + Id EmitPackUnorm2x16(EmitContext& ctx, Id value) { return ctx.OpPackUnorm2x16(ctx.U32[1], value); } diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp index e4071bb95..83e8afd78 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp @@ -529,7 +529,7 @@ void EmitStoreBufferBoundsCheck(EmitContext& ctx, Id index, Id buffer_size, auto // Bounds checking enabled, wrap in a conditional branch. auto compare_index = index; if (N > 1) { - index = ctx.OpIAdd(ctx.U32[1], index, ctx.ConstU32(N - 1)); + compare_index = ctx.OpIAdd(ctx.U32[1], index, ctx.ConstU32(N - 1)); } const Id in_bounds = ctx.OpULessThan(ctx.U1[1], compare_index, buffer_size); const Id in_bounds_label = ctx.OpLabel(); diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h b/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h index 9b7528be8..269f372d5 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h +++ b/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h @@ -202,13 +202,12 @@ Id EmitSelectF32(EmitContext& ctx, Id cond, Id true_value, Id false_value); Id EmitSelectF64(EmitContext& ctx, Id cond, Id true_value, Id false_value); Id EmitBitCastU16F16(EmitContext& ctx, Id value); Id EmitBitCastU32F32(EmitContext& ctx, Id value); -Id EmitBitCastU64F64(EmitContext& ctx, Id value); Id EmitBitCastF16U16(EmitContext& ctx, Id value); Id EmitBitCastF32U32(EmitContext& ctx, Id value); -void EmitBitCastF64U64(EmitContext& ctx); Id EmitPackUint2x32(EmitContext& ctx, Id value); Id EmitUnpackUint2x32(EmitContext& ctx, Id value); -Id EmitPackFloat2x32(EmitContext& ctx, Id value); +Id EmitPackDouble2x32(EmitContext& ctx, Id value); +Id EmitUnpackDouble2x32(EmitContext& ctx, Id value); Id EmitPackUnorm2x16(EmitContext& ctx, Id value); Id EmitUnpackUnorm2x16(EmitContext& ctx, Id value); Id EmitPackSnorm2x16(EmitContext& ctx, Id value); @@ -483,6 +482,8 @@ Id EmitImageAtomicSMin32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords Id EmitImageAtomicUMin32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, Id value); Id EmitImageAtomicSMax32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, Id value); Id EmitImageAtomicUMax32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, Id value); +Id EmitImageAtomicFMax32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, Id value); +Id EmitImageAtomicFMin32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, Id value); Id EmitImageAtomicInc32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, Id value); Id EmitImageAtomicDec32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, Id value); Id EmitImageAtomicAnd32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, Id value); diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp index 8433251ff..2640030df 100644 --- a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp +++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp @@ -869,6 +869,7 @@ void EmitContext::DefineImagesAndSamplers() { } if (std::ranges::any_of(info.images, &ImageResource::is_atomic)) { image_u32 = TypePointer(spv::StorageClass::Image, U32[1]); + image_f32 = TypePointer(spv::StorageClass::Image, F32[1]); } if (info.samplers.empty()) { return; diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.h b/src/shader_recompiler/backend/spirv/spirv_emit_context.h index 784748658..38d55e0e4 100644 --- a/src/shader_recompiler/backend/spirv/spirv_emit_context.h +++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.h @@ -207,6 +207,7 @@ public: Id invocation_id{}; Id subgroup_local_invocation_id{}; Id image_u32{}; + Id image_f32{}; Id shared_memory_u8{}; Id shared_memory_u16{}; diff --git a/src/shader_recompiler/frontend/control_flow_graph.cpp b/src/shader_recompiler/frontend/control_flow_graph.cpp index cf1882b8c..b53db9e94 100644 --- a/src/shader_recompiler/frontend/control_flow_graph.cpp +++ b/src/shader_recompiler/frontend/control_flow_graph.cpp @@ -4,6 +4,7 @@ #include #include #include "common/assert.h" +#include "common/logging/log.h" #include "shader_recompiler/frontend/control_flow_graph.h" namespace Shader::Gcn { @@ -67,6 +68,39 @@ static bool IgnoresExecMask(const GcnInst& inst) { return false; } +static std::optional ResolveSetPcTarget(std::span list, u32 setpc_index, + std::span pc_map) { + if (setpc_index < 3) { + return std::nullopt; + } + + const auto& getpc = list[setpc_index - 3]; + const auto& arith = list[setpc_index - 2]; + const auto& setpc = list[setpc_index]; + + if (getpc.opcode != Opcode::S_GETPC_B64 || + !(arith.opcode == Opcode::S_ADD_U32 || arith.opcode == Opcode::S_SUB_U32) || + setpc.opcode != Opcode::S_SETPC_B64) + return std::nullopt; + + if (getpc.dst[0].code != setpc.src[0].code || arith.dst[0].code != setpc.src[0].code) + return std::nullopt; + + if (arith.src_count < 2 || arith.src[1].field != OperandField::LiteralConst) + return std::nullopt; + + const u32 imm = arith.src[1].code; + + const s32 signed_offset = + (arith.opcode == Opcode::S_ADD_U32) ? static_cast(imm) : -static_cast(imm); + + const u32 base_pc = pc_map[setpc_index - 3] + getpc.length; + + const u32 result_pc = static_cast(static_cast(base_pc) + signed_offset); + LOG_DEBUG(Render_Recompiler, "SetPC target: {} + {} = {}", base_pc, signed_offset, result_pc); + return result_pc & ~0x3u; +} + static constexpr size_t LabelReserveSize = 32; CFG::CFG(Common::ObjectPool& block_pool_, std::span inst_list_) @@ -89,9 +123,20 @@ void CFG::EmitLabels() { index_to_pc[i] = pc; const GcnInst inst = inst_list[i]; if (inst.IsUnconditionalBranch()) { - const u32 target = inst.BranchTarget(pc); + u32 target = inst.BranchTarget(pc); + if (inst.opcode == Opcode::S_SETPC_B64) { + if (auto t = ResolveSetPcTarget(inst_list, i, index_to_pc)) { + target = *t; + } else { + ASSERT_MSG( + false, + "S_SETPC_B64 without a resolvable offset at PC {:#x} (Index {}): Involved " + "instructions not recognized or invalid pattern", + pc, i); + } + } AddLabel(target); - // Emit this label so that the block ends with s_branch instruction + // Emit this label so that the block ends with the branching instruction AddLabel(pc + inst.length); } else if (inst.IsConditionalBranch()) { const u32 true_label = inst.BranchTarget(pc); @@ -102,6 +147,7 @@ void CFG::EmitLabels() { const u32 next_label = pc + inst.length; AddLabel(next_label); } + pc += inst.length; } index_to_pc[inst_list.size()] = pc; @@ -280,7 +326,18 @@ void CFG::LinkBlocks() { // Find the branch targets from the instruction and link the blocks. // Note: Block end address is one instruction after end_inst. const u32 branch_pc = block.end - end_inst.length; - const u32 target_pc = end_inst.BranchTarget(branch_pc); + u32 target_pc = 0; + if (end_inst.opcode == Opcode::S_SETPC_B64) { + auto tgt = ResolveSetPcTarget(inst_list, block.end_index, index_to_pc); + ASSERT_MSG(tgt, + "S_SETPC_B64 without a resolvable offset at PC {:#x} (Index {}): Involved " + "instructions not recognized or invalid pattern", + branch_pc, block.end_index); + target_pc = *tgt; + } else { + target_pc = end_inst.BranchTarget(branch_pc); + } + if (end_inst.IsUnconditionalBranch()) { auto* target_block = get_block(target_pc); ++target_block->num_predecessors; diff --git a/src/shader_recompiler/frontend/instruction.cpp b/src/shader_recompiler/frontend/instruction.cpp index a0c132053..246c2b85a 100644 --- a/src/shader_recompiler/frontend/instruction.cpp +++ b/src/shader_recompiler/frontend/instruction.cpp @@ -18,7 +18,7 @@ bool GcnInst::IsTerminateInstruction() const { } bool GcnInst::IsUnconditionalBranch() const { - return opcode == Opcode::S_BRANCH; + return opcode == Opcode::S_BRANCH || opcode == Opcode::S_SETPC_B64; } bool GcnInst::IsFork() const { diff --git a/src/shader_recompiler/frontend/translate/scalar_flow.cpp b/src/shader_recompiler/frontend/translate/scalar_flow.cpp index 0e02b77a2..cd1cf51f0 100644 --- a/src/shader_recompiler/frontend/translate/scalar_flow.cpp +++ b/src/shader_recompiler/frontend/translate/scalar_flow.cpp @@ -18,6 +18,7 @@ void Translator::EmitFlowControl(u32 pc, const GcnInst& inst) { return; case Opcode::S_GETPC_B64: return S_GETPC_B64(pc, inst); + case Opcode::S_SETPC_B64: case Opcode::S_WAITCNT: case Opcode::S_NOP: case Opcode::S_ENDPGM: diff --git a/src/shader_recompiler/frontend/translate/translate.cpp b/src/shader_recompiler/frontend/translate/translate.cpp index 230f3917f..e49f95d9a 100644 --- a/src/shader_recompiler/frontend/translate/translate.cpp +++ b/src/shader_recompiler/frontend/translate/translate.cpp @@ -336,7 +336,7 @@ T Translator::GetSrc64(const InstOperand& operand) { const auto value_lo = ir.GetVectorReg(IR::VectorReg(operand.code)); const auto value_hi = ir.GetVectorReg(IR::VectorReg(operand.code + 1)); if constexpr (is_float) { - value = ir.PackFloat2x32(ir.CompositeConstruct(value_lo, value_hi)); + value = ir.PackDouble2x32(ir.CompositeConstruct(value_lo, value_hi)); } else { value = ir.PackUint2x32(ir.CompositeConstruct(value_lo, value_hi)); } @@ -444,10 +444,9 @@ void Translator::SetDst64(const InstOperand& operand, const IR::U64F64& value_ra value_untyped = ir.FPSaturate(value_raw); } } - const IR::U64 value = - is_float ? ir.BitCast(IR::F64{value_untyped}) : IR::U64{value_untyped}; - const IR::Value unpacked{ir.UnpackUint2x32(value)}; + const IR::Value unpacked{is_float ? ir.UnpackDouble2x32(IR::F64{value_untyped}) + : ir.UnpackUint2x32(IR::U64{value_untyped})}; const IR::U32 lo{ir.CompositeExtract(unpacked, 0U)}; const IR::U32 hi{ir.CompositeExtract(unpacked, 1U)}; switch (operand.field) { @@ -518,7 +517,9 @@ void Translator::EmitFetch(const GcnInst& inst) { const auto values = ir.CompositeConstruct(ir.GetAttribute(attr, 0), ir.GetAttribute(attr, 1), ir.GetAttribute(attr, 2), ir.GetAttribute(attr, 3)); - const auto swizzled = ApplySwizzle(ir, values, buffer.DstSelect()); + const auto converted = + IR::ApplyReadNumberConversionVec4(ir, values, buffer.GetNumberConversion()); + const auto swizzled = ApplySwizzle(ir, converted, buffer.DstSelect()); for (u32 i = 0; i < 4; i++) { ir.SetVectorReg(dst_reg++, IR::F32{ir.CompositeExtract(swizzled, i)}); } diff --git a/src/shader_recompiler/frontend/translate/vector_alu.cpp b/src/shader_recompiler/frontend/translate/vector_alu.cpp index 787cf6ad3..3ce86c131 100644 --- a/src/shader_recompiler/frontend/translate/vector_alu.cpp +++ b/src/shader_recompiler/frontend/translate/vector_alu.cpp @@ -513,13 +513,13 @@ void Translator::V_LSHLREV_B32(const GcnInst& inst) { void Translator::V_AND_B32(const GcnInst& inst) { const IR::U32 src0{GetSrc(inst.src[0])}; - const IR::U32 src1{ir.GetVectorReg(IR::VectorReg(inst.src[1].code))}; + const IR::U32 src1{GetSrc(inst.src[1])}; SetDst(inst.dst[0], ir.BitwiseAnd(src0, src1)); } void Translator::V_OR_B32(bool is_xor, const GcnInst& inst) { const IR::U32 src0{GetSrc(inst.src[0])}; - const IR::U32 src1{ir.GetVectorReg(IR::VectorReg(inst.src[1].code))}; + const IR::U32 src1{GetSrc(inst.src[1])}; SetDst(inst.dst[0], is_xor ? ir.BitwiseXor(src0, src1) : IR::U32(ir.BitwiseOr(src0, src1))); } @@ -579,7 +579,7 @@ void Translator::V_MBCNT_U32_B32(bool is_low, const GcnInst& inst) { void Translator::V_ADD_I32(const GcnInst& inst) { // Signed or unsigned components const IR::U32 src0{GetSrc(inst.src[0])}; - const IR::U32 src1{ir.GetVectorReg(IR::VectorReg(inst.src[1].code))}; + const IR::U32 src1{GetSrc(inst.src[1])}; const IR::U32 result{ir.IAdd(src0, src1)}; SetDst(inst.dst[0], result); diff --git a/src/shader_recompiler/frontend/translate/vector_memory.cpp b/src/shader_recompiler/frontend/translate/vector_memory.cpp index ed7788d8c..5639bc56a 100644 --- a/src/shader_recompiler/frontend/translate/vector_memory.cpp +++ b/src/shader_recompiler/frontend/translate/vector_memory.cpp @@ -115,8 +115,12 @@ void Translator::EmitVectorMemory(const GcnInst& inst) { return IMAGE_ATOMIC(AtomicOp::Smin, inst); case Opcode::IMAGE_ATOMIC_UMIN: return IMAGE_ATOMIC(AtomicOp::Umin, inst); + case Opcode::IMAGE_ATOMIC_FMIN: + return IMAGE_ATOMIC(AtomicOp::Fmin, inst); case Opcode::IMAGE_ATOMIC_SMAX: return IMAGE_ATOMIC(AtomicOp::Smax, inst); + case Opcode::IMAGE_ATOMIC_FMAX: + return IMAGE_ATOMIC(AtomicOp::Fmax, inst); case Opcode::IMAGE_ATOMIC_UMAX: return IMAGE_ATOMIC(AtomicOp::Umax, inst); case Opcode::IMAGE_ATOMIC_AND: @@ -139,6 +143,7 @@ void Translator::EmitVectorMemory(const GcnInst& inst) { case Opcode::IMAGE_SAMPLE_C_LZ: case Opcode::IMAGE_SAMPLE_O: case Opcode::IMAGE_SAMPLE_L_O: + case Opcode::IMAGE_SAMPLE_B_O: case Opcode::IMAGE_SAMPLE_LZ_O: case Opcode::IMAGE_SAMPLE_C_O: case Opcode::IMAGE_SAMPLE_C_LZ_O: @@ -466,6 +471,10 @@ void Translator::IMAGE_ATOMIC(AtomicOp op, const GcnInst& inst) { return ir.ImageAtomicIMax(handle, body, value, true, info); case AtomicOp::Umax: return ir.ImageAtomicUMax(handle, body, value, info); + case AtomicOp::Fmax: + return ir.ImageAtomicFMax(handle, body, value, info); + case AtomicOp::Fmin: + return ir.ImageAtomicFMin(handle, body, value, info); case AtomicOp::And: return ir.ImageAtomicAnd(handle, body, value, info); case AtomicOp::Or: diff --git a/src/shader_recompiler/info.h b/src/shader_recompiler/info.h index 8dcf9c5c4..12e48c8e4 100644 --- a/src/shader_recompiler/info.h +++ b/src/shader_recompiler/info.h @@ -196,6 +196,7 @@ struct Info { bool has_discard{}; bool has_image_gather{}; bool has_image_query{}; + bool uses_atomic_float_min_max{}; bool uses_lane_id{}; bool uses_group_quad{}; bool uses_group_ballot{}; @@ -280,6 +281,11 @@ constexpr AmdGpu::Image ImageResource::GetSharp(const Info& info) const noexcept // Fall back to null image if unbound. return AmdGpu::Image::Null(); } + const auto data_fmt = image.GetDataFmt(); + if (is_depth && data_fmt != AmdGpu::DataFormat::Format16 && + data_fmt != AmdGpu::DataFormat::Format32) { + return AmdGpu::Image::NullDepth(); + } return image; } diff --git a/src/shader_recompiler/ir/ir_emitter.cpp b/src/shader_recompiler/ir/ir_emitter.cpp index e8836bb4c..01d945178 100644 --- a/src/shader_recompiler/ir/ir_emitter.cpp +++ b/src/shader_recompiler/ir/ir_emitter.cpp @@ -84,16 +84,6 @@ IR::F16 IREmitter::BitCast(const IR::U16& value) { return Inst(Opcode::BitCastF16U16, value); } -template <> -IR::U64 IREmitter::BitCast(const IR::F64& value) { - return Inst(Opcode::BitCastU64F64, value); -} - -template <> -IR::F64 IREmitter::BitCast(const IR::U64& value) { - return Inst(Opcode::BitCastF64U64, value); -} - U1 IREmitter::ConditionRef(const U1& value) { return Inst(Opcode::ConditionRef, value); } @@ -841,8 +831,12 @@ Value IREmitter::UnpackUint2x32(const U64& value) { return Inst(Opcode::UnpackUint2x32, value); } -F64 IREmitter::PackFloat2x32(const Value& vector) { - return Inst(Opcode::PackFloat2x32, vector); +F64 IREmitter::PackDouble2x32(const Value& vector) { + return Inst(Opcode::PackDouble2x32, vector); +} + +Value IREmitter::UnpackDouble2x32(const F64& value) { + return Inst(Opcode::UnpackDouble2x32, value); } U32 IREmitter::Pack2x16(const AmdGpu::NumberFormat number_format, const Value& vector) { @@ -1876,6 +1870,16 @@ Value IREmitter::ImageAtomicUMax(const Value& handle, const Value& coords, const return Inst(Opcode::ImageAtomicUMax32, Flags{info}, handle, coords, value); } +Value IREmitter::ImageAtomicFMax(const Value& handle, const Value& coords, const Value& value, + TextureInstInfo info) { + return Inst(Opcode::ImageAtomicFMax32, Flags{info}, handle, coords, value); +} + +Value IREmitter::ImageAtomicFMin(const Value& handle, const Value& coords, const Value& value, + TextureInstInfo info) { + return Inst(Opcode::ImageAtomicFMin32, Flags{info}, handle, coords, value); +} + Value IREmitter::ImageAtomicIMax(const Value& handle, const Value& coords, const Value& value, bool is_signed, TextureInstInfo info) { return is_signed ? ImageAtomicSMax(handle, coords, value, info) diff --git a/src/shader_recompiler/ir/ir_emitter.h b/src/shader_recompiler/ir/ir_emitter.h index 186d83a07..8f8a12736 100644 --- a/src/shader_recompiler/ir/ir_emitter.h +++ b/src/shader_recompiler/ir/ir_emitter.h @@ -181,7 +181,8 @@ public: [[nodiscard]] U64 PackUint2x32(const Value& vector); [[nodiscard]] Value UnpackUint2x32(const U64& value); - [[nodiscard]] F64 PackFloat2x32(const Value& vector); + [[nodiscard]] F64 PackDouble2x32(const Value& vector); + [[nodiscard]] Value UnpackDouble2x32(const F64& value); [[nodiscard]] U32 Pack2x16(AmdGpu::NumberFormat number_format, const Value& vector); [[nodiscard]] Value Unpack2x16(AmdGpu::NumberFormat number_format, const U32& value); @@ -320,6 +321,10 @@ public: const Value& value, TextureInstInfo info); [[nodiscard]] Value ImageAtomicUMax(const Value& handle, const Value& coords, const Value& value, TextureInstInfo info); + [[nodiscard]] Value ImageAtomicFMax(const Value& handle, const Value& coords, + const Value& value, TextureInstInfo info); + [[nodiscard]] Value ImageAtomicFMin(const Value& handle, const Value& coords, + const Value& value, TextureInstInfo info); [[nodiscard]] Value ImageAtomicIMax(const Value& handle, const Value& coords, const Value& value, bool is_signed, TextureInstInfo info); [[nodiscard]] Value ImageAtomicInc(const Value& handle, const Value& coords, const Value& value, diff --git a/src/shader_recompiler/ir/microinstruction.cpp b/src/shader_recompiler/ir/microinstruction.cpp index 580156f5b..a57310fb9 100644 --- a/src/shader_recompiler/ir/microinstruction.cpp +++ b/src/shader_recompiler/ir/microinstruction.cpp @@ -94,6 +94,8 @@ bool Inst::MayHaveSideEffects() const noexcept { case Opcode::ImageAtomicUMin32: case Opcode::ImageAtomicSMax32: case Opcode::ImageAtomicUMax32: + case Opcode::ImageAtomicFMax32: + case Opcode::ImageAtomicFMin32: case Opcode::ImageAtomicInc32: case Opcode::ImageAtomicDec32: case Opcode::ImageAtomicAnd32: diff --git a/src/shader_recompiler/ir/opcodes.inc b/src/shader_recompiler/ir/opcodes.inc index 4932ff9a0..ab6dbfde9 100644 --- a/src/shader_recompiler/ir/opcodes.inc +++ b/src/shader_recompiler/ir/opcodes.inc @@ -191,14 +191,13 @@ OPCODE(SelectF64, F64, U1, // Bitwise conversions OPCODE(BitCastU16F16, U16, F16, ) OPCODE(BitCastU32F32, U32, F32, ) -OPCODE(BitCastU64F64, U64, F64, ) OPCODE(BitCastF16U16, F16, U16, ) OPCODE(BitCastF32U32, F32, U32, ) -OPCODE(BitCastF64U64, F64, U64, ) OPCODE(PackUint2x32, U64, U32x2, ) OPCODE(UnpackUint2x32, U32x2, U64, ) -OPCODE(PackFloat2x32, F64, F32x2, ) +OPCODE(PackDouble2x32, F64, U32x2, ) +OPCODE(UnpackDouble2x32, U32x2, F64, ) OPCODE(PackUnorm2x16, U32, F32x2, ) OPCODE(UnpackUnorm2x16, F32x2, U32, ) @@ -421,6 +420,8 @@ OPCODE(ImageAtomicSMin32, U32, Opaq OPCODE(ImageAtomicUMin32, U32, Opaque, Opaque, U32, ) OPCODE(ImageAtomicSMax32, U32, Opaque, Opaque, U32, ) OPCODE(ImageAtomicUMax32, U32, Opaque, Opaque, U32, ) +OPCODE(ImageAtomicFMax32, F32, Opaque, Opaque, F32, ) +OPCODE(ImageAtomicFMin32, F32, Opaque, Opaque, F32, ) OPCODE(ImageAtomicInc32, U32, Opaque, Opaque, U32, ) OPCODE(ImageAtomicDec32, U32, Opaque, Opaque, U32, ) OPCODE(ImageAtomicAnd32, U32, Opaque, Opaque, U32, ) diff --git a/src/shader_recompiler/ir/passes/ir_passes.h b/src/shader_recompiler/ir/passes/ir_passes.h index 760dbb112..06e4ac850 100644 --- a/src/shader_recompiler/ir/passes/ir_passes.h +++ b/src/shader_recompiler/ir/passes/ir_passes.h @@ -21,6 +21,7 @@ void ReadLaneEliminationPass(IR::Program& program); void ResourceTrackingPass(IR::Program& program); void CollectShaderInfoPass(IR::Program& program); void LowerBufferFormatToRaw(IR::Program& program); +void LowerFp64ToFp32(IR::Program& program); void RingAccessElimination(const IR::Program& program, const RuntimeInfo& runtime_info); void TessellationPreprocess(IR::Program& program, RuntimeInfo& runtime_info); void HullShaderTransform(IR::Program& program, RuntimeInfo& runtime_info); diff --git a/src/shader_recompiler/ir/passes/lower_buffer_format_to_raw.cpp b/src/shader_recompiler/ir/passes/lower_buffer_format_to_raw.cpp index 3fdc6f0cd..65be02541 100644 --- a/src/shader_recompiler/ir/passes/lower_buffer_format_to_raw.cpp +++ b/src/shader_recompiler/ir/passes/lower_buffer_format_to_raw.cpp @@ -196,13 +196,19 @@ static void LowerBufferFormatInst(IR::Block& block, IR::Inst& inst, Info& info) const auto buffer{desc.GetSharp(info)}; const auto is_inst_typed = flags.inst_data_fmt != AmdGpu::DataFormat::FormatInvalid; - const auto data_format = is_inst_typed ? flags.inst_data_fmt.Value() : buffer.GetDataFmt(); - const auto num_format = is_inst_typed ? flags.inst_num_fmt.Value() : buffer.GetNumberFmt(); + const auto data_format = + is_inst_typed ? AmdGpu::RemapDataFormat(flags.inst_data_fmt.Value()) : buffer.GetDataFmt(); const auto format_info = FormatInfo{ .data_format = data_format, - .num_format = num_format, - .swizzle = is_inst_typed ? AmdGpu::IdentityMapping : buffer.DstSelect(), - .num_conversion = AmdGpu::MapNumberConversion(num_format), + .num_format = is_inst_typed + ? AmdGpu::RemapNumberFormat(flags.inst_num_fmt.Value(), data_format) + : buffer.GetNumberFmt(), + .swizzle = is_inst_typed + ? AmdGpu::RemapSwizzle(flags.inst_data_fmt.Value(), AmdGpu::IdentityMapping) + : buffer.DstSelect(), + .num_conversion = is_inst_typed ? AmdGpu::MapNumberConversion(flags.inst_num_fmt.Value(), + flags.inst_data_fmt.Value()) + : buffer.GetNumberConversion(), .num_components = AmdGpu::NumComponents(data_format), }; diff --git a/src/shader_recompiler/ir/passes/lower_fp64_to_fp32.cpp b/src/shader_recompiler/ir/passes/lower_fp64_to_fp32.cpp new file mode 100644 index 000000000..3c30e75b4 --- /dev/null +++ b/src/shader_recompiler/ir/passes/lower_fp64_to_fp32.cpp @@ -0,0 +1,186 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "shader_recompiler/info.h" +#include "shader_recompiler/ir/basic_block.h" +#include "shader_recompiler/ir/ir_emitter.h" +#include "shader_recompiler/ir/program.h" + +namespace Shader::Optimization { + +constexpr s32 F64ToF32Exp = +1023 - 127; +constexpr s32 F32ToF64Exp = +127 - 1023; + +static IR::F32 PackedF64ToF32(IR::IREmitter& ir, const IR::Value& packed) { + const IR::U32 lo{ir.CompositeExtract(packed, 0)}; + const IR::U32 hi{ir.CompositeExtract(packed, 1)}; + const IR::U32 sign{ir.BitFieldExtract(hi, ir.Imm32(31), ir.Imm32(1))}; + const IR::U32 exp{ir.BitFieldExtract(hi, ir.Imm32(20), ir.Imm32(11))}; + const IR::U32 mantissa_hi{ir.BitFieldExtract(hi, ir.Imm32(0), ir.Imm32(20))}; + const IR::U32 mantissa_lo{ir.BitFieldExtract(lo, ir.Imm32(29), ir.Imm32(3))}; + const IR::U32 mantissa{ + ir.BitwiseOr(ir.ShiftLeftLogical(mantissa_hi, ir.Imm32(3)), mantissa_lo)}; + const IR::U32 exp_if_subnorm{ + ir.Select(ir.IEqual(exp, ir.Imm32(0)), ir.Imm32(0), ir.IAdd(exp, ir.Imm32(F64ToF32Exp)))}; + const IR::U32 exp_if_infnan{ + ir.Select(ir.IEqual(exp, ir.Imm32(0x7ff)), ir.Imm32(0xff), exp_if_subnorm)}; + const IR::U32 result{ + ir.BitwiseOr(ir.ShiftLeftLogical(sign, ir.Imm32(31)), + ir.BitwiseOr(ir.ShiftLeftLogical(exp_if_infnan, ir.Imm32(23)), mantissa))}; + return ir.BitCast(result); +} + +IR::Value F32ToPackedF64(IR::IREmitter& ir, const IR::Value& raw) { + const IR::U32 value{ir.BitCast(IR::F32(raw))}; + const IR::U32 sign{ir.BitFieldExtract(value, ir.Imm32(31), ir.Imm32(1))}; + const IR::U32 exp{ir.BitFieldExtract(value, ir.Imm32(23), ir.Imm32(8))}; + const IR::U32 mantissa{ir.BitFieldExtract(value, ir.Imm32(0), ir.Imm32(23))}; + const IR::U32 mantissa_hi{ir.BitFieldExtract(mantissa, ir.Imm32(3), ir.Imm32(20))}; + const IR::U32 mantissa_lo{ir.BitFieldExtract(mantissa, ir.Imm32(0), ir.Imm32(3))}; + const IR::U32 exp_if_subnorm{ + ir.Select(ir.IEqual(exp, ir.Imm32(0)), ir.Imm32(0), ir.IAdd(exp, ir.Imm32(F32ToF64Exp)))}; + const IR::U32 exp_if_infnan{ + ir.Select(ir.IEqual(exp, ir.Imm32(0xff)), ir.Imm32(0x7ff), exp_if_subnorm)}; + const IR::U32 lo{ir.ShiftLeftLogical(mantissa_lo, ir.Imm32(29))}; + const IR::U32 hi{ + ir.BitwiseOr(ir.ShiftLeftLogical(sign, ir.Imm32(31)), + ir.BitwiseOr(ir.ShiftLeftLogical(exp_if_infnan, ir.Imm32(20)), mantissa_hi))}; + return ir.CompositeConstruct(lo, hi); +} + +static IR::Opcode Replace(IR::Opcode op) { + switch (op) { + case IR::Opcode::CompositeConstructF64x2: + return IR::Opcode::CompositeConstructF32x2; + case IR::Opcode::CompositeConstructF64x3: + return IR::Opcode::CompositeConstructF32x3; + case IR::Opcode::CompositeConstructF64x4: + return IR::Opcode::CompositeConstructF32x4; + case IR::Opcode::CompositeExtractF64x2: + return IR::Opcode::CompositeExtractF32x2; + case IR::Opcode::CompositeExtractF64x3: + return IR::Opcode::CompositeExtractF32x3; + case IR::Opcode::CompositeExtractF64x4: + return IR::Opcode::CompositeExtractF32x4; + case IR::Opcode::CompositeInsertF64x2: + return IR::Opcode::CompositeInsertF32x2; + case IR::Opcode::CompositeInsertF64x3: + return IR::Opcode::CompositeInsertF32x3; + case IR::Opcode::CompositeInsertF64x4: + return IR::Opcode::CompositeInsertF32x4; + case IR::Opcode::CompositeShuffleF64x2: + return IR::Opcode::CompositeShuffleF32x2; + case IR::Opcode::CompositeShuffleF64x3: + return IR::Opcode::CompositeShuffleF32x3; + case IR::Opcode::CompositeShuffleF64x4: + return IR::Opcode::CompositeShuffleF32x4; + case IR::Opcode::SelectF64: + return IR::Opcode::SelectF64; + case IR::Opcode::FPAbs64: + return IR::Opcode::FPAbs32; + case IR::Opcode::FPAdd64: + return IR::Opcode::FPAdd32; + case IR::Opcode::FPFma64: + return IR::Opcode::FPFma32; + case IR::Opcode::FPMax64: + return IR::Opcode::FPMax32; + case IR::Opcode::FPMin64: + return IR::Opcode::FPMin32; + case IR::Opcode::FPMul64: + return IR::Opcode::FPMul32; + case IR::Opcode::FPDiv64: + return IR::Opcode::FPDiv32; + case IR::Opcode::FPNeg64: + return IR::Opcode::FPNeg32; + case IR::Opcode::FPRecip64: + return IR::Opcode::FPRecip32; + case IR::Opcode::FPRecipSqrt64: + return IR::Opcode::FPRecipSqrt32; + case IR::Opcode::FPSaturate64: + return IR::Opcode::FPSaturate32; + case IR::Opcode::FPClamp64: + return IR::Opcode::FPClamp32; + case IR::Opcode::FPRoundEven64: + return IR::Opcode::FPRoundEven32; + case IR::Opcode::FPFloor64: + return IR::Opcode::FPFloor32; + case IR::Opcode::FPCeil64: + return IR::Opcode::FPCeil32; + case IR::Opcode::FPTrunc64: + return IR::Opcode::FPTrunc32; + case IR::Opcode::FPFract64: + return IR::Opcode::FPFract32; + case IR::Opcode::FPFrexpSig64: + return IR::Opcode::FPFrexpSig32; + case IR::Opcode::FPFrexpExp64: + return IR::Opcode::FPFrexpExp32; + case IR::Opcode::FPOrdEqual64: + return IR::Opcode::FPOrdEqual32; + case IR::Opcode::FPUnordEqual64: + return IR::Opcode::FPUnordEqual32; + case IR::Opcode::FPOrdNotEqual64: + return IR::Opcode::FPOrdNotEqual32; + case IR::Opcode::FPUnordNotEqual64: + return IR::Opcode::FPUnordNotEqual32; + case IR::Opcode::FPOrdLessThan64: + return IR::Opcode::FPOrdLessThan32; + case IR::Opcode::FPUnordLessThan64: + return IR::Opcode::FPUnordLessThan32; + case IR::Opcode::FPOrdGreaterThan64: + return IR::Opcode::FPOrdGreaterThan32; + case IR::Opcode::FPUnordGreaterThan64: + return IR::Opcode::FPUnordGreaterThan32; + case IR::Opcode::FPOrdLessThanEqual64: + return IR::Opcode::FPOrdLessThanEqual32; + case IR::Opcode::FPUnordLessThanEqual64: + return IR::Opcode::FPUnordLessThanEqual32; + case IR::Opcode::FPOrdGreaterThanEqual64: + return IR::Opcode::FPOrdGreaterThanEqual32; + case IR::Opcode::FPUnordGreaterThanEqual64: + return IR::Opcode::FPUnordGreaterThanEqual32; + case IR::Opcode::FPIsNan64: + return IR::Opcode::FPIsNan32; + case IR::Opcode::FPIsInf64: + return IR::Opcode::FPIsInf32; + case IR::Opcode::ConvertS32F64: + return IR::Opcode::ConvertS32F32; + case IR::Opcode::ConvertF32F64: + return IR::Opcode::Identity; + case IR::Opcode::ConvertF64F32: + return IR::Opcode::Identity; + case IR::Opcode::ConvertF64S32: + return IR::Opcode::ConvertF32S32; + case IR::Opcode::ConvertF64U32: + return IR::Opcode::ConvertF32U32; + default: + return op; + } +} + +static void Lower(IR::Block& block, IR::Inst& inst) { + switch (inst.GetOpcode()) { + case IR::Opcode::PackDouble2x32: { + IR::IREmitter ir(block, IR::Block::InstructionList::s_iterator_to(inst)); + inst.ReplaceUsesWith(PackedF64ToF32(ir, inst.Arg(0))); + break; + } + case IR::Opcode::UnpackDouble2x32: { + IR::IREmitter ir(block, IR::Block::InstructionList::s_iterator_to(inst)); + inst.ReplaceUsesWith(F32ToPackedF64(ir, inst.Arg(0))); + break; + } + default: + inst.ReplaceOpcode(Replace(inst.GetOpcode())); + break; + } +} + +void LowerFp64ToFp32(IR::Program& program) { + for (IR::Block* const block : program.blocks) { + for (IR::Inst& inst : block->Instructions()) { + Lower(*block, inst); + } + } +} + +} // namespace Shader::Optimization diff --git a/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp b/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp index 778da149f..cc0bf83d3 100644 --- a/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp +++ b/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp @@ -101,6 +101,8 @@ bool IsImageAtomicInstruction(const IR::Inst& inst) { case IR::Opcode::ImageAtomicUMin32: case IR::Opcode::ImageAtomicSMax32: case IR::Opcode::ImageAtomicUMax32: + case IR::Opcode::ImageAtomicFMax32: + case IR::Opcode::ImageAtomicFMin32: case IR::Opcode::ImageAtomicInc32: case IR::Opcode::ImageAtomicDec32: case IR::Opcode::ImageAtomicAnd32: @@ -361,6 +363,12 @@ void PatchImageSharp(IR::Block& block, IR::Inst& inst, Info& info, Descriptors& LOG_ERROR(Render_Vulkan, "Shader compiled with unbound image!"); image = AmdGpu::Image::Null(); } + const auto data_fmt = image.GetDataFmt(); + if (inst_info.is_depth && data_fmt != AmdGpu::DataFormat::Format16 && + data_fmt != AmdGpu::DataFormat::Format32) { + LOG_ERROR(Render_Vulkan, "Shader compiled using non-depth image with depth instruction!"); + image = AmdGpu::Image::NullDepth(); + } ASSERT(image.GetType() != AmdGpu::ImageType::Invalid); const bool is_written = inst.GetOpcode() == IR::Opcode::ImageWrite; diff --git a/src/shader_recompiler/ir/passes/shader_info_collection_pass.cpp b/src/shader_recompiler/ir/passes/shader_info_collection_pass.cpp index 219378a6c..f53a0f4d4 100644 --- a/src/shader_recompiler/ir/passes/shader_info_collection_pass.cpp +++ b/src/shader_recompiler/ir/passes/shader_info_collection_pass.cpp @@ -44,7 +44,8 @@ void Visit(Info& info, const IR::Inst& inst) { case IR::Opcode::BitCastF16U16: info.uses_fp16 = true; break; - case IR::Opcode::BitCastU64F64: + case IR::Opcode::PackDouble2x32: + case IR::Opcode::UnpackDouble2x32: info.uses_fp64 = true; break; case IR::Opcode::ImageWrite: @@ -70,6 +71,10 @@ void Visit(Info& info, const IR::Inst& inst) { case IR::Opcode::ImageQueryLod: info.has_image_query = true; break; + case IR::Opcode::ImageAtomicFMax32: + case IR::Opcode::ImageAtomicFMin32: + info.uses_atomic_float_min_max = true; + break; case IR::Opcode::LaneId: info.uses_lane_id = true; break; diff --git a/src/shader_recompiler/ir/reinterpret.h b/src/shader_recompiler/ir/reinterpret.h index b65b19928..99819cbb9 100644 --- a/src/shader_recompiler/ir/reinterpret.h +++ b/src/shader_recompiler/ir/reinterpret.h @@ -34,6 +34,18 @@ inline F32 ApplyReadNumberConversion(IREmitter& ir, const F32& value, case AmdGpu::NumberConversion::UnormToUbnorm: // Convert 0...1 to -1...1 return ir.FPSub(ir.FPMul(value, ir.Imm32(2.f)), ir.Imm32(1.f)); + case AmdGpu::NumberConversion::Sint8ToSnormNz: { + const IR::U32 additon = ir.IAdd(ir.IMul(ir.BitCast(value), ir.Imm32(2)), ir.Imm32(1)); + const IR::F32 left = ir.ConvertSToF(32, 32, additon); + const IR::F32 max = ir.Imm32(float(std::numeric_limits::max())); + return ir.FPDiv(left, max); + } + case AmdGpu::NumberConversion::Sint16ToSnormNz: { + const IR::U32 additon = ir.IAdd(ir.IMul(ir.BitCast(value), ir.Imm32(2)), ir.Imm32(1)); + const IR::F32 left = ir.ConvertSToF(32, 32, additon); + const IR::F32 max = ir.Imm32(float(std::numeric_limits::max())); + return ir.FPDiv(left, max); + } default: UNREACHABLE(); } @@ -66,6 +78,20 @@ inline F32 ApplyWriteNumberConversion(IREmitter& ir, const F32& value, case AmdGpu::NumberConversion::UnormToUbnorm: // Convert -1...1 to 0...1 return ir.FPDiv(ir.FPAdd(value, ir.Imm32(1.f)), ir.Imm32(2.f)); + case AmdGpu::NumberConversion::Sint8ToSnormNz: { + const IR::F32 max = ir.Imm32(float(std::numeric_limits::max())); + const IR::F32 mul = ir.FPMul(ir.FPClamp(value, ir.Imm32(-1.f), ir.Imm32(1.f)), max); + const IR::F32 left = ir.FPSub(mul, ir.Imm32(1.f)); + const IR::U32 raw = ir.ConvertFToS(32, ir.FPDiv(left, ir.Imm32(2.f))); + return ir.BitCast(raw); + } + case AmdGpu::NumberConversion::Sint16ToSnormNz: { + const IR::F32 max = ir.Imm32(float(std::numeric_limits::max())); + const IR::F32 mul = ir.FPMul(ir.FPClamp(value, ir.Imm32(-1.f), ir.Imm32(1.f)), max); + const IR::F32 left = ir.FPSub(mul, ir.Imm32(1.f)); + const IR::U32 raw = ir.ConvertFToS(32, ir.FPDiv(left, ir.Imm32(2.f))); + return ir.BitCast(raw); + } default: UNREACHABLE(); } diff --git a/src/shader_recompiler/profile.h b/src/shader_recompiler/profile.h index 3b2854d59..853e4854d 100644 --- a/src/shader_recompiler/profile.h +++ b/src/shader_recompiler/profile.h @@ -15,6 +15,7 @@ struct Profile { bool support_int8{}; bool support_int16{}; bool support_int64{}; + bool support_float64{}; bool support_vertex_instance_id{}; bool support_float_controls{}; bool support_separate_denorm_behavior{}; @@ -28,6 +29,7 @@ struct Profile { bool supports_native_cube_calc{}; bool supports_trinary_minmax{}; bool supports_robust_buffer_access{}; + bool supports_image_fp32_atomic_min_max{}; bool has_broken_spirv_clamp{}; bool lower_left_origin_mode{}; bool needs_manual_interpolation{}; diff --git a/src/shader_recompiler/recompiler.cpp b/src/shader_recompiler/recompiler.cpp index 5004e0beb..3e0bd98d2 100644 --- a/src/shader_recompiler/recompiler.cpp +++ b/src/shader_recompiler/recompiler.cpp @@ -60,6 +60,9 @@ IR::Program TranslateProgram(std::span code, Pools& pools, Info& info program.post_order_blocks = Shader::IR::PostOrder(program.syntax_list.front()); // Run optimization passes + if (!profile.support_float64) { + Shader::Optimization::LowerFp64ToFp32(program); + } Shader::Optimization::SsaRewritePass(program.post_order_blocks); Shader::Optimization::ConstantPropagationPass(program.post_order_blocks); Shader::Optimization::IdentityRemovalPass(program.blocks); diff --git a/src/shader_recompiler/runtime_info.h b/src/shader_recompiler/runtime_info.h index 517392b98..b8ed42f5b 100644 --- a/src/shader_recompiler/runtime_info.h +++ b/src/shader_recompiler/runtime_info.h @@ -169,10 +169,10 @@ static constexpr u32 MaxColorBuffers = 8; struct PsColorBuffer { AmdGpu::NumberFormat num_format : 4; - AmdGpu::NumberConversion num_conversion : 2; + AmdGpu::NumberConversion num_conversion : 3; AmdGpu::Liverpool::ShaderExportFormat export_format : 4; u32 needs_unorm_fixup : 1; - u32 pad : 21; + u32 pad : 20; AmdGpu::CompMapping swizzle; auto operator<=>(const PsColorBuffer&) const noexcept = default; diff --git a/src/video_core/amdgpu/liverpool.cpp b/src/video_core/amdgpu/liverpool.cpp index 967b952c6..4c8e3367a 100644 --- a/src/video_core/amdgpu/liverpool.cpp +++ b/src/video_core/amdgpu/liverpool.cpp @@ -455,14 +455,14 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span dcb, std::span(header); const auto offset = draw_indirect->data_offset; - const auto size = sizeof(DrawIndirectArgs); + const auto stride = sizeof(DrawIndirectArgs); if (DebugState.DumpingCurrentReg()) { DebugState.PushRegsDump(base_addr, reinterpret_cast(header), regs); } if (rasterizer) { const auto cmd_address = reinterpret_cast(header); rasterizer->ScopeMarkerBegin(fmt::format("gfx:{}:DrawIndirect", cmd_address)); - rasterizer->DrawIndirect(false, indirect_args_addr, offset, size, 1, 0); + rasterizer->DrawIndirect(false, indirect_args_addr, offset, stride, 1, 0); rasterizer->ScopeMarkerEnd(); } break; @@ -471,7 +471,7 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span dcb, std::span(header); const auto offset = draw_index_indirect->data_offset; - const auto size = sizeof(DrawIndexedIndirectArgs); + const auto stride = sizeof(DrawIndexedIndirectArgs); if (DebugState.DumpingCurrentReg()) { DebugState.PushRegsDump(base_addr, reinterpret_cast(header), regs); } @@ -479,25 +479,46 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span dcb, std::span(header); rasterizer->ScopeMarkerBegin( fmt::format("gfx:{}:DrawIndexIndirect", cmd_address)); - rasterizer->DrawIndirect(true, indirect_args_addr, offset, size, 1, 0); + rasterizer->DrawIndirect(true, indirect_args_addr, offset, stride, 1, 0); rasterizer->ScopeMarkerEnd(); } break; } - case PM4ItOpcode::DrawIndexIndirectCountMulti: { + case PM4ItOpcode::DrawIndexIndirectMulti: { const auto* draw_index_indirect = reinterpret_cast(header); const auto offset = draw_index_indirect->data_offset; if (DebugState.DumpingCurrentReg()) { DebugState.PushRegsDump(base_addr, reinterpret_cast(header), regs); } + if (rasterizer) { + const auto cmd_address = reinterpret_cast(header); + rasterizer->ScopeMarkerBegin( + fmt::format("gfx:{}:DrawIndexIndirectMulti", cmd_address)); + rasterizer->DrawIndirect(true, indirect_args_addr, offset, + draw_index_indirect->stride, + draw_index_indirect->count, 0); + rasterizer->ScopeMarkerEnd(); + } + break; + } + case PM4ItOpcode::DrawIndexIndirectCountMulti: { + const auto* draw_index_indirect = + reinterpret_cast(header); + const auto offset = draw_index_indirect->data_offset; + if (DebugState.DumpingCurrentReg()) { + DebugState.PushRegsDump(base_addr, reinterpret_cast(header), regs); + } if (rasterizer) { const auto cmd_address = reinterpret_cast(header); rasterizer->ScopeMarkerBegin( fmt::format("gfx:{}:DrawIndexIndirectCountMulti", cmd_address)); - rasterizer->DrawIndirect( - true, indirect_args_addr, offset, draw_index_indirect->stride, - draw_index_indirect->count, draw_index_indirect->countAddr); + rasterizer->DrawIndirect(true, indirect_args_addr, offset, + draw_index_indirect->stride, + draw_index_indirect->count, + draw_index_indirect->count_indirect_enable.Value() + ? draw_index_indirect->count_addr + : 0); rasterizer->ScopeMarkerEnd(); } break; diff --git a/src/video_core/amdgpu/liverpool.h b/src/video_core/amdgpu/liverpool.h index 8f9292f1c..c4bebd05f 100644 --- a/src/video_core/amdgpu/liverpool.h +++ b/src/video_core/amdgpu/liverpool.h @@ -924,15 +924,11 @@ struct Liverpool { } [[nodiscard]] NumberFormat GetNumberFmt() const { - // There is a small difference between T# and CB number types, account for it. - return RemapNumberFormat(info.number_type == NumberFormat::SnormNz - ? NumberFormat::Srgb - : info.number_type.Value(), - info.format); + return RemapNumberFormat(GetFixedNumberFormat(), info.format); } [[nodiscard]] NumberConversion GetNumberConversion() const { - return MapNumberConversion(info.number_type); + return MapNumberConversion(GetFixedNumberFormat(), info.format); } [[nodiscard]] CompMapping Swizzle() const { @@ -973,6 +969,13 @@ struct Liverpool { const auto mrt_swizzle = mrt_swizzles[swap_idx][components_idx]; return RemapSwizzle(info.format, mrt_swizzle); } + + private: + [[nodiscard]] NumberFormat GetFixedNumberFormat() const { + // There is a small difference between T# and CB number types, account for it. + return info.number_type == NumberFormat::SnormNz ? NumberFormat::Srgb + : info.number_type.Value(); + } }; enum ContextRegs : u32 { diff --git a/src/video_core/amdgpu/pm4_cmds.h b/src/video_core/amdgpu/pm4_cmds.h index ae1d32e00..6b55f5b65 100644 --- a/src/video_core/amdgpu/pm4_cmds.h +++ b/src/video_core/amdgpu/pm4_cmds.h @@ -860,6 +860,24 @@ struct PM4CmdDrawIndexIndirect { }; struct PM4CmdDrawIndexIndirectMulti { + PM4Type3Header header; ///< header + u32 data_offset; ///< Byte aligned offset where the required data structure starts + union { + u32 dw2; + BitField<0, 16, u32> base_vtx_loc; ///< Offset where the CP will write the + ///< BaseVertexLocation it fetched from memory + }; + union { + u32 dw3; + BitField<0, 16, u32> start_inst_loc; ///< Offset where the CP will write the + ///< StartInstanceLocation it fetched from memory + }; + u32 count; ///< Count of data structures to loop through before going to next packet + u32 stride; ///< Stride in memory from one data structure to the next + u32 draw_initiator; ///< Draw Initiator Register +}; + +struct PM4CmdDrawIndexIndirectCountMulti { PM4Type3Header header; ///< header u32 data_offset; ///< Byte aligned offset where the required data structure starts union { @@ -874,14 +892,14 @@ struct PM4CmdDrawIndexIndirectMulti { }; union { u32 dw4; - BitField<0, 16, u32> drawIndexLoc; ///< register offset to write the Draw Index count + BitField<0, 16, u32> draw_index_loc; ///< register offset to write the Draw Index count BitField<30, 1, u32> - countIndirectEnable; ///< Indicates the data structure count is in memory + count_indirect_enable; ///< Indicates the data structure count is in memory BitField<31, 1, u32> - drawIndexEnable; ///< Enables writing of Draw Index count to DRAW_INDEX_LOC + draw_index_enable; ///< Enables writing of Draw Index count to DRAW_INDEX_LOC }; u32 count; ///< Count of data structures to loop through before going to next packet - u64 countAddr; ///< DWord aligned Address[31:2]; Valid if countIndirectEnable is set + u64 count_addr; ///< DWord aligned Address[31:2]; Valid if countIndirectEnable is set u32 stride; ///< Stride in memory from one data structure to the next u32 draw_initiator; ///< Draw Initiator Register }; diff --git a/src/video_core/amdgpu/resource.h b/src/video_core/amdgpu/resource.h index 64a85c812..9060074fb 100644 --- a/src/video_core/amdgpu/resource.h +++ b/src/video_core/amdgpu/resource.h @@ -68,7 +68,7 @@ struct Buffer { } NumberConversion GetNumberConversion() const noexcept { - return MapNumberConversion(NumberFormat(num_format)); + return MapNumberConversion(NumberFormat(num_format), DataFormat(data_format)); } u32 GetStride() const noexcept { @@ -219,6 +219,19 @@ struct Image { return image; } + static constexpr Image NullDepth() { + Image image{}; + image.data_format = u64(DataFormat::Format32); + image.num_format = u64(NumberFormat::Float); + image.dst_sel_x = u64(CompSwizzle::Red); + image.dst_sel_y = u64(CompSwizzle::Green); + image.dst_sel_z = u64(CompSwizzle::Blue); + image.dst_sel_w = u64(CompSwizzle::Alpha); + image.tiling_index = u64(TilingMode::Texture_MicroTiled); + image.type = u64(ImageType::Color2D); + return image; + } + bool Valid() const { return (type & 0x8u) != 0; } @@ -292,7 +305,7 @@ struct Image { } NumberConversion GetNumberConversion() const noexcept { - return MapNumberConversion(NumberFormat(num_format)); + return MapNumberConversion(NumberFormat(num_format), DataFormat(data_format)); } TilingMode GetTilingMode() const { diff --git a/src/video_core/amdgpu/types.h b/src/video_core/amdgpu/types.h index d1cf19076..ab0df689e 100644 --- a/src/video_core/amdgpu/types.h +++ b/src/video_core/amdgpu/types.h @@ -197,6 +197,8 @@ enum class NumberConversion : u32 { UintToUscaled = 1, SintToSscaled = 2, UnormToUbnorm = 3, + Sint8ToSnormNz = 5, + Sint16ToSnormNz = 6, }; struct CompMapping { @@ -287,6 +289,7 @@ inline NumberFormat RemapNumberFormat(const NumberFormat format, const DataForma case NumberFormat::Uscaled: return NumberFormat::Uint; case NumberFormat::Sscaled: + case NumberFormat::SnormNz: return NumberFormat::Sint; case NumberFormat::Ubnorm: return NumberFormat::Unorm; @@ -336,14 +339,28 @@ inline CompMapping RemapSwizzle(const DataFormat format, const CompMapping swizz } } -inline NumberConversion MapNumberConversion(const NumberFormat format) { - switch (format) { +inline NumberConversion MapNumberConversion(const NumberFormat num_fmt, const DataFormat data_fmt) { + switch (num_fmt) { case NumberFormat::Uscaled: return NumberConversion::UintToUscaled; case NumberFormat::Sscaled: return NumberConversion::SintToSscaled; case NumberFormat::Ubnorm: return NumberConversion::UnormToUbnorm; + case NumberFormat::SnormNz: { + switch (data_fmt) { + case DataFormat::Format8: + case DataFormat::Format8_8: + case DataFormat::Format8_8_8_8: + return NumberConversion::Sint8ToSnormNz; + case DataFormat::Format16: + case DataFormat::Format16_16: + case DataFormat::Format16_16_16_16: + return NumberConversion::Sint16ToSnormNz; + default: + UNREACHABLE_MSG("data_fmt = {}", u32(data_fmt)); + } + } default: return NumberConversion::None; } diff --git a/src/video_core/buffer_cache/buffer_cache.cpp b/src/video_core/buffer_cache/buffer_cache.cpp index cdf736a89..fb9fd755e 100644 --- a/src/video_core/buffer_cache/buffer_cache.cpp +++ b/src/video_core/buffer_cache/buffer_cache.cpp @@ -177,8 +177,8 @@ void BufferCache::BindVertexBuffers(const Vulkan::GraphicsPipeline& pipeline) { if (instance.IsVertexInputDynamicState()) { cmdbuf.bindVertexBuffers(0, num_buffers, host_buffers.data(), host_offsets.data()); } else { - cmdbuf.bindVertexBuffers2EXT(0, num_buffers, host_buffers.data(), host_offsets.data(), - host_sizes.data(), host_strides.data()); + cmdbuf.bindVertexBuffers2(0, num_buffers, host_buffers.data(), host_offsets.data(), + host_sizes.data(), host_strides.data()); } } diff --git a/src/video_core/renderer_vulkan/host_passes/pp_pass.cpp b/src/video_core/renderer_vulkan/host_passes/pp_pass.cpp index c854e124f..73dd3a7b5 100644 --- a/src/video_core/renderer_vulkan/host_passes/pp_pass.cpp +++ b/src/video_core/renderer_vulkan/host_passes/pp_pass.cpp @@ -14,7 +14,7 @@ namespace Vulkan::HostPasses { -void PostProcessingPass::Create(vk::Device device) { +void PostProcessingPass::Create(vk::Device device, const vk::Format surface_format) { static const std::array pp_shaders{ HostShaders::FS_TRI_VERT, HostShaders::POST_PROCESS_FRAG, @@ -76,9 +76,9 @@ void PostProcessingPass::Create(vk::Device device) { Check<"create pp pipeline layout">(device.createPipelineLayoutUnique(layout_info)); const std::array pp_color_formats{ - vk::Format::eB8G8R8A8Unorm, // swapchain.GetSurfaceFormat().format, + surface_format, }; - const vk::PipelineRenderingCreateInfoKHR pipeline_rendering_ci{ + const vk::PipelineRenderingCreateInfo pipeline_rendering_ci{ .colorAttachmentCount = pp_color_formats.size(), .pColorAttachmentFormats = pp_color_formats.data(), }; diff --git a/src/video_core/renderer_vulkan/host_passes/pp_pass.h b/src/video_core/renderer_vulkan/host_passes/pp_pass.h index 6127bb5c1..f95c02e8d 100644 --- a/src/video_core/renderer_vulkan/host_passes/pp_pass.h +++ b/src/video_core/renderer_vulkan/host_passes/pp_pass.h @@ -19,7 +19,7 @@ public: u32 hdr = 0; }; - void Create(vk::Device device); + void Create(vk::Device device, vk::Format surface_format); void Render(vk::CommandBuffer cmdbuf, vk::ImageView input, vk::Extent2D input_size, Frame& output, Settings settings); diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp index 354e22331..7c020a012 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp @@ -122,21 +122,21 @@ GraphicsPipeline::GraphicsPipeline( }; boost::container::static_vector dynamic_states = { - vk::DynamicState::eViewportWithCountEXT, vk::DynamicState::eScissorWithCountEXT, - vk::DynamicState::eBlendConstants, vk::DynamicState::eDepthTestEnableEXT, - vk::DynamicState::eDepthWriteEnableEXT, vk::DynamicState::eDepthCompareOpEXT, - vk::DynamicState::eDepthBiasEnableEXT, vk::DynamicState::eDepthBias, - vk::DynamicState::eStencilTestEnableEXT, vk::DynamicState::eStencilReference, - vk::DynamicState::eStencilCompareMask, vk::DynamicState::eStencilWriteMask, - vk::DynamicState::eStencilOpEXT, vk::DynamicState::eCullModeEXT, - vk::DynamicState::eFrontFaceEXT, + vk::DynamicState::eViewportWithCount, vk::DynamicState::eScissorWithCount, + vk::DynamicState::eBlendConstants, vk::DynamicState::eDepthTestEnable, + vk::DynamicState::eDepthWriteEnable, vk::DynamicState::eDepthCompareOp, + vk::DynamicState::eDepthBiasEnable, vk::DynamicState::eDepthBias, + vk::DynamicState::eStencilTestEnable, vk::DynamicState::eStencilReference, + vk::DynamicState::eStencilCompareMask, vk::DynamicState::eStencilWriteMask, + vk::DynamicState::eStencilOp, vk::DynamicState::eCullMode, + vk::DynamicState::eFrontFace, }; if (instance.IsPrimitiveRestartDisableSupported()) { - dynamic_states.push_back(vk::DynamicState::ePrimitiveRestartEnableEXT); + dynamic_states.push_back(vk::DynamicState::ePrimitiveRestartEnable); } if (instance.IsDepthBoundsSupported()) { - dynamic_states.push_back(vk::DynamicState::eDepthBoundsTestEnableEXT); + dynamic_states.push_back(vk::DynamicState::eDepthBoundsTestEnable); dynamic_states.push_back(vk::DynamicState::eDepthBounds); } if (instance.IsDynamicColorWriteMaskSupported()) { @@ -145,7 +145,7 @@ GraphicsPipeline::GraphicsPipeline( if (instance.IsVertexInputDynamicState()) { dynamic_states.push_back(vk::DynamicState::eVertexInputEXT); } else if (!vertex_bindings.empty()) { - dynamic_states.push_back(vk::DynamicState::eVertexInputBindingStrideEXT); + dynamic_states.push_back(vk::DynamicState::eVertexInputBindingStride); } const vk::PipelineDynamicStateCreateInfo dynamic_info = { @@ -212,7 +212,7 @@ GraphicsPipeline::GraphicsPipeline( }); } - const vk::PipelineRenderingCreateInfoKHR pipeline_rendering_ci = { + const vk::PipelineRenderingCreateInfo pipeline_rendering_ci = { .colorAttachmentCount = key.num_color_attachments, .pColorAttachmentFormats = key.color_formats.data(), .depthAttachmentFormat = key.depth_format, diff --git a/src/video_core/renderer_vulkan/vk_instance.cpp b/src/video_core/renderer_vulkan/vk_instance.cpp index 0df020116..1004d850f 100644 --- a/src/video_core/renderer_vulkan/vk_instance.cpp +++ b/src/video_core/renderer_vulkan/vk_instance.cpp @@ -203,12 +203,15 @@ std::string Instance::GetDriverVersionName() { } bool Instance::CreateDevice() { - const vk::StructureChain feature_chain = physical_device.getFeatures2< - vk::PhysicalDeviceFeatures2, vk::PhysicalDeviceVulkan11Features, - vk::PhysicalDeviceVulkan12Features, vk::PhysicalDeviceRobustness2FeaturesEXT, - vk::PhysicalDeviceExtendedDynamicState3FeaturesEXT, - vk::PhysicalDevicePrimitiveTopologyListRestartFeaturesEXT, - vk::PhysicalDevicePortabilitySubsetFeaturesKHR>(); + const vk::StructureChain feature_chain = + physical_device + .getFeatures2(); features = feature_chain.get().features; const vk::StructureChain properties_chain = physical_device.getProperties2< @@ -240,18 +243,6 @@ bool Instance::CreateDevice() { return false; }; - // These extensions are promoted by Vulkan 1.3, but for greater compatibility we use Vulkan 1.2 - // with extensions. - ASSERT(add_extension(VK_KHR_FORMAT_FEATURE_FLAGS_2_EXTENSION_NAME)); - ASSERT(add_extension(VK_KHR_DYNAMIC_RENDERING_EXTENSION_NAME)); - ASSERT(add_extension(VK_EXT_SHADER_DEMOTE_TO_HELPER_INVOCATION_EXTENSION_NAME)); - ASSERT(add_extension(VK_KHR_SYNCHRONIZATION_2_EXTENSION_NAME)); - ASSERT(add_extension(VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME)); - ASSERT(add_extension(VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME)); - ASSERT(add_extension(VK_EXT_TOOLING_INFO_EXTENSION_NAME) || - driver_id == vk::DriverId::eIntelProprietaryWindows); - ASSERT(add_extension(VK_KHR_MAINTENANCE_4_EXTENSION_NAME)); - // Required ASSERT(add_extension(VK_KHR_SWAPCHAIN_EXTENSION_NAME)); ASSERT(add_extension(VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME)); @@ -282,6 +273,13 @@ bool Instance::CreateDevice() { image_load_store_lod = add_extension(VK_AMD_SHADER_IMAGE_LOAD_STORE_LOD_EXTENSION_NAME); amd_gcn_shader = add_extension(VK_AMD_GCN_SHADER_EXTENSION_NAME); amd_shader_trinary_minmax = add_extension(VK_AMD_SHADER_TRINARY_MINMAX_EXTENSION_NAME); + shader_atomic_float2 = add_extension(VK_EXT_SHADER_ATOMIC_FLOAT_2_EXTENSION_NAME); + if (shader_atomic_float2) { + shader_atomic_float2_features = + feature_chain.get(); + LOG_INFO(Render_Vulkan, "- shaderImageFloat32AtomicMinMax: {}", + shader_atomic_float2_features.shaderImageFloat32AtomicMinMax); + } const bool calibrated_timestamps = TRACY_GPU_ENABLED ? add_extension(VK_EXT_CALIBRATED_TIMESTAMPS_EXTENSION_NAME) : false; @@ -324,6 +322,7 @@ bool Instance::CreateDevice() { feature_chain.get(); const auto vk11_features = feature_chain.get(); const auto vk12_features = feature_chain.get(); + const auto vk13_features = feature_chain.get(); vk::StructureChain device_chain = { vk::DeviceCreateInfo{ .queueCreateInfoCount = 1u, @@ -339,8 +338,10 @@ bool Instance::CreateDevice() { .geometryShader = features.geometryShader, .tessellationShader = features.tessellationShader, .logicOp = features.logicOp, + .multiDrawIndirect = features.multiDrawIndirect, .depthBiasClamp = features.depthBiasClamp, .fillModeNonSolid = features.fillModeNonSolid, + .depthBounds = features.depthBounds, .multiViewport = features.multiViewport, .samplerAnisotropy = features.samplerAnisotropy, .vertexPipelineStoresAndAtomics = features.vertexPipelineStoresAndAtomics, @@ -372,26 +373,14 @@ bool Instance::CreateDevice() { .hostQueryReset = vk12_features.hostQueryReset, .timelineSemaphore = vk12_features.timelineSemaphore, }, - // Vulkan 1.3 promoted extensions - vk::PhysicalDeviceDynamicRenderingFeaturesKHR{ - .dynamicRendering = true, + vk::PhysicalDeviceVulkan13Features{ + .robustImageAccess = vk13_features.robustImageAccess, + .shaderDemoteToHelperInvocation = vk13_features.shaderDemoteToHelperInvocation, + .synchronization2 = vk13_features.synchronization2, + .dynamicRendering = vk13_features.dynamicRendering, + .maintenance4 = vk13_features.maintenance4, }, - vk::PhysicalDeviceShaderDemoteToHelperInvocationFeaturesEXT{ - .shaderDemoteToHelperInvocation = true, - }, - vk::PhysicalDeviceSynchronization2Features{ - .synchronization2 = true, - }, - vk::PhysicalDeviceExtendedDynamicStateFeaturesEXT{ - .extendedDynamicState = true, - }, - vk::PhysicalDeviceExtendedDynamicState2FeaturesEXT{ - .extendedDynamicState2 = true, - }, - vk::PhysicalDeviceMaintenance4FeaturesKHR{ - .maintenance4 = true, - }, - // Other extensions + // Extensions vk::PhysicalDeviceCustomBorderColorFeaturesEXT{ .customBorderColors = true, .customBorderColorWithoutFormat = true, @@ -421,6 +410,10 @@ bool Instance::CreateDevice() { vk::PhysicalDeviceLegacyVertexAttributesFeaturesEXT{ .legacyVertexAttributes = true, }, + vk::PhysicalDeviceShaderAtomicFloat2FeaturesEXT{ + .shaderImageFloat32AtomicMinMax = + shader_atomic_float2_features.shaderImageFloat32AtomicMinMax, + }, #ifdef __APPLE__ portability_features, #endif @@ -450,6 +443,9 @@ bool Instance::CreateDevice() { if (!legacy_vertex_attributes) { device_chain.unlink(); } + if (!shader_atomic_float2) { + device_chain.unlink(); + } auto [device_result, dev] = physical_device.createDeviceUnique(device_chain.get()); if (device_result != vk::Result::eSuccess) { @@ -542,12 +538,14 @@ void Instance::CollectDeviceParameters() { LOG_INFO(Render_Vulkan, "GPU_Vulkan_Extensions: {}", extensions); } -void Instance::CollectToolingInfo() { - if (GetDriverID() == vk::DriverId::eAmdProprietary) { - // Currently causes issues with Reshade on AMD proprietary, disabled until fix released. +void Instance::CollectToolingInfo() const { + if (driver_id == vk::DriverId::eAmdProprietary || + driver_id == vk::DriverId::eIntelProprietaryWindows) { + // AMD: Causes issues with Reshade. + // Intel: Causes crash on start. return; } - const auto [tools_result, tools] = physical_device.getToolPropertiesEXT(); + const auto [tools_result, tools] = physical_device.getToolProperties(); if (tools_result != vk::Result::eSuccess) { LOG_ERROR(Render_Vulkan, "Could not get Vulkan tool properties: {}", vk::to_string(tools_result)); diff --git a/src/video_core/renderer_vulkan/vk_instance.h b/src/video_core/renderer_vulkan/vk_instance.h index a9de01f84..573473869 100644 --- a/src/video_core/renderer_vulkan/vk_instance.h +++ b/src/video_core/renderer_vulkan/vk_instance.h @@ -89,6 +89,11 @@ public: return features.depthBounds; } + /// Returns true if 64-bit floats are supported in shaders + bool IsShaderFloat64Supported() const { + return features.shaderFloat64; + } + /// Returns true when VK_EXT_custom_border_color is supported bool IsCustomBorderColorSupported() const { return custom_border_color; @@ -160,6 +165,12 @@ public: return amd_shader_trinary_minmax; } + /// Returns true when the shaderImageFloat32AtomicMinMax feature of + /// VK_EXT_shader_atomic_float2 is supported. + bool IsShaderAtomicFloatImage32MinMaxSupported() const { + return shader_atomic_float2 && shader_atomic_float2_features.shaderImageFloat32AtomicMinMax; + } + /// Returns true when geometry shaders are supported by the device bool IsGeometryStageSupported() const { return features.geometryShader; @@ -311,7 +322,7 @@ private: /// Collects telemetry information from the device. void CollectDeviceParameters(); - void CollectToolingInfo(); + void CollectToolingInfo() const; /// Gets the supported feature flags for a format. [[nodiscard]] vk::FormatFeatureFlags2 GetFormatFeatureFlags(vk::Format format) const; @@ -331,6 +342,7 @@ private: vk::PhysicalDevicePortabilitySubsetFeaturesKHR portability_features; vk::PhysicalDeviceExtendedDynamicState3FeaturesEXT dynamic_state_3_features; vk::PhysicalDeviceRobustness2FeaturesEXT robustness2_features; + vk::PhysicalDeviceShaderAtomicFloat2FeaturesEXT shader_atomic_float2_features; vk::DriverIdKHR driver_id; vk::UniqueDebugUtilsMessengerEXT debug_callback{}; std::string vendor_name; @@ -355,6 +367,7 @@ private: bool image_load_store_lod{}; bool amd_gcn_shader{}; bool amd_shader_trinary_minmax{}; + bool shader_atomic_float2{}; bool portability_subset{}; }; diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index bad2a549c..0a0c81d4c 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -26,6 +26,8 @@ using Shader::LogicalStage; using Shader::Stage; using Shader::VsOutput; +constexpr static auto SpirvVersion1_6 = 0x00010600U; + constexpr static std::array DescriptorHeapSizes = { vk::DescriptorPoolSize{vk::DescriptorType::eUniformBuffer, 8192}, vk::DescriptorPoolSize{vk::DescriptorType::eStorageBuffer, 1024}, @@ -192,8 +194,9 @@ PipelineCache::PipelineCache(const Instance& instance_, Scheduler& scheduler_, desc_heap{instance, scheduler.GetMasterSemaphore(), DescriptorHeapSizes} { const auto& vk12_props = instance.GetVk12Properties(); profile = Shader::Profile{ - .supported_spirv = instance.ApiVersion() >= VK_API_VERSION_1_3 ? 0x00010600U : 0x00010500U, + .supported_spirv = SpirvVersion1_6, .subgroup_size = instance.SubgroupSize(), + .support_float64 = instance.IsShaderFloat64Supported(), .support_fp32_denorm_preserve = bool(vk12_props.shaderDenormPreserveFloat32), .support_fp32_denorm_flush = bool(vk12_props.shaderDenormFlushToZeroFloat32), .support_fp32_round_to_zero = bool(vk12_props.shaderRoundingModeRTZFloat32), @@ -203,6 +206,7 @@ PipelineCache::PipelineCache(const Instance& instance_, Scheduler& scheduler_, .supports_native_cube_calc = instance_.IsAmdGcnShaderSupported(), .supports_trinary_minmax = instance_.IsAmdShaderTrinaryMinMaxSupported(), .supports_robust_buffer_access = instance_.IsRobustBufferAccess2Supported(), + .supports_image_fp32_atomic_min_max = instance_.IsShaderAtomicFloatImage32MinMaxSupported(), .needs_manual_interpolation = instance.IsFragmentShaderBarycentricSupported() && instance.GetDriverID() == vk::DriverId::eNvidiaProprietary, .needs_lds_barriers = instance.GetDriverID() == vk::DriverId::eNvidiaProprietary || diff --git a/src/video_core/renderer_vulkan/vk_platform.cpp b/src/video_core/renderer_vulkan/vk_platform.cpp index e656369b2..c49e62f4e 100644 --- a/src/video_core/renderer_vulkan/vk_platform.cpp +++ b/src/video_core/renderer_vulkan/vk_platform.cpp @@ -22,6 +22,10 @@ #include "sdl_window.h" #include "video_core/renderer_vulkan/vk_platform.h" +#ifdef __APPLE__ +#include +#endif + namespace Vulkan { static const char* const VALIDATION_LAYER_NAME = "VK_LAYER_KHRONOS_validation"; @@ -223,8 +227,19 @@ vk::UniqueInstance CreateInstance(Frontend::WindowSystemType window_type, bool e LOG_INFO(Render_Vulkan, "Creating vulkan instance"); #ifdef __APPLE__ +#ifndef ENABLE_QT_GUI + // Initialize the environment with the path to the MoltenVK ICD, so that the loader will + // find it. + static const auto icd_path = [] { + char path[PATH_MAX]; + u32 length = PATH_MAX; + _NSGetExecutablePath(path, &length); + return std::filesystem::path(path).parent_path() / "MoltenVK_icd.json"; + }(); + setenv("VK_DRIVER_FILES", icd_path.c_str(), true); +#endif // If the Vulkan loader exists in /usr/local/lib, give it priority. The Vulkan SDK - // installs it here by default but it is not in the default library search path. + // installs it here by default, but it is not in the default library search path. // The loader has a clause to check for it, but at a lower priority than the bundled // libMoltenVK.dylib, so we need to handle it ourselves to give it priority. static const std::string usr_local_path = "/usr/local/lib/libvulkan.dylib"; diff --git a/src/video_core/renderer_vulkan/vk_platform.h b/src/video_core/renderer_vulkan/vk_platform.h index 6a6ebeb15..b8f5f9f11 100644 --- a/src/video_core/renderer_vulkan/vk_platform.h +++ b/src/video_core/renderer_vulkan/vk_platform.h @@ -18,7 +18,7 @@ class WindowSDL; namespace Vulkan { -constexpr u32 TargetVulkanApiVersion = VK_API_VERSION_1_2; +constexpr u32 TargetVulkanApiVersion = VK_API_VERSION_1_3; vk::SurfaceKHR CreateSurface(vk::Instance instance, const Frontend::WindowSDL& emu_window); diff --git a/src/video_core/renderer_vulkan/vk_presenter.cpp b/src/video_core/renderer_vulkan/vk_presenter.cpp index 4a6a5c7c2..09dd23cb6 100644 --- a/src/video_core/renderer_vulkan/vk_presenter.cpp +++ b/src/video_core/renderer_vulkan/vk_presenter.cpp @@ -130,7 +130,7 @@ Presenter::Presenter(Frontend::WindowSDL& window_, AmdGpu::Liverpool* liverpool_ } fsr_pass.Create(device, instance.GetAllocator(), num_images); - pp_pass.Create(device); + pp_pass.Create(device, swapchain.GetSurfaceFormat().format); ImGui::Layer::AddLayer(Common::Singleton::Instance()); } @@ -270,7 +270,25 @@ Frame* Presenter::PrepareLastFrame() { return frame; } -Frame* Presenter::PrepareFrameInternal(VideoCore::ImageId image_id, bool is_eop) { +static vk::Format GetFrameViewFormat(const Libraries::VideoOut::PixelFormat format) { + switch (format) { + case Libraries::VideoOut::PixelFormat::A8B8G8R8Srgb: + return vk::Format::eR8G8B8A8Srgb; + case Libraries::VideoOut::PixelFormat::A8R8G8B8Srgb: + return vk::Format::eB8G8R8A8Srgb; + case Libraries::VideoOut::PixelFormat::A2R10G10B10: + case Libraries::VideoOut::PixelFormat::A2R10G10B10Srgb: + case Libraries::VideoOut::PixelFormat::A2R10G10B10Bt2020Pq: + return vk::Format::eA2R10G10B10UnormPack32; + default: + break; + } + UNREACHABLE_MSG("Unknown format={}", static_cast(format)); + return {}; +} + +Frame* Presenter::PrepareFrameInternal(VideoCore::ImageId image_id, + const Libraries::VideoOut::PixelFormat format, bool is_eop) { // Request a free presentation frame. Frame* frame = GetRenderFrame(); @@ -324,7 +342,7 @@ Frame* Presenter::PrepareFrameInternal(VideoCore::ImageId image_id, bool is_eop) cmdbuf); VideoCore::ImageViewInfo info{}; - info.format = image.info.pixel_format; + info.format = GetFrameViewFormat(format); // Exclude alpha from output frame to avoid blending with UI. info.mapping = vk::ComponentMapping{ .r = vk::ComponentSwizzle::eIdentity, diff --git a/src/video_core/renderer_vulkan/vk_presenter.h b/src/video_core/renderer_vulkan/vk_presenter.h index ad2708474..8ed2052ee 100644 --- a/src/video_core/renderer_vulkan/vk_presenter.h +++ b/src/video_core/renderer_vulkan/vk_presenter.h @@ -70,11 +70,12 @@ public: auto desc = VideoCore::TextureCache::VideoOutDesc{attribute, cpu_address}; const auto image_id = texture_cache.FindImage(desc); texture_cache.UpdateImage(image_id, is_eop ? nullptr : &flip_scheduler); - return PrepareFrameInternal(image_id, is_eop); + return PrepareFrameInternal(image_id, attribute.attrib.pixel_format, is_eop); } Frame* PrepareBlankFrame(bool is_eop) { - return PrepareFrameInternal(VideoCore::NULL_IMAGE_ID, is_eop); + return PrepareFrameInternal(VideoCore::NULL_IMAGE_ID, + Libraries::VideoOut::PixelFormat::Unknown, is_eop); } VideoCore::Image& RegisterVideoOutSurface( @@ -119,7 +120,8 @@ public: } private: - Frame* PrepareFrameInternal(VideoCore::ImageId image_id, bool is_eop = true); + Frame* PrepareFrameInternal(VideoCore::ImageId image_id, + Libraries::VideoOut::PixelFormat format, bool is_eop = true); Frame* GetRenderFrame(); void SetExpectedGameSize(s32 width, s32 height); diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index b04b4a07e..e7b42a34b 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -618,8 +618,9 @@ void Rasterizer::BindTextures(const Shader::Info& stage, Shader::Backend::Bindin if (instance.IsNullDescriptorSupported()) { image_infos.emplace_back(VK_NULL_HANDLE, VK_NULL_HANDLE, vk::ImageLayout::eGeneral); } else { - auto& null_image = texture_cache.GetImageView(VideoCore::NULL_IMAGE_VIEW_ID); - image_infos.emplace_back(VK_NULL_HANDLE, *null_image.image_view, + auto& null_image_view = + texture_cache.FindTexture(VideoCore::NULL_IMAGE_ID, desc.view_info); + image_infos.emplace_back(VK_NULL_HANDLE, *null_image_view.image_view, vk::ImageLayout::eGeneral); } } else { @@ -696,14 +697,19 @@ void Rasterizer::BindTextures(const Shader::Info& stage, Shader::Backend::Bindin void Rasterizer::BeginRendering(const GraphicsPipeline& pipeline, RenderState& state) { int cb_index = 0; - for (auto& [image_id, desc] : cb_descs) { + for (auto attach_idx = 0u; attach_idx < state.num_color_attachments; ++attach_idx) { + if (state.color_attachments[attach_idx].imageView == VK_NULL_HANDLE) { + continue; + } + + auto& [image_id, desc] = cb_descs[cb_index++]; if (auto& old_img = texture_cache.GetImage(image_id); old_img.binding.needs_rebind) { auto& view = texture_cache.FindRenderTarget(desc); ASSERT(view.image_id != image_id); image_id = bound_images.emplace_back(view.image_id); auto& image = texture_cache.GetImage(view.image_id); - state.color_attachments[cb_index].imageView = *view.image_view; - state.color_attachments[cb_index].imageLayout = image.last_state.layout; + state.color_attachments[attach_idx].imageView = *view.image_view; + state.color_attachments[attach_idx].imageLayout = image.last_state.layout; const auto mip = view.info.range.base.level; state.width = std::min(state.width, std::max(image.info.size.width >> mip, 1u)); @@ -722,8 +728,7 @@ void Rasterizer::BeginRendering(const GraphicsPipeline& pipeline, RenderState& s desc.view_info.range); } image.usage.render_target = 1u; - state.color_attachments[cb_index].imageLayout = image.last_state.layout; - ++cb_index; + state.color_attachments[attach_idx].imageLayout = image.last_state.layout; } if (db_desc) { diff --git a/src/video_core/renderer_vulkan/vk_scheduler.cpp b/src/video_core/renderer_vulkan/vk_scheduler.cpp index a48d93dee..8d4188a22 100644 --- a/src/video_core/renderer_vulkan/vk_scheduler.cpp +++ b/src/video_core/renderer_vulkan/vk_scheduler.cpp @@ -170,29 +170,29 @@ void Scheduler::SubmitExecution(SubmitInfo& info) { void DynamicState::Commit(const Instance& instance, const vk::CommandBuffer& cmdbuf) { if (dirty_state.viewports) { dirty_state.viewports = false; - cmdbuf.setViewportWithCountEXT(viewports); + cmdbuf.setViewportWithCount(viewports); } if (dirty_state.scissors) { dirty_state.scissors = false; - cmdbuf.setScissorWithCountEXT(scissors); + cmdbuf.setScissorWithCount(scissors); } if (dirty_state.depth_test_enabled) { dirty_state.depth_test_enabled = false; - cmdbuf.setDepthTestEnableEXT(depth_test_enabled); + cmdbuf.setDepthTestEnable(depth_test_enabled); } if (dirty_state.depth_write_enabled) { dirty_state.depth_write_enabled = false; // Note that this must be set in a command buffer even if depth test is disabled. - cmdbuf.setDepthWriteEnableEXT(depth_write_enabled); + cmdbuf.setDepthWriteEnable(depth_write_enabled); } if (depth_test_enabled && dirty_state.depth_compare_op) { dirty_state.depth_compare_op = false; - cmdbuf.setDepthCompareOpEXT(depth_compare_op); + cmdbuf.setDepthCompareOp(depth_compare_op); } if (dirty_state.depth_bounds_test_enabled) { dirty_state.depth_bounds_test_enabled = false; if (instance.IsDepthBoundsSupported()) { - cmdbuf.setDepthBoundsTestEnableEXT(depth_bounds_test_enabled); + cmdbuf.setDepthBoundsTestEnable(depth_bounds_test_enabled); } } if (depth_bounds_test_enabled && dirty_state.depth_bounds) { @@ -203,7 +203,7 @@ void DynamicState::Commit(const Instance& instance, const vk::CommandBuffer& cmd } if (dirty_state.depth_bias_enabled) { dirty_state.depth_bias_enabled = false; - cmdbuf.setDepthBiasEnableEXT(depth_bias_enabled); + cmdbuf.setDepthBiasEnable(depth_bias_enabled); } if (depth_bias_enabled && dirty_state.depth_bias) { dirty_state.depth_bias = false; @@ -211,28 +211,28 @@ void DynamicState::Commit(const Instance& instance, const vk::CommandBuffer& cmd } if (dirty_state.stencil_test_enabled) { dirty_state.stencil_test_enabled = false; - cmdbuf.setStencilTestEnableEXT(stencil_test_enabled); + cmdbuf.setStencilTestEnable(stencil_test_enabled); } if (stencil_test_enabled) { if (dirty_state.stencil_front_ops && dirty_state.stencil_back_ops && stencil_front_ops == stencil_back_ops) { dirty_state.stencil_front_ops = false; dirty_state.stencil_back_ops = false; - cmdbuf.setStencilOpEXT(vk::StencilFaceFlagBits::eFrontAndBack, - stencil_front_ops.fail_op, stencil_front_ops.pass_op, - stencil_front_ops.depth_fail_op, stencil_front_ops.compare_op); + cmdbuf.setStencilOp(vk::StencilFaceFlagBits::eFrontAndBack, stencil_front_ops.fail_op, + stencil_front_ops.pass_op, stencil_front_ops.depth_fail_op, + stencil_front_ops.compare_op); } else { if (dirty_state.stencil_front_ops) { dirty_state.stencil_front_ops = false; - cmdbuf.setStencilOpEXT(vk::StencilFaceFlagBits::eFront, stencil_front_ops.fail_op, - stencil_front_ops.pass_op, stencil_front_ops.depth_fail_op, - stencil_front_ops.compare_op); + cmdbuf.setStencilOp(vk::StencilFaceFlagBits::eFront, stencil_front_ops.fail_op, + stencil_front_ops.pass_op, stencil_front_ops.depth_fail_op, + stencil_front_ops.compare_op); } if (dirty_state.stencil_back_ops) { dirty_state.stencil_back_ops = false; - cmdbuf.setStencilOpEXT(vk::StencilFaceFlagBits::eBack, stencil_back_ops.fail_op, - stencil_back_ops.pass_op, stencil_back_ops.depth_fail_op, - stencil_back_ops.compare_op); + cmdbuf.setStencilOp(vk::StencilFaceFlagBits::eBack, stencil_back_ops.fail_op, + stencil_back_ops.pass_op, stencil_back_ops.depth_fail_op, + stencil_back_ops.compare_op); } } if (dirty_state.stencil_front_reference && dirty_state.stencil_back_reference && @@ -291,16 +291,16 @@ void DynamicState::Commit(const Instance& instance, const vk::CommandBuffer& cmd if (dirty_state.primitive_restart_enable) { dirty_state.primitive_restart_enable = false; if (instance.IsPrimitiveRestartDisableSupported()) { - cmdbuf.setPrimitiveRestartEnableEXT(primitive_restart_enable); + cmdbuf.setPrimitiveRestartEnable(primitive_restart_enable); } } if (dirty_state.cull_mode) { dirty_state.cull_mode = false; - cmdbuf.setCullModeEXT(cull_mode); + cmdbuf.setCullMode(cull_mode); } if (dirty_state.front_face) { dirty_state.front_face = false; - cmdbuf.setFrontFaceEXT(front_face); + cmdbuf.setFrontFace(front_face); } if (dirty_state.blend_constants) { dirty_state.blend_constants = false; diff --git a/src/video_core/texture_cache/image_info.cpp b/src/video_core/texture_cache/image_info.cpp index 26928eaf7..39322f449 100644 --- a/src/video_core/texture_cache/image_info.cpp +++ b/src/video_core/texture_cache/image_info.cpp @@ -16,14 +16,15 @@ using VideoOutFormat = Libraries::VideoOut::PixelFormat; static vk::Format ConvertPixelFormat(const VideoOutFormat format) { switch (format) { - case VideoOutFormat::A8R8G8B8Srgb: - return vk::Format::eB8G8R8A8Srgb; case VideoOutFormat::A8B8G8R8Srgb: + // Remaining formats are mapped to RGBA for internal consistency and changed to BGRA in the + // frame image view. + case VideoOutFormat::A8R8G8B8Srgb: return vk::Format::eR8G8B8A8Srgb; case VideoOutFormat::A2R10G10B10: case VideoOutFormat::A2R10G10B10Srgb: case VideoOutFormat::A2R10G10B10Bt2020Pq: - return vk::Format::eA2R10G10B10UnormPack32; + return vk::Format::eA2B10G10R10UnormPack32; default: break; } diff --git a/src/video_core/texture_cache/image_view.h b/src/video_core/texture_cache/image_view.h index 23c703d23..6a17490bf 100644 --- a/src/video_core/texture_cache/image_view.h +++ b/src/video_core/texture_cache/image_view.h @@ -34,8 +34,6 @@ struct ImageViewInfo { struct Image; -constexpr Common::SlotId NULL_IMAGE_VIEW_ID{0}; - struct ImageView { ImageView(const Vulkan::Instance& instance, const ImageViewInfo& info, Image& image, ImageId image_id); diff --git a/src/video_core/texture_cache/texture_cache.cpp b/src/video_core/texture_cache/texture_cache.cpp index d41ee57cc..82f4d6413 100644 --- a/src/video_core/texture_cache/texture_cache.cpp +++ b/src/video_core/texture_cache/texture_cache.cpp @@ -8,6 +8,7 @@ #include "common/debug.h" #include "video_core/buffer_cache/buffer_cache.h" #include "video_core/page_manager.h" +#include "video_core/renderer_vulkan/liverpool_to_vk.h" #include "video_core/renderer_vulkan/vk_instance.h" #include "video_core/renderer_vulkan/vk_scheduler.h" #include "video_core/texture_cache/host_compatibility.h" @@ -23,31 +24,41 @@ TextureCache::TextureCache(const Vulkan::Instance& instance_, Vulkan::Scheduler& BufferCache& buffer_cache_, PageManager& tracker_) : instance{instance_}, scheduler{scheduler_}, buffer_cache{buffer_cache_}, tracker{tracker_}, tile_manager{instance, scheduler} { + // Create basic null image at fixed image ID. + const auto null_id = GetNullImage(vk::Format::eR8G8B8A8Unorm); + ASSERT(null_id.index == NULL_IMAGE_ID.index); +} + +TextureCache::~TextureCache() = default; + +ImageId TextureCache::GetNullImage(const vk::Format format) { + const auto existing_image = null_images.find(format); + if (existing_image != null_images.end()) { + return existing_image->second; + } + ImageInfo info{}; - info.pixel_format = vk::Format::eR8G8B8A8Unorm; + info.pixel_format = format; info.type = vk::ImageType::e2D; - info.tiling_idx = u32(AmdGpu::TilingMode::Texture_MicroTiled); + info.tiling_idx = static_cast(AmdGpu::TilingMode::Texture_MicroTiled); info.num_bits = 32; info.UpdateSize(); + const ImageId null_id = slot_images.insert(instance, scheduler, info); - ASSERT(null_id.index == NULL_IMAGE_ID.index); auto& img = slot_images[null_id]; + const vk::Image& null_image = img.image; - Vulkan::SetObjectName(instance.GetDevice(), null_image, "Null Image"); + Vulkan::SetObjectName(instance.GetDevice(), null_image, + fmt::format("Null Image ({})", vk::to_string(format))); + img.flags = ImageFlagBits::Empty; img.track_addr = img.info.guest_address; img.track_addr_end = img.info.guest_address + img.info.guest_size; - ImageViewInfo view_info; - const auto null_view_id = - slot_image_views.insert(instance, view_info, slot_images[null_id], null_id); - ASSERT(null_view_id.index == NULL_IMAGE_VIEW_ID.index); - const vk::ImageView& null_image_view = slot_image_views[null_view_id].image_view.get(); - Vulkan::SetObjectName(instance.GetDevice(), null_image_view, "Null Image View"); + null_images.emplace(format, null_id); + return null_id; } -TextureCache::~TextureCache() = default; - void TextureCache::MarkAsMaybeDirty(ImageId image_id, Image& image) { if (image.hash == 0) { // Initialize hash @@ -296,7 +307,7 @@ ImageId TextureCache::FindImage(BaseDesc& desc, FindFlags flags) { const auto& info = desc.info; if (info.guest_address == 0) [[unlikely]] { - return NULL_IMAGE_ID; + return GetNullImage(info.pixel_format); } std::scoped_lock lock{mutex}; @@ -319,15 +330,14 @@ ImageId TextureCache::FindImage(BaseDesc& desc, FindFlags flags) { continue; } if (False(flags & FindFlags::RelaxFmt) && - !IsVulkanFormatCompatible(info.pixel_format, cache_image.info.pixel_format)) { + (!IsVulkanFormatCompatible(info.pixel_format, cache_image.info.pixel_format) || + (cache_image.info.type != info.type && info.size != Extent3D{1, 1, 1}))) { continue; } if (True(flags & FindFlags::ExactFmt) && info.pixel_format != cache_image.info.pixel_format) { continue; } - 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/texture_cache.h b/src/video_core/texture_cache/texture_cache.h index f262768ea..b6bf88958 100644 --- a/src/video_core/texture_cache/texture_cache.h +++ b/src/video_core/texture_cache/texture_cache.h @@ -246,6 +246,9 @@ private: } } + /// Gets or creates a null image for a particular format. + ImageId GetNullImage(vk::Format format); + /// Create an image from the given parameters [[nodiscard]] ImageId InsertImage(const ImageInfo& info, VAddr cpu_addr); @@ -285,6 +288,7 @@ private: Common::SlotVector slot_images; Common::SlotVector slot_image_views; tsl::robin_map samplers; + tsl::robin_map null_images; PageTable page_table; std::mutex mutex;